├── .github ├── .gitignore └── workflows │ ├── pkgdown.yaml │ ├── test-coverage.yaml │ ├── R-CMD-check.yaml │ └── pr-commands.yaml ├── vignettes └── .gitignore ├── cleanup ├── LICENSE ├── tests ├── testthat.R └── testthat │ └── test-aaa.R ├── man ├── figures │ ├── card.png │ ├── logo.png │ └── README-unnamed-chunk-3-1.png ├── euclid-package.Rd ├── boolean_operations.Rd ├── approx_angle.Rd ├── bisector.Rd ├── equidistant_line.Rd ├── barycenter.Rd ├── geometry_measures.Rd ├── collinear.Rd ├── in_order.Rd ├── map_to.Rd ├── centroid.Rd ├── circumcenter.Rd ├── geometry_class.Rd ├── parallel.Rd ├── normal.Rd ├── distance_squared.Rd ├── constant_in.Rd ├── has_intersection.Rd ├── is_degenerate.Rd ├── tetrahedron.Rd ├── radical.Rd ├── geometry_turns.Rd ├── bbox.Rd ├── triangle.Rd ├── iso_cube.Rd ├── segment.Rd ├── project.Rd ├── sphere.Rd ├── iso_rect.Rd ├── def.Rd ├── plane.Rd ├── ray.Rd ├── exact_numeric.Rd ├── weighted_point.Rd ├── direction.Rd ├── line.Rd ├── subgeometries.Rd ├── vec.Rd ├── intersection.Rd ├── location_predicates.Rd ├── point.Rd ├── euclid_plot.Rd ├── euclid_geometry.Rd ├── circle.Rd └── affine_transformation.Rd ├── pkgdown └── favicon │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ └── apple-touch-icon-180x180.png ├── .gitignore ├── src ├── Makevars.win ├── Makevars.in ├── euclid_types.h ├── api.h ├── intersection.cpp ├── distance.cpp ├── geometry_measures.cpp ├── tetrahedron.cpp ├── geometry_projection.cpp ├── is_degenerate.h ├── normal.h ├── triangle.cpp ├── distance.h ├── iso_rect.cpp ├── iso_cube.cpp ├── get_vertex.h ├── match.h ├── point_w.cpp ├── get_edge.h ├── exact_numeric.h ├── segment.cpp ├── sphere.cpp ├── sphere.h ├── iso_rect.h ├── iso_cube.h ├── tetrahedron.h └── intersection.h ├── .Rbuildignore ├── R ├── test-helpers.R ├── euclid-package.R ├── geometry_boolop.R ├── geometry_math.R ├── geometry_summary.R ├── aaa.R ├── geometry_op.R ├── tetrahedron.R ├── triangle.R ├── iso_cube.R ├── iso_rect.R ├── sphere.R ├── segment.R └── point_w.R ├── codecov.yml ├── inst └── include │ └── internal │ ├── cran.h │ └── cgal_types.h ├── euclid.Rproj ├── LICENSE.md ├── DESCRIPTION └── configure /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /cleanup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rm -f src/Makevars configure.log O 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2020 2 | COPYRIGHT HOLDER: Thomas Lin Pedersen 3 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(euclid) 3 | 4 | test_check("euclid") 5 | -------------------------------------------------------------------------------- /man/figures/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/man/figures/card.png -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/man/figures/logo.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/man/figures/README-unnamed-chunk-3-1.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasp85/euclid/HEAD/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | configure.log 5 | src/*.o 6 | src/*.so 7 | docs 8 | src/Makevars 9 | man/.DS_Store 10 | .DS_Store 11 | inst/doc 12 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | CXX_STD = CXX14 2 | 3 | PKG_CPPFLAGS=-DCGAL_DO_NOT_USE_BOOST_MP -DCGAL_USE_GMPXX -DBOOST_NO_AUTO_PTR -I../inst/include/internal/ 4 | 5 | PKG_LIBS = -lmpfr -lgmp 6 | -------------------------------------------------------------------------------- /src/Makevars.in: -------------------------------------------------------------------------------- 1 | CXX_STD = CXX14 2 | 3 | PKG_CPPFLAGS=-DCGAL_DO_NOT_USE_BOOST_MP -DCGAL_USE_GMPXX -DCGAL_NDEBUG @cflags@ -I../inst/include/internal/ 4 | 5 | PKG_LIBS = @libs@ $(@SYS@_LIBS) 6 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^euclid\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^CODE_OF_CONDUCT\.md$ 6 | ^codecov\.yml$ 7 | ^\.github$ 8 | ^_pkgdown\.yml$ 9 | ^docs$ 10 | ^pkgdown$ 11 | -------------------------------------------------------------------------------- /R/test-helpers.R: -------------------------------------------------------------------------------- 1 | create_bad_lines <- function(dim = 2) { 2 | if (dim == 2) { 3 | l <- line(point(0, 0), vec(c(1, 0), c(1, 0))) 4 | } else { 5 | l <- line(point(0, 0, 0), vec(c(1, 0), c(1, 0), c(1, 0))) 6 | } 7 | l[c(1, 2, NA)] 8 | } 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /R/euclid-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | #' @import rlang 3 | "_PACKAGE" 4 | 5 | # The following block is used by usethis to automatically manage 6 | # roxygen namespace tags. Modify with care! 7 | ## usethis namespace: start 8 | #' @useDynLib euclid, .registration = TRUE 9 | #' @import cli 10 | ## usethis namespace: end 11 | NULL 12 | -------------------------------------------------------------------------------- /inst/include/internal/cran.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define cerr clog 10 | 11 | namespace std { 12 | inline int _exit_mask(int x) { 13 | Rf_error("exit called"); 14 | } 15 | } 16 | #define exit _exit_mask 17 | 18 | namespace std { 19 | inline int _abort_mask() { 20 | Rf_error("abort called"); 21 | } 22 | } 23 | #define abort _abort_mask 24 | -------------------------------------------------------------------------------- /src/euclid_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cgal_types.h" 4 | #include "bbox.h" 5 | #include "circle.h" 6 | #include "direction.h" 7 | #include "exact_numeric.h" 8 | #include "iso_cube.h" 9 | #include "iso_rect.h" 10 | #include "line.h" 11 | #include "plane.h" 12 | #include "point.h" 13 | #include "point_w.h" 14 | #include "ray.h" 15 | #include "segment.h" 16 | #include "sphere.h" 17 | #include "tetrahedron.h" 18 | #include "transform.h" 19 | #include "triangle.h" 20 | #include "vector.h" 21 | -------------------------------------------------------------------------------- /euclid.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | 24 | MarkdownWrap: Column 25 | MarkdownWrapAtColumn: 80 26 | -------------------------------------------------------------------------------- /src/api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template 7 | cpp11::external_pointer get_ext_pointer(SEXP val) { 8 | if (TYPEOF(val) != VECSXP) { 9 | cpp11::stop("Provided object not an euclid class"); 10 | } 11 | SEXP pointer = VECTOR_ELT(val, 0); 12 | if (TYPEOF(pointer) != EXTPTRSXP) { 13 | cpp11::stop("Provided object not an euclid class"); 14 | } 15 | cpp11::external_pointer ex_p(pointer); 16 | if (ex_p.get() == nullptr) { 17 | cpp11::stop("Provided object is not of the correct class or has been cleared from memory"); 18 | } 19 | return ex_p; 20 | } 21 | -------------------------------------------------------------------------------- /src/intersection.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry_vector.h" 2 | #include 3 | #include 4 | 5 | [[cpp11::register]] 6 | cpp11::writable::list geometry_intersection(geometry_vector_base_p geo1, geometry_vector_base_p geo2) { 7 | if (geo1.get() == nullptr || geo2.get() == nullptr) { 8 | cpp11::stop("Data structure pointer cleared from memory"); 9 | } 10 | return geo1->intersection(*geo2); 11 | } 12 | 13 | [[cpp11::register]] 14 | cpp11::writable::logicals geometry_do_intersect(geometry_vector_base_p geo1, geometry_vector_base_p geo2) { 15 | if (geo1.get() == nullptr || geo2.get() == nullptr) { 16 | cpp11::stop("Data structure pointer cleared from memory"); 17 | } 18 | return geo1->do_intersect(*geo2); 19 | } 20 | -------------------------------------------------------------------------------- /src/distance.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry_vector.h" 2 | #include "exact_numeric.h" 3 | #include 4 | #include 5 | 6 | [[cpp11::register]] 7 | exact_numeric_p geometry_squared_distance(geometry_vector_base_p geo1, geometry_vector_base_p geo2) { 8 | if (geo1.get() == nullptr || geo2.get() == nullptr) { 9 | cpp11::stop("Data structure pointer cleared from memory"); 10 | } 11 | std::vector res = geo1->squared_distance(*geo2); 12 | exact_numeric* vec(new exact_numeric(res)); 13 | return {vec}; 14 | } 15 | 16 | [[cpp11::register]] 17 | cpp11::writable::doubles_matrix<> geometry_distance_matrix(geometry_vector_base_p geo1, geometry_vector_base_p geo2) { 18 | if (geo1.get() == nullptr || geo2.get() == nullptr) { 19 | cpp11::stop("Data structure pointer cleared from memory"); 20 | } 21 | return geo1->distance_matrix(*geo2); 22 | } 23 | -------------------------------------------------------------------------------- /src/geometry_measures.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry_vector.h" 2 | 3 | #include 4 | 5 | [[cpp11::register]] 6 | cpp11::writable::doubles geometry_approx_length(geometry_vector_base_p geometries) { 7 | if (geometries.get() == nullptr) { 8 | cpp11::stop("Data structure pointer cleared from memory"); 9 | } 10 | return geometries->length(); 11 | } 12 | [[cpp11::register]] 13 | cpp11::writable::doubles geometry_approx_area(geometry_vector_base_p geometries) { 14 | if (geometries.get() == nullptr) { 15 | cpp11::stop("Data structure pointer cleared from memory"); 16 | } 17 | return geometries->area(); 18 | } 19 | [[cpp11::register]] 20 | cpp11::writable::doubles geometry_approx_volume(geometry_vector_base_p geometries) { 21 | if (geometries.get() == nullptr) { 22 | cpp11::stop("Data structure pointer cleared from memory"); 23 | } 24 | return geometries->volume(); 25 | } 26 | -------------------------------------------------------------------------------- /src/tetrahedron.cpp: -------------------------------------------------------------------------------- 1 | #include "tetrahedron.h" 2 | #include "point.h" 3 | 4 | [[cpp11::register]] 5 | tetrahedron_p create_tetrahedron_empty() { 6 | std::vector vec; 7 | tetrahedron *result(new tetrahedron(vec)); 8 | return {result}; 9 | } 10 | 11 | [[cpp11::register]] 12 | tetrahedron_p create_tetrahedron_4points(point3_p p, point3_p q, point3_p r, point3_p s) { 13 | if (p.get() == nullptr || q.get() == nullptr || r.get() == nullptr || s.get() == nullptr) { 14 | cpp11::stop("Data structure pointer cleared from memory"); 15 | } 16 | std::vector vec; 17 | vec.reserve(p->size()); 18 | for (size_t i = 0; i < p->size(); ++i) { 19 | if (!(*p)[i] || !(*q)[i] || !(*r)[i] || !(*s)[i]) { 20 | vec.push_back(Tetrahedron::NA_value()); 21 | continue; 22 | } 23 | vec.emplace_back((*p)[i], (*q)[i], (*r)[i], (*s)[i]); 24 | } 25 | tetrahedron *result(new tetrahedron(vec)); 26 | 27 | return {result}; 28 | } 29 | -------------------------------------------------------------------------------- /tests/testthat/test-aaa.R: -------------------------------------------------------------------------------- 1 | test_that("validate_constructor_input works", { 2 | num <- runif(10) 3 | exact_num <- exact_numeric(num) 4 | geo2 <- point(num, num) 5 | geo3 <- sphere(point(num, num, num), num) 6 | 7 | expect_error(validate_constructor_input(geo2, geo3)) 8 | expect_error(validate_constructor_input(geo2, num[1:4])) 9 | 10 | res <- validate_constructor_input(num) 11 | expect_true(all(res[[1]] == exact_num)) 12 | 13 | res <- validate_constructor_input(num, .convert_numerics = FALSE) 14 | expect_true(all(res[[1]] == num)) 15 | 16 | res <- validate_constructor_input(geo2, num[2]) 17 | expect_equal(lengths(res), c(10, 10)) 18 | }) 19 | 20 | test_that("check_geometry_input works", { 21 | num <- runif(10) 22 | geo2 <- point(num, num) 23 | geo3 <- sphere(point(num, num, num), num) 24 | expect_error(check_geometry_input(geo2, geo3)) 25 | expect_error(check_geometry_input(geo2, num)) 26 | expect_silent(check_geometry_input(geo2, NULL, geo2)) 27 | }) 28 | -------------------------------------------------------------------------------- /man/euclid-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/euclid-package.R 3 | \docType{package} 4 | \name{euclid-package} 5 | \alias{euclid} 6 | \alias{euclid-package} 7 | \title{euclid: Exact Computation Geometry Framework Based on 'CGAL'} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | Provides a rich framework for doing computation geometry by interfacing with the 'CGAL' library. The 'euclid' package provides new vector types for standard geometric objects and supports exact computations on them. 12 | } 13 | \seealso{ 14 | Useful links: 15 | \itemize{ 16 | \item \url{https://r-euclid.com} 17 | \item \url{https://github.com/thomasp85/euclid} 18 | \item Report bugs at \url{https://github.com/thomasp85/euclid/issues} 19 | } 20 | 21 | } 22 | \author{ 23 | \strong{Maintainer}: Thomas Lin Pedersen \email{thomasp85@gmail.com} (\href{https://orcid.org/0000-0002-5147-4711}{ORCID}) 24 | 25 | } 26 | \keyword{internal} 27 | -------------------------------------------------------------------------------- /man/boolean_operations.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_boolop.R 3 | \name{boolean_operations} 4 | \alias{boolean_operations} 5 | \alias{union} 6 | \alias{difference} 7 | \alias{symmetric_difference} 8 | \alias{complement} 9 | \title{Boolean operations on geometries} 10 | \usage{ 11 | union(x, y, ...) 12 | 13 | difference(x, y, ...) 14 | 15 | symmetric_difference(x, y, ...) 16 | 17 | complement(x, ...) 18 | } 19 | \arguments{ 20 | \item{x, y}{vectors of geometries} 21 | 22 | \item{...}{arguments passed on to methods} 23 | } 24 | \value{ 25 | Return value will depend on the different implementations 26 | } 27 | \description{ 28 | While intersection calculation and queries are the only part of the boolean 29 | operators provided by euclid, it provides generics for the remaining ones so 30 | that other packages may fill the gap (e.g. polyclid for 2D boolean operations 31 | on polygons). 32 | } 33 | \seealso{ 34 | Other Boolean operations: 35 | \code{\link{intersection}()} 36 | } 37 | \concept{Boolean operations} 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2020 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 | -------------------------------------------------------------------------------- /man/approx_angle.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_measures.R 3 | \name{approx_angle} 4 | \alias{approx_angle} 5 | \title{Calculate angle between geometries} 6 | \usage{ 7 | approx_angle(x, y) 8 | } 9 | \arguments{ 10 | \item{x, y}{Geometry vectors. Only surfaces, curves, and arrows are allowed} 11 | } 12 | \value{ 13 | A numeric vector given angular difference in radians 14 | } 15 | \description{ 16 | Angles cannot be given exactly since vector angle relies on the vector length 17 | as well as \link{acos}. This function calculate the angle between two geometries. 18 | This is defined for surfaces, curves, and arrows. If a surface is supplied 19 | the angle will be calculated based on the surface normal and modified to 20 | match the original geometry. 21 | } 22 | \examples{ 23 | # Angle between two lines 24 | approx_angle(line(3, 7, -1), line(-5, 4, 2)) 25 | 26 | # Angle between vector and plane 27 | approx_angle(plane(-4, 1, 19, -4), vec(5, -7, 2)) 28 | 29 | } 30 | \seealso{ 31 | Other Measures: 32 | \code{\link{distance_squared}()}, 33 | \code{\link{geometry_measures}} 34 | } 35 | \concept{Measures} 36 | -------------------------------------------------------------------------------- /man/bisector.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_locations.R 3 | \name{bisector} 4 | \alias{bisector} 5 | \title{Calculate the bisector of two geometries} 6 | \usage{ 7 | bisector(x, y) 8 | } 9 | \arguments{ 10 | \item{x, y}{Vector of points, 2D lines, or planes} 11 | } 12 | \value{ 13 | A vector of lines or planes depending on the dimensionality of x and 14 | y 15 | } 16 | \description{ 17 | A bisector is a geometry that splits the space in two, i.e. a line in 2D and 18 | a plane in 3D. Constructing a bisector from 2 geometries creates a split 19 | where the edge at any given point has equal length to the two geometries. 20 | } 21 | \examples{ 22 | b <- bisector(point(4, -2), point(7, 5)) 23 | b 24 | 25 | plot(c(point(4, -2), point(7, 5)), pch = 16, cex = 2) 26 | euclid_plot(b, col = "firebrick") 27 | 28 | } 29 | \seealso{ 30 | Other Locations: 31 | \code{\link{barycenter}()}, 32 | \code{\link{centroid}()}, 33 | \code{\link{circumcenter}()}, 34 | \code{\link{equidistant_line}()}, 35 | \code{\link{point}()}, 36 | \code{\link{radical}()}, 37 | \code{\link{weighted_point}()} 38 | } 39 | \concept{Locations} 40 | -------------------------------------------------------------------------------- /R/geometry_boolop.R: -------------------------------------------------------------------------------- 1 | #' Boolean operations on geometries 2 | #' 3 | #' While intersection calculation and queries are the only part of the boolean 4 | #' operators provided by euclid, it provides generics for the remaining ones so 5 | #' that other packages may fill the gap (e.g. polyclid for 2D boolean operations 6 | #' on polygons). 7 | #' 8 | #' @param x,y vectors of geometries 9 | #' @param ... arguments passed on to methods 10 | #' 11 | #' @return Return value will depend on the different implementations 12 | #' 13 | #' @name boolean_operations 14 | #' @rdname boolean_operations 15 | #' 16 | #' @family Boolean operations 17 | #' 18 | #' @importFrom generics union 19 | #' @rawNamespace export(union) 20 | #' @aliases union 21 | #' @usage union(x, y, ...) 22 | #' 23 | #' 24 | NULL 25 | 26 | #' @rdname boolean_operations 27 | #' @export 28 | difference <- function(x, y, ...) { 29 | UseMethod("difference") 30 | } 31 | #' @rdname boolean_operations 32 | #' @export 33 | symmetric_difference <- function(x, y, ...) { 34 | UseMethod("symmetric_difference") 35 | } 36 | #' @rdname boolean_operations 37 | #' @export 38 | complement <- function(x, ...) { 39 | UseMethod("complement") 40 | } 41 | -------------------------------------------------------------------------------- /R/geometry_math.R: -------------------------------------------------------------------------------- 1 | geometry_math_cumsum <- function(x) { 2 | UseMethod("geometry_math_cumsum") 3 | } 4 | #' @export 5 | geometry_math_cumsum.default <- function(x) { 6 | cli_abort("{.cls {class(x)[1]}} does not support {.fn cumsum}") 7 | } 8 | geometry_math_cumprod <- function(x) { 9 | UseMethod("geometry_math_cumprod") 10 | } 11 | #' @export 12 | geometry_math_cumprod.default <- function(x) { 13 | cli_abort("{.cls {class(x)[1]}} does not support {.fn cumprod}") 14 | } 15 | geometry_math_cummin <- function(x) { 16 | UseMethod("geometry_math_cummin") 17 | } 18 | #' @export 19 | geometry_math_cummin.default <- function(x) { 20 | cli_abort("{.cls {class(x)[1]}} does not support {.fn cummin}") 21 | } 22 | geometry_math_cummax <- function(x) { 23 | UseMethod("geometry_math_cummax") 24 | } 25 | #' @export 26 | geometry_math_cummax.default <- function(x) { 27 | cli_abort("{.cls {class(x)[1]}} does not support {.fn cummax}") 28 | } 29 | 30 | #' @export 31 | Math.euclid_geometry <- function(x, ...) { 32 | res <- switch(.Generic, 33 | "cumsum" = geometry_math_cumsum(x), 34 | "cumprod" = geometry_math_cumprod(x), 35 | "cummin" = geometry_math_cummin(x), 36 | "cummax" = geometry_math_cummax(x) 37 | ) 38 | restore_euclid_vector(res, x) 39 | } 40 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: euclid 2 | Title: Exact Computation Geometry Framework Based on 'CGAL' 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | c(person(given = "Thomas Lin", 6 | family = "Pedersen", 7 | role = c("aut", "cre"), 8 | email = "thomasp85@gmail.com", 9 | comment = c(ORCID = "0000-0002-5147-4711"))) 10 | Description: Provides a rich framework for doing computation geometry by 11 | interfacing with the 'CGAL' library. The 'euclid' package provides new 12 | vector types for standard geometric objects and supports exact computations 13 | on them. 14 | License: MIT + file LICENSE 15 | Encoding: UTF-8 16 | Roxygen: list(markdown = TRUE) 17 | RoxygenNote: 7.2.1 18 | LinkingTo: 19 | cpp11 (>= 0.2.3), 20 | cgalh, 21 | BH 22 | SystemRequirements: 23 | C++14, 24 | gmp, 25 | mpfr 26 | Imports: 27 | rlang, 28 | utils, 29 | grid, 30 | graphics, 31 | grDevices, 32 | cli, 33 | generics 34 | URL: https://r-euclid.com, https://github.com/thomasp85/euclid 35 | BugReports: https://github.com/thomasp85/euclid/issues 36 | Suggests: 37 | knitr, 38 | covr, 39 | testthat, 40 | rmarkdown 41 | VignetteBuilder: knitr 42 | Remotes: 43 | dickoa/cgalh 44 | -------------------------------------------------------------------------------- /man/equidistant_line.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_locations.R 3 | \name{equidistant_line} 4 | \alias{equidistant_line} 5 | \title{Construct the equidistant line of 2 or three points} 6 | \usage{ 7 | equidistant_line(x, y, z = NULL) 8 | } 9 | \arguments{ 10 | \item{x, y, z}{Vector of points. If z is given they must be in 3D, otherwise in 11 | 2D} 12 | } 13 | \value{ 14 | A vector of lines 15 | } 16 | \description{ 17 | An equidistant line is a line that at any given point is the same distance to 18 | the points that defines it. In 2 dimensions it is equivalent to the bisector 19 | of the two points. In 3 dimensions it is defined for 3 points (2 points would 20 | give a bisector plane). 21 | } 22 | \examples{ 23 | equidistant_line( 24 | point(3, 7, 2), 25 | point(6, -4, 9), 26 | point(2, 1, -5) 27 | ) 28 | 29 | # In 2D you get the bisector 30 | equidistant_line(point(2, 5), point(3, 2)) == bisector(point(2, 5), point(3, 2)) 31 | 32 | } 33 | \seealso{ 34 | Other Locations: 35 | \code{\link{barycenter}()}, 36 | \code{\link{bisector}()}, 37 | \code{\link{centroid}()}, 38 | \code{\link{circumcenter}()}, 39 | \code{\link{point}()}, 40 | \code{\link{radical}()}, 41 | \code{\link{weighted_point}()} 42 | } 43 | \concept{Locations} 44 | -------------------------------------------------------------------------------- /man/barycenter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_locations.R 3 | \name{barycenter} 4 | \alias{barycenter} 5 | \title{Calculate barycenter of a set of weighted points} 6 | \usage{ 7 | barycenter(x, y, z = NULL, t = NULL) 8 | } 9 | \arguments{ 10 | \item{x, y, z, t}{vectors of weighted points. \code{z} and \code{t} are 11 | optional} 12 | } 13 | \value{ 14 | A point vector 15 | } 16 | \description{ 17 | The barycenter is the center of mass of 2 or more points. euclid supports 18 | calculating the barycenter of up to 4 weighted points. If the weights are the 19 | same between the points the barycenter corresponds to the centroid (or 20 | midpoint in the case of two points). 21 | } 22 | \examples{ 23 | p <- weighted_point( 24 | x = c(4, 0, -3), 25 | y = c(2, -4, 7), 26 | w = c(20, 8, 13) 27 | ) 28 | bc <- barycenter(p[1], p[2], p[3]) 29 | bc 30 | 31 | plot(p, cex = as.numeric(def(p, "w") / 8)) 32 | euclid_plot(bc, pch = 16, col = "firebrick", cex = 3) 33 | 34 | } 35 | \seealso{ 36 | Other Locations: 37 | \code{\link{bisector}()}, 38 | \code{\link{centroid}()}, 39 | \code{\link{circumcenter}()}, 40 | \code{\link{equidistant_line}()}, 41 | \code{\link{point}()}, 42 | \code{\link{radical}()}, 43 | \code{\link{weighted_point}()} 44 | } 45 | \concept{Locations} 46 | -------------------------------------------------------------------------------- /src/geometry_projection.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry_vector.h" 2 | 3 | [[cpp11::register]] 4 | geometry_vector_base_p geometry_project_to_line(geometry_vector_base_p geometries, geometry_vector_base_p lines) { 5 | if (geometries.get() == nullptr || lines.get() == nullptr) { 6 | cpp11::stop("Data structure pointer cleared from memory"); 7 | } 8 | return geometries->project_to_line(*lines); 9 | } 10 | 11 | [[cpp11::register]] 12 | geometry_vector_base_p geometry_project_to_plane(geometry_vector_base_p geometries, geometry_vector_base_p planes) { 13 | if (geometries.get() == nullptr || planes.get() == nullptr) { 14 | cpp11::stop("Data structure pointer cleared from memory"); 15 | } 16 | return geometries->project_to_plane(*planes); 17 | } 18 | 19 | [[cpp11::register]] 20 | geometry_vector_base_p geometry_map_to_plane(geometry_vector_base_p geometries, geometry_vector_base_p planes) { 21 | if (geometries.get() == nullptr || planes.get() == nullptr) { 22 | cpp11::stop("Data structure pointer cleared from memory"); 23 | } 24 | return geometries->map_to_plane(*planes); 25 | } 26 | 27 | [[cpp11::register]] 28 | geometry_vector_base_p geometry_normal(geometry_vector_base_p geometries) { 29 | if (geometries.get() == nullptr) { 30 | cpp11::stop("Data structure pointer cleared from memory"); 31 | } 32 | return geometries->normal(); 33 | } 34 | -------------------------------------------------------------------------------- /man/geometry_measures.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_measures.R 3 | \name{geometry_measures} 4 | \alias{geometry_measures} 5 | \alias{approx_length} 6 | \alias{approx_area} 7 | \alias{approx_volume} 8 | \alias{approx_radius} 9 | \title{Approximate geometry measures} 10 | \usage{ 11 | approx_length(x) 12 | 13 | approx_area(x) 14 | 15 | approx_volume(x) 16 | 17 | approx_radius(x) 18 | } 19 | \arguments{ 20 | \item{x}{A geometry vector} 21 | } 22 | \value{ 23 | A numeric vector 24 | } 25 | \description{ 26 | These functions provide approximate measures of length, area, and volume of 27 | geometries where relevant. The reason for approximation is that some measures 28 | require square root operations or multiplication by π, both operations where 29 | exactness is necessarily lost. Not all geometries have meaningful measures, 30 | e.g. a direction is dimensionless, a line is without area, and a circle has 31 | no volume. Some geometries are infinite in some measures, e.g. a plane has an 32 | infinite area. The return value will reflect this. 33 | } 34 | \examples{ 35 | 36 | approx_length(vec(point(1:4, 4:7))) 37 | approx_area(circle(point(0, 0), 5:9)) 38 | approx_volume(sphere(point(0, 0, 0), 5:9)) 39 | 40 | } 41 | \seealso{ 42 | Other Measures: 43 | \code{\link{approx_angle}()}, 44 | \code{\link{distance_squared}()} 45 | } 46 | \concept{Measures} 47 | -------------------------------------------------------------------------------- /man/collinear.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{collinear} 4 | \alias{collinear} 5 | \alias{coplanar} 6 | \title{Check geometries for whether they are collinear or coplanar} 7 | \usage{ 8 | collinear(x, y, z = NULL) 9 | 10 | coplanar(x, y, z = NULL, t = NULL) 11 | } 12 | \arguments{ 13 | \item{x, y, z, t}{Vector of geometries.} 14 | } 15 | \value{ 16 | A logical vector 17 | } 18 | \description{ 19 | Geometries are collinear if they lie on the same line and coplanar if they 20 | lie on the same plane. collinearity can be tested between two curves or three 21 | points, weighted points, circles, or spheres. coplanarity can be tested 22 | between two surfaces or 4 points, weighted points, or spheres 23 | } 24 | \examples{ 25 | # Collinear points 26 | collinear(point(0, 0), point(1, 1), point(2, 2)) 27 | 28 | # Coplanar triangles 29 | coplanar( 30 | triangle(point(0, 0, 0), point(1, 1, 1), point(1, 1, 0)), 31 | triangle(point(0, 0, 0), point(-1, -1, -1), point(-1, -1, 0)) 32 | ) 33 | 34 | } 35 | \seealso{ 36 | Other Predicates: 37 | \code{\link{constant_in}}, 38 | \code{\link{geometry_class}}, 39 | \code{\link{geometry_turns}}, 40 | \code{\link{has_intersection}()}, 41 | \code{\link{in_order}()}, 42 | \code{\link{is_degenerate}()}, 43 | \code{\link{location_predicates}}, 44 | \code{\link{parallel}()} 45 | } 46 | \concept{Predicates} 47 | -------------------------------------------------------------------------------- /src/is_degenerate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cgal_types.h" 4 | 5 | template 6 | inline bool is_degenerate_impl(const T& geo) { 7 | return geo.is_degenerate(); 8 | } 9 | template<> 10 | inline bool is_degenerate_impl(const Circle_3& geo) { 11 | return geo.squared_radius() == 0.0 || geo.supporting_plane().is_degenerate(); 12 | } 13 | template<> 14 | inline bool is_degenerate_impl(const Point_2& geo) { 15 | return false; 16 | } 17 | template<> 18 | inline bool is_degenerate_impl(const Point_3& geo) { 19 | return false; 20 | } 21 | template<> 22 | inline bool is_degenerate_impl(const Vector_2& geo) { 23 | return false; 24 | } 25 | template<> 26 | inline bool is_degenerate_impl(const Vector_3& geo) { 27 | return false; 28 | } 29 | template<> 30 | inline bool is_degenerate_impl(const Direction_2& geo) { 31 | return false; 32 | } 33 | template<> 34 | inline bool is_degenerate_impl(const Direction_3& geo) { 35 | return false; 36 | } 37 | template<> 38 | inline bool is_degenerate_impl(const Weighted_point_2& geo) { 39 | return false; 40 | } 41 | template<> 42 | inline bool is_degenerate_impl(const Weighted_point_3& geo) { 43 | return false; 44 | } 45 | 46 | template 47 | inline bool invalid_geo(const T& geo) { 48 | return geo.is_na() || is_degenerate_impl(geo); 49 | } 50 | -------------------------------------------------------------------------------- /R/geometry_summary.R: -------------------------------------------------------------------------------- 1 | geometry_summary_sum <- function(x, na_rm) { 2 | UseMethod("geometry_summary_sum") 3 | } 4 | #' @export 5 | geometry_summary_sum.default <- function(x, na_rm) { 6 | cli_abort("The provided geometry does not support {.fn sum}") 7 | } 8 | geometry_summary_prod <- function(x, na_rm) { 9 | UseMethod("geometry_summary_prod") 10 | } 11 | #' @export 12 | geometry_summary_prod.default <- function(x, na_rm) { 13 | cli_abort("The provided geometry does not support {.fn prod}") 14 | } 15 | geometry_summary_min <- function(x, na_rm) { 16 | UseMethod("geometry_summary_min") 17 | } 18 | #' @export 19 | geometry_summary_min.default <- function(x, na_rm) { 20 | cli_abort("The provided geometry does not support {.fn min}") 21 | } 22 | geometry_summary_max <- function(x, na_rm) { 23 | UseMethod("geometry_summary_max") 24 | } 25 | #' @export 26 | geometry_summary_max.default <- function(x, na_rm) { 27 | cli_abort("The provided geometry does not support {.fn max}") 28 | } 29 | 30 | #' @export 31 | Summary.euclid_geometry <- function(..., na.rm) { 32 | na.rm = isTRUE(na.rm) 33 | input <- do.call(c, list(...)) 34 | res <- switch(.Generic, 35 | "sum" = geometry_summary_sum(input, na.rm), 36 | "prod" = geometry_summary_prod(input, na.rm), 37 | "min" = geometry_summary_min(input, na.rm), 38 | "max" = geometry_summary_max(input, na.rm) 39 | ) 40 | restore_euclid_vector(res, input) 41 | } 42 | -------------------------------------------------------------------------------- /src/normal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cgal_types.h" 4 | #include 5 | 6 | template 7 | inline U normal_impl(const T& geometry) { 8 | return U::NA_value(); 9 | } 10 | 11 | template<> 12 | inline Direction_3 normal_impl(const Circle_3& geometry) { 13 | return geometry.supporting_plane().orthogonal_direction(); 14 | } 15 | 16 | template<> 17 | inline Direction_2 normal_impl(const Direction_2& geometry) { 18 | return geometry.perpendicular(CGAL::POSITIVE); 19 | } 20 | 21 | template<> 22 | inline Direction_2 normal_impl(const Line_2& geometry) { 23 | return geometry.direction().perpendicular(CGAL::POSITIVE); 24 | } 25 | 26 | template<> 27 | inline Direction_3 normal_impl(const Plane& geometry) { 28 | return geometry.orthogonal_direction(); 29 | } 30 | 31 | template<> 32 | inline Direction_2 normal_impl(const Ray_2& geometry) { 33 | return geometry.direction().perpendicular(CGAL::POSITIVE); 34 | } 35 | 36 | template<> 37 | inline Direction_2 normal_impl(const Segment_2& geometry) { 38 | return geometry.direction().perpendicular(CGAL::POSITIVE); 39 | } 40 | 41 | template<> 42 | inline Direction_3 normal_impl(const Triangle_3& geometry) { 43 | return geometry.supporting_plane().orthogonal_direction(); 44 | } 45 | 46 | template<> 47 | inline Direction_2 normal_impl(const Vector_2& geometry) { 48 | return geometry.direction().perpendicular(CGAL::POSITIVE); 49 | } 50 | -------------------------------------------------------------------------------- /.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 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | steps: 23 | - uses: actions/checkout@v3 24 | 25 | - uses: r-lib/actions/setup-pandoc@v2 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::pkgdown, local::. 34 | needs: website 35 | 36 | - name: Build site 37 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 38 | shell: Rscript {0} 39 | 40 | - name: Deploy to GitHub pages 🚀 41 | if: github.event_name != 'pull_request' 42 | uses: JamesIves/github-pages-deploy-action@v4.4.1 43 | with: 44 | clean: false 45 | branch: gh-pages 46 | folder: docs 47 | -------------------------------------------------------------------------------- /man/in_order.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{in_order} 4 | \alias{in_order} 5 | \alias{in_order_along} 6 | \title{Check whether points are collinear and ordered along the line} 7 | \usage{ 8 | in_order(x, y, z) 9 | 10 | in_order_along(x) 11 | } 12 | \arguments{ 13 | \item{x, y, z}{Vectors of points} 14 | } 15 | \value{ 16 | A logical vector 17 | } 18 | \description{ 19 | If points are \link{collinear} it is possible to ask whether they are ordered 20 | along the common line. This function is not strict, i.e. it is possible for 21 | the middle point to coincide with either the first or last point and still be 22 | regarded as ordered between them. This functionality comes in two versions, 23 | one that access orderedness between three vectors of points and one that 24 | assess it along a single vector. Note that for the later version the return 25 | value is 2 elements shorter than the input vector. 26 | } 27 | \examples{ 28 | p <- point(1:30, 1:30) 29 | p <- p[sample(30)] 30 | 31 | in_order(p[1:10], p[11:20], p[21:30]) 32 | 33 | in_order_along(p) 34 | 35 | } 36 | \seealso{ 37 | Other Predicates: 38 | \code{\link{collinear}()}, 39 | \code{\link{constant_in}}, 40 | \code{\link{geometry_class}}, 41 | \code{\link{geometry_turns}}, 42 | \code{\link{has_intersection}()}, 43 | \code{\link{is_degenerate}()}, 44 | \code{\link{location_predicates}}, 45 | \code{\link{parallel}()} 46 | } 47 | \concept{Predicates} 48 | -------------------------------------------------------------------------------- /man/map_to.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_projection.R 3 | \name{map_to} 4 | \alias{map_to} 5 | \alias{map_to.euclid_geometry} 6 | \title{Map 3D geometries to 2D based on plane} 7 | \usage{ 8 | map_to(x, target, ...) 9 | 10 | \method{map_to}{euclid_geometry}(x, target, ...) 11 | } 12 | \arguments{ 13 | \item{x}{A vector of geometries to project or map} 14 | 15 | \item{target}{A vector of planes} 16 | 17 | \item{...}{Arguments passed on to methods} 18 | } 19 | \value{ 20 | A vector of geometries in 2 dimensions 21 | } 22 | \description{ 23 | 3 dimensional geometries can be converted to 2 dimensions by mapping them to 24 | a plane. Not all 3 dimensional geometries can be converted to 2 dimensions as 25 | it requires for them to have a 2 dimensional counterpart. This rules out iso 26 | cubes, planes, and tetrahedrons. Spheres are a special case that will get 27 | converted to circles with the same radius. As no ellipsoid geometry exist 28 | the plane of circles is ignored and mapping a 3D circles thus creates a 2D 29 | circle of the same radius irrespective of the supporting plane of the circle. 30 | } 31 | \examples{ 32 | # Map a 3D point cloud to a plane defined by three random points 33 | p <- point(sample(10), sample(10), sample(10)) 34 | support <- p[sample(10, 3)] 35 | map_to(p, plane(support[1], support[2], support[3])) 36 | 37 | } 38 | \seealso{ 39 | Other Projections: 40 | \code{\link{normal}()}, 41 | \code{\link{project}()} 42 | } 43 | \concept{Projections} 44 | -------------------------------------------------------------------------------- /man/centroid.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_locations.R 3 | \name{centroid} 4 | \alias{centroid} 5 | \title{Calculate the centroid of a geometry or a set of points} 6 | \usage{ 7 | centroid(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A vector of triangles or tetrahedrons, or points if \code{y}, \code{z}, or \code{t} 11 | are provided} 12 | 13 | \item{...}{arguments passed on to methods. Specifically the method for 14 | \code{euclid_geometry} vectors accept up to three additional vectors of 15 | \code{euclid_point} (\code{y}, \code{z}, and \code{t} arguments) if \code{x} is a \code{euclid_point} 16 | vector} 17 | } 18 | \value{ 19 | a vector of points 20 | } 21 | \description{ 22 | The centroid is the geometric mean of the vertices in a geometry. euclid 23 | supports centroid calculations for triangles, tetrahedrons and 2-4 points 24 | } 25 | \examples{ 26 | p <- point(c(1, 5, 6), c(1, -2, 4)) 27 | # centroid of triangle 28 | t <- triangle(p[1], p[2], p[3]) 29 | centroid(t) 30 | 31 | plot(t, col = "grey", border = NA) 32 | euclid_plot(centroid(t), pch = 16, cex = 2, col = "firebrick") 33 | 34 | # Same as providing the points directly 35 | centroid(p[1], p[2], p[3]) 36 | 37 | } 38 | \seealso{ 39 | Other Locations: 40 | \code{\link{barycenter}()}, 41 | \code{\link{bisector}()}, 42 | \code{\link{circumcenter}()}, 43 | \code{\link{equidistant_line}()}, 44 | \code{\link{point}()}, 45 | \code{\link{radical}()}, 46 | \code{\link{weighted_point}()} 47 | } 48 | \concept{Locations} 49 | -------------------------------------------------------------------------------- /man/circumcenter.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_locations.R 3 | \name{circumcenter} 4 | \alias{circumcenter} 5 | \title{Calculate circumcenter of triangles, tetrahedrons, or sets of points} 6 | \usage{ 7 | circumcenter(...) 8 | } 9 | \arguments{ 10 | \item{...}{geometry vectors. Either a triangle or tetrahedron vector, or 2, 11 | 3, or 4 point vectors. If 4 are provided they must be in 3D} 12 | } 13 | \value{ 14 | A points vector 15 | } 16 | \description{ 17 | The circumcenter is the center of the circle of sphere passing through the 18 | vertices of a geometry (or the provided points). 19 | } 20 | \examples{ 21 | # Circumcenter of triangle 22 | t <- triangle(point(1, 1), point(5, -2), point(6, 4)) 23 | circumcenter(t) 24 | 25 | # Same as providing the points directly 26 | circumcenter(point(1, 1), point(5, -2), point(6, 4)) 27 | 28 | # Same as getting the center of a circle constructed from the points 29 | circ <- circle(point(1, 1), point(5, -2), point(6, 4)) 30 | vert(circ) 31 | 32 | plot(circ) 33 | euclid_plot(t, col = "grey", border = NA) 34 | euclid_plot(circumcenter(t), cex = 3, col = "firebrick", lwd = 2) 35 | euclid_plot(vert(circ), cex = 1.5, col = "steelblue", lwd = 2) 36 | 37 | } 38 | \seealso{ 39 | Other Locations: 40 | \code{\link{barycenter}()}, 41 | \code{\link{bisector}()}, 42 | \code{\link{centroid}()}, 43 | \code{\link{equidistant_line}()}, 44 | \code{\link{point}()}, 45 | \code{\link{radical}()}, 46 | \code{\link{weighted_point}()} 47 | } 48 | \concept{Locations} 49 | -------------------------------------------------------------------------------- /.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 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: any::covr 27 | needs: coverage 28 | 29 | - name: Test coverage 30 | run: | 31 | covr::codecov( 32 | quiet = FALSE, 33 | clean = FALSE, 34 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package") 35 | ) 36 | shell: Rscript {0} 37 | 38 | - name: Show testthat output 39 | if: always() 40 | run: | 41 | ## -------------------------------------------------------------------- 42 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 43 | shell: bash 44 | 45 | - name: Upload test results 46 | if: failure() 47 | uses: actions/upload-artifact@v3 48 | with: 49 | name: coverage-test-failures 50 | path: ${{ runner.temp }}/package 51 | -------------------------------------------------------------------------------- /man/geometry_class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{geometry_class} 4 | \alias{geometry_class} 5 | \alias{is_surface} 6 | \alias{is_volume} 7 | \alias{is_curve} 8 | \alias{is_arrow} 9 | \alias{is_location} 10 | \title{Query geometry class} 11 | \usage{ 12 | is_surface(x) 13 | 14 | is_volume(x) 15 | 16 | is_curve(x) 17 | 18 | is_arrow(x) 19 | 20 | is_location(x) 21 | } 22 | \arguments{ 23 | \item{x}{A geometry vector} 24 | } 25 | \value{ 26 | a logical scalar 27 | } 28 | \description{ 29 | While the geometry types provided by euclid are primitives, they can be 30 | grouped based on their properties. Lines, rays, and segments are all curves 31 | in space, with rays and segments being the semi-finite and finite version of 32 | lines respectively. Circles, planes, triangles, and iso rectangles are all 33 | surfaces in the sense that they define an area but doesn't have a volume. 34 | Spheres and iso cubes are volumes. Vectors and directions are arrows. Points 35 | and weighted points are locations. The class of geometry can be checked with 36 | the given functions 37 | } 38 | \examples{ 39 | l <- line(3, 8, 1) 40 | is_surface(l) 41 | is_curve(l) 42 | is_volume(l) 43 | 44 | } 45 | \seealso{ 46 | Other Predicates: 47 | \code{\link{collinear}()}, 48 | \code{\link{constant_in}}, 49 | \code{\link{geometry_turns}}, 50 | \code{\link{has_intersection}()}, 51 | \code{\link{in_order}()}, 52 | \code{\link{is_degenerate}()}, 53 | \code{\link{location_predicates}}, 54 | \code{\link{parallel}()} 55 | } 56 | \concept{Predicates} 57 | -------------------------------------------------------------------------------- /man/parallel.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{parallel} 4 | \alias{parallel} 5 | \title{Check geometries for parallelity} 6 | \usage{ 7 | parallel(x, y) 8 | } 9 | \arguments{ 10 | \item{x, y}{vectors of geometries} 11 | } 12 | \value{ 13 | a logical vector giving the parallelity of the input 14 | } 15 | \description{ 16 | If two geometries are parallel they have the same orientation. euclid allows 17 | assessing parallelity of a broad range of geometries. If \code{x} and \code{y} are 18 | surfaces the parallelity of their supporting plane is returned. If \code{x} and 19 | \code{y} are curves, the parallelity of their supporting line is returned. If it 20 | is a mix of surface and curve, the parallelity is calculated as whether the 21 | supporting plane and supporting line intersects at a point. 22 | } 23 | \examples{ 24 | # Are two triangles parallel (only meaningful in 3D) 25 | p <- point(sample(20, 6), sample(20, 6), sample(20, 6)) 26 | t1 <- triangle(p[1], p[2], p[3]) 27 | t2 <- triangle(p[4], p[5], p[6]) 28 | 29 | parallel(t1, t2) 30 | 31 | # Same as: 32 | parallel(as_plane(t1), as_plane(t2)) 33 | 34 | # Parallelity of surface and curve 35 | parallel(t1, line(p[5], p[6])) 36 | 37 | } 38 | \seealso{ 39 | Other Predicates: 40 | \code{\link{collinear}()}, 41 | \code{\link{constant_in}}, 42 | \code{\link{geometry_class}}, 43 | \code{\link{geometry_turns}}, 44 | \code{\link{has_intersection}()}, 45 | \code{\link{in_order}()}, 46 | \code{\link{is_degenerate}()}, 47 | \code{\link{location_predicates}} 48 | } 49 | \concept{Predicates} 50 | -------------------------------------------------------------------------------- /R/aaa.R: -------------------------------------------------------------------------------- 1 | validate_constructor_input <- function(..., .convert_numerics = TRUE) { 2 | inputs <- lapply(list(...), function(x) { 3 | if (.convert_numerics && !is_base_geometry(x) && is.numeric(x)) { 4 | x <- as_exact_numeric(x) 5 | } 6 | x 7 | }) 8 | dim <- unique(unlist(lapply(inputs, dim))) 9 | if (length(dim) > 1) { 10 | cli_abort("Inputs must be of the same dimensionality") 11 | } 12 | input_lengths <- lengths(inputs) 13 | if (length(input_lengths) == 0 || any(lengths(input_lengths)) == 0) { 14 | return(list()) 15 | } 16 | max_length <- max(input_lengths) 17 | if (any(input_lengths != 1 & input_lengths != max_length)) { 18 | cli_abort("Inputs must be either scalar or of the same length") 19 | } 20 | inputs[input_lengths == 1] <- lapply(inputs[input_lengths == 1], function(x) { 21 | rep_len(x, max_length) 22 | }) 23 | inputs 24 | } 25 | check_geometry_input <- function(..., .name) { 26 | input <- list(...) 27 | input <- input[!vapply(input, is.null, logical(1))] 28 | if (!all(vapply(input, is_base_geometry, logical(1)))) { 29 | cli_abort("{.fn {.name}} is only defined for geometries") 30 | } 31 | if (length(unique(vapply(input, dim, integer(1)))) != 1) { 32 | cli_abort("Geometries must have the same dimensionality") 33 | } 34 | invisible(NULL) 35 | } 36 | 37 | get_ptr <- function(x) .subset2(x, 1L) 38 | 39 | restore_euclid_vector <- function(x, old) { 40 | x <- list(x) 41 | attributes(x) <- attributes(old) 42 | x 43 | } 44 | 45 | # rep_len is broken on R < 4.0 46 | rep_len <- function(x, length) { 47 | rep(x, length.out = length) 48 | } 49 | -------------------------------------------------------------------------------- /man/normal.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_projection.R 3 | \name{normal} 4 | \alias{normal} 5 | \alias{normal.euclid_geometry} 6 | \title{Get the normal of a geometry} 7 | \usage{ 8 | normal(x, ...) 9 | 10 | \method{normal}{euclid_geometry}(x, y = NULL, ...) 11 | } 12 | \arguments{ 13 | \item{x, y}{Geometry vectors. If \code{y} is given both \code{x} and \code{y} must be 14 | convertible to vectors and be in 3D} 15 | 16 | \item{...}{Arguments passed on to methods} 17 | } 18 | \value{ 19 | A vector of directions with the same dimensionality as the input 20 | } 21 | \description{ 22 | A normal is a direction perpendicular to a geometry. Since normals doesn't 23 | have any magnitude they are returned as directions rather than vectors. 24 | Normals are not given for all geometries. In 2 dimensions they are defined 25 | for line-like geometries (directions, vectors, lines, rays, and segments), 26 | while in 3 dimensions they are given for plane-like geometries (circles, 27 | triangles, and planes). 28 | } 29 | \examples{ 30 | # Get normals to a vector of directions 31 | d <- direction(sample(10, 5), sample(10,5)) 32 | normal(d) 33 | 34 | # Get the normal of a triangle 35 | t <- triangle(point(4, 7, 1), point(9, 2, 1), point(1, 6, 8)) 36 | normal(t) 37 | 38 | # equivalent to the normal of the supporting plane 39 | normal(t) == normal(as_plane(t)) 40 | 41 | # Get direction orthogonal to two vectors 42 | normal(vec(5, 2, -7), vec(-1, 4, 9)) 43 | 44 | } 45 | \seealso{ 46 | Other Projections: 47 | \code{\link{map_to}()}, 48 | \code{\link{project}()} 49 | } 50 | \concept{Projections} 51 | -------------------------------------------------------------------------------- /src/triangle.cpp: -------------------------------------------------------------------------------- 1 | #include "triangle.h" 2 | #include "point.h" 3 | 4 | [[cpp11::register]] 5 | triangle2_p create_triangle_2_empty() { 6 | std::vector vec; 7 | triangle2 *result(new triangle2(vec)); 8 | return {result}; 9 | } 10 | 11 | [[cpp11::register]] 12 | triangle2_p create_triangle_2_3points(point2_p p, point2_p q, point2_p r) { 13 | if (p.get() == nullptr || q.get() == nullptr || r.get() == nullptr) { 14 | cpp11::stop("Data structure pointer cleared from memory"); 15 | } 16 | std::vector vec; 17 | vec.reserve(p->size()); 18 | for (size_t i = 0; i < p->size(); ++i) { 19 | if (!(*p)[i] || !(*q)[i] || !(*r)[i]) { 20 | vec.push_back(Triangle_2::NA_value()); 21 | continue; 22 | } 23 | vec.emplace_back((*p)[i], (*q)[i], (*r)[i]); 24 | } 25 | triangle2 *result(new triangle2(vec)); 26 | 27 | return {result}; 28 | } 29 | 30 | [[cpp11::register]] 31 | triangle3_p create_triangle_3_empty() { 32 | std::vector vec; 33 | triangle3 *result(new triangle3(vec)); 34 | return {result}; 35 | } 36 | 37 | [[cpp11::register]] 38 | triangle3_p create_triangle_3_3points(point3_p p, point3_p q, point3_p r) { 39 | if (p.get() == nullptr || q.get() == nullptr || r.get() == nullptr) { 40 | cpp11::stop("Data structure pointer cleared from memory"); 41 | } 42 | std::vector vec; 43 | vec.reserve(p->size()); 44 | for (size_t i = 0; i < p->size(); ++i) { 45 | if (!(*p)[i] || !(*q)[i] || !(*r)[i]) { 46 | vec.push_back(Triangle_3::NA_value()); 47 | continue; 48 | } 49 | vec.emplace_back((*p)[i], (*q)[i], (*r)[i]); 50 | } 51 | triangle3 *result(new triangle3(vec)); 52 | 53 | return {result}; 54 | } 55 | -------------------------------------------------------------------------------- /man/distance_squared.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_measures.R 3 | \name{distance_squared} 4 | \alias{distance_squared} 5 | \alias{approx_distance_matrix} 6 | \title{Calculate distances between geometries} 7 | \usage{ 8 | distance_squared(x, y, ...) 9 | 10 | approx_distance_matrix(x, y, ...) 11 | } 12 | \arguments{ 13 | \item{x, y}{geometry vectors or bounding boxes} 14 | 15 | \item{...}{arguments passed on to methods} 16 | } 17 | \value{ 18 | A \code{euclid_exact_numeric} vector for \code{distance_squared()} and a 19 | numeric matrix for \code{distance_matrix()} 20 | } 21 | \description{ 22 | The minimum distance between two arbitrary geometries is non-trivial and is 23 | only exactly defined for non-circular geometries. \code{distance_squared()} will 24 | return the exact squared distance between geometries with \code{x} and \code{y} being 25 | recycled to the maximum length of either. \code{distance_matrix} will return a 26 | matrix of distances given as numerics (and thus not exact), with the 27 | geometries of \code{x} in the rows and the geometries of \code{y} in the columns so 28 | that the value of \code{mat[i, j]} corresponds to the distance between \code{x[i]} and 29 | \code{y[j]}. 30 | } 31 | \examples{ 32 | # Calculate distances between lines and rays in 3D 33 | p <- point(sample(100, 20), sample(100, 20), sample(100, 20)) 34 | l <- line(p[1:5], p[6:10]) 35 | r <- ray(p[11:15], p[16:20]) 36 | 37 | # Pairwise exact distance 38 | distance_squared(l, r) 39 | 40 | # All distances 41 | approx_distance_matrix(l, r) 42 | 43 | } 44 | \seealso{ 45 | Other Measures: 46 | \code{\link{approx_angle}()}, 47 | \code{\link{geometry_measures}} 48 | } 49 | \concept{Measures} 50 | -------------------------------------------------------------------------------- /man/constant_in.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{constant_in} 4 | \alias{constant_in} 5 | \alias{is_constant_in} 6 | \alias{has_constant_x} 7 | \alias{has_constant_y} 8 | \alias{has_constant_z} 9 | \title{Check geometries for axis alignment} 10 | \usage{ 11 | is_constant_in(x, axis) 12 | 13 | has_constant_x(x) 14 | 15 | has_constant_y(x) 16 | 17 | has_constant_z(x) 18 | } 19 | \arguments{ 20 | \item{x}{A geometry vector} 21 | 22 | \item{axis}{One or more specifications of the axes to check against as \code{"x"}, 23 | \code{"y"}, \code{"z"} or \code{1}, \code{2}, \code{3}} 24 | } 25 | \value{ 26 | A logical vector 27 | } 28 | \description{ 29 | These predicate functions checks for whether a geometry varies along a given 30 | axis. While it is common in 2 dimensions to ask whether something is vertical 31 | or horizontal that notion does not scale well to 3 dimensions and euclid 32 | instead elects to check for whether a given geometry is constant in a 33 | specific coordinate value. As such, e.g. \code{has_constant_x()} is equivalent to 34 | asking whether a given 2 dimensional geometry is vertical but also works for 35 | 3 dimensional geometries. 36 | } 37 | \examples{ 38 | # Check for horizontal vector 39 | v <- vec(1, -2:2) 40 | has_constant_y(v) 41 | 42 | # Use recycling to check all axes 43 | is_constant_in(vec(2, 0, -5), c("x", "y", "z")) 44 | 45 | } 46 | \seealso{ 47 | Other Predicates: 48 | \code{\link{collinear}()}, 49 | \code{\link{geometry_class}}, 50 | \code{\link{geometry_turns}}, 51 | \code{\link{has_intersection}()}, 52 | \code{\link{in_order}()}, 53 | \code{\link{is_degenerate}()}, 54 | \code{\link{location_predicates}}, 55 | \code{\link{parallel}()} 56 | } 57 | \concept{Predicates} 58 | -------------------------------------------------------------------------------- /man/has_intersection.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_intersection.R 3 | \name{has_intersection} 4 | \alias{has_intersection} 5 | \alias{\%is_intersecting\%} 6 | \title{Query whether geometries intersect} 7 | \usage{ 8 | has_intersection(x, y, ...) 9 | 10 | x \%is_intersecting\% y 11 | } 12 | \arguments{ 13 | \item{x, y}{Geometry vectors or bounding boxes} 14 | 15 | \item{...}{Arguments passed on to methods} 16 | } 17 | \value{ 18 | a logical vector 19 | } 20 | \description{ 21 | While it is sometimes necessary to get the actual intersection geometry, it 22 | may other times only be necessary to query whether an intersection exists. 23 | This is in general much faster than checking if \code{\link[=intersection]{intersection()}} returns 24 | \code{NULL}. Further, it is not bounded by the same constraint that an exact 25 | intersection must be constructable so a wider range of geometries are 26 | supported for the predicate. If the query is not possible it will return \code{NA} 27 | rather than an error 28 | } 29 | \examples{ 30 | # Example of the predicate 31 | t <- triangle(point(0, 0), point(1, 1), point(0, 1)) 32 | l <- line(1, -1, c(0, 1, 2)) 33 | has_intersection(t, l) 34 | 35 | # Use binary operator instead 36 | t \%is_intersecting\% l 37 | 38 | # 2 dimensional circles are better supported 39 | l \%is_intersecting\% circle(point(7, 4), 9) 40 | 41 | } 42 | \seealso{ 43 | Other Intersections: 44 | \code{\link{intersection}()} 45 | 46 | Other Predicates: 47 | \code{\link{collinear}()}, 48 | \code{\link{constant_in}}, 49 | \code{\link{geometry_class}}, 50 | \code{\link{geometry_turns}}, 51 | \code{\link{in_order}()}, 52 | \code{\link{is_degenerate}()}, 53 | \code{\link{location_predicates}}, 54 | \code{\link{parallel}()} 55 | } 56 | \concept{Intersections} 57 | \concept{Predicates} 58 | -------------------------------------------------------------------------------- /man/is_degenerate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{is_degenerate} 4 | \alias{is_degenerate} 5 | \title{Check if a geometry is degenerate} 6 | \usage{ 7 | is_degenerate(x) 8 | } 9 | \arguments{ 10 | \item{x}{A geometry vector} 11 | } 12 | \value{ 13 | A logical vector 14 | } 15 | \description{ 16 | Geometries can be ill-defined for several reasons, e.g. circles with a radius 17 | of zero, segments that start and end at the same location, etc. See details 18 | for a list of conditions that are considered degenerate. 19 | } 20 | \details{ 21 | The following conditions are considered to produce degenerate geometries: 22 | \itemize{ 23 | \item A circle having a radius of zero 24 | \item An iso-rectangle with zero width or zero height 25 | \item An iso-cuboid with zero width, zero height, or zero depth 26 | \item A 2D line with the coefficients being zero 27 | \item A 3D line that is degenerated to a point 28 | \item A plane with the coefficients being zero 29 | \item A ray with the source and the second defining point being equal 30 | \item A segment with the source and target being equal 31 | \item A sphere with a radius of zero 32 | \item A tetrahedron with all vertices being co-planar 33 | \item A triangle with collinear vertices 34 | } 35 | } 36 | \examples{ 37 | # A line without direction 38 | l <- line(point(0, 5), vec(0, 0)) 39 | is_degenerate(l) 40 | 41 | # A circle without radius 42 | circ <- circle(point(3, -1), 0) 43 | is_degenerate(circ) 44 | 45 | } 46 | \seealso{ 47 | Other Predicates: 48 | \code{\link{collinear}()}, 49 | \code{\link{constant_in}}, 50 | \code{\link{geometry_class}}, 51 | \code{\link{geometry_turns}}, 52 | \code{\link{has_intersection}()}, 53 | \code{\link{in_order}()}, 54 | \code{\link{location_predicates}}, 55 | \code{\link{parallel}()} 56 | } 57 | \concept{Predicates} 58 | -------------------------------------------------------------------------------- /man/tetrahedron.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tetrahedron.R 3 | \name{tetrahedron} 4 | \alias{tetrahedron} 5 | \alias{is_tetrahedron} 6 | \alias{as_tetrahedron} 7 | \title{Vector of tetrahedrons} 8 | \usage{ 9 | tetrahedron(...) 10 | 11 | is_tetrahedron(x) 12 | 13 | as_tetrahedron(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{x}{A vector of tetrahedrons or an object to convert to it} 19 | } 20 | \value{ 21 | An \code{euclid_tetrahedron} vector 22 | } 23 | \description{ 24 | A tetrahedron, or triangular pyramid, is a polyhedron consisting of 4 25 | triangles between 4 vertices. A tetrahedron splits the euclidean space in two 26 | by the plane defined by the first 3 vertices and the positive side being the 27 | side that includes the fourth vertex. If a all 4 vertices are coplanar the 28 | tetrahedron is considered \link[=is_degenerate]{degenerate}. Tetrahedrons only 29 | exists in 3 dimensions. 30 | } 31 | \section{Constructors}{ 32 | 33 | \strong{3 dimensional tetrahedrons} 34 | \itemize{ 35 | \item Providing four points will construct tetrahedrons in the order the points 36 | are given. 37 | } 38 | } 39 | 40 | \examples{ 41 | p <- point(sample(8), sample(8), sample(8)) 42 | tetrahedron(p[1:2], p[3:4], p[5:6], p[7:8]) 43 | 44 | } 45 | \seealso{ 46 | Other Geometries: 47 | \code{\link{circle}()}, 48 | \code{\link{direction}()}, 49 | \code{\link{iso_cube}()}, 50 | \code{\link{iso_rect}()}, 51 | \code{\link{line}()}, 52 | \code{\link{plane}()}, 53 | \code{\link{point}()}, 54 | \code{\link{ray}()}, 55 | \code{\link{segment}()}, 56 | \code{\link{sphere}()}, 57 | \code{\link{triangle}()}, 58 | \code{\link{vec}()}, 59 | \code{\link{weighted_point}()} 60 | 61 | Other Volumes: 62 | \code{\link{iso_cube}()}, 63 | \code{\link{sphere}()} 64 | } 65 | \concept{Geometries} 66 | \concept{Volumes} 67 | -------------------------------------------------------------------------------- /man/radical.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_locations.R 3 | \name{radical} 4 | \alias{radical} 5 | \title{Calculate radical point, line, or plane of two circles or spheres} 6 | \usage{ 7 | radical(x, y, z = NULL) 8 | } 9 | \arguments{ 10 | \item{x, y, z}{vector of circles in 2D or spheres. If \code{z} is given the radical 11 | point will be computed} 12 | } 13 | \value{ 14 | A vector of lines or planes depending on the input 15 | } 16 | \description{ 17 | The radical line or plane is the line (or plane) perpendicular to the line 18 | connecting the two circles or spheres and positioned such that at any point 19 | the tangent lines drawn to the two circles or spheres will have equal length. 20 | If the circles or sphere cross the radical will be positioned at the crossing 21 | points. It is not defined for cocentric circles and spheres. The radical 22 | point is defined as the intersection point of the three radical lines of 23 | three circles, or three radical planes of three spheres. 24 | } 25 | \examples{ 26 | c1 <- circle(point(3, 6), 12) 27 | c2 <- circle(point(-5, 0), 3) 28 | c3 <- circle(point(-3, 7), 1) 29 | 30 | # Radical line 31 | radical(c1, c2) 32 | 33 | # radical point 34 | radical(c1, c2, c3) 35 | 36 | plot(c(c1, c2, c3), bg = "grey", fg = NA) 37 | euclid_plot(c( 38 | radical(c1, c2), 39 | radical(c2, c3), 40 | radical(c1, c3) 41 | ), col = "firebrick") 42 | euclid_plot(radical(c1, c2, c3), pch = 16, cex = 2, col = "steelblue") 43 | 44 | # Radical plane 45 | radical( 46 | sphere(point(3, 6, 3), 12), 47 | sphere(point(-6, 2, 7), 3) 48 | ) 49 | 50 | } 51 | \seealso{ 52 | Other Locations: 53 | \code{\link{barycenter}()}, 54 | \code{\link{bisector}()}, 55 | \code{\link{centroid}()}, 56 | \code{\link{circumcenter}()}, 57 | \code{\link{equidistant_line}()}, 58 | \code{\link{point}()}, 59 | \code{\link{weighted_point}()} 60 | } 61 | \concept{Locations} 62 | -------------------------------------------------------------------------------- /man/geometry_turns.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{geometry_turns} 4 | \alias{geometry_turns} 5 | \alias{turns_left} 6 | \alias{turns_right} 7 | \alias{turn_along} 8 | \title{Query the side of the turn constructed by three consecutive points} 9 | \usage{ 10 | turns_left(x, y, z) 11 | 12 | turns_right(x, y, z) 13 | 14 | turn_along(x) 15 | } 16 | \arguments{ 17 | \item{x, y, z}{Vectors of 2D points} 18 | } 19 | \value{ 20 | Either a logical vector or an integer vector (for \code{turn_along()}) 21 | } 22 | \description{ 23 | In 2D the bend three consecutive points form can be oriented either to the 24 | left or two the right. Asking whether it takes a left turn is equivalent to 25 | asking whether \code{z} lies on the positive side of the supporting line of 26 | \code{x}->\code{y}. euclid provides two predicate functions to check whether left or 27 | right turns are constructed from three given point vectors (note that 28 | collinear points will be \code{FALSE} for both predicates). It also provides a 29 | function that calculate turn orientation along a single vector of points. The 30 | result of this are given as: \code{1} (left), \code{0} (collinear), or \code{-1} (right). 31 | Note that the output of \code{turn_along()} is two elements shorter than the 32 | input. 33 | } 34 | \examples{ 35 | p <- point(sample(20, 12), sample(20, 12)) 36 | 37 | turns_left(p[1:4], p[5:8], p[9:12]) 38 | 39 | turn_along(p) 40 | 41 | plot(segment(p[-12], p[-1])) 42 | euclid_plot(p[-c(1, 12)][turn_along(p) == 1], pch = 16, col = "firebrick") 43 | euclid_plot(p[-c(1, 12)][turn_along(p) == -1], pch = 16, col = "steelblue") 44 | 45 | } 46 | \seealso{ 47 | Other Predicates: 48 | \code{\link{collinear}()}, 49 | \code{\link{constant_in}}, 50 | \code{\link{geometry_class}}, 51 | \code{\link{has_intersection}()}, 52 | \code{\link{in_order}()}, 53 | \code{\link{is_degenerate}()}, 54 | \code{\link{location_predicates}}, 55 | \code{\link{parallel}()} 56 | } 57 | \concept{Predicates} 58 | -------------------------------------------------------------------------------- /man/bbox.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bbox.R 3 | \name{bbox} 4 | \alias{bbox} 5 | \alias{is_bbox} 6 | \alias{as_bbox} 7 | \title{Create a vector of bounding boxes} 8 | \usage{ 9 | bbox(..., default_dim = 2) 10 | 11 | is_bbox(x) 12 | 13 | as_bbox(x) 14 | } 15 | \arguments{ 16 | \item{...}{Either a vector of geometries or a range of numeric vectors (4 for 17 | 2D bounding boxes and 6 for 3D) interpreted in the order xmin, ymin, zmin, 18 | xmax, ymax, zmax.} 19 | 20 | \item{default_dim}{The dimensionality when constructing an empty vector} 21 | 22 | \item{x}{vector of bounding boxes or geometries} 23 | } 24 | \value{ 25 | An \code{euclid_bbox} vector 26 | } 27 | \description{ 28 | Bounding boxes denote the exten of geometries. It follows that bounding boxes 29 | are only defined for geometries that are located in space and have finite 30 | extent. Thus, vectors, lines, directions, rays, etc. does not have bounding 31 | boxes, while e.g. spheres, circles, segments, etc has. Since the extent of 32 | geometries cannot always be given exact (e.g. for circles), bounding boxes 33 | are defined in regular floating point precision. Bounding boxes can be 34 | compared for equality and be tested for whether they overlap. Adding bounding 35 | boxes together will give the bounding box containing both. 36 | } 37 | \examples{ 38 | # Construction 39 | bbox(10, -2, 15, 0) 40 | 41 | seg <- segment(point(sample(10, 4), sample(10, 4)), 42 | point(sample(10, 4), sample(10, 4))) 43 | 44 | boxes <- bbox(seg) 45 | boxes 46 | 47 | plot(seg) 48 | euclid_plot(boxes, fg = "firebrick") 49 | 50 | # Comparison 51 | boxes[1] == boxes 52 | 53 | boxes[1:2] \%is_intersecting\% boxes[3:4] 54 | 55 | # Addition 56 | boxes[1] + boxes[2] 57 | 58 | cumsum(boxes) 59 | 60 | plot(sum(boxes), bg = "grey", fg = NA) 61 | euclid_plot(boxes) 62 | 63 | # Conversion 64 | as.matrix(boxes) 65 | 66 | } 67 | \seealso{ 68 | Other non-geometry vectors: 69 | \code{\link{affine_transformation}}, 70 | \code{\link{exact_numeric}()} 71 | } 72 | \concept{non-geometry vectors} 73 | -------------------------------------------------------------------------------- /man/triangle.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/triangle.R 3 | \name{triangle} 4 | \alias{triangle} 5 | \alias{is_triangle} 6 | \alias{as_triangle} 7 | \title{Vector of triangles} 8 | \usage{ 9 | triangle(..., default_dim = 2) 10 | 11 | is_triangle(x) 12 | 13 | as_triangle(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A vector of triangles or an object to convert to it} 21 | } 22 | \value{ 23 | An \code{euclid_triangle} vector 24 | } 25 | \description{ 26 | Triangles are sets of three vertices in either 2 or three dimensions. In 27 | three dimensions the order of the vertices determines the orientation of the 28 | triangle with vertices being in counter-clockwise order from the facing side. 29 | If the 3 vertices are colinear the triangle is considered to be 30 | \link[=is_degenerate]{degenerate} 31 | } 32 | \section{Constructors}{ 33 | 34 | \strong{2 and 3 dimensional triangles} 35 | \itemize{ 36 | \item Providing three points will construct triangles in the order the points are 37 | given. 38 | } 39 | } 40 | 41 | \examples{ 42 | # Construction 43 | p <- point(sample(6), sample(6)) 44 | t <- triangle(p[1:2], p[3:4], p[5:6]) 45 | t 46 | plot(t) 47 | 48 | # 3D triangles can be converted to planes 49 | p <- point(sample(6), sample(6), sample(6)) 50 | t <- triangle(p[1:2], p[3:4], p[5:6]) 51 | as_plane(t) 52 | 53 | } 54 | \seealso{ 55 | Other Geometries: 56 | \code{\link{circle}()}, 57 | \code{\link{direction}()}, 58 | \code{\link{iso_cube}()}, 59 | \code{\link{iso_rect}()}, 60 | \code{\link{line}()}, 61 | \code{\link{plane}()}, 62 | \code{\link{point}()}, 63 | \code{\link{ray}()}, 64 | \code{\link{segment}()}, 65 | \code{\link{sphere}()}, 66 | \code{\link{tetrahedron}()}, 67 | \code{\link{vec}()}, 68 | \code{\link{weighted_point}()} 69 | 70 | Other Surfaces: 71 | \code{\link{circle}()}, 72 | \code{\link{iso_rect}()}, 73 | \code{\link{plane}()} 74 | } 75 | \concept{Geometries} 76 | \concept{Surfaces} 77 | -------------------------------------------------------------------------------- /.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 | branches: [main, master] 12 | 13 | name: R-CMD-check 14 | 15 | jobs: 16 | R-CMD-check: 17 | runs-on: ${{ matrix.config.os }} 18 | 19 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config: 25 | - {os: macos-latest, r: 'release'} 26 | 27 | - {os: windows-latest, r: 'release'} 28 | # Use 3.6 to trigger usage of RTools35 29 | - {os: windows-latest, r: '3.6'} 30 | # use 4.1 to check with rtools40's older compiler 31 | - {os: windows-latest, r: '4.1'} 32 | 33 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 34 | - {os: ubuntu-latest, r: 'release'} 35 | - {os: ubuntu-latest, r: 'oldrel-1'} 36 | - {os: ubuntu-latest, r: 'oldrel-2'} 37 | - {os: ubuntu-latest, r: 'oldrel-3'} 38 | - {os: ubuntu-latest, r: 'oldrel-4'} 39 | 40 | env: 41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 42 | R_KEEP_PKG_SOURCE: yes 43 | 44 | steps: 45 | - uses: actions/checkout@v3 46 | 47 | - uses: r-lib/actions/setup-pandoc@v2 48 | 49 | - uses: r-lib/actions/setup-r@v2 50 | with: 51 | r-version: ${{ matrix.config.r }} 52 | http-user-agent: ${{ matrix.config.http-user-agent }} 53 | use-public-rspm: true 54 | 55 | - uses: r-lib/actions/setup-r-dependencies@v2 56 | with: 57 | extra-packages: any::rcmdcheck 58 | needs: check 59 | 60 | - uses: r-lib/actions/check-r-package@v2 61 | with: 62 | upload-snapshots: true 63 | -------------------------------------------------------------------------------- /src/distance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cgal_types.h" 4 | #include "is_degenerate.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | template 12 | inline std::vector squared_distance_impl(const std::vector& geo1, const std::vector& geo2) { 13 | if (geo1.size() == 0 || geo2.size() == 0) { 14 | return {}; 15 | } 16 | size_t output_size = std::max(geo1.size(), geo2.size()); 17 | std::vector res; 18 | res.reserve(output_size); 19 | for (size_t i = 0; i < output_size; ++i) { 20 | if (invalid_geo(geo1[i % geo1.size()]) || invalid_geo(geo2[i % geo2.size()])) { 21 | res.push_back(Exact_number::NA_value()); 22 | continue; 23 | } 24 | Exact_number dist2 = CGAL::squared_distance(geo1[i % geo1.size()], geo2[i % geo2.size()]); 25 | res.push_back(dist2); 26 | } 27 | return res; 28 | } 29 | 30 | inline std::vector unknown_squared_distance_impl(size_t size) { 31 | std::vector res; 32 | res.reserve(size); 33 | for (size_t i = 0; i < size; ++i) { 34 | res.push_back(Exact_number::NA_value()); 35 | } 36 | return res; 37 | } 38 | 39 | template 40 | inline cpp11::writable::doubles_matrix<> distance_matrix_impl(const std::vector& geo1, const std::vector& geo2) { 41 | cpp11::writable::doubles_matrix<> res(geo1.size(), geo2.size()); 42 | for (size_t i = 0; i < geo1.size(); ++i) { 43 | for (size_t j = 0; j < geo2.size(); ++j) { 44 | if (invalid_geo(geo1[i]) || invalid_geo(geo2[j])) { 45 | res(i, j) = R_NaReal; 46 | continue; 47 | } 48 | res(i, j) = CGAL::sqrt(CGAL::to_double(CGAL::squared_distance(geo1[i], geo2[j]))); 49 | } 50 | } 51 | return res; 52 | } 53 | 54 | inline cpp11::writable::doubles_matrix<> unknown_distance_matrix_impl(size_t nrow, size_t ncol) { 55 | cpp11::writable::doubles_matrix<> res(nrow, ncol); 56 | for (size_t i = 0; i < nrow; ++i) { 57 | for (size_t j = 0; j < ncol; ++j) { 58 | res(i, j) = R_NaReal; 59 | } 60 | } 61 | return res; 62 | } 63 | -------------------------------------------------------------------------------- /man/iso_cube.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iso_cube.R 3 | \name{iso_cube} 4 | \alias{iso_cube} 5 | \alias{is_iso_cube} 6 | \alias{as_iso_cube} 7 | \title{Vector of iso cubes} 8 | \usage{ 9 | iso_cube(...) 10 | 11 | is_iso_cube(x) 12 | 13 | as_iso_cube(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{x}{A vector of iso cubes or an object to convert to it} 19 | } 20 | \value{ 21 | An \code{euclid_iso_cube} vector 22 | } 23 | \description{ 24 | Iso cubes are axis-aligned cuboids, i.e. a cube with faces parallel to the x, 25 | y, and z plane. Iso cuboids are the 3 dimensional version of 26 | \link[=iso_rect]{iso rectangles} and shares the feature that it can be constructed 27 | from bounding boxes (in 3D). An Iso cube is \link[=is_degenerate]{degenerate} all 28 | its vertices are coplanar. 29 | } 30 | \section{Constructors}{ 31 | 32 | \strong{3 dimensional iso cubes} 33 | \itemize{ 34 | \item Providing a bbox will create iso cubes matching the bbox 35 | \item Providing two points will create iso cubes with the points as diagonal 36 | opposite vertices 37 | \item Providing 6 numeric will construct iso cubes with the given extent, 38 | with the numerics being interpreted in the following order: minimum x, 39 | minimum y, minimum z, maximum x, maximum y, and maximum z 40 | } 41 | } 42 | 43 | \examples{ 44 | # Construction 45 | p <- point(sample(10, 2), sample(10, 2), sample(10, 2)) 46 | iso_cube(p[1], p[2]) 47 | 48 | iso_cube(4, 10, 7, 16, -4, 0) 49 | 50 | s <- sphere(point(5, 9, 2), 13) 51 | iso_cube(bbox(s)) 52 | 53 | } 54 | \seealso{ 55 | Other Geometries: 56 | \code{\link{circle}()}, 57 | \code{\link{direction}()}, 58 | \code{\link{iso_rect}()}, 59 | \code{\link{line}()}, 60 | \code{\link{plane}()}, 61 | \code{\link{point}()}, 62 | \code{\link{ray}()}, 63 | \code{\link{segment}()}, 64 | \code{\link{sphere}()}, 65 | \code{\link{tetrahedron}()}, 66 | \code{\link{triangle}()}, 67 | \code{\link{vec}()}, 68 | \code{\link{weighted_point}()} 69 | 70 | Other Volumes: 71 | \code{\link{sphere}()}, 72 | \code{\link{tetrahedron}()} 73 | } 74 | \concept{Geometries} 75 | \concept{Volumes} 76 | -------------------------------------------------------------------------------- /man/segment.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/segment.R 3 | \name{segment} 4 | \alias{segment} 5 | \alias{is_segment} 6 | \alias{as_segment} 7 | \title{Vector of segments} 8 | \usage{ 9 | segment(..., default_dim = 2) 10 | 11 | is_segment(x) 12 | 13 | as_segment(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A vector of segments or an object to convert to it} 21 | } 22 | \value{ 23 | An \code{euclid_segment} vector 24 | } 25 | \description{ 26 | A segment is a finite directed line going from one point to another. If the 27 | two points are equal it is considered to be \link[=is_degenerate]{degenerate}. A 28 | segment can be flipped be taking its negative. 29 | } 30 | \section{Constructors}{ 31 | 32 | \strong{2 and 3 dimensional segments} 33 | \itemize{ 34 | \item Providing two points will construct segments starting at the first point 35 | and ending at the second 36 | \item Providing a point and a vector will construct segments starting at the 37 | point and ending at the point defined by the start point plus the vector 38 | } 39 | } 40 | 41 | \examples{ 42 | # Construction 43 | p <- point(sample(4), sample(4)) 44 | s <- segment(p[1:2], p[3:4]) 45 | s 46 | 47 | plot(s) 48 | 49 | segment(p[1:2], as_vec(p[3:4])) 50 | 51 | # Flip segments 52 | -s 53 | 54 | # Segments can be converted to vectors, directions and lines 55 | as_vec(s) 56 | 57 | as_direction(s) 58 | 59 | as_line(s) 60 | plot(s) 61 | euclid_plot(as_line(s), lty = 2) 62 | 63 | } 64 | \seealso{ 65 | Other Geometries: 66 | \code{\link{circle}()}, 67 | \code{\link{direction}()}, 68 | \code{\link{iso_cube}()}, 69 | \code{\link{iso_rect}()}, 70 | \code{\link{line}()}, 71 | \code{\link{plane}()}, 72 | \code{\link{point}()}, 73 | \code{\link{ray}()}, 74 | \code{\link{sphere}()}, 75 | \code{\link{tetrahedron}()}, 76 | \code{\link{triangle}()}, 77 | \code{\link{vec}()}, 78 | \code{\link{weighted_point}()} 79 | 80 | Other Curves: 81 | \code{\link{line}()}, 82 | \code{\link{ray}()} 83 | } 84 | \concept{Curves} 85 | \concept{Geometries} 86 | -------------------------------------------------------------------------------- /src/iso_rect.cpp: -------------------------------------------------------------------------------- 1 | #include "iso_rect.h" 2 | #include "point.h" 3 | #include "exact_numeric.h" 4 | #include "bbox.h" 5 | 6 | [[cpp11::register]] 7 | iso_rect_p create_iso_rect_empty() { 8 | std::vector vec; 9 | iso_rect *result(new iso_rect(vec)); 10 | return {result}; 11 | } 12 | 13 | [[cpp11::register]] 14 | iso_rect_p create_iso_rect_pq(point2_p p, point2_p q) { 15 | if (p.get() == nullptr || q.get() == nullptr) { 16 | cpp11::stop("Data structure pointer cleared from memory"); 17 | } 18 | std::vector vec; 19 | vec.reserve(p->size()); 20 | for (size_t i = 0; i < p->size(); ++i) { 21 | if (!(*p)[i] || !(*q)[i]) { 22 | vec.push_back(Iso_rectangle::NA_value()); 23 | continue; 24 | } 25 | vec.emplace_back((*p)[i], (*q)[i]); 26 | } 27 | iso_rect *result(new iso_rect(vec)); 28 | 29 | return {result}; 30 | } 31 | 32 | [[cpp11::register]] 33 | iso_rect_p create_iso_rect_minmax(exact_numeric_p minx, exact_numeric_p miny, exact_numeric_p maxx, exact_numeric_p maxy) { 34 | if (minx.get() == nullptr || miny.get() == nullptr || maxx.get() == nullptr || maxy.get() == nullptr) { 35 | cpp11::stop("Data structure pointer cleared from memory"); 36 | } 37 | std::vector vec; 38 | vec.reserve(minx->size()); 39 | for (size_t i = 0; i < minx->size(); ++i) { 40 | if (!(*minx)[i] || !(*miny)[i] || !(*maxx)[i] || !(*maxy)[i]) { 41 | vec.push_back(Iso_rectangle::NA_value()); 42 | continue; 43 | } 44 | vec.emplace_back((*minx)[i], (*miny)[i], (*maxx)[i], (*maxy)[i]); 45 | } 46 | iso_rect *result(new iso_rect(vec)); 47 | 48 | return {result}; 49 | } 50 | 51 | [[cpp11::register]] 52 | iso_rect_p create_iso_rect_bbox(bbox2_p bbox) { 53 | if (bbox.get() == nullptr) { 54 | cpp11::stop("Data structure pointer cleared from memory"); 55 | } 56 | std::vector vec; 57 | vec.reserve(bbox->size()); 58 | for (size_t i = 0; i < bbox->size(); ++i) { 59 | if (!(*bbox)[i]) { 60 | vec.push_back(Iso_rectangle::NA_value()); 61 | continue; 62 | } 63 | vec.emplace_back((*bbox)[i]); 64 | } 65 | iso_rect *result(new iso_rect(vec)); 66 | 67 | return {result}; 68 | } 69 | -------------------------------------------------------------------------------- /man/project.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_projection.R 3 | \name{project} 4 | \alias{project} 5 | \alias{project.euclid_geometry} 6 | \title{Project geometries to lines and planes} 7 | \usage{ 8 | project(x, target, ...) 9 | 10 | \method{project}{euclid_geometry}(x, target, ...) 11 | } 12 | \arguments{ 13 | \item{x}{A vector of geometries to project or map} 14 | 15 | \item{target}{A vector of lines or planes} 16 | 17 | \item{...}{Arguments passed on to methods} 18 | } 19 | \value{ 20 | A vector of geometries of the same class as \code{x} 21 | } 22 | \description{ 23 | Projecting geometries to lines and planes transforms the geometries to lie on 24 | the given line or plane by movement orthogonal to the line or plane. The 25 | geometry of the result of the projection is the same as the input. Not all 26 | geometries can be projected to lines and planes. Specifically, iso rectangles 27 | and iso cubes cannot do either and only 3 dimensional geometries can be 28 | projected to a plane. Circles and spheres are special in the sense that no 29 | ellipse and ellipsoid primitives exist. Because of this, projections will 30 | retain the radius and circularity of the primitives despite this not being 31 | geometrically sound. Projecting a 3D circle to a line will keep the 32 | orientation of the circle, while projecting it to a plane will adopt the 33 | orientation of the plane. 34 | } 35 | \examples{ 36 | # Project a collection of segments to a line 37 | p <- point(sample(100, 8), sample(100, 8)) 38 | s <- segment(p[1:4], p[5:8]) 39 | 40 | project(s, line(3, 1, -6)) 41 | 42 | plot(line(3, 1, -6), xlim = c(-50, 100), ylim = c(-50, 100)) 43 | euclid_plot(s, col = c("firebrick", "goldenrod", "steelblue", "forestgreen")) 44 | euclid_plot( 45 | project(s, line(3, 1, -6)), 46 | col = c("firebrick", "goldenrod", "steelblue", "forestgreen"), 47 | lwd = seq(20, 3, length.out = 4) 48 | ) 49 | 50 | # Project a line to the x plane 51 | project(line(point(3, 7, -3), point(-2, 6, 0)), 52 | plane(point(0, 0, 0), vec(1, 0, 0))) 53 | 54 | } 55 | \seealso{ 56 | Other Projections: 57 | \code{\link{map_to}()}, 58 | \code{\link{normal}()} 59 | } 60 | \concept{Projections} 61 | -------------------------------------------------------------------------------- /src/iso_cube.cpp: -------------------------------------------------------------------------------- 1 | #include "iso_cube.h" 2 | #include "point.h" 3 | #include "exact_numeric.h" 4 | #include "bbox.h" 5 | 6 | [[cpp11::register]] 7 | iso_cube_p create_iso_cube_empty() { 8 | std::vector vec; 9 | iso_cube *result(new iso_cube(vec)); 10 | return {result}; 11 | } 12 | 13 | [[cpp11::register]] 14 | iso_cube_p create_iso_cube_pq(point3_p p, point3_p q) { 15 | if (p.get() == nullptr || q.get() == nullptr) { 16 | cpp11::stop("Data structure pointer cleared from memory"); 17 | } 18 | std::vector vec; 19 | vec.reserve(p->size()); 20 | for (size_t i = 0; i < p->size(); ++i) { 21 | if (!(*p)[i] || !(*q)[i]) { 22 | vec.push_back(Iso_cuboid::NA_value()); 23 | continue; 24 | } 25 | vec.emplace_back((*p)[i], (*q)[i]); 26 | } 27 | iso_cube *result(new iso_cube(vec)); 28 | 29 | return {result}; 30 | } 31 | 32 | [[cpp11::register]] 33 | iso_cube_p create_iso_cube_minmax(exact_numeric_p minx, exact_numeric_p miny, exact_numeric_p minz, exact_numeric_p maxx, exact_numeric_p maxy, exact_numeric_p maxz) { 34 | if (minx.get() == nullptr || miny.get() == nullptr || minz.get() == nullptr || maxx.get() == nullptr || maxy.get() == nullptr || maxz.get() == nullptr) { 35 | cpp11::stop("Data structure pointer cleared from memory"); 36 | } 37 | std::vector vec; 38 | vec.reserve(minx->size()); 39 | for (size_t i = 0; i < minx->size(); ++i) { 40 | if (!(*minx)[i] || !(*miny)[i] || !(*minz)[i] || !(*maxx)[i] || !(*maxy)[i] || !(*maxz)[i]) { 41 | vec.push_back(Iso_cuboid::NA_value()); 42 | continue; 43 | } 44 | vec.emplace_back((*minx)[i], (*miny)[i], (*minz)[i], (*maxx)[i], (*maxy)[i], (*maxz)[i]); 45 | } 46 | iso_cube *result(new iso_cube(vec)); 47 | 48 | return {result}; 49 | } 50 | 51 | [[cpp11::register]] 52 | iso_cube_p create_iso_cube_bbox(bbox3_p bbox) { 53 | if (bbox.get() == nullptr) { 54 | cpp11::stop("Data structure pointer cleared from memory"); 55 | } 56 | std::vector vec; 57 | vec.reserve(bbox->size()); 58 | for (size_t i = 0; i < bbox->size(); ++i) { 59 | if (!(*bbox)[i]) { 60 | vec.push_back(Iso_cuboid::NA_value()); 61 | continue; 62 | } 63 | vec.emplace_back((*bbox)[i]); 64 | } 65 | iso_cube *result(new iso_cube(vec)); 66 | 67 | return {result}; 68 | } 69 | -------------------------------------------------------------------------------- /man/sphere.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sphere.R 3 | \name{sphere} 4 | \alias{sphere} 5 | \alias{is_sphere} 6 | \alias{as_sphere} 7 | \title{Vector of spheres} 8 | \usage{ 9 | sphere(...) 10 | 11 | is_sphere(x) 12 | 13 | as_sphere(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{x}{A vector of spheres or an object to convert to it} 19 | } 20 | \value{ 21 | An \code{euclid_sphere} vector 22 | } 23 | \description{ 24 | A sphere is a 3 dimensional object modelling the surface of a ball. As such 25 | it is an extension of the concept of a 2 dimensional circle to 3D, though a 26 | circle can also exist in three dimensions. A sphere has a center and a 27 | radius. if the radius is 0 it is considered to be \link[=is_degenerate]{degenerate}. 28 | } 29 | \section{Constructors}{ 30 | 31 | \strong{3 dimensional spheres} 32 | \itemize{ 33 | \item Providing 4 points will construct the unique sphere the passes through all 34 | 4 points (points must not be coplanar) 35 | \item Providing 3 points will construct the smallest sphere that passes through 36 | all 3 points 37 | \item Providing 2 points will construct the smallest sphere passing through both 38 | points 39 | \item Providing a point and numeric will construct spheres centered on the point 40 | with a squared radius set to the numeric 41 | \item Providing a circle will construct the diametral sphere of the circle 42 | } 43 | } 44 | 45 | \examples{ 46 | # Construction 47 | p <- point(sample(8), sample(8), sample(8)) 48 | sphere(p, 4) 49 | 50 | sphere(p[1:2], p[3:4], p[5:6], p[7:8]) 51 | 52 | sphere(p[1:2], p[3:4], p[5:6]) 53 | 54 | sphere(p[1:2], p[3:4]) 55 | 56 | circ <- circle(p[1], as_vec(p[2]), 6) 57 | sphere(circ) 58 | 59 | } 60 | \seealso{ 61 | Other Geometries: 62 | \code{\link{circle}()}, 63 | \code{\link{direction}()}, 64 | \code{\link{iso_cube}()}, 65 | \code{\link{iso_rect}()}, 66 | \code{\link{line}()}, 67 | \code{\link{plane}()}, 68 | \code{\link{point}()}, 69 | \code{\link{ray}()}, 70 | \code{\link{segment}()}, 71 | \code{\link{tetrahedron}()}, 72 | \code{\link{triangle}()}, 73 | \code{\link{vec}()}, 74 | \code{\link{weighted_point}()} 75 | 76 | Other Volumes: 77 | \code{\link{iso_cube}()}, 78 | \code{\link{tetrahedron}()} 79 | } 80 | \concept{Geometries} 81 | \concept{Volumes} 82 | -------------------------------------------------------------------------------- /src/get_vertex.h: -------------------------------------------------------------------------------- 1 | #include "cgal_types.h" 2 | #include 3 | 4 | template 5 | inline U get_vertex_impl(const T& geometry, int which) { 6 | return geometry.vertex(which); 7 | } 8 | 9 | template<> 10 | inline Point_2 get_vertex_impl(const Circle_2& geometry, int which) { 11 | return geometry.center(); 12 | } 13 | 14 | template<> 15 | inline Point_3 get_vertex_impl(const Circle_3& geometry, int which) { 16 | return geometry.center(); 17 | } 18 | 19 | template<> 20 | inline Point_2 get_vertex_impl(const Direction_2& geometry, int which) { 21 | return Point_2::NA_value(); 22 | } 23 | 24 | template<> 25 | inline Point_3 get_vertex_impl(const Direction_3& geometry, int which) { 26 | return Point_3::NA_value(); 27 | } 28 | 29 | template<> 30 | inline Point_2 get_vertex_impl(const Line_2& geometry, int which) { 31 | return geometry.point(0); 32 | } 33 | 34 | template<> 35 | inline Point_3 get_vertex_impl(const Line_3& geometry, int which) { 36 | return geometry.point(0); 37 | } 38 | 39 | template<> 40 | inline Point_3 get_vertex_impl(const Plane& geometry, int which) { 41 | return geometry.point(); 42 | } 43 | 44 | template<> 45 | inline Point_2 get_vertex_impl(const Point_2& geometry, int which) { 46 | return geometry; 47 | } 48 | 49 | template<> 50 | inline Point_3 get_vertex_impl(const Point_3& geometry, int which) { 51 | return geometry; 52 | } 53 | 54 | template<> 55 | inline Point_2 get_vertex_impl(const Weighted_point_2& geometry, int which) { 56 | return geometry.point(); 57 | } 58 | 59 | template<> 60 | inline Point_3 get_vertex_impl(const Weighted_point_3& geometry, int which) { 61 | return geometry.point(); 62 | } 63 | 64 | template<> 65 | inline Point_2 get_vertex_impl(const Ray_2& geometry, int which) { 66 | return geometry.source(); 67 | } 68 | 69 | template<> 70 | inline Point_3 get_vertex_impl(const Ray_3& geometry, int which) { 71 | return geometry.source(); 72 | } 73 | 74 | template<> 75 | inline Point_3 get_vertex_impl(const Sphere& geometry, int which) { 76 | return geometry.center(); 77 | } 78 | 79 | template<> 80 | inline Point_2 get_vertex_impl(const Vector_2& geometry, int which) { 81 | return Point_2::NA_value(); 82 | } 83 | 84 | template<> 85 | inline Point_3 get_vertex_impl(const Vector_3& geometry, int which) { 86 | return Point_3::NA_value(); 87 | } 88 | -------------------------------------------------------------------------------- /man/iso_rect.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iso_rect.R 3 | \name{iso_rect} 4 | \alias{iso_rect} 5 | \alias{is_iso_rect} 6 | \alias{as_iso_rect} 7 | \title{Vector of iso rectangles} 8 | \usage{ 9 | iso_rect(...) 10 | 11 | is_iso_rect(x) 12 | 13 | as_iso_rect(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{x}{A vector of iso rectangles or an object to convert to it} 19 | } 20 | \value{ 21 | An \code{euclid_iso_rect} vector 22 | } 23 | \description{ 24 | Iso rectangles are axis aligned rectangles mimicking bounding boxes but given 25 | by exact numerics (bounding boxes are defined by floats because not all 26 | geometries have exact defined extent). Internally they are stored as two 27 | points giving the bottom-left and top-right corner of the rectangle. Iso 28 | rectangles are considered \link[=is_degenerate]{degenerate} all its vertices are 29 | colinear. 30 | } 31 | \section{Constructors}{ 32 | 33 | \strong{2 dimensional iso rectangles} 34 | \itemize{ 35 | \item Providing a bbox will create iso rectangles matching the bbox 36 | \item Providing two points will create iso rectangles with the points as diagonal 37 | opposite vertices 38 | \item Providing 4 numeric will construct iso rectangles with the given extent, 39 | with the numerics being interpreted in the following order: left, bottom, 40 | right, top 41 | } 42 | } 43 | 44 | \examples{ 45 | # Construction 46 | p <- point(sample(10, 2), sample(10, 2)) 47 | r <- iso_rect(p[1], p[2]) 48 | r 49 | 50 | plot(r) 51 | euclid_plot(p, pch = 16) 52 | 53 | r <- iso_rect(4, 10, 7, 16) 54 | plot(r, bg = "grey", fg = NA) 55 | abline(v = 4, lty = 2) 56 | abline(v = 7, lty = 2) 57 | abline(h = 10, lty = 2) 58 | abline(h = 16, lty = 2) 59 | 60 | circ <- circle(point(5, 9), 13) 61 | iso_rect(bbox(circ)) 62 | 63 | } 64 | \seealso{ 65 | Other Geometries: 66 | \code{\link{circle}()}, 67 | \code{\link{direction}()}, 68 | \code{\link{iso_cube}()}, 69 | \code{\link{line}()}, 70 | \code{\link{plane}()}, 71 | \code{\link{point}()}, 72 | \code{\link{ray}()}, 73 | \code{\link{segment}()}, 74 | \code{\link{sphere}()}, 75 | \code{\link{tetrahedron}()}, 76 | \code{\link{triangle}()}, 77 | \code{\link{vec}()}, 78 | \code{\link{weighted_point}()} 79 | 80 | Other Surfaces: 81 | \code{\link{circle}()}, 82 | \code{\link{plane}()}, 83 | \code{\link{triangle}()} 84 | } 85 | \concept{Geometries} 86 | \concept{Surfaces} 87 | -------------------------------------------------------------------------------- /man/def.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/definition.R 3 | \name{def} 4 | \alias{def} 5 | \alias{def.euclid_geometry} 6 | \alias{def.euclid_affine_transformation} 7 | \alias{def<-} 8 | \alias{def<-.euclid_geometry} 9 | \alias{def<-.euclid_affine_transformation} 10 | \alias{definition_names} 11 | \title{Access the exact values that defines the geometries} 12 | \usage{ 13 | def(x, ...) 14 | 15 | \method{def}{euclid_geometry}(x, name, which = NULL, ...) 16 | 17 | \method{def}{euclid_affine_transformation}(x, i, j, ...) 18 | 19 | def(x, ...) <- value 20 | 21 | \method{def}{euclid_geometry}(x, name, which = NULL, ...) <- value 22 | 23 | \method{def}{euclid_affine_transformation}(x, i, j, ...) <- value 24 | 25 | definition_names(x, ...) 26 | } 27 | \arguments{ 28 | \item{x}{A geometry vector} 29 | 30 | \item{...}{parameters to pass on} 31 | 32 | \item{name}{Either a name or the index of the definition to extract, as 33 | matched to \code{definition_names(x)}} 34 | 35 | \item{which}{An integer vector giving the vertex from which the definition 36 | should be extracted, or \code{NULL} to extract all 37 | to extract all.} 38 | 39 | \item{i, j}{The row and column of the cell in the transformation to fetch.} 40 | 41 | \item{value}{An \code{exact_numeric} vector or an object convertible to one} 42 | } 43 | \value{ 44 | An exact_numeric vector 45 | } 46 | \description{ 47 | This function gives access to the underlying values defining the geometries 48 | in a vector. As such they return the same information as calling 49 | \code{\link[=as.matrix]{as.matrix()}} on a geometry vector except the return value is kept as an 50 | exact numeric and that you can extract from single elements if the 51 | cardinality of the geometry exceeds 1. 52 | } 53 | \examples{ 54 | # Get squared radius of circle 55 | circ <- circle(point(4, 7), 25) 56 | def(circ, "r2") 57 | 58 | # Set r2 to 10 59 | def(circ, "r2") <- 10 60 | circ 61 | 62 | # Get all the x values from the source of segments 63 | s <- segment(point(sample(10, 4), sample(10, 4)), 64 | point(sample(10, 4), sample(10, 4))) 65 | def(s, "x", 1L) 66 | 67 | # Get y for all subelements 68 | def(s, "y") 69 | 70 | # Extract cell values from transformation matrices 71 | m <- affine_rotate(c(pi/2, pi/3)) 72 | def(m, 1, 2) 73 | 74 | } 75 | \seealso{ 76 | Other Geometry methods: 77 | \code{\link{euclid_geometry}}, 78 | \code{\link{subgeometries}} 79 | } 80 | \concept{Geometry methods} 81 | -------------------------------------------------------------------------------- /man/plane.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plane.R 3 | \name{plane} 4 | \alias{plane} 5 | \alias{is_plane} 6 | \alias{as_plane} 7 | \title{Vector of planes} 8 | \usage{ 9 | plane(...) 10 | 11 | is_plane(x) 12 | 13 | as_plane(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{x}{A vector of planes or an object to convert to it} 19 | } 20 | \value{ 21 | An \code{euclid_plane} vector 22 | } 23 | \description{ 24 | A plane splits the euclidean space in two by all the points that satisfy the 25 | plane equation \code{ax + by + cz + d = 0}. As can be seen this is an extension of 26 | the concept of lines in 2 dimensions though lines can also exist in three 27 | dimensions. 28 | } 29 | \section{Constructors}{ 30 | 31 | \strong{3 dimensional planes} 32 | \itemize{ 33 | \item Providing 4 numberics will construct planes with coefficients from the 4 34 | numerics in the order given. 35 | \item Providing 3 points will construct planes passing through the three points 36 | \item Providing a point and vector will construct planes that goes through the 37 | point and are orthogonal to the vector 38 | \item Providing a point and a direction will construct planes that goes through 39 | the point and are orthogonal to the direction 40 | \item Providing a point and a line will construct planes that goes through the 41 | point and 2 points on the line 42 | \item Providing a point and a ray will construct planes that goes through the 43 | point and 2 points on the ray 44 | \item Providing a point and a segment will construct planes that goes through the 45 | point and the two points making up the segment 46 | \item Providing a circle will construct planes that contains the circle 47 | \item Providing a triangle will construct planes that contains the triangle 48 | } 49 | } 50 | 51 | \examples{ 52 | # Construction 53 | p <- plane(sample(10, 2), sample(10, 2), sample(10, 2), sample(10, 2)) 54 | p 55 | 56 | } 57 | \seealso{ 58 | Other Geometries: 59 | \code{\link{circle}()}, 60 | \code{\link{direction}()}, 61 | \code{\link{iso_cube}()}, 62 | \code{\link{iso_rect}()}, 63 | \code{\link{line}()}, 64 | \code{\link{point}()}, 65 | \code{\link{ray}()}, 66 | \code{\link{segment}()}, 67 | \code{\link{sphere}()}, 68 | \code{\link{tetrahedron}()}, 69 | \code{\link{triangle}()}, 70 | \code{\link{vec}()}, 71 | \code{\link{weighted_point}()} 72 | 73 | Other Surfaces: 74 | \code{\link{circle}()}, 75 | \code{\link{iso_rect}()}, 76 | \code{\link{triangle}()} 77 | } 78 | \concept{Geometries} 79 | \concept{Surfaces} 80 | -------------------------------------------------------------------------------- /man/ray.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ray.R 3 | \name{ray} 4 | \alias{ray} 5 | \alias{is_ray} 6 | \alias{as_ray} 7 | \title{Vector of rays} 8 | \usage{ 9 | ray(..., default_dim = 2) 10 | 11 | is_ray(x) 12 | 13 | as_ray(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A vector of rays or an object to convert to it} 21 | } 22 | \value{ 23 | An \code{euclid_ray} vector 24 | } 25 | \description{ 26 | A ray is a directed line that starts at a given point and extends to 27 | infinity. As such it does not have a magnitude (like a vector) but only a 28 | beginning and a direction. A ray is considered to be 29 | \link[=is_degenerate]{degenerate} if constructed be two points that are equal (and 30 | thus have no direction). Rays can be flipped by taking their negative 31 | } 32 | \section{Constructors}{ 33 | 34 | \strong{2 and 3 dimensional rays} 35 | \itemize{ 36 | \item Providing two points will construct rays starting at the first point and 37 | going through the second 38 | \item Providing a point and a vector will construct rays starting at the point 39 | and moving in the direction of the vector. 40 | \item Providing a point and a direction will construct rays starting at the point 41 | and moving in the given direction. 42 | \item Providing a point and a line will construct rays starting at the point 43 | and moving in the direction of the line. Note that the point does not have 44 | lie on line. 45 | } 46 | } 47 | 48 | \examples{ 49 | # Construction 50 | p <- point(sample(10, 3), sample(10, 3)) 51 | r <- ray(p[1], p[2:3]) 52 | r 53 | plot(p, pch = c(16, 17, 17)) 54 | euclid_plot(r) 55 | 56 | ray(p, -as_vec(p)) 57 | 58 | ray(p, direction(7, -2)) 59 | 60 | ray(p, line(3, 10, -8)) 61 | 62 | # Flip direction 63 | -r 64 | 65 | # Convert to vector, direction, or lines 66 | as_vec(r) 67 | 68 | as_direction(r) 69 | 70 | as_line(r) 71 | 72 | } 73 | \seealso{ 74 | Other Geometries: 75 | \code{\link{circle}()}, 76 | \code{\link{direction}()}, 77 | \code{\link{iso_cube}()}, 78 | \code{\link{iso_rect}()}, 79 | \code{\link{line}()}, 80 | \code{\link{plane}()}, 81 | \code{\link{point}()}, 82 | \code{\link{segment}()}, 83 | \code{\link{sphere}()}, 84 | \code{\link{tetrahedron}()}, 85 | \code{\link{triangle}()}, 86 | \code{\link{vec}()}, 87 | \code{\link{weighted_point}()} 88 | 89 | Other Curves: 90 | \code{\link{line}()}, 91 | \code{\link{segment}()} 92 | } 93 | \concept{Curves} 94 | \concept{Geometries} 95 | -------------------------------------------------------------------------------- /R/geometry_op.R: -------------------------------------------------------------------------------- 1 | geometry_op_plus <- function(e1, e2) { 2 | UseMethod("geometry_op_plus") 3 | } 4 | #' @export 5 | geometry_op_plus.default <- function(e1, e2) { 6 | cli_abort("The provided geometries ({.cls {class(e1)[1]}} and {.cls {class(e2)[1]}}) does not define a {.code +} operation") 7 | } 8 | geometry_op_minus <- function(e1, e2) { 9 | UseMethod("geometry_op_minus") 10 | } 11 | #' @export 12 | geometry_op_minus.default <- function(e1, e2) { 13 | cli_abort("The provided geometries ({.cls {class(e1)[1]}} and {.cls {class(e2)[1]}}) does not define a {.code -} operation") 14 | } 15 | geometry_op_multiply <- function(e1, e2) { 16 | UseMethod("geometry_op_multiply") 17 | } 18 | #' @export 19 | geometry_op_multiply.default <- function(e1, e2) { 20 | if (!is_geometry(e1) && is_geometry(e2)) return(geometry_op_multiply(e2, e1)) 21 | cli_abort("The provided geometries ({.cls {class(e1)[1]}} and {.cls {class(e2)[1]}}) does not define a {.code *} operation") 22 | } 23 | geometry_op_divide <- function(e1, e2) { 24 | UseMethod("geometry_op_divide") 25 | } 26 | #' @export 27 | geometry_op_divide.default <- function(e1, e2) { 28 | cli_abort("The provided geometries ({.cls {class(e1)[1]}} and {.cls {class(e2)[1]}}) does not define a {.code /} operation") 29 | } 30 | geometry_op_less <- function(e1, e2) { 31 | UseMethod("geometry_op_less") 32 | } 33 | #' @export 34 | geometry_op_less.default <- function(e1, e2) { 35 | cli_abort("The provided geometries ({.cls {class(e1)[1]}} and {.cls {class(e2)[1]}}) does not define a {.code <} operation") 36 | } 37 | geometry_op_greater <- function(e1, e2) { 38 | UseMethod("geometry_op_greater") 39 | } 40 | #' @export 41 | geometry_op_greater.default <- function(e1, e2) { 42 | cli_abort("The provided geometries ({.cls {class(e1)[1]}} and {.cls {class(e2)[1]}}) does not define a {.code >} operation") 43 | } 44 | #' @export 45 | Ops.euclid_geometry <- function(e1, e2) { 46 | if (.Generic %in% c("==", "!=") && (length(e1) == 0 || length(e2) == 0)) { 47 | return(e1[integer(0)]) 48 | } 49 | switch(.Generic, 50 | "==" = geometry_is_equal(get_ptr(e1), get_ptr(e2)), 51 | "!=" = !geometry_is_equal(get_ptr(e1), get_ptr(e2)), 52 | "+" = geometry_op_plus(e1, e2), 53 | "-" = geometry_op_minus(e1, e2), 54 | "*" = geometry_op_multiply(e1, e2), 55 | "/" = geometry_op_divide(e1, e2), 56 | "<" = geometry_op_less(e1, e2), 57 | "<=" = !geometry_op_greater(e1, e2), 58 | ">" = geometry_op_greater(e1, e2), 59 | ">=" = !geometry_op_less(e1, e2), 60 | cli_abort("The {.code {.Generic}} operator is not defined for {.cls euclid_geometry} vectors") 61 | ) 62 | } 63 | -------------------------------------------------------------------------------- /man/exact_numeric.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/exact_numeric.R 3 | \name{exact_numeric} 4 | \alias{exact_numeric} 5 | \alias{is_exact_numeric} 6 | \alias{as_exact_numeric} 7 | \title{Exact numeric representation} 8 | \usage{ 9 | exact_numeric(x = numeric(0)) 10 | 11 | is_exact_numeric(x) 12 | 13 | as_exact_numeric(x) 14 | } 15 | \arguments{ 16 | \item{x}{An object coercible to \code{numeric}} 17 | } 18 | \value{ 19 | An \code{euclid_exact_numeric} vector 20 | } 21 | \description{ 22 | In order to have exact geometric computations it is necessary to avoid the 23 | issues of floating point arithmetic. CGAL can work with a range of different 24 | exact kernels, where euclid uses the \code{Exact_predicates_exact_constructions} 25 | predifined kernel. Moving values in and out of R would force a conversion to 26 | double thus destroying the exactness of computation, so euclid provides a new 27 | numeric vector class that simply maps to the underlying exact representation. 28 | While the exact numeric class generally behaves as a standard numeric there 29 | are operations that are not possible because it will loose the exactness, 30 | e.g. trigonometric functions, roots, etc. If any unsupported function is 31 | needed it is necessary to convert back to numeric (thus loosing exactness). 32 | Anywhere euclid takes a numeric input it will convert it to an exact numeric 33 | before use. 34 | } 35 | \note{ 36 | Exact numeric only have one form of non-finite value: \code{NA}. Any other 37 | non-finite value will be converted to that. 38 | } 39 | \examples{ 40 | # Standard use 41 | exact_numeric(runif(5)) 42 | 43 | # Non-finite numbers are all converted to NA 44 | exact_numeric(NaN) 45 | 46 | # this is also true for division by zero 47 | exact_numeric(runif(5)) / 0 48 | 49 | # Exact numerics are more limited on operations but doesn't have the 50 | # weirdness of floating point arithmetic: 51 | float <- c(1, 2, 3) 52 | identical(float[1] + float[2], float[3]) 53 | float <- float / 10 54 | identical((float[1] + float[2]) * 10, float[3] * 10) 55 | 56 | exact <- exact_numeric(c(1, 2, 3)) 57 | identical(as.numeric(exact[1] + exact[2]), as.numeric(exact[3])) 58 | exact <- exact / 10 59 | identical(as.numeric((exact[1] + exact[2]) * 10), as.numeric(exact[3] * 10)) 60 | 61 | # This only holds if the input are exact to begin with 62 | identical( 63 | as.numeric(exact_numeric(1/10 + 2/10)), 64 | as.numeric(exact_numeric(3/10)) 65 | ) 66 | 67 | } 68 | \seealso{ 69 | Other non-geometry vectors: 70 | \code{\link{affine_transformation}}, 71 | \code{\link{bbox}()} 72 | } 73 | \concept{non-geometry vectors} 74 | -------------------------------------------------------------------------------- /man/weighted_point.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/point_w.R 3 | \name{weighted_point} 4 | \alias{weighted_point} 5 | \alias{is_weighted_point} 6 | \alias{as_weighted_point} 7 | \title{Vector of weighted points} 8 | \usage{ 9 | weighted_point(..., default_dim = 2) 10 | 11 | is_weighted_point(x) 12 | 13 | as_weighted_point(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A point vector or an object to convert to it} 21 | } 22 | \value{ 23 | a \code{euclid_point_w} vector 24 | } 25 | \description{ 26 | This class is an extension (but not a subclass of) \code{\link[=point]{point()}}. It simply 27 | attaches a numeric weight to the point that is carried along with it. 28 | However, most of the functionality of points are lost with this vector class 29 | as all the a math and sorting operations are gone due to ambiguity on whether 30 | to operate on the point or the weight. Weighted points can easily be 31 | converted to points however. 32 | } 33 | \section{Constructors}{ 34 | 35 | \strong{2 dimensional weighted points} 36 | \itemize{ 37 | \item Providing three numeric vector will construct points with those x and y 38 | coordinate and a weight. 39 | \item Providing a point and a numeric will construct points with the given weight 40 | } 41 | 42 | \strong{3 dimensional weighted points} 43 | \itemize{ 44 | \item Providing four numeric vector will construct points with those x, y and z 45 | coordinate and a weight. 46 | \item Providing a point and a numeric will construct points with the given weight 47 | } 48 | } 49 | 50 | \examples{ 51 | # Construction 52 | wp <- weighted_point(sample(5), sample(5), runif(5)) 53 | wp 54 | 55 | # Convert to point 56 | p <- as_point(wp) 57 | p 58 | 59 | # Construct from point and weight 60 | 61 | weighted_point(p, 1:5) 62 | 63 | } 64 | \seealso{ 65 | Other Geometries: 66 | \code{\link{circle}()}, 67 | \code{\link{direction}()}, 68 | \code{\link{iso_cube}()}, 69 | \code{\link{iso_rect}()}, 70 | \code{\link{line}()}, 71 | \code{\link{plane}()}, 72 | \code{\link{point}()}, 73 | \code{\link{ray}()}, 74 | \code{\link{segment}()}, 75 | \code{\link{sphere}()}, 76 | \code{\link{tetrahedron}()}, 77 | \code{\link{triangle}()}, 78 | \code{\link{vec}()} 79 | 80 | Other Locations: 81 | \code{\link{barycenter}()}, 82 | \code{\link{bisector}()}, 83 | \code{\link{centroid}()}, 84 | \code{\link{circumcenter}()}, 85 | \code{\link{equidistant_line}()}, 86 | \code{\link{point}()}, 87 | \code{\link{radical}()} 88 | } 89 | \concept{Geometries} 90 | \concept{Locations} 91 | -------------------------------------------------------------------------------- /.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: Commands 8 | 9 | jobs: 10 | document: 11 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 12 | name: document 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - uses: r-lib/actions/pr-fetch@v2 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - uses: r-lib/actions/setup-r@v2 24 | with: 25 | use-public-rspm: true 26 | 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | extra-packages: any::roxygen2 30 | needs: pr-document 31 | 32 | - name: Document 33 | run: roxygen2::roxygenise() 34 | shell: Rscript {0} 35 | 36 | - name: commit 37 | run: | 38 | git config --local user.name "$GITHUB_ACTOR" 39 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 40 | git add man/\* NAMESPACE 41 | git commit -m 'Document' 42 | 43 | - uses: r-lib/actions/pr-push@v2 44 | with: 45 | repo-token: ${{ secrets.GITHUB_TOKEN }} 46 | 47 | style: 48 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 49 | name: style 50 | runs-on: ubuntu-latest 51 | env: 52 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 53 | steps: 54 | - uses: actions/checkout@v3 55 | 56 | - uses: r-lib/actions/pr-fetch@v2 57 | with: 58 | repo-token: ${{ secrets.GITHUB_TOKEN }} 59 | 60 | - uses: r-lib/actions/setup-r@v2 61 | 62 | - name: Install dependencies 63 | run: install.packages("styler") 64 | shell: Rscript {0} 65 | 66 | - name: Style 67 | run: styler::style_pkg() 68 | shell: Rscript {0} 69 | 70 | - name: commit 71 | run: | 72 | git config --local user.name "$GITHUB_ACTOR" 73 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 74 | git add \*.R 75 | git commit -m 'Style' 76 | 77 | - uses: r-lib/actions/pr-push@v2 78 | with: 79 | repo-token: ${{ secrets.GITHUB_TOKEN }} 80 | -------------------------------------------------------------------------------- /man/direction.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/direction.R 3 | \name{direction} 4 | \alias{direction} 5 | \alias{is_direction} 6 | \alias{between} 7 | \alias{as_direction} 8 | \title{Vector of directions} 9 | \usage{ 10 | direction(..., default_dim = 2) 11 | 12 | is_direction(x) 13 | 14 | between(x, d1, d2) 15 | 16 | as_direction(x) 17 | } 18 | \arguments{ 19 | \item{...}{Various input. See the Constructor section.} 20 | 21 | \item{default_dim}{The dimensionality when constructing an empty vector} 22 | 23 | \item{x}{A direction vector or an object to convert to it} 24 | 25 | \item{d1, d2}{direction vectors to relate to} 26 | } 27 | \value{ 28 | a \code{euclid_direction} vector 29 | } 30 | \description{ 31 | Directions are vectors where you have forgot about the length. They are used 32 | much in the same way as normalised vectors (vectors with a magnitude of 1), 33 | but since vectors cannot be normalized while maintaining exactness it is 34 | preferable to simply have a data type where you ignore the magnitude. The 35 | direction can be flipped by taking the negative. 2 dimensional directions can 36 | be considered as angles and can thus be sorted and compared. The same is not 37 | true for directions in 3 dimensions. 38 | } 39 | \section{Constructors}{ 40 | 41 | \strong{2 and 3 dimensional directions} 42 | \itemize{ 43 | \item Providing 2 or 3 numerics will create directions with the given delta 44 | values. (2 numerics will give 2 dimensional directions, 3 will give 3 45 | dimensional directions). 46 | \item Providing vectors will construct directions as the direction of the given 47 | vectors. 48 | \item Providing lines will construct directions as the direction of the given 49 | lines. 50 | \item Providing rays will construct directions as the direction of the given 51 | rays. 52 | \item Providing segments will construct directions as the direction of the given 53 | segments. 54 | } 55 | } 56 | 57 | \examples{ 58 | # Constructions 59 | d <- direction(sample(10, 3), sample(10, 3)) 60 | d 61 | plot(d, col = c("firebrick", "goldenrod", "steelblue")) 62 | 63 | # flipping the direction 64 | -d 65 | 66 | # Relations 67 | d[1] < d[2] 68 | 69 | min(d) 70 | 71 | sort(d) 72 | 73 | between(d[1], d[2], d[3]) 74 | 75 | } 76 | \seealso{ 77 | Other Geometries: 78 | \code{\link{circle}()}, 79 | \code{\link{iso_cube}()}, 80 | \code{\link{iso_rect}()}, 81 | \code{\link{line}()}, 82 | \code{\link{plane}()}, 83 | \code{\link{point}()}, 84 | \code{\link{ray}()}, 85 | \code{\link{segment}()}, 86 | \code{\link{sphere}()}, 87 | \code{\link{tetrahedron}()}, 88 | \code{\link{triangle}()}, 89 | \code{\link{vec}()}, 90 | \code{\link{weighted_point}()} 91 | 92 | Other Arrows: 93 | \code{\link{vec}()} 94 | } 95 | \concept{Arrows} 96 | \concept{Geometries} 97 | -------------------------------------------------------------------------------- /R/tetrahedron.R: -------------------------------------------------------------------------------- 1 | #' Vector of tetrahedrons 2 | #' 3 | #' A tetrahedron, or triangular pyramid, is a polyhedron consisting of 4 4 | #' triangles between 4 vertices. A tetrahedron splits the euclidean space in two 5 | #' by the plane defined by the first 3 vertices and the positive side being the 6 | #' side that includes the fourth vertex. If a all 4 vertices are coplanar the 7 | #' tetrahedron is considered [degenerate][is_degenerate]. Tetrahedrons only 8 | #' exists in 3 dimensions. 9 | #' 10 | #' @param ... Various input. See the Constructor section. 11 | #' @param x A vector of tetrahedrons or an object to convert to it 12 | #' 13 | #' @return An `euclid_tetrahedron` vector 14 | #' 15 | #' @section Constructors: 16 | #' **3 dimensional tetrahedrons** 17 | #' - Providing four points will construct tetrahedrons in the order the points 18 | #' are given. 19 | #' 20 | #' @export 21 | #' 22 | #' @family Geometries 23 | #' @family Volumes 24 | #' 25 | #' @examples 26 | #' p <- point(sample(8), sample(8), sample(8)) 27 | #' tetrahedron(p[1:2], p[3:4], p[5:6], p[7:8]) 28 | #' 29 | tetrahedron <- function(...) { 30 | inputs <- validate_constructor_input(...) 31 | 32 | if (length(inputs) == 0) { 33 | return(new_tetrahedron_empty()) 34 | } 35 | 36 | points <- inputs[vapply(inputs, is_point, logical(1))] 37 | 38 | if (length(points) == 4) { 39 | new_tetrahedron_from_4_points(points[[1]], points[[2]], points[[3]], points[[4]]) 40 | } else { 41 | cli_abort("Can't construct a {.cls euclid_tetrahedron} vector from the given input") 42 | } 43 | } 44 | #' @rdname tetrahedron 45 | #' @export 46 | is_tetrahedron <- function(x) inherits(x, "euclid_tetrahedron") 47 | 48 | 49 | # Conversion -------------------------------------------------------------- 50 | 51 | #' @rdname tetrahedron 52 | #' @export 53 | as_tetrahedron <- function(x) { 54 | UseMethod("as_tetrahedron") 55 | } 56 | #' @export 57 | as_tetrahedron.default <- function(x) { 58 | abort("Can't convert the input to a {.cls euclid_tetrahedron} vector") 59 | } 60 | #' @export 61 | as_tetrahedron.euclid_tetrahedron <- function(x) x 62 | 63 | # Misc -------------------------------------------------------------------- 64 | 65 | #' @export 66 | seq.euclid_tetrahedron <- function(from, to, length.out = NULL, along.with = NULL, ...) { 67 | tetrahedron( 68 | seq(vert(from, 1), vert(to, 1), length.out, along.with), 69 | seq(vert(from, 2), vert(to, 2), length.out, along.with), 70 | seq(vert(from, 3), vert(to, 3), length.out, along.with), 71 | seq(vert(from, 4), vert(to, 4), length.out, along.with) 72 | ) 73 | } 74 | 75 | # Internal Constructors --------------------------------------------------- 76 | 77 | new_tetrahedron_empty <- function() { 78 | new_geometry_vector(create_tetrahedron_empty()) 79 | } 80 | new_tetrahedron_from_4_points <- function(p, q, r, s) { 81 | new_geometry_vector(create_tetrahedron_4points(get_ptr(p), get_ptr(q), get_ptr(r), get_ptr(s))) 82 | } 83 | -------------------------------------------------------------------------------- /src/match.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "cgal_types.h" 6 | 7 | // Only assumes working == 8 | template 9 | inline cpp11::writable::integers match_impl(const std::vector& x, const std::vector& lookup) { 10 | cpp11::writable::integers results; 11 | results.reserve(x.size()); 12 | 13 | int NA_ind = -1; 14 | 15 | for (size_t i = 0; i < lookup.size(); ++i) { 16 | if (NA_ind == -1 && !lookup[i]) { 17 | NA_ind = i; 18 | } 19 | } 20 | auto start = lookup.begin(); 21 | for (auto iter = x.begin(); iter != x.end(); ++iter) { 22 | if (iter->is_na()) { 23 | if (NA_ind == -1) { 24 | results.push_back(R_NaInt); 25 | } else { 26 | results.push_back(NA_ind + 1); 27 | } 28 | continue; 29 | } 30 | auto match = std::find(lookup.begin(), lookup.end(), *iter); 31 | if (match == lookup.end()) { 32 | results.push_back(R_NaInt); 33 | } else { 34 | results.push_back((match - start) + 1); 35 | } 36 | } 37 | 38 | return results; 39 | } 40 | 41 | // Efficient version for types with < comparison 42 | template 43 | inline cpp11::writable::integers match_map_impl(const std::vector& x, const std::vector& lookup) { 44 | std::map lookup_map; 45 | 46 | int NA_ind = -1; 47 | for (size_t i = 0; i < lookup.size(); ++i) { 48 | if (!lookup[i]) { 49 | if (NA_ind == -1) NA_ind = i; 50 | continue; 51 | } 52 | if (lookup_map.find(lookup[i]) == lookup_map.end()) { 53 | lookup_map[lookup[i]] = i; 54 | } 55 | } 56 | cpp11::writable::integers results; 57 | results.reserve(x.size()); 58 | for (auto iter = x.begin(); iter != x.end(); ++iter) { 59 | if (iter->is_na()) { 60 | if (NA_ind == -1) { 61 | results.push_back(R_NaInt); 62 | } else { 63 | results.push_back(NA_ind + 1); 64 | } 65 | continue; 66 | } 67 | auto match = lookup_map.find(*iter); 68 | if (match == lookup_map.end()) { 69 | results.push_back(R_NaInt); 70 | } else { 71 | results.push_back(match->second + 1); 72 | } 73 | } 74 | 75 | return results; 76 | } 77 | 78 | template<> 79 | inline cpp11::writable::integers match_impl(const std::vector& x, const std::vector& lookup) { 80 | return match_map_impl(x, lookup); 81 | } 82 | 83 | template<> 84 | inline cpp11::writable::integers match_impl(const std::vector& x, const std::vector& lookup) { 85 | return match_map_impl(x, lookup); 86 | } 87 | 88 | template<> 89 | inline cpp11::writable::integers match_impl(const std::vector& x, const std::vector& lookup) { 90 | return match_map_impl(x, lookup); 91 | } 92 | 93 | template<> 94 | inline cpp11::writable::integers match_impl(const std::vector& x, const std::vector& lookup) { 95 | return match_map_impl(x, lookup); 96 | } 97 | -------------------------------------------------------------------------------- /man/line.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/line.R 3 | \name{line} 4 | \alias{line} 5 | \alias{is_line} 6 | \alias{as_line} 7 | \title{Vector of lines} 8 | \usage{ 9 | line(..., default_dim = 2) 10 | 11 | is_line(x) 12 | 13 | as_line(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A vector of lines or an object to convert to it} 21 | } 22 | \value{ 23 | An \code{euclid_line} vector 24 | } 25 | \description{ 26 | A line is an undirected infinite line. For 2D it can be defined by the line 27 | equation \code{ax + by + c = 0} whereas for 3D it is usually defined by a point 28 | and a direction in the same way as rays, but implicitly extending to infinity 29 | in the opposite direction as well. 30 | } 31 | \section{Constructors}{ 32 | 33 | \strong{2 dimensional line} 34 | \itemize{ 35 | \item Providing 3 numerics will create lines with the given line equation 36 | \item Providing two points will construct lines going through those 37 | \item Providing a point and a vector will construct lines going through the point 38 | and extending in the direction of the vector. 39 | \item Providing a point and a direction will construct lines going through the 40 | point and extending in the given direction. 41 | \item Providing a ray will construct the supporting line for the ray 42 | \item Providing a segment will construct the supporting line for the segment 43 | } 44 | 45 | \strong{3 dimensional line} 46 | \itemize{ 47 | \item Providing two points will construct lines going through those 48 | \item Providing a point and a vector will construct lines going through the point 49 | and extending in the direction of the vector. 50 | \item Providing a point and a direction will construct lines going through the 51 | point and extending in the given direction. 52 | \item Providing a ray will construct the supporting line for the ray 53 | \item Providing a segment will construct the supporting line for the segment 54 | } 55 | } 56 | 57 | \examples{ 58 | # Construction 59 | l <- line(sample(10, 2), sample(10, 2), sample(10, 2)) 60 | l 61 | 62 | # 3D lines cannot be constructed from coefficients 63 | p <- point(sample(6), sample(6), sample(6)) 64 | line(p[1:3], p[4:6]) 65 | 66 | # Construction from point and vector 67 | line(p, vec(4, -2, 0)) 68 | 69 | } 70 | \seealso{ 71 | Other Geometries: 72 | \code{\link{circle}()}, 73 | \code{\link{direction}()}, 74 | \code{\link{iso_cube}()}, 75 | \code{\link{iso_rect}()}, 76 | \code{\link{plane}()}, 77 | \code{\link{point}()}, 78 | \code{\link{ray}()}, 79 | \code{\link{segment}()}, 80 | \code{\link{sphere}()}, 81 | \code{\link{tetrahedron}()}, 82 | \code{\link{triangle}()}, 83 | \code{\link{vec}()}, 84 | \code{\link{weighted_point}()} 85 | 86 | Other Curves: 87 | \code{\link{ray}()}, 88 | \code{\link{segment}()} 89 | } 90 | \concept{Curves} 91 | \concept{Geometries} 92 | -------------------------------------------------------------------------------- /man/subgeometries.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/subgeometries.R 3 | \name{subgeometries} 4 | \alias{subgeometries} 5 | \alias{vert} 6 | \alias{vert<-} 7 | \alias{edge} 8 | \alias{edge_count} 9 | \title{Extract vertices and edges from geometries} 10 | \usage{ 11 | vert(x, which = NULL, ...) 12 | 13 | vert(x, which = NULL, ...) <- value 14 | 15 | edge(x, which = NULL, ...) 16 | 17 | edge_count(x) 18 | } 19 | \arguments{ 20 | \item{x}{A vector of geometries} 21 | 22 | \item{which}{An integer vector giving the vertex/edge to extract, or \code{NULL} 23 | to extract all.} 24 | 25 | \item{...}{arguments passed on to methods} 26 | 27 | \item{value}{An \code{euclid_point} vector of the same dimensionality as \code{x}} 28 | } 29 | \value{ 30 | A \code{euclid_point} vector for \code{vert()} or a \code{euclid_segment} vector for 31 | \code{edge()} matching the dimensionality of \code{x} 32 | } 33 | \description{ 34 | Geometries located in space (all except directions and vectors), have one or 35 | more points supporting it. Those can be extracted and modifed with \code{vert()}. 36 | For geometries with a cardinality above one there's a choice of which support 37 | point to extract/modify or all. Geometries consisting of more than one vertex 38 | (segments, triangles, and tetrahedrons) also have associated edges that can 39 | be extracted (but not modified) with \code{edge()}. \code{edge_count()} provides the 40 | number of edges in each element in the geometry vector. The length of the 41 | output of \code{edge(x)} is \code{sum(edge_count(x))}. 42 | } 43 | \section{Vertex definition}{ 44 | 45 | For geometries that are defined exclusively by points the definition of the 46 | output is straight forward, e.g. for triangles \code{vert()} will extract one or 47 | all of the corners depending on the value of \code{which}. For the other 48 | geometries the output is defined according to the below: 49 | \itemize{ 50 | \item \strong{circles and spheres}: The vertex is the center 51 | \item \strong{rays}: The vertex is the source 52 | \item \strong{lines and planes}: The vertex is an arbitrary point on the geometry 53 | } 54 | } 55 | 56 | \examples{ 57 | # Get the source vertex in a segment 58 | s <- segment(point(3, 6), point(1, -7)) 59 | vert(s, 1) 60 | 61 | # And the target 62 | vert(s, 2) 63 | 64 | # Not providing an index extracts them all 65 | vert(s) 66 | 67 | # Set the source of a segment 68 | vert(s, 1) <- point(0, 0) 69 | s 70 | 71 | # Get a point on a line 72 | l <- line(4, 7, -1) 73 | vert(l) 74 | 75 | # Setting the vertex of a line moves it so it runs through it 76 | point(1, 2) \%is_on\% l 77 | vert(l) <- point(1, 2) 78 | point(1, 2) \%is_on\% l 79 | 80 | # Get one of the sides from a triangle 81 | t <- triangle(point(1, 2), point(6, 3), point(3, 1)) 82 | edge(t, 2) 83 | 84 | # or all 85 | edge(t) 86 | } 87 | \seealso{ 88 | Other Geometry methods: 89 | \code{\link{def}()}, 90 | \code{\link{euclid_geometry}} 91 | } 92 | \concept{Geometry methods} 93 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Anticonf (tm) script by Jeroen Ooms (2020) 3 | # This script will query 'pkg-config' for the required cflags and ldflags. 4 | # If pkg-config is unavailable or does not find the library, try setting 5 | # INCLUDE_DIR and LIB_DIR manually via e.g: 6 | # R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib' 7 | 8 | # Library settings 9 | SYS="UNIX" 10 | PKG_CONFIG_NAME="mpfr gmp" 11 | PKG_DEB_NAME="libgmp-dev libmpfr-dev" 12 | PKG_RPM_NAME="gmp-devel mpfr-devel" 13 | PKG_CSW_NAME="gmp_dev mpfr_dev" 14 | PKG_BREW_NAME="gmp mpfr" 15 | PKG_TEST_HEADER="" 16 | PKG_LIBS="-lmpfr -lgmp" 17 | 18 | # Alternative config on MacOS for native APIs 19 | # Freetype is not actually used on MacOS right now, but will be in the future 20 | if [[ "$OSTYPE" == "darwin"* ]]; then 21 | SYS="DARWIN" 22 | PKG_CONFIG_NAME="--static mpfr gmp" 23 | PKG_TEST_HEADER="" 24 | PKG_LIBS="-lmpfr -lgmp" 25 | fi 26 | 27 | # Use pkg-config if available 28 | if [ $(command -v pkg-config) ]; then 29 | PKGCONFIG_CFLAGS=$(pkg-config --cflags --silence-errors ${PKG_CONFIG_NAME}) 30 | PKGCONFIG_LIBS=$(pkg-config --libs ${PKG_CONFIG_NAME}) 31 | fi 32 | 33 | # Note that cflags may be empty in case of success 34 | if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then 35 | echo "Found INCLUDE_DIR and/or LIB_DIR!" 36 | PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS" 37 | PKG_LIBS="-L$LIB_DIR $PKG_LIBS" 38 | elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then 39 | echo "Found pkg-config cflags and libs!" 40 | PKG_CFLAGS=${PKGCONFIG_CFLAGS} 41 | PKG_LIBS=${PKGCONFIG_LIBS} 42 | fi 43 | 44 | # For debugging 45 | echo "Using PKG_CFLAGS=$PKG_CFLAGS" 46 | echo "Using PKG_LIBS=$PKG_LIBS" 47 | 48 | # Find compiler 49 | CC=$(${R_HOME}/bin/R CMD config CC) 50 | CFLAGS=$(${R_HOME}/bin/R CMD config CFLAGS) 51 | CPPFLAGS=$(${R_HOME}/bin/R CMD config CPPFLAGS) 52 | 53 | # Test configuration 54 | echo "#include $PKG_TEST_HEADER" | ${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E -xc - >/dev/null 2>configure.log 55 | 56 | # Customize the error 57 | if [ $? -ne 0 ]; then 58 | echo "--------------------------- [ANTICONF] --------------------------------" 59 | echo "Configuration failed to find the $PKG_CONFIG_NAME library. Try installing:" 60 | echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu, etc)" 61 | echo " * rpm: $PKG_RPM_NAME (Fedora, EPEL)" 62 | echo " * csw: $PKG_CSW_NAME (Solaris)" 63 | echo " * brew: $PKG_BREW_NAME (OSX)" 64 | echo "If $PKG_CONFIG_NAME is already installed, check that 'pkg-config' is in your" 65 | echo "PATH and PKG_CONFIG_PATH contains a $PKG_CONFIG_NAME.pc file. If pkg-config" 66 | echo "is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:" 67 | echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'" 68 | echo "-------------------------- [ERROR MESSAGE] ---------------------------" 69 | cat configure.log 70 | echo "--------------------------------------------------------------------" 71 | exit 1 72 | fi 73 | 74 | # Write to Makevars 75 | sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" -e "s|@SYS@|$SYS|g" src/Makevars.in > src/Makevars 76 | 77 | # Success 78 | exit 0 79 | -------------------------------------------------------------------------------- /man/vec.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vector.R 3 | \name{vec} 4 | \alias{vec} 5 | \alias{is_vec} 6 | \alias{as_vec} 7 | \title{Vector of vectors} 8 | \usage{ 9 | vec(..., default_dim = 2) 10 | 11 | is_vec(x) 12 | 13 | as_vec(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A vector of vectors or an object to convert to it} 21 | } 22 | \value{ 23 | An \code{euclid_vector} vector 24 | } 25 | \description{ 26 | A geometrical vector is somewhat different from the concept of a vector in 27 | programming hence the slightly confusing terminology. In geometry a vector is 28 | a direction and a magnitude most often defined by a point in space where the 29 | direction is defined as the direction from the origin to the point and the 30 | magnitude is defined as the distance from the origin to the point. 31 | } 32 | \section{Constructors}{ 33 | 34 | \strong{2 dimensional vectors} 35 | \itemize{ 36 | \item Providing a point will construct vectors pointing to the points from the 37 | origin centered. 38 | \item Providing two exact numeric vectors will construct vectors pointing to the 39 | point defined by the coordinates given. 40 | \item Providing a ray will construct vectors pointing in the same direction as 41 | the ray 42 | } 43 | 44 | \strong{3 dimensional vectors} 45 | \itemize{ 46 | \item Providing a point will construct vectors pointing to the points from the 47 | origin centered. 48 | \item Providing three exact numeric vectors will construct vectors pointing to 49 | the point defined by the coordinates given. 50 | \item Providing a ray will construct vectors pointing in the same direction as 51 | the ray 52 | } 53 | } 54 | 55 | \examples{ 56 | 57 | # Create vectors from points: 58 | v1 <- vec(x = 1:5, y = 4:8) 59 | 60 | # Vectors can be added and subtracted 61 | v1[1] + v1[2] 62 | 63 | v1[5] - v1[3] 64 | 65 | # You can invert a vector by taking its negative 66 | -v1 67 | 68 | # As vectors can be added you can also use sum() and cumsum() 69 | sum(v1) 70 | cumsum(v1) 71 | 72 | # Multiplying and dividing a vector by a numeric changes its magnitude 73 | v1 * 10 74 | v1 / 2.5 75 | 76 | # Multiplying two vectors gives the inner product of the two 77 | v1[1:2] * v1[3:4] 78 | 79 | # Vectors can be converted to points, directions and transformation matrices 80 | as_point(v1) 81 | as_direction(v1) 82 | as_affine_transformation(v1) 83 | 84 | } 85 | \seealso{ 86 | Other Geometries: 87 | \code{\link{circle}()}, 88 | \code{\link{direction}()}, 89 | \code{\link{iso_cube}()}, 90 | \code{\link{iso_rect}()}, 91 | \code{\link{line}()}, 92 | \code{\link{plane}()}, 93 | \code{\link{point}()}, 94 | \code{\link{ray}()}, 95 | \code{\link{segment}()}, 96 | \code{\link{sphere}()}, 97 | \code{\link{tetrahedron}()}, 98 | \code{\link{triangle}()}, 99 | \code{\link{weighted_point}()} 100 | 101 | Other Arrows: 102 | \code{\link{direction}()} 103 | } 104 | \concept{Arrows} 105 | \concept{Geometries} 106 | -------------------------------------------------------------------------------- /src/point_w.cpp: -------------------------------------------------------------------------------- 1 | #include "point_w.h" 2 | #include "point.h" 3 | #include "exact_numeric.h" 4 | 5 | #include 6 | 7 | [[cpp11::register]] 8 | point_w2_p create_point_w_2_empty() { 9 | std::vector vec; 10 | point_w2 *result(new point_w2(vec)); 11 | return {result}; 12 | } 13 | 14 | [[cpp11::register]] 15 | point_w2_p create_point_w_2_x_y_w(exact_numeric_p x, exact_numeric_p y, exact_numeric_p w) { 16 | if (x.get() == nullptr || y.get() == nullptr || w.get() == nullptr) { 17 | cpp11::stop("Data structure pointer cleared from memory"); 18 | } 19 | std::vector vec; 20 | vec.reserve(x->size()); 21 | for (size_t i = 0; i < x->size(); ++i) { 22 | if (!(*x)[i] || !(*y)[i] || !(*w)[i]) { 23 | vec.push_back(Weighted_point_2::NA_value()); 24 | continue; 25 | } 26 | vec.emplace_back(Point_2(Kernel::FT((*x)[i]), Kernel::FT((*y)[i])), (*w)[i]); 27 | } 28 | point_w2 *result(new point_w2(vec)); 29 | 30 | return {result}; 31 | } 32 | 33 | [[cpp11::register]] 34 | point_w2_p create_point_w_2_p_w(point2_p p, exact_numeric_p w) { 35 | if (p.get() == nullptr || w.get() == nullptr) { 36 | cpp11::stop("Data structure pointer cleared from memory"); 37 | } 38 | std::vector vec; 39 | vec.reserve(p->size()); 40 | for (size_t i = 0; i < p->size(); ++i) { 41 | if (!(*p)[i] || !(*w)[i]) { 42 | vec.push_back(Weighted_point_2::NA_value()); 43 | continue; 44 | } 45 | vec.emplace_back((*p)[i], (*w)[i]); 46 | } 47 | point_w2 *result(new point_w2(vec)); 48 | 49 | return {result}; 50 | } 51 | 52 | [[cpp11::register]] 53 | point_w3_p create_point_w_3_empty() { 54 | std::vector vec; 55 | point_w3 *result(new point_w3(vec)); 56 | return {result}; 57 | } 58 | 59 | [[cpp11::register]] 60 | point_w3_p create_point_w_3_x_y_z_w(exact_numeric_p x, exact_numeric_p y, exact_numeric_p z, exact_numeric_p w) { 61 | if (x.get() == nullptr || y.get() == nullptr || z.get() == nullptr || w.get() == nullptr) { 62 | cpp11::stop("Data structure pointer cleared from memory"); 63 | } 64 | std::vector vec; 65 | vec.reserve(x->size()); 66 | for (size_t i = 0; i < x->size(); ++i) { 67 | if (!(*x)[i] || !(*y)[i] || !(*z)[i] || !(*w)[i]) { 68 | vec.push_back(Weighted_point_3::NA_value()); 69 | continue; 70 | } 71 | vec.emplace_back(Point_3(Kernel::FT((*x)[i]), Kernel::FT((*y)[i]), Kernel::FT((*z)[i])), (*w)[i]); 72 | } 73 | point_w3 *result(new point_w3(vec)); 74 | 75 | return {result}; 76 | } 77 | 78 | [[cpp11::register]] 79 | point_w3_p create_point_w_3_p_w(point3_p p, exact_numeric_p w) { 80 | if (p.get() == nullptr || w.get() == nullptr) { 81 | cpp11::stop("Data structure pointer cleared from memory"); 82 | } 83 | std::vector vec; 84 | vec.reserve(p->size()); 85 | for (size_t i = 0; i < p->size(); ++i) { 86 | if (!(*p)[i] || !(*w)[i]) { 87 | vec.push_back(Weighted_point_3::NA_value()); 88 | continue; 89 | } 90 | vec.emplace_back((*p)[i], (*w)[i]); 91 | } 92 | point_w3 *result(new point_w3(vec)); 93 | 94 | return {result}; 95 | } 96 | -------------------------------------------------------------------------------- /man/intersection.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_intersection.R 3 | \name{intersection} 4 | \alias{intersection} 5 | \alias{intersection_circle} 6 | \alias{intersection_iso_rect} 7 | \alias{intersection_plane} 8 | \alias{intersection_point} 9 | \alias{intersection_line} 10 | \alias{intersection_ray} 11 | \alias{intersection_segment} 12 | \alias{intersection_sphere} 13 | \alias{intersection_triangle} 14 | \title{Calculate intersections between geometries} 15 | \usage{ 16 | intersection(x, y, ...) 17 | 18 | intersection_circle(x, y) 19 | 20 | intersection_iso_rect(x, y) 21 | 22 | intersection_plane(x, y) 23 | 24 | intersection_point(x, y) 25 | 26 | intersection_line(x, y) 27 | 28 | intersection_ray(x, y) 29 | 30 | intersection_segment(x, y) 31 | 32 | intersection_sphere(x, y) 33 | 34 | intersection_triangle(x, y) 35 | } 36 | \arguments{ 37 | \item{x, y}{Geometry vectors or bounding boxes} 38 | 39 | \item{...}{arguments passed on to methods} 40 | } 41 | \value{ 42 | a list of scalar geometry vectors and \code{NULL}s depending on the result 43 | of the intersection query, or a vector of geometries as requested. 44 | } 45 | \description{ 46 | An intersection between two geometries is defined as the geometry that is 47 | contained in both geometries. In other words all points laying on the 48 | intersection is also part of the two incoming geometries. It follows that the 49 | result of calculating intersections can be varied, even between the same 50 | combination of geometries. For example, The intersection between a triangle 51 | and a plane can be \code{NULL}, a point, a segment, or a triangle depending on 52 | their relative position and orientation. Because of this, and to avoid 53 | confusion around the return type \code{intersection()} always return a list of 54 | scalar geometries or \code{NULL}s. Intersections can only be calculated between 55 | geometries located in space, which rules out vectors and directions. Further, 56 | not all combinations of geometries have exact intersections defined (circles 57 | and spheres are especially limited). euclid also provides a list of type-safe 58 | intersection functions that allways returns a vector of geometries of the 59 | requested type. Intersections that doesn't match the requested type are 60 | returned as \code{NA}, as are non-intersecting pairs. It is thus not possible to 61 | determine if an intersection occurs using these functions. 62 | } 63 | \examples{ 64 | # Example of the difference in output 65 | t <- triangle(point(0, 0), point(1, 1), point(0, 1)) 66 | l <- line(1, -1, c(0, 1, 2)) 67 | i <- intersection(t, l) 68 | i 69 | 70 | plot(t, col = "grey", border = NA, xlim= c(-0.5, 1), ylim = c(0, 1.5)) 71 | euclid_plot(l) 72 | for (int in i) { 73 | euclid_plot(int, col = "firebrick", pch = 16, cex = 2, lwd = 3) 74 | } 75 | 76 | # Input is symmetric 77 | intersection(l, t) 78 | 79 | # Request only segment intersections 80 | intersection_segment(l, t) 81 | 82 | } 83 | \seealso{ 84 | Other Intersections: 85 | \code{\link{has_intersection}()} 86 | 87 | Other Boolean operations: 88 | \code{\link{boolean_operations}} 89 | } 90 | \concept{Boolean operations} 91 | \concept{Intersections} 92 | -------------------------------------------------------------------------------- /man/location_predicates.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_predicates.R 3 | \name{location_predicates} 4 | \alias{location_predicates} 5 | \alias{has_on} 6 | \alias{\%is_on\%} 7 | \alias{has_inside} 8 | \alias{\%is_inside\%} 9 | \alias{has_outside} 10 | \alias{\%is_outside\%} 11 | \alias{has_on_positive_side} 12 | \alias{\%is_on_positive_side\%} 13 | \alias{has_on_negative_side} 14 | \alias{\%is_on_negative_side\%} 15 | \title{Query location of points relative to geometry} 16 | \usage{ 17 | has_on(x, y) 18 | 19 | y \%is_on\% x 20 | 21 | has_inside(x, y) 22 | 23 | y \%is_inside\% x 24 | 25 | has_outside(x, y) 26 | 27 | y \%is_outside\% x 28 | 29 | has_on_positive_side(x, y) 30 | 31 | y \%is_on_positive_side\% x 32 | 33 | has_on_negative_side(x, y) 34 | 35 | y \%is_on_negative_side\% x 36 | } 37 | \arguments{ 38 | \item{x}{A geometry vector} 39 | 40 | \item{y}{A points vector} 41 | } 42 | \value{ 43 | A logical vector 44 | } 45 | \description{ 46 | Point predicates is at the heart of many geometry algorithms. These set of 47 | functions allows you to query the location of points relative to a given 48 | geometry vector. The functions are provided as both regular and binary 49 | operators. 50 | } 51 | \details{ 52 | There are two types of location predicates: 53 | \subsection{Containment (inside and outside)}{ 54 | 55 | Queries whether a point is inside or outside the boundary defined by the 56 | geometry. In 2D it is defined for geometries that have an area, In 3D it is 57 | defined for geometries that has a volume. This means that containment is 58 | defined for 2D circles but not 3D circles. 59 | } 60 | 61 | \subsection{Orientation (positive and negative)}{ 62 | 63 | Queries whether a point lies on the positive or negative side of a geometry. 64 | It is defined for geometries that splits the dimensional space in two. This 65 | means that it is not defined for rays, segments, etc with a finite length. 66 | 67 | For both types of predicates there are also the condition that the point lies 68 | \emph{on} the geometry. This predicate is equivalent for both types of predicates 69 | and is defined for all geometries that has a location. This means that e.g. 70 | directions and vectors do not define this predicate. 71 | } 72 | } 73 | \examples{ 74 | p <- point(sample(10, 50, TRUE), sample(10, 50, TRUE)) 75 | t <- triangle(point(1, 5), point(10, 10), point(5, 1)) 76 | 77 | plot(t, col = "grey", border = NA) 78 | euclid_plot(p[p \%is_on\% t], pch = 16, col = "firebrick") 79 | euclid_plot(p[p \%is_inside\% t], pch = 16, col = "steelblue") 80 | euclid_plot(p[p \%is_outside\% t], pch = 16, col = "goldenrod") 81 | 82 | l <- line(-1, 1, 2) 83 | plot(l, xlim = c(1, 10), ylim = c(1, 10)) 84 | euclid_plot(p[p \%is_on_positive_side\% l], pch = 16, col = "firebrick") 85 | euclid_plot(p[p \%is_on_negative_side\% l], pch = 16, col = "steelblue") 86 | } 87 | \seealso{ 88 | Other Predicates: 89 | \code{\link{collinear}()}, 90 | \code{\link{constant_in}}, 91 | \code{\link{geometry_class}}, 92 | \code{\link{geometry_turns}}, 93 | \code{\link{has_intersection}()}, 94 | \code{\link{in_order}()}, 95 | \code{\link{is_degenerate}()}, 96 | \code{\link{parallel}()} 97 | } 98 | \concept{Predicates} 99 | -------------------------------------------------------------------------------- /inst/include/internal/cgal_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cran.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | template 37 | class with_NA : public T { 38 | public: 39 | using T::T; 40 | 41 | with_NA() : T(), _valid(true) {} 42 | with_NA(const T& copy) : T(copy), _valid(true) {} 43 | 44 | operator bool() const { return _valid; } 45 | bool is_na() const { return !_valid; } 46 | 47 | bool get_flag(size_t i) const { return _info[i % 5]; } 48 | void set_flag(size_t i, bool val) { _info[i % 5] = val; } 49 | 50 | T base() const { return {*this}; } 51 | 52 | static with_NA NA_value() { 53 | with_NA invalid; 54 | invalid._valid = false; 55 | return invalid; 56 | } 57 | 58 | private: 59 | bool _valid = true; 60 | std::bitset<5> _info = 0b00000; 61 | }; 62 | 63 | typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; 64 | typedef with_NA Exact_number; 65 | typedef with_NA Circle_2; 66 | typedef with_NA Circle_3; 67 | typedef with_NA Direction_2; 68 | typedef with_NA Direction_3; 69 | typedef with_NA Iso_cuboid; 70 | typedef with_NA Iso_rectangle; 71 | typedef with_NA Line_2; 72 | typedef with_NA Line_3; 73 | typedef with_NA Plane; 74 | typedef with_NA Point_2; 75 | typedef with_NA Point_3; 76 | typedef with_NA Ray_2; 77 | typedef with_NA Ray_3; 78 | typedef with_NA Segment_2; 79 | typedef with_NA Segment_3; 80 | typedef with_NA Sphere; 81 | typedef with_NA Tetrahedron; 82 | typedef with_NA Triangle_2; 83 | typedef with_NA Triangle_3; 84 | typedef with_NA Vector_2; 85 | typedef with_NA Vector_3; 86 | typedef with_NA Weighted_point_2; 87 | typedef with_NA Weighted_point_3; 88 | typedef with_NA Aff_transformation_2; 89 | typedef with_NA Aff_transformation_3; 90 | typedef with_NA Bbox_2; 91 | typedef with_NA Bbox_3; 92 | -------------------------------------------------------------------------------- /man/point.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/point.R 3 | \name{point} 4 | \alias{point} 5 | \alias{is_point} 6 | \alias{as_point} 7 | \title{Vector of points} 8 | \usage{ 9 | point(..., default_dim = 2) 10 | 11 | is_point(x) 12 | 13 | as_point(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A point vector or an object to convert to it} 21 | } 22 | \value{ 23 | a \code{euclid_point} vector 24 | } 25 | \description{ 26 | Points are the fundamental unit in geometry, from which other geometric 27 | primitives can be constructed. Points support less than and greater than 28 | operations which are handled lexicographically. This means that point vectors 29 | can also be sorted and ranked. Subtracking a point from a point gives a 30 | vector. Adding and subtracting a vector to a point translates the point by 31 | the vector. 32 | } 33 | \section{Constructors}{ 34 | 35 | \strong{2 dimensional points} 36 | \itemize{ 37 | \item Providing two numeric vector will construct points with those x and y 38 | coordinate. 39 | \item Providing a vector will construct a point at the location the vector 40 | points to. 41 | \item Providing a weighted point vector will create a points at the same 42 | locations without weights. 43 | } 44 | 45 | \strong{3 dimensional points} 46 | \itemize{ 47 | \item Providing three numeric vector will construct points with those x, y, and z 48 | coordinate. 49 | \item Providing a vector will construct a point at the location the vector 50 | points to. 51 | \item Providing a weighted point vector will create a points at the same 52 | locations without weights. 53 | } 54 | } 55 | 56 | \examples{ 57 | num1 <- exact_numeric(runif(5)) 58 | num2 <- exact_numeric(runif(5)) 59 | num3 <- exact_numeric(runif(5)) 60 | 61 | # 2 dimensions 62 | p <- point(num1, num2) 63 | p 64 | 65 | plot(p) 66 | 67 | # 3 dimensions 68 | point(num1, num2, num3) 69 | 70 | # Standard R vectors are automatically converted to exact_numeric vectors 71 | point(runif(5), runif(5)) 72 | 73 | # Convert points to vectors 74 | as_vec(p) 75 | 76 | # Arithmetic 77 | # Translate by adding vector 78 | p + vec(3, 7) 79 | 80 | # Create vector by subtracting points 81 | p[1:2] - p[3:4] 82 | 83 | # Sorting etc. 84 | sort(p) 85 | min(p) 86 | cummax(p) 87 | rank(p) 88 | p[1:2] < p[3:4] 89 | 90 | } 91 | \seealso{ 92 | Other Geometries: 93 | \code{\link{circle}()}, 94 | \code{\link{direction}()}, 95 | \code{\link{iso_cube}()}, 96 | \code{\link{iso_rect}()}, 97 | \code{\link{line}()}, 98 | \code{\link{plane}()}, 99 | \code{\link{ray}()}, 100 | \code{\link{segment}()}, 101 | \code{\link{sphere}()}, 102 | \code{\link{tetrahedron}()}, 103 | \code{\link{triangle}()}, 104 | \code{\link{vec}()}, 105 | \code{\link{weighted_point}()} 106 | 107 | Other Locations: 108 | \code{\link{barycenter}()}, 109 | \code{\link{bisector}()}, 110 | \code{\link{centroid}()}, 111 | \code{\link{circumcenter}()}, 112 | \code{\link{equidistant_line}()}, 113 | \code{\link{radical}()}, 114 | \code{\link{weighted_point}()} 115 | } 116 | \concept{Geometries} 117 | \concept{Locations} 118 | -------------------------------------------------------------------------------- /src/get_edge.h: -------------------------------------------------------------------------------- 1 | #include "cgal_types.h" 2 | #include 3 | 4 | template 5 | inline U get_edge_impl(const T& geometry, int which) { 6 | return U::NA_value(); 7 | } 8 | 9 | template<> 10 | inline Segment_2 get_edge_impl(const Segment_2& geometry, int which) { 11 | return geometry; 12 | } 13 | 14 | template<> 15 | inline Segment_3 get_edge_impl(const Segment_3& geometry, int which) { 16 | return geometry; 17 | } 18 | 19 | template<> 20 | inline Segment_2 get_edge_impl(const Iso_rectangle& geometry, int which) { 21 | switch (which) { 22 | case 0: return Segment_2(geometry.vertex(0), geometry.vertex(1)); 23 | case 1: return Segment_2(geometry.vertex(1), geometry.vertex(2)); 24 | case 2: return Segment_2(geometry.vertex(2), geometry.vertex(3)); 25 | case 3: return Segment_2(geometry.vertex(3), geometry.vertex(0)); 26 | } 27 | return Segment_2::NA_value(); 28 | } 29 | 30 | template<> 31 | inline Segment_3 get_edge_impl(const Iso_cuboid& geometry, int which) { 32 | switch (which) { 33 | case 0: return Segment_3(geometry.vertex(0), geometry.vertex(1)); 34 | case 1: return Segment_3(geometry.vertex(1), geometry.vertex(2)); 35 | case 2: return Segment_3(geometry.vertex(2), geometry.vertex(3)); 36 | case 3: return Segment_3(geometry.vertex(3), geometry.vertex(0)); 37 | case 4: return Segment_3(geometry.vertex(4), geometry.vertex(5)); 38 | case 5: return Segment_3(geometry.vertex(5), geometry.vertex(6)); 39 | case 6: return Segment_3(geometry.vertex(6), geometry.vertex(7)); 40 | case 7: return Segment_3(geometry.vertex(7), geometry.vertex(4)); 41 | case 8: return Segment_3(geometry.vertex(0), geometry.vertex(5)); 42 | case 9: return Segment_3(geometry.vertex(1), geometry.vertex(6)); 43 | case 10: return Segment_3(geometry.vertex(2), geometry.vertex(7)); 44 | case 11: return Segment_3(geometry.vertex(3), geometry.vertex(4)); 45 | } 46 | return Segment_3::NA_value(); 47 | } 48 | 49 | template<> 50 | inline Segment_2 get_edge_impl(const Triangle_2& geometry, int which) { 51 | switch (which) { 52 | case 0: return Segment_2(geometry.vertex(0), geometry.vertex(1)); 53 | case 1: return Segment_2(geometry.vertex(1), geometry.vertex(2)); 54 | case 2: return Segment_2(geometry.vertex(2), geometry.vertex(0)); 55 | } 56 | return Segment_2::NA_value(); 57 | } 58 | 59 | template<> 60 | inline Segment_3 get_edge_impl(const Triangle_3& geometry, int which) { 61 | switch (which) { 62 | case 0: return Segment_3(geometry.vertex(0), geometry.vertex(1)); 63 | case 1: return Segment_3(geometry.vertex(1), geometry.vertex(2)); 64 | case 2: return Segment_3(geometry.vertex(2), geometry.vertex(0)); 65 | } 66 | return Segment_3::NA_value(); 67 | } 68 | 69 | template<> 70 | inline Segment_3 get_edge_impl(const Tetrahedron& geometry, int which) { 71 | switch (which) { 72 | case 0: return Segment_3(geometry.vertex(0), geometry.vertex(1)); 73 | case 1: return Segment_3(geometry.vertex(1), geometry.vertex(2)); 74 | case 2: return Segment_3(geometry.vertex(2), geometry.vertex(0)); 75 | case 3: return Segment_3(geometry.vertex(0), geometry.vertex(4)); 76 | case 4: return Segment_3(geometry.vertex(1), geometry.vertex(4)); 77 | case 5: return Segment_3(geometry.vertex(2), geometry.vertex(4)); 78 | } 79 | return Segment_3::NA_value(); 80 | } 81 | -------------------------------------------------------------------------------- /src/exact_numeric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "cgal_types.h" 11 | 12 | class exact_numeric { 13 | private: 14 | std::vector _storage; 15 | 16 | public: 17 | exact_numeric() {} 18 | ~exact_numeric() {} 19 | 20 | // Construction 21 | exact_numeric(const cpp11::doubles& x) { 22 | _storage.reserve(x.size()); 23 | for (R_xlen_t i = 0; i < x.size(); ++i) { 24 | if (R_finite(x[i])) { 25 | _storage.emplace_back(x[i]); 26 | } else { 27 | _storage.push_back(Exact_number::NA_value()); 28 | } 29 | } 30 | } 31 | exact_numeric(const std::vector& x) : _storage(x.begin(), x.end()) {} 32 | exact_numeric(std::vector& x) { 33 | _storage.swap(x); 34 | } 35 | exact_numeric(const exact_numeric& x) : _storage(x._storage) {} 36 | exact_numeric& operator=(const exact_numeric& copy) { 37 | _storage.clear(); 38 | _storage.insert(_storage.end(), copy._storage.begin(), copy._storage.end()); 39 | return *this; 40 | } 41 | const std::vector& get_storage() { return _storage; } 42 | 43 | // Utility 44 | size_t size() const { 45 | return _storage.size(); 46 | } 47 | Exact_number operator[](size_t index) const { 48 | return _storage[index]; 49 | } 50 | void clear() { _storage.clear(); } 51 | void swap(exact_numeric& x) { _storage.swap(x._storage); } 52 | void push_back(Exact_number x) { _storage.push_back(x); } 53 | exact_numeric subset(cpp11::integers index) const; 54 | exact_numeric assign(cpp11::integers index, const exact_numeric& value) const; 55 | exact_numeric combine(cpp11::list_of< cpp11::external_pointer > extra) const; 56 | cpp11::writable::logicals is_na() const; 57 | bool any_na() const; 58 | 59 | // Convert back 60 | cpp11::writable::doubles as_numeric() const; 61 | 62 | // Relation 63 | cpp11::writable::logicals operator==(const exact_numeric& x) const; 64 | cpp11::writable::logicals operator<(const exact_numeric& x) const; 65 | cpp11::writable::logicals operator>(const exact_numeric& x) const; 66 | exact_numeric unique() const; 67 | cpp11::writable::logicals duplicated() const; 68 | int any_duplicated() const; 69 | cpp11::writable::integers rank() const; 70 | cpp11::writable::integers match(const exact_numeric& table) const; 71 | 72 | // Math 73 | exact_numeric operator+(const exact_numeric& x) const; 74 | exact_numeric operator-(const exact_numeric& x) const; 75 | exact_numeric operator-() const; 76 | exact_numeric operator*(const exact_numeric& x) const; 77 | exact_numeric operator/(const exact_numeric& x) const; 78 | 79 | exact_numeric abs() const; 80 | cpp11::writable::integers sign() const; 81 | 82 | exact_numeric cumsum() const; 83 | exact_numeric cumprod() const; 84 | exact_numeric cummax() const; 85 | exact_numeric cummin() const; 86 | exact_numeric diff(int lag) const; 87 | exact_numeric sort(bool decreasing, cpp11::logicals na_last) const; 88 | 89 | // Summary 90 | exact_numeric sum(bool na_rm) const; 91 | exact_numeric prod(bool na_rm) const; 92 | exact_numeric min(bool na_rm) const; 93 | exact_numeric max(bool na_rm) const; 94 | }; 95 | 96 | typedef cpp11::external_pointer exact_numeric_p; 97 | -------------------------------------------------------------------------------- /src/segment.cpp: -------------------------------------------------------------------------------- 1 | #include "segment.h" 2 | #include "point.h" 3 | #include "vector.h" 4 | 5 | #include 6 | 7 | [[cpp11::register]] 8 | segment2_p create_segment_2_empty() { 9 | std::vector vec; 10 | segment2 *result(new segment2(vec)); 11 | return {result}; 12 | } 13 | 14 | [[cpp11::register]] 15 | segment2_p create_segment_2_p_q(point2_p p, point2_p q) { 16 | if (p.get() == nullptr || q.get() == nullptr) { 17 | cpp11::stop("Data structure pointer cleared from memory"); 18 | } 19 | std::vector vec; 20 | vec.reserve(p->size()); 21 | for (size_t i = 0; i < p->size(); ++i) { 22 | if (!(*p)[i] || !(*q)[i]) { 23 | vec.push_back(Segment_2::NA_value()); 24 | continue; 25 | } 26 | vec.emplace_back((*p)[i], (*q)[i]); 27 | } 28 | segment2 *result(new segment2(vec)); 29 | 30 | return {result}; 31 | } 32 | 33 | [[cpp11::register]] 34 | segment2_p create_segment_2_p_v(point2_p p, vector2_p v) { 35 | if (p.get() == nullptr || v.get() == nullptr) { 36 | cpp11::stop("Data structure pointer cleared from memory"); 37 | } 38 | std::vector vec; 39 | vec.reserve(p->size()); 40 | for (size_t i = 0; i < p->size(); ++i) { 41 | if (!(*p)[i] || !(*v)[i]) { 42 | vec.push_back(Segment_2::NA_value()); 43 | continue; 44 | } 45 | vec.emplace_back((*p)[i], (*p)[i] + (*v)[i]); 46 | } 47 | segment2 *result(new segment2(vec)); 48 | 49 | return {result}; 50 | } 51 | 52 | [[cpp11::register]] 53 | segment2_p segment_2_negate(segment2_p x) { 54 | if (x.get() == nullptr) { 55 | cpp11::stop("Data structure pointer cleared from memory"); 56 | } 57 | std::vector vec = -(*x); 58 | segment2 *result(new segment2(vec)); 59 | 60 | return {result}; 61 | } 62 | 63 | [[cpp11::register]] 64 | segment3_p create_segment_3_empty() { 65 | std::vector vec; 66 | segment3 *result(new segment3(vec)); 67 | return {result}; 68 | } 69 | 70 | [[cpp11::register]] 71 | segment3_p create_segment_3_p_q(point3_p p, point3_p q) { 72 | if (p.get() == nullptr || q.get() == nullptr) { 73 | cpp11::stop("Data structure pointer cleared from memory"); 74 | } 75 | std::vector vec; 76 | vec.reserve(p->size()); 77 | for (size_t i = 0; i < p->size(); ++i) { 78 | if (!(*p)[i] || !(*q)[i]) { 79 | vec.push_back(Segment_3::NA_value()); 80 | continue; 81 | } 82 | vec.emplace_back((*p)[i], (*q)[i]); 83 | } 84 | segment3 *result(new segment3(vec)); 85 | 86 | return {result}; 87 | } 88 | 89 | [[cpp11::register]] 90 | segment3_p create_segment_3_p_v(point3_p p, vector3_p v) { 91 | if (p.get() == nullptr || v.get() == nullptr) { 92 | cpp11::stop("Data structure pointer cleared from memory"); 93 | } 94 | std::vector vec; 95 | vec.reserve(p->size()); 96 | for (size_t i = 0; i < p->size(); ++i) { 97 | if (!(*p)[i] || !(*v)[i]) { 98 | vec.push_back(Segment_3::NA_value()); 99 | continue; 100 | } 101 | vec.emplace_back((*p)[i], (*p)[i] + (*v)[i]); 102 | } 103 | segment3 *result(new segment3(vec)); 104 | 105 | return {result}; 106 | } 107 | 108 | [[cpp11::register]] 109 | segment3_p segment_3_negate(segment3_p x) { 110 | if (x.get() == nullptr) { 111 | cpp11::stop("Data structure pointer cleared from memory"); 112 | } 113 | std::vector vec = -(*x); 114 | segment3 *result(new segment3(vec)); 115 | 116 | return {result}; 117 | } 118 | -------------------------------------------------------------------------------- /R/triangle.R: -------------------------------------------------------------------------------- 1 | #' Vector of triangles 2 | #' 3 | #' Triangles are sets of three vertices in either 2 or three dimensions. In 4 | #' three dimensions the order of the vertices determines the orientation of the 5 | #' triangle with vertices being in counter-clockwise order from the facing side. 6 | #' If the 3 vertices are colinear the triangle is considered to be 7 | #' [degenerate][is_degenerate] 8 | #' 9 | #' @param ... Various input. See the Constructor section. 10 | #' @param default_dim The dimensionality when constructing an empty vector 11 | #' @param x A vector of triangles or an object to convert to it 12 | #' 13 | #' @return An `euclid_triangle` vector 14 | #' 15 | #' @section Constructors: 16 | #' **2 and 3 dimensional triangles** 17 | #' - Providing three points will construct triangles in the order the points are 18 | #' given. 19 | #' 20 | #' @export 21 | #' 22 | #' @family Geometries 23 | #' @family Surfaces 24 | #' 25 | #' @examples 26 | #' # Construction 27 | #' p <- point(sample(6), sample(6)) 28 | #' t <- triangle(p[1:2], p[3:4], p[5:6]) 29 | #' t 30 | #' plot(t) 31 | #' 32 | #' # 3D triangles can be converted to planes 33 | #' p <- point(sample(6), sample(6), sample(6)) 34 | #' t <- triangle(p[1:2], p[3:4], p[5:6]) 35 | #' as_plane(t) 36 | #' 37 | triangle <- function(..., default_dim = 2) { 38 | inputs <- validate_constructor_input(...) 39 | 40 | if (length(inputs) == 0) { 41 | return(new_triangle_empty(default_dim)) 42 | } 43 | 44 | points <- inputs[vapply(inputs, is_point, logical(1))] 45 | 46 | if (length(points) == 3) { 47 | new_triangle_from_3_points(points[[1]], points[[2]], points[[3]]) 48 | } else { 49 | cli_abort("Can't construct a {.cls euclid_triangle} vector from the given input") 50 | } 51 | } 52 | #' @rdname triangle 53 | #' @export 54 | is_triangle <- function(x) inherits(x, "euclid_triangle") 55 | 56 | 57 | # Conversion -------------------------------------------------------------- 58 | 59 | #' @rdname triangle 60 | #' @export 61 | as_triangle <- function(x) { 62 | UseMethod("as_triangle") 63 | } 64 | #' @export 65 | as_triangle.default <- function(x) { 66 | cli_abort("Can't convert the input to a {.cls euclid_triangle} vector") 67 | } 68 | #' @export 69 | as_triangle.euclid_triangle <- function(x) x 70 | 71 | #' @export 72 | as_plane.euclid_triangle <- function(x) { 73 | plane(x) 74 | } 75 | 76 | # Misc -------------------------------------------------------------------- 77 | 78 | #' @export 79 | seq.euclid_triangle <- function(from, to, length.out = NULL, along.with = NULL, ...) { 80 | if (dim(from) != dim(to)) { 81 | cli_abort("{.arg from} and {.arg to} must have the same number of dimensions") 82 | } 83 | triangle( 84 | seq(vert(from, 1), vert(to, 1), length.out, along.with), 85 | seq(vert(from, 2), vert(to, 2), length.out, along.with), 86 | seq(vert(from, 3), vert(to, 3), length.out, along.with) 87 | ) 88 | } 89 | 90 | # Internal Constructors --------------------------------------------------- 91 | 92 | new_triangle_empty <- function(dim) { 93 | if (dim == 2) { 94 | new_geometry_vector(create_triangle_2_empty()) 95 | } else { 96 | new_geometry_vector(create_triangle_3_empty()) 97 | } 98 | } 99 | new_triangle_from_3_points <- function(p, q, r) { 100 | if (dim(p) == 2) { 101 | new_geometry_vector(create_triangle_2_3points(get_ptr(p), get_ptr(q), get_ptr(r))) 102 | } else { 103 | new_geometry_vector(create_triangle_3_3points(get_ptr(p), get_ptr(q), get_ptr(r))) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/sphere.cpp: -------------------------------------------------------------------------------- 1 | #include "sphere.h" 2 | #include "point.h" 3 | #include "circle.h" 4 | 5 | [[cpp11::register]] 6 | sphere_p create_sphere_empty() { 7 | std::vector vec; 8 | sphere *result(new sphere(vec)); 9 | return {result}; 10 | } 11 | 12 | [[cpp11::register]] 13 | sphere_p create_sphere_center_radius(point3_p center, exact_numeric_p r2) { 14 | if (center.get() == nullptr || r2.get() == nullptr) { 15 | cpp11::stop("Data structure pointer cleared from memory"); 16 | } 17 | std::vector vec; 18 | vec.reserve(center->size()); 19 | for (size_t i = 0; i < center->size(); ++i) { 20 | if (!(*center)[i] || !(*r2)[i] || (*r2)[i] < 0.0) { 21 | vec.push_back(Sphere::NA_value()); 22 | continue; 23 | } 24 | vec.emplace_back((*center)[i], (*r2)[i]); 25 | } 26 | sphere *result(new sphere(vec)); 27 | 28 | return {result}; 29 | } 30 | 31 | [[cpp11::register]] 32 | sphere_p create_sphere_4_point(point3_p p, point3_p q, point3_p r, point3_p s) { 33 | if (p.get() == nullptr || q.get() == nullptr || r.get() == nullptr || s.get() == nullptr) { 34 | cpp11::stop("Data structure pointer cleared from memory"); 35 | } 36 | std::vector vec; 37 | vec.reserve(p->size()); 38 | for (size_t i = 0; i < p->size(); ++i) { 39 | if (!(*p)[i] || !(*q)[i] || !(*r)[i] || !(*s)[i]) { 40 | vec.push_back(Sphere::NA_value()); 41 | continue; 42 | } 43 | if (CGAL::coplanar((*p)[i], (*q)[i], (*r)[i], (*s)[i])) { 44 | cpp11::warning("sphere cannot be constructed from 4 coplanar points"); 45 | vec.push_back(Sphere::NA_value()); 46 | continue; 47 | } 48 | vec.emplace_back((*p)[i], (*q)[i], (*r)[i], (*s)[i]); 49 | } 50 | sphere *result(new sphere(vec)); 51 | 52 | return {result}; 53 | } 54 | 55 | [[cpp11::register]] 56 | sphere_p create_sphere_3_point(point3_p p, point3_p q, point3_p r) { 57 | if (p.get() == nullptr || q.get() == nullptr || r.get() == nullptr) { 58 | cpp11::stop("Data structure pointer cleared from memory"); 59 | } 60 | std::vector vec; 61 | vec.reserve(p->size()); 62 | for (size_t i = 0; i < p->size(); ++i) { 63 | if (!(*p)[i] || !(*q)[i] || !(*r)[i]) { 64 | vec.push_back(Sphere::NA_value()); 65 | continue; 66 | } 67 | vec.emplace_back((*p)[i], (*q)[i], (*r)[i]); 68 | } 69 | sphere *result(new sphere(vec)); 70 | 71 | return {result}; 72 | } 73 | 74 | [[cpp11::register]] 75 | sphere_p create_sphere_2_point(point3_p p, point3_p q) { 76 | if (p.get() == nullptr || q.get() == nullptr) { 77 | cpp11::stop("Data structure pointer cleared from memory"); 78 | } 79 | std::vector vec; 80 | vec.reserve(p->size()); 81 | for (size_t i = 0; i < p->size(); ++i) { 82 | if (!(*p)[i] || !(*q)[i]) { 83 | vec.push_back(Sphere::NA_value()); 84 | continue; 85 | } 86 | vec.emplace_back((*p)[i], (*q)[i]); 87 | } 88 | sphere *result(new sphere(vec)); 89 | 90 | return {result}; 91 | } 92 | 93 | [[cpp11::register]] 94 | sphere_p create_sphere_circle(circle3_p circ) { 95 | if (circ.get() == nullptr) { 96 | cpp11::stop("Data structure pointer cleared from memory"); 97 | } 98 | std::vector vec; 99 | vec.reserve(circ->size()); 100 | for (size_t i = 0; i < circ->size(); ++i) { 101 | if (!(*circ)[i]) { 102 | vec.push_back(Sphere::NA_value()); 103 | continue; 104 | } 105 | vec.emplace_back((*circ)[i]); 106 | } 107 | sphere *result(new sphere(vec)); 108 | 109 | return {result}; 110 | } 111 | -------------------------------------------------------------------------------- /man/euclid_plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry_plot.R 3 | \name{euclid_plot} 4 | \alias{euclid_plot} 5 | \alias{euclid_grob} 6 | \alias{plot.euclid_geometry} 7 | \title{Plotting functions for geometries} 8 | \usage{ 9 | euclid_plot(x, ..., mapping_plane = "z") 10 | 11 | euclid_grob( 12 | x, 13 | ..., 14 | unit = "native", 15 | name = NULL, 16 | gp = gpar(), 17 | vp = NULL, 18 | mapping_plane = "z" 19 | ) 20 | 21 | \method{plot}{euclid_geometry}( 22 | x, 23 | y, 24 | xlim = NULL, 25 | ylim = NULL, 26 | mapping_plane = "z", 27 | add = FALSE, 28 | axes = TRUE, 29 | frame.plot = axes, 30 | ... 31 | ) 32 | } 33 | \arguments{ 34 | \item{x}{A geometry vector} 35 | 36 | \item{...}{Arguments passed along to the specific drawing method. 37 | \itemize{ 38 | \item points (and weighted points) use \code{\link[graphics:points]{points()}} and 39 | \code{\link[grid:grid.points]{pointsGrob()}} 40 | \item circles use \code{\link[graphics:symbols]{symbols()}} and 41 | \code{\link[grid:grid.circle]{circleGrob()}} 42 | \item directions and vectors use \code{\link[graphics:arrows]{arrows()}} and 43 | \code{\link[grid:grid.segments]{segmentsGrob()}} 44 | \item iso rectangles and bounding boxes use \code{\link[graphics:symbols]{symbols()}} and 45 | \code{\link[grid:grid.rect]{rectGrob()}} 46 | \item lines, rays, and segments use \code{\link[graphics:segments]{segments()}} and 47 | \code{\link[grid:grid.segments]{segmentsGrob()}} 48 | \item triangles use \link[graphics:polygon]{polygon()} and 49 | \code{\link[grid:grid.polygon]{polygonGrob()}} 50 | }} 51 | 52 | \item{mapping_plane}{either \code{"x"}, \code{"y"}, \code{"z"}, or a scalar plane geometry} 53 | 54 | \item{unit}{The \link[grid:unit]{unit} the values in the geometry corresponds to.} 55 | 56 | \item{name}{The name of the created grob} 57 | 58 | \item{gp}{A \link[grid:gpar]{gpar} object giving the graphical parameters to use 59 | for rendering} 60 | 61 | \item{vp}{A \link[grid:viewport]{viewport} or \code{NULL}} 62 | 63 | \item{y}{ignored} 64 | 65 | \item{xlim, ylim}{Limits of the plot scale. If not given they will be 66 | calculated from the bounding box of the input} 67 | 68 | \item{add}{Should a new plot be created or should the rendering be added to 69 | the existing plot?} 70 | 71 | \item{axes}{Should axes be drawn?} 72 | 73 | \item{frame.plot}{Should a box be drawn around the plotting region?} 74 | } 75 | \value{ 76 | \code{euclid_plot} is called for its side effects, \code{euclid_grob} returns a 77 | \link[grid:grid.grob]{grob} 78 | } 79 | \description{ 80 | euclid provide interfaces for both base and grid graphics that allows you to 81 | visualise the geometries you are working with. There is only functionality 82 | for 2D geometries so 3D geometries will be mapped to the plane given by the 83 | \code{mapping_plane} argument. The \code{plot} method for geometries will behave like 84 | the base \code{\link[=plot]{plot()}} method and set up a new plotting window based on the 85 | given settings. \code{euclid_plot} will add to the existing plot and thus use the 86 | coordinate system currently in effect. \code{euclid_grob} will create a grob that 87 | can be rendered with \code{\link[grid:grid.draw]{grid.draw()}}. 88 | } 89 | \examples{ 90 | # Example visualisation of radical points and lines 91 | c1 <- circle(point(3, 6), 12) 92 | c2 <- circle(point(-5, 0), 3) 93 | c3 <- circle(point(-3, 7), 1) 94 | 95 | plot(c(c1, c2, c3), bg = "grey", fg = NA) 96 | euclid_plot(c( 97 | radical(c1, c2), 98 | radical(c2, c3), 99 | radical(c1, c3) 100 | ), col = "firebrick") 101 | euclid_plot(radical(c1, c2, c3), pch = 16, cex = 2, col = "steelblue") 102 | 103 | } 104 | -------------------------------------------------------------------------------- /R/iso_cube.R: -------------------------------------------------------------------------------- 1 | #' Vector of iso cubes 2 | #' 3 | #' Iso cubes are axis-aligned cuboids, i.e. a cube with faces parallel to the x, 4 | #' y, and z plane. Iso cuboids are the 3 dimensional version of 5 | #' [iso rectangles][iso_rect] and shares the feature that it can be constructed 6 | #' from bounding boxes (in 3D). An Iso cube is [degenerate][is_degenerate] all 7 | #' its vertices are coplanar. 8 | #' 9 | #' @param ... Various input. See the Constructor section. 10 | #' @param x A vector of iso cubes or an object to convert to it 11 | #' 12 | #' @return An `euclid_iso_cube` vector 13 | #' 14 | #' @section Constructors: 15 | #' **3 dimensional iso cubes** 16 | #' - Providing a bbox will create iso cubes matching the bbox 17 | #' - Providing two points will create iso cubes with the points as diagonal 18 | #' opposite vertices 19 | #' - Providing 6 numeric will construct iso cubes with the given extent, 20 | #' with the numerics being interpreted in the following order: minimum x, 21 | #' minimum y, minimum z, maximum x, maximum y, and maximum z 22 | #' 23 | #' @export 24 | #' 25 | #' @family Geometries 26 | #' @family Volumes 27 | #' 28 | #' @examples 29 | #' # Construction 30 | #' p <- point(sample(10, 2), sample(10, 2), sample(10, 2)) 31 | #' iso_cube(p[1], p[2]) 32 | #' 33 | #' iso_cube(4, 10, 7, 16, -4, 0) 34 | #' 35 | #' s <- sphere(point(5, 9, 2), 13) 36 | #' iso_cube(bbox(s)) 37 | #' 38 | iso_cube <- function(...) { 39 | inputs <- validate_constructor_input(...) 40 | 41 | if (length(inputs) == 0) { 42 | return(new_iso_cube_empty()) 43 | } 44 | 45 | bboxes <- inputs[vapply(inputs, is_bbox, logical(1))] 46 | numbers <- inputs[vapply(inputs, is_exact_numeric, logical(1))] 47 | points <- inputs[vapply(inputs, is_point, logical(1))] 48 | 49 | if (length(bboxes) == 1) { 50 | new_iso_cube_from_bbox(bboxes[[1]]) 51 | } else if (length(points) == 2) { 52 | new_iso_cube_from_2_points(points[[1]], points[[2]]) 53 | } else if (length(numbers) == 6) { 54 | new_iso_cube_from_6_numbers(numbers[[1]], numbers[[2]], numbers[[3]], numbers[[4]], numbers[[5]], numbers[[6]]) 55 | } else { 56 | cli_abort("Can't construct a {.cls euclid_iso_cube} vector from the given input") 57 | } 58 | } 59 | #' @rdname iso_cube 60 | #' @export 61 | is_iso_cube <- function(x) inherits(x, "euclid_iso_cube") 62 | 63 | 64 | # Conversion -------------------------------------------------------------- 65 | 66 | #' @rdname iso_cube 67 | #' @export 68 | as_iso_cube <- function(x) { 69 | UseMethod("as_iso_cube") 70 | } 71 | #' @export 72 | as_iso_cube.default <- function(x) { 73 | cli_abort("Can't convert the input to a {.cls euclid_iso_cube} vector") 74 | } 75 | #' @export 76 | as_iso_cube.euclid_iso_cube <- function(x) x 77 | 78 | # Misc -------------------------------------------------------------------- 79 | 80 | #' @export 81 | seq.euclid_iso_cube <- function(from, to, length.out = NULL, along.with = NULL, ...) { 82 | iso_cube( 83 | seq(vert(from, 1), vert(to, 1), length.out, along.with), 84 | seq(vert(from, 8), vert(to, 8), length.out, along.with) 85 | ) 86 | } 87 | 88 | # Internal Constructors --------------------------------------------------- 89 | 90 | new_iso_cube_empty <- function() { 91 | new_geometry_vector(create_iso_cube_empty()) 92 | } 93 | new_iso_cube_from_bbox <- function(bbox) { 94 | new_geometry_vector(create_iso_cube_bbox(get_ptr(bbox))) 95 | } 96 | new_iso_cube_from_2_points <- function(p, q) { 97 | new_geometry_vector(create_iso_cube_pq(get_ptr(p), get_ptr(q))) 98 | } 99 | new_iso_cube_from_6_numbers <- function(x0, y0, z0, x1, y1, z1) { 100 | new_geometry_vector(create_iso_cube_minmax(get_ptr(x0), get_ptr(y0), get_ptr(z0), get_ptr(x1), get_ptr(y1), get_ptr(z1))) 101 | } 102 | -------------------------------------------------------------------------------- /src/sphere.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "cgal_types.h" 6 | #include "geometry_vector.h" 7 | #include "exact_numeric.h" 8 | #include "circle.h" 9 | #include "intersection.h" 10 | #include "distance.h" 11 | 12 | class sphere : public geometry_vector { 13 | public: 14 | using geometry_vector::geometry_vector; 15 | ~sphere() = default; 16 | 17 | Primitive geometry_type() const { return SPHERE; } 18 | 19 | cpp11::writable::strings def_names() const { 20 | return {"x", "y", "z", "r2"}; 21 | } 22 | 23 | Exact_number get_single_definition(size_t i, int which, int element) const { 24 | switch(which) { 25 | case 0: return _storage[i].center().x(); 26 | case 1: return _storage[i].center().y(); 27 | case 2: return _storage[i].center().z(); 28 | case 3: return _storage[i].squared_radius(); 29 | } 30 | return _storage[i].center().x(); 31 | } 32 | 33 | std::vector get_row(size_t i, size_t j) const { 34 | return { 35 | CGAL::to_double(_storage[i].center().x().exact()), 36 | CGAL::to_double(_storage[i].center().y().exact()), 37 | CGAL::to_double(_storage[i].center().z().exact()), 38 | CGAL::to_double(_storage[i].squared_radius().exact()) 39 | }; 40 | } 41 | 42 | cpp11::writable::list intersection(const geometry_vector_base& other) const { 43 | if (other.dimensions() != dimensions()) { 44 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 45 | } 46 | switch (other.geometry_type()) { 47 | case PLANE: return intersection_impl(get_vector_of_geo(other), _storage); 48 | case POINT: return intersection_impl(get_vector_of_geo(other), _storage); 49 | case SPHERE: return intersection_impl(_storage, get_vector_of_geo(other)); 50 | default: cpp11::stop("Don't know how to calculate the intersection of these geometries"); 51 | } 52 | } 53 | 54 | cpp11::writable::logicals do_intersect(const geometry_vector_base& other) const { 55 | if (other.dimensions() != dimensions()) { 56 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 57 | } 58 | switch (other.geometry_type()) { 59 | case ISOCUBE: return do_intersect_impl(get_vector_of_geo(other), _storage); 60 | case LINE: return do_intersect_impl(get_vector_of_geo(other), _storage); 61 | case PLANE: return do_intersect_impl(get_vector_of_geo(other), _storage); 62 | case POINT: return do_intersect_impl(get_vector_of_geo(other), _storage); 63 | case RAY: return do_intersect_impl(get_vector_of_geo(other), _storage); 64 | case SEGMENT: return do_intersect_impl(get_vector_of_geo(other), _storage); 65 | case SPHERE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 66 | case TETRAHEDRON: return do_intersect_impl(_storage, get_vector_of_geo(other)); 67 | case TRIANGLE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 68 | default: return unknown_intersect_impl(std::max(size(), other.size())); 69 | } 70 | } 71 | 72 | std::vector squared_distance(const geometry_vector_base& other) const { 73 | if (other.dimensions() != dimensions()) { 74 | cpp11::stop("Only geometries of the same dimensionality allowed"); 75 | } 76 | return unknown_squared_distance_impl(std::max(size(), other.size())); 77 | } 78 | 79 | cpp11::writable::doubles_matrix<> distance_matrix(const geometry_vector_base& other) const { 80 | if (other.dimensions() != dimensions()) { 81 | cpp11::stop("Only geometries of the same dimensionality allowed"); 82 | } 83 | return unknown_distance_matrix_impl(size(), other.size()); 84 | } 85 | }; 86 | 87 | typedef cpp11::external_pointer sphere_p; 88 | -------------------------------------------------------------------------------- /src/iso_rect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "cgal_types.h" 6 | #include "geometry_vector.h" 7 | #include "exact_numeric.h" 8 | #include "intersection.h" 9 | #include "distance.h" 10 | 11 | class iso_rect : public geometry_vector { 12 | public: 13 | using geometry_vector::geometry_vector; 14 | ~iso_rect() = default; 15 | 16 | Primitive geometry_type() const { return ISORECT; } 17 | 18 | size_t cardinality(size_t i) const { return 4; } 19 | size_t n_edges(size_t i) const { return 4; } 20 | size_t long_length() const { return size() * 4; } 21 | 22 | cpp11::writable::strings def_names() const { 23 | return {"x", "y"}; 24 | } 25 | 26 | Exact_number get_single_definition(size_t i, int which, int element) const { 27 | switch(which) { 28 | case 0: return _storage[i].vertex(element).x(); 29 | case 1: return _storage[i].vertex(element).y(); 30 | } 31 | return _storage[i].vertex(0).x(); 32 | } 33 | 34 | std::vector get_row(size_t i, size_t j) const { 35 | return { 36 | CGAL::to_double(_storage[i].vertex(j).x().exact()), 37 | CGAL::to_double(_storage[i].vertex(j).y().exact()) 38 | }; 39 | } 40 | 41 | cpp11::writable::list intersection(const geometry_vector_base& other) const { 42 | if (other.dimensions() != dimensions()) { 43 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 44 | } 45 | switch (other.geometry_type()) { 46 | case ISORECT: return intersection_impl(_storage, get_vector_of_geo(other)); 47 | case LINE: return intersection_impl(_storage, get_vector_of_geo(other)); 48 | case POINT: return intersection_impl(_storage, get_vector_of_geo(other)); 49 | case RAY: return intersection_impl(_storage, get_vector_of_geo(other)); 50 | case SEGMENT: return intersection_impl(_storage, get_vector_of_geo(other)); 51 | case TRIANGLE: return intersection_impl(_storage, get_vector_of_geo(other)); 52 | default: cpp11::stop("Don't know how to calculate the intersection of these geometries"); 53 | } 54 | } 55 | 56 | cpp11::writable::logicals do_intersect(const geometry_vector_base& other) const { 57 | if (other.dimensions() != dimensions()) { 58 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 59 | } 60 | switch (other.geometry_type()) { 61 | case CIRCLE: return do_intersect_impl(get_vector_of_geo(other), _storage); 62 | case ISORECT: return do_intersect_impl(_storage, get_vector_of_geo(other)); 63 | case LINE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 64 | case POINT: return do_intersect_impl(_storage, get_vector_of_geo(other)); 65 | case RAY: return do_intersect_impl(_storage, get_vector_of_geo(other)); 66 | case SEGMENT: return do_intersect_impl(_storage, get_vector_of_geo(other)); 67 | case TRIANGLE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 68 | default: return unknown_intersect_impl(std::max(size(), other.size())); 69 | } 70 | } 71 | 72 | std::vector squared_distance(const geometry_vector_base& other) const { 73 | if (other.dimensions() != dimensions()) { 74 | cpp11::stop("Only geometries of the same dimensionality allowed"); 75 | } 76 | return unknown_squared_distance_impl(std::max(size(), other.size())); 77 | } 78 | 79 | cpp11::writable::doubles_matrix<> distance_matrix(const geometry_vector_base& other) const { 80 | if (other.dimensions() != dimensions()) { 81 | cpp11::stop("Only geometries of the same dimensionality allowed"); 82 | } 83 | return unknown_distance_matrix_impl(size(), other.size()); 84 | } 85 | }; 86 | 87 | typedef cpp11::external_pointer iso_rect_p; 88 | -------------------------------------------------------------------------------- /R/iso_rect.R: -------------------------------------------------------------------------------- 1 | #' Vector of iso rectangles 2 | #' 3 | #' Iso rectangles are axis aligned rectangles mimicking bounding boxes but given 4 | #' by exact numerics (bounding boxes are defined by floats because not all 5 | #' geometries have exact defined extent). Internally they are stored as two 6 | #' points giving the bottom-left and top-right corner of the rectangle. Iso 7 | #' rectangles are considered [degenerate][is_degenerate] all its vertices are 8 | #' colinear. 9 | #' 10 | #' @param ... Various input. See the Constructor section. 11 | #' @param x A vector of iso rectangles or an object to convert to it 12 | #' 13 | #' @return An `euclid_iso_rect` vector 14 | #' 15 | #' @section Constructors: 16 | #' **2 dimensional iso rectangles** 17 | #' - Providing a bbox will create iso rectangles matching the bbox 18 | #' - Providing two points will create iso rectangles with the points as diagonal 19 | #' opposite vertices 20 | #' - Providing 4 numeric will construct iso rectangles with the given extent, 21 | #' with the numerics being interpreted in the following order: left, bottom, 22 | #' right, top 23 | #' 24 | #' @export 25 | #' 26 | #' @family Geometries 27 | #' @family Surfaces 28 | #' 29 | #' @examples 30 | #' # Construction 31 | #' p <- point(sample(10, 2), sample(10, 2)) 32 | #' r <- iso_rect(p[1], p[2]) 33 | #' r 34 | #' 35 | #' plot(r) 36 | #' euclid_plot(p, pch = 16) 37 | #' 38 | #' r <- iso_rect(4, 10, 7, 16) 39 | #' plot(r, bg = "grey", fg = NA) 40 | #' abline(v = 4, lty = 2) 41 | #' abline(v = 7, lty = 2) 42 | #' abline(h = 10, lty = 2) 43 | #' abline(h = 16, lty = 2) 44 | #' 45 | #' circ <- circle(point(5, 9), 13) 46 | #' iso_rect(bbox(circ)) 47 | #' 48 | iso_rect <- function(...) { 49 | inputs <- validate_constructor_input(...) 50 | 51 | if (length(inputs) == 0) { 52 | return(new_iso_rect_empty()) 53 | } 54 | 55 | bboxes <- inputs[vapply(inputs, is_bbox, logical(1))] 56 | numbers <- inputs[vapply(inputs, is_exact_numeric, logical(1))] 57 | points <- inputs[vapply(inputs, is_point, logical(1))] 58 | 59 | if (length(bboxes) == 1) { 60 | new_iso_rect_from_bbox(bboxes[[1]]) 61 | } else if (length(points) == 2) { 62 | new_iso_rect_from_2_points(points[[1]], points[[2]]) 63 | } else if (length(numbers) == 4) { 64 | new_iso_rect_from_4_numbers(numbers[[1]], numbers[[2]], numbers[[3]], numbers[[4]]) 65 | } else { 66 | cli_abort("Can't construct a {.cls euclid_iso_rect} vector from the given input") 67 | } 68 | } 69 | #' @rdname iso_rect 70 | #' @export 71 | is_iso_rect <- function(x) inherits(x, "euclid_iso_rect") 72 | 73 | 74 | # Conversion -------------------------------------------------------------- 75 | 76 | #' @rdname iso_rect 77 | #' @export 78 | as_iso_rect <- function(x) { 79 | UseMethod("as_iso_rect") 80 | } 81 | #' @export 82 | as_iso_rect.default <- function(x) { 83 | cli_abort("Can't convert the input to a {.cls euclid_iso_rect} vector") 84 | } 85 | #' @export 86 | as_iso_rect.euclid_iso_rect <- function(x) x 87 | 88 | # Misc -------------------------------------------------------------------- 89 | 90 | #' @export 91 | seq.euclid_iso_rect <- function(from, to, length.out = NULL, along.with = NULL, ...) { 92 | iso_rect( 93 | seq(vert(from, 1), vert(to, 1), length.out, along.with), 94 | seq(vert(from, 3), vert(to, 3), length.out, along.with) 95 | ) 96 | } 97 | 98 | # Internal Constructors --------------------------------------------------- 99 | 100 | new_iso_rect_empty <- function() { 101 | new_geometry_vector(create_iso_rect_empty()) 102 | } 103 | new_iso_rect_from_bbox <- function(bbox) { 104 | new_geometry_vector(create_iso_rect_bbox(get_ptr(bbox))) 105 | } 106 | new_iso_rect_from_2_points <- function(p, q) { 107 | new_geometry_vector(create_iso_rect_pq(get_ptr(p), get_ptr(q))) 108 | } 109 | new_iso_rect_from_4_numbers <- function(l, b, r, t) { 110 | new_geometry_vector(create_iso_rect_minmax(get_ptr(l), get_ptr(b), get_ptr(r), get_ptr(t))) 111 | } 112 | -------------------------------------------------------------------------------- /src/iso_cube.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "cgal_types.h" 6 | #include "geometry_vector.h" 7 | #include "exact_numeric.h" 8 | #include "intersection.h" 9 | #include "distance.h" 10 | 11 | class iso_cube : public geometry_vector { 12 | public: 13 | using geometry_vector::geometry_vector; 14 | ~iso_cube() = default; 15 | 16 | Primitive geometry_type() const { return ISOCUBE; } 17 | size_t cardinality(size_t i) const { return 8; } 18 | size_t n_edges(size_t i) const { return 12; } 19 | size_t long_length() const { return size() * 8; } 20 | 21 | cpp11::writable::strings def_names() const { 22 | return {"x", "y", "z"}; 23 | } 24 | 25 | Exact_number get_single_definition(size_t i, int which, int element) const { 26 | switch(which) { 27 | case 0: return _storage[i].vertex(element).x(); 28 | case 1: return _storage[i].vertex(element).y(); 29 | case 2: return _storage[i].vertex(element).z(); 30 | } 31 | return _storage[i].vertex(0).x(); 32 | } 33 | 34 | std::vector get_row(size_t i, size_t j) const { 35 | return { 36 | CGAL::to_double(_storage[i].vertex(j).x().exact()), 37 | CGAL::to_double(_storage[i].vertex(j).y().exact()), 38 | CGAL::to_double(_storage[i].vertex(j).z().exact()) 39 | }; 40 | } 41 | 42 | cpp11::writable::list intersection(const geometry_vector_base& other) const { 43 | if (other.dimensions() != dimensions()) { 44 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 45 | } 46 | switch (other.geometry_type()) { 47 | case ISOCUBE: return intersection_impl(_storage, get_vector_of_geo(other)); 48 | case LINE: return intersection_impl(_storage, get_vector_of_geo(other)); 49 | case PLANE: return intersection_impl(_storage, get_vector_of_geo(other)); 50 | case POINT: return intersection_impl(_storage, get_vector_of_geo(other)); 51 | case RAY: return intersection_impl(_storage, get_vector_of_geo(other)); 52 | case SEGMENT: return intersection_impl(_storage, get_vector_of_geo(other)); 53 | case TRIANGLE: return intersection_impl(_storage, get_vector_of_geo(other)); 54 | default: cpp11::stop("Don't know how to calculate the intersection of these geometries"); 55 | } 56 | } 57 | 58 | cpp11::writable::logicals do_intersect(const geometry_vector_base& other) const { 59 | if (other.dimensions() != dimensions()) { 60 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 61 | } 62 | switch (other.geometry_type()) { 63 | case ISOCUBE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 64 | case LINE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 65 | case PLANE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 66 | case POINT: return do_intersect_impl(_storage, get_vector_of_geo(other)); 67 | case RAY: return do_intersect_impl(_storage, get_vector_of_geo(other)); 68 | case SEGMENT: return do_intersect_impl(_storage, get_vector_of_geo(other)); 69 | case TRIANGLE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 70 | default: return unknown_intersect_impl(std::max(size(), other.size())); 71 | } 72 | } 73 | 74 | std::vector squared_distance(const geometry_vector_base& other) const { 75 | if (other.dimensions() != dimensions()) { 76 | cpp11::stop("Only geometries of the same dimensionality allowed"); 77 | } 78 | return unknown_squared_distance_impl(std::max(size(), other.size())); 79 | } 80 | 81 | cpp11::writable::doubles_matrix<> distance_matrix(const geometry_vector_base& other) const { 82 | if (other.dimensions() != dimensions()) { 83 | cpp11::stop("Only geometries of the same dimensionality allowed"); 84 | } 85 | return unknown_distance_matrix_impl(size(), other.size()); 86 | } 87 | }; 88 | 89 | typedef cpp11::external_pointer iso_cube_p; 90 | -------------------------------------------------------------------------------- /man/euclid_geometry.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geometry.R 3 | \name{euclid_geometry} 4 | \alias{euclid_geometry} 5 | \alias{is_geometry} 6 | \alias{geometry_type} 7 | \alias{cardinality} 8 | \title{Vector of geometries} 9 | \usage{ 10 | is_geometry(x) 11 | 12 | geometry_type(x) 13 | 14 | cardinality(x, ...) 15 | } 16 | \arguments{ 17 | \item{x}{An \code{euclid_geometry} object} 18 | 19 | \item{...}{Arguments passed on to methods} 20 | } 21 | \description{ 22 | The base vector type for geometries in euclid is the \code{euclid_geometry} class. 23 | While this is a virtual class (you cannot construct it, only its subclasses), 24 | many of the common vector operations are defined here. Further it is possible 25 | to check whether a vector is a geometry of any type using \code{is_geometry()}. 26 | While most geometries are atomic, a few are defined by multiples of the same 27 | geometry, e.g. segments and triangles which are defined by 2 and 3 points 28 | respectively. The cardinality of each geometry in a vector can be queried 29 | with \code{cardinality()}. The matrix conversion will place each sub-geometry of 30 | a geometry on a new row, meaning that the final number of rows in a matrix 31 | constructed from \code{x} is not \code{length(x)} but \code{sum(cardinality(x))}. 32 | } 33 | \section{Vector behaviour}{ 34 | 35 | Geometry vectors in euclid are made to behave as closely as possible to what 36 | you expect from normal R vectors. However, they are implemented as external 37 | pointers to the exact C representation meaning that they cannot be restored 38 | across sessions or saved to RData/RDS files. Despite being external pointers 39 | they mimick R's copy-on-modify semantics so you should not worry about side 40 | effects when changing a geometry vector. 41 | 42 | The following is a list of standard R methods defined for geometry 43 | vectors: 44 | \itemize{ 45 | \item \code{\link[=as.matrix]{as.matrix()}} - converts the geometry to a standard R matrix of numerics 46 | \item \code{\link[=as.character]{as.character()}} - provides a textual representation of the geometry 47 | \item \code{\link[=format]{format()}} - as above 48 | \item \code{\link[=as.list]{as.list()}} - splits the vector into single elements in a list 49 | \item \code{\link[=str]{str()}} - provides a condensed view of the vector 50 | \item \code{\link[=length]{length()}} - gives the number of geometries in the vector 51 | \item \code{\link[=rep]{rep()}} - replicates elements in the vector 52 | \item \code{\link[=seq]{seq()}} - interpolate between two geometries of the same type and 53 | dimensionality 54 | \item \code{\link[=dim]{dim()}} - gives the dimensionality of the geometry (2 or 3) 55 | \item [\code{[}] and [\code{[[}] - extract elements from the vector 56 | \item [\verb{[<-}] and [\verb{[[<-}] - assigns elements into the vector. Since \code{NA} values 57 | are not supported it is not possible to assign past the length of the 58 | vector 59 | \item \code{\link{$}} and \code{\link{$<-}} - will throw an error since geometry vectors are unnamed 60 | \item \code{\link[=c]{c()}} - combine multiple geometries of the same type 61 | \item \code{\link[=unique]{unique()}} - returns the unique elements of the vector keeping the order 62 | \item \code{\link[=duplicated]{duplicated()}} - gives whether an element has been seen before in the 63 | vector 64 | \item \code{\link[=anyDuplicated]{anyDuplicated()}} - Tells if any element in the vector is a duplicate 65 | \item \code{\link{==}} and \code{\link{!=}} - Test for equality between elements. 66 | \item \code{\link[=transform]{transform()}} - Transform geometries in the vector according to an 67 | \link{affine_transformation} 68 | } 69 | 70 | Do note that since geometries with exact representation is not easily 71 | hashable, the implementation of \code{unique()} and \code{duplicated()} is not very 72 | efficient (except for points since they can be sorted). 73 | } 74 | 75 | \examples{ 76 | p <- point(sample(10, 4), sample(10, 4)) 77 | p 78 | p[2] 79 | p[3] <- point(14, 20) 80 | p 81 | p2 <- c(p, p) 82 | p2 83 | unique(p2) 84 | sort(p) 85 | rep(p, each = 2) 86 | as.list(p) 87 | transform(p, affine_rotate(pi/3)) 88 | 89 | } 90 | \seealso{ 91 | Other Geometry methods: 92 | \code{\link{def}()}, 93 | \code{\link{subgeometries}} 94 | } 95 | \concept{Geometry methods} 96 | -------------------------------------------------------------------------------- /man/circle.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/circle.R 3 | \name{circle} 4 | \alias{circle} 5 | \alias{is_circle} 6 | \alias{as_circle} 7 | \title{Vector of circles} 8 | \usage{ 9 | circle(..., default_dim = 2) 10 | 11 | is_circle(x) 12 | 13 | as_circle(x) 14 | } 15 | \arguments{ 16 | \item{...}{Various input. See the Constructor section.} 17 | 18 | \item{default_dim}{The dimensionality when constructing an empty vector} 19 | 20 | \item{x}{A circle vector or an object to convert to it} 21 | } 22 | \value{ 23 | An \code{euclid_circle} vector 24 | } 25 | \description{ 26 | A circle is defined by a center and a radius (given as the squared radius due 27 | to the inexactness of square roots). In 3 dimensions a circle is a disc and 28 | thus have an orientation given by an orthogonal direction. If the radius is 0 29 | the circle is considered \link[=is_degenerate]{degenerate}. 30 | } 31 | \section{Constructors}{ 32 | 33 | \strong{2 dimensional circles} 34 | \itemize{ 35 | \item Providing one point and one numeric vector will construct circles centered 36 | at the point with the \strong{squared} radius given by the numeric. 37 | \item Providing two points will construct circles centered between the two 38 | points with a radius of half the distance between the two points. 39 | \item Providing three point vectors will construct the unique circle that pass 40 | through the three points. 41 | } 42 | 43 | \strong{3 dimensional circles} 44 | \itemize{ 45 | \item Providing three point vectors will construct the unique circle that pass 46 | through the three points. 47 | \item Providing a point, a numeric, and a plane will construct a circle centered 48 | on the point with the squared radius given by the numeric and the 49 | orientation given by the plane. The point must lie on the plane 50 | \item Providing a point, a numeric, and a vector will construct a circle centered 51 | on the point with the squared radius given by the numeric and the 52 | orientation orthogonal to the vector 53 | \item Providing a point, a numeric, and a direction will construct a circle 54 | centered on the point with the squared radius given by the numeric and the 55 | orientation orthogonal to the direction 56 | \item Providing two spheres will construct a circle given by the intersection of 57 | the two spheres. The spheres must intersect 58 | \item Providing a sphere and a plane will construct a circle given by the 59 | intersection of the sphere and the plane. The sphere and plane must 60 | intersect. 61 | } 62 | } 63 | 64 | \examples{ 65 | ## 2 Dimensions 66 | 67 | point1 <- point(runif(5), runif(5)) 68 | point2 <- point(runif(5), runif(5)) 69 | point3 <- point(runif(5), runif(5)) 70 | number <- exact_numeric(1:5) 71 | 72 | # Construction with center and radius 73 | circ <- circle(point1, number) 74 | circ 75 | plot(circ) 76 | 77 | # integers and numerics are converted automatically 78 | circle(point1, 1:5) 79 | 80 | # You are free to name the input for readability 81 | circle(center = point1, radius = number) 82 | 83 | # Construction with 2 points 84 | circle(point1, point2) 85 | 86 | # Construction with 3 points 87 | circle(point1, point2, point3) 88 | 89 | plot(circle(point1[1], point1[2], point1[3])) 90 | euclid_plot(point1[1:3], col = "firebrick", pch = 16, cex = 3) 91 | 92 | ## 3 Dimensions 93 | 94 | point1 <- point(runif(5), runif(5), runif(5)) 95 | point2 <- point(runif(5), runif(5), runif(5)) 96 | point3 <- point(runif(5), runif(5), runif(5)) 97 | 98 | circ <- circle(point1, point2, point3) 99 | 100 | circle(point1, number, as_vec(point2)) 101 | 102 | # Conversion 103 | as_plane(circ) 104 | 105 | as_sphere(circ) 106 | 107 | } 108 | \seealso{ 109 | Other Geometries: 110 | \code{\link{direction}()}, 111 | \code{\link{iso_cube}()}, 112 | \code{\link{iso_rect}()}, 113 | \code{\link{line}()}, 114 | \code{\link{plane}()}, 115 | \code{\link{point}()}, 116 | \code{\link{ray}()}, 117 | \code{\link{segment}()}, 118 | \code{\link{sphere}()}, 119 | \code{\link{tetrahedron}()}, 120 | \code{\link{triangle}()}, 121 | \code{\link{vec}()}, 122 | \code{\link{weighted_point}()} 123 | 124 | Other Surfaces: 125 | \code{\link{iso_rect}()}, 126 | \code{\link{plane}()}, 127 | \code{\link{triangle}()} 128 | } 129 | \concept{Geometries} 130 | \concept{Surfaces} 131 | -------------------------------------------------------------------------------- /R/sphere.R: -------------------------------------------------------------------------------- 1 | #' Vector of spheres 2 | #' 3 | #' A sphere is a 3 dimensional object modelling the surface of a ball. As such 4 | #' it is an extension of the concept of a 2 dimensional circle to 3D, though a 5 | #' circle can also exist in three dimensions. A sphere has a center and a 6 | #' radius. if the radius is 0 it is considered to be [degenerate][is_degenerate]. 7 | #' 8 | #' @param ... Various input. See the Constructor section. 9 | #' @param x A vector of spheres or an object to convert to it 10 | #' 11 | #' @return An `euclid_sphere` vector 12 | #' 13 | #' @section Constructors: 14 | #' **3 dimensional spheres** 15 | #' - Providing 4 points will construct the unique sphere the passes through all 16 | #' 4 points (points must not be coplanar) 17 | #' - Providing 3 points will construct the smallest sphere that passes through 18 | #' all 3 points 19 | #' - Providing 2 points will construct the smallest sphere passing through both 20 | #' points 21 | #' - Providing a point and numeric will construct spheres centered on the point 22 | #' with a squared radius set to the numeric 23 | #' - Providing a circle will construct the diametral sphere of the circle 24 | #' 25 | #' @export 26 | #' 27 | #' @family Geometries 28 | #' @family Volumes 29 | #' 30 | #' @examples 31 | #' # Construction 32 | #' p <- point(sample(8), sample(8), sample(8)) 33 | #' sphere(p, 4) 34 | #' 35 | #' sphere(p[1:2], p[3:4], p[5:6], p[7:8]) 36 | #' 37 | #' sphere(p[1:2], p[3:4], p[5:6]) 38 | #' 39 | #' sphere(p[1:2], p[3:4]) 40 | #' 41 | #' circ <- circle(p[1], as_vec(p[2]), 6) 42 | #' sphere(circ) 43 | #' 44 | sphere <- function(...) { 45 | inputs <- validate_constructor_input(...) 46 | 47 | if (length(inputs) == 0) { 48 | return(new_sphere_empty()) 49 | } 50 | 51 | points <- inputs[vapply(inputs, is_point, logical(1))] 52 | numbers <- inputs[vapply(inputs, is_exact_numeric, logical(1))] 53 | circles <- inputs[vapply(inputs, is_circle, logical(1))] 54 | 55 | if (length(points) == 4) { 56 | new_sphere_from_4_points(points[[1]], points[[2]], points[[3]], points[[4]]) 57 | } else if (length(points) == 3) { 58 | new_sphere_from_3_points(points[[1]], points[[2]], points[[3]]) 59 | } else if (length(points) == 2) { 60 | new_sphere_from_2_points(points[[1]], points[[2]]) 61 | } else if (length(points) == 1 && length(numbers) == 1) { 62 | new_sphere_from_center_radius(points[[1]], numbers[[1]]) 63 | } else if (length(circles) == 1) { 64 | new_sphere_from_circle(circles[[1]]) 65 | } else { 66 | cli_abort("Can't construct a {.cls euclid_sphere} vector from the given input") 67 | } 68 | } 69 | #' @rdname sphere 70 | #' @export 71 | is_sphere <- function(x) inherits(x, "euclid_sphere") 72 | 73 | 74 | # Conversion -------------------------------------------------------------- 75 | 76 | #' @rdname sphere 77 | #' @export 78 | as_sphere <- function(x) { 79 | UseMethod("as_sphere") 80 | } 81 | #' @export 82 | as_sphere.default <- function(x) { 83 | cli_abort("Can't convert the input to a {.cls euclid_sphere} vector") 84 | } 85 | #' @export 86 | as_sphere.euclid_sphere <- function(x) x 87 | 88 | # Misc -------------------------------------------------------------------- 89 | 90 | #' @export 91 | seq.euclid_sphere <- function(from, to, length.out = NULL, along.with = NULL, ...) { 92 | sphere( 93 | seq(vert(from), vert(to), length.out, along.with), 94 | seq(def(from, "r2"), def(to, "r2"), length.out, along.with) 95 | ) 96 | } 97 | 98 | # Internal Constructors --------------------------------------------------- 99 | 100 | new_sphere_empty <- function() { 101 | new_geometry_vector(create_sphere_empty()) 102 | } 103 | new_sphere_from_2_points <- function(p, q) { 104 | new_geometry_vector(create_sphere_2_point(get_ptr(p), get_ptr(q))) 105 | } 106 | new_sphere_from_3_points <- function(p, q, r) { 107 | new_geometry_vector(create_sphere_3_point(get_ptr(p), get_ptr(q), get_ptr(r))) 108 | } 109 | new_sphere_from_4_points <- function(p, q, r, s) { 110 | new_geometry_vector(create_sphere_4_point(get_ptr(p), get_ptr(q), get_ptr(r), get_ptr(s))) 111 | } 112 | new_sphere_from_center_radius <- function(center, r2) { 113 | new_geometry_vector(create_sphere_center_radius(get_ptr(center), get_ptr(r2))) 114 | } 115 | new_sphere_from_circle <- function(circ) { 116 | new_geometry_vector(create_sphere_circle(get_ptr(circ))) 117 | } 118 | -------------------------------------------------------------------------------- /src/tetrahedron.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "cgal_types.h" 6 | #include "geometry_vector.h" 7 | #include "exact_numeric.h" 8 | #include "intersection.h" 9 | #include "distance.h" 10 | 11 | class tetrahedron : public geometry_vector { 12 | public: 13 | using geometry_vector::geometry_vector; 14 | ~tetrahedron() = default; 15 | 16 | Primitive geometry_type() const { return TETRAHEDRON; } 17 | 18 | size_t cardinality(size_t i) const { return 4; } 19 | size_t n_edges(size_t i) const { return 6; } 20 | size_t long_length() const { return size() * 4; } 21 | 22 | cpp11::writable::strings def_names() const { 23 | return {"x", "y", "z"}; 24 | } 25 | 26 | Exact_number get_single_definition(size_t i, int which, int element) const { 27 | switch(which) { 28 | case 0: return _storage[i].vertex(element).x(); 29 | case 1: return _storage[i].vertex(element).y(); 30 | case 2: return _storage[i].vertex(element).z(); 31 | } 32 | return _storage[i].vertex(element).x(); 33 | } 34 | 35 | std::vector get_row(size_t i, size_t j) const { 36 | return { 37 | CGAL::to_double(_storage[i].vertex(j).x().exact()), 38 | CGAL::to_double(_storage[i].vertex(j).y().exact()), 39 | CGAL::to_double(_storage[i].vertex(j).z().exact()) 40 | }; 41 | } 42 | 43 | cpp11::writable::list intersection(const geometry_vector_base& other) const { 44 | if (other.dimensions() != dimensions()) { 45 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 46 | } 47 | switch (other.geometry_type()) { 48 | case LINE: return intersection_impl(get_vector_of_geo(other), _storage); 49 | case PLANE: return intersection_impl(get_vector_of_geo(other), _storage); 50 | case POINT: return intersection_impl(get_vector_of_geo(other), _storage); 51 | case RAY: return intersection_impl(get_vector_of_geo(other), _storage); 52 | case SEGMENT: return intersection_impl(get_vector_of_geo(other), _storage); 53 | case TRIANGLE: return intersection_impl(_storage, get_vector_of_geo(other)); 54 | default: cpp11::stop("Don't know how to calculate the intersection of these geometries"); 55 | } 56 | } 57 | 58 | cpp11::writable::logicals do_intersect(const geometry_vector_base& other) const { 59 | if (other.dimensions() != dimensions()) { 60 | cpp11::stop("Only geometries of the same dimensionality can intersect"); 61 | } 62 | switch (other.geometry_type()) { 63 | case ISOCUBE: return do_intersect_impl(get_vector_of_geo(other), _storage); 64 | case LINE: return do_intersect_impl(get_vector_of_geo(other), _storage); 65 | case PLANE: return do_intersect_impl(get_vector_of_geo(other), _storage); 66 | case POINT: return do_intersect_impl(get_vector_of_geo(other), _storage); 67 | case RAY: return do_intersect_impl(get_vector_of_geo(other), _storage); 68 | case SEGMENT: return do_intersect_impl(get_vector_of_geo(other), _storage); 69 | case SPHERE: return do_intersect_impl(get_vector_of_geo(other), _storage); 70 | case TETRAHEDRON: return do_intersect_impl(_storage, get_vector_of_geo(other)); 71 | case TRIANGLE: return do_intersect_impl(_storage, get_vector_of_geo(other)); 72 | default: return unknown_intersect_impl(std::max(size(), other.size())); 73 | } 74 | } 75 | 76 | std::vector squared_distance(const geometry_vector_base& other) const { 77 | if (other.dimensions() != dimensions()) { 78 | cpp11::stop("Only geometries of the same dimensionality allowed"); 79 | } 80 | switch (other.geometry_type()) { 81 | case POINT: return squared_distance_impl(get_vector_of_geo(other), _storage); 82 | default: return unknown_squared_distance_impl(std::max(size(), other.size())); 83 | } 84 | } 85 | 86 | cpp11::writable::doubles_matrix<> distance_matrix(const geometry_vector_base& other) const { 87 | if (other.dimensions() != dimensions()) { 88 | cpp11::stop("Only geometries of the same dimensionality allowed"); 89 | } 90 | switch (other.geometry_type()) { 91 | case POINT: return distance_matrix_impl(get_vector_of_geo(other), _storage); 92 | default: return unknown_distance_matrix_impl(size(), other.size()); 93 | } 94 | } 95 | }; 96 | 97 | typedef cpp11::external_pointer tetrahedron_p; 98 | -------------------------------------------------------------------------------- /man/affine_transformation.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/transform.R 3 | \name{affine_transformation} 4 | \alias{affine_transformation} 5 | \alias{affine_identity} 6 | \alias{affine_matrix} 7 | \alias{affine_translate} 8 | \alias{affine_scale} 9 | \alias{affine_scale2} 10 | \alias{affine_shear} 11 | \alias{affine_reflect} 12 | \alias{affine_rotate} 13 | \alias{is_affine_transformation} 14 | \alias{as_affine_transformation} 15 | \alias{inverse} 16 | \alias{is_reflecting} 17 | \title{Create affine transformation matrices} 18 | \usage{ 19 | affine_identity(dim = 2L) 20 | 21 | affine_matrix(mat) 22 | 23 | affine_translate(vec) 24 | 25 | affine_scale(fac, dim = 2L) 26 | 27 | affine_scale2(x = 1, y = 1, z = NA, dim = NA) 28 | 29 | affine_shear( 30 | x = NA, 31 | y = NA, 32 | xy = NA, 33 | xz = NA, 34 | yx = NA, 35 | yz = NA, 36 | zx = NA, 37 | zy = NA 38 | ) 39 | 40 | affine_reflect(x = FALSE, y = FALSE, z = FALSE, dim = 2L) 41 | 42 | affine_rotate(rho, axis = NA, direction = NA, yaw = NA, pitch = NA, roll = NA) 43 | 44 | is_affine_transformation(x) 45 | 46 | as_affine_transformation(x) 47 | 48 | inverse(x) 49 | 50 | is_reflecting(x) 51 | } 52 | \arguments{ 53 | \item{dim}{The dimensionality of the transformation matrix} 54 | 55 | \item{mat}{An object that can be converted to an affine transformation matrix 56 | vector. Matrices and arrays can be converted provided they have the correct 57 | dimensions. List of matrices can be converted provided that all matrices have 58 | the same dimensions and that the dimensions is correct} 59 | 60 | \item{vec}{A vector of vectors or an object convertible to one} 61 | 62 | \item{fac}{A scaling factor to apply} 63 | 64 | \item{x, y, z, xy, xz, yx, yz, zx, zy}{Scaling and shearing factors for each separate 65 | dimension/plane, or flags to indicate whether to reflect on the given axis} 66 | 67 | \item{rho}{An angle in radians to rotate (counterclockwise)} 68 | 69 | \item{axis}{For 3 dimensional rotation, which axis to rotate around} 70 | 71 | \item{direction}{A direction vector or an object convertible to one} 72 | 73 | \item{yaw, pitch, roll}{Angles in radians for yaw, pitch, and roll rotation.} 74 | } 75 | \value{ 76 | An \code{euclid_affine_transformation} vector 77 | } 78 | \description{ 79 | These functions allow you to create vectors of transformation matrices for 80 | affine transformation in 2 or 3 dimensions. Transformation matrices are used 81 | to apply transformations to geometries using \code{\link[=transform]{transform()}}. Transformations 82 | can be stacked by multiplying them together. This is generally more 83 | performant than applying transformations one by one to geometries. 84 | Transformations can be reversed by applying the inverse transformation to a 85 | geometry. The inverse transformation matrix can be obtained using 86 | \code{inverse()}. Affine transformation matrices have an additional column and row 87 | compared to linear transformation matrices. This means that matrices for 2 88 | dimensional transformations are 3x3 and matrices for 2 dimensional 89 | transformations are 4x4. In both cases the last row will consist of 0's and a 90 | final scaling factor (usually 1). Rotation is generally not possible to do 91 | while maintaining exactness as sine and cosine cannot be calculate to 92 | exactness. 3 dimensional rotation can either be done around an axis, around 93 | a direction, or be defining specific angles of rotation for yaw, pitch, and 94 | roll. 95 | } 96 | \note{ 97 | Circles and spheres only transforms correctly with euclidean 98 | transformations (i.e. translation, rotations, and reflection) as well as 99 | scaling by the same factor along all dimensions (as provided by 100 | \code{affine_scaling()}). Shearing and stretching/squeezing will only affect the 101 | location of the center, not the circularity of the geometry. 102 | } 103 | \examples{ 104 | # Rotate triangle around its centroid and then around the center 105 | p <- point(sample(10, 3), sample(10, 3)) 106 | t <- triangle(p[1], p[2], p[3]) 107 | 108 | ct <- centroid(t) 109 | # Assemble transformation (remember reverse order) 110 | trans <- affine_rotate(pi/4) * 111 | affine_translate(vec(ct)) * 112 | affine_rotate(2*pi/5) * 113 | affine_translate(-vec(ct)) 114 | 115 | t2 <- transform(t, trans) 116 | 117 | plot(c(t, t2), col = c("grey", "firebrick"), border = NA) 118 | 119 | } 120 | \seealso{ 121 | Other non-geometry vectors: 122 | \code{\link{bbox}()}, 123 | \code{\link{exact_numeric}()} 124 | } 125 | \concept{non-geometry vectors} 126 | -------------------------------------------------------------------------------- /src/intersection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cgal_types.h" 4 | #include "geometry_vector.h" 5 | #include "is_degenerate.h" 6 | #include 7 | #include 8 | #include 9 | 10 | struct Intersection_visitor { 11 | typedef geometry_vector_base_p result_type; 12 | 13 | result_type operator()(const Kernel::Circle_3& i) const { 14 | return create_scalar_geometry(Circle_3(i)); 15 | } 16 | result_type operator()(const Kernel::Iso_rectangle_2& i) const { 17 | return create_scalar_geometry(Iso_rectangle(i)); 18 | } 19 | result_type operator()(const Kernel::Iso_cuboid_3& i) const { 20 | return create_scalar_geometry(Iso_cuboid(i)); 21 | } 22 | result_type operator()(const Kernel::Line_2& i) const { 23 | return create_scalar_geometry(Line_2(i)); 24 | } 25 | result_type operator()(const Kernel::Line_3& i) const { 26 | return create_scalar_geometry(Line_3(i)); 27 | } 28 | result_type operator()(const Kernel::Plane_3& i) const { 29 | return create_scalar_geometry(Plane(i)); 30 | } 31 | result_type operator()(const Kernel::Point_2& i) const { 32 | return create_scalar_geometry(Point_2(i)); 33 | } 34 | result_type operator()(const Kernel::Point_3& i) const { 35 | return create_scalar_geometry(Point_3(i)); 36 | } 37 | result_type operator()(const std::vector& i) const { 38 | std::vector points; 39 | for (size_t j = 0; j < i.size(); ++j) points.emplace_back(i[j]); 40 | return create_geometry_vector(points); 41 | } 42 | result_type operator()(const std::vector& i) const { 43 | std::vector points; 44 | for (size_t j = 0; j < i.size(); ++j) points.emplace_back(i[j]); 45 | return create_geometry_vector(points); 46 | } 47 | result_type operator()(const Kernel::Ray_2& i) const { 48 | return create_scalar_geometry(Ray_2(i)); 49 | } 50 | result_type operator()(const Kernel::Ray_3& i) const { 51 | return create_scalar_geometry(Ray_3(i)); 52 | } 53 | result_type operator()(const Kernel::Segment_2& i) const { 54 | return create_scalar_geometry(Segment_2(i)); 55 | } 56 | result_type operator()(const Kernel::Segment_3& i) const { 57 | return create_scalar_geometry(Segment_3(i)); 58 | } 59 | result_type operator()(const Kernel::Sphere_3& i) const { 60 | return create_scalar_geometry(Sphere(i)); 61 | } 62 | result_type operator()(const Kernel::Triangle_2& i) const { 63 | return create_scalar_geometry(Triangle_2(i)); 64 | } 65 | result_type operator()(const Kernel::Triangle_3& i) const { 66 | return create_scalar_geometry(Triangle_3(i)); 67 | } 68 | }; 69 | 70 | template 71 | inline cpp11::writable::list intersection_impl(const std::vector& geo1, const std::vector& geo2) { 72 | if (geo1.size() == 0 || geo2.size() == 0) { 73 | return {}; 74 | } 75 | size_t output_size = std::max(geo1.size(), geo2.size()); 76 | cpp11::writable::list result; 77 | result.reserve(output_size); 78 | for (size_t i = 0; i < output_size; ++i) { 79 | if (invalid_geo(geo1[i % geo1.size()]) || invalid_geo(geo2[i % geo2.size()])) { 80 | result.push_back(R_NilValue); 81 | continue; 82 | } 83 | auto overlap = CGAL::intersection(geo1[i % geo1.size()], geo2[i % geo2.size()]); 84 | if (overlap) { 85 | result.push_back(boost::apply_visitor(Intersection_visitor(), *overlap)); 86 | } else { 87 | result.push_back(R_NilValue); 88 | } 89 | } 90 | return result; 91 | } 92 | 93 | template 94 | inline cpp11::writable::logicals do_intersect_impl(const std::vector& geo1, const std::vector& geo2) { 95 | if (geo1.size() == 0 || geo2.size() == 0) { 96 | return {}; 97 | } 98 | size_t output_size = std::max(geo1.size(), geo2.size()); 99 | cpp11::writable::logicals result; 100 | result.reserve(output_size); 101 | for (size_t i = 0; i < output_size; ++i) { 102 | if (invalid_geo(geo1[i % geo1.size()]) || invalid_geo(geo2[i % geo2.size()])) { 103 | result.push_back(NA_LOGICAL); 104 | continue; 105 | } 106 | bool overlap = CGAL::do_intersect(geo1[i % geo1.size()], geo2[i % geo2.size()]); 107 | result.push_back((Rboolean) overlap); 108 | } 109 | return result; 110 | } 111 | 112 | inline cpp11::writable::logicals unknown_intersect_impl(size_t size) { 113 | cpp11::writable::logicals res(size); 114 | for (size_t i = 0; i < size; ++i) { 115 | res[i] = NA_LOGICAL; 116 | } 117 | return res; 118 | } 119 | -------------------------------------------------------------------------------- /R/segment.R: -------------------------------------------------------------------------------- 1 | #' Vector of segments 2 | #' 3 | #' A segment is a finite directed line going from one point to another. If the 4 | #' two points are equal it is considered to be [degenerate][is_degenerate]. A 5 | #' segment can be flipped be taking its negative. 6 | #' 7 | #' @param ... Various input. See the Constructor section. 8 | #' @param default_dim The dimensionality when constructing an empty vector 9 | #' @param x A vector of segments or an object to convert to it 10 | #' 11 | #' @return An `euclid_segment` vector 12 | #' 13 | #' @section Constructors: 14 | #' **2 and 3 dimensional segments** 15 | #' - Providing two points will construct segments starting at the first point 16 | #' and ending at the second 17 | #' - Providing a point and a vector will construct segments starting at the 18 | #' point and ending at the point defined by the start point plus the vector 19 | #' 20 | #' @export 21 | #' 22 | #' @family Geometries 23 | #' @family Curves 24 | #' 25 | #' @examples 26 | #' # Construction 27 | #' p <- point(sample(4), sample(4)) 28 | #' s <- segment(p[1:2], p[3:4]) 29 | #' s 30 | #' 31 | #' plot(s) 32 | #' 33 | #' segment(p[1:2], as_vec(p[3:4])) 34 | #' 35 | #' # Flip segments 36 | #' -s 37 | #' 38 | #' # Segments can be converted to vectors, directions and lines 39 | #' as_vec(s) 40 | #' 41 | #' as_direction(s) 42 | #' 43 | #' as_line(s) 44 | #' plot(s) 45 | #' euclid_plot(as_line(s), lty = 2) 46 | #' 47 | segment <- function(..., default_dim = 2) { 48 | inputs <- validate_constructor_input(...) 49 | 50 | if (length(inputs) == 0) { 51 | return(new_segment_empty(default_dim)) 52 | } 53 | 54 | points <- inputs[vapply(inputs, is_point, logical(1))] 55 | vectors <- inputs[vapply(inputs, is_vec, logical(1))] 56 | 57 | if (length(points) == 2) { 58 | new_segment_from_2_points(points[[1]], points[[2]]) 59 | } else if (length(points) == 1 && length(vectors) == 1) { 60 | new_segment_from_point_vector(points[[1]], vectors[[1]]) 61 | } else { 62 | abort("Can't construct a {.cls euclid_segment} vector from the given input") 63 | } 64 | } 65 | #' @rdname segment 66 | #' @export 67 | is_segment <- function(x) inherits(x, "euclid_segment") 68 | 69 | # Conversion -------------------------------------------------------------- 70 | 71 | #' @rdname segment 72 | #' @export 73 | as_segment <- function(x) { 74 | UseMethod("as_segment") 75 | } 76 | #' @export 77 | as_segment.default <- function(x) { 78 | abort("Can't convert the input to a {.cls euclid_segment} vector") 79 | } 80 | #' @export 81 | as_segment.euclid_segment <- function(x) x 82 | 83 | #' @export 84 | as_vec.euclid_segment <- function(x) { 85 | vec(x) 86 | } 87 | #' @export 88 | as_direction.euclid_segment <- function(x) { 89 | direction(x) 90 | } 91 | #' @export 92 | as_line.euclid_segment <- function(x) { 93 | line(x) 94 | } 95 | 96 | # Operators --------------------------------------------------------------- 97 | 98 | #' @export 99 | geometry_op_minus.euclid_segment <- function(e1, e2) { 100 | if (!missing(e2)) { 101 | cli_abort("Segments cannot be subtracted, only negated") 102 | } 103 | if (dim(e1) == 2) { 104 | restore_euclid_vector(segment_2_negate(get_ptr(e1)), e1) 105 | } else { 106 | restore_euclid_vector(segment_3_negate(get_ptr(e1)), e1) 107 | } 108 | } 109 | 110 | # Misc -------------------------------------------------------------------- 111 | 112 | #' @export 113 | seq.euclid_segment <- function(from, to, length.out = NULL, along.with = NULL, ...) { 114 | if (dim(from) != dim(to)) { 115 | cli_abort("{.arg from} and {.arg to} must have the same number of dimensions") 116 | } 117 | segment( 118 | seq(vert(from, 1), vert(to, 1), length.out, along.with), 119 | seq(vert(from, 2), vert(to, 2), length.out, along.with) 120 | ) 121 | } 122 | 123 | # Internal Constructors --------------------------------------------------- 124 | 125 | new_segment_empty <- function(dim) { 126 | if (dim == 2) { 127 | new_geometry_vector(create_segment_2_empty()) 128 | } else { 129 | new_geometry_vector(create_segment_3_empty()) 130 | } 131 | } 132 | new_segment_from_2_points <- function(p, q) { 133 | if (dim(p) == 2) { 134 | new_geometry_vector(create_segment_2_p_q(get_ptr(p), get_ptr(q))) 135 | } else { 136 | new_geometry_vector(create_segment_3_p_q(get_ptr(p), get_ptr(q))) 137 | } 138 | } 139 | new_segment_from_point_vector <- function(p, v) { 140 | if (dim(p) == 2) { 141 | new_geometry_vector(create_segment_2_p_v(get_ptr(p), get_ptr(v))) 142 | } else { 143 | new_geometry_vector(create_segment_3_p_v(get_ptr(p), get_ptr(v))) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /R/point_w.R: -------------------------------------------------------------------------------- 1 | #' Vector of weighted points 2 | #' 3 | #' This class is an extension (but not a subclass of) [point()]. It simply 4 | #' attaches a numeric weight to the point that is carried along with it. 5 | #' However, most of the functionality of points are lost with this vector class 6 | #' as all the a math and sorting operations are gone due to ambiguity on whether 7 | #' to operate on the point or the weight. Weighted points can easily be 8 | #' converted to points however. 9 | #' 10 | #' @param ... Various input. See the Constructor section. 11 | #' @param default_dim The dimensionality when constructing an empty vector 12 | #' @param x A point vector or an object to convert to it 13 | #' 14 | #' @return a `euclid_point_w` vector 15 | #' 16 | #' @section Constructors: 17 | #' **2 dimensional weighted points** 18 | #' - Providing three numeric vector will construct points with those x and y 19 | #' coordinate and a weight. 20 | #' - Providing a point and a numeric will construct points with the given weight 21 | #' 22 | #' **3 dimensional weighted points** 23 | #' - Providing four numeric vector will construct points with those x, y and z 24 | #' coordinate and a weight. 25 | #' - Providing a point and a numeric will construct points with the given weight 26 | #' 27 | #' @export 28 | #' 29 | #' @family Geometries 30 | #' @family Locations 31 | #' 32 | #' @examples 33 | #' # Construction 34 | #' wp <- weighted_point(sample(5), sample(5), runif(5)) 35 | #' wp 36 | #' 37 | #' # Convert to point 38 | #' p <- as_point(wp) 39 | #' p 40 | #' 41 | #' # Construct from point and weight 42 | #' 43 | #' weighted_point(p, 1:5) 44 | #' 45 | weighted_point <- function(..., default_dim = 2) { 46 | inputs <- validate_constructor_input(...) 47 | 48 | if (length(inputs) == 0) { 49 | return(new_point_w_empty(default_dim)) 50 | } 51 | 52 | points <- inputs[vapply(inputs, is_point, logical(1))] 53 | numbers <- inputs[vapply(inputs, is_exact_numeric, logical(1))] 54 | 55 | if (length(points) == 1 && length(numbers) > 0) { 56 | new_point_w_from_pw(points[[1]], numbers[[1]]) 57 | } else if (length(numbers) == 3) { 58 | new_point_w_from_xyw(numbers[[1]], numbers[[2]], numbers[[3]]) 59 | } else if (length(numbers) == 4) { 60 | new_point_w_from_xyzw(numbers[[1]], numbers[[2]], numbers[[3]], numbers[[4]]) 61 | } else { 62 | cli_abort("Can't construct a {.cls euclid_point_w} vector from the given input") 63 | } 64 | } 65 | #' @rdname weighted_point 66 | #' @export 67 | is_weighted_point <- function(x) inherits(x, "euclid_point_w") 68 | 69 | # Conversion -------------------------------------------------------------- 70 | 71 | #' @rdname weighted_point 72 | #' @export 73 | as_weighted_point <- function(x) { 74 | UseMethod("as_weighted_point") 75 | } 76 | #' @export 77 | as_weighted_point.default <- function(x) { 78 | cli_abort("Can't convert the input to a {.cls euclid_point_w} vector") 79 | } 80 | #' @export 81 | as_weighted_point.euclid_point_w <- function(x) x 82 | 83 | #' @export 84 | as_point.euclid_point_w <- function(x) { 85 | point(x) 86 | } 87 | 88 | 89 | # Misc -------------------------------------------------------------------- 90 | 91 | #' @export 92 | seq.euclid_point_w <- function(from, to, length.out = NULL, along.with = NULL, ...) { 93 | if (dim(from) != dim(to)) { 94 | cli_abort("{.arg from} and {.arg to} must have the same number of dimensions") 95 | } 96 | if (!is.null(along.with)) { 97 | length.out = length(along.with) 98 | } 99 | if (is.null(length.out)) { 100 | cli_abort("Either {.arg length.out} or {.arg along.with} must be provided") 101 | } 102 | weighted_point( 103 | seq(vert(from), vert(to), length.out = length.out), 104 | seq(def(from, "w"), def(to, "w"), length.out = length.out) 105 | ) 106 | } 107 | 108 | # Internal constructors --------------------------------------------------- 109 | 110 | new_point_w_empty <- function(dim) { 111 | if (dim == 2) { 112 | new_geometry_vector(create_point_w_2_empty()) 113 | } else { 114 | new_geometry_vector(create_point_w_3_empty()) 115 | } 116 | } 117 | new_point_w_from_xyw <- function(x, y, w) { 118 | new_geometry_vector(create_point_w_2_x_y_w(get_ptr(x), get_ptr(y), get_ptr(w))) 119 | } 120 | new_point_w_from_xyzw <- function(x, y, z, w) { 121 | new_geometry_vector(create_point_w_3_x_y_z_w(get_ptr(x), get_ptr(y), get_ptr(z), get_ptr(w))) 122 | } 123 | new_point_w_from_pw <- function(p, w) { 124 | if (dim(p) == 2) { 125 | new_geometry_vector(create_point_w_2_p_w(get_ptr(p), get_ptr(w))) 126 | } else { 127 | new_geometry_vector(create_point_w_3_p_w(get_ptr(p), get_ptr(w))) 128 | } 129 | } 130 | --------------------------------------------------------------------------------