├── .Rbuildignore ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── LICENSE.note ├── NAMESPACE ├── NEWS.md ├── R ├── aaa.R ├── arc.R ├── arc_bar.R ├── autodensity.R ├── autohistogram.R ├── autopoint.R ├── bezier.R ├── bspline.R ├── bspline_closed.R ├── circle.R ├── concaveman.R ├── cpp11.R ├── diagonal.R ├── diagonal_wide.R ├── ellipse.R ├── errorbar.R ├── facet_grid_paginate.R ├── facet_matrix.R ├── facet_row.R ├── facet_stereo.R ├── facet_wrap_paginate.R ├── facet_zoom.R ├── ggforce-package.R ├── ggproto-classes.R ├── interpolate.R ├── labeller.R ├── link.R ├── mark_circle.R ├── mark_ellipse.R ├── mark_hull.R ├── mark_label.R ├── mark_rect.R ├── parallel_sets.R ├── position-jitternormal.R ├── position_auto.R ├── position_floatstack.R ├── regon.R ├── scale-depth.R ├── scale-unit.R ├── shape.R ├── sina.R ├── spiro.R ├── themes.R ├── trans.R ├── trans_linear.R ├── utilities.R ├── voronoi.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── man ├── facet_grid_paginate.Rd ├── facet_matrix.Rd ├── facet_row.Rd ├── facet_stereo.Rd ├── facet_wrap_paginate.Rd ├── facet_zoom.Rd ├── figures │ ├── README-example-1.png │ ├── lifecycle-archived.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-questioning.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ ├── logo.png │ └── logo.svg ├── gather_set_data.Rd ├── geom_arc.Rd ├── geom_arc_bar.Rd ├── geom_autohistogram.Rd ├── geom_autopoint.Rd ├── geom_bezier.Rd ├── geom_bspline.Rd ├── geom_bspline_closed.Rd ├── geom_circle.Rd ├── geom_delvor.Rd ├── geom_diagonal.Rd ├── geom_diagonal_wide.Rd ├── geom_ellipse.Rd ├── geom_link.Rd ├── geom_mark_circle.Rd ├── geom_mark_ellipse.Rd ├── geom_mark_hull.Rd ├── geom_mark_rect.Rd ├── geom_parallel_sets.Rd ├── geom_regon.Rd ├── geom_shape.Rd ├── geom_sina.Rd ├── geom_spiro.Rd ├── ggforce-extensions.Rd ├── ggforce-package.Rd ├── interpolateDataFrame.Rd ├── label_tex.Rd ├── linear_trans.Rd ├── n_pages.Rd ├── position_auto.Rd ├── position_jitternormal.Rd ├── power_trans.Rd ├── radial_trans.Rd ├── scale_depth.Rd ├── scale_unit.Rd ├── shapeGrob.Rd ├── stat_err.Rd ├── theme_no_axes.Rd └── trans_reverser.Rd ├── pkgdown └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico ├── revdep ├── README.md ├── cran.md ├── failures.md └── problems.md └── src ├── .gitignore ├── bSpline.cpp ├── bezier.cpp ├── concaveman.cpp ├── concaveman.h ├── cpp11.cpp ├── deBoor.cpp ├── deBoor.h ├── ellipseEnclose.cpp ├── enclose.cpp ├── pointPath.cpp └── predicates.cpp /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\.travis\.yml$ 4 | ^appveyor\.yml$ 5 | ^README\.Rmd$ 6 | ^README-.*\.png$ 7 | ^CODE_OF_CONDUCT\.md$ 8 | ^_pkgdown\.yml$ 9 | ^docs$ 10 | ^pkgdown$ 11 | ^\.github/workflows/R-CMD-check\.yaml$ 12 | ^vignettes/guides$ 13 | ^codecov\.yml$ 14 | ^cran-comments\.md$ 15 | ^CRAN-RELEASE$ 16 | ^\.github$ 17 | ^revdep 18 | ^CRAN-SUBMISSION$ 19 | ^LICENSE.md$ 20 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at codeofconduct@posit.co. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at 118 | . 119 | 120 | Community Impact Guidelines were inspired by 121 | [Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion]. 122 | 123 | For answers to common questions about this code of conduct, see the FAQ at 124 | . Translations are available at . 125 | 126 | [homepage]: https://www.contributor-covenant.org 127 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | 12 | name: R-CMD-check.yaml 13 | 14 | permissions: read-all 15 | 16 | jobs: 17 | R-CMD-check: 18 | runs-on: ${{ matrix.config.os }} 19 | 20 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | config: 26 | - {os: macos-latest, r: 'release'} 27 | 28 | - {os: windows-latest, r: 'release'} 29 | # use 4.0 or 4.1 to check with rtools40's older compiler 30 | - {os: windows-latest, r: 'oldrel-4'} 31 | 32 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 33 | - {os: ubuntu-latest, r: 'release'} 34 | - {os: ubuntu-latest, r: 'oldrel-1'} 35 | - {os: ubuntu-latest, r: 'oldrel-2'} 36 | - {os: ubuntu-latest, r: 'oldrel-3'} 37 | - {os: ubuntu-latest, r: 'oldrel-4'} 38 | 39 | env: 40 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 41 | R_KEEP_PKG_SOURCE: yes 42 | 43 | steps: 44 | - uses: actions/checkout@v4 45 | 46 | - uses: r-lib/actions/setup-pandoc@v2 47 | 48 | - uses: r-lib/actions/setup-r@v2 49 | with: 50 | r-version: ${{ matrix.config.r }} 51 | http-user-agent: ${{ matrix.config.http-user-agent }} 52 | use-public-rspm: true 53 | 54 | - uses: r-lib/actions/setup-r-dependencies@v2 55 | with: 56 | extra-packages: any::rcmdcheck 57 | needs: check 58 | 59 | - uses: r-lib/actions/check-r-package@v2 60 | with: 61 | upload-snapshots: true 62 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 63 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | 11 | name: pkgdown.yaml 12 | 13 | permissions: read-all 14 | 15 | jobs: 16 | pkgdown: 17 | runs-on: ubuntu-latest 18 | # Only restrict concurrency for non-PR jobs 19 | concurrency: 20 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 21 | env: 22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 23 | permissions: 24 | contents: write 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - uses: r-lib/actions/setup-pandoc@v2 29 | 30 | - uses: r-lib/actions/setup-r@v2 31 | with: 32 | use-public-rspm: true 33 | 34 | - uses: r-lib/actions/setup-r-dependencies@v2 35 | with: 36 | extra-packages: any::pkgdown, local::. 37 | needs: website 38 | 39 | - name: Build site 40 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 41 | shell: Rscript {0} 42 | 43 | - name: Deploy to GitHub pages 🚀 44 | if: github.event_name != 'pull_request' 45 | uses: JamesIves/github-pages-deploy-action@v4.5.0 46 | with: 47 | clean: false 48 | branch: gh-pages 49 | folder: docs 50 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: pr-commands.yaml 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | document: 13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 14 | name: document 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | permissions: 19 | contents: write 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - uses: r-lib/actions/pr-fetch@v2 24 | with: 25 | repo-token: ${{ secrets.GITHUB_TOKEN }} 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | extra-packages: any::roxygen2 34 | needs: pr-document 35 | 36 | - name: Document 37 | run: roxygen2::roxygenise() 38 | shell: Rscript {0} 39 | 40 | - name: commit 41 | run: | 42 | git config --local user.name "$GITHUB_ACTOR" 43 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 44 | git add man/\* NAMESPACE 45 | git commit -m 'Document' 46 | 47 | - uses: r-lib/actions/pr-push@v2 48 | with: 49 | repo-token: ${{ secrets.GITHUB_TOKEN }} 50 | 51 | style: 52 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 53 | name: style 54 | runs-on: ubuntu-latest 55 | env: 56 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 57 | permissions: 58 | contents: write 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - uses: r-lib/actions/pr-fetch@v2 63 | with: 64 | repo-token: ${{ secrets.GITHUB_TOKEN }} 65 | 66 | - uses: r-lib/actions/setup-r@v2 67 | 68 | - name: Install dependencies 69 | run: install.packages("styler") 70 | shell: Rscript {0} 71 | 72 | - name: Style 73 | run: styler::style_pkg() 74 | shell: Rscript {0} 75 | 76 | - name: commit 77 | run: | 78 | git config --local user.name "$GITHUB_ACTOR" 79 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 80 | git add \*.R 81 | git commit -m 'Style' 82 | 83 | - uses: r-lib/actions/pr-push@v2 84 | with: 85 | repo-token: ${{ secrets.GITHUB_TOKEN }} 86 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | 8 | name: test-coverage.yaml 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | test-coverage: 14 | runs-on: ubuntu-latest 15 | env: 16 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: r-lib/actions/setup-r@v2 22 | with: 23 | use-public-rspm: true 24 | 25 | - uses: r-lib/actions/setup-r-dependencies@v2 26 | with: 27 | extra-packages: any::covr, any::xml2 28 | needs: coverage 29 | 30 | - name: Test coverage 31 | run: | 32 | cov <- covr::package_coverage( 33 | quiet = FALSE, 34 | clean = FALSE, 35 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") 36 | ) 37 | print(cov) 38 | covr::to_cobertura(cov) 39 | shell: Rscript {0} 40 | 41 | - uses: codecov/codecov-action@v5 42 | with: 43 | # Fail if error if not on PR, or if on PR and token is given 44 | fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} 45 | files: ./cobertura.xml 46 | plugins: noop 47 | disable_search: true 48 | token: ${{ secrets.CODECOV_TOKEN }} 49 | 50 | - name: Show testthat output 51 | if: always() 52 | run: | 53 | ## -------------------------------------------------------------------- 54 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 55 | shell: bash 56 | 57 | - name: Upload test results 58 | if: failure() 59 | uses: actions/upload-artifact@v4 60 | with: 61 | name: coverage-test-failures 62 | path: ${{ runner.temp }}/package 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | .Rproj.user 3 | .Rhistory 4 | .RData 5 | *.Rproj 6 | src/*.o 7 | src/*.so 8 | src/*.dll 9 | inst/doc 10 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ggforce 2 | Type: Package 3 | Title: Accelerating 'ggplot2' 4 | Version: 0.5.0.9000 5 | Authors@R: 6 | c(person(given = "Thomas Lin", 7 | family = "Pedersen", 8 | role = c("cre", "aut"), 9 | email = "thomasp85@gmail.com", 10 | comment = c(ORCID = "0000-0002-5147-4711")), 11 | person("RStudio", 12 | role = "cph")) 13 | Maintainer: Thomas Lin Pedersen 14 | Description: The aim of 'ggplot2' is to aid in visual data investigations. This 15 | focus has led to a lack of facilities for composing specialised plots. 16 | 'ggforce' aims to be a collection of mainly new stats and geoms that fills 17 | this gap. All additional functionality is aimed to come through the official 18 | extension system so using 'ggforce' should be a stable experience. 19 | URL: https://ggforce.data-imaginist.com, https://github.com/thomasp85/ggforce 20 | BugReports: https://github.com/thomasp85/ggforce/issues 21 | License: MIT + file LICENSE 22 | Encoding: UTF-8 23 | Depends: 24 | ggplot2 (>= 3.5.0), 25 | R (>= 3.3.0) 26 | Imports: 27 | grid, 28 | scales, 29 | MASS, 30 | tweenr (>= 0.1.5), 31 | gtable, 32 | rlang, 33 | polyclip, 34 | stats, 35 | grDevices, 36 | tidyselect, 37 | withr, 38 | utils, 39 | lifecycle, 40 | cli, 41 | vctrs, 42 | systemfonts 43 | RoxygenNote: 7.3.2 44 | LinkingTo: 45 | cpp11 46 | Suggests: 47 | sessioninfo, 48 | deldir, 49 | latex2exp, 50 | reshape2, 51 | units (>= 0.8.0), 52 | covr 53 | Collate: 54 | 'aaa.R' 55 | 'shape.R' 56 | 'arc_bar.R' 57 | 'arc.R' 58 | 'autodensity.R' 59 | 'autohistogram.R' 60 | 'autopoint.R' 61 | 'bezier.R' 62 | 'bspline.R' 63 | 'bspline_closed.R' 64 | 'circle.R' 65 | 'concaveman.R' 66 | 'cpp11.R' 67 | 'diagonal.R' 68 | 'diagonal_wide.R' 69 | 'ellipse.R' 70 | 'errorbar.R' 71 | 'facet_grid_paginate.R' 72 | 'facet_matrix.R' 73 | 'facet_row.R' 74 | 'facet_stereo.R' 75 | 'facet_wrap_paginate.R' 76 | 'facet_zoom.R' 77 | 'ggforce-package.R' 78 | 'ggproto-classes.R' 79 | 'interpolate.R' 80 | 'labeller.R' 81 | 'link.R' 82 | 'mark_circle.R' 83 | 'mark_ellipse.R' 84 | 'mark_hull.R' 85 | 'mark_label.R' 86 | 'mark_rect.R' 87 | 'parallel_sets.R' 88 | 'position-jitternormal.R' 89 | 'position_auto.R' 90 | 'position_floatstack.R' 91 | 'regon.R' 92 | 'scale-depth.R' 93 | 'scale-unit.R' 94 | 'sina.R' 95 | 'spiro.R' 96 | 'themes.R' 97 | 'trans.R' 98 | 'trans_linear.R' 99 | 'utilities.R' 100 | 'voronoi.R' 101 | 'zzz.R' 102 | Roxygen: list(markdown = TRUE) 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2019 2 | COPYRIGHT HOLDER: Thomas Lin Pedersen 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 Thomas Lin Pedersen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.note: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | The concaveman.h file is redistributed with the following license 3 | 4 | BSD 2-Clause License 5 | 6 | Copyright (c) 2019, sadaszewski 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | -------------------------------------------------------------------------------- 31 | The robust-predicates code used in concaveman is redistributed with the 32 | following license 33 | 34 | `Robust-Predicate` is licensed under the following terms: 35 | 36 | This program may be freely redistributed under the condition that the copyright 37 | notices (including this entire header) are not removed, and no compensation is 38 | received through use of the software. Private, research, and institutional use 39 | is free. You may distribute modified versions of this code `UNDER THE CONDITION 40 | THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE SAME FILE REMAIN UNDER 41 | COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE AND OBJECT CODE ARE MADE FREELY 42 | AVAILABLE WITHOUT CHARGE, AND CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS`. 43 | Distribution of this code as part of a commercial system is permissible `ONLY BY 44 | DIRECT ARRANGEMENT WITH THE AUTHOR`. (If you are not directly supplying this 45 | code to a customer, and you are instead telling them how they can obtain it for 46 | free, then you are not required to make any arrangement with me.) 47 | 48 | `DISCLAIMER`: Neither I nor: Columbia University, the Massachusetts Institute of 49 | Technology, the University of Sydney, nor the National Aeronautics and Space 50 | Administration warrant this code in any way whatsoever. This code is provided 51 | "as-is" to be used at your own risk. 52 | 53 | -------------------------------------------------------------------------------- 54 | -------------------------------------------------------------------------------- /R/aaa.R: -------------------------------------------------------------------------------- 1 | utils::globalVariables(c( 2 | 'x', 'y' 3 | )) 4 | 5 | `%||%` <- function(x, y) { 6 | if (is.null(x)) y else x 7 | } 8 | 9 | is.waive <- function(x) inherits(x, 'waiver') 10 | 11 | `%|W|%` <- function(x, y) { 12 | if (is.waive(x)) y else x 13 | } 14 | 15 | expand_default <- function(scale, discrete = c(0, 0.6), 16 | continuous = c(0.05, 0)) { 17 | scale$expand %|W|% if (scale$is_discrete()) discrete else continuous 18 | } 19 | 20 | combine_aes <- function(aes1, aes2) { 21 | aes_all <- c(aes1[setdiff(names(aes1), names(aes2))], aes2) 22 | class(aes_all) <- class(aes1) 23 | aes_all 24 | } 25 | 26 | empty_data <- function(x) { 27 | length(x) == 0 || nrow(x) == 0 28 | } 29 | 30 | # This function is like base::make.unique, but it 31 | # maintains the ordering of the original names if the values 32 | # are sorted. 33 | make_unique <- function(x, sep = '.') { 34 | if (!anyDuplicated(x)) return(x) 35 | groups <- match(x, unique(x)) 36 | suffix <- unsplit(lapply(split(x, groups), seq_along), groups) 37 | max_chars <- nchar(max(suffix)) 38 | suffix_format <- paste0('%0', max_chars, 'd') 39 | paste0(x, sep, sprintf(suffix_format, suffix)) 40 | } 41 | -------------------------------------------------------------------------------- /R/autodensity.R: -------------------------------------------------------------------------------- 1 | #' @rdname geom_autohistogram 2 | #' @inheritParams ggplot2::geom_point 3 | #' @inheritParams ggplot2::geom_density 4 | #' @export 5 | geom_autodensity <- function(mapping = NULL, data = NULL, 6 | stat = "autodensity", position = "floatstack", 7 | ..., 8 | bw = "nrd0", 9 | adjust = 1, 10 | kernel = "gaussian", 11 | n = 512, 12 | trim = FALSE, 13 | na.rm = FALSE, 14 | show.legend = NA, 15 | inherit.aes = TRUE, 16 | outline.type = "upper") { 17 | extra_mapping <- aes(x = .panel_x, y = .panel_y) 18 | if (is.null(mapping$x)) mapping$x <- extra_mapping$x 19 | if (is.null(mapping$y)) mapping$y <- extra_mapping$y 20 | class(mapping) <- 'uneval' 21 | 22 | layer( 23 | data = data, 24 | mapping = mapping, 25 | stat = stat, 26 | geom = GeomAutoarea, 27 | position = position, 28 | show.legend = show.legend, 29 | inherit.aes = inherit.aes, 30 | params = list2( 31 | bw = bw, 32 | adjust = adjust, 33 | kernel = kernel, 34 | n = n, 35 | trim = trim, 36 | na.rm = na.rm, 37 | ..., 38 | outline.type = outline.type 39 | ) 40 | ) 41 | } 42 | #' @rdname ggforce-extensions 43 | #' @format NULL 44 | #' @usage NULL 45 | #' @export 46 | StatAutodensity <- ggproto('StatAutodensity', StatDensity, 47 | setup_params = function(data, params) { 48 | params$panel_range <- lapply(split(data$y, data$PANEL), function(y) { 49 | if (length(y) == 0) return() 50 | range(y, na.rm=TRUE) 51 | }) 52 | params$panel_count <- lapply(split(data$y, data$PANEL), function(y)length(y[is.finite(y)])) 53 | 54 | params 55 | }, 56 | compute_group = function(self, data, scales, bw = "nrd0", adjust = 1, kernel = "gaussian", 57 | n = 512, trim = FALSE, na.rm = FALSE, panel_range = list(), panel_count = list()) { 58 | if (scales$x$is_discrete()) { 59 | bins <- split(data, factor(data$x, levels = seq_len(scales$x$range_c$range[2]))) 60 | binned <- lapply(as.integer(names(bins)), function(x) { 61 | count <- nrow(bins[[x]]) 62 | pad <- if (count == 0) 0.5 else 0.3 63 | pad <- pad * c(-1, 1) 64 | data_frame0( 65 | x = x + pad, 66 | density = count / nrow(data) 67 | ) 68 | }) 69 | binned <- vec_rbind(!!!binned) 70 | binned$scaled <- binned$density / max(binned$density) 71 | binned$ndensity <- binned$density / max(binned$density) 72 | binned$count <- binned$density * nrow(data) 73 | binned$n <- nrow(data) 74 | } else { 75 | binned <- ggproto_parent(StatDensity, self)$compute_group( 76 | data, scales, bw = bw, adjust = adjust, kernel = kernel, 77 | n = n, trim = trim, na.rm = na.rm 78 | ) 79 | } 80 | panel_range <- panel_range[[data$PANEL[1]]] 81 | panel_count <- panel_count[[data$PANEL[1]]] 82 | ymin <- panel_range[1] 83 | binned$y <- ymin + binned$ndensity * (panel_range[2] - panel_range[1]) * nrow(data) / panel_count 84 | 85 | binned$ymin <- ymin 86 | binned$ymax <- binned$y 87 | binned 88 | }, 89 | 90 | default_aes = aes(weight = 1), 91 | required_aes = c("x", "y") 92 | ) 93 | #' @rdname ggforce-extensions 94 | #' @format NULL 95 | #' @usage NULL 96 | #' @export 97 | GeomAutoarea <- ggproto('GeomAutoarea', GeomArea, 98 | setup_data = function(data, params) { 99 | data[order(data$PANEL, data$group, data$x), ] 100 | }, 101 | draw_panel = function(self, data, panel_params, coord, na.rm = FALSE, ...) { 102 | y_range <- coord$range(panel_params)$y 103 | y_span <- y_range[2] - y_range[1] 104 | panel_min <- min(data$ymin) 105 | panel_span <- max(data$ymax) - panel_min 106 | data$ymin <- ((data$ymin - panel_min) / panel_span) * y_span * 0.9 + y_range[1] 107 | data$ymax <- ((data$ymax - panel_min) / panel_span) * y_span * 0.9 + y_range[1] 108 | ggproto_parent(GeomArea, self)$draw_panel( 109 | data = data, 110 | panel_params = panel_params, 111 | coord = coord, 112 | na.rm = na.rm, 113 | ... 114 | ) 115 | } 116 | ) 117 | -------------------------------------------------------------------------------- /R/autohistogram.R: -------------------------------------------------------------------------------- 1 | #' A distribution geoms that fills the panel and works with discrete and continuous data 2 | #' 3 | #' These versions of the histogram and density geoms have been designed 4 | #' specifically for diagonal plotting with [facet_matrix()]. They differ from 5 | #' [ggplot2::geom_histogram()] and [ggplot2::geom_density()] in that they 6 | #' defaults to mapping `x` and `y` to `.panel_x` and `.panel_y` respectively, 7 | #' they ignore the y scale of the panel and fills it out, and they work for both 8 | #' continuous and discrete x scales. 9 | #' 10 | #' @inheritParams ggplot2::geom_histogram 11 | #' 12 | #' @seealso [facet_matrix] for creating matrix grids 13 | #' 14 | #' @export 15 | #' 16 | #' @examples 17 | #' # A matrix plot with a mix of discrete and continuous variables 18 | #' p <- ggplot(mpg) + 19 | #' geom_autopoint() + 20 | #' facet_matrix(vars(drv:fl), layer.diag = 2, grid.y.diag = FALSE) 21 | #' p 22 | #' 23 | #' # Diagonal histograms 24 | #' p + geom_autohistogram() 25 | #' 26 | #' # Diagonal density distributions 27 | #' p + geom_autodensity() 28 | #' 29 | #' # You can use them like regular layers with groupings etc 30 | #' p + geom_autodensity(aes(colour = drv, fill = drv), 31 | #' alpha = 0.4) 32 | geom_autohistogram <- function(mapping = NULL, data = NULL, 33 | stat = "autobin", position = "floatstack", 34 | ..., 35 | bins = NULL, 36 | na.rm = FALSE, 37 | show.legend = NA, 38 | inherit.aes = TRUE) { 39 | extra_mapping <- aes(x = .panel_x, y = .panel_y) 40 | if (is.null(mapping$x)) mapping$x <- extra_mapping$x 41 | if (is.null(mapping$y)) mapping$y <- extra_mapping$y 42 | class(mapping) <- 'uneval' 43 | 44 | layer( 45 | data = data, 46 | mapping = mapping, 47 | stat = stat, 48 | geom = GeomAutorect, 49 | position = position, 50 | show.legend = show.legend, 51 | inherit.aes = inherit.aes, 52 | params = list2( 53 | bins = bins, 54 | na.rm = na.rm, 55 | ... 56 | ) 57 | ) 58 | } 59 | #' @rdname ggforce-extensions 60 | #' @format NULL 61 | #' @usage NULL 62 | #' @export 63 | StatAutobin <- ggproto('StatAutobin', StatBin, 64 | setup_params = function(data, params) { 65 | if (is.null(params$bins)) params$bins <- 30 66 | params$panel_range <- lapply(split(data$y, data$PANEL), function(y) { 67 | if (length(y) == 0) return() 68 | range(y, na.rm=TRUE) 69 | }) 70 | params$panel_count <- lapply(split(data$y, data$PANEL), function(y)length(y[is.finite(y)])) 71 | 72 | params 73 | }, 74 | compute_group = function(self, data, scales, binwidth = NULL, bins = NULL, 75 | center = NULL, boundary = NULL, 76 | closed = c("right", "left"), pad = FALSE, 77 | breaks = NULL, panel_range = list(), panel_count = list(), 78 | # The following arguments are not used, but must 79 | # be listed so parameters are computed correctly 80 | origin = NULL, right = NULL, drop = NULL, 81 | width = NULL) { 82 | if (scales$x$is_discrete()) { 83 | binned <- lapply(split(data, data$x), function(d) { 84 | data_frame0( 85 | count = nrow(d), 86 | x = d$x[1], 87 | xmin = d$x[1] - 0.5, 88 | xmax = d$x[1] + 0.5, 89 | width = 1 90 | ) 91 | }) 92 | binned <- vec_rbind(!!!binned) 93 | binned$density <- binned$count / sum(binned$count) 94 | binned$ncount <- binned$count / max(binned$count) 95 | binned$ndensity <- binned$density / max(binned$density) 96 | } else { 97 | binned <- ggproto_parent(StatBin, self)$compute_group( 98 | data, scales, binwidth = binwidth, bins = bins, center = center, 99 | boundary = boundary, closed = closed, pad = pad, breaks = breaks, 100 | origin = origin, right = right, drop = drop 101 | ) 102 | } 103 | 104 | panel_range <- panel_range[[data$PANEL[1]]] 105 | panel_count <- panel_count[[data$PANEL[1]]] 106 | binned$ymin <- panel_range[1] 107 | binned$ymax <- binned$ymin + binned$ncount * (panel_range[2] - panel_range[1]) * nrow(data) / panel_count 108 | binned$y <- (binned$ymin + binned$ymax) / 2 109 | binned 110 | }, 111 | 112 | default_aes = aes(weight = 1), 113 | required_aes = c("x", "y") 114 | ) 115 | #' @rdname ggforce-extensions 116 | #' @format NULL 117 | #' @usage NULL 118 | #' @export 119 | GeomAutorect <- ggproto('PositionAutorect', GeomRect, 120 | draw_panel = function(self, data, panel_params, coord, ...) { 121 | y_range <- coord$range(panel_params)$y 122 | y_span <- y_range[2] - y_range[1] 123 | panel_min <- min(data$ymin) 124 | panel_span <- max(data$ymax) - panel_min 125 | data$ymin <- ((data$ymin - panel_min) / panel_span) * y_span * 0.9 + y_range[1] 126 | data$ymax <- ((data$ymax - panel_min) / panel_span) * y_span * 0.9 + y_range[1] 127 | ggproto_parent(GeomRect, self)$draw_panel(data, panel_params, coord, ...) 128 | }, 129 | extra_params = c('na.rm', 'lineend', 'linejoin') 130 | ) 131 | -------------------------------------------------------------------------------- /R/autopoint.R: -------------------------------------------------------------------------------- 1 | #' A point geom specialised for scatterplot matrices 2 | #' 3 | #' This geom is a specialisation of [ggplot2::geom_point()] with two changes. It 4 | #' defaults to mapping `x` and `y` to `.panel_x` and `.panel_y` respectively, 5 | #' and it defaults to using [position_auto()] to jitter the points based on the 6 | #' combination of position scale types. 7 | #' 8 | #' @inheritParams ggplot2::geom_point 9 | #' 10 | #' @seealso [facet_matrix] for how to lay out scatterplot matrices and 11 | #' [position_auto] for information about the position adjustments 12 | #' 13 | #' @export 14 | #' 15 | #' @examples 16 | #' # Continuous vs continuous: No jitter 17 | #' ggplot(mpg) + geom_autopoint(aes(cty, hwy)) 18 | #' 19 | #' # Continuous vs discrete: sina jitter 20 | #' ggplot(mpg) + geom_autopoint(aes(cty, drv)) 21 | #' 22 | #' # Discrete vs discrete: disc-jitter 23 | #' ggplot(mpg) + geom_autopoint(aes(fl, drv)) 24 | #' 25 | #' # Used with facet_matrix (x and y are automatically mapped) 26 | #' ggplot(mpg) + 27 | #' geom_autopoint() + 28 | #' facet_matrix(vars(drv:fl)) 29 | #' 30 | geom_autopoint <- function(mapping = NULL, data = NULL, 31 | stat = "identity", position = "auto", 32 | ..., 33 | na.rm = FALSE, 34 | show.legend = NA, 35 | inherit.aes = TRUE) { 36 | extra_mapping <- aes(x = .panel_x, y = .panel_y) 37 | if (is.null(mapping$x)) mapping$x <- extra_mapping$x 38 | if (is.null(mapping$y)) mapping$y <- extra_mapping$y 39 | class(mapping) <- 'uneval' 40 | layer( 41 | data = data, 42 | mapping = mapping, 43 | stat = stat, 44 | geom = GeomPoint, 45 | position = position, 46 | show.legend = show.legend, 47 | inherit.aes = inherit.aes, 48 | params = list2( 49 | na.rm = na.rm, 50 | ... 51 | ) 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /R/bspline_closed.R: -------------------------------------------------------------------------------- 1 | #' Create closed b-spline shapes 2 | #' 3 | #' This geom creates closed b-spline curves and draws them as shapes. The 4 | #' closed b-spline is achieved by wrapping the control points rather than the 5 | #' knots. The *0 version uses the [grid::xsplineGrob()] function with 6 | #' `open = FALSE` and can thus not be manipulated as a shape geom in the same 7 | #' way as the base version (expand, contract, etc). 8 | #' 9 | #' @section Aesthetics: 10 | #' geom_bspline_closed understand the following aesthetics (required aesthetics 11 | #' are in bold): 12 | #' 13 | #' - **x** 14 | #' - **y** 15 | #' - color 16 | #' - fill 17 | #' - linewidth 18 | #' - linetype 19 | #' - alpha 20 | #' 21 | #' @section Computed variables: 22 | #' 23 | #' \describe{ 24 | #' \item{x, y}{The coordinates for the path describing the spline} 25 | #' \item{index}{The progression along the interpolation mapped between 0 and 1} 26 | #' } 27 | #' 28 | #' @inheritParams ggplot2::geom_polygon 29 | #' @inheritParams ggplot2::stat_identity 30 | #' 31 | #' @param n The number of points generated for each spline 32 | #' 33 | #' @author Thomas Lin Pedersen. The C++ code for De Boor's algorithm has been 34 | #' adapted from 35 | #' \href{https://chi3x10.wordpress.com/2009/10/18/de-boor-algorithm-in-c/}{Jason Yu-Tseh Chi implementation} 36 | #' 37 | #' @name geom_bspline_closed 38 | #' @rdname geom_bspline_closed 39 | #' 40 | #' @examples 41 | #' # Create 6 random control points 42 | #' controls <- data.frame( 43 | #' x = runif(6), 44 | #' y = runif(6) 45 | #' ) 46 | #' 47 | #' ggplot(controls, aes(x, y)) + 48 | #' geom_polygon(fill = NA, colour = 'grey') + 49 | #' geom_point(colour = 'red') + 50 | #' geom_bspline_closed(alpha = 0.5) 51 | #' 52 | #' # The 0 version approximates the correct shape 53 | #' ggplot(controls, aes(x, y)) + 54 | #' geom_polygon(fill = NA, colour = 'grey') + 55 | #' geom_point(colour = 'red') + 56 | #' geom_bspline_closed0(alpha = 0.5) 57 | #' 58 | #' # But only the standard version supports geom_shape operations 59 | #' # Be aware of self-intersections though 60 | #' ggplot(controls, aes(x, y)) + 61 | #' geom_polygon(fill = NA, colour = 'grey') + 62 | #' geom_point(colour = 'red') + 63 | #' geom_bspline_closed(alpha = 0.5, expand = unit(2, 'cm')) 64 | NULL 65 | 66 | #' @rdname geom_bspline_closed 67 | #' @export 68 | stat_bspline_closed <- function(mapping = NULL, data = NULL, geom = 'shape', 69 | position = 'identity', na.rm = FALSE, n = 100, 70 | show.legend = NA, inherit.aes = TRUE, ...) { 71 | layer( 72 | stat = StatBspline, data = data, mapping = mapping, geom = geom, 73 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 74 | params = list2(na.rm = na.rm, n = n, ...) 75 | ) 76 | } 77 | #' @rdname geom_bspline_closed 78 | #' @export 79 | geom_bspline_closed <- function(mapping = NULL, data = NULL, stat = 'bspline', 80 | position = 'identity', n = 100, na.rm = FALSE, 81 | show.legend = NA, inherit.aes = TRUE, ...) { 82 | layer( 83 | data = data, mapping = mapping, stat = stat, geom = GeomShape, 84 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 85 | params = list2(na.rm = na.rm, n = n, type = 'closed', ...) 86 | ) 87 | } 88 | #' @rdname ggforce-extensions 89 | #' @format NULL 90 | #' @usage NULL 91 | #' @importFrom grid xsplineGrob gpar 92 | #' @export 93 | GeomBsplineClosed0 <- ggproto('GeomBspline0', GeomPolygon, 94 | draw_panel = function(data, panel_scales, coord, na.rm = FALSE) { 95 | coords <- coord$transform(data, panel_scales) 96 | if (!is.integer(coords$group)) { 97 | coords$group <- match(coords$group, unique0(coords$group)) 98 | } 99 | startPoint <- match(unique0(coords$group), coords$group) 100 | xsplineGrob(coords$x, coords$y, 101 | id = coords$group, default.units = 'native', 102 | shape = 1, open = FALSE, 103 | gp = gpar( 104 | col = coords$colour[startPoint], 105 | fill = ggplot2::fill_alpha(coords$fill[startPoint], coords$alpha[startPoint]), 106 | lwd = (coords$linewidth[startPoint] %||% coords$size[startPoint]) * .pt, 107 | lty = coords$linetype[startPoint] 108 | ) 109 | ) 110 | } 111 | ) 112 | 113 | #' @rdname geom_bspline_closed 114 | #' @export 115 | geom_bspline_closed0 <- function(mapping = NULL, data = NULL, stat = 'identity', 116 | position = 'identity', na.rm = FALSE, 117 | show.legend = NA, inherit.aes = TRUE, ...) { 118 | layer( 119 | data = data, mapping = mapping, stat = stat, geom = GeomBsplineClosed0, 120 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 121 | params = list2(na.rm = na.rm, ...) 122 | ) 123 | } 124 | -------------------------------------------------------------------------------- /R/circle.R: -------------------------------------------------------------------------------- 1 | #' @include arc_bar.R 2 | #' @include shape.R 3 | NULL 4 | 5 | #' Circles based on center and radius 6 | #' 7 | #' This set of stats and geoms makes it possible to draw circles based on a 8 | #' center point and a radius. In contrast to using 9 | #' [ggplot2::geom_point()], the size of the circles are related to the 10 | #' coordinate system and not to a separate scale. These functions are intended 11 | #' for cartesian coordinate systems and will only produce a true circle if 12 | #' [ggplot2::coord_fixed()] is used. 13 | #' 14 | #' @note If the intend is to draw a bubble chart then use 15 | #' [ggplot2::geom_point()] and map a variable to the size scale 16 | #' 17 | #' @section Aesthetics: 18 | #' geom_circle understand the following aesthetics (required aesthetics are in 19 | #' bold): 20 | #' 21 | #' - **x0** 22 | #' - **y0** 23 | #' - **r** 24 | #' - color 25 | #' - fill 26 | #' - linewidth 27 | #' - linetype 28 | #' - alpha 29 | #' - lineend 30 | #' 31 | #' @section Computed variables: 32 | #' 33 | #' \describe{ 34 | #' \item{x, y}{The start coordinates for the segment} 35 | #' } 36 | #' 37 | #' @inheritParams ggplot2::geom_path 38 | #' @inheritParams ggplot2::stat_identity 39 | #' 40 | #' @param n The number of points on the generated path per full circle. 41 | #' 42 | #' @name geom_circle 43 | #' @rdname geom_circle 44 | #' @seealso [geom_arc_bar()] for drawing arcs with fill 45 | #' 46 | #' @examples 47 | #' # Lets make some data 48 | #' circles <- data.frame( 49 | #' x0 = rep(1:3, 3), 50 | #' y0 = rep(1:3, each = 3), 51 | #' r = seq(0.1, 1, length.out = 9) 52 | #' ) 53 | #' 54 | #' # Behold some circles 55 | #' ggplot() + 56 | #' geom_circle(aes(x0 = x0, y0 = y0, r = r, fill = r), data = circles) 57 | #' 58 | #' # Use coord_fixed to ensure true circularity 59 | #' ggplot() + 60 | #' geom_circle(aes(x0 = x0, y0 = y0, r = r, fill = r), data = circles) + 61 | #' coord_fixed() 62 | #' 63 | NULL 64 | 65 | #' @rdname ggforce-extensions 66 | #' @format NULL 67 | #' @usage NULL 68 | #' @importFrom grid arcCurvature 69 | #' @export 70 | StatCircle <- ggproto('StatCircle', Stat, 71 | compute_panel = function(data, scales, n = 360) { 72 | # Avoid some weird interaction if x and y are mapped at the global level 73 | data$x <- NULL 74 | data$y <- NULL 75 | data$start <- 0 76 | data$end <- 2 * pi 77 | arcPaths(data, n + 1) 78 | }, 79 | 80 | required_aes = c('x0', 'y0', 'r') 81 | ) 82 | #' @rdname geom_circle 83 | #' @export 84 | stat_circle <- function(mapping = NULL, data = NULL, geom = 'circle', 85 | position = 'identity', n = 360, na.rm = FALSE, 86 | show.legend = NA, inherit.aes = TRUE, ...) { 87 | layer( 88 | stat = StatCircle, data = data, mapping = mapping, geom = geom, 89 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 90 | params = list2(na.rm = na.rm, n = n, ...) 91 | ) 92 | } 93 | #' @rdname ggforce-extensions 94 | #' @format NULL 95 | #' @usage NULL 96 | #' @export 97 | GeomCircle <- ggproto('GeomCircle', GeomShape, 98 | default_aes = combine_aes(GeomShape$default_aes, aes(colour = 'black', fill = NA)) 99 | ) 100 | #' @rdname geom_circle 101 | #' @inheritParams geom_shape 102 | #' @export 103 | geom_circle <- function(mapping = NULL, data = NULL, stat = 'circle', 104 | position = 'identity', n = 360, expand = 0, radius = 0, 105 | na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, 106 | ...) { 107 | layer( 108 | data = data, mapping = mapping, stat = stat, geom = GeomCircle, 109 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 110 | params = list2(n = n, na.rm = na.rm, ...) 111 | ) 112 | } 113 | -------------------------------------------------------------------------------- /R/concaveman.R: -------------------------------------------------------------------------------- 1 | concaveman <- function(points, concavity, threshold) { 2 | if (nrow(points) < 4) return(unname(points)) 3 | hull <- as.integer(grDevices::chull(points)) - 1L 4 | concaveman_c(points, hull, concavity, threshold) 5 | } 6 | -------------------------------------------------------------------------------- /R/cpp11.R: -------------------------------------------------------------------------------- 1 | # Generated by cpp11: do not edit by hand 2 | 3 | splinePath <- function(x, y, degree, knots, detail, type) { 4 | .Call(`_ggforce_splinePath`, x, y, degree, knots, detail, type) 5 | } 6 | 7 | getSplines <- function(x, y, id, detail, type) { 8 | .Call(`_ggforce_getSplines`, x, y, id, detail, type) 9 | } 10 | 11 | bezierPath <- function(x, y, detail) { 12 | .Call(`_ggforce_bezierPath`, x, y, detail) 13 | } 14 | 15 | getBeziers <- function(x, y, id, detail) { 16 | .Call(`_ggforce_getBeziers`, x, y, id, detail) 17 | } 18 | 19 | concaveman_c <- function(p, h, concavity, threshold) { 20 | .Call(`_ggforce_concaveman_c`, p, h, concavity, threshold) 21 | } 22 | 23 | enclose_ellip_points <- function(x, y, id, tol) { 24 | .Call(`_ggforce_enclose_ellip_points`, x, y, id, tol) 25 | } 26 | 27 | enclose_points <- function(x, y, id) { 28 | .Call(`_ggforce_enclose_points`, x, y, id) 29 | } 30 | 31 | points_to_path <- function(pos, path, close) { 32 | .Call(`_ggforce_points_to_path`, pos, path, close) 33 | } 34 | -------------------------------------------------------------------------------- /R/diagonal_wide.R: -------------------------------------------------------------------------------- 1 | #' Draw an area defined by an upper and lower diagonal 2 | #' 3 | #' The `geom_diagonal_wide()` function draws a *thick* diagonal, that is, a 4 | #' polygon confined between a lower and upper [diagonal][geom_diagonal]. This 5 | #' geom is bidirectional and the direction can be controlled with the 6 | #' `orientation` argument. 7 | #' 8 | #' @section Aesthetics: 9 | #' geom_diagonal_wide understand the following aesthetics 10 | #' (required aesthetics are in bold): 11 | #' 12 | #' - **x** 13 | #' - **y** 14 | #' - **group** 15 | #' - color 16 | #' - linewidth 17 | #' - linetype 18 | #' - alpha 19 | #' - lineend 20 | #' 21 | #' @inheritParams geom_shape 22 | #' @inheritParams ggplot2::stat_identity 23 | #' @inheritParams ggplot2::geom_line 24 | #' 25 | #' @param n The number of points to create for each of the bounding diagonals 26 | #' 27 | #' @param strength The proportion to move the control point along the x-axis 28 | #' towards the other end of the bezier curve 29 | #' 30 | #' @inheritSection ggplot2::geom_line Orientation 31 | #' 32 | #' @name geom_diagonal_wide 33 | #' @rdname geom_diagonal_wide 34 | #' 35 | #' @examples 36 | #' data <- data.frame( 37 | #' x = c(1, 2, 2, 1, 2, 3, 3, 2), 38 | #' y = c(1, 2, 3, 2, 3, 1, 2, 5), 39 | #' group = c(1, 1, 1, 1, 2, 2, 2, 2) 40 | #' ) 41 | #' 42 | #' ggplot(data) + 43 | #' geom_diagonal_wide(aes(x, y, group = group)) 44 | #' 45 | #' # The strength control the steepness 46 | #' ggplot(data, aes(x, y, group = group)) + 47 | #' geom_diagonal_wide(strength = 0.75, alpha = 0.5, fill = 'red') + 48 | #' geom_diagonal_wide(strength = 0.25, alpha = 0.5, fill = 'blue') 49 | #' 50 | #' # The diagonal_wide geom uses geom_shape under the hood, so corner rounding 51 | #' # etc are all there 52 | #' ggplot(data) + 53 | #' geom_diagonal_wide(aes(x, y, group = group), radius = unit(5, 'mm')) 54 | #' 55 | NULL 56 | 57 | #' @rdname ggforce-extensions 58 | #' @format NULL 59 | #' @usage NULL 60 | #' @export 61 | StatDiagonalWide <- ggproto('StatDiagonalWide', Stat, 62 | setup_params = function(data, params) { 63 | params$flipped_aes <- has_flipped_aes(data, params, ambiguous = TRUE) 64 | params 65 | }, 66 | setup_data = function(data, params) { 67 | data$flipped_aes <- params$flipped_aes 68 | if (any(table(data$group) != 4)) { 69 | cli::cli_abort('Each group must consist of 4 points') 70 | } 71 | data 72 | }, 73 | compute_panel = function(data, scales, strength = 0.5, n = 100, flipped_aes = FALSE) { 74 | data <- flip_data(data, flipped_aes) 75 | data <- data[order(data$group, data$x, data$y), ] 76 | lower <- data[rep_len(c(TRUE, FALSE, TRUE, FALSE), nrow(data)), ] 77 | upper <- data[rep_len(c(FALSE, TRUE, FALSE, TRUE), nrow(data)), ] 78 | lower <- add_controls(lower, strength) 79 | upper <- add_controls(upper[rev(seq_len(nrow(upper))), ], strength) 80 | lower <- StatBezier$compute_panel(lower, scales, n) 81 | upper <- StatBezier$compute_panel(upper, scales, n) 82 | diagonals <- vec_rbind(lower, upper) 83 | diagonals$index <- NULL 84 | diagonals <- diagonals[order(diagonals$group), ] 85 | flip_data(diagonals, flipped_aes) 86 | }, 87 | required_aes = c('x', 'y', 'group'), 88 | extra_params = c('na.rm', 'n', 'strength', 'orientation') 89 | ) 90 | #' @rdname geom_diagonal_wide 91 | #' @export 92 | stat_diagonal_wide <- function(mapping = NULL, data = NULL, geom = 'shape', 93 | position = 'identity', n = 100, strength = 0.5, 94 | na.rm = FALSE, orientation = NA, show.legend = NA, 95 | inherit.aes = TRUE, ...) { 96 | layer( 97 | stat = StatDiagonalWide, data = data, mapping = mapping, geom = geom, 98 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 99 | params = list2(na.rm = na.rm, orientation = orientation, n = n, 100 | strength = strength, ...) 101 | ) 102 | } 103 | #' @rdname geom_diagonal_wide 104 | #' @export 105 | geom_diagonal_wide <- function(mapping = NULL, data = NULL, stat = 'diagonal_wide', 106 | position = 'identity', n = 100, na.rm = FALSE, 107 | orientation = NA, strength = 0.5, 108 | show.legend = NA, inherit.aes = TRUE, ...) { 109 | layer( 110 | data = data, mapping = mapping, stat = stat, geom = GeomShape, 111 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 112 | params = list2(na.rm = na.rm, orientation = orientation, n = n, 113 | strength = strength, ...) 114 | ) 115 | } 116 | -------------------------------------------------------------------------------- /R/ellipse.R: -------------------------------------------------------------------------------- 1 | #' Draw (super)ellipses based on the coordinate system scale 2 | #' 3 | #' This is a generalisation of [geom_circle()] that allows you to draw 4 | #' ellipses at a specified angle and center relative to the coordinate system. 5 | #' Apart from letting you draw regular ellipsis, the stat is using the 6 | #' generalised formula for superellipses which can be utilised by setting the 7 | #' `m1` and `m2` aesthetics. If you only set the m1 the m2 value will follow 8 | #' that to ensure a symmetric appearance. 9 | #' 10 | #' @section Aesthetics: 11 | #' geom_arc understand the following aesthetics (required aesthetics are in 12 | #' bold): 13 | #' 14 | #' - **x0** 15 | #' - **y0** 16 | #' - **a** 17 | #' - **b** 18 | #' - **angle** 19 | #' - m1 20 | #' - m2 21 | #' - color 22 | #' - fill 23 | #' - linewidth 24 | #' - linetype 25 | #' - alpha 26 | #' - lineend 27 | #' 28 | #' @section Computed variables: 29 | #' 30 | #' \describe{ 31 | #' \item{x, y}{The coordinates for the points along the ellipse} 32 | #' } 33 | #' 34 | #' @inheritParams ggplot2::geom_path 35 | #' @inheritParams ggplot2::stat_identity 36 | #' 37 | #' @param n The number of points to sample along the ellipse. 38 | #' 39 | #' @name geom_ellipse 40 | #' @rdname geom_ellipse 41 | #' 42 | #' @examples 43 | #' # Basic usage 44 | #' ggplot() + 45 | #' geom_ellipse(aes(x0 = 0, y0 = 0, a = 10, b = 3, angle = 0)) + 46 | #' coord_fixed() 47 | #' 48 | #' # Rotation 49 | #' # Note that it expects radians and rotates the ellipse counter-clockwise 50 | #' ggplot() + 51 | #' geom_ellipse(aes(x0 = 0, y0 = 0, a = 10, b = 3, angle = pi / 4)) + 52 | #' coord_fixed() 53 | #' 54 | #' # Draw a super ellipse 55 | #' ggplot() + 56 | #' geom_ellipse(aes(x0 = 0, y0 = 0, a = 6, b = 3, angle = -pi / 3, m1 = 3)) + 57 | #' coord_fixed() 58 | NULL 59 | 60 | #' @rdname ggforce-extensions 61 | #' @format NULL 62 | #' @usage NULL 63 | #' @export 64 | StatEllip <- ggproto('StatEllip', Stat, 65 | setup_data = function(data, params) { 66 | data$m1 <- if (is.null(data$m1)) 2 else data$m1 67 | data$m2 <- if (is.null(data$m2)) data$m1 else data$m2 68 | data 69 | }, 70 | compute_panel = function(self, data, scales, n = 360) { 71 | if (empty_data(data)) return(data) 72 | data$group <- make_unique(data$group) 73 | n_ellipses <- nrow(data) 74 | data <- data[rep(seq_len(n_ellipses), each = n), ] 75 | points <- rep(seq(0, 2 * pi, length.out = n + 1)[seq_len(n)], 76 | n_ellipses) 77 | cos_p <- cos(points) 78 | sin_p <- sin(points) 79 | x_tmp <- abs(cos_p)^(2 / data$m1) * data$a * sign(cos_p) 80 | y_tmp <- abs(sin_p)^(2 / data$m2) * data$b * sign(sin_p) 81 | data$x <- data$x0 + x_tmp * cos(data$angle) - y_tmp * sin(data$angle) 82 | data$y <- data$y0 + x_tmp * sin(data$angle) + y_tmp * cos(data$angle) 83 | data 84 | }, 85 | required_aes = c('x0', 'y0', 'a', 'b', 'angle'), 86 | default_aes = aes(m1 = NA, m2 = NA), 87 | extra_params = c('n', 'na.rm') 88 | ) 89 | #' @rdname geom_ellipse 90 | #' @export 91 | stat_ellip <- function(mapping = NULL, data = NULL, geom = 'circle', 92 | position = 'identity', n = 360, na.rm = FALSE, 93 | show.legend = NA, inherit.aes = TRUE, ...) { 94 | layer( 95 | stat = StatEllip, data = data, mapping = mapping, geom = geom, 96 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 97 | params = list2(na.rm = na.rm, n = n, ...) 98 | ) 99 | } 100 | 101 | #' @rdname geom_ellipse 102 | #' @export 103 | geom_ellipse <- function(mapping = NULL, data = NULL, stat = 'ellip', 104 | position = 'identity', n = 360, na.rm = FALSE, 105 | show.legend = NA, inherit.aes = TRUE, ...) { 106 | layer( 107 | data = data, mapping = mapping, stat = stat, geom = GeomCircle, 108 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 109 | params = list2(n = n, na.rm = na.rm, ...) 110 | ) 111 | } 112 | -------------------------------------------------------------------------------- /R/errorbar.R: -------------------------------------------------------------------------------- 1 | #' @rdname ggforce-extensions 2 | #' @format NULL 3 | #' @usage NULL 4 | #' @export 5 | StatErr <- ggproto( 6 | "StatErr", 7 | Stat, 8 | required_aes = c('xmin', 'x', 'xmax', 'ymin', 'y', 'ymax'), 9 | compute_group = function(data, scales) { 10 | data_frame0( 11 | x = c(data$xmin, data$x), 12 | xend = c(data$xmax, data$x), 13 | y = c(data$y, data$ymin), 14 | yend = c(data$y, data$ymax) 15 | )[c(matrix(seq_len(2 * nrow(data)), nrow = 2, byrow = TRUE)), ] 16 | } 17 | ) 18 | 19 | #' Intervals in vertical and horizontal directions 20 | #' 21 | #' `stat_err` draws intervals of points (`x`, `y`) in vertical (`ymin`, `ymax`) 22 | #' and horizontal (`xmin`, `xmax`) directions. 23 | #' 24 | #' @section Aesthetics: 25 | #' `stat_err()` understands the following aesthetics (required aesthetics are in 26 | #' bold): 27 | #' 28 | #' - **x** 29 | #' - **xmin** 30 | #' - **xmax** 31 | #' - **y** 32 | #' - **ymin** 33 | #' - **ymax** 34 | #' - alpha 35 | #' - color 36 | #' - group 37 | #' - linetype 38 | #' - linewidth 39 | #' 40 | #' @examples 41 | #' library(ggplot2) 42 | #' 43 | #' x <- 1:3 44 | #' xmin <- x - 2.5 45 | #' xmax <- x + 2.5 46 | #' d <- data.frame( 47 | #' x = x, y = x, xmin = xmin, ymin = xmin, xmax = xmax, ymax = xmax, 48 | #' color = as.factor(x) 49 | #' ) 50 | #' ggplot( 51 | #' d, 52 | #' aes(x = x, y = y, xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, color = color) 53 | #' ) + 54 | #' stat_err(size = 2) 55 | #' 56 | #' @inheritParams ggplot2::layer 57 | #' @inheritParams ggplot2::geom_errorbar 58 | #' @inheritParams ggplot2::stat_identity 59 | #' 60 | #' @importFrom ggplot2 layer 61 | #' 62 | #' @export 63 | stat_err <- function( 64 | mapping = NULL, data = NULL, geom = "segment", position = "identity", 65 | na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, ... 66 | ) { 67 | layer( 68 | stat = StatErr, data = data, mapping = mapping, geom = geom, 69 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 70 | params = list2(na.rm = na.rm, ...) 71 | ) 72 | } 73 | -------------------------------------------------------------------------------- /R/facet_grid_paginate.R: -------------------------------------------------------------------------------- 1 | #' Split facet_grid over multiple plots 2 | #' 3 | #' This extension to [ggplot2::facet_grid()] will allow you to split 4 | #' a facetted plot over multiple pages. You define a number of rows and columns 5 | #' per page as well as the page number to plot, and the function will 6 | #' automatically only plot the correct panels. Usually this will be put in a 7 | #' loop to render all pages one by one. 8 | #' 9 | #' @inheritParams ggplot2::facet_grid 10 | #' @param ncol Number of columns per page 11 | #' @param nrow Number of rows per page 12 | #' @param page The page to draw 13 | #' @param byrow Should the pages be created row-wise or column wise 14 | #' 15 | #' @note If either `ncol` or `nrow` is `NULL` this function will 16 | #' fall back to the standard `facet_grid` functionality. 17 | #' 18 | #' @family ggforce facets 19 | #' @seealso [n_pages()] to compute the total number of pages in a paginated 20 | #' faceted plot 21 | #' 22 | #' @export 23 | #' 24 | #' @examples 25 | #' # Draw a small section of the grid 26 | #' ggplot(diamonds) + 27 | #' geom_point(aes(carat, price), alpha = 0.1) + 28 | #' facet_grid_paginate(color ~ cut:clarity, ncol = 3, nrow = 3, page = 4) 29 | facet_grid_paginate <- function(facets, margins = FALSE, scales = 'fixed', 30 | space = 'fixed', shrink = TRUE, 31 | labeller = 'label_value', as.table = TRUE, 32 | switch = NULL, drop = TRUE, ncol = NULL, 33 | nrow = NULL, page = 1, byrow = TRUE) { 34 | facet <- facet_grid(facets, 35 | margins = margins, scales = scales, 36 | space = space, shrink = shrink, labeller = labeller, 37 | as.table = as.table, switch = switch, drop = drop 38 | ) 39 | if (is.null(nrow) || is.null(ncol)) { 40 | facet 41 | } else { 42 | ggproto(NULL, FacetGridPaginate, 43 | shrink = shrink, 44 | params = c( 45 | facet$params, 46 | list(ncol = ncol, nrow = nrow, page = page, byrow = byrow) 47 | ) 48 | ) 49 | } 50 | } 51 | 52 | #' @rdname ggforce-extensions 53 | #' @format NULL 54 | #' @usage NULL 55 | #' @importFrom gtable gtable_add_rows gtable_add_cols 56 | #' @export 57 | FacetGridPaginate <- ggproto('FacetGridPaginate', FacetGrid, 58 | compute_layout = function(data, params) { 59 | layout <- FacetGrid$compute_layout(data, params) 60 | row_bin <- ceiling(layout$ROW / params$nrow) 61 | col_bin <- ceiling(layout$COL / params$ncol) 62 | bin_layout <- matrix(seq_len(max(row_bin) * max(col_bin)), 63 | nrow = max(row_bin), byrow = params$byrow 64 | ) 65 | layout$page <- bin_layout[(col_bin - 1) * nrow(bin_layout) + row_bin] 66 | layout 67 | }, 68 | draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord, data, 69 | theme, params) { 70 | include <- which(layout$page == params$page) 71 | panels <- panels[include] 72 | ranges <- ranges[include] 73 | layout <- layout[include, , drop = FALSE] 74 | layout$ROW <- layout$ROW - min(layout$ROW) + 1 75 | layout$COL <- layout$COL - min(layout$COL) + 1 76 | layout$PANEL <- 1:dim(layout)[1] 77 | x_scale_ind <- unique0(layout$SCALE_X) 78 | x_scales <- x_scales[x_scale_ind] 79 | layout$SCALE_X <- match(layout$SCALE_X, x_scale_ind) 80 | y_scale_ind <- unique0(layout$SCALE_Y) 81 | y_scales <- y_scales[y_scale_ind] 82 | layout$SCALE_Y <- match(layout$SCALE_Y, y_scale_ind) 83 | table <- FacetGrid$draw_panels(panels, layout, x_scales, y_scales, ranges, 84 | coord, data, theme, params) 85 | if (max(layout$ROW) != params$nrow) { 86 | spacing <- theme$panel.spacing.y %||% theme$panel.spacing 87 | missing_rows <- params$nrow - max(layout$ROW) 88 | table <- gtable_add_rows(table, unit(missing_rows, 'null')) 89 | table <- gtable_add_rows(table, spacing * missing_rows) 90 | } 91 | if (max(layout$COL) != params$ncol) { 92 | spacing <- theme$panel.spacing.x %||% theme$panel.spacing 93 | missing_cols <- params$ncol - max(layout$COL) 94 | table <- gtable_add_cols(table, unit(missing_cols, 'null')) 95 | table <- gtable_add_cols(table, spacing * missing_cols) 96 | } 97 | table 98 | } 99 | ) 100 | -------------------------------------------------------------------------------- /R/facet_row.R: -------------------------------------------------------------------------------- 1 | #' One-dimensional facets 2 | #' 3 | #' These facets are one-dimensional versions of [ggplot2::facet_wrap()], 4 | #' arranging the panels in either a single row or a single column. This 5 | #' restriction makes it possible to support a `space` argument as seen in 6 | #' [ggplot2::facet_grid()] which, if set to `"free"` will allow the panels to be 7 | #' sized based on the relative range of their scales. Another way of thinking 8 | #' about them are one-dimensional versions of [ggplot2::facet_grid()] (ie. 9 | #' `. ~ {var}` or `{var} ~ .`), but with the ability to position the strip at 10 | #' either side of the panel. However you look at it it is the best of both world 11 | #' if you just need one dimension. 12 | #' 13 | #' @inheritParams ggplot2::facet_wrap 14 | #' @param space Should the size of the panels be fixed or relative to the range 15 | #' of the respective position scales 16 | #' 17 | #' @export 18 | #' 19 | #' @examples 20 | #' # Standard use 21 | #' ggplot(mtcars) + 22 | #' geom_point(aes(disp, mpg)) + 23 | #' facet_col(~gear) 24 | #' # It retains the ability to have unique scales for each panel 25 | #' ggplot(mtcars) + 26 | #' geom_point(aes(disp, mpg)) + 27 | #' facet_col(~gear, scales = 'free') 28 | #' 29 | #' # But can have free sizing along the stacking dimension 30 | #' ggplot(mtcars) + 31 | #' geom_point(aes(disp, mpg)) + 32 | #' facet_col(~gear, scales = 'free', space = 'free') 33 | #' 34 | #' # And you can position the strip where-ever you like 35 | #' ggplot(mtcars) + 36 | #' geom_point(aes(disp, mpg)) + 37 | #' facet_col(~gear, scales = 'free', space = 'free', strip.position = 'bottom') 38 | #' 39 | facet_row <- function(facets, scales = "fixed", space = "fixed", 40 | shrink = TRUE, labeller = "label_value", 41 | drop = TRUE, strip.position = 'top') { 42 | space <- match.arg(space, c('free', 'fixed')) 43 | facet <- facet_wrap(facets, nrow = 1, scales = scales, shrink = shrink, labeller = labeller, drop = drop, strip.position = strip.position) 44 | params <- facet$params 45 | 46 | if ("space" %in% fn_fmls_names(facet_wrap)) { 47 | params$space_free <- list(x = space == 'free', y = FALSE) 48 | } else { 49 | params$space_free <- space == 'free' 50 | } 51 | 52 | ggproto(NULL, FacetRow, shrink = shrink, params = params) 53 | } 54 | #' @rdname ggforce-extensions 55 | #' @format NULL 56 | #' @usage NULL 57 | #' @export 58 | FacetRow <- ggproto('FacetRow', FacetWrap, 59 | draw_panels = function(self, panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) { 60 | combined <- ggproto_parent(FacetWrap, self)$draw_panels(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) 61 | if (isTRUE(params$space_free)) { 62 | widths <- vapply(layout$PANEL, function(i) diff(ranges[[i]]$x.range), numeric(1)) 63 | panel_widths <- unit(widths, "null") 64 | combined$widths[panel_cols(combined)$l] <- panel_widths 65 | } 66 | combined 67 | } 68 | ) 69 | 70 | #' @rdname facet_row 71 | #' @export 72 | facet_col <- function(facets, scales = "fixed", space = "fixed", 73 | shrink = TRUE, labeller = "label_value", 74 | drop = TRUE, strip.position = 'top') { 75 | space <- match.arg(space, c('free', 'fixed')) 76 | facet <- facet_wrap(facets, ncol = 1, scales = scales, shrink = shrink, labeller = labeller, drop = drop, strip.position = strip.position) 77 | params <- facet$params 78 | 79 | if ("space" %in% fn_fmls_names(facet_wrap)) { 80 | params$space_free <- list(x = FALSE, y = space == 'free') 81 | } else { 82 | params$space_free <- space == 'free' 83 | } 84 | ggproto(NULL, FacetCol, shrink = shrink, params = params) 85 | } 86 | #' @rdname ggforce-extensions 87 | #' @format NULL 88 | #' @usage NULL 89 | #' @export 90 | FacetCol <- ggproto('FacetCol', FacetWrap, 91 | draw_panels = function(self, panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) { 92 | combined <- ggproto_parent(FacetWrap, self)$draw_panels(panels, layout, x_scales, y_scales, ranges, coord, data, theme, params) 93 | if (isTRUE(params$space_free)) { 94 | heights <- vapply(layout$PANEL, function(i) diff(ranges[[i]]$y.range), numeric(1)) 95 | panel_heights <- unit(heights, "null") 96 | combined$heights[panel_rows(combined)$t] <- panel_heights 97 | } 98 | combined 99 | } 100 | ) 101 | -------------------------------------------------------------------------------- /R/facet_stereo.R: -------------------------------------------------------------------------------- 1 | #' Create a stereogram plot 2 | #' 3 | #' This, arguably pretty useless function, lets you create plots with a sense of 4 | #' depth by creating two slightly different versions of the plot that 5 | #' corresponds to how the eyes would see it if the plot was 3 dimensional. To 6 | #' experience the effect look at the plots through 3D hardware such as Google 7 | #' Cardboard or by relaxing the eyes and focusing into the distance. The 8 | #' depth of a point is calculated for layers having a depth aesthetic supplied. 9 | #' The scaling of the depth can be controlled with [scale_depth()] as 10 | #' you would control any aesthetic. Negative values will result in features 11 | #' placed behind the paper plane, while positive values will result in 12 | #' features hovering in front of the paper. While features within each layer is 13 | #' sorted so those closest to you are plotted on top of those more distant, this 14 | #' cannot be done between layers. Thus, layers are always plotted on top of 15 | #' each others, even if the features in one layer lies behind features in a 16 | #' layer behind it. The depth experience is inaccurate and should not be used 17 | #' for conveying important data. Regard this more as a party-trick... 18 | #' 19 | #' @param IPD The interpupillary distance (in mm) used for calculating point 20 | #' displacement. The default value is an average of both genders 21 | #' 22 | #' @param panel.size The final plot size in mm. As IPD this is used to calculate 23 | #' point displacement. Don't take this value too literal but experiment until 24 | #' you get a nice effect. Lower values gives higher displacement and thus 25 | #' require the plots to be observed from a closer distance 26 | #' 27 | #' @inheritParams ggplot2::facet_wrap 28 | #' 29 | #' @family ggforce facets 30 | #' 31 | #' @export 32 | #' 33 | #' @examples 34 | #' # You'll have to accept a warning about depth being an unknown aesthetic 35 | #' ggplot(mtcars) + 36 | #' geom_point(aes(mpg, disp, depth = cyl)) + 37 | #' facet_stereo() 38 | facet_stereo <- function(IPD = 63.5, panel.size = 200, shrink = TRUE) { 39 | ggproto(NULL, FacetStereo, 40 | shrink = shrink, 41 | params = list( 42 | IPD = IPD, panel.size = panel.size 43 | ) 44 | ) 45 | } 46 | 47 | #' @rdname ggforce-extensions 48 | #' @format NULL 49 | #' @usage NULL 50 | #' @importFrom scales rescale 51 | #' @importFrom gtable gtable_add_cols 52 | #' @export 53 | FacetStereo <- ggproto('FacetStereo', Facet, 54 | compute_layout = function(data, params) { 55 | data_frame0(PANEL = c(1L, 2L), SCALE_X = 1L, SCALE_Y = 1L) 56 | }, 57 | map_data = function(data, layout, params) { 58 | if (empty(data)) { 59 | return(cbind(data, PANEL = integer(0))) 60 | } 61 | vec_rbind( 62 | cbind(data, PANEL = 1L), 63 | cbind(data, PANEL = 2L) 64 | ) 65 | }, 66 | finish_data = function(data, layout, x_scales, y_scales, params) { 67 | if ('depth' %in% names(data)) { 68 | if ('.interp' %in% names(data)) { 69 | data$depth2 <- vec_rbind(!!!lapply(split(data, data$PANEL), interpolateDataFrame))$depth 70 | } else { 71 | data$depth2 <- data$depth 72 | } 73 | group_order <- order( 74 | sapply(split(data$depth2, data$group), quantile, probs = 0.9, 75 | na.rm = TRUE) 76 | ) 77 | data <- vec_rbind(!!!split(data, data$group)[group_order]) 78 | data[data$group == -1, ] <- data[data$group == -1, ][order(data$depth2[data$group == -1]), ] 79 | data$group[data$group != -1] <- match(data$group[data$group != -1], 80 | unique0(data$group[data$group != -1])) 81 | x_range <- x_scales[[1]]$dimension(expand_default(x_scales[[1]])) 82 | k <- ifelse(data$PANEL == 1, -1, 1) * params$IPD / 2 83 | x_transform <- function(d) { 84 | h <- rescale(d, to = c(-1, 1) * params$panel.size / 2, from = x_range) 85 | new_pos <- h + (h - k) * data$depth2 86 | rescale(new_pos, to = x_range, from = c(-1, 1) * params$panel.size / 2) 87 | } 88 | data <- transform_position(data, x_transform) 89 | data$depth2 <- NULL 90 | } 91 | data 92 | }, 93 | draw_panels = function(panels, layout, x_scales, y_scales, ranges, coord, 94 | data, theme, params) { 95 | axes <- render_axes(ranges, ranges, coord, theme, FALSE) 96 | panelGrobs <- create_panels(panels, axes$x, axes$y) 97 | spacing <- theme$panel.spacing.x %||% theme$panel.spacing 98 | panel <- gtable_add_cols(panelGrobs[[1]], spacing) 99 | cbind(panel, panelGrobs[[2]], size = 'first') 100 | }, 101 | draw_labels = function(panels, layout, x_scales, y_scales, ranges, coord, data, 102 | theme, labels, params) { 103 | panel_dim <- find_panel(panels) 104 | 105 | xlab_height_top <- grobHeight(labels$x[[1]]) 106 | panels <- gtable_add_rows(panels, xlab_height_top, pos = 0) 107 | panels <- gtable_add_grob(panels, labels$x[[1]], 108 | name = 'xlab-t', 109 | l = panel_dim$l, r = panel_dim$r, t = 1, clip = 'off' 110 | ) 111 | 112 | xlab_height_bottom <- grobHeight(labels$x[[2]]) 113 | panels <- gtable_add_rows(panels, xlab_height_bottom, pos = -1) 114 | panels <- gtable_add_grob(panels, labels$x[[2]], 115 | name = 'xlab-b', 116 | l = panel_dim$l, r = panel_dim$r, t = -1, clip = 'off' 117 | ) 118 | 119 | panel_dim <- find_panel(panels) 120 | 121 | ylab_width_left <- grobWidth(labels$y[[1]]) 122 | panels <- gtable_add_cols(panels, ylab_width_left, pos = 0) 123 | panels <- gtable_add_grob(panels, labels$y[[1]], 124 | name = 'ylab-l', 125 | l = 1, b = panel_dim$b, t = panel_dim$t, clip = 'off' 126 | ) 127 | 128 | ylab_width_right <- grobWidth(labels$y[[2]]) 129 | panels <- gtable_add_cols(panels, ylab_width_right, pos = -1) 130 | panels <- gtable_add_grob(panels, labels$y[[2]], 131 | name = 'ylab-r', 132 | l = -1, b = panel_dim$b, t = panel_dim$t, clip = 'off' 133 | ) 134 | 135 | panels 136 | } 137 | ) 138 | -------------------------------------------------------------------------------- /R/ggforce-package.R: -------------------------------------------------------------------------------- 1 | #' @useDynLib ggforce 2 | #' @import ggplot2 3 | #' 4 | #' @examples 5 | #' rocketData <- data.frame( 6 | #' x = c(1, 1, 2, 2), 7 | #' y = c(1, 2, 2, 3) 8 | #' ) 9 | #' rocketData <- do.call(rbind, lapply(seq_len(500) - 1, function(i) { 10 | #' rocketData$y <- rocketData$y - c(0, i / 500) 11 | #' rocketData$group <- i + 1 12 | #' rocketData 13 | #' })) 14 | #' rocketData2 <- data.frame( 15 | #' x = c(2, 2.25, 2), 16 | #' y = c(2, 2.5, 3) 17 | #' ) 18 | #' rocketData2 <- do.call(rbind, lapply(seq_len(500) - 1, function(i) { 19 | #' rocketData2$x[2] <- rocketData2$x[2] - i * 0.25 / 500 20 | #' rocketData2$group <- i + 1 + 500 21 | #' rocketData2 22 | #' })) 23 | #' 24 | #' ggplot() + geom_link(aes( 25 | #' x = 2, y = 2, xend = 3, yend = 3, alpha = after_stat(index), 26 | #' size = after_stat(index) 27 | #' ), colour = 'goldenrod', n = 500) + 28 | #' geom_bezier(aes(x = x, y = y, group = group, colour = after_stat(index)), 29 | #' data = rocketData 30 | #' ) + 31 | #' geom_bezier(aes(x = y, y = x, group = group, colour = after_stat(index)), 32 | #' data = rocketData 33 | #' ) + 34 | #' geom_bezier(aes(x = x, y = y, group = group, colour = 1), 35 | #' data = rocketData2 36 | #' ) + 37 | #' geom_bezier(aes(x = y, y = x, group = group, colour = 1), 38 | #' data = rocketData2 39 | #' ) + 40 | #' geom_text(aes(x = 1.65, y = 1.65, label = 'ggplot2', angle = 45), 41 | #' colour = 'white', size = 15 42 | #' ) + 43 | #' coord_fixed() + 44 | #' scale_x_reverse() + 45 | #' scale_y_reverse() + 46 | #' scale_alpha(range = c(1, 0), guide = 'none') + 47 | #' scale_size_continuous( 48 | #' range = c(20, 0.1), trans = 'exp', 49 | #' guide = 'none' 50 | #' ) + 51 | #' scale_color_continuous(guide = 'none') + 52 | #' xlab('') + ylab('') + 53 | #' ggtitle('ggforce: Accelerating ggplot2') + 54 | #' theme(plot.title = element_text(size = 20)) 55 | #' @keywords internal 56 | "_PACKAGE" 57 | 58 | ## usethis namespace: start 59 | #' @import rlang 60 | #' @import vctrs 61 | #' @importFrom lifecycle deprecated 62 | #' @useDynLib ggforce, .registration = TRUE 63 | ## usethis namespace: end 64 | NULL 65 | -------------------------------------------------------------------------------- /R/ggproto-classes.R: -------------------------------------------------------------------------------- 1 | #' ggforce extensions to ggplot2 2 | #' 3 | #' ggforce makes heavy use of the ggproto class system to extend the 4 | #' functionality of ggplot2. In general the actual classes should be of little 5 | #' interest to users as the standard ggplot2 api of using geom_* and stat_* 6 | #' functions for building up the plot is encouraged. 7 | #' 8 | #' @name ggforce-extensions 9 | #' @rdname ggforce-extensions 10 | #' 11 | NULL 12 | -------------------------------------------------------------------------------- /R/interpolate.R: -------------------------------------------------------------------------------- 1 | #' @rdname ggforce-extensions 2 | #' @format NULL 3 | #' @usage NULL 4 | #' @importFrom grid segmentsGrob polylineGrob gpar 5 | GeomPathInterpolate <- ggproto('GeomPathInterpolate', GeomPath, 6 | draw_panel = function(self, data, panel_scales, coord, arrow = NULL, 7 | lineend = 'butt', linejoin = 'round', linemitre = 1, 8 | na.rm = FALSE) { 9 | if (!anyDuplicated(data$group)) { 10 | cli::cli_inform(c( 11 | "{.fn {snake_class(self)}}: Each group consists of only one observation.", 12 | i = "Do you need to adjust the {.field group} aesthetic?" 13 | )) 14 | } 15 | data <- data[order(data$group), , drop = FALSE] 16 | data <- interpolateDataFrame(data) 17 | munched <- coord_munch(coord, data, panel_scales) 18 | rows <- stats::ave(seq_len(nrow(munched)), munched$group, 19 | FUN = length 20 | ) 21 | munched <- munched[rows >= 2, ] 22 | if (nrow(munched) < 2) { 23 | return(zeroGrob()) 24 | } 25 | attr <- dapply(data, 'group', function(df) { 26 | data_frame0( 27 | solid = identical(unique0(df$linetype), 1), 28 | constant = nrow(unique0(df[, names(df) %in% c( 29 | 'alpha', 'colour', 30 | 'linewidth', 'size', 'linetype' 31 | )])) == 1 32 | ) 33 | }) 34 | 35 | solid_lines <- all(attr$solid) 36 | constant <- all(attr$constant) 37 | if (!solid_lines && !constant) { 38 | cli::cli_abort("{.fn {snake_class(self)}} can't have varying {.field colour}, {.field linewidth}, and/or {.field alpha} along the line when {.field linetype} isn't solid") 39 | } 40 | n <- nrow(munched) 41 | group_diff <- munched$group[-1] != munched$group[-n] 42 | start <- c(TRUE, group_diff) 43 | end <- c(group_diff, TRUE) 44 | if (!constant) { 45 | segmentsGrob(munched$x[!end], munched$y[!end], munched$x[!start], 46 | munched$y[!start], 47 | default.units = 'native', arrow = arrow, 48 | gp = gpar( 49 | col = alpha(munched$colour, munched$alpha)[!end], 50 | fill = ggplot2::fill_alpha(munched$colour[!end], munched$alpha[!end]), 51 | lwd = (munched$linewidth[!end] %||% munched$size[!end]) * .pt, 52 | lty = munched$linetype[!end], 53 | lineend = lineend, linejoin = linejoin, linemitre = linemitre 54 | ) 55 | ) 56 | } 57 | else { 58 | id <- match(munched$group, unique0(munched$group)) 59 | polylineGrob(munched$x, munched$y, 60 | id = id, default.units = 'native', 61 | arrow = arrow, gp = gpar( 62 | col = alpha(munched$colour, munched$alpha)[start], 63 | fill = ggplot2::fill_alpha(munched$colour[start], munched$alpha[start]), 64 | lwd = (munched$linewidth[start] %||% munched$size[start]) * .pt, 65 | lty = munched$linetype[start], lineend = lineend, 66 | linejoin = linejoin, linemitre = linemitre 67 | ) 68 | ) 69 | } 70 | }, 71 | handle_na = function(data, params) { 72 | data 73 | } 74 | ) 75 | #' Interpolate layer data 76 | #' 77 | #' @param data A data.frame with data for a layer 78 | #' 79 | #' @return A similar data.frame with NA values interpolated 80 | #' 81 | #' @importFrom tweenr tween_t 82 | #' @keywords internal 83 | #' @export 84 | interpolateDataFrame <- function(data) { 85 | if (is.null(data$group)) { 86 | cli::cli_abort('data must have a group column') 87 | } 88 | interpLengths <- lengths(split(data$group, data$group)) 89 | for (i in seq_len(ncol(data))) { 90 | if (names(data)[i] %in% c('x', 'y', 'index', 'group', '.interp') || 91 | all(is.na(data[[i]]))) { 92 | next 93 | } 94 | if (length(unique0(data[[i]][data$.interp])) > 1) { 95 | next 96 | } 97 | interpValues <- split(data[[i]][!data$.interp], data$group[!data$.interp]) 98 | data[[i]] <- unlist(tween_t(interpValues, interpLengths)) 99 | } 100 | data[, names(data) != '.interp'] 101 | } 102 | -------------------------------------------------------------------------------- /R/labeller.R: -------------------------------------------------------------------------------- 1 | #' A labeller function to parse TeX syntax 2 | #' 3 | #' This function formats the strip labels of facet grids and wraps that contains 4 | #' TeX expressions. The latex2exp package must be installed. 5 | #' 6 | #' @seealso [ggplot2::labeller], [latex2exp::TeX()] 7 | #' 8 | #' @inheritParams ggplot2::label_parsed 9 | #' @inheritDotParams ggplot2::label_parsed -labels 10 | #' 11 | #' @examples 12 | #' # requires latex2exp package be installed 13 | #' if (requireNamespace("latex2exp", quietly = TRUE)) { 14 | #' library(ggplot2) 15 | #' d <- data.frame(x = 1, y = 1, facet = "$\\beta$") 16 | #' ggplot(d, aes(x, y)) + 17 | #' geom_point() + 18 | #' facet_wrap(~ facet, labeller = label_tex) 19 | #' } 20 | #' @importFrom ggplot2 label_parsed 21 | #' @export 22 | label_tex <- function(labels, ...) { 23 | check_installed('latex2exp', 'to parse tex equations') 24 | label_parsed( 25 | data_frame0(!!!lapply(labels, latex2exp::TeX, output = "character")), 26 | ... 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /R/position-jitternormal.R: -------------------------------------------------------------------------------- 1 | #' Jitter points with normally distributed random noise 2 | #' 3 | #' [ggplot2::geom_jitter()] adds random noise to points using a uniform 4 | #' distribution. When many points are plotted, they appear in a rectangle. This 5 | #' position jitters points using a normal distribution instead, resulting in 6 | #' more circular clusters. 7 | #' 8 | #' @family position adjustments 9 | #' @param sd_x,sd_y Standard deviation to add along the x and y axes. The 10 | #' function uses [stats::rnorm()] with `mean = 0` behind the scenes. 11 | #' 12 | #' If omitted, defaults to 0.15. As with [ggplot2::geom_jitter()], categorical 13 | #' data is aligned on the integers, so a standard deviation of more than 0.2 14 | #' will spread the data so it's not possible to see the distinction between 15 | #' the categories. 16 | #' @inheritParams ggplot2::position_jitter 17 | #' @export 18 | #' @examples 19 | #' # Example data 20 | #' df <- data.frame( 21 | #' x = sample(1:3, 1500, TRUE), 22 | #' y = sample(1:3, 1500, TRUE) 23 | #' ) 24 | #' 25 | #' # position_jitter results in rectangular clusters 26 | #' ggplot(df, aes(x = x, y = y)) + 27 | #' geom_point(position = position_jitter()) 28 | #' 29 | #' # geom_jitternormal results in more circular clusters 30 | #' ggplot(df, aes(x = x, y = y)) + 31 | #' geom_point(position = position_jitternormal()) 32 | #' 33 | #' # You can adjust the standard deviations along both axes 34 | #' # Tighter circles 35 | #' ggplot(df, aes(x = x, y = y)) + 36 | #' geom_point(position = position_jitternormal(sd_x = 0.08, sd_y = 0.08)) 37 | #' 38 | #' # Oblong shapes 39 | #' ggplot(df, aes(x = x, y = y)) + 40 | #' geom_point(position = position_jitternormal(sd_x = 0.2, sd_y = 0.08)) 41 | #' 42 | #' # Only add random noise to one dimension 43 | #' ggplot(df, aes(x = x, y = y)) + 44 | #' geom_point( 45 | #' position = position_jitternormal(sd_x = 0.15, sd_y = 0), 46 | #' alpha = 0.1 47 | #' ) 48 | position_jitternormal <- function(sd_x = NULL, sd_y = NULL, seed = NA) { 49 | ggproto(NULL, PositionJitterNormal, 50 | sd_x = sd_x, 51 | sd_y = sd_y, 52 | seed = seed 53 | ) 54 | } 55 | #' @rdname ggforce-extensions 56 | #' @format NULL 57 | #' @usage NULL 58 | #' @export 59 | PositionJitterNormal <- ggproto('PositionJitterNormal', 60 | Position, 61 | seed = NA, 62 | required_aes = c('x', 'y'), 63 | 64 | setup_params = function(self, data) { 65 | if (!is.null(self$seed) && is.na(self$seed)) { 66 | seed <- sample.int(.Machine$integer.max, 1L) 67 | } else { 68 | seed <- self$seed 69 | } 70 | list( 71 | sd_x = self$sd_x %||% 0.15, 72 | sd_y = self$sd_y %||% 0.15, 73 | seed = seed 74 | ) 75 | }, 76 | 77 | compute_layer = function(data, params, panel) { 78 | trans_x <- if (params$sd_x > 0) { 79 | function(x) x + rnorm(length(x), sd = params$sd_x) 80 | } 81 | trans_y <- if (params$sd_y > 0) { 82 | function(x) x + rnorm(length(x), sd = params$sd_y) 83 | } 84 | 85 | # Make sure x and y jitter is only calculated once for all position aesthetics 86 | # Takes aesthetic names from ggplot_global 87 | x_aes <- intersect(c("x", "xmin", "xmax", "xend", "xintercept", "xmin_final", "xmax_final", 88 | "xlower", "xmiddle", "xupper", "x0"), names(data)) 89 | x <- if (length(x_aes) == 0) 0 else data[[x_aes[1]]] 90 | y_aes <- intersect(c("y", "ymin", "ymax", "yend", "yintercept", "ymin_final", "ymax_final", 91 | "lower", "middle", "upper", "y0"), names(data)) 92 | y <- if (length(y_aes) == 0) 0 else data[[y_aes[1]]] 93 | dummy_data <- data_frame0(x = x, y = y, .size = nrow(data)) 94 | fixed_jitter <- with_seed_null(params$seed, transform_position(dummy_data, trans_x, trans_y)) 95 | x_jit <- fixed_jitter$x - x 96 | y_jit <- fixed_jitter$y - y 97 | # Avoid nan values, if x or y has Inf values 98 | x_jit[is.infinite(x)] <- 0 99 | y_jit[is.infinite(y)] <- 0 100 | 101 | # Apply jitter 102 | transform_position(data, function(x) x + x_jit, function(x) x + y_jit) 103 | } 104 | ) 105 | -------------------------------------------------------------------------------- /R/position_auto.R: -------------------------------------------------------------------------------- 1 | #' Jitter based on scale types 2 | #' 3 | #' This position adjustment is able to select a meaningful jitter of the data 4 | #' based on the combination of positional scale types. It behaves differently 5 | #' depending on if none, one, or both the x and y scales are discrete. If both 6 | #' are discrete it will jitter the datapoints evenly inside a disc, if one of 7 | #' them is discrete it will jitter the discrete dimension to follow the density 8 | #' along the other dimension (like a sina plot). If neither are discrete it will 9 | #' not do any jittering. 10 | #' 11 | #' @param jitter.width The maximal width of the jitter 12 | #' @param bw The smoothing bandwidth to use in the case of sina jittering. See 13 | #' the `bw` argument in [stats::density] 14 | #' @param scale Should the width of jittering be scaled based on the number of 15 | #' points in the group 16 | #' @param seed A seed to supply to make the jittering reproducible across layers 17 | #' 18 | #' @seealso [geom_autopoint] for a point geom that uses auto-position by default 19 | #' 20 | #' @export 21 | #' 22 | #' @examples 23 | #' # Continuous vs continuous: No jitter 24 | #' ggplot(mpg) + geom_point(aes(cty, hwy), position = 'auto') 25 | #' 26 | #' # Continuous vs discrete: sina jitter 27 | #' ggplot(mpg) + geom_point(aes(cty, drv), position = 'auto') 28 | #' 29 | #' # Discrete vs discrete: disc-jitter 30 | #' ggplot(mpg) + geom_point(aes(fl, drv), position = 'auto') 31 | #' 32 | #' # Don't scale the jitter based on group size 33 | #' ggplot(mpg) + geom_point(aes(cty, drv), position = position_auto(scale = FALSE)) 34 | #' ggplot(mpg) + geom_point(aes(fl, drv), position = position_auto(scale = FALSE)) 35 | #' 36 | position_auto <- function(jitter.width = 0.75, bw = 'nrd0', scale = TRUE, seed = NA) { 37 | if (!is.null(seed) && is.na(seed)) { 38 | seed <- sample.int(.Machine$integer.max, 1L) 39 | } 40 | 41 | ggproto(NULL, PositionAuto, 42 | jitter.width = jitter.width, 43 | seed = seed, bw = bw, scale = scale 44 | ) 45 | } 46 | #' @rdname ggforce-extensions 47 | #' @format NULL 48 | #' @usage NULL 49 | #' @export 50 | #' @importFrom withr with_seed 51 | PositionAuto <- ggproto('PositionAuto', Position, 52 | jitter.width = 0.75, 53 | seed = NULL, 54 | bw = 'nrd0', 55 | scale = TRUE, 56 | setup_params = function(self, data) { 57 | list(jitter.width = self$jitter.width, bw = self$bw, seed = self$seed, scale = self$scale) 58 | }, 59 | compute_panel = function(data, params, scales) { 60 | discrete_x <- scales$x$is_discrete() 61 | discrete_y <- scales$y$is_discrete() 62 | if (!discrete_x && !discrete_y) { 63 | return(data) 64 | } 65 | if (discrete_x && discrete_y) { 66 | comb <- table(data$x, data$y) 67 | max_n <- max(comb) 68 | if (params$scale) { 69 | weight <- sqrt(comb[cbind(as.character(data$x), as.character(data$y))] / max_n) * (params$jitter.width / 2) 70 | } else { 71 | weight <- params$jitter.width / 2 72 | } 73 | if (is.null(params$seed)) { 74 | adj <- sample_disc(length(data$x), weight) 75 | } else { 76 | adj <- with_seed(params$seed, sample_disc(length(data$x), weight)) 77 | } 78 | data$x <- data$x + adj$x 79 | data$y <- data$y + adj$y 80 | data 81 | } else { 82 | trans_x <- trans_y <- identity 83 | if (discrete_x) { 84 | trans_x <- function(x) x + sina_trans(x, data$y, params$jitter.width / 2, params$bw, params$scale) 85 | } else { 86 | trans_y <- function(x) x + sina_trans(x, data$x, params$jitter.width / 2, params$bw, params$scale) 87 | } 88 | if (is.null(params$seed)) { 89 | transform_position(data, trans_x, trans_y) 90 | } else { 91 | with_seed(params$seed, transform_position(data, trans_x, trans_y)) 92 | } 93 | } 94 | } 95 | ) 96 | 97 | sina_trans <- function(x, val, max_width, bw = 'nrd0', scale = TRUE) { 98 | max_size <- max(table(x)) 99 | by_ind <- split(seq_along(x), x) 100 | x_new <- unlist(lapply(by_ind, function(i) { 101 | val_x <- val[i] 102 | if (length(unique0(val_x)) < 2) { 103 | return(stats::runif(length(val_x), min = -max_width, max = max_width)) 104 | } 105 | if (length(val_x) < 3) { 106 | return(0) 107 | } 108 | range <- range(val_x, na.rm = TRUE) 109 | bw <- calc_bw(val_x, bw) 110 | dens <- stats::density(val_x, bw = bw, from = range[1], to = range[2]) 111 | densf <- stats::approxfun(dens$x, dens$y, rule = 2) 112 | x_mod <- densf(val_x) 113 | x_mod <- x_mod / max(x_mod) 114 | if (scale) x_mod <- x_mod * length(val_x) / max_size 115 | stats::runif(length(val_x), min = -1, max = 1) * max_width * x_mod 116 | })) 117 | x_new[match(seq_along(x), unlist(by_ind))] 118 | } 119 | sample_disc <- function(n, r_disc = 1) { 120 | r = sqrt(stats::runif(n, 0, 1)) 121 | theta = stats::runif(n, 0, 2*pi) 122 | x <- r * cos(theta) * r_disc 123 | y <- r * sin(theta) * r_disc 124 | list(x = x, y = y) 125 | } 126 | -------------------------------------------------------------------------------- /R/position_floatstack.R: -------------------------------------------------------------------------------- 1 | # Only for use with autohistogram and autodensity 2 | 3 | #' @rdname ggforce-extensions 4 | #' @format NULL 5 | #' @usage NULL 6 | #' @export 7 | PositionFloatstack <- ggproto('PositionFloatstack', PositionStack, 8 | setup_params = function(self, data) { 9 | flipped_aes <- has_flipped_aes(data) 10 | data <- flip_data(data, flipped_aes) 11 | list( 12 | var = self$var %||% if (flipped_aes) 'xmax' else 'ymax', 13 | fill = self$fill, 14 | vjust = self$vjust, 15 | reverse = self$reverse, 16 | flipped_aes = flipped_aes 17 | ) 18 | }, 19 | compute_panel = function(self, data, params, scales) { 20 | data <- flip_data(data, params$flipped_aes) 21 | panel_min <- data$ymin[1] 22 | 23 | data$y <- data$y - panel_min 24 | data$ymin <- data$ymin - panel_min 25 | data$ymax <- data$ymax - panel_min 26 | 27 | data <- flip_data(data, params$flipped_aes) 28 | data <- ggproto_parent(PositionStack, self)$compute_panel(data, params, scales) 29 | data <- flip_data(data, params$flipped_aes) 30 | 31 | data$y <- data$y + panel_min 32 | data$ymin <- data$ymin + panel_min 33 | data$ymax <- data$ymax + panel_min 34 | 35 | flip_data(data, params$flipped_aes) 36 | } 37 | ) 38 | -------------------------------------------------------------------------------- /R/regon.R: -------------------------------------------------------------------------------- 1 | #' Draw regular polygons by specifying number of sides 2 | #' 3 | #' This geom makes it easy to construct regular polygons (polygons where all 4 | #' sides and angles are equal) by specifying the number of sides, position, and 5 | #' size. The polygons are always rotated so that they "rest" on a flat side, but 6 | #' this can be changed with the angle aesthetic. The size is based on the radius 7 | #' of their circumcircle and is thus not proportional to their area. 8 | #' 9 | #' @section Aesthetics: 10 | #' geom_regon understand the following aesthetics (required aesthetics are in 11 | #' bold): 12 | #' 13 | #' - **x0** x coordinate 14 | #' - **y0** y coordinate 15 | #' - **sides** the number of sides for regon 16 | #' - **r** the ratio of regon with respect to plot 17 | #' - **angle** regon rotation angle (unit is radian) 18 | #' - color 19 | #' - fill 20 | #' - size 21 | #' - linetype 22 | #' - alpha 23 | #' - lineend 24 | #' 25 | #' @section Computed variables: 26 | #' 27 | #' \describe{ 28 | #' \item{x, y}{The coordinates for the corners of the polygon} 29 | #' } 30 | #' 31 | #' @inheritParams ggplot2::geom_polygon 32 | #' @inheritParams ggplot2::stat_identity 33 | #' 34 | #' @name geom_regon 35 | #' @rdname geom_regon 36 | #' 37 | #' @examples 38 | #' ggplot() + 39 | #' geom_regon(aes(x0 = runif(8), y0 = runif(8), sides = sample(3:10, 8), 40 | #' angle = 0, r = runif(8) / 10)) + 41 | #' coord_fixed() 42 | #' 43 | #' # The polygons are drawn with geom_shape, so can be manipulated as such 44 | #' ggplot() + 45 | #' geom_regon(aes(x0 = runif(8), y0 = runif(8), sides = sample(3:10, 8), 46 | #' angle = 0, r = runif(8) / 10), 47 | #' expand = unit(1, 'cm'), radius = unit(1, 'cm')) + 48 | #' coord_fixed() 49 | NULL 50 | 51 | #' @rdname ggforce-extensions 52 | #' @format NULL 53 | #' @usage NULL 54 | #' @export 55 | StatRegon <- ggproto('StatRegon', Stat, 56 | compute_layer = function(self, data, params, panels) { 57 | if (empty_data(data)) return(data) 58 | pos <- unlist(lapply(data$sides, function(n) { 59 | p <- (seq_len(n) - 1) / n 60 | if (n %% 2 == 0) p <- p + p[2] / 2 61 | p * 2 * pi 62 | })) 63 | data$group <- make_unique(data$group) 64 | data <- data[rep(seq_len(nrow(data)), data$sides), ] 65 | x_tmp <- sin(pos) * data$r 66 | y_tmp <- cos(pos) * data$r 67 | data$x <- data$x0 + x_tmp * cos(data$angle) - y_tmp * sin(data$angle) 68 | data$y <- data$y0 + x_tmp * sin(data$angle) + y_tmp * cos(data$angle) 69 | data 70 | }, 71 | required_aes = c('x0', 'y0', 'sides', 'angle', 'r'), 72 | extra_params = c('na.rm') 73 | ) 74 | 75 | #' @rdname geom_regon 76 | #' @export 77 | stat_regon <- function(mapping = NULL, data = NULL, geom = 'shape', 78 | position = 'identity', na.rm = FALSE, show.legend = NA, 79 | inherit.aes = TRUE, ...) { 80 | layer( 81 | stat = StatRegon, data = data, mapping = mapping, geom = geom, 82 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 83 | params = list2(na.rm = na.rm, ...) 84 | ) 85 | } 86 | #' @rdname geom_regon 87 | #' @export 88 | geom_regon <- function(mapping = NULL, data = NULL, stat = 'regon', 89 | position = 'identity', na.rm = FALSE, 90 | show.legend = NA, inherit.aes = TRUE, ...) { 91 | layer( 92 | data = data, mapping = mapping, stat = stat, geom = GeomShape, 93 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 94 | params = list2(na.rm = na.rm, ...) 95 | ) 96 | } 97 | -------------------------------------------------------------------------------- /R/scale-depth.R: -------------------------------------------------------------------------------- 1 | #' Scales for depth perception 2 | #' 3 | #' These scales serve to scale the depth aesthetic when creating stereographic 4 | #' plots. The range specifies the relative distance between the points and the 5 | #' paper plane in relation to the distance between the eyes and the paper plane 6 | #' i.e. a range of c(-0.5, 0.5) would put the highest values midways between 7 | #' the eyes and the image plane and the lowest values the same distance behind 8 | #' the image plane. To ensure a nice viewing experience these values should not 9 | #' exceed ~0.3 as it would get hard for the eyes to consolidate the two 10 | #' pictures. 11 | #' 12 | #' @param ... arguments passed on to continuous_scale or discrete_scale 13 | #' 14 | #' @param range The relative range as related to the distance between the eyes 15 | #' and the paper plane. 16 | #' 17 | #' @export 18 | #' @importFrom scales rescale_pal 19 | #' 20 | #' @examples 21 | #' ggplot(mtcars) + 22 | #' geom_point(aes(mpg, disp, depth = cyl)) + 23 | #' scale_depth(range = c(-0.1, 0.25)) + 24 | #' facet_stereo() 25 | scale_depth <- function(..., range = c(0, 0.3)) { 26 | continuous_scale('depth', 'depth_c', rescale_pal(range), ...) 27 | } 28 | 29 | #' @rdname scale_depth 30 | #' 31 | #' @export 32 | scale_depth_continuous <- scale_depth 33 | 34 | #' @rdname scale_depth 35 | #' 36 | #' @export 37 | scale_depth_discrete <- function(..., range = c(0, 0.3)) { 38 | discrete_scale( 39 | 'depth', 'depth_d', 40 | function(n) seq(range[1], range[2], length.out = n), ... 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /R/scale-unit.R: -------------------------------------------------------------------------------- 1 | #' Position scales for units data 2 | #' 3 | #' `r lifecycle::badge('deprecated')` These are the default scales for the units 4 | #' class. These will usually be added automatically. To override manually, use 5 | #' `scale_*_unit`. 6 | #' 7 | #' @param ... Passed on to `units::scale_x_units()` or `units::scale_y_units()` 8 | #' 9 | #' @name scale_unit 10 | #' @aliases NULL 11 | #' @keywords internal 12 | NULL 13 | 14 | #' @rdname scale_unit 15 | #' @export 16 | #' @importFrom scales censor 17 | scale_x_unit <- function(...) { 18 | lifecycle::deprecate_soft('0.3.4', "scale_x_unit()", "units::scale_x_units()") 19 | check_installed('units', 'to use scale_x_unit') 20 | units::scale_x_units(...) 21 | } 22 | #' @rdname scale_unit 23 | #' @export 24 | #' @importFrom scales censor 25 | scale_y_unit <- function(...) { 26 | lifecycle::deprecate_soft('0.3.4', "scale_y_unit()", "units::scale_y_units()") 27 | check_installed('units', 'to use scale_y_unit') 28 | units::scale_y_units(...) 29 | } 30 | -------------------------------------------------------------------------------- /R/spiro.R: -------------------------------------------------------------------------------- 1 | #' Draw spirograms based on the radii of the different "wheels" involved 2 | #' 3 | #' This, rather pointless, geom allows you to draw spirograms, as known from the 4 | #' popular drawing toy where lines were traced by inserting a pencil into a hole 5 | #' in a small gear that would then trace around inside another gear. The 6 | #' potential practicality of this geom is slim and it excists mainly for fun and 7 | #' art. 8 | #' 9 | #' @section Aesthetics: 10 | #' stat_spiro and geom_spiro understand the following aesthetics (required 11 | #' aesthetics are in bold): 12 | #' 13 | #' - **R** 14 | #' - **r** 15 | #' - **d** 16 | #' - x0 17 | #' - y0 18 | #' - outer 19 | #' - color 20 | #' - size 21 | #' - linetype 22 | #' - alpha 23 | #' 24 | #' @section Computed variables: 25 | #' 26 | #' \describe{ 27 | #' \item{x, y}{The coordinates for the path describing the spirogram} 28 | #' \item{index}{The progression along the spirogram mapped between 0 and 1} 29 | #' } 30 | #' 31 | #' @inheritParams ggplot2::geom_path 32 | #' @inheritParams ggplot2::stat_identity 33 | #' 34 | #' @param n The number of points that should be used to draw a fully closed 35 | #' spirogram. If `revolutions < 1` the actual number of points will be less 36 | #' than this. 37 | #' 38 | #' @param revolutions The number of times the inner gear should revolve around 39 | #' inside the outer gear. If `NULL` the number of revolutions to reach the 40 | #' starting position is calculated and used. 41 | #' 42 | #' @name geom_spiro 43 | #' @rdname geom_spiro 44 | #' 45 | #' @examples 46 | #' # Basic usage 47 | #' ggplot() + 48 | #' geom_spiro(aes(R = 10, r = 3, d = 5)) 49 | #' 50 | #' # Only draw a portion 51 | #' ggplot() + 52 | #' geom_spiro(aes(R = 10, r = 3, d = 5), revolutions = 1.2) 53 | #' 54 | #' # Let the inner gear circle the outside of the outer gear 55 | #' ggplot() + 56 | #' geom_spiro(aes(R = 10, r = 3, d = 5, outer = TRUE)) 57 | NULL 58 | 59 | #' @rdname ggforce-extensions 60 | #' @format NULL 61 | #' @usage NULL 62 | #' @importFrom MASS fractions 63 | #' @export 64 | StatSpiro <- ggproto('StatSpiro', Stat, 65 | compute_panel = function(data, scales, n = 500, revolutions = NULL) { 66 | if (empty_data(data)) return(data) 67 | if (is.null(data$outer)) data$outer <- FALSE 68 | if (is.null(data$x0)) data$x0 <- 0 69 | if (is.null(data$y0)) data$y0 <- 0 70 | n_spiro <- nrow(data) 71 | data$group <- make_unique(data$group) 72 | if (is.null(revolutions)) { 73 | revo <- attr(fractions(data$r / data$R), 'fracs') 74 | revo <- as.numeric(sub('/.*$', '', revo)) 75 | } else { 76 | revo <- revolutions 77 | } 78 | data <- data[rep(seq_len(n_spiro), n * revo), ] 79 | data$rho <- unlist(lapply(revo, function(r) { 80 | seq(0, 2 * pi * r, length.out = n * r) 81 | })) 82 | data$index <- unlist(lapply(revo, function(r) { 83 | seq(0, 1, length.out = n * r) 84 | })) 85 | data$x <- data$x0 + ifelse( 86 | data$outer, 87 | (data$R + data$r) * cos(data$rho) - data$d * cos(data$rho * (data$R + data$r) / data$r), 88 | (data$R - data$r) * cos(data$rho) + data$d * cos(data$rho * (data$R - data$r) / data$r) 89 | ) 90 | data$y <- data$y0 + ifelse( 91 | data$outer, 92 | (data$R + data$r) * sin(data$rho) - data$d * sin(data$rho * (data$R + data$r) / data$r), 93 | (data$R - data$r) * sin(data$rho) - data$d * sin(data$rho * (data$R - data$r) / data$r) 94 | ) 95 | data 96 | }, 97 | required_aes = c('R', 'r', 'd'), 98 | default_aes = aes(outer = FALSE, x0 = 0, y0 = 0), 99 | extra_params = c('na.rm', 'n', 'revolutions') 100 | ) 101 | 102 | #' @rdname geom_spiro 103 | #' @export 104 | stat_spiro <- function(mapping = NULL, data = NULL, geom = 'path', 105 | position = 'identity', na.rm = FALSE, n = 500, 106 | revolutions = NULL, show.legend = NA, inherit.aes = TRUE, 107 | ...) { 108 | layer( 109 | stat = StatSpiro, data = data, mapping = mapping, geom = geom, 110 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 111 | params = list2(na.rm = na.rm, n = n, revolutions = revolutions, ...) 112 | ) 113 | } 114 | 115 | #' @rdname geom_spiro 116 | #' @export 117 | geom_spiro <- function(mapping = NULL, data = NULL, stat = 'spiro', 118 | position = 'identity', arrow = NULL, n = 500, 119 | lineend = 'butt', na.rm = FALSE, show.legend = NA, 120 | inherit.aes = TRUE, ...) { 121 | layer( 122 | data = data, mapping = mapping, stat = stat, geom = GeomPath, 123 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 124 | params = list2(arrow = arrow, lineend = lineend, na.rm = na.rm, n = n, ...) 125 | ) 126 | } 127 | -------------------------------------------------------------------------------- /R/themes.R: -------------------------------------------------------------------------------- 1 | #' Theme without axes and gridlines 2 | #' 3 | #' This theme is a simple wrapper around any complete theme that removes the 4 | #' axis text, title and ticks as well as the grid lines for plots where these 5 | #' have little meaning. 6 | #' 7 | #' @param base.theme The theme to use as a base for the new theme. Defaults to 8 | #' [ggplot2::theme_bw()]. 9 | #' 10 | #' @return A modified version of base.theme 11 | #' 12 | #' @export 13 | #' 14 | #' @examples 15 | #' p <- ggplot() + geom_point(aes(x = wt, y = qsec), data = mtcars) 16 | #' 17 | #' p + theme_no_axes() 18 | #' p + theme_no_axes(theme_grey()) 19 | #' 20 | theme_no_axes <- function(base.theme = theme_bw()) { 21 | base.theme %+replace% 22 | theme( 23 | axis.text = element_blank(), 24 | axis.title = element_blank(), 25 | axis.ticks = element_blank(), 26 | panel.grid = element_blank() 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /R/trans.R: -------------------------------------------------------------------------------- 1 | #' Create a power transformation object 2 | #' 3 | #' This function can be used to create a proper trans object that encapsulates 4 | #' a power transformation (x^n). 5 | #' 6 | #' @param n The degree of the power transformation 7 | #' 8 | #' @return A trans object 9 | #' 10 | #' @importFrom scales trans_new extended_breaks format_format 11 | #' @importFrom MASS fractions 12 | #' 13 | #' @export 14 | #' 15 | #' @examples 16 | #' # Power of 2 transformations 17 | #' trans <- power_trans(2) 18 | #' trans$transform(1:10) 19 | #' 20 | #' # Cubic root transformation 21 | #' trans <- power_trans(1 / 3) 22 | #' trans$transform(1:10) 23 | #' 24 | #' # Use it in a plot 25 | #' ggplot() + 26 | #' geom_line(aes(x = 1:10, y = 1:10)) + 27 | #' scale_x_continuous(trans = power_trans(2), 28 | #' expand = c(0, 1)) 29 | power_trans <- function(n) { 30 | trans_new( 31 | name = paste0('power of ', fractions(n)), 32 | transform = function(x) { 33 | x^n 34 | }, 35 | inverse = function(x) { 36 | x^(1 / n) 37 | }, 38 | breaks = extended_breaks(), 39 | format = format_format(), 40 | domain = c(0, Inf) 41 | ) 42 | } 43 | #' Create radial data in a cartesian coordinate system 44 | #' 45 | #' This function creates a trans object that converts radial data to their 46 | #' corresponding coordinates in cartesian space. The trans object is created for 47 | #' a specific radius and angle range that will be mapped to the unit circle so 48 | #' data doesn't have to be normalized to 0-1 and 0-2*pi in advance. While there 49 | #' exists a clear mapping from radial to cartesian, the inverse is not true as 50 | #' radial representation is periodic. It is impossible to know how many 51 | #' revolutions around the unit circle a point has taken from reading its 52 | #' coordinates. The inverse function will always assume that coordinates are in 53 | #' their first revolution i.e. map them back within the range of a.range. 54 | #' 55 | #' @param r.range The range in radius that correspond to 0 - 1 in the unit 56 | #' circle. 57 | #' 58 | #' @param a.range The range in angles that correspond to 2*pi - 0. As radians 59 | #' are normally measured counterclockwise while radial displays are read 60 | #' clockwise it's an inverse mapping 61 | #' 62 | #' @param offset The offset in angles to apply. Determines that start position 63 | #' on the circle. pi/2 (the default) corresponds to 12 o'clock. 64 | #' 65 | #' @param pad Adds to the end points of the angle range in order to separate the 66 | #' start and end point. Defaults to 0.5 67 | #' 68 | #' @param clip Should input data be clipped to r.range and a.range or be allowed 69 | #' to extend beyond. Defaults to FALSE (no clipping) 70 | #' 71 | #' @return A trans object. The transform method for the object takes an r 72 | #' (radius) and a (angle) argument and returns a data.frame with x and y columns 73 | #' with rows for each element in r/a. The inverse method takes an x and y 74 | #' argument and returns a data.frame with r and a columns and rows for each 75 | #' element in x/y. 76 | #' 77 | #' @note While trans objects are often used to modify scales in ggplot2, radial 78 | #' transformation is different as it is a coordinate transformation and takes 79 | #' two arguments. Consider it a trans version of coord_polar and use it to 80 | #' transform your data prior to plotting. 81 | #' 82 | #' @importFrom scales trans_new extended_breaks format_format 83 | #' 84 | #' @export 85 | #' 86 | #' @examples 87 | #' # Some data in radial form 88 | #' rad <- data.frame(r = seq(1, 10, by = 0.1), a = seq(1, 10, by = 0.1)) 89 | #' 90 | #' # Create a transformation 91 | #' radial <- radial_trans(c(0, 1), c(0, 5)) 92 | #' 93 | #' # Get data in x, y 94 | #' cart <- radial$transform(rad$r, rad$a) 95 | #' 96 | #' # Have a look 97 | #' ggplot() + 98 | #' geom_path(aes(x = x, y = y), data = cart, color = 'forestgreen') + 99 | #' geom_path(aes(x = r, y = a), data = rad, color = 'firebrick') 100 | radial_trans <- function(r.range, a.range, offset = pi / 2, pad = 0.5, 101 | clip = FALSE) { 102 | a.range[which.min(a.range)] <- min(a.range) - pad 103 | a.range[which.max(a.range)] <- max(a.range) + pad 104 | trans_new( 105 | name = paste0( 106 | 'radial-to-cartesian: ', 107 | r.range[1], '-', r.range[2], ' -> 0-1; ', 108 | a.range[1], '-', a.range[2], ' -> 2pi-0' 109 | ), 110 | transform = function(r, a) { 111 | if (clip) { 112 | r[r < min(r.range)] <- min(r.range) 113 | r[r > max(r.range)] <- max(r.range) 114 | a[a < min(a.range)] <- min(a.range) 115 | a[a > max(a.range)] <- max(a.range) 116 | } 117 | if (diff(r.range) == 0) { 118 | r <- 1 119 | } else { 120 | r <- (r - r.range[1]) / diff(r.range) 121 | } 122 | if (diff(a.range) == 0) { 123 | a <- offset 124 | } else { 125 | a <- offset + (a - a.range[1]) / diff(a.range) * -2 * pi 126 | } 127 | data_frame0(x = r * cos(a), y = r * sin(a)) 128 | }, 129 | inverse = function(x, y) { 130 | r <- sqrt(x^2 + y^2) * diff(r.range) + r.range[1] 131 | angle <- -(atan2(y, x) - offset) 132 | angle[angle < 0] <- 2 * pi + angle[angle < 0] 133 | a <- angle / (2 * pi) * diff(a.range) + a.range[1] 134 | data_frame0(r = r, a = a) 135 | }, 136 | breaks = extended_breaks(), 137 | format = format_format() 138 | ) 139 | } 140 | #' Reverse a transformation 141 | #' 142 | #' While the scales package export a reverse_trans object it does not allow for 143 | #' reversing of already transformed ranged - e.g. a reverse exp transformation 144 | #' is not possible. trans_reverser takes a trans object or something coercible 145 | #' to one and creates a reverse version of it. 146 | #' 147 | #' @param trans A trans object or an object that can be converted to one using 148 | #' [scales::as.trans()] 149 | #' 150 | #' @return A trans object 151 | #' 152 | #' @importFrom scales as.trans trans_new asn_trans atanh_trans boxcox_trans 153 | #' date_trans exp_trans identity_trans log10_trans log1p_trans log2_trans 154 | #' logit_trans log_trans probability_trans probit_trans reciprocal_trans 155 | #' reverse_trans sqrt_trans time_trans 156 | #' 157 | #' @export 158 | #' 159 | #' @examples 160 | #' # Lets make a plot 161 | #' p <- ggplot() + 162 | #' geom_line(aes(x = 1:10, y = 1:10)) 163 | #' 164 | #' # scales already have a reverse trans 165 | #' p + scale_x_continuous(trans = 'reverse') 166 | #' 167 | #' # But what if you wanted to reverse an already log transformed scale? 168 | #' p + scale_x_continuous(trans = trans_reverser('log')) 169 | trans_reverser <- function(trans) { 170 | transformOrig <- as.trans(trans) 171 | trans_new( 172 | name = paste0('reverse-', transformOrig$name), 173 | transform = function(x) { 174 | -transformOrig$transform(x) 175 | }, 176 | inverse = function(x) { 177 | transformOrig$inverse(-x) 178 | }, 179 | breaks = transformOrig$breaks, 180 | format = transformOrig$format, 181 | domain = transformOrig$domain 182 | ) 183 | } 184 | -------------------------------------------------------------------------------- /R/trans_linear.R: -------------------------------------------------------------------------------- 1 | #' Create a custom linear transformation 2 | #' 3 | #' This function lets you compose transformations based on a sequence of linear 4 | #' transformations. If the transformations are parameterised the parameters will 5 | #' become arguments in the transformation function. The transformations are 6 | #' one of `rotate`, `shear`, `stretch`, `translate`, and 7 | #' `reflect`. 8 | #' 9 | #' @param ... A number of transformation functions. 10 | #' 11 | #' @return `linear_trans` creates a trans object. The other functions 12 | #' return a 3x3 transformation matrix. 13 | #' 14 | #' @export 15 | #' @importFrom scales trans_new 16 | #' 17 | #' @examples 18 | #' trans <- linear_trans(rotate(a), shear(1, 0), translate(x1, y1)) 19 | #' square <- data.frame(x = c(0, 0, 1, 1), y = c(0, 1, 1, 0)) 20 | #' square2 <- trans$transform(square$x, square$y, a = pi / 3, x1 = 4, y1 = 8) 21 | #' square3 <- trans$transform(square$x, square$y, a = pi / 1.5, x1 = 2, y1 = -6) 22 | #' square <- rbind(square, square2, square3) 23 | #' square$group <- rep(1:3, each = 4) 24 | #' ggplot(square, aes(x, y, group = group)) + 25 | #' geom_polygon(aes(fill = factor(group)), colour = 'black') 26 | linear_trans <- function(...) { 27 | calls <- as.list(substitute(list2(...)))[-1] 28 | transformations <- sapply(calls, deparse) 29 | args <- unlist(lapply(calls, function(call) { 30 | args <- as.list(call)[-1] 31 | as.character(args[sapply(args, 'class') == 'name']) 32 | })) 33 | args <- unique0(args) 34 | if (any(c('x', 'y') %in% args)) { 35 | cli::cli_abort('{.arg x} and {.arg y} are preserved argument names') 36 | } 37 | args <- c('x', 'y', args) 38 | trans_fun <- function() { 39 | env <- environment() 40 | trans_mat <- Reduce(function(l, r) r %*% l, 41 | lapply(calls, eval, envir = env)) 42 | trans <- trans_mat %*% rbind(x, y, z = 1) 43 | data_frame0(x = trans[1, ], y = trans[2, ]) 44 | } 45 | formals(trans_fun) <- structure(rep(list(quote(expr = )), length(args)), 46 | names = args) 47 | inv_fun <- function() { 48 | env <- environment() 49 | trans_mat <- Reduce(function(l, r) r %*% l, 50 | lapply(calls, eval, envir = env)) 51 | trans_mat <- solve(trans_mat) 52 | trans <- trans_mat %*% rbind(x, y, z = 1) 53 | data_frame0(x = trans[1, ], y = trans[2, ]) 54 | } 55 | formals(inv_fun) <- structure(rep(list(quote(expr = )), length(args)), 56 | names = args) 57 | trans_new( 58 | name = paste0('linear: ', paste(transformations, collapse = ', ')), 59 | transform = trans_fun, 60 | inverse = inv_fun, 61 | breaks = extended_breaks(), 62 | format = format_format() 63 | ) 64 | } 65 | 66 | #' @rdname linear_trans 67 | #' @param angle An angle in radians 68 | rotate <- function(angle) { 69 | matrix(c(cos(angle), -sin(angle), 0, sin(angle), cos(angle), 0, 0, 0, 1), 70 | ncol = 3) 71 | } 72 | #' @rdname linear_trans 73 | #' @param x the transformation magnitude in the x-direction 74 | #' @param y the transformation magnitude in the x-direction 75 | stretch <- function(x, y) { 76 | matrix(c(x, 0, 0, 0, y, 0, 0, 0, 1), ncol = 3) 77 | } 78 | #' @rdname linear_trans 79 | shear <- function(x, y) { 80 | matrix(c(1, y, 0, x, 1, 0, 0, 0, 1), ncol = 3) 81 | } 82 | #' @rdname linear_trans 83 | translate <- function(x, y) { 84 | matrix(c(1, 0, 0, 0, 1, 0, x, y, 1), ncol = 3) 85 | } 86 | #' @rdname linear_trans 87 | reflect <- function(x, y) { 88 | l <- x^2 + y^2 89 | matrix( 90 | c( 91 | (x^2 - y^2) / l, 92 | 2 * x * y / l, 93 | 0, 94 | 2 * x * y / l, 95 | (y^2 - x^2) / l, 96 | 0, 97 | 0, 98 | 0, 99 | 1 100 | ), 101 | ncol = 3 102 | ) 103 | } 104 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | globalVariables(c( 2 | "from_theme", 3 | "colour", 4 | "ink", 5 | "linewidth", 6 | "linetype" 7 | )) 8 | 9 | default_axis_guide <- NULL 10 | .onLoad <- function(...) { 11 | if (utils::packageVersion("ggplot2") > "3.2.1") { 12 | default_axis_guide <<- ggplot2::waiver() 13 | } else { 14 | default_axis_guide <<- "none" 15 | } 16 | 17 | if ("element_geom" %in% getNamespaceExports("ggplot2")) { 18 | ggplot2::update_geom_defaults(GeomArc0, ggplot2::aes(colour = from_theme(colour %||% ink), linewidth = from_theme(linewidth), linetype = from_theme(linetype))) 19 | ggplot2::update_geom_defaults(GeomArcBar, ggplot2::aes(colour = from_theme(colour %||% ink))) 20 | ggplot2::update_geom_defaults(GeomCircle, ggplot2::aes(colour = from_theme(colour %||% ink))) 21 | ggplot2::update_geom_defaults(GeomMarkCircle, ggplot2::aes(colour = from_theme(colour %||% ink))) 22 | ggplot2::update_geom_defaults(GeomMarkEllipse, ggplot2::aes(colour = from_theme(colour %||% ink))) 23 | ggplot2::update_geom_defaults(GeomMarkHull, ggplot2::aes(colour = from_theme(colour %||% ink))) 24 | ggplot2::update_geom_defaults(GeomMarkRect, ggplot2::aes(colour = from_theme(colour %||% ink))) 25 | } 26 | 27 | ggplot2::register_theme_elements( 28 | zoom = element_rect(), 29 | zoom.x = element_rect(), 30 | zoom.y = element_rect(), 31 | element_tree = list( 32 | zoom = ggplot2::el_def('element_rect', 'strip.background'), 33 | zoom.x = ggplot2::el_def('element_rect', 'zoom'), 34 | zoom.y = ggplot2::el_def('element_rect', 'zoom') 35 | ) 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | # ggforce 16 | 17 | 18 | [![R-CMD-check](https://github.com/thomasp85/ggforce/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/thomasp85/ggforce/actions/workflows/R-CMD-check.yaml) 19 | [![CRAN_Release_Badge](http://www.r-pkg.org/badges/version-ago/ggforce)](https://CRAN.R-project.org/package=ggforce) 20 | [![CRAN_Download_Badge](http://cranlogs.r-pkg.org/badges/ggforce)](https://CRAN.R-project.org/package=ggforce) 21 | [![Codecov test coverage](https://codecov.io/gh/thomasp85/ggforce/graph/badge.svg)](https://app.codecov.io/gh/thomasp85/ggforce) 22 | 23 | 24 | *Accelerating ggplot2* 25 | 26 | `ggforce` is a package aimed at providing missing functionality to `ggplot2` 27 | through the extension system introduced with `ggplot2` v2.0.0. Broadly speaking 28 | `ggplot2` has been aimed primarily at explorative data visualization in order to 29 | investigate the data at hand, and less at providing utilities for composing 30 | custom plots a la [D3.js](https://d3js.org). `ggforce` is mainly an attempt to 31 | address these "shortcomings" (design choices might be a better description). The 32 | goal is to provide a repository of geoms, stats, etc. that are as well 33 | documented and implemented as the official ones found in `ggplot2`. 34 | 35 | ## Installation 36 | 37 | You can install the released version of ggforce from [CRAN](https://CRAN.R-project.org) with: 38 | 39 | ``` r 40 | install.packages("ggforce") 41 | ``` 42 | 43 | And the development version from [GitHub](https://github.com/) with: 44 | 45 | ``` r 46 | # install.packages("devtools") 47 | devtools::install_github("thomasp85/ggforce") 48 | ``` 49 | 50 | ## Features 51 | `ggforce` is by design a collection of features with the only commonality being 52 | their tie to the `ggplot2` API. Because of this an overview of all features 53 | would get too long for a README. The package has a [website](https://ggforce.data-imaginist.com) 54 | where every feature is described and justified with examples and plots. There 55 | should be a plot in the README of a visualization package though, so without 56 | further ado: 57 | 58 | ```{r example} 59 | library(ggforce) 60 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 61 | geom_point() + 62 | facet_zoom(x = Species == "versicolor") 63 | ``` 64 | 65 | ## Code of Conduct 66 | Please note that the 'ggforce' project is released with a 67 | [Contributor Code of Conduct](https://ggforce.data-imaginist.com/CODE_OF_CONDUCT.html). 68 | By contributing to this project, you agree to abide by its terms. 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # ggforce 5 | 6 | 7 | 8 | [![R-CMD-check](https://github.com/thomasp85/ggforce/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/thomasp85/ggforce/actions/workflows/R-CMD-check.yaml) 9 | [![CRAN_Release_Badge](http://www.r-pkg.org/badges/version-ago/ggforce)](https://CRAN.R-project.org/package=ggforce) 10 | [![CRAN_Download_Badge](http://cranlogs.r-pkg.org/badges/ggforce)](https://CRAN.R-project.org/package=ggforce) 11 | [![Codecov test 12 | coverage](https://codecov.io/gh/thomasp85/ggforce/graph/badge.svg)](https://app.codecov.io/gh/thomasp85/ggforce) 13 | 14 | 15 | *Accelerating ggplot2* 16 | 17 | `ggforce` is a package aimed at providing missing functionality to 18 | `ggplot2` through the extension system introduced with `ggplot2` v2.0.0. 19 | Broadly speaking `ggplot2` has been aimed primarily at explorative data 20 | visualization in order to investigate the data at hand, and less at 21 | providing utilities for composing custom plots a la 22 | [D3.js](https://d3js.org). `ggforce` is mainly an attempt to address 23 | these “shortcomings” (design choices might be a better description). The 24 | goal is to provide a repository of geoms, stats, etc. that are as well 25 | documented and implemented as the official ones found in `ggplot2`. 26 | 27 | ## Installation 28 | 29 | You can install the released version of ggforce from 30 | [CRAN](https://CRAN.R-project.org) with: 31 | 32 | ``` r 33 | install.packages("ggforce") 34 | ``` 35 | 36 | And the development version from [GitHub](https://github.com/) with: 37 | 38 | ``` r 39 | # install.packages("devtools") 40 | devtools::install_github("thomasp85/ggforce") 41 | ``` 42 | 43 | ## Features 44 | 45 | `ggforce` is by design a collection of features with the only 46 | commonality being their tie to the `ggplot2` API. Because of this an 47 | overview of all features would get too long for a README. The package 48 | has a [website](https://ggforce.data-imaginist.com) where every feature 49 | is described and justified with examples and plots. There should be a 50 | plot in the README of a visualization package though, so without further 51 | ado: 52 | 53 | ``` r 54 | library(ggforce) 55 | #> Loading required package: ggplot2 56 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 57 | geom_point() + 58 | facet_zoom(x = Species == "versicolor") 59 | ``` 60 | 61 | 62 | 63 | ## Code of Conduct 64 | 65 | Please note that the ‘ggforce’ project is released with a [Contributor 66 | Code of 67 | Conduct](https://ggforce.data-imaginist.com/CODE_OF_CONDUCT.html). By 68 | contributing to this project, you agree to abide by its terms. 69 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | destination: docs 2 | url: https://ggforce.data-imaginist.com 3 | 4 | authors: 5 | Thomas Lin Pedersen: 6 | href: https://data-imaginist.com 7 | 8 | template: 9 | bootstrap: 5 10 | bootswatch: sandstone 11 | 12 | navbar: 13 | left: 14 | - icon: fa-home fa-lg 15 | href: index.html 16 | - text: Reference 17 | href: reference/index.html 18 | - text: News 19 | menu: 20 | - text: "Release notes" 21 | - text: "Version 0.3.0" 22 | href: https://www.data-imaginist.com/2019/a-flurry-of-facets/ 23 | - text: "Version 0.2.0" 24 | href: https://www.data-imaginist.com/2019/the-ggforce-awakens-again/ 25 | - text: "Version 0.1.0" 26 | href: https://www.data-imaginist.com/2016/announcing-ggforce/ 27 | - text: "------------------" 28 | - text: "Change log" 29 | href: news/index.html 30 | right: 31 | - icon: fa-github fa-lg 32 | href: https://github.com/thomasp85/ggforce 33 | 34 | reference: 35 | - title: "Shapes" 36 | desc: > 37 | Shapes are, in essence, anything with volume. These geoms allow you to 38 | draw differnt types of parameterised shapes, all taking advantage of the 39 | benefit of the `geom_shape` improvements to `geom_polygon`. 40 | contents: 41 | - geom_shape 42 | - geom_circle 43 | - geom_ellipse 44 | - geom_regon 45 | - geom_arc_bar 46 | - geom_bspline_closed 47 | - geom_diagonal_wide 48 | - geom_parallel_sets 49 | - geom_voronoi_tile 50 | - title: "Lines" 51 | desc: > 52 | The different line geoms are all parameterised versions of different line 53 | types, greatly easing your pain when needing a special type of stroke. 54 | Many of them have several versions depending on whether you want to show 55 | gradients along the lines, interpolate between endpoint aesthetics, or 56 | simply have a barebone version. 57 | contents: 58 | - geom_link 59 | - geom_arc 60 | - geom_bezier 61 | - geom_bspline 62 | - geom_diagonal 63 | - geom_spiro 64 | - geom_voronoi_segment 65 | - title: "Annotation" 66 | desc: > 67 | Annotation is important for storytelling, and ggforce provides a family of 68 | geoms that makes it easy to draw attention to, and describe, features of 69 | the plot. They all work in the same way, but differ in the way they 70 | enclose the area you want to draw attention to. 71 | contents: 72 | - geom_mark_rect 73 | - geom_mark_circle 74 | - geom_mark_ellipse 75 | - geom_mark_hull 76 | - title: "Facets" 77 | desc: > 78 | Facets are one of the greatest things in ggplot2, and ggforce comes with 79 | more of the awesomeness, both with variants of `facet_grid` and 80 | `facet_wrap`, as well as completely new takes on faceting. 81 | contents: 82 | - facet_matrix 83 | - facet_zoom 84 | - facet_row 85 | - facet_wrap_paginate 86 | - facet_grid_paginate 87 | - facet_stereo 88 | - title: "Scales" 89 | desc: > 90 | While separate packages comes with different palettes for already 91 | established scales, ggforce provides two completely new ones. 92 | contents: 93 | - scale_x_unit 94 | - scale_depth 95 | - title: "Transformations" 96 | desc: > 97 | Transformations can both be used to transform scales and coordinate 98 | systems but can also be used more broadly for describing specific types of 99 | spatial transformation of data. 100 | contents: 101 | - trans_reverser 102 | - power_trans 103 | - radial_trans 104 | - linear_trans 105 | - title: "Misc" 106 | desc: > 107 | ggforce contains an assortment of various stuff that doesn't fit into a 108 | bigger bucket. That doesn't make it any less useful. 109 | contents: 110 | - geom_sina 111 | - geom_autopoint 112 | - geom_autodensity 113 | - label_tex 114 | - stat_err 115 | - position_auto 116 | - position_jitternormal 117 | - gather_set_data 118 | - n_pages 119 | - theme_no_axes 120 | - ggforce-package 121 | - ggforce-extensions 122 | 123 | figures: 124 | dev: grDevices::png 125 | 126 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | Bigger patch release addressing a range of bugs, as well as removing the 2 | concaveman, Rcpp, and RcppEigen dependency in favor of cpp11 3 | 4 | ## revdepcheck results 5 | 6 | We checked 107 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 7 | 8 | * We saw 0 new problems 9 | * We failed to check 0 packages 10 | -------------------------------------------------------------------------------- /man/facet_grid_paginate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/facet_grid_paginate.R 3 | \name{facet_grid_paginate} 4 | \alias{facet_grid_paginate} 5 | \title{Split facet_grid over multiple plots} 6 | \usage{ 7 | facet_grid_paginate( 8 | facets, 9 | margins = FALSE, 10 | scales = "fixed", 11 | space = "fixed", 12 | shrink = TRUE, 13 | labeller = "label_value", 14 | as.table = TRUE, 15 | switch = NULL, 16 | drop = TRUE, 17 | ncol = NULL, 18 | nrow = NULL, 19 | page = 1, 20 | byrow = TRUE 21 | ) 22 | } 23 | \arguments{ 24 | \item{facets}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Please use \code{rows} 25 | and \code{cols} instead.} 26 | 27 | \item{margins}{Either a logical value or a character 28 | vector. Margins are additional facets which contain all the data 29 | for each of the possible values of the faceting variables. If 30 | \code{FALSE}, no additional facets are included (the 31 | default). If \code{TRUE}, margins are included for all faceting 32 | variables. If specified as a character vector, it is the names of 33 | variables for which margins are to be created.} 34 | 35 | \item{scales}{Are scales shared across all facets (the default, 36 | \code{"fixed"}), or do they vary across rows (\code{"free_x"}), 37 | columns (\code{"free_y"}), or both rows and columns (\code{"free"})?} 38 | 39 | \item{space}{If \code{"fixed"}, the default, all panels have the same size. 40 | If \code{"free_y"} their height will be proportional to the length of the 41 | y scale; if \code{"free_x"} their width will be proportional to the 42 | length of the x scale; or if \code{"free"} both height and width will 43 | vary. This setting has no effect unless the appropriate scales also vary.} 44 | 45 | \item{shrink}{If \code{TRUE}, will shrink scales to fit output of 46 | statistics, not raw data. If \code{FALSE}, will be range of raw data 47 | before statistical summary.} 48 | 49 | \item{labeller}{A function that takes one data frame of labels and 50 | returns a list or data frame of character vectors. Each input 51 | column corresponds to one factor. Thus there will be more than 52 | one with \code{vars(cyl, am)}. Each output 53 | column gets displayed as one separate line in the strip 54 | label. This function should inherit from the "labeller" S3 class 55 | for compatibility with \code{\link[ggplot2:labeller]{labeller()}}. You can use different labeling 56 | functions for different kind of labels, for example use \code{\link[ggplot2:label_parsed]{label_parsed()}} for 57 | formatting facet labels. \code{\link[ggplot2:label_value]{label_value()}} is used by default, 58 | check it for more details and pointers to other options.} 59 | 60 | \item{as.table}{If \code{TRUE}, the default, the facets are laid out like 61 | a table with highest values at the bottom-right. If \code{FALSE}, the 62 | facets are laid out like a plot with the highest value at the top-right.} 63 | 64 | \item{switch}{By default, the labels are displayed on the top and 65 | right of the plot. If \code{"x"}, the top labels will be 66 | displayed to the bottom. If \code{"y"}, the right-hand side 67 | labels will be displayed to the left. Can also be set to 68 | \code{"both"}.} 69 | 70 | \item{drop}{If \code{TRUE}, the default, all factor levels not used in the 71 | data will automatically be dropped. If \code{FALSE}, all factor levels 72 | will be shown, regardless of whether or not they appear in the data.} 73 | 74 | \item{ncol}{Number of columns per page} 75 | 76 | \item{nrow}{Number of rows per page} 77 | 78 | \item{page}{The page to draw} 79 | 80 | \item{byrow}{Should the pages be created row-wise or column wise} 81 | } 82 | \description{ 83 | This extension to \code{\link[ggplot2:facet_grid]{ggplot2::facet_grid()}} will allow you to split 84 | a facetted plot over multiple pages. You define a number of rows and columns 85 | per page as well as the page number to plot, and the function will 86 | automatically only plot the correct panels. Usually this will be put in a 87 | loop to render all pages one by one. 88 | } 89 | \note{ 90 | If either \code{ncol} or \code{nrow} is \code{NULL} this function will 91 | fall back to the standard \code{facet_grid} functionality. 92 | } 93 | \examples{ 94 | # Draw a small section of the grid 95 | ggplot(diamonds) + 96 | geom_point(aes(carat, price), alpha = 0.1) + 97 | facet_grid_paginate(color ~ cut:clarity, ncol = 3, nrow = 3, page = 4) 98 | } 99 | \seealso{ 100 | \code{\link[=n_pages]{n_pages()}} to compute the total number of pages in a paginated 101 | faceted plot 102 | 103 | Other ggforce facets: 104 | \code{\link{facet_stereo}()}, 105 | \code{\link{facet_wrap_paginate}()}, 106 | \code{\link{facet_zoom}()} 107 | } 108 | \concept{ggforce facets} 109 | -------------------------------------------------------------------------------- /man/facet_matrix.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/facet_matrix.R 3 | \name{facet_matrix} 4 | \alias{facet_matrix} 5 | \title{Facet by different data columns} 6 | \usage{ 7 | facet_matrix( 8 | rows, 9 | cols = rows, 10 | shrink = TRUE, 11 | switch = NULL, 12 | labeller = "label_value", 13 | flip.rows = FALSE, 14 | alternate.axes = FALSE, 15 | layer.lower = NULL, 16 | layer.diag = NULL, 17 | layer.upper = NULL, 18 | layer.continuous = NULL, 19 | layer.discrete = NULL, 20 | layer.mixed = NULL, 21 | grid.y.diag = TRUE 22 | ) 23 | } 24 | \arguments{ 25 | \item{rows, cols}{A specification of the data columns to put in the rows and 26 | columns of the facet grid. They are specified using the \code{\link[ggplot2:vars]{ggplot2::vars()}} 27 | function wherein you can use standard tidyselect syntax as known from e.g. 28 | \code{dplyr::select()}. These data values will be made available to the different 29 | layers through the \code{.panel_x} and \code{.panel_y} variables.} 30 | 31 | \item{shrink}{If \code{TRUE}, will shrink scales to fit output of 32 | statistics, not raw data. If \code{FALSE}, will be range of raw data 33 | before statistical summary.} 34 | 35 | \item{switch}{By default, the labels are displayed on the top and 36 | right of the plot. If \code{"x"}, the top labels will be 37 | displayed to the bottom. If \code{"y"}, the right-hand side 38 | labels will be displayed to the left. Can also be set to 39 | \code{"both"}.} 40 | 41 | \item{labeller}{A function that takes one data frame of labels and 42 | returns a list or data frame of character vectors. Each input 43 | column corresponds to one factor. Thus there will be more than 44 | one with \code{vars(cyl, am)}. Each output 45 | column gets displayed as one separate line in the strip 46 | label. This function should inherit from the "labeller" S3 class 47 | for compatibility with \code{\link[ggplot2:labeller]{labeller()}}. You can use different labeling 48 | functions for different kind of labels, for example use \code{\link[ggplot2:label_parsed]{label_parsed()}} for 49 | formatting facet labels. \code{\link[ggplot2:label_value]{label_value()}} is used by default, 50 | check it for more details and pointers to other options.} 51 | 52 | \item{flip.rows}{Should the order of the rows be reversed so that, if the 53 | rows and columns are equal, the diagonal goes from bottom-left to top-right 54 | instead of top-left to bottom-right.} 55 | 56 | \item{alternate.axes}{Should axes be drawn at alternating positions.} 57 | 58 | \item{layer.lower, layer.diag, layer.upper}{Specification for where each layer 59 | should appear. The default (\code{NULL}) will allow any layer that has not been 60 | specified directly to appear at that position. Putting e.g. \code{layer.diag = 2} 61 | will make the second layer appear on the diagonal as well as remove that 62 | layer from any position that has \code{NULL}. Using \code{TRUE} will put all layers at 63 | that position, and using \code{FALSE} will conversely remove all layers. These 64 | settings will only have an effect if the grid is symmetric.} 65 | 66 | \item{layer.continuous, layer.discrete, layer.mixed}{As above, but instead of 67 | referencing panel positions it references the combination of position scales 68 | in the panel. Continuous panels have both a continuous x and y axis, discrete 69 | panels have both a discrete x and y axis, and mixed panels have one of each. 70 | Unlike the position based specifications above these also have an effect in 71 | non-symmetric grids.} 72 | 73 | \item{grid.y.diag}{Should the y grid be removed from the diagonal? In certain 74 | situations the diagonal are used to plot the distribution of the column data 75 | and will thus not use the y-scale. Removing the y gridlines can indicate 76 | this.} 77 | } 78 | \description{ 79 | The \code{facet_matrix()} facet allows you to put different data columns into 80 | different rows and columns in a grid of panels. If the same data columns are 81 | present in both the rows and the columns of the grid, and used together with 82 | \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}} it is also known as a scatterplot matrix, and if 83 | other geoms are used it is sometimes referred to as a pairs plot. 84 | \code{facet_matrix} is so flexible that these types are simply a subset of its 85 | capabilities, as any combination of data columns can be plotted against each 86 | other using any type of geom. Layers should use the \code{.panel_x} and \code{.panel_y} 87 | placeholders to map aesthetics to, in order to access the row and column 88 | data. 89 | } 90 | \note{ 91 | Due to the special nature of this faceting it slightly breaks the 92 | ggplot2 API, in that any positional scale settings are ignored. This is 93 | because each row and column in the grid will potentially have very different 94 | scale types and it is not currently possible to have multiple different scale 95 | specifications in the same plot object. 96 | } 97 | \examples{ 98 | # Standard use: 99 | ggplot(mpg) + 100 | geom_point(aes(x = .panel_x, y = .panel_y)) + 101 | facet_matrix(vars(displ, cty, hwy)) 102 | 103 | # Switch the diagonal, alternate the axes and style strips as axis labels 104 | ggplot(mpg) + 105 | geom_point(aes(x = .panel_x, y = .panel_y)) + 106 | facet_matrix(vars(displ, cty, hwy), flip.rows = TRUE, 107 | alternate.axes = TRUE, switch = 'both') + 108 | theme(strip.background = element_blank(), 109 | strip.placement = 'outside', 110 | strip.text = element_text(size = 12)) 111 | 112 | # Mix discrete and continuous columns. Use geom_autopoint for scale-based jitter 113 | ggplot(mpg) + 114 | geom_autopoint() + 115 | facet_matrix(vars(drv:fl)) 116 | 117 | # Have a special diagonal layer 118 | ggplot(mpg) + 119 | geom_autopoint() + 120 | geom_autodensity() + 121 | facet_matrix(vars(drv:fl), layer.diag = 2) 122 | 123 | \donttest{ 124 | # Show continuous panels in upper triangle as contours and rest as binned 125 | ggplot(mpg) + 126 | geom_autopoint() + 127 | geom_autodensity() + 128 | geom_density2d(aes(x = .panel_x, y = .panel_y)) + 129 | geom_bin2d(aes(x = .panel_x, y = .panel_y)) + 130 | facet_matrix(vars(drv:fl), layer.lower = 1, layer.diag = 2, 131 | layer.continuous = -4, layer.discrete = -3, layer.mixed = -3) 132 | } 133 | 134 | # Make asymmetric grid 135 | ggplot(mpg) + 136 | geom_boxplot(aes(x = .panel_x, y = .panel_y, group = .panel_x)) + 137 | facet_matrix(rows = vars(cty, hwy), cols = vars(drv, fl)) 138 | 139 | } 140 | \seealso{ 141 | \link{geom_autopoint}, \link{geom_autohistogram}, \link{geom_autodensity}, and 142 | \link{position_auto} for geoms and positions that adapts to different positional 143 | scale types 144 | } 145 | -------------------------------------------------------------------------------- /man/facet_row.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/facet_row.R 3 | \name{facet_row} 4 | \alias{facet_row} 5 | \alias{facet_col} 6 | \title{One-dimensional facets} 7 | \usage{ 8 | facet_row( 9 | facets, 10 | scales = "fixed", 11 | space = "fixed", 12 | shrink = TRUE, 13 | labeller = "label_value", 14 | drop = TRUE, 15 | strip.position = "top" 16 | ) 17 | 18 | facet_col( 19 | facets, 20 | scales = "fixed", 21 | space = "fixed", 22 | shrink = TRUE, 23 | labeller = "label_value", 24 | drop = TRUE, 25 | strip.position = "top" 26 | ) 27 | } 28 | \arguments{ 29 | \item{facets}{A set of variables or expressions quoted by \code{\link[ggplot2:vars]{vars()}} 30 | and defining faceting groups on the rows or columns dimension. 31 | The variables can be named (the names are passed to \code{labeller}). 32 | 33 | For compatibility with the classic interface, can also be a 34 | formula or character vector. Use either a one sided formula, \code{~a + b}, 35 | or a character vector, \code{c("a", "b")}.} 36 | 37 | \item{scales}{Should scales be fixed (\code{"fixed"}, the default), 38 | free (\code{"free"}), or free in one dimension (\code{"free_x"}, 39 | \code{"free_y"})?} 40 | 41 | \item{space}{Should the size of the panels be fixed or relative to the range 42 | of the respective position scales} 43 | 44 | \item{shrink}{If \code{TRUE}, will shrink scales to fit output of 45 | statistics, not raw data. If \code{FALSE}, will be range of raw data 46 | before statistical summary.} 47 | 48 | \item{labeller}{A function that takes one data frame of labels and 49 | returns a list or data frame of character vectors. Each input 50 | column corresponds to one factor. Thus there will be more than 51 | one with \code{vars(cyl, am)}. Each output 52 | column gets displayed as one separate line in the strip 53 | label. This function should inherit from the "labeller" S3 class 54 | for compatibility with \code{\link[ggplot2:labeller]{labeller()}}. You can use different labeling 55 | functions for different kind of labels, for example use \code{\link[ggplot2:label_parsed]{label_parsed()}} for 56 | formatting facet labels. \code{\link[ggplot2:label_value]{label_value()}} is used by default, 57 | check it for more details and pointers to other options.} 58 | 59 | \item{drop}{If \code{TRUE}, the default, all factor levels not used in the 60 | data will automatically be dropped. If \code{FALSE}, all factor levels 61 | will be shown, regardless of whether or not they appear in the data.} 62 | 63 | \item{strip.position}{By default, the labels are displayed on the top of 64 | the plot. Using \code{strip.position} it is possible to place the labels on 65 | either of the four sides by setting \code{strip.position = c("top", 66 | "bottom", "left", "right")}} 67 | } 68 | \description{ 69 | These facets are one-dimensional versions of \code{\link[ggplot2:facet_wrap]{ggplot2::facet_wrap()}}, 70 | arranging the panels in either a single row or a single column. This 71 | restriction makes it possible to support a \code{space} argument as seen in 72 | \code{\link[ggplot2:facet_grid]{ggplot2::facet_grid()}} which, if set to \code{"free"} will allow the panels to be 73 | sized based on the relative range of their scales. Another way of thinking 74 | about them are one-dimensional versions of \code{\link[ggplot2:facet_grid]{ggplot2::facet_grid()}} (ie. 75 | \code{. ~ {var}} or \code{{var} ~ .}), but with the ability to position the strip at 76 | either side of the panel. However you look at it it is the best of both world 77 | if you just need one dimension. 78 | } 79 | \examples{ 80 | # Standard use 81 | ggplot(mtcars) + 82 | geom_point(aes(disp, mpg)) + 83 | facet_col(~gear) 84 | # It retains the ability to have unique scales for each panel 85 | ggplot(mtcars) + 86 | geom_point(aes(disp, mpg)) + 87 | facet_col(~gear, scales = 'free') 88 | 89 | # But can have free sizing along the stacking dimension 90 | ggplot(mtcars) + 91 | geom_point(aes(disp, mpg)) + 92 | facet_col(~gear, scales = 'free', space = 'free') 93 | 94 | # And you can position the strip where-ever you like 95 | ggplot(mtcars) + 96 | geom_point(aes(disp, mpg)) + 97 | facet_col(~gear, scales = 'free', space = 'free', strip.position = 'bottom') 98 | 99 | } 100 | -------------------------------------------------------------------------------- /man/facet_stereo.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/facet_stereo.R 3 | \name{facet_stereo} 4 | \alias{facet_stereo} 5 | \title{Create a stereogram plot} 6 | \usage{ 7 | facet_stereo(IPD = 63.5, panel.size = 200, shrink = TRUE) 8 | } 9 | \arguments{ 10 | \item{IPD}{The interpupillary distance (in mm) used for calculating point 11 | displacement. The default value is an average of both genders} 12 | 13 | \item{panel.size}{The final plot size in mm. As IPD this is used to calculate 14 | point displacement. Don't take this value too literal but experiment until 15 | you get a nice effect. Lower values gives higher displacement and thus 16 | require the plots to be observed from a closer distance} 17 | 18 | \item{shrink}{If \code{TRUE}, will shrink scales to fit output of 19 | statistics, not raw data. If \code{FALSE}, will be range of raw data 20 | before statistical summary.} 21 | } 22 | \description{ 23 | This, arguably pretty useless function, lets you create plots with a sense of 24 | depth by creating two slightly different versions of the plot that 25 | corresponds to how the eyes would see it if the plot was 3 dimensional. To 26 | experience the effect look at the plots through 3D hardware such as Google 27 | Cardboard or by relaxing the eyes and focusing into the distance. The 28 | depth of a point is calculated for layers having a depth aesthetic supplied. 29 | The scaling of the depth can be controlled with \code{\link[=scale_depth]{scale_depth()}} as 30 | you would control any aesthetic. Negative values will result in features 31 | placed behind the paper plane, while positive values will result in 32 | features hovering in front of the paper. While features within each layer is 33 | sorted so those closest to you are plotted on top of those more distant, this 34 | cannot be done between layers. Thus, layers are always plotted on top of 35 | each others, even if the features in one layer lies behind features in a 36 | layer behind it. The depth experience is inaccurate and should not be used 37 | for conveying important data. Regard this more as a party-trick... 38 | } 39 | \examples{ 40 | # You'll have to accept a warning about depth being an unknown aesthetic 41 | ggplot(mtcars) + 42 | geom_point(aes(mpg, disp, depth = cyl)) + 43 | facet_stereo() 44 | } 45 | \seealso{ 46 | Other ggforce facets: 47 | \code{\link{facet_grid_paginate}()}, 48 | \code{\link{facet_wrap_paginate}()}, 49 | \code{\link{facet_zoom}()} 50 | } 51 | \concept{ggforce facets} 52 | -------------------------------------------------------------------------------- /man/facet_wrap_paginate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/facet_wrap_paginate.R 3 | \name{facet_wrap_paginate} 4 | \alias{facet_wrap_paginate} 5 | \title{Split facet_wrap over multiple plots} 6 | \usage{ 7 | facet_wrap_paginate( 8 | facets, 9 | nrow = NULL, 10 | ncol = NULL, 11 | scales = "fixed", 12 | shrink = TRUE, 13 | labeller = "label_value", 14 | as.table = TRUE, 15 | switch = deprecated(), 16 | drop = TRUE, 17 | dir = "h", 18 | strip.position = "top", 19 | page = 1 20 | ) 21 | } 22 | \arguments{ 23 | \item{facets}{A set of variables or expressions quoted by \code{\link[ggplot2:vars]{vars()}} 24 | and defining faceting groups on the rows or columns dimension. 25 | The variables can be named (the names are passed to \code{labeller}). 26 | 27 | For compatibility with the classic interface, can also be a 28 | formula or character vector. Use either a one sided formula, \code{~a + b}, 29 | or a character vector, \code{c("a", "b")}.} 30 | 31 | \item{nrow, ncol}{Number of rows and columns} 32 | 33 | \item{scales}{Should scales be fixed (\code{"fixed"}, the default), 34 | free (\code{"free"}), or free in one dimension (\code{"free_x"}, 35 | \code{"free_y"})?} 36 | 37 | \item{shrink}{If \code{TRUE}, will shrink scales to fit output of 38 | statistics, not raw data. If \code{FALSE}, will be range of raw data 39 | before statistical summary.} 40 | 41 | \item{labeller}{A function that takes one data frame of labels and 42 | returns a list or data frame of character vectors. Each input 43 | column corresponds to one factor. Thus there will be more than 44 | one with \code{vars(cyl, am)}. Each output 45 | column gets displayed as one separate line in the strip 46 | label. This function should inherit from the "labeller" S3 class 47 | for compatibility with \code{\link[ggplot2:labeller]{labeller()}}. You can use different labeling 48 | functions for different kind of labels, for example use \code{\link[ggplot2:label_parsed]{label_parsed()}} for 49 | formatting facet labels. \code{\link[ggplot2:label_value]{label_value()}} is used by default, 50 | check it for more details and pointers to other options.} 51 | 52 | \item{as.table}{If \code{TRUE}, the default, the facets are laid out like 53 | a table with highest values at the bottom-right. If \code{FALSE}, the 54 | facets are laid out like a plot with the highest value at the top-right.} 55 | 56 | \item{switch}{By default, the labels are displayed on the top and 57 | right of the plot. If \code{"x"}, the top labels will be 58 | displayed to the bottom. If \code{"y"}, the right-hand side 59 | labels will be displayed to the left. Can also be set to 60 | \code{"both"}.} 61 | 62 | \item{drop}{If \code{TRUE}, the default, all factor levels not used in the 63 | data will automatically be dropped. If \code{FALSE}, all factor levels 64 | will be shown, regardless of whether or not they appear in the data.} 65 | 66 | \item{dir}{Direction: either \code{"h"} for horizontal, the default, or \code{"v"}, 67 | for vertical.} 68 | 69 | \item{strip.position}{By default, the labels are displayed on the top of 70 | the plot. Using \code{strip.position} it is possible to place the labels on 71 | either of the four sides by setting \code{strip.position = c("top", 72 | "bottom", "left", "right")}} 73 | 74 | \item{page}{The page to draw} 75 | } 76 | \description{ 77 | This extension to \code{\link[ggplot2:facet_wrap]{ggplot2::facet_wrap()}} will allow you to split 78 | a facetted plot over multiple pages. You define a number of rows and columns 79 | per page as well as the page number to plot, and the function will 80 | automatically only plot the correct panels. Usually this will be put in a 81 | loop to render all pages one by one. 82 | } 83 | \note{ 84 | If either \code{ncol} or \code{nrow} is \code{NULL} this function will 85 | fall back to the standard \code{facet_wrap} functionality. 86 | } 87 | \examples{ 88 | ggplot(diamonds) + 89 | geom_point(aes(carat, price), alpha = 0.1) + 90 | facet_wrap_paginate(~ cut:clarity, ncol = 3, nrow = 3, page = 4) 91 | 92 | } 93 | \seealso{ 94 | \code{\link[=n_pages]{n_pages()}} to compute the total number of pages in a paginated 95 | faceted plot 96 | 97 | Other ggforce facets: 98 | \code{\link{facet_grid_paginate}()}, 99 | \code{\link{facet_stereo}()}, 100 | \code{\link{facet_zoom}()} 101 | } 102 | \concept{ggforce facets} 103 | -------------------------------------------------------------------------------- /man/facet_zoom.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/facet_zoom.R 3 | \name{facet_zoom} 4 | \alias{facet_zoom} 5 | \title{Facet data for zoom with context} 6 | \usage{ 7 | facet_zoom( 8 | x, 9 | y, 10 | xy, 11 | zoom.data, 12 | xlim = NULL, 13 | ylim = NULL, 14 | split = FALSE, 15 | horizontal = TRUE, 16 | zoom.size = 2, 17 | show.area = TRUE, 18 | shrink = TRUE 19 | ) 20 | } 21 | \arguments{ 22 | \item{x, y, xy}{An expression evaluating to a logical vector that determines 23 | the subset of data to zoom in on} 24 | 25 | \item{zoom.data}{An expression evaluating to a logical vector. If \code{TRUE} 26 | the data only shows in the zoom panels. If \code{FALSE} the data only show in 27 | the context panel. If \code{NA} the data will show in all panels.} 28 | 29 | \item{xlim, ylim}{Specific zoom ranges for each axis. If present they will 30 | override \code{x}, \code{y}, and/or \code{xy}.} 31 | 32 | \item{split}{If both \code{x} and \code{y} is given, should each axis zoom 33 | be shown separately as well? Defaults to \code{FALSE}} 34 | 35 | \item{horizontal}{If both \code{x} and \code{y} is given and 36 | \code{split = FALSE} How should the zoom panel be positioned relative to the 37 | full data panel? Defaults to \code{TRUE}} 38 | 39 | \item{zoom.size}{Sets the relative size of the zoom panel to the full data 40 | panel. The default (\code{2}) makes the zoom panel twice the size of the full 41 | data panel.} 42 | 43 | \item{show.area}{Should the zoom area be drawn below the data points on the 44 | full data panel? Defaults to \code{TRUE}.} 45 | 46 | \item{shrink}{If \code{TRUE}, will shrink scales to fit output of 47 | statistics, not raw data. If \code{FALSE}, will be range of raw data 48 | before statistical summary.} 49 | } 50 | \description{ 51 | This facetting provides the means to zoom in on a subset of the data, while 52 | keeping the view of the full dataset as a separate panel. The zoomed-in area 53 | will be indicated on the full dataset panel for reference. It is possible to 54 | zoom in on both the x and y axis at the same time. If this is done it is 55 | possible to both get each zoom separately and combined or just combined. 56 | } 57 | \examples{ 58 | # Zoom in on the versicolor species on the x-axis 59 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 60 | geom_point() + 61 | facet_zoom(x = Species == 'versicolor') 62 | 63 | # Zoom in on versicolor on both axes 64 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 65 | geom_point() + 66 | facet_zoom(xy = Species == 'versicolor') 67 | 68 | # Use different zoom criteria on each axis 69 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 70 | geom_point() + 71 | facet_zoom(x = Species != 'setosa', y = Species == 'versicolor') 72 | 73 | # Get each axis zoom separately as well 74 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 75 | geom_point() + 76 | facet_zoom(xy = Species == 'versicolor', split = TRUE) 77 | 78 | # Define the zoom area directly 79 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 80 | geom_point() + 81 | facet_zoom(xlim = c(2, 4)) 82 | 83 | # Selectively show data in the zoom panel 84 | ggplot(iris, aes(Petal.Length, Petal.Width, colour = Species)) + 85 | geom_point() + 86 | facet_zoom(x = Species == 'versicolor', zoom.data = Species == 'versicolor') 87 | } 88 | \seealso{ 89 | Other ggforce facets: 90 | \code{\link{facet_grid_paginate}()}, 91 | \code{\link{facet_stereo}()}, 92 | \code{\link{facet_wrap_paginate}()} 93 | } 94 | \concept{ggforce facets} 95 | -------------------------------------------------------------------------------- /man/figures/README-example-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/man/figures/README-example-1.png -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclearchivedarchived -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledefunctdefunct -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledeprecateddeprecated -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycleexperimentalexperimental -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclematuringmaturing -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclequestioningquestioning -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclestablestable -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesupersededsuperseded -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/man/figures/logo.png -------------------------------------------------------------------------------- /man/gather_set_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/parallel_sets.R 3 | \name{gather_set_data} 4 | \alias{gather_set_data} 5 | \title{Tidy data for use with geom_parallel_sets} 6 | \usage{ 7 | gather_set_data(data, x, id_name = "id") 8 | } 9 | \arguments{ 10 | \item{data}{A tidy dataframe with some categorical columns} 11 | 12 | \item{x}{The columns to use for axes in the parallel sets diagram} 13 | 14 | \item{id_name}{The name of the column that will contain the original index of 15 | the row.} 16 | } 17 | \value{ 18 | A data.frame 19 | } 20 | \description{ 21 | This helper function makes it easy to change tidy data into a tidy(er) format 22 | that can be used by geom_parallel_sets. 23 | } 24 | \examples{ 25 | data <- reshape2::melt(Titanic) 26 | head(gather_set_data(data, 1:4)) 27 | head(gather_set_data(data, c("Class","Sex","Age","Survived"))) 28 | } 29 | -------------------------------------------------------------------------------- /man/geom_autopoint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/autopoint.R 3 | \name{geom_autopoint} 4 | \alias{geom_autopoint} 5 | \title{A point geom specialised for scatterplot matrices} 6 | \usage{ 7 | geom_autopoint( 8 | mapping = NULL, 9 | data = NULL, 10 | stat = "identity", 11 | position = "auto", 12 | ..., 13 | na.rm = FALSE, 14 | show.legend = NA, 15 | inherit.aes = TRUE 16 | ) 17 | } 18 | \arguments{ 19 | \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}}. If specified and 20 | \code{inherit.aes = TRUE} (the default), it is combined with the default mapping 21 | at the top level of the plot. You must supply \code{mapping} if there is no plot 22 | mapping.} 23 | 24 | \item{data}{The data to be displayed in this layer. There are three 25 | options: 26 | 27 | If \code{NULL}, the default, the data is inherited from the plot 28 | data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. 29 | 30 | A \code{data.frame}, or other object, will override the plot 31 | data. All objects will be fortified to produce a data frame. See 32 | \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. 33 | 34 | A \code{function} will be called with a single argument, 35 | the plot data. The return value must be a \code{data.frame}, and 36 | will be used as the layer data. A \code{function} can be created 37 | from a \code{formula} (e.g. \code{~ head(.x, 10)}).} 38 | 39 | \item{stat}{The statistical transformation to use on the data for this layer. 40 | When using a \verb{geom_*()} function to construct a layer, the \code{stat} 41 | argument can be used the override the default coupling between geoms and 42 | stats. The \code{stat} argument accepts the following: 43 | \itemize{ 44 | \item A \code{Stat} ggproto subclass, for example \code{StatCount}. 45 | \item A string naming the stat. To give the stat as a string, strip the 46 | function name of the \code{stat_} prefix. For example, to use \code{stat_count()}, 47 | give the stat as \code{"count"}. 48 | \item For more information and other ways to specify the stat, see the 49 | \link[ggplot2:layer_stats]{layer stat} documentation. 50 | }} 51 | 52 | \item{position}{A position adjustment to use on the data for this layer. This 53 | can be used in various ways, including to prevent overplotting and 54 | improving the display. The \code{position} argument accepts the following: 55 | \itemize{ 56 | \item The result of calling a position function, such as \code{position_jitter()}. 57 | This method allows for passing extra arguments to the position. 58 | \item A string naming the position adjustment. To give the position as a 59 | string, strip the function name of the \code{position_} prefix. For example, 60 | to use \code{position_jitter()}, give the position as \code{"jitter"}. 61 | \item For more information and other ways to specify the position, see the 62 | \link[ggplot2:layer_positions]{layer position} documentation. 63 | }} 64 | 65 | \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}'s \code{params} argument. These 66 | arguments broadly fall into one of 4 categories below. Notably, further 67 | arguments to the \code{position} argument, or aesthetics that are required 68 | can \emph{not} be passed through \code{...}. Unknown arguments that are not part 69 | of the 4 categories below are ignored. 70 | \itemize{ 71 | \item Static aesthetics that are not mapped to a scale, but are at a fixed 72 | value and apply to the layer as a whole. For example, \code{colour = "red"} 73 | or \code{linewidth = 3}. The geom's documentation has an \strong{Aesthetics} 74 | section that lists the available options. The 'required' aesthetics 75 | cannot be passed on to the \code{params}. Please note that while passing 76 | unmapped aesthetics as vectors is technically possible, the order and 77 | required length is not guaranteed to be parallel to the input data. 78 | \item When constructing a layer using 79 | a \verb{stat_*()} function, the \code{...} argument can be used to pass on 80 | parameters to the \code{geom} part of the layer. An example of this is 81 | \code{stat_density(geom = "area", outline.type = "both")}. The geom's 82 | documentation lists which parameters it can accept. 83 | \item Inversely, when constructing a layer using a 84 | \verb{geom_*()} function, the \code{...} argument can be used to pass on parameters 85 | to the \code{stat} part of the layer. An example of this is 86 | \code{geom_area(stat = "density", adjust = 0.5)}. The stat's documentation 87 | lists which parameters it can accept. 88 | \item The \code{key_glyph} argument of \code{\link[ggplot2:layer]{layer()}} may also be passed on through 89 | \code{...}. This can be one of the functions described as 90 | \link[ggplot2:draw_key]{key glyphs}, to change the display of the layer in the legend. 91 | }} 92 | 93 | \item{na.rm}{If \code{FALSE}, the default, missing values are removed with 94 | a warning. If \code{TRUE}, missing values are silently removed.} 95 | 96 | \item{show.legend}{logical. Should this layer be included in the legends? 97 | \code{NA}, the default, includes if any aesthetics are mapped. 98 | \code{FALSE} never includes, and \code{TRUE} always includes. 99 | It can also be a named logical vector to finely select the aesthetics to 100 | display.} 101 | 102 | \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, 103 | rather than combining with them. This is most useful for helper functions 104 | that define both data and aesthetics and shouldn't inherit behaviour from 105 | the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} 106 | } 107 | \description{ 108 | This geom is a specialisation of \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}} with two changes. It 109 | defaults to mapping \code{x} and \code{y} to \code{.panel_x} and \code{.panel_y} respectively, 110 | and it defaults to using \code{\link[=position_auto]{position_auto()}} to jitter the points based on the 111 | combination of position scale types. 112 | } 113 | \examples{ 114 | # Continuous vs continuous: No jitter 115 | ggplot(mpg) + geom_autopoint(aes(cty, hwy)) 116 | 117 | # Continuous vs discrete: sina jitter 118 | ggplot(mpg) + geom_autopoint(aes(cty, drv)) 119 | 120 | # Discrete vs discrete: disc-jitter 121 | ggplot(mpg) + geom_autopoint(aes(fl, drv)) 122 | 123 | # Used with facet_matrix (x and y are automatically mapped) 124 | ggplot(mpg) + 125 | geom_autopoint() + 126 | facet_matrix(vars(drv:fl)) 127 | 128 | } 129 | \seealso{ 130 | \link{facet_matrix} for how to lay out scatterplot matrices and 131 | \link{position_auto} for information about the position adjustments 132 | } 133 | -------------------------------------------------------------------------------- /man/ggforce-extensions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shape.R, R/arc_bar.R, R/arc.R, R/autodensity.R, 3 | % R/autohistogram.R, R/bezier.R, R/bspline.R, R/bspline_closed.R, 4 | % R/circle.R, R/diagonal.R, R/diagonal_wide.R, R/ellipse.R, R/errorbar.R, 5 | % R/facet_grid_paginate.R, R/facet_matrix.R, R/facet_row.R, R/facet_stereo.R, 6 | % R/facet_wrap_paginate.R, R/facet_zoom.R, R/ggproto-classes.R, 7 | % R/interpolate.R, R/link.R, R/mark_circle.R, R/mark_ellipse.R, 8 | % R/mark_hull.R, R/mark_rect.R, R/parallel_sets.R, R/position-jitternormal.R, 9 | % R/position_auto.R, R/position_floatstack.R, R/regon.R, R/sina.R, R/spiro.R, 10 | % R/voronoi.R 11 | \docType{data} 12 | \name{GeomShape} 13 | \alias{GeomShape} 14 | \alias{StatArcBar} 15 | \alias{StatPie} 16 | \alias{GeomArcBar} 17 | \alias{StatArc} 18 | \alias{GeomArc} 19 | \alias{StatArc2} 20 | \alias{StatArc0} 21 | \alias{GeomArc0} 22 | \alias{StatAutodensity} 23 | \alias{GeomAutoarea} 24 | \alias{StatAutobin} 25 | \alias{GeomAutorect} 26 | \alias{StatBezier} 27 | \alias{StatBezier2} 28 | \alias{StatBezier0} 29 | \alias{GeomBezier0} 30 | \alias{StatBspline} 31 | \alias{StatBspline2} 32 | \alias{GeomBspline0} 33 | \alias{GeomBsplineClosed0} 34 | \alias{StatCircle} 35 | \alias{GeomCircle} 36 | \alias{StatDiagonal} 37 | \alias{StatDiagonal2} 38 | \alias{StatDiagonal0} 39 | \alias{StatDiagonalWide} 40 | \alias{StatEllip} 41 | \alias{StatErr} 42 | \alias{FacetGridPaginate} 43 | \alias{FacetMatrix} 44 | \alias{FacetRow} 45 | \alias{FacetCol} 46 | \alias{FacetStereo} 47 | \alias{FacetWrapPaginate} 48 | \alias{FacetZoom} 49 | \alias{ggforce-extensions} 50 | \alias{GeomPathInterpolate} 51 | \alias{StatLink} 52 | \alias{StatLink2} 53 | \alias{GeomMarkCircle} 54 | \alias{GeomMarkEllipse} 55 | \alias{GeomMarkHull} 56 | \alias{GeomMarkRect} 57 | \alias{StatParallelSets} 58 | \alias{StatParallelSetsAxes} 59 | \alias{GeomParallelSetsAxes} 60 | \alias{PositionJitterNormal} 61 | \alias{PositionAuto} 62 | \alias{PositionFloatstack} 63 | \alias{StatRegon} 64 | \alias{StatSina} 65 | \alias{StatSpiro} 66 | \alias{StatVoronoiTile} 67 | \alias{StatVoronoiSegment} 68 | \alias{StatDelaunayTile} 69 | \alias{StatDelaunaySegment} 70 | \alias{StatDelaunaySegment2} 71 | \alias{StatDelvorSummary} 72 | \title{ggforce extensions to ggplot2} 73 | \description{ 74 | ggforce makes heavy use of the ggproto class system to extend the 75 | functionality of ggplot2. In general the actual classes should be of little 76 | interest to users as the standard ggplot2 api of using geom_* and stat_* 77 | functions for building up the plot is encouraged. 78 | } 79 | \keyword{datasets} 80 | -------------------------------------------------------------------------------- /man/ggforce-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ggforce-package.R 3 | \docType{package} 4 | \name{ggforce-package} 5 | \alias{ggforce} 6 | \alias{ggforce-package} 7 | \title{ggforce: Accelerating 'ggplot2'} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | The aim of 'ggplot2' is to aid in visual data investigations. This focus has led to a lack of facilities for composing specialised plots. 'ggforce' aims to be a collection of mainly new stats and geoms that fills this gap. All additional functionality is aimed to come through the official extension system so using 'ggforce' should be a stable experience. 12 | } 13 | \examples{ 14 | rocketData <- data.frame( 15 | x = c(1, 1, 2, 2), 16 | y = c(1, 2, 2, 3) 17 | ) 18 | rocketData <- do.call(rbind, lapply(seq_len(500) - 1, function(i) { 19 | rocketData$y <- rocketData$y - c(0, i / 500) 20 | rocketData$group <- i + 1 21 | rocketData 22 | })) 23 | rocketData2 <- data.frame( 24 | x = c(2, 2.25, 2), 25 | y = c(2, 2.5, 3) 26 | ) 27 | rocketData2 <- do.call(rbind, lapply(seq_len(500) - 1, function(i) { 28 | rocketData2$x[2] <- rocketData2$x[2] - i * 0.25 / 500 29 | rocketData2$group <- i + 1 + 500 30 | rocketData2 31 | })) 32 | 33 | ggplot() + geom_link(aes( 34 | x = 2, y = 2, xend = 3, yend = 3, alpha = after_stat(index), 35 | size = after_stat(index) 36 | ), colour = 'goldenrod', n = 500) + 37 | geom_bezier(aes(x = x, y = y, group = group, colour = after_stat(index)), 38 | data = rocketData 39 | ) + 40 | geom_bezier(aes(x = y, y = x, group = group, colour = after_stat(index)), 41 | data = rocketData 42 | ) + 43 | geom_bezier(aes(x = x, y = y, group = group, colour = 1), 44 | data = rocketData2 45 | ) + 46 | geom_bezier(aes(x = y, y = x, group = group, colour = 1), 47 | data = rocketData2 48 | ) + 49 | geom_text(aes(x = 1.65, y = 1.65, label = 'ggplot2', angle = 45), 50 | colour = 'white', size = 15 51 | ) + 52 | coord_fixed() + 53 | scale_x_reverse() + 54 | scale_y_reverse() + 55 | scale_alpha(range = c(1, 0), guide = 'none') + 56 | scale_size_continuous( 57 | range = c(20, 0.1), trans = 'exp', 58 | guide = 'none' 59 | ) + 60 | scale_color_continuous(guide = 'none') + 61 | xlab('') + ylab('') + 62 | ggtitle('ggforce: Accelerating ggplot2') + 63 | theme(plot.title = element_text(size = 20)) 64 | } 65 | \seealso{ 66 | Useful links: 67 | \itemize{ 68 | \item \url{https://ggforce.data-imaginist.com} 69 | \item \url{https://github.com/thomasp85/ggforce} 70 | \item Report bugs at \url{https://github.com/thomasp85/ggforce/issues} 71 | } 72 | 73 | } 74 | \author{ 75 | \strong{Maintainer}: Thomas Lin Pedersen \email{thomasp85@gmail.com} (\href{https://orcid.org/0000-0002-5147-4711}{ORCID}) 76 | 77 | Other contributors: 78 | \itemize{ 79 | \item RStudio [copyright holder] 80 | } 81 | 82 | } 83 | \keyword{internal} 84 | -------------------------------------------------------------------------------- /man/interpolateDataFrame.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/interpolate.R 3 | \name{interpolateDataFrame} 4 | \alias{interpolateDataFrame} 5 | \title{Interpolate layer data} 6 | \usage{ 7 | interpolateDataFrame(data) 8 | } 9 | \arguments{ 10 | \item{data}{A data.frame with data for a layer} 11 | } 12 | \value{ 13 | A similar data.frame with NA values interpolated 14 | } 15 | \description{ 16 | Interpolate layer data 17 | } 18 | \keyword{internal} 19 | -------------------------------------------------------------------------------- /man/label_tex.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/labeller.R 3 | \name{label_tex} 4 | \alias{label_tex} 5 | \title{A labeller function to parse TeX syntax} 6 | \usage{ 7 | label_tex(labels, ...) 8 | } 9 | \arguments{ 10 | \item{labels}{Data frame of labels. Usually contains only one 11 | element, but faceting over multiple factors entails multiple 12 | label variables.} 13 | 14 | \item{...}{ 15 | Arguments passed on to \code{\link[ggplot2:labellers]{ggplot2::label_parsed}} 16 | \describe{ 17 | \item{\code{multi_line}}{Whether to display the labels of multiple factors 18 | on separate lines.} 19 | }} 20 | } 21 | \description{ 22 | This function formats the strip labels of facet grids and wraps that contains 23 | TeX expressions. The latex2exp package must be installed. 24 | } 25 | \examples{ 26 | # requires latex2exp package be installed 27 | if (requireNamespace("latex2exp", quietly = TRUE)) { 28 | library(ggplot2) 29 | d <- data.frame(x = 1, y = 1, facet = "$\\\\beta$") 30 | ggplot(d, aes(x, y)) + 31 | geom_point() + 32 | facet_wrap(~ facet, labeller = label_tex) 33 | } 34 | } 35 | \seealso{ 36 | \link[ggplot2:labeller]{ggplot2::labeller}, \code{\link[latex2exp:TeX]{latex2exp::TeX()}} 37 | } 38 | -------------------------------------------------------------------------------- /man/linear_trans.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/trans_linear.R 3 | \name{linear_trans} 4 | \alias{linear_trans} 5 | \alias{rotate} 6 | \alias{stretch} 7 | \alias{shear} 8 | \alias{translate} 9 | \alias{reflect} 10 | \title{Create a custom linear transformation} 11 | \usage{ 12 | linear_trans(...) 13 | 14 | rotate(angle) 15 | 16 | stretch(x, y) 17 | 18 | shear(x, y) 19 | 20 | translate(x, y) 21 | 22 | reflect(x, y) 23 | } 24 | \arguments{ 25 | \item{...}{A number of transformation functions.} 26 | 27 | \item{angle}{An angle in radians} 28 | 29 | \item{x}{the transformation magnitude in the x-direction} 30 | 31 | \item{y}{the transformation magnitude in the x-direction} 32 | } 33 | \value{ 34 | \code{linear_trans} creates a trans object. The other functions 35 | return a 3x3 transformation matrix. 36 | } 37 | \description{ 38 | This function lets you compose transformations based on a sequence of linear 39 | transformations. If the transformations are parameterised the parameters will 40 | become arguments in the transformation function. The transformations are 41 | one of \code{rotate}, \code{shear}, \code{stretch}, \code{translate}, and 42 | \code{reflect}. 43 | } 44 | \examples{ 45 | trans <- linear_trans(rotate(a), shear(1, 0), translate(x1, y1)) 46 | square <- data.frame(x = c(0, 0, 1, 1), y = c(0, 1, 1, 0)) 47 | square2 <- trans$transform(square$x, square$y, a = pi / 3, x1 = 4, y1 = 8) 48 | square3 <- trans$transform(square$x, square$y, a = pi / 1.5, x1 = 2, y1 = -6) 49 | square <- rbind(square, square2, square3) 50 | square$group <- rep(1:3, each = 4) 51 | ggplot(square, aes(x, y, group = group)) + 52 | geom_polygon(aes(fill = factor(group)), colour = 'black') 53 | } 54 | -------------------------------------------------------------------------------- /man/n_pages.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/facet_wrap_paginate.R 3 | \name{n_pages} 4 | \alias{n_pages} 5 | \title{Determine the number of pages in a paginated facet plot} 6 | \usage{ 7 | n_pages(plot) 8 | } 9 | \arguments{ 10 | \item{plot}{A ggplot object using either facet_wrap_paginate or 11 | facet_grid_paginate} 12 | } 13 | \value{ 14 | If the plot uses using either facet_wrap_paginate or 15 | facet_grid_paginate it returns the total number of pages. Otherwise it 16 | returns NULL 17 | } 18 | \description{ 19 | This is a simple helper that returns the number of pages it takes to plot all 20 | panels when using \code{\link[=facet_wrap_paginate]{facet_wrap_paginate()}} and 21 | \code{\link[=facet_grid_paginate]{facet_grid_paginate()}}. It partially builds the plot so depending 22 | on the complexity of your plot it might take some time to calculate... 23 | } 24 | \examples{ 25 | p <- ggplot(diamonds) + 26 | geom_point(aes(carat, price), alpha = 0.1) + 27 | facet_wrap_paginate(~ cut:clarity, ncol = 3, nrow = 3, page = 1) 28 | n_pages(p) 29 | } 30 | -------------------------------------------------------------------------------- /man/position_auto.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/position_auto.R 3 | \name{position_auto} 4 | \alias{position_auto} 5 | \title{Jitter based on scale types} 6 | \usage{ 7 | position_auto(jitter.width = 0.75, bw = "nrd0", scale = TRUE, seed = NA) 8 | } 9 | \arguments{ 10 | \item{jitter.width}{The maximal width of the jitter} 11 | 12 | \item{bw}{The smoothing bandwidth to use in the case of sina jittering. See 13 | the \code{bw} argument in \link[stats:density]{stats::density}} 14 | 15 | \item{scale}{Should the width of jittering be scaled based on the number of 16 | points in the group} 17 | 18 | \item{seed}{A seed to supply to make the jittering reproducible across layers} 19 | } 20 | \description{ 21 | This position adjustment is able to select a meaningful jitter of the data 22 | based on the combination of positional scale types. It behaves differently 23 | depending on if none, one, or both the x and y scales are discrete. If both 24 | are discrete it will jitter the datapoints evenly inside a disc, if one of 25 | them is discrete it will jitter the discrete dimension to follow the density 26 | along the other dimension (like a sina plot). If neither are discrete it will 27 | not do any jittering. 28 | } 29 | \examples{ 30 | # Continuous vs continuous: No jitter 31 | ggplot(mpg) + geom_point(aes(cty, hwy), position = 'auto') 32 | 33 | # Continuous vs discrete: sina jitter 34 | ggplot(mpg) + geom_point(aes(cty, drv), position = 'auto') 35 | 36 | # Discrete vs discrete: disc-jitter 37 | ggplot(mpg) + geom_point(aes(fl, drv), position = 'auto') 38 | 39 | # Don't scale the jitter based on group size 40 | ggplot(mpg) + geom_point(aes(cty, drv), position = position_auto(scale = FALSE)) 41 | ggplot(mpg) + geom_point(aes(fl, drv), position = position_auto(scale = FALSE)) 42 | 43 | } 44 | \seealso{ 45 | \link{geom_autopoint} for a point geom that uses auto-position by default 46 | } 47 | -------------------------------------------------------------------------------- /man/position_jitternormal.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/position-jitternormal.R 3 | \name{position_jitternormal} 4 | \alias{position_jitternormal} 5 | \title{Jitter points with normally distributed random noise} 6 | \usage{ 7 | position_jitternormal(sd_x = NULL, sd_y = NULL, seed = NA) 8 | } 9 | \arguments{ 10 | \item{sd_x, sd_y}{Standard deviation to add along the x and y axes. The 11 | function uses \code{\link[stats:Normal]{stats::rnorm()}} with \code{mean = 0} behind the scenes. 12 | 13 | If omitted, defaults to 0.15. As with \code{\link[ggplot2:geom_jitter]{ggplot2::geom_jitter()}}, categorical 14 | data is aligned on the integers, so a standard deviation of more than 0.2 15 | will spread the data so it's not possible to see the distinction between 16 | the categories.} 17 | 18 | \item{seed}{A random seed to make the jitter reproducible. 19 | Useful if you need to apply the same jitter twice, e.g., for a point and 20 | a corresponding label. 21 | The random seed is reset after jittering. 22 | If \code{NA} (the default value), the seed is initialised with a random value; 23 | this makes sure that two subsequent calls start with a different seed. 24 | Use \code{NULL} to use the current random seed and also avoid resetting 25 | (the behaviour of \pkg{ggplot} 2.2.1 and earlier).} 26 | } 27 | \description{ 28 | \code{\link[ggplot2:geom_jitter]{ggplot2::geom_jitter()}} adds random noise to points using a uniform 29 | distribution. When many points are plotted, they appear in a rectangle. This 30 | position jitters points using a normal distribution instead, resulting in 31 | more circular clusters. 32 | } 33 | \examples{ 34 | # Example data 35 | df <- data.frame( 36 | x = sample(1:3, 1500, TRUE), 37 | y = sample(1:3, 1500, TRUE) 38 | ) 39 | 40 | # position_jitter results in rectangular clusters 41 | ggplot(df, aes(x = x, y = y)) + 42 | geom_point(position = position_jitter()) 43 | 44 | # geom_jitternormal results in more circular clusters 45 | ggplot(df, aes(x = x, y = y)) + 46 | geom_point(position = position_jitternormal()) 47 | 48 | # You can adjust the standard deviations along both axes 49 | # Tighter circles 50 | ggplot(df, aes(x = x, y = y)) + 51 | geom_point(position = position_jitternormal(sd_x = 0.08, sd_y = 0.08)) 52 | 53 | # Oblong shapes 54 | ggplot(df, aes(x = x, y = y)) + 55 | geom_point(position = position_jitternormal(sd_x = 0.2, sd_y = 0.08)) 56 | 57 | # Only add random noise to one dimension 58 | ggplot(df, aes(x = x, y = y)) + 59 | geom_point( 60 | position = position_jitternormal(sd_x = 0.15, sd_y = 0), 61 | alpha = 0.1 62 | ) 63 | } 64 | \concept{position adjustments} 65 | -------------------------------------------------------------------------------- /man/power_trans.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/trans.R 3 | \name{power_trans} 4 | \alias{power_trans} 5 | \title{Create a power transformation object} 6 | \usage{ 7 | power_trans(n) 8 | } 9 | \arguments{ 10 | \item{n}{The degree of the power transformation} 11 | } 12 | \value{ 13 | A trans object 14 | } 15 | \description{ 16 | This function can be used to create a proper trans object that encapsulates 17 | a power transformation (x^n). 18 | } 19 | \examples{ 20 | # Power of 2 transformations 21 | trans <- power_trans(2) 22 | trans$transform(1:10) 23 | 24 | # Cubic root transformation 25 | trans <- power_trans(1 / 3) 26 | trans$transform(1:10) 27 | 28 | # Use it in a plot 29 | ggplot() + 30 | geom_line(aes(x = 1:10, y = 1:10)) + 31 | scale_x_continuous(trans = power_trans(2), 32 | expand = c(0, 1)) 33 | } 34 | -------------------------------------------------------------------------------- /man/radial_trans.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/trans.R 3 | \name{radial_trans} 4 | \alias{radial_trans} 5 | \title{Create radial data in a cartesian coordinate system} 6 | \usage{ 7 | radial_trans(r.range, a.range, offset = pi/2, pad = 0.5, clip = FALSE) 8 | } 9 | \arguments{ 10 | \item{r.range}{The range in radius that correspond to 0 - 1 in the unit 11 | circle.} 12 | 13 | \item{a.range}{The range in angles that correspond to 2*pi - 0. As radians 14 | are normally measured counterclockwise while radial displays are read 15 | clockwise it's an inverse mapping} 16 | 17 | \item{offset}{The offset in angles to apply. Determines that start position 18 | on the circle. pi/2 (the default) corresponds to 12 o'clock.} 19 | 20 | \item{pad}{Adds to the end points of the angle range in order to separate the 21 | start and end point. Defaults to 0.5} 22 | 23 | \item{clip}{Should input data be clipped to r.range and a.range or be allowed 24 | to extend beyond. Defaults to FALSE (no clipping)} 25 | } 26 | \value{ 27 | A trans object. The transform method for the object takes an r 28 | (radius) and a (angle) argument and returns a data.frame with x and y columns 29 | with rows for each element in r/a. The inverse method takes an x and y 30 | argument and returns a data.frame with r and a columns and rows for each 31 | element in x/y. 32 | } 33 | \description{ 34 | This function creates a trans object that converts radial data to their 35 | corresponding coordinates in cartesian space. The trans object is created for 36 | a specific radius and angle range that will be mapped to the unit circle so 37 | data doesn't have to be normalized to 0-1 and 0-2*pi in advance. While there 38 | exists a clear mapping from radial to cartesian, the inverse is not true as 39 | radial representation is periodic. It is impossible to know how many 40 | revolutions around the unit circle a point has taken from reading its 41 | coordinates. The inverse function will always assume that coordinates are in 42 | their first revolution i.e. map them back within the range of a.range. 43 | } 44 | \note{ 45 | While trans objects are often used to modify scales in ggplot2, radial 46 | transformation is different as it is a coordinate transformation and takes 47 | two arguments. Consider it a trans version of coord_polar and use it to 48 | transform your data prior to plotting. 49 | } 50 | \examples{ 51 | # Some data in radial form 52 | rad <- data.frame(r = seq(1, 10, by = 0.1), a = seq(1, 10, by = 0.1)) 53 | 54 | # Create a transformation 55 | radial <- radial_trans(c(0, 1), c(0, 5)) 56 | 57 | # Get data in x, y 58 | cart <- radial$transform(rad$r, rad$a) 59 | 60 | # Have a look 61 | ggplot() + 62 | geom_path(aes(x = x, y = y), data = cart, color = 'forestgreen') + 63 | geom_path(aes(x = r, y = a), data = rad, color = 'firebrick') 64 | } 65 | -------------------------------------------------------------------------------- /man/scale_depth.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/scale-depth.R 3 | \name{scale_depth} 4 | \alias{scale_depth} 5 | \alias{scale_depth_continuous} 6 | \alias{scale_depth_discrete} 7 | \title{Scales for depth perception} 8 | \usage{ 9 | scale_depth(..., range = c(0, 0.3)) 10 | 11 | scale_depth_continuous(..., range = c(0, 0.3)) 12 | 13 | scale_depth_discrete(..., range = c(0, 0.3)) 14 | } 15 | \arguments{ 16 | \item{...}{arguments passed on to continuous_scale or discrete_scale} 17 | 18 | \item{range}{The relative range as related to the distance between the eyes 19 | and the paper plane.} 20 | } 21 | \description{ 22 | These scales serve to scale the depth aesthetic when creating stereographic 23 | plots. The range specifies the relative distance between the points and the 24 | paper plane in relation to the distance between the eyes and the paper plane 25 | i.e. a range of c(-0.5, 0.5) would put the highest values midways between 26 | the eyes and the image plane and the lowest values the same distance behind 27 | the image plane. To ensure a nice viewing experience these values should not 28 | exceed ~0.3 as it would get hard for the eyes to consolidate the two 29 | pictures. 30 | } 31 | \examples{ 32 | ggplot(mtcars) + 33 | geom_point(aes(mpg, disp, depth = cyl)) + 34 | scale_depth(range = c(-0.1, 0.25)) + 35 | facet_stereo() 36 | } 37 | -------------------------------------------------------------------------------- /man/scale_unit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/scale-unit.R 3 | \name{scale_unit} 4 | \alias{scale_x_unit} 5 | \alias{scale_y_unit} 6 | \title{Position scales for units data} 7 | \usage{ 8 | scale_x_unit(...) 9 | 10 | scale_y_unit(...) 11 | } 12 | \arguments{ 13 | \item{...}{Passed on to \code{units::scale_x_units()} or \code{units::scale_y_units()}} 14 | } 15 | \description{ 16 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} These are the default scales for the units 17 | class. These will usually be added automatically. To override manually, use 18 | \verb{scale_*_unit}. 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /man/shapeGrob.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shape.R 3 | \name{shapeGrob} 4 | \alias{shapeGrob} 5 | \title{The grob powering geom_shape} 6 | \usage{ 7 | shapeGrob( 8 | x = c(0, 0.5, 1, 0.5), 9 | y = c(0.5, 1, 0.5, 0), 10 | id = NULL, 11 | id.lengths = NULL, 12 | expand = 0, 13 | radius = 0, 14 | default.units = "npc", 15 | name = NULL, 16 | gp = gpar(), 17 | vp = NULL 18 | ) 19 | } 20 | \arguments{ 21 | \item{x}{A numeric vector or unit object specifying x-locations.} 22 | 23 | \item{y}{A numeric vector or unit object specifying y-locations.} 24 | 25 | \item{id}{A numeric vector used to separate locations in \code{x} and 26 | \code{y} into multiple polygons. All locations with the same 27 | \code{id} belong to the same polygon.} 28 | 29 | \item{id.lengths}{A numeric vector used to separate locations in \code{x} and 30 | \code{y} into multiple polygons. Specifies consecutive blocks of 31 | locations which make up separate polygons.} 32 | 33 | \item{expand}{An expansion size to expand each shape with, given in units 34 | or a numeric refering to \code{default.units}} 35 | 36 | \item{radius}{The corner radius to apply to each shape, given in units 37 | or a numeric refering to \code{default.units}} 38 | 39 | \item{default.units}{A string indicating the default units to use 40 | if \code{x}, \code{y}, \code{width}, or \code{height} 41 | are only given as numeric vectors.} 42 | 43 | \item{name}{ A character identifier. } 44 | 45 | \item{gp}{An object of class \code{"gpar"}, typically the output 46 | from a call to the function \code{\link[grid]{gpar}}. This is basically 47 | a list of graphical parameter settings.} 48 | 49 | \item{vp}{A Grid viewport object (or NULL).} 50 | } 51 | \value{ 52 | A grob of class \code{shape} or, of \code{expand} and \code{radius} are \code{0} a 53 | regular polygon grob 54 | } 55 | \description{ 56 | This is the underlying grob constructor for \code{\link[=geom_shape]{geom_shape()}}. It is exported 57 | for others to use but with limited support 58 | } 59 | \keyword{internal} 60 | -------------------------------------------------------------------------------- /man/stat_err.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/errorbar.R 3 | \name{stat_err} 4 | \alias{stat_err} 5 | \title{Intervals in vertical and horizontal directions} 6 | \usage{ 7 | stat_err( 8 | mapping = NULL, 9 | data = NULL, 10 | geom = "segment", 11 | position = "identity", 12 | na.rm = FALSE, 13 | show.legend = NA, 14 | inherit.aes = TRUE, 15 | ... 16 | ) 17 | } 18 | \arguments{ 19 | \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}}. If specified and 20 | \code{inherit.aes = TRUE} (the default), it is combined with the default mapping 21 | at the top level of the plot. You must supply \code{mapping} if there is no plot 22 | mapping.} 23 | 24 | \item{data}{The data to be displayed in this layer. There are three 25 | options: 26 | 27 | If \code{NULL}, the default, the data is inherited from the plot 28 | data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. 29 | 30 | A \code{data.frame}, or other object, will override the plot 31 | data. All objects will be fortified to produce a data frame. See 32 | \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. 33 | 34 | A \code{function} will be called with a single argument, 35 | the plot data. The return value must be a \code{data.frame}, and 36 | will be used as the layer data. A \code{function} can be created 37 | from a \code{formula} (e.g. \code{~ head(.x, 10)}).} 38 | 39 | \item{geom}{The geometric object to use to display the data for this layer. 40 | When using a \verb{stat_*()} function to construct a layer, the \code{geom} argument 41 | can be used to override the default coupling between stats and geoms. The 42 | \code{geom} argument accepts the following: 43 | \itemize{ 44 | \item A \code{Geom} ggproto subclass, for example \code{GeomPoint}. 45 | \item A string naming the geom. To give the geom as a string, strip the 46 | function name of the \code{geom_} prefix. For example, to use \code{geom_point()}, 47 | give the geom as \code{"point"}. 48 | \item For more information and other ways to specify the geom, see the 49 | \link[ggplot2:layer_geoms]{layer geom} documentation. 50 | }} 51 | 52 | \item{position}{A position adjustment to use on the data for this layer. This 53 | can be used in various ways, including to prevent overplotting and 54 | improving the display. The \code{position} argument accepts the following: 55 | \itemize{ 56 | \item The result of calling a position function, such as \code{position_jitter()}. 57 | This method allows for passing extra arguments to the position. 58 | \item A string naming the position adjustment. To give the position as a 59 | string, strip the function name of the \code{position_} prefix. For example, 60 | to use \code{position_jitter()}, give the position as \code{"jitter"}. 61 | \item For more information and other ways to specify the position, see the 62 | \link[ggplot2:layer_positions]{layer position} documentation. 63 | }} 64 | 65 | \item{na.rm}{If \code{FALSE}, the default, missing values are removed with 66 | a warning. If \code{TRUE}, missing values are silently removed.} 67 | 68 | \item{show.legend}{logical. Should this layer be included in the legends? 69 | \code{NA}, the default, includes if any aesthetics are mapped. 70 | \code{FALSE} never includes, and \code{TRUE} always includes. 71 | It can also be a named logical vector to finely select the aesthetics to 72 | display.} 73 | 74 | \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, 75 | rather than combining with them. This is most useful for helper functions 76 | that define both data and aesthetics and shouldn't inherit behaviour from 77 | the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} 78 | 79 | \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}'s \code{params} argument. These 80 | arguments broadly fall into one of 4 categories below. Notably, further 81 | arguments to the \code{position} argument, or aesthetics that are required 82 | can \emph{not} be passed through \code{...}. Unknown arguments that are not part 83 | of the 4 categories below are ignored. 84 | \itemize{ 85 | \item Static aesthetics that are not mapped to a scale, but are at a fixed 86 | value and apply to the layer as a whole. For example, \code{colour = "red"} 87 | or \code{linewidth = 3}. The geom's documentation has an \strong{Aesthetics} 88 | section that lists the available options. The 'required' aesthetics 89 | cannot be passed on to the \code{params}. Please note that while passing 90 | unmapped aesthetics as vectors is technically possible, the order and 91 | required length is not guaranteed to be parallel to the input data. 92 | \item When constructing a layer using 93 | a \verb{stat_*()} function, the \code{...} argument can be used to pass on 94 | parameters to the \code{geom} part of the layer. An example of this is 95 | \code{stat_density(geom = "area", outline.type = "both")}. The geom's 96 | documentation lists which parameters it can accept. 97 | \item Inversely, when constructing a layer using a 98 | \verb{geom_*()} function, the \code{...} argument can be used to pass on parameters 99 | to the \code{stat} part of the layer. An example of this is 100 | \code{geom_area(stat = "density", adjust = 0.5)}. The stat's documentation 101 | lists which parameters it can accept. 102 | \item The \code{key_glyph} argument of \code{\link[ggplot2:layer]{layer()}} may also be passed on through 103 | \code{...}. This can be one of the functions described as 104 | \link[ggplot2:draw_key]{key glyphs}, to change the display of the layer in the legend. 105 | }} 106 | } 107 | \description{ 108 | \code{stat_err} draws intervals of points (\code{x}, \code{y}) in vertical (\code{ymin}, \code{ymax}) 109 | and horizontal (\code{xmin}, \code{xmax}) directions. 110 | } 111 | \section{Aesthetics}{ 112 | 113 | \code{stat_err()} understands the following aesthetics (required aesthetics are in 114 | bold): 115 | \itemize{ 116 | \item \strong{x} 117 | \item \strong{xmin} 118 | \item \strong{xmax} 119 | \item \strong{y} 120 | \item \strong{ymin} 121 | \item \strong{ymax} 122 | \item alpha 123 | \item color 124 | \item group 125 | \item linetype 126 | \item linewidth 127 | } 128 | } 129 | 130 | \examples{ 131 | library(ggplot2) 132 | 133 | x <- 1:3 134 | xmin <- x - 2.5 135 | xmax <- x + 2.5 136 | d <- data.frame( 137 | x = x, y = x, xmin = xmin, ymin = xmin, xmax = xmax, ymax = xmax, 138 | color = as.factor(x) 139 | ) 140 | ggplot( 141 | d, 142 | aes(x = x, y = y, xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, color = color) 143 | ) + 144 | stat_err(size = 2) 145 | 146 | } 147 | -------------------------------------------------------------------------------- /man/theme_no_axes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/themes.R 3 | \name{theme_no_axes} 4 | \alias{theme_no_axes} 5 | \title{Theme without axes and gridlines} 6 | \usage{ 7 | theme_no_axes(base.theme = theme_bw()) 8 | } 9 | \arguments{ 10 | \item{base.theme}{The theme to use as a base for the new theme. Defaults to 11 | \code{\link[ggplot2:ggtheme]{ggplot2::theme_bw()}}.} 12 | } 13 | \value{ 14 | A modified version of base.theme 15 | } 16 | \description{ 17 | This theme is a simple wrapper around any complete theme that removes the 18 | axis text, title and ticks as well as the grid lines for plots where these 19 | have little meaning. 20 | } 21 | \examples{ 22 | p <- ggplot() + geom_point(aes(x = wt, y = qsec), data = mtcars) 23 | 24 | p + theme_no_axes() 25 | p + theme_no_axes(theme_grey()) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /man/trans_reverser.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/trans.R 3 | \name{trans_reverser} 4 | \alias{trans_reverser} 5 | \title{Reverse a transformation} 6 | \usage{ 7 | trans_reverser(trans) 8 | } 9 | \arguments{ 10 | \item{trans}{A trans object or an object that can be converted to one using 11 | \code{\link[scales:new_transform]{scales::as.trans()}}} 12 | } 13 | \value{ 14 | A trans object 15 | } 16 | \description{ 17 | While the scales package export a reverse_trans object it does not allow for 18 | reversing of already transformed ranged - e.g. a reverse exp transformation 19 | is not possible. trans_reverser takes a trans object or something coercible 20 | to one and creates a reverse version of it. 21 | } 22 | \examples{ 23 | # Lets make a plot 24 | p <- ggplot() + 25 | geom_line(aes(x = 1:10, y = 1:10)) 26 | 27 | # scales already have a reverse trans 28 | p + scale_x_continuous(trans = 'reverse') 29 | 30 | # But what if you wanted to reverse an already log transformed scale? 31 | p + scale_x_continuous(trans = trans_reverser('log')) 32 | } 33 | -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/ggforce/128001400bee8ae1a27c772b6d9677660db01778/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Revdeps 2 | 3 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 107 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.dll 4 | -------------------------------------------------------------------------------- /src/bSpline.cpp: -------------------------------------------------------------------------------- 1 | #include "deBoor.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace cpp11::literals; 10 | 11 | #include 12 | 13 | cpp11::writable::doubles createKnots(int nControl, int degree) { 14 | int nKnots = nControl + degree + 1; 15 | 16 | cpp11::writable::doubles knots; 17 | knots.reserve(nKnots); 18 | for (int i = 0; i < nKnots; i++) { 19 | if (i < degree + 1) { 20 | knots.push_back(0); 21 | } else if (i < nKnots - degree) { 22 | knots.push_back(knots[i-1] + 1); 23 | } else { 24 | knots.push_back(knots[i-1]); 25 | } 26 | } 27 | return knots; 28 | } 29 | std::vector createOpenKnots(int nControl, int degree) { 30 | int nKnots = nControl + degree + 1; 31 | 32 | std::vector knots (nKnots, 0); 33 | for (int i = 0; i < nKnots; i++) { 34 | if (i < 1) knots[i] = 0; 35 | else knots[i] = knots[i-1] + 1; 36 | } 37 | return knots; 38 | } 39 | std::vector createControls(const cpp11::doubles& x, const cpp11::doubles& y) { 40 | int nControls = x.size(); 41 | std::vector controls(nControls, Point()); 42 | for (int i = 0; i < nControls; i++) { 43 | controls[i] = Point(x[i], y[i]); 44 | } 45 | return controls; 46 | } 47 | [[cpp11::register]] 48 | cpp11::writable::doubles_matrix<> splinePath(cpp11::doubles x, cpp11::doubles y, 49 | int degree, cpp11::doubles knots, 50 | int detail, cpp11::strings type) { 51 | std::vector controls = createControls(x, y); 52 | std::vector knots_vec(knots.begin(), knots.end()); 53 | if (type[0] == "closed") { 54 | controls.push_back(controls[0]); 55 | controls.push_back(controls[1]); 56 | controls.push_back(controls[2]); 57 | } 58 | cpp11::writable::doubles_matrix<> res(detail, 2); 59 | double zJump = (knots_vec[knots_vec.size()-1-degree] - knots_vec[degree]); 60 | if (type[0] == "clamped") { 61 | zJump /= double(detail-1); 62 | } else { 63 | zJump /= double(detail); 64 | } 65 | for (int i = 0; i < detail; i++) { 66 | Point point; 67 | if (i == detail-1 && type[0] == "clamped") { 68 | point = controls[controls.size()-1]; 69 | } else { 70 | double z = knots_vec[degree] + i * zJump; 71 | int zInt = whichInterval(z, knots_vec); 72 | point = deBoor(degree, degree, zInt, z, knots_vec, controls); 73 | } 74 | res(i, 0) = point.x; 75 | res(i, 1) = point.y; 76 | } 77 | return res; 78 | } 79 | [[cpp11::register]] 80 | cpp11::writable::list getSplines(cpp11::doubles x, cpp11::doubles y, 81 | cpp11::integers id, int detail, 82 | cpp11::strings type) { 83 | std::vector nControls; 84 | std::vector pathID; 85 | nControls.push_back(1); 86 | pathID.push_back(id[0]); 87 | for (int i = 1; i < id.size(); i++) { 88 | if (id[i] == pathID.back()) { 89 | nControls.back()++; 90 | } else { 91 | nControls.push_back(1); 92 | pathID.push_back(id[i]); 93 | } 94 | } 95 | int nPaths = nControls.size(); 96 | cpp11::writable::doubles_matrix<> paths(nPaths * detail, 2); 97 | cpp11::writable::integers pathsID(nPaths * detail); 98 | int controlsStart = 0; 99 | R_xlen_t pathStart = 0; 100 | for (int i = 0; i < nPaths; i++) { 101 | cpp11::writable::doubles knots; 102 | int degree = nControls[i] <= 3 ? nControls[i] - 1 : 3; 103 | if (type[0] == "clamped") { 104 | knots = createKnots(nControls[i], degree); 105 | } else if (type[0] == "open") { 106 | knots = createOpenKnots(nControls[i], degree); 107 | } else if (type[0] == "closed") { 108 | if (nControls[i] < 3) { 109 | cpp11::stop("At least 3 control points must be provided for closed b-splines"); 110 | } 111 | degree = 3; 112 | knots = createOpenKnots(nControls[i] + 3, degree); 113 | } else { 114 | cpp11::stop("type must be either \"open\", \"closed\", or \"clamped\""); 115 | } 116 | cpp11::writable::doubles x_tmp(x.begin() + controlsStart, x.begin() + controlsStart + nControls[i]); 117 | cpp11::writable::doubles y_tmp(y.begin() + controlsStart, y.begin() + controlsStart + nControls[i]); 118 | cpp11::doubles_matrix<> path = splinePath(x_tmp, y_tmp, degree, knots, detail, type); 119 | for (R_xlen_t j = 0; j < path.nrow(); ++j) { 120 | pathsID[pathStart + j] = pathID[i]; 121 | paths(pathStart + j, 0) = path(j, 0); 122 | paths(pathStart + j, 1) = path(j, 1); 123 | } 124 | controlsStart += nControls[i]; 125 | pathStart += path.nrow(); 126 | } 127 | return cpp11::writable::list({ 128 | "paths"_nm = paths, 129 | "pathID"_nm = pathsID 130 | }); 131 | } 132 | -------------------------------------------------------------------------------- /src/bezier.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace cpp11::literals; 7 | 8 | #include 9 | 10 | double Bezier2(double t, const cpp11::doubles& w) { 11 | double t2 = t * t; 12 | double mt = 1-t; 13 | double mt2 = mt * mt; 14 | return w[0]*mt2 + w[1]*2*mt*t + w[2]*t2; 15 | } 16 | double Bezier3(double t, const cpp11::doubles& w) { 17 | double t2 = t * t; 18 | double t3 = t2 * t; 19 | double mt = 1-t; 20 | double mt2 = mt * mt; 21 | double mt3 = mt2 * mt; 22 | return w[0]*mt3 + 3*w[1]*mt2*t + 3*w[2]*mt*t2 + w[3]*t3; 23 | } 24 | [[cpp11::register]] 25 | cpp11::writable::doubles_matrix<> bezierPath(const cpp11::doubles& x, const cpp11::doubles& y, int detail) { 26 | cpp11::writable::doubles_matrix<> res(detail, 2); 27 | detail = detail - 1; 28 | double step = 1.0/detail; 29 | double t; 30 | if (x.size() == 3) { 31 | for (int i = 0; i < detail; i++) { 32 | t = i * step; 33 | res(i, 0) = Bezier2(t, x); 34 | res(i, 1) = Bezier2(t, y); 35 | } 36 | } else if (x.size() == 4) { 37 | for (int i = 0; i < detail; i++) { 38 | t = i * step; 39 | res(i, 0) = Bezier3(t, x); 40 | res(i, 1) = Bezier3(t, y); 41 | } 42 | } else { 43 | cpp11::stop("Only support for quadratic and cubic beziers"); 44 | } 45 | res(detail, 0) = x[x.size() - 1]; 46 | res(detail, 1) = y[y.size() - 1]; 47 | return res; 48 | } 49 | [[cpp11::register]] 50 | cpp11::writable::list getBeziers(const cpp11::doubles& x, const cpp11::doubles& y, 51 | const cpp11::integers& id, int detail) { 52 | std::vector nControls; 53 | std::vector pathID; 54 | nControls.push_back(1); 55 | pathID.push_back(id[0]); 56 | for (int i = 1; i < id.size(); i++) { 57 | if (id[i] == pathID.back()) { 58 | nControls.back()++; 59 | } else { 60 | nControls.push_back(1); 61 | pathID.push_back(id[i]); 62 | } 63 | } 64 | size_t nPaths = nControls.size(); 65 | cpp11::writable::doubles_matrix<> paths(nPaths * detail, 2); 66 | cpp11::writable::integers pathsID(nPaths * detail); 67 | int controlsStart = 0; 68 | R_xlen_t pathStart = 0; 69 | for (size_t i = 0; i < nPaths; i++) { 70 | cpp11::writable::doubles x_tmp(x.begin() + controlsStart, x.begin() + controlsStart + nControls[i]); 71 | cpp11::writable::doubles y_tmp(y.begin() + controlsStart, y.begin() + controlsStart + nControls[i]); 72 | cpp11::doubles_matrix<> path = bezierPath(x_tmp, y_tmp, detail); 73 | for (R_xlen_t j = 0; j < path.nrow(); ++j) { 74 | pathsID[pathStart + j] = pathID[i]; 75 | paths(pathStart + j, 0) = path(j, 0); 76 | paths(pathStart + j, 1) = path(j, 1); 77 | } 78 | controlsStart += nControls[i]; 79 | pathStart += path.nrow(); 80 | } 81 | return cpp11::writable::list({ 82 | "paths"_nm = paths, 83 | "pathID"_nm = pathsID 84 | }); 85 | } 86 | -------------------------------------------------------------------------------- /src/concaveman.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "concaveman.h" 6 | 7 | #include 8 | 9 | [[cpp11::register]] 10 | cpp11::writable::doubles_matrix<> concaveman_c(cpp11::doubles_matrix<> p, cpp11::integers h, 11 | double concavity, double threshold) { 12 | typedef std::array point_type; 13 | std::vector points(p.nrow()); 14 | for (auto i = 0; i < p.nrow(); ++i) { 15 | points[i] = {p(i, 0), p(i, 1)}; 16 | } 17 | std::vector hull(h.size()); 18 | for (auto i = 0; i < h.size(); ++i) { 19 | hull[i] = h[i]; 20 | } 21 | 22 | auto chull = concaveman(points, hull, concavity, threshold); 23 | 24 | cpp11::writable::doubles_matrix<> res(chull.size(), 2); 25 | 26 | for (size_t i = 0; i < chull.size(); ++i) { 27 | res(i, 0) = chull[i][0]; 28 | res(i, 1) = chull[i][1]; 29 | } 30 | 31 | return res; 32 | } 33 | -------------------------------------------------------------------------------- /src/cpp11.cpp: -------------------------------------------------------------------------------- 1 | // Generated by cpp11: do not edit by hand 2 | // clang-format off 3 | 4 | 5 | #include "cpp11/declarations.hpp" 6 | #include 7 | 8 | // bSpline.cpp 9 | cpp11::writable::doubles_matrix<> splinePath(cpp11::doubles x, cpp11::doubles y, int degree, cpp11::doubles knots, int detail, cpp11::strings type); 10 | extern "C" SEXP _ggforce_splinePath(SEXP x, SEXP y, SEXP degree, SEXP knots, SEXP detail, SEXP type) { 11 | BEGIN_CPP11 12 | return cpp11::as_sexp(splinePath(cpp11::as_cpp>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(degree), cpp11::as_cpp>(knots), cpp11::as_cpp>(detail), cpp11::as_cpp>(type))); 13 | END_CPP11 14 | } 15 | // bSpline.cpp 16 | cpp11::writable::list getSplines(cpp11::doubles x, cpp11::doubles y, cpp11::integers id, int detail, cpp11::strings type); 17 | extern "C" SEXP _ggforce_getSplines(SEXP x, SEXP y, SEXP id, SEXP detail, SEXP type) { 18 | BEGIN_CPP11 19 | return cpp11::as_sexp(getSplines(cpp11::as_cpp>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(id), cpp11::as_cpp>(detail), cpp11::as_cpp>(type))); 20 | END_CPP11 21 | } 22 | // bezier.cpp 23 | cpp11::writable::doubles_matrix<> bezierPath(const cpp11::doubles& x, const cpp11::doubles& y, int detail); 24 | extern "C" SEXP _ggforce_bezierPath(SEXP x, SEXP y, SEXP detail) { 25 | BEGIN_CPP11 26 | return cpp11::as_sexp(bezierPath(cpp11::as_cpp>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(detail))); 27 | END_CPP11 28 | } 29 | // bezier.cpp 30 | cpp11::writable::list getBeziers(const cpp11::doubles& x, const cpp11::doubles& y, const cpp11::integers& id, int detail); 31 | extern "C" SEXP _ggforce_getBeziers(SEXP x, SEXP y, SEXP id, SEXP detail) { 32 | BEGIN_CPP11 33 | return cpp11::as_sexp(getBeziers(cpp11::as_cpp>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(id), cpp11::as_cpp>(detail))); 34 | END_CPP11 35 | } 36 | // concaveman.cpp 37 | cpp11::writable::doubles_matrix<> concaveman_c(cpp11::doubles_matrix<> p, cpp11::integers h, double concavity, double threshold); 38 | extern "C" SEXP _ggforce_concaveman_c(SEXP p, SEXP h, SEXP concavity, SEXP threshold) { 39 | BEGIN_CPP11 40 | return cpp11::as_sexp(concaveman_c(cpp11::as_cpp>>(p), cpp11::as_cpp>(h), cpp11::as_cpp>(concavity), cpp11::as_cpp>(threshold))); 41 | END_CPP11 42 | } 43 | // ellipseEnclose.cpp 44 | cpp11::writable::data_frame enclose_ellip_points(cpp11::doubles x, cpp11::doubles y, cpp11::integers id, double tol); 45 | extern "C" SEXP _ggforce_enclose_ellip_points(SEXP x, SEXP y, SEXP id, SEXP tol) { 46 | BEGIN_CPP11 47 | return cpp11::as_sexp(enclose_ellip_points(cpp11::as_cpp>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(id), cpp11::as_cpp>(tol))); 48 | END_CPP11 49 | } 50 | // enclose.cpp 51 | cpp11::writable::data_frame enclose_points(cpp11::doubles x, cpp11::doubles y, cpp11::integers id); 52 | extern "C" SEXP _ggforce_enclose_points(SEXP x, SEXP y, SEXP id) { 53 | BEGIN_CPP11 54 | return cpp11::as_sexp(enclose_points(cpp11::as_cpp>(x), cpp11::as_cpp>(y), cpp11::as_cpp>(id))); 55 | END_CPP11 56 | } 57 | // pointPath.cpp 58 | cpp11::writable::list points_to_path(cpp11::doubles_matrix<> pos, cpp11::list_of< cpp11::doubles_matrix<> > path, bool close); 59 | extern "C" SEXP _ggforce_points_to_path(SEXP pos, SEXP path, SEXP close) { 60 | BEGIN_CPP11 61 | return cpp11::as_sexp(points_to_path(cpp11::as_cpp>>(pos), cpp11::as_cpp >>>(path), cpp11::as_cpp>(close))); 62 | END_CPP11 63 | } 64 | 65 | extern "C" { 66 | static const R_CallMethodDef CallEntries[] = { 67 | {"_ggforce_bezierPath", (DL_FUNC) &_ggforce_bezierPath, 3}, 68 | {"_ggforce_concaveman_c", (DL_FUNC) &_ggforce_concaveman_c, 4}, 69 | {"_ggforce_enclose_ellip_points", (DL_FUNC) &_ggforce_enclose_ellip_points, 4}, 70 | {"_ggforce_enclose_points", (DL_FUNC) &_ggforce_enclose_points, 3}, 71 | {"_ggforce_getBeziers", (DL_FUNC) &_ggforce_getBeziers, 4}, 72 | {"_ggforce_getSplines", (DL_FUNC) &_ggforce_getSplines, 5}, 73 | {"_ggforce_points_to_path", (DL_FUNC) &_ggforce_points_to_path, 3}, 74 | {"_ggforce_splinePath", (DL_FUNC) &_ggforce_splinePath, 6}, 75 | {NULL, NULL, 0} 76 | }; 77 | } 78 | 79 | extern "C" attribute_visible void R_init_ggforce(DllInfo* dll){ 80 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 81 | R_useDynamicSymbols(dll, FALSE); 82 | R_forceSymbols(dll, TRUE); 83 | } 84 | -------------------------------------------------------------------------------- /src/deBoor.cpp: -------------------------------------------------------------------------------- 1 | // Taken from https://chi3x10.wordpress.com/2009/10/18/de-boor-algorithm-in-c/ 2 | 3 | #include "deBoor.h" 4 | 5 | Point::Point() { 6 | x = 0.0; 7 | y = 0.0; 8 | } 9 | Point::Point(double xInit, double yInit) { 10 | x = xInit; 11 | y = yInit; 12 | } 13 | Point Point::operator=(const Point pt) { 14 | x = pt.x; 15 | y = pt.y; 16 | return *this; 17 | } 18 | Point Point::operator+(const Point pt) const { 19 | Point temp; 20 | temp.x = x + pt.x; 21 | temp.y = y + pt.y; 22 | return temp; 23 | } 24 | Point Point::operator*(double m) const { 25 | Point temp; 26 | temp.x = x*m; 27 | temp.y = y*m; 28 | return temp; 29 | } 30 | Point Point::operator/(double m) const { 31 | Point temp; 32 | temp.x = x/m; 33 | temp.y = y/m; 34 | return temp; 35 | } 36 | 37 | Point deBoor(int k, int degree, int i, double x, std::vector knots, 38 | std::vector ctrlPoints) { 39 | // Please see wikipedia page for detail 40 | // note that the algorithm here kind of traverses in reverse order 41 | // comapred to that in the wikipedia page 42 | if(k == 0) { 43 | return ctrlPoints[i]; 44 | } else { 45 | double alpha = (x - knots[i])/(knots[i+degree + 1 - k] - knots[i]); 46 | return (deBoor(k - 1, degree, i - 1, x, knots, ctrlPoints)*(1 - alpha) + 47 | deBoor(k - 1, degree, i, x, knots, ctrlPoints)*alpha); 48 | } 49 | } 50 | 51 | int whichInterval(double x, std::vector knots) { 52 | int ti = knots.size(); 53 | for(int i = 1; i < ti - 1; i++) { 54 | if(x < knots[i]) 55 | return(i - 1); 56 | else if(x == knots[ti - 1]) 57 | return(ti - 1); 58 | } 59 | return -1; 60 | } 61 | -------------------------------------------------------------------------------- /src/deBoor.h: -------------------------------------------------------------------------------- 1 | // Taken from https://chi3x10.wordpress.com/2009/10/18/de-boor-algorithm-in-c/ 2 | 3 | #include 4 | 5 | // Class for dealing with points/vectors in a 2-dimensional space 6 | class Point { 7 | public: 8 | double x; 9 | double y; 10 | 11 | Point(); 12 | Point(double xInit, double yInit); 13 | 14 | // copy assignment operator 15 | Point operator=(const Point pt); 16 | 17 | // Arithmatic operators 18 | Point operator+(const Point pt) const; 19 | Point operator*(double m) const; 20 | Point operator/(double m) const; 21 | }; 22 | 23 | // Find the interval in knots where x resides 24 | int whichInterval(double x, std::vector knots); 25 | 26 | // Calculate the position along the B-spline given by x 27 | // The spline is defined by the degree, knots and ctrlPoints. When calling k 28 | // should equal degree but due to the recursive nature k will decrease to 29 | // zero during recursion. i gives the interval in knots that x resides in 30 | Point deBoor(int k, int degree, int i, double x, std::vector knots, 31 | std::vector ctrlPoints); 32 | -------------------------------------------------------------------------------- /src/pointPath.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace cpp11::literals; 7 | 8 | #include 9 | #include 10 | 11 | double distSquared(std::pair p, std::pair p1) { 12 | double x = p1.first - p.first; 13 | double y = p1.second - p.second; 14 | return x * x + y * y; 15 | } 16 | std::pair projection(std::pair a, std::pair b, std::pair p, bool clamp) { 17 | if (a.first == b.first && a.second == b.second) return a; 18 | double length2 = distSquared(a, b); 19 | std::pair norm(b.first - a.first, b.second - a.second); 20 | std::pair pa(p.first - a.first, p.second - a.second); 21 | double t = (norm.first * pa.first + norm.second * pa.second) / length2; 22 | if (clamp) { 23 | t = std::max(0.0, std::min(1.0, t)); 24 | } 25 | norm.first = t * norm.first + a.first; 26 | norm.second = t * norm.second + a.second; 27 | return norm; 28 | } 29 | void dist_to_path(double x, double y, const cpp11::list_of< cpp11::doubles_matrix<> >& path, std::vector &res, bool closed_poly) { 30 | double shortest_dist = -1; 31 | std::pair closest; 32 | std::pair point(x, y); 33 | for (R_xlen_t i = 0; i < path.size(); ++i) { 34 | for (R_xlen_t j = 0; j < path[i].nrow(); ++j) { 35 | if (j == path[i].nrow() && !closed_poly) break; 36 | std::pair a(path[i](j, 0), path[i](j, 1)); 37 | R_xlen_t k = j == path[i].nrow() - 1 ? 0 : j + 1; 38 | std::pair b(path[i](k, 0), path[i](k, 1)); 39 | std::pair close = projection(a, b, point, true); 40 | double dist = std::sqrt(distSquared(point, close)); 41 | if (shortest_dist < 0 || dist < shortest_dist) { 42 | shortest_dist = dist; 43 | closest = close; 44 | } 45 | } 46 | } 47 | res.clear(); 48 | res.push_back(closest.first); 49 | res.push_back(closest.second); 50 | res.push_back(shortest_dist); 51 | } 52 | 53 | [[cpp11::register]] 54 | cpp11::writable::list points_to_path(cpp11::doubles_matrix<> pos, cpp11::list_of< cpp11::doubles_matrix<> > path, bool close) { 55 | std::vector res_container; 56 | cpp11::writable::doubles_matrix<> proj(pos.nrow(), 2); 57 | cpp11::writable::doubles dist(pos.nrow()); 58 | for (R_xlen_t i = 0; i < pos.nrow(); ++i) { 59 | dist_to_path(pos(i, 0), pos(i, 1), path, res_container, close); 60 | proj(i, 0) = res_container[0]; 61 | proj(i, 1) = res_container[1]; 62 | dist[i] = res_container[2]; 63 | } 64 | return cpp11::writable::list({ 65 | "projection"_nm = proj, 66 | "distance"_nm = dist 67 | }); 68 | } 69 | --------------------------------------------------------------------------------