├── configure.win ├── cleanup ├── .gitattributes ├── src ├── Makevars.win ├── Makevars.in ├── init.c ├── clipper.h └── interface.cpp ├── .Rbuildignore ├── NAMESPACE ├── .gitignore ├── R ├── First.R └── clipper.R ├── .travis.yml ├── appveyor.yml ├── DESCRIPTION ├── man ├── pointinpolygon.Rd ├── polyminkowski.Rd ├── polysimplify.Rd ├── polyoffset.Rd ├── polyclip.Rd └── polylineoffset.Rd ├── README.md ├── NEWS └── configure.ac /configure.win: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cleanup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f config.* src/Makevars 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | data/* binary 3 | src/* text=lf 4 | R/* text=lf 5 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | PKG_CPPFLAGS = -DPOLYCLIP_LONG64="signed long long" -DPOLYCLIP_ULONG64="unsigned long long" 2 | 3 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | README.md 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | ^\.travis\.yml$ 5 | \.gcov$ 6 | \.gcda$ 7 | \.gcno$ 8 | ^appveyor\.yml$ 9 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | export(polyclip,polyoffset,polylineoffset,polysimplify,polyminkowski,pointinpolygon) 2 | useDynLib(polyclip, .registration=TRUE, .fixes="e") 3 | -------------------------------------------------------------------------------- /src/Makevars.in: -------------------------------------------------------------------------------- 1 | PKG_CPPFLAGS = @POLYCLIP_CPPFLAGS@ 2 | PKG_CXXFLAGS = @POLYCLIP_CXXFLAGS@ 3 | PKG_LIBS = @POLYCLIP_LIBS@ 4 | OBJECTS = init.o interface.o @POLYCLIP_OBJECTS@ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Emacs backups 2 | *~ 3 | 4 | # History files 5 | .Rhistory 6 | 7 | # Example code in package build process 8 | *-Ex.R 9 | 10 | # R data files from past sessions 11 | .Rdata 12 | .Rproj.user 13 | 14 | # R-studio files 15 | polyclip.Rproj 16 | 17 | # covr files 18 | src/*.gcov 19 | src/*.gcda 20 | src/*.gcno 21 | 22 | # .o files 23 | src/*o 24 | 25 | config.* 26 | src/Makevars 27 | autom4te.cache -------------------------------------------------------------------------------- /R/First.R: -------------------------------------------------------------------------------- 1 | # First.R 2 | # 3 | # $Revision: 1.1 $ $Date: 2013/10/19 03:06:59 $ 4 | # 5 | 6 | .onLoad <- function(...) {} 7 | 8 | .onAttach <- function(libname, pkgname) { 9 | dfile <- system.file("DESCRIPTION", package="polyclip") 10 | ver <- read.dcf(file=dfile, fields="Version") 11 | clipperbuild <- read.dcf(file=dfile, fields="Note") 12 | msg <- paste("polyclip", ver, clipperbuild) 13 | packageStartupMessage(msg) 14 | invisible(NULL) 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | matrix: 3 | include: 4 | - os: osx 5 | r: release 6 | - os: linux 7 | r: release 8 | - os: linux 9 | r: devel 10 | sudo: false 11 | cache: packages 12 | after_success: 13 | - if [[ "$TRAVIS_R_VERSION" != "devel" && ("$TRAVIS_OS_NAME" == "osx") ]]; then R CMD INSTALL --build $PKG_TARBALL; fi 14 | - if [[ "$TRAVIS_R_VERSION" != "devel" && ("$TRAVIS_OS_NAME" == "linux") ]]; then Rscript -e 'install.packages("covr");covr::codecov(type="all")'; fi 15 | d,eploy: 16 | provider: releases 17 | api_key: 18 | secure: qau8PJIhPE6JlM+2mhGPuacL6MjUMd9HITKGUoXUwLBbtHKsYDYwLRWnqLDnsexOUeZm/dWHpixPD8TxKIrkT1P59jRkHKhC0k0tgs6NMB+JjRRFGzFp6m0DSkpei/6/F6owR9lWXyqxL8j+ownzBNiTtX356yr+XcLBZz/yxNJYD7FjLA0NDjoAEY92vmczNZRXJcjq9i3gehok3PHvybBZvi+sXvjorvLbewy36DRypA7TpAmgwqOi4Flxzbb8VFO6AsPwydZtI3PCpnd9KcaQ3uirb1De5Q1PWNIENdzLCGl9A9LOAKiMLa4k8KrhbmynBihvUisSwhoLEF0GI2qB4P191KOAjsEM9uWHrX3UqvlatGA/bhDtqwjoDyHeXSGv7USerk6CFzk2+kh67yCOZGg5HFB62lMxxXWzxMEfw9kisXV+aL7zeZjfsXHJ1sXlvAsEx2sk6NIYGSkQJcUbKL6UhJ15EJw1X33c3xfzFh66QzUELJ0bkVm5hFLvLPtcKGSU2NmLQArIYJbCf1rcXkjHQDl0IBYWK4dBXU27kckSiCWV71NMDIGUSV6SuGaPGCyRH6trmSjNdmMCh/5zOo8MBvm/D8y/yUrkj+qkQ7+XUSPFfL7xHzakrdlHE39L35IFIhiKzNTwy3LWblupYHStN81TgANwMVJc1gc= 19 | skip_cleanup: true 20 | file_glob: true 21 | file: polyclip_*.t*gz 22 | on: 23 | tags: true 24 | condition: "$TRAVIS_R_VERSION != devel && ($TRAVIS_OS_NAME == osx)" 25 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # DO NOT CHANGE the "init" and "install" sections below 2 | 3 | # Download script file from GitHub 4 | init: 5 | ps: | 6 | $ErrorActionPreference = "Stop" 7 | Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" 8 | Import-Module '..\appveyor-tool.ps1' 9 | 10 | install: 11 | ps: Bootstrap 12 | 13 | # Adapt as necessary starting from here 14 | 15 | environment: 16 | global: 17 | WARNINGS_ARE_ERRORS: 1 18 | access_token: 19 | secure: GCPohlv0NVqq0Cv3wh0Luw1OgO3jS4hyU7zUkA4a/TPMsqq1mYSm0/8Qb3uPrR7d 20 | 21 | build_script: 22 | - travis-tool.sh install_deps 23 | 24 | test_script: 25 | - travis-tool.sh run_tests 26 | 27 | on_failure: 28 | - 7z a failure.zip *.Rcheck\* 29 | - appveyor PushArtifact failure.zip 30 | 31 | artifacts: 32 | - path: '*.Rcheck\**\*.log' 33 | name: Logs 34 | 35 | - path: '*.Rcheck\**\*.out' 36 | name: Logs 37 | 38 | - path: '*.Rcheck\**\*.fail' 39 | name: Logs 40 | 41 | - path: '*.Rcheck\**\*.Rout' 42 | name: Logs 43 | 44 | - path: '\*_*.tar.gz' 45 | name: Bits 46 | 47 | - path: '\*_*.zip' 48 | name: Bits 49 | 50 | deploy: 51 | provider: GitHub 52 | auth_token: 53 | secure: JTQgGqunZXLviRDLkYL5BATyY59WDDcviRFy0F8e6F8/ilVzfCcg/hlidtpZqObw 54 | artifact: /\*_*.zip/ 55 | on: 56 | branch: 57 | - master 58 | - /v\d\.\d-\d/ 59 | appveyor_repo_tag: true 60 | 61 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: polyclip 2 | Version: 1.10-7 3 | Date: 2024-07-23 4 | Title: Polygon Clipping 5 | Authors@R: c(person("Angus", "Johnson", role = "aut", 6 | comment="C++ original, http://www.angusj.com/delphi/clipper.php"), 7 | person("Adrian", "Baddeley", role = c("aut", "trl", "cre"), 8 | email = "Adrian.Baddeley@curtin.edu.au"), 9 | person("Kurt", "Hornik", role = "ctb"), 10 | person(c("Brian", "D."), "Ripley", role = "ctb"), 11 | person("Elliott", "Sales de Andrade", role="ctb"), 12 | person("Paul", "Murrell", role = "ctb"), 13 | person("Ege", "Rubak", role="ctb"), 14 | person("Mark", "Padgham", role="ctb")) 15 | Maintainer: Adrian Baddeley 16 | Depends: R (>= 3.5.0) 17 | Description: R port of Angus Johnson's open source library 'Clipper'. Performs polygon clipping operations (intersection, union, set minus, set difference) for polygonal regions of arbitrary complexity, including holes. Computes offset polygons (spatial buffer zones, morphological dilations, Minkowski dilations) for polygonal regions and polygonal lines. Computes Minkowski Sum of general polygons. There is a function for removing self-intersections from polygon data. 18 | License: BSL 19 | URL: https://www.angusj.com, https://sourceforge.net/projects/polyclipping, https://github.com/baddstats/polyclip 20 | BugReports: https://github.com/baddstats/polyclip/issues 21 | ByteCompile: true 22 | Note: built from Clipper C++ version 6.4.0 23 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Native symbol registration table for polyclip package 4 | 5 | */ 6 | 7 | #include 8 | #include 9 | #include // for NULL 10 | #include 11 | 12 | SEXP Csimplify(SEXP A, 13 | SEXP pft, 14 | SEXP X0, 15 | SEXP Y0, 16 | SEXP Eps); 17 | 18 | SEXP Cclipbool(SEXP A, 19 | SEXP B, 20 | SEXP pftA, 21 | SEXP pftB, 22 | SEXP ct, 23 | SEXP X0, 24 | SEXP Y0, 25 | SEXP Eps, 26 | SEXP clo); 27 | 28 | SEXP Cpolyoffset(SEXP A, 29 | SEXP del, 30 | SEXP jt, 31 | SEXP mlim, 32 | SEXP atol, 33 | SEXP X0, 34 | SEXP Y0, 35 | SEXP Eps); 36 | 37 | SEXP Clineoffset(SEXP A, 38 | SEXP del, 39 | SEXP jt, 40 | SEXP et, 41 | SEXP mlim, 42 | SEXP atol, 43 | SEXP X0, 44 | SEXP Y0, 45 | SEXP Eps); 46 | 47 | SEXP Cminksum(SEXP A, 48 | SEXP B, 49 | SEXP clo, 50 | SEXP X0, 51 | SEXP Y0, 52 | SEXP Eps); 53 | 54 | SEXP Cpiptest(SEXP P, 55 | SEXP A, 56 | SEXP X0, 57 | SEXP Y0, 58 | SEXP Eps); 59 | 60 | static const R_CMethodDef CEntries[] = { 61 | {NULL, NULL, 0} 62 | }; 63 | 64 | static const R_CallMethodDef CallEntries[] = { 65 | {"Csimplify", (DL_FUNC) &Csimplify, 5}, 66 | {"Cclipbool", (DL_FUNC) &Cclipbool, 9}, 67 | {"Cpolyoffset", (DL_FUNC) &Cpolyoffset, 8}, 68 | {"Clineoffset", (DL_FUNC) &Clineoffset, 9}, 69 | {"Cminksum", (DL_FUNC) &Cminksum, 6}, 70 | {"Cpiptest", (DL_FUNC) &Cpiptest, 5}, 71 | {NULL, NULL, 0} 72 | }; 73 | 74 | 75 | void R_init_polyclip(DllInfo *dll) 76 | { 77 | R_registerRoutines(dll, CEntries, CallEntries, NULL, NULL); 78 | R_useDynamicSymbols(dll, FALSE); 79 | } 80 | -------------------------------------------------------------------------------- /man/pointinpolygon.Rd: -------------------------------------------------------------------------------- 1 | \name{pointinpolygon} 2 | \alias{pointinpolygon} 3 | \title{Test Whether a Point Lies Inside a Polygon} 4 | \description{ 5 | Test whether each point lies inside a specified polygon. 6 | } 7 | \usage{ 8 | pointinpolygon(P, A, eps, x0, y0) 9 | } 10 | \arguments{ 11 | \item{P}{ 12 | Spatial coordinates of the points to be tested. 13 | A list of two vectors named \code{x} and \code{y}. 14 | } 15 | \item{A}{ 16 | A single polygon, specified as a list of two vectors 17 | named \code{x} and \code{y}. 18 | } 19 | \item{eps}{Spatial resolution for coordinates.} 20 | \item{x0,y0}{Spatial origin for coordinates.} 21 | } 22 | \value{ 23 | An integer vector with one entry for each point in \code{P}. 24 | The result is 0 if the point lies outside \code{A}, 25 | 1 if the point lies inside \code{A}, and -1 if it lies on the 26 | boundary. 27 | } 28 | \details{ 29 | This is part of an interface to the polygon-clipping library 30 | \code{Clipper} written by Angus Johnson. 31 | 32 | The argument \code{A} represents a closed polygon. 33 | It should be 34 | a list containing two components \code{x} and \code{y} 35 | giving the coordinates of the vertices. 36 | The last vertex should 37 | not repeat the first vertex. 38 | 39 | \bold{Calculations are performed in integer arithmetic} 40 | after subtracting \code{x0,y0} from the coordinates, 41 | dividing by \code{eps}, and rounding to the nearest integer. 42 | Thus, \code{eps} is the effective spatial resolution. 43 | The default values ensure reasonable accuracy. 44 | } 45 | \seealso{ 46 | \code{\link{polyclip}}. 47 | } 48 | \author{Angus Johnson. 49 | Ported to \R by Adrian Baddeley 50 | \email{Adrian.Baddeley@curtin.edu.au}. 51 | } 52 | \examples{ 53 | A <- list(x=1:10, y=c(1:5,5:1)) 54 | P <- list(x=4, y=2) 55 | pointinpolygon(P, A) 56 | } 57 | \references{ 58 | Clipper Website: \url{http://www.angusj.com} 59 | 60 | Vatti, B. (1992) A generic solution to polygon clipping. 61 | \emph{Communications of the ACM} \bold{35} (7) 56--63. 62 | \url{https://dl.acm.org/doi/10.1145/129902.129906} 63 | 64 | Agoston, M.K. (2005) 65 | \emph{Computer graphics and geometric modeling: 66 | implementation and algorithms.} 67 | Springer-Verlag. 68 | \url{http://books.google.com/books?q=vatti+clipping+agoston} 69 | } 70 | \keyword{spatial} 71 | \keyword{math} 72 | -------------------------------------------------------------------------------- /man/polyminkowski.Rd: -------------------------------------------------------------------------------- 1 | \name{polyminkowski} 2 | \alias{polyminkowski} 3 | \title{ 4 | Minkowski Sum of Polygon with Path 5 | } 6 | \description{ 7 | Compute the Minkowski Sum of a polygon with 8 | a path or paths. 9 | } 10 | \usage{ 11 | polyminkowski(A, B, \dots, eps, x0, y0, 12 | closed=TRUE) 13 | } 14 | \arguments{ 15 | \item{A}{ 16 | Data specifying a polygon or polygons. See Details. 17 | } 18 | \item{B}{ 19 | Data specifying a path or paths. See Details. 20 | } 21 | \item{\dots}{ 22 | Ignored. 23 | } 24 | \item{eps}{Spatial resolution for coordinates.} 25 | \item{x0,y0}{Spatial origin for coordinates.} 26 | \item{closed}{ 27 | Logical value indicating whether the resulting path 28 | should be interpreted as closed (last vertex joined to first vertex 29 | and interior filled in). 30 | } 31 | } 32 | \details{ 33 | This is an interface to the function \code{MinkowskiSum} in 34 | Angus Johnson's \code{C++} library \pkg{Clipper}. 35 | 36 | It computes the Minkowski Sum of the polygon \code{A} 37 | (including its interior) with the path or paths \code{B} 38 | (\emph{excluding} their interior). 39 | 40 | The argument \code{A} should be 41 | a list containing two components \code{x} and \code{y} 42 | giving the coordinates of the vertices of a single polygon. 43 | The last vertex should 44 | not repeat the first vertex. 45 | 46 | The argument \code{B} should be either 47 | \itemize{ 48 | \item 49 | a list containing two components \code{x} and \code{y} 50 | giving the coordinates of the vertices of a single polygon or path. 51 | The last vertex should 52 | not repeat the first vertex. 53 | \item 54 | a \code{list} of \code{list(x,y)} structures giving 55 | the coordinates of the vertices of several polygons or paths. 56 | } 57 | 58 | \bold{Calculations are performed in integer arithmetic} 59 | after subtracting \code{x0,y0} from the coordinates, 60 | dividing by \code{eps}, and rounding to the nearest integer. 61 | Thus, \code{eps} is the effective spatial resolution. 62 | The default values ensure reasonable accuracy. 63 | } 64 | \value{ 65 | Data specifying polygons, in the same format as \code{A}. 66 | } 67 | \author{Angus Johnson. 68 | Ported to \R by Adrian Baddeley 69 | \email{Adrian.Baddeley@curtin.edu.au}. 70 | } 71 | \references{ 72 | Clipper Website: \url{http://www.angusj.com} 73 | } 74 | \seealso{ 75 | \code{\link{polyclip}}, 76 | \code{\link{polyoffset}}, 77 | \code{\link{polylineoffset}}, 78 | \code{\link{polysimplify}} 79 | } 80 | \examples{ 81 | A <- list(list(x=c(-3,3,3,-3),y=c(-3,-3,3,3))) 82 | B <- list(list(x=c(-1,1,1,-1),y=c(-1,-1,1,1))) 83 | C <- polyminkowski(A, B) 84 | opa <- par(mfrow=c(1,3)) 85 | rr <- c(-4, 4) 86 | plot(rr, rr, type="n", axes=FALSE, xlab="", ylab="", main="A") 87 | polygon(A[[1]], col="blue") 88 | plot(rr,rr, type="n", axes=FALSE, xlab="", ylab="", main="B") 89 | polygon(B[[1]], border="red", lwd=4) 90 | plot(rr,rr, type="n", axes=FALSE, xlab="", ylab="", main="A+B") 91 | polygon(C[[1]], col="green") 92 | polygon(C[[2]], col="white") 93 | par(opa) 94 | } 95 | \keyword{spatial} 96 | \keyword{manip} 97 | 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | polyclip 2 | ======== 3 | 4 | [![Travis-CI Build Status](https://travis-ci.org/baddstats/polyclip.png?branch=master)](https://travis-ci.org/baddstats/polyclip) 5 | [![codecov.io](https://codecov.io/github/baddstats/polyclip/coverage.svg?branch=master)](https://codecov.io/github/baddstats/polyclip?branch=master) 6 | [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/polyclip)](http://cran.r-project.org/web/packages/polyclip) 7 | [![Research software impact](http://depsy.org/api/package/cran/polyclip/badge.svg)](http://depsy.org/package/r/polyclip) 8 | 9 | This repository holds the contributed R-package `polyclip`, which is 10 | an R port of Angus Johnson's library 11 | [Clipper](http://angusj.com/delphi/clipper.php) for polygon clipping. 12 | 13 | ## Version of Clipper Library 14 | 15 | This version of `polyclip` is derived from Clipper1, the original version of 16 | the Clipper C++ library, version `6.4.0 [r496]` which was obtained from the 17 | [Sourceforge repository](https://sourceforge.net/projects/polyclipping) 18 | (click `Code` then `Download snapshot`). 19 | Minor changes have been made to the C++ code to satisfy the 20 | requirements for R packages (namely, data type declarations must be portable, 21 | and error messages must go through R's error handler). 22 | 23 | **Note:** If your system already includes the `polyclipping` library 24 | (another derivative of `clipper`) 25 | then **that version of the library will be used**. 26 | That is, the R package `polyclip` will be compiled against 27 | the executable library `polyclipping` on your system, 28 | rather than using the bundled source code of `clipper 6.4.0` 29 | that comes with the `polyclip` sources. 30 | 31 | ## Installation 32 | 33 | The current official release of `polyclip` is available 34 | on [CRAN](http://cran.r-project.org/web/packages/polyclip) 35 | and can be downloaded and installed automatically 36 | using the R command `install.packages`. 37 | 38 | The code in this repository is the development version, 39 | which may be newer than the official release. 40 | The easiest way to install the development version of `polyclip` 41 | from github is through the `remotes` package: 42 | 43 | ```R 44 | require(remotes) 45 | install_github('baddstats/polyclip') 46 | ``` 47 | 48 | If you don't have `remotes` installed you should first run 49 | 50 | ```R 51 | install.packages('remotes') 52 | ``` 53 | 54 | ## Trouble installing? 55 | 56 | When trying to install `polyclip`, some users get an error message 57 | like the following: 58 | ```R 59 | configure: error: in /tmp/Rtmp7967a6f2/polyclip: 60 | configure: error: C++ compiler cannot create executables 61 | See config.log for more details 62 | ERROR: configuration failed for package 'polyclip' 63 | ``` 64 | This is a problem with file permissions on your system. 65 | If this happens to you, the simplest solution is: 66 | ```R 67 | wget https://cran.r-project.org/src/contrib/polyclip_1.10-7.tar.gz 68 | tar -zxvf polyclip_1.10-7.tar.gz 69 | Rscript -e "library('devtools'); devtools::install('polyclip')" 70 | ``` 71 | 72 | ## Bug reports 73 | 74 | Users of `polyclip` are encouraged to report bugs here 75 | (go to *issues* in the menu above, 76 | and press *new issue* to start a new bug report 77 | or feature request). 78 | 79 | ## Making your own changes 80 | 81 | Feel free to fork `polyclip`, make changes to the code, 82 | and ask us to include them in the package by making a github *pull request*. 83 | 84 | -------------------------------------------------------------------------------- /man/polysimplify.Rd: -------------------------------------------------------------------------------- 1 | \name{polysimplify} 2 | \alias{polysimplify} 3 | \title{ 4 | Remove Self-Intersections from a Polygon 5 | } 6 | \description{ 7 | This function attempts to remove self-intersections and duplicated 8 | vertices from the given polygon. 9 | } 10 | \usage{ 11 | polysimplify(A, \dots, eps, x0, y0, 12 | filltype = c("evenodd", "nonzero", "positive", "negative")) 13 | } 14 | \arguments{ 15 | \item{A}{ 16 | Data specifying a polygon or polygons. See Details. 17 | } 18 | \item{\dots}{ 19 | Ignored. 20 | } 21 | \item{eps}{Spatial resolution for coordinates.} 22 | \item{x0,y0}{Spatial origin for coordinates.} 23 | \item{filltype}{Polygon-filling rule. See Details.} 24 | } 25 | \details{ 26 | This is an interface to the function \code{SimplifyPolygons} in 27 | Angus Johnson's \code{C++} library \pkg{Clipper}. 28 | It tries to remove self-intersections from the supplied polygon, 29 | by performing a boolean union operation using the nominated 30 | \code{filltype}. The result may be one or several polygons. 31 | 32 | The argument \code{A} should be either 33 | \itemize{ 34 | \item 35 | a list containing two components \code{x} and \code{y} 36 | giving the coordinates of the vertices of a single polygon. 37 | The last vertex should 38 | not repeat the first vertex. 39 | \item 40 | a \code{list} of \code{list(x,y)} structures giving 41 | the coordinates of the vertices of several polygons. 42 | } 43 | 44 | The argument \code{filltype} determines the polygon fill type. 45 | \describe{ 46 | \item{Even-Odd:}{ 47 | The default rule is \emph{even-odd} filling, 48 | in which every polygon edge demarcates a boundary between 49 | the inside and outside of the region. 50 | It does not matter whether a polygon is traversed in 51 | clockwise or anticlockwise order. Holes are determined 52 | simply by their locations relative to other polygons such that 53 | outers contain holes and holes contain outers. 54 | } 55 | \item{Non-Zero:}{ 56 | Under the \emph{nonzero} filling rule, an outer boundary must be 57 | traversed in clockwise order, while a hole must be traversed 58 | in anticlockwise order. 59 | } 60 | \item{Positive:}{ 61 | Under the \code{positive} filling rule, the filled region 62 | consists of all points with positive winding number. 63 | } 64 | \item{Negative:}{ 65 | Under the \code{negative} filling rule, the filled region 66 | consists of all points with negative winding number. 67 | } 68 | } 69 | 70 | \bold{Calculations are performed in integer arithmetic} 71 | after subtracting \code{x0,y0} from the coordinates, 72 | dividing by \code{eps}, and rounding to the nearest integer. 73 | Thus, \code{eps} is the effective spatial resolution. 74 | The default values ensure reasonable accuracy. 75 | } 76 | \value{ 77 | Data specifying polygons, in the same format as \code{A}. 78 | } 79 | \author{Angus Johnson. 80 | Ported to \R by Adrian Baddeley 81 | \email{Adrian.Baddeley@curtin.edu.au}. 82 | } 83 | \references{ 84 | Clipper Website: \url{http://www.angusj.com} 85 | } 86 | \seealso{ 87 | \code{\link{polyclip}}, 88 | \code{\link{polyoffset}}, 89 | \code{\link{polylineoffset}}, 90 | \code{\link{polyminkowski}} 91 | } 92 | \examples{ 93 | theta <- 2 * pi * (0:5) * 2/5 94 | 95 | A <- list(list(x=sin(theta), y=cos(theta))) 96 | B <- polysimplify(A, filltype="nonzero") 97 | 98 | opa <- par(mfrow=c(1,2)) 99 | plot(c(-1,1),c(-1,1), type="n", axes=FALSE, xlab="", ylab="") 100 | with(A[[1]], segments(x[-6], y[-6], x[-1], y[-1], col="red")) 101 | points(A[[1]], col="red") 102 | 103 | with(A[[1]], text(x[1:5], y[1:5], labels=1:5, cex=2)) 104 | plot(c(-1,1),c(-1,1), type="n", axes=FALSE, xlab="", ylab="") 105 | polygon(B[[1]], lwd=3, col="green") 106 | with(B[[1]], text(x,y,labels=seq_along(x), cex=2)) 107 | par(opa) 108 | } 109 | \keyword{spatial} 110 | \keyword{manip} 111 | 112 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 2 | CHANGES IN polyclip VERSION 1.10-7 3 | 4 | SIGNIFICANT USER-VISIBLE CHANGES 5 | 6 | o Minor changes to placate package checker. 7 | 8 | o polyclip now requires R 3.5.0 or later. 9 | 10 | CHANGES IN polyclip VERSION 1.10-6 11 | 12 | SIGNIFICANT USER-VISIBLE CHANGES 13 | 14 | o Minor changes to placate package checker. 15 | 16 | CHANGES IN polyclip VERSION 1.10-5 17 | 18 | SIGNIFICANT USER-VISIBLE CHANGES 19 | 20 | o Minor changes to placate package checker. 21 | 22 | CHANGES IN polyclip VERSION 1.10-4 23 | 24 | SIGNIFICANT USER-VISIBLE CHANGES 25 | 26 | o Minor changes to placate package checker. 27 | 28 | CHANGES IN polyclip VERSION 1.10-3 29 | 30 | SIGNIFICANT USER-VISIBLE CHANGES 31 | 32 | o Minor changes to placate package checker. 33 | 34 | CHANGES IN polyclip VERSION 1.10-2 35 | 36 | SIGNIFICANT USER-VISIBLE CHANGES 37 | 38 | o Minor changes in help files to placate package checker. 39 | 40 | CHANGES IN polyclip VERSION 1.10-1 41 | 42 | SIGNIFICANT USER-VISIBLE CHANGES 43 | 44 | o Minor changes to placate package checker. 45 | 46 | CHANGES IN polyclip VERSION 1.10-0 47 | 48 | SIGNIFICANT USER-VISIBLE CHANGES 49 | 50 | o polyclip 51 | Now handles polygonal lines as well as closed polygons. 52 | New argument 'closed' specifies whether A is a closed polygon. 53 | [Thanks to Paul Murrell.] 54 | 55 | CHANGES IN polyclip VERSION 1.9-1 56 | 57 | SIGNIFICANT USER-VISIBLE CHANGES 58 | 59 | o Changes to installation procedure (namely the choice of C++ compiler) 60 | to comply with CRAN requirements. 61 | 62 | CHANGES IN polyclip VERSION 1.9-0 63 | 64 | SIGNIFICANT USER-VISIBLE CHANGES 65 | 66 | o NEWS file has been added. 67 | 68 | o Internal code has been modified to work in the 'datacamp' environment. 69 | There are no changes to the user interface or the functionality. 70 | 71 | CHANGES IN polyclip VERSION 1.8-4 72 | 73 | SIGNIFICANT USER-VISIBLE CHANGES 74 | 75 | o No visible changes. 76 | 77 | CHANGES IN polyclip VERSION 1.8-3 78 | 79 | NEW FUNCTIONS 80 | 81 | o pointinpolygon 82 | Test whether a point falls inside a polygon. 83 | 84 | SIGNIFICANT USER-VISIBLE CHANGES 85 | 86 | o Internal changes to comply with new CRAN requirements. 87 | 88 | o Improvements to package installation. 89 | 90 | CHANGES IN polyclip VERSION 1.6-1 91 | 92 | SIGNIFICANT USER-VISIBLE CHANGES 93 | 94 | o Internal changes to comply with new CRAN requirements. 95 | 96 | o Improvements to package installation. 97 | 98 | CHANGES IN polyclip VERSION 1.5-6 99 | 100 | SIGNIFICANT USER-VISIBLE CHANGES 101 | 102 | o The code can be compiled using older C++ compilers. 103 | 104 | o The package can be installed on a wider range of systems. 105 | 106 | CHANGES IN polyclip VERSION 1.5-1 107 | 108 | SIGNIFICANT USER-VISIBLE CHANGES 109 | 110 | o Package prints a startup message. 111 | 112 | CHANGES IN polyclip VERSION 1.4-1 113 | 114 | SIGNIFICANT USER-VISIBLE CHANGES 115 | 116 | o No history is available. 117 | 118 | CHANGES IN polyclip VERSION 1.3-2 119 | 120 | SIGNIFICANT USER-VISIBLE CHANGES 121 | 122 | o No history is available. 123 | 124 | CHANGES IN polyclip VERSION 1.3-0 125 | 126 | SIGNIFICANT USER-VISIBLE CHANGES 127 | 128 | o No history is available. 129 | 130 | CHANGES IN polyclip VERSION 1.2-0 131 | 132 | SIGNIFICANT USER-VISIBLE CHANGES 133 | 134 | o No history is available. 135 | 136 | CHANGES IN polyclip VERSION 1.1-0 137 | 138 | SIGNIFICANT USER-VISIBLE CHANGES 139 | 140 | o No history is available. 141 | 142 | CHANGES IN polyclip VERSION 1.0-0 143 | 144 | SIGNIFICANT USER-VISIBLE CHANGES 145 | 146 | o No history is available. 147 | 148 | CHANGES IN polyclip VERSION 0.0-1 149 | 150 | SIGNIFICANT USER-VISIBLE CHANGES 151 | 152 | o This is the first public release of 'polyclip'. 153 | 154 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(polyclip,[1.9-1]) 2 | CXXFLAGS=`"${R_HOME}/bin/R" CMD config CXXFLAGS` 3 | CPPFLAGS=`"${R_HOME}/bin/R" CMD config CPPFLAGS` 4 | AC_PROG_CXX 5 | AC_LANG([C++]) 6 | dnl Check for pkgconf or pkg-config 7 | if test -z "$PKG_CONFIG"; then 8 | if command -v pkgconf 2>&1 > /dev/null; then 9 | PKG_CONFIG=pkgconf 10 | else 11 | if command -v pkg-config 2>&1 > /dev/null; then 12 | PKG_CONFIG=pkg-config 13 | fi 14 | fi 15 | fi 16 | echo "Using PKG_CONFIG: $PKG_CONFIG" 17 | dnl Check if polyclipping library is installed on system. 18 | if test -n "$PKG_CONFIG" && $PKG_CONFIG --exists polyclipping; then 19 | echo Compiling against system copy of polyclipping library. 20 | POLYCLIP_LIBS=`$PKG_CONFIG --libs polyclipping` 21 | POLYCLIP_CPPFLAGS=`$PKG_CONFIG --cflags polyclipping` 22 | dnl The polyclip type declarations are needed to compile the R interface 23 | LLTYPES="-DPOLYCLIP_LONG64=\"signed long long\" -DPOLYCLIP_ULONG64=\"unsigned long long\"" 24 | POLYCLIP_CPPFLAGS="${POLYCLIP_CPPFLAGS} ${LLTYPES}" 25 | else 26 | echo Compiling against bundled copy of clipper library. 27 | dnl Check for availability of 64-bit integer types in C++ 28 | dnl Signed 64-bit integer 29 | long64="" 30 | name64="signed 64-bit integers (cInt)" 31 | if test "${CXX1X}" != ""; then 32 | long64="signed long long" 33 | POLYCLIP_LONG64="${long64}" 34 | else 35 | AC_CHECK_TYPES([int64_t],[long64="int64_t"],[],[#include ]) 36 | if test "${long64}" != ""; then 37 | POLYCLIP_LONG64="${long64}" 38 | else 39 | AC_CHECK_TYPES([int_fast64_t],[long64="int_fast64_t"],[],[#include ]) 40 | if test "${long64}" != ""; then 41 | POLYCLIP_LONG64="${long64}" 42 | else 43 | AC_CHECK_TYPES([int_least64_t],[long64="int_least64_t"],[],[#include ]) 44 | if test "${long64}" != ""; then 45 | POLYCLIP_LONG64="${long64}" 46 | else 47 | AC_CHECK_TYPES([long long],[long64="long long"]) 48 | if test "${long64}" != ""; then 49 | POLYCLIP_LONG64="${long64}" 50 | else 51 | echo "Error: unable to find a C++ data type for ${name64}" 52 | exit 1 53 | fi 54 | fi 55 | fi 56 | fi 57 | fi 58 | echo " In the clipper library, ${name64}" 59 | echo " will be declared as '${long64}'" 60 | dnl Unsigned 64-bit integer 61 | ulong64="" 62 | uname64="unsigned 64-bit integers (cUInt)" 63 | if test "${CXX1X}" != ""; then 64 | ulong64="unsigned long long" 65 | POLYCLIP_ULONG64="${ulong64}" 66 | else 67 | AC_CHECK_TYPES([uint64_t],[ulong64="uint64_t"],[],[#include ]) 68 | if test "${ulong64}" != ""; then 69 | POLYCLIP_ULONG64="${ulong64}" 70 | else 71 | AC_CHECK_TYPES([uint_fast64_t],[ulong64="uint_fast64_t"],[],[#include ]) 72 | if test "${ulong64}" != ""; then 73 | POLYCLIP_ULONG64="${ulong64}" 74 | else 75 | AC_CHECK_TYPES([uint_least64_t],[ulong64="uint_least64_t"],[],[#include ]) 76 | if test "${ulong64}" != ""; then 77 | POLYCLIP_ULONG64="${ulong64}" 78 | else 79 | AC_CHECK_TYPES([unsigned long long],[ulong64="unsigned long long"]) 80 | if test "${ulong64}" != ""; then 81 | POLYCLIP_ULONG64="${ulong64}" 82 | else 83 | echo "Error: unable to find a C++ data type for ${uname64}" 84 | exit 1 85 | fi 86 | fi 87 | fi 88 | fi 89 | fi 90 | echo " In the clipper library, ${uname64}" 91 | echo " will be declared as '${ulong64}'" 92 | dnl Put results in C++ preprocessor flags 93 | POLYCLIP_CPPFLAGS="-DPOLYCLIP_LONG64=\"${POLYCLIP_LONG64}\" -DPOLYCLIP_ULONG64=\"${POLYCLIP_ULONG64}\"" 94 | POLYCLIP_OBJECTS="clipper.o" 95 | fi 96 | POLYCLIP_CXXFLAGS="${CXXFLAGS}" 97 | AC_SUBST(POLYCLIP_CPPFLAGS) 98 | AC_SUBST(POLYCLIP_CXXFLAGS) 99 | AC_SUBST(POLYCLIP_LIBS) 100 | AC_SUBST(POLYCLIP_OBJECTS) 101 | AC_CONFIG_FILES([src/Makevars]) 102 | AC_OUTPUT 103 | 104 | 105 | -------------------------------------------------------------------------------- /man/polyoffset.Rd: -------------------------------------------------------------------------------- 1 | \name{polyoffset} 2 | \alias{polyoffset} 3 | \title{Polygon Offset} 4 | \description{ 5 | Given a polygonal region, compute the offset region (aka: guard region, 6 | buffer region, morphological dilation) formed by shifting the 7 | boundary outwards by a specified distance. 8 | } 9 | \usage{ 10 | polyoffset(A, delta, 11 | \dots, 12 | eps, x0, y0, 13 | miterlim=2, arctol=abs(delta)/100, 14 | jointype=c("square", "round", "miter")) 15 | } 16 | \arguments{ 17 | \item{A}{Data specifying polygons. See Details.} 18 | \item{delta}{Distance over which the boundary should be shifted.} 19 | \item{\dots}{Ignored.} 20 | \item{eps}{Spatial resolution for coordinates.} 21 | \item{x0,y0}{Spatial origin for coordinates.} 22 | \item{miterlim,arctol}{Tolerance parameters: see Details.} 23 | \item{jointype}{ 24 | Type of join operation to be performed at each vertex. 25 | See Details. 26 | } 27 | } 28 | \value{ 29 | Data specifying polygons, in the same format as \code{A}. 30 | } 31 | \details{ 32 | This is part of an interface to the polygon-clipping library 33 | \code{Clipper} written by Angus Johnson. 34 | 35 | Given a polygonal region \code{A}, 36 | the function \code{polyoffset} computes the offset region 37 | (also known as the morphological dilation, guard region, 38 | buffer region, etc) obtained by shifting the boundary of \code{A} 39 | outward by the distance \code{delta}. 40 | 41 | The argument \code{A} represents a region in the 42 | Euclidean plane bounded by closed polygons. The format is either 43 | \itemize{ 44 | \item 45 | a list containing two components \code{x} and \code{y} 46 | giving the coordinates of the vertices of a single polygon. 47 | The last vertex should 48 | not repeat the first vertex. 49 | \item 50 | a \code{list} of \code{list(x,y)} structures giving 51 | the coordinates of the vertices of several polygons. 52 | } 53 | Note that calculations are performed in integer arithmetic: see below. 54 | 55 | The argument \code{jointype} determines what happens at the convex vertices 56 | of \code{A}. See the Examples for illustrations. 57 | \itemize{ 58 | \item \code{jointype="round"}: a circular arc is generated. 59 | \item \code{jointype="square"}: the circular arc is 60 | replaced by a single straight line. 61 | \item \code{jointype="miter"}: the circular arc is 62 | omitted entirely, or replaced by a single straight line. 63 | } 64 | 65 | The arguments \code{miterlim} and \code{arctol} are tolerances. 66 | \itemize{ 67 | \item if \code{jointype="round"}, then \code{arctol} is the maximum 68 | permissible distance between the true circular arc and its 69 | discretised approximation. 70 | \item if \code{jointype="miter"}, then \code{miterlimit * delta} 71 | is the maximum permissible displacement between the original vertex and the 72 | corresponding offset vertex if the circular arc were to be 73 | omitted entirely. The default is \code{miterlimit=2} 74 | which is also the minimum value. 75 | } 76 | 77 | \bold{Calculations are performed in integer arithmetic} 78 | after subtracting \code{x0,y0} from the coordinates, 79 | dividing by \code{eps}, and rounding to the nearest 64-bit integer. 80 | Thus, \code{eps} is the effective spatial resolution. 81 | The default values ensure reasonable accuracy. 82 | } 83 | \author{Angus Johnson. 84 | Ported to \R by Adrian Baddeley 85 | \email{Adrian.Baddeley@curtin.edu.au}. 86 | } 87 | \seealso{ 88 | \code{\link{polylineoffset}}, 89 | \code{\link{polyclip}}, 90 | \code{\link{polysimplify}}, 91 | \code{\link{polyminkowski}} 92 | } 93 | \examples{ 94 | A <- list(list(x=c(4,8,8,2,6), y=c(3,3,8,8,6))) 95 | plot(c(0,10),c(0,10), type="n", main="jointype=square", axes=FALSE, xlab="", ylab="") 96 | polygon(A[[1]], col="grey") 97 | C <- polyoffset(A, 1, jointype="square") 98 | polygon(C[[1]], lwd=3, border="blue") 99 | plot(c(0,10),c(0,10), type="n", main="jointype=round", axes=FALSE, xlab="", ylab="") 100 | polygon(A[[1]], col="grey") 101 | C <- polyoffset(A, 1, jointype="round") 102 | polygon(C[[1]], lwd=3, border="blue") 103 | plot(c(0,10),c(0,10), type="n", main="jointype=miter", axes=FALSE, xlab="", ylab="") 104 | polygon(A[[1]], col="grey") 105 | C <- polyoffset(A, 1, jointype="miter") 106 | polygon(C[[1]], lwd=3, border="blue") 107 | } 108 | \references{ 109 | Clipper Website: \url{http://www.angusj.com} 110 | 111 | Vatti, B. (1992) A generic solution to polygon clipping. 112 | \emph{Communications of the ACM} \bold{35} (7) 56--63. 113 | \url{https://dl.acm.org/doi/10.1145/129902.129906} 114 | 115 | Agoston, M.K. (2005) 116 | \emph{Computer graphics and geometric modeling: 117 | implementation and algorithms.} 118 | Springer-Verlag. 119 | \url{http://books.google.com/books?q=vatti+clipping+agoston} 120 | 121 | Chen, X. and McMains, S. (2005) 122 | Polygon Offsetting by Computing Winding Numbers. 123 | Paper no. DETC2005-85513 in \emph{Proceedings of IDETC/CIE 2005} 124 | (ASME 2005 International Design Engineering Technical Conferences 125 | and Computers and Information in Engineering Conference), 126 | pp. 565--575 127 | \url{https://mcmains.me.berkeley.edu/pubs/DAC05OffsetPolygon.pdf} 128 | } 129 | \keyword{spatial} 130 | \keyword{math} 131 | -------------------------------------------------------------------------------- /man/polyclip.Rd: -------------------------------------------------------------------------------- 1 | \name{polyclip} 2 | \alias{polyclip} 3 | \title{Polygon Clipping} 4 | \description{ 5 | Find intersection, union or set difference of two polygonal regions 6 | or polygonal lines. 7 | } 8 | \usage{ 9 | polyclip(A, B, op=c("intersection", "union", "minus", "xor"), 10 | \dots, 11 | eps, x0, y0, 12 | fillA=c("evenodd", "nonzero", "positive", "negative"), 13 | fillB=c("evenodd", "nonzero", "positive", "negative"), 14 | closed = TRUE) 15 | } 16 | \arguments{ 17 | \item{A,B}{ 18 | Data specifying polygons. See Details. 19 | } 20 | \item{op}{ 21 | Set operation to be performed to combine \code{A} and \code{B}. 22 | One of the character strings \code{"intersection"}, 23 | \code{"union"}, \code{"minus"} or \code{"xor"} (partially matched). 24 | } 25 | \item{\dots}{Ignored.} 26 | \item{eps}{ 27 | Spatial resolution for coordinates. A single positive 28 | numeric value. 29 | } 30 | \item{x0,y0}{Spatial origin for coordinates. Numeric values.} 31 | \item{fillA,fillB}{ 32 | Polygon-filling rules for \code{A} and \code{B}. 33 | Each argument is one of the character strings 34 | \code{"evenodd"}, \code{"nonzero"}, \code{"positive"} or 35 | \code{"negative"} (partially matched). 36 | } 37 | \item{closed}{ 38 | Logical value specifying whether \code{A} is a 39 | closed polygon (\code{closed=TRUE}, the default) 40 | or an open polyline (\code{closed=FALSE}). 41 | } 42 | } 43 | \value{ 44 | Data specifying polygons, in the same format as \code{A} and \code{B}. 45 | } 46 | \details{ 47 | This is an interface to the polygon-clipping library 48 | \code{Clipper} written by Angus Johnson. 49 | 50 | Given two polygonal regions \code{A} and \code{B} 51 | the function \code{polyclip} performs one of the following 52 | geometrical operations: 53 | \itemize{ 54 | \item \code{op="intersection"}: set intersection of \code{A} and \code{B}. 55 | \item \code{op="union"}: set union of \code{A} and \code{B}. 56 | \item \code{op="minus"}: set subtraction (sometimes called set difference): 57 | the region covered by \code{A} that is not covered by \code{B}. 58 | \item \code{op="xor"}: exclusive set difference (sometimes called 59 | exclusive-or): the region covered by exactly one of the sets 60 | \code{A} and \code{B}. 61 | } 62 | 63 | Each of the arguments \code{A} and \code{B} represents a region in the 64 | Euclidean plane bounded by closed polygons. The format of these 65 | arguments is either 66 | \itemize{ 67 | \item 68 | a list containing two components \code{x} and \code{y} 69 | giving the coordinates of the vertices of a single polygon. 70 | The last vertex should 71 | not repeat the first vertex. 72 | \item 73 | a \code{list} of \code{list(x,y)} structures giving 74 | the coordinates of the vertices of several polygons. 75 | } 76 | Note that calculations are performed in integer arithmetic: see below. 77 | 78 | The interpretation of the polygons 79 | depends on the \emph{polygon-filling rule} for \code{A} and \code{B} 80 | that is specified by the arguments \code{fillA} and \code{fillB} 81 | respectively. 82 | \describe{ 83 | \item{Even-Odd:}{ 84 | The default rule is \emph{even-odd} filling, 85 | in which every polygon edge demarcates a boundary between 86 | the inside and outside of the region. 87 | It does not matter whether a polygon is traversed in 88 | clockwise or anticlockwise order. Holes are determined 89 | simply by their locations relative to other polygons such that 90 | outers contain holes and holes contain outers. 91 | } 92 | \item{Non-Zero:}{ 93 | Under the \emph{nonzero} filling rule, an outer boundary must be 94 | traversed in clockwise order, while a hole must be traversed 95 | in anticlockwise order. 96 | } 97 | \item{Positive:}{ 98 | Under the \code{positive} filling rule, the filled region 99 | consists of all points with positive winding number. 100 | } 101 | \item{Negative:}{ 102 | Under the \code{negative} filling rule, the filled region 103 | consists of all points with negative winding number. 104 | } 105 | } 106 | 107 | \bold{Calculations are performed in integer arithmetic} 108 | after subtracting \code{x0,y0} from the coordinates, 109 | dividing by \code{eps}, and rounding to the nearest integer. 110 | Thus, \code{eps} is the effective spatial resolution. 111 | The default values ensure reasonable accuracy. 112 | } 113 | \seealso{ 114 | \code{\link{polysimplify}}, 115 | \code{\link{polyoffset}}, 116 | \code{\link{polylineoffset}}, 117 | \code{\link{polyminkowski}} 118 | } 119 | \author{ 120 | Angus Johnson. 121 | Ported to \R by Adrian Baddeley 122 | \email{Adrian.Baddeley@curtin.edu.au}. 123 | Additional modification by Paul Murrell. 124 | } 125 | \examples{ 126 | A <- list(list(x=1:10, y=c(1:5,5:1))) 127 | B <- list(list(x=c(2,8,8,2),y=c(0,0,10,10))) 128 | 129 | plot(c(0,10),c(0,10), type="n", axes=FALSE, 130 | xlab="", ylab="", main="intersection of closed polygons") 131 | invisible(lapply(A, polygon)) 132 | invisible(lapply(B, polygon)) 133 | C <- polyclip(A, B) 134 | polygon(C[[1]], lwd=3, col=3) 135 | 136 | plot(c(0,10),c(0,10), type="n", axes=FALSE, 137 | xlab="", ylab="", main="intersection of open polyline and closed polygon") 138 | invisible(lapply(A, polygon)) 139 | invisible(lapply(B, polygon)) 140 | E <- polyclip(A, B, closed=FALSE) 141 | invisible(lapply(E, lines, col="purple", lwd=5)) 142 | } 143 | \references{ 144 | Clipper Website: \url{http://www.angusj.com} 145 | 146 | Vatti, B. (1992) A generic solution to polygon clipping. 147 | \emph{Communications of the ACM} \bold{35} (7) 56--63. 148 | \url{https://dl.acm.org/doi/10.1145/129902.129906} 149 | 150 | Agoston, M.K. (2005) 151 | \emph{Computer graphics and geometric modeling: 152 | implementation and algorithms.} 153 | Springer-Verlag. 154 | \url{http://books.google.com/books?q=vatti+clipping+agoston} 155 | 156 | Chen, X. and McMains, S. (2005) 157 | Polygon Offsetting by Computing Winding Numbers. 158 | Paper no. DETC2005-85513 in \emph{Proceedings of IDETC/CIE 2005} 159 | (ASME 2005 International Design Engineering Technical Conferences 160 | and Computers and Information in Engineering Conference), 161 | pp. 565--575 162 | \url{https://mcmains.me.berkeley.edu/pubs/DAC05OffsetPolygon.pdf} 163 | } 164 | \keyword{spatial} 165 | \keyword{math} 166 | -------------------------------------------------------------------------------- /man/polylineoffset.Rd: -------------------------------------------------------------------------------- 1 | \name{polylineoffset} 2 | \alias{polylineoffset} 3 | \title{Polygonal Line Offset} 4 | \description{ 5 | Given a list of polygonal lines, compute the offset region (guard region, 6 | buffer region, morphological dilation) formed by shifting the 7 | boundary outwards by a specified distance. 8 | } 9 | \usage{ 10 | polylineoffset(A, delta, 11 | \dots, 12 | eps, x0, y0, 13 | miterlim=2, arctol=abs(delta)/100, 14 | jointype=c("square", "round", "miter"), 15 | endtype = c("closedpolygon", "closedline", 16 | "openbutt", "opensquare", "openround", 17 | "closed", "butt", "square", "round")) 18 | } 19 | \arguments{ 20 | \item{A}{Data specifying polygons. See Details.} 21 | \item{delta}{Distance over which the boundary should be shifted.} 22 | \item{\dots}{Ignored.} 23 | \item{eps}{Spatial resolution for coordinates.} 24 | \item{x0,y0}{Spatial origin for coordinates.} 25 | \item{miterlim,arctol}{Tolerance parameters: see Details.} 26 | \item{jointype}{ 27 | Type of join operation to be performed at each vertex. 28 | See Details. 29 | } 30 | \item{endtype}{ 31 | Type of geometrical operation to be performed at the start and end 32 | of each line. See Details. 33 | } 34 | } 35 | \value{ 36 | Data specifying polygons, in the same format as \code{A}. 37 | } 38 | \details{ 39 | This is part of an interface to the polygon-clipping library 40 | \code{Clipper} written by Angus Johnson. 41 | 42 | Given a list of polygonal lines \code{A}, 43 | the function \code{polylineoffset} computes the offset region 44 | (also known as the morphological dilation, guard region, 45 | buffer region, etc) obtained by shifting the boundary of \code{A} 46 | outward by the distance \code{delta}. 47 | 48 | The argument \code{A} represents a polygonal line (broken line) 49 | or a list of broken lines. The format is either 50 | \itemize{ 51 | \item 52 | a list containing two components \code{x} and \code{y} 53 | giving the coordinates of successive vertices of the broken line. 54 | \item 55 | a \code{list} of \code{list(x,y)} structures giving 56 | the coordinates of the vertices of several broken lines. 57 | } 58 | Lines may be self-intersecting and different lines may intersect each other. 59 | Note that calculations are performed in integer arithmetic: see below. 60 | 61 | The argument \code{jointype} determines what happens at the vertices 62 | of each line. See the Examples for illustrations. 63 | \itemize{ 64 | \item \code{jointype="round"}: a circular arc is generated. 65 | \item \code{jointype="square"}: the circular arc is 66 | replaced by a single straight line. 67 | \item \code{jointype="miter"}: the circular arc is 68 | omitted entirely, or replaced by a single straight line. 69 | } 70 | The argument \code{endtype} determines what happens at the beginning 71 | and end of each line. See the Examples for illustrations. 72 | \itemize{ 73 | \item \code{endtype="closedpolygon"}: ends are joined together (using the 74 | \code{jointype} value) and the path filled as a polygon. 75 | \item \code{endtype="closedline"}: ends are joined together (using the 76 | \code{jointype} value) and the path is filled as a polyline. 77 | \item \code{endtype="openbutt"}: ends are squared off abruptly. 78 | \item \code{endtype="opensquare"}: 79 | ends are squared off at distance \code{delta}. 80 | \item \code{endtype="openround"}: ends are replaced by a semicircular arc. 81 | } 82 | The values \code{endtype="closed"}, \code{"butt"}, \code{"square"} 83 | and \code{"round"} are deprecated; they are 84 | equivalent to \code{endtype="closedpolygon"}, 85 | \code{"openbutt"}, \code{"opensquare"} and \code{"openround"} 86 | respectively. 87 | 88 | The arguments \code{miterlim} and \code{arctol} are tolerances. 89 | \itemize{ 90 | \item if \code{jointype="round"}, then \code{arctol} is the maximum 91 | permissible distance between the true circular arc and its 92 | discretised approximation. 93 | \item if \code{jointype="miter"}, then \code{miterlimit * delta} 94 | is the maximum permissible displacement between the original vertex and the 95 | corresponding offset vertex if the circular arc were to be 96 | omitted entirely. The default is \code{miterlimit=2} 97 | which is also the minimum value. 98 | } 99 | 100 | 101 | \bold{Calculations are performed in integer arithmetic} 102 | after subtracting \code{x0,y0} from the coordinates, 103 | dividing by \code{eps}, and rounding to the nearest integer. 104 | Thus, \code{eps} is the effective spatial resolution. 105 | The default values ensure reasonable accuracy. 106 | } 107 | \author{Angus Johnson. 108 | Ported to \R by Adrian Baddeley 109 | \email{Adrian.Baddeley@curtin.edu.au}. 110 | } 111 | \seealso{ 112 | \code{\link{polyoffset}}, 113 | \code{\link{polysimplify}}, 114 | \code{\link{polyclip}}, 115 | \code{\link{polyminkowski}} 116 | } 117 | \examples{ 118 | A <- list(list(x=c(4,8,8,2,6), y=c(3,3,8,8,6))) 119 | 120 | plot(c(0,10),c(0,10), type="n", 121 | main="jointype=square, endtype=opensquare", 122 | axes=FALSE, xlab="", ylab="") 123 | lines(A[[1]], col="grey", lwd=3) 124 | C <- polylineoffset(A, 0.5, jointype="square", endtype="opensquare") 125 | polygon(C[[1]], lwd=3, border="blue") 126 | 127 | plot(c(0,10),c(0,10), type="n", 128 | main="jointype=round, endtype=openround", 129 | axes=FALSE, xlab="", ylab="") 130 | lines(A[[1]], col="grey", lwd=3) 131 | C <- polylineoffset(A, 0.5, jointype="round", endtype="openround") 132 | polygon(C[[1]], lwd=3, border="blue") 133 | } 134 | \references{ 135 | Clipper Website: \url{http://www.angusj.com} 136 | 137 | Vatti, B. (1992) A generic solution to polygon clipping. 138 | \emph{Communications of the ACM} \bold{35} (7) 56--63. 139 | \url{https://dl.acm.org/doi/10.1145/129902.129906} 140 | 141 | Agoston, M.K. (2005) 142 | \emph{Computer graphics and geometric modeling: 143 | implementation and algorithms.} 144 | Springer-Verlag. 145 | \url{http://books.google.com/books?q=vatti+clipping+agoston} 146 | 147 | Chen, X. and McMains, S. (2005) 148 | Polygon Offsetting by Computing Winding Numbers. 149 | Paper no. DETC2005-85513 in \emph{Proceedings of IDETC/CIE 2005} 150 | (ASME 2005 International Design Engineering Technical Conferences 151 | and Computers and Information in Engineering Conference), 152 | pp. 565--575 153 | \url{https://mcmains.me.berkeley.edu/pubs/DAC05OffsetPolygon.pdf} 154 | } 155 | \keyword{spatial} 156 | \keyword{math} 157 | -------------------------------------------------------------------------------- /R/clipper.R: -------------------------------------------------------------------------------- 1 | # 2 | # clipper.R 3 | # 4 | # Interface to Clipper C++ code 5 | # 6 | # $Revision: 1.15 $ $Date: 2018/06/14 02:02:36 $ 7 | # 8 | 9 | validxy <- function(P) { 10 | is.list(P) && all(c("x","y") %in% names(P)) && 11 | is.vector(P$x) && is.vector(P$y) && length(P$x)==length(P$y) 12 | } 13 | 14 | validpoly <- function(P) { 15 | is.list(P) && all(unlist(lapply(P, validxy))) 16 | } 17 | 18 | xrange <- function(z) { range(z$x) } 19 | yrange <- function(z) { range(z$y) } 20 | 21 | ensurexydouble <- function(P) lapply(P[c("x", "y")], 22 | "storage.mode<-", value="double") 23 | 24 | ensuredouble <- function(A) lapply(A, ensurexydouble) 25 | 26 | aspolygonlist <- function(A) lapply(A, "names<-", value=c("x", "y")) 27 | 28 | polysimplify <- 29 | function(A, 30 | ..., 31 | eps, x0, y0, 32 | filltype=c("evenodd", "nonzero", "positive", "negative") 33 | ) { 34 | # validate parameters and convert to integer codes 35 | filltype <- match.arg(filltype) 36 | pft <- match(filltype, c("evenodd", "nonzero", "positive", "negative")) 37 | # validate polygon 38 | if(!validpoly(A)) { 39 | if(validxy(A)) A <- list(A) else 40 | stop("Argument A should be a list of lists, each containing vectors x,y") 41 | } 42 | # determine value of 'eps' if missing 43 | if(missing(eps) || missing(x0) || missing(y0)) { 44 | xr <- range(range(unlist(lapply(A, xrange)))) 45 | yr <- range(range(unlist(lapply(A, yrange)))) 46 | if(missing(eps)) eps <- max(diff(xr), diff(yr))/1e9 47 | if(missing(x0)) x0 <- mean(xr) 48 | if(missing(y0)) y0 <- mean(yr) 49 | } 50 | # call clipper library on each component path 51 | result <- list() 52 | A <- ensuredouble(A) 53 | storage.mode(pft) <- "integer" 54 | storage.mode(x0) <- storage.mode(y0) <- storage.mode(eps) <- "double" 55 | result <- .Call(eCsimplify, 56 | A, pft, x0, y0, eps, 57 | PACKAGE="polyclip") 58 | return(aspolygonlist(result)) 59 | } 60 | 61 | polyclip <- 62 | function(A, B, 63 | op=c("intersection", "union", "minus", "xor"), 64 | ..., 65 | eps, x0, y0, 66 | fillA=c("evenodd", "nonzero", "positive", "negative"), 67 | fillB=c("evenodd", "nonzero", "positive", "negative"), 68 | closed=TRUE 69 | ) { 70 | # validate parameters and convert to integer codes 71 | op <- match.arg(op) 72 | fillA <- match.arg(fillA) 73 | fillB <- match.arg(fillB) 74 | ct <- match(op, c("intersection", "union", "minus", "xor")) 75 | pftA <- match(fillA, c("evenodd", "nonzero", "positive", "negative")) 76 | pftB <- match(fillB, c("evenodd", "nonzero", "positive", "negative")) 77 | # validate polygons and rescale 78 | if(!validpoly(A)) { 79 | if(validxy(A)) A <- list(A) else 80 | stop("Argument A should be a list of lists, each containing vectors x,y") 81 | } 82 | if(!validpoly(B)) { 83 | if(validxy(B)) B <- list(B) else 84 | stop("Argument B should be a list of lists, each containing vectors x,y") 85 | } 86 | # 87 | # determine value of 'eps' if missing 88 | if(missing(eps) || missing(x0) || missing(y0)) { 89 | xr <- range(range(unlist(lapply(A, xrange))), 90 | range(unlist(lapply(B, xrange)))) 91 | yr <- range(range(unlist(lapply(A, yrange))), 92 | range(unlist(lapply(B, yrange)))) 93 | if(missing(eps)) eps <- max(diff(xr), diff(yr))/1e9 94 | if(missing(x0)) x0 <- mean(xr) 95 | if(missing(y0)) y0 <- mean(yr) 96 | } 97 | # call clipper library 98 | A <- ensuredouble(A) 99 | B <- ensuredouble(B) 100 | storage.mode(ct) <- storage.mode(pftA) <- storage.mode(pftB) <- "integer" 101 | storage.mode(x0) <- storage.mode(y0) <- storage.mode(eps) <- "double" 102 | storage.mode(closed) <- "logical" 103 | ans <- .Call(eCclipbool, 104 | A, B, pftA, pftB, ct, 105 | x0, y0, eps, closed, 106 | PACKAGE="polyclip") 107 | return(aspolygonlist(ans)) 108 | } 109 | 110 | polyoffset <- 111 | function(A, delta, 112 | ..., 113 | eps, x0, y0, 114 | miterlim=2, arctol=abs(delta)/100, 115 | jointype = c("square", "round", "miter") 116 | ) { 117 | # validate parameters and convert to integer codes 118 | jointype <- match.arg(jointype) 119 | jt <- match(jointype, c("square", "round", "miter")) 120 | # validate polygons and rescale 121 | if(!validpoly(A)) { 122 | if(validxy(A)) A <- list(A) else 123 | stop("Argument A should be a list of lists, each containing vectors x,y") 124 | } 125 | # determine value of 'eps' if missing 126 | if(missing(eps) || missing(x0) || missing(y0)) { 127 | xr <- range(unlist(lapply(A, xrange))) 128 | yr <- range(unlist(lapply(A, yrange))) 129 | if(missing(eps)) eps <- max(diff(xr), diff(yr))/1e9 130 | if(missing(x0)) x0 <- mean(xr) 131 | if(missing(y0)) y0 <- mean(yr) 132 | } 133 | # arc tolerance 134 | arctol <- max(eps/4, arctol) 135 | # call clipper library 136 | A <- ensuredouble(A) 137 | storage.mode(jt) <- "integer" 138 | storage.mode(delta) <- 139 | storage.mode(miterlim) <- storage.mode(arctol) <- "double" 140 | storage.mode(x0) <- storage.mode(y0) <- storage.mode(eps) <- "double" 141 | ans <- .Call(eCpolyoffset, 142 | A, delta, jt, 143 | miterlim, arctol, x0, y0, eps, 144 | PACKAGE="polyclip") 145 | return(aspolygonlist(ans)) 146 | } 147 | 148 | 149 | polylineoffset <- 150 | function(A, delta, 151 | ..., 152 | eps, x0, y0, 153 | miterlim=2, arctol=abs(delta)/100, 154 | jointype = c("square", "round", "miter"), 155 | endtype = c("closedpolygon", "closedline", 156 | "openbutt", "opensquare", "openround", 157 | "closed", "butt", "square", "round") 158 | ) { 159 | ## validate parameters and convert to integer codes 160 | jointype <- match.arg(jointype) 161 | jt <- match(jointype, c("square", "round", "miter")) 162 | 163 | endtype <- match.arg(endtype) 164 | if(endtype == "closed") endtype <- "closedpolygon" 165 | if(endtype %in% c("butt", "square", "round")) 166 | endtype <- paste0("open", endtype) 167 | et <- match(endtype, c("closedpolygon", "closedline", 168 | "openbutt", "opensquare", "openround")) 169 | 170 | ## validate polygons and rescale 171 | if(!validpoly(A)) { 172 | if(validxy(A)) A <- list(A) else 173 | stop("Argument A should be a list of lists, each containing vectors x,y") 174 | } 175 | ## determine value of 'eps' if missing 176 | if(missing(eps) || missing(x0) || missing(y0)) { 177 | xr <- range(unlist(lapply(A, xrange))) 178 | yr <- range(unlist(lapply(A, yrange))) 179 | if(missing(eps)) eps <- max(diff(xr), diff(yr))/1e9 180 | if(missing(x0)) x0 <- mean(xr) 181 | if(missing(y0)) y0 <- mean(yr) 182 | } 183 | # arc tolerance 184 | arctol <- max(eps/4, arctol) 185 | # call clipper library 186 | A <- ensuredouble(A) 187 | storage.mode(jt) <- storage.mode(et) <- "integer" 188 | storage.mode(delta) <- storage.mode(miterlim) <- 189 | storage.mode(arctol) <- "double" 190 | storage.mode(x0) <- storage.mode(y0) <- storage.mode(eps) <- "double" 191 | ans <- .Call(eClineoffset, 192 | A, delta, jt, et, 193 | miterlim, arctol, x0, y0, eps, 194 | PACKAGE="polyclip") 195 | return(aspolygonlist(ans)) 196 | } 197 | 198 | polyminkowski <- 199 | function(A, B, 200 | ..., 201 | eps, x0, y0, 202 | closed=TRUE 203 | ) { 204 | # validate parameters and convert to integer codes 205 | closed <- as.logical(closed) 206 | # validate polygons/paths 207 | if(!validpoly(A)) { 208 | if(validxy(A)) A <- list(A) else 209 | stop("Argument A should be a list of lists, each containing vectors x,y") 210 | } 211 | if(length(A) > 1) 212 | stop("Not implemented when A consists of more than one polygon") 213 | if(!validpoly(B)) { 214 | if(validxy(B)) B <- list(B) else 215 | stop("Argument B should be a list of lists, each containing vectors x,y") 216 | } 217 | # determine value of 'eps' if missing 218 | if(missing(eps) || missing(x0) || missing(y0)) { 219 | xr <- range(range(unlist(lapply(A, xrange)))) 220 | yr <- range(range(unlist(lapply(A, yrange)))) 221 | xr <- range(xr, range(unlist(lapply(B, xrange)))) 222 | yr <- range(yr, range(unlist(lapply(B, yrange)))) 223 | if(missing(eps)) eps <- max(diff(xr), diff(yr))/1e9 224 | if(missing(x0)) x0 <- xr[1] 225 | if(missing(y0)) y0 <- yr[1] - diff(yr)/16 226 | } 227 | # call clipper library on each component path 228 | A <- ensuredouble(A) 229 | B <- ensuredouble(B) 230 | storage.mode(x0) <- storage.mode(y0) <- storage.mode(eps) <- "double" 231 | storage.mode(closed) <- "logical" 232 | result <- .Call(eCminksum, 233 | A, B, closed, x0, y0, eps, 234 | PACKAGE="polyclip") 235 | return(aspolygonlist(result)) 236 | } 237 | 238 | pointinpolygon <- 239 | function(P, A, eps, x0, y0) { 240 | # validate arguments 241 | if(!validxy(P)) 242 | stop("Argument P should be a list containing vectors x,y") 243 | if(!validxy(A)) 244 | stop("Argument A should be a list containing vectors x,y") 245 | # determine value of 'eps' if missing 246 | if(missing(eps) || missing(x0) || missing(y0)) { 247 | xr <- xrange(A) 248 | yr <- yrange(A) 249 | if(missing(eps)) eps <- max(diff(xr), diff(yr))/1e9 250 | if(missing(x0)) x0 <- mean(xr) 251 | if(missing(y0)) y0 <- mean(yr) 252 | } 253 | # call clipper library 254 | A <- ensurexydouble(A) 255 | P <- ensurexydouble(P) 256 | storage.mode(x0) <- storage.mode(y0) <- storage.mode(eps) <- "double" 257 | ans <- .Call(eCpiptest, 258 | P, A, 259 | x0, y0, eps, 260 | PACKAGE="polyclip") 261 | return(ans) 262 | } 263 | -------------------------------------------------------------------------------- /src/clipper.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * * 3 | * Author : Angus Johnson * 4 | * Version : 6.4.0 * 5 | * Date : 2 July 2015 * 6 | * Website : http://www.angusj.com * 7 | * Copyright : Angus Johnson 2010-2015 * 8 | * * 9 | * License: * 10 | * Use, modification & distribution is subject to Boost Software License Ver 1. * 11 | * http://www.boost.org/LICENSE_1_0.txt * 12 | * * 13 | * Attributions: * 14 | * The code in this library is an extension of Bala Vatti's clipping algorithm: * 15 | * "A generic solution to polygon clipping" * 16 | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 17 | * http://portal.acm.org/citation.cfm?id=129906 * 18 | * * 19 | * Computer graphics and geometric modeling: implementation and algorithms * 20 | * By Max K. Agoston * 21 | * Springer; 1 edition (January 4, 2005) * 22 | * http://books.google.com/books?q=vatti+clipping+agoston * 23 | * * 24 | * See also: * 25 | * "Polygon Offsetting by Computing Winding Numbers" * 26 | * Paper no. DETC2005-85513 pp. 565-575 * 27 | * ASME 2005 International Design Engineering Technical Conferences * 28 | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 29 | * September 24-28, 2005 , Long Beach, California, USA * 30 | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 31 | * * 32 | *******************************************************************************/ 33 | 34 | /**************************************************************************** 35 | * * 36 | * Modified by Adrian Baddeley * 37 | * for inclusion in an 'R' package * 38 | * * 39 | * Alterations are switched on/off by variable 'R_PACKAGE' * 40 | * Alterations are marked by 'ajb' * 41 | ****************************************************************************/ 42 | 43 | // ajb start 44 | #define R_PACKAGE 45 | // ajb end 46 | 47 | #ifndef clipper_hpp 48 | #define clipper_hpp 49 | 50 | #define CLIPPER_VERSION "6.2.6" 51 | 52 | //use_int32: When enabled 32bit ints are used instead of 64bit ints. This 53 | //improve performance but coordinate values are limited to the range +/- 46340 54 | //#define use_int32 55 | 56 | //use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. 57 | //#define use_xyz 58 | 59 | //use_lines: Enables line clipping. Adds a very minor cost to performance. 60 | #define use_lines 61 | 62 | //use_deprecated: Enables temporary support for the obsolete functions 63 | //#define use_deprecated 64 | 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | 75 | namespace ClipperLib { 76 | 77 | enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; 78 | enum PolyType { ptSubject, ptClip }; 79 | //By far the most widely used winding rules for polygon filling are 80 | //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 81 | //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 82 | //see http://glprogramming.com/red/chapter11.html 83 | enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; 84 | 85 | // ajb start 86 | #ifndef POLYCLIP_LONG64 87 | // This should not happen!! Probably the configure file was not executed 88 | // Fall back to 32 bits 89 | #ifndef use_32 90 | #define use_32 91 | #endif 92 | #endif 93 | // ajb end 94 | 95 | #ifdef use_int32 96 | typedef int cInt; 97 | static cInt const loRange = 0x7FFF; 98 | static cInt const hiRange = 0x7FFF; 99 | #else 100 | // ajb start 101 | // 64-bit types in were found using a configuration script 102 | #include 103 | typedef POLYCLIP_LONG64 cInt; 104 | typedef POLYCLIP_LONG64 long64; 105 | typedef POLYCLIP_ULONG64 ulong64; 106 | static cInt const loRange = 0x3FFFFFFF; 107 | static cInt const hiRange = (((cInt) 0x3FFFFFFF) << 32 | 0xFFFFFFFF); 108 | // was: static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; 109 | // ajb end 110 | #endif 111 | 112 | struct IntPoint { 113 | cInt X; 114 | cInt Y; 115 | #ifdef use_xyz 116 | cInt Z; 117 | IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; 118 | #else 119 | IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; 120 | #endif 121 | 122 | friend inline bool operator== (const IntPoint& a, const IntPoint& b) 123 | { 124 | return a.X == b.X && a.Y == b.Y; 125 | } 126 | friend inline bool operator!= (const IntPoint& a, const IntPoint& b) 127 | { 128 | return a.X != b.X || a.Y != b.Y; 129 | } 130 | }; 131 | //------------------------------------------------------------------------------ 132 | 133 | typedef std::vector< IntPoint > Path; 134 | typedef std::vector< Path > Paths; 135 | 136 | inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} 137 | inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} 138 | 139 | #ifndef R_PACKAGE 140 | std::ostream& operator <<(std::ostream &s, const IntPoint &p); 141 | std::ostream& operator <<(std::ostream &s, const Path &p); 142 | std::ostream& operator <<(std::ostream &s, const Paths &p); 143 | #endif 144 | 145 | struct DoublePoint 146 | { 147 | double X; 148 | double Y; 149 | DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} 150 | DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} 151 | }; 152 | //------------------------------------------------------------------------------ 153 | 154 | #ifdef use_xyz 155 | typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); 156 | #endif 157 | 158 | enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; 159 | enum JoinType {jtSquare, jtRound, jtMiter}; 160 | enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; 161 | 162 | class PolyNode; 163 | typedef std::vector< PolyNode* > PolyNodes; 164 | 165 | class PolyNode 166 | { 167 | public: 168 | PolyNode(); 169 | virtual ~PolyNode(){}; 170 | Path Contour; 171 | PolyNodes Childs; 172 | PolyNode* Parent; 173 | PolyNode* GetNext() const; 174 | bool IsHole() const; 175 | bool IsOpen() const; 176 | int ChildCount() const; 177 | private: 178 | unsigned Index; //node index in Parent.Childs 179 | bool m_IsOpen; 180 | JoinType m_jointype; 181 | EndType m_endtype; 182 | PolyNode* GetNextSiblingUp() const; 183 | void AddChild(PolyNode& child); 184 | friend class Clipper; //to access Index 185 | friend class ClipperOffset; 186 | }; 187 | 188 | class PolyTree: public PolyNode 189 | { 190 | public: 191 | ~PolyTree(){Clear();}; 192 | PolyNode* GetFirst() const; 193 | void Clear(); 194 | int Total() const; 195 | private: 196 | PolyNodes AllNodes; 197 | friend class Clipper; //to access AllNodes 198 | }; 199 | 200 | bool Orientation(const Path &poly); 201 | double Area(const Path &poly); 202 | int PointInPolygon(const IntPoint &pt, const Path &path); 203 | 204 | void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 205 | void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); 206 | void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); 207 | 208 | void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); 209 | void CleanPolygon(Path& poly, double distance = 1.415); 210 | void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); 211 | void CleanPolygons(Paths& polys, double distance = 1.415); 212 | 213 | void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); 214 | void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); 215 | void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); 216 | 217 | void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); 218 | void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); 219 | void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); 220 | 221 | void ReversePath(Path& p); 222 | void ReversePaths(Paths& p); 223 | 224 | struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; 225 | 226 | //enums that are used internally ... 227 | enum EdgeSide { esLeft = 1, esRight = 2}; 228 | 229 | //forward declarations (for stuff used internally) ... 230 | struct TEdge; 231 | struct IntersectNode; 232 | struct LocalMinimum; 233 | struct OutPt; 234 | struct OutRec; 235 | struct Join; 236 | 237 | typedef std::vector < OutRec* > PolyOutList; 238 | typedef std::vector < TEdge* > EdgeList; 239 | typedef std::vector < Join* > JoinList; 240 | typedef std::vector < IntersectNode* > IntersectList; 241 | 242 | //------------------------------------------------------------------------------ 243 | 244 | //ClipperBase is the ancestor to the Clipper class. It should not be 245 | //instantiated directly. This class simply abstracts the conversion of sets of 246 | //polygon coordinates into edge objects that are stored in a LocalMinima list. 247 | class ClipperBase 248 | { 249 | public: 250 | ClipperBase(); 251 | virtual ~ClipperBase(); 252 | virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); 253 | bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); 254 | virtual void Clear(); 255 | IntRect GetBounds(); 256 | bool PreserveCollinear() {return m_PreserveCollinear;}; 257 | void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; 258 | protected: 259 | void DisposeLocalMinimaList(); 260 | TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); 261 | virtual void Reset(); 262 | TEdge* ProcessBound(TEdge* E, bool IsClockwise); 263 | void InsertScanbeam(const cInt Y); 264 | bool PopScanbeam(cInt &Y); 265 | bool LocalMinimaPending(); 266 | bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); 267 | OutRec* CreateOutRec(); 268 | void DisposeAllOutRecs(); 269 | void DisposeOutRec(PolyOutList::size_type index); 270 | void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); 271 | void DeleteFromAEL(TEdge *e); 272 | void UpdateEdgeIntoAEL(TEdge *&e); 273 | 274 | typedef std::vector MinimaList; 275 | MinimaList::iterator m_CurrentLM; 276 | MinimaList m_MinimaList; 277 | 278 | bool m_UseFullRange; 279 | EdgeList m_edges; 280 | bool m_PreserveCollinear; 281 | bool m_HasOpenPaths; 282 | PolyOutList m_PolyOuts; 283 | TEdge *m_ActiveEdges; 284 | 285 | typedef std::priority_queue ScanbeamList; 286 | ScanbeamList m_Scanbeam; 287 | }; 288 | //------------------------------------------------------------------------------ 289 | 290 | class Clipper : public virtual ClipperBase 291 | { 292 | public: 293 | Clipper(int initOptions = 0); 294 | bool Execute(ClipType clipType, 295 | Paths &solution, 296 | PolyFillType fillType = pftEvenOdd); 297 | bool Execute(ClipType clipType, 298 | Paths &solution, 299 | PolyFillType subjFillType, 300 | PolyFillType clipFillType); 301 | bool Execute(ClipType clipType, 302 | PolyTree &polytree, 303 | PolyFillType fillType = pftEvenOdd); 304 | bool Execute(ClipType clipType, 305 | PolyTree &polytree, 306 | PolyFillType subjFillType, 307 | PolyFillType clipFillType); 308 | bool ReverseSolution() { return m_ReverseOutput; }; 309 | void ReverseSolution(bool value) {m_ReverseOutput = value;}; 310 | bool StrictlySimple() {return m_StrictSimple;}; 311 | void StrictlySimple(bool value) {m_StrictSimple = value;}; 312 | //set the callback function for z value filling on intersections (otherwise Z is 0) 313 | #ifdef use_xyz 314 | void ZFillFunction(ZFillCallback zFillFunc); 315 | #endif 316 | protected: 317 | virtual bool ExecuteInternal(); 318 | private: 319 | JoinList m_Joins; 320 | JoinList m_GhostJoins; 321 | IntersectList m_IntersectList; 322 | ClipType m_ClipType; 323 | typedef std::list MaximaList; 324 | MaximaList m_Maxima; 325 | TEdge *m_SortedEdges; 326 | bool m_ExecuteLocked; 327 | PolyFillType m_ClipFillType; 328 | PolyFillType m_SubjFillType; 329 | bool m_ReverseOutput; 330 | bool m_UsingPolyTree; 331 | bool m_StrictSimple; 332 | #ifdef use_xyz 333 | ZFillCallback m_ZFill; //custom callback 334 | #endif 335 | void SetWindingCount(TEdge& edge); 336 | bool IsEvenOddFillType(const TEdge& edge) const; 337 | bool IsEvenOddAltFillType(const TEdge& edge) const; 338 | void InsertLocalMinimaIntoAEL(const cInt botY); 339 | void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); 340 | void AddEdgeToSEL(TEdge *edge); 341 | bool PopEdgeFromSEL(TEdge *&edge); 342 | void CopyAELToSEL(); 343 | void DeleteFromSEL(TEdge *e); 344 | void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); 345 | bool IsContributing(const TEdge& edge) const; 346 | bool IsTopHorz(const cInt XPos); 347 | void DoMaxima(TEdge *e); 348 | void ProcessHorizontals(); 349 | void ProcessHorizontal(TEdge *horzEdge); 350 | void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 351 | OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); 352 | OutRec* GetOutRec(int idx); 353 | void AppendPolygon(TEdge *e1, TEdge *e2); 354 | void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); 355 | OutPt* AddOutPt(TEdge *e, const IntPoint &pt); 356 | OutPt* GetLastOutPt(TEdge *e); 357 | bool ProcessIntersections(const cInt topY); 358 | void BuildIntersectList(const cInt topY); 359 | void ProcessIntersectList(); 360 | void ProcessEdgesAtTopOfScanbeam(const cInt topY); 361 | void BuildResult(Paths& polys); 362 | void BuildResult2(PolyTree& polytree); 363 | void SetHoleState(TEdge *e, OutRec *outrec); 364 | void DisposeIntersectNodes(); 365 | bool FixupIntersectionOrder(); 366 | void FixupOutPolygon(OutRec &outrec); 367 | void FixupOutPolyline(OutRec &outrec); 368 | bool IsHole(TEdge *e); 369 | bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); 370 | void FixHoleLinkage(OutRec &outrec); 371 | void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); 372 | void ClearJoins(); 373 | void ClearGhostJoins(); 374 | void AddGhostJoin(OutPt *op, const IntPoint offPt); 375 | bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); 376 | void JoinCommonEdges(); 377 | void DoSimplePolygons(); 378 | void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); 379 | void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); 380 | void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); 381 | #ifdef use_xyz 382 | void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); 383 | #endif 384 | }; 385 | //------------------------------------------------------------------------------ 386 | 387 | class ClipperOffset 388 | { 389 | public: 390 | ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); 391 | ~ClipperOffset(); 392 | void AddPath(const Path& path, JoinType joinType, EndType endType); 393 | void AddPaths(const Paths& paths, JoinType joinType, EndType endType); 394 | void Execute(Paths& solution, double delta); 395 | void Execute(PolyTree& solution, double delta); 396 | void Clear(); 397 | double MiterLimit; 398 | double ArcTolerance; 399 | private: 400 | Paths m_destPolys; 401 | Path m_srcPoly; 402 | Path m_destPoly; 403 | std::vector m_normals; 404 | double m_delta, m_sinA, m_sin, m_cos; 405 | double m_miterLim, m_StepsPerRad; 406 | IntPoint m_lowest; 407 | PolyNode m_polyNodes; 408 | 409 | void FixOrientations(); 410 | void DoOffset(double delta); 411 | void OffsetPoint(int j, int& k, JoinType jointype); 412 | void DoSquare(int j, int k); 413 | void DoMiter(int j, int k, double r); 414 | void DoRound(int j, int k); 415 | }; 416 | //------------------------------------------------------------------------------ 417 | 418 | #ifndef R_PACKAGE 419 | class clipperException : public std::exception 420 | { 421 | public: 422 | clipperException(const char* description): m_descr(description) {} 423 | virtual ~clipperException() throw() {} 424 | virtual const char* what() const throw() {return m_descr.c_str();} 425 | private: 426 | std::string m_descr; 427 | }; 428 | #endif 429 | //------------------------------------------------------------------------------ 430 | 431 | } //ClipperLib namespace 432 | 433 | #endif //clipper_hpp 434 | 435 | 436 | -------------------------------------------------------------------------------- /src/interface.cpp: -------------------------------------------------------------------------------- 1 | #include "clipper.h" 2 | #include 3 | #include 4 | 5 | #define ERREUR(MESSAGE) Rf_error(MESSAGE) 6 | 7 | using namespace std; 8 | using namespace ClipperLib; 9 | 10 | void CopyToPath(int *x, int *y, int n, ClipperLib::Path &p) 11 | { 12 | p.clear(); 13 | p.reserve(n); 14 | for (int i = 0; i < n; i++) 15 | p.push_back(IntPoint(x[i], y[i])); 16 | } 17 | 18 | void CopyFromPath(ClipperLib::Path &p, int *x, int *y, int nmax, int *n) 19 | { 20 | int N; 21 | *n = N = p.size(); 22 | if(N <= nmax) { 23 | for (int i = 0; i < N; i++) 24 | { 25 | x[i] = p[i].X; 26 | y[i] = p[i].Y; 27 | } 28 | } 29 | } 30 | 31 | void ScaleToPath(double *x, double *y, int n, ClipperLib::Path &p, 32 | double x0, double y0, double eps) 33 | { 34 | int i; 35 | cInt cxi, cyi; 36 | p.clear(); 37 | p.reserve(n); 38 | for (i = 0; i < n; i++) { 39 | cxi = (cInt) ((x[i] - x0)/eps); 40 | cyi = (cInt) ((y[i] - y0)/eps); 41 | p.push_back(IntPoint(cxi, cyi)); 42 | } 43 | } 44 | 45 | void ScaleFromPath(ClipperLib::Path &p, double *x, double *y, int nmax, int *n, 46 | double x0, double y0, double eps) 47 | { 48 | int N; 49 | *n = N = p.size(); 50 | if(N <= nmax) { 51 | for (int i = 0; i < N; i++) 52 | { 53 | x[i] = x0 + eps * ((double) p[i].X); 54 | y[i] = y0 + eps * ((double) p[i].Y); 55 | } 56 | } 57 | } 58 | 59 | void ScaleToPoint(double x, double y, ClipperLib::IntPoint &p, 60 | double x0, double y0, double eps) 61 | { 62 | p.X = (cInt) ((x - x0)/eps); 63 | p.Y = (cInt) ((y - y0)/eps); 64 | } 65 | 66 | extern "C" { 67 | SEXP Csimplify(SEXP A, 68 | SEXP pft, 69 | SEXP X0, 70 | SEXP Y0, 71 | SEXP Eps) { 72 | int nA, i, nAi, m, mi, mitrue; 73 | double *x, *y, *xx, *yy; 74 | SEXP Ai = R_NilValue; 75 | SEXP out, outi, xouti, youti; 76 | int pftcode; 77 | PolyFillType filltype; 78 | double x0, y0, eps; 79 | 80 | // protect arguments from garbage collector 81 | PROTECT(A = AS_LIST(A)); 82 | PROTECT(pft = AS_INTEGER(pft)); 83 | PROTECT(X0 = AS_NUMERIC(X0)); 84 | PROTECT(Y0 = AS_NUMERIC(Y0)); 85 | PROTECT(Eps = AS_NUMERIC(Eps)); 86 | // that's 5 arguments 87 | 88 | // number of polygons 89 | nA = LENGTH(A); 90 | 91 | // Initialise object containing n polygons 92 | Paths polyA(nA); 93 | 94 | // Get scale parameters 95 | x0 = *(NUMERIC_POINTER(X0)); 96 | y0 = *(NUMERIC_POINTER(Y0)); 97 | eps = *(NUMERIC_POINTER(Eps)); 98 | 99 | // copy data 100 | for(i = 0; i < nA; i++) { 101 | Ai = VECTOR_ELT(A, i); 102 | nAi = LENGTH(VECTOR_ELT(Ai, 0)); 103 | x = NUMERIC_POINTER(VECTOR_ELT(Ai, 0)); 104 | y = NUMERIC_POINTER(VECTOR_ELT(Ai, 1)); 105 | ScaleToPath(x, y, nAi, polyA[i], x0, y0, eps); 106 | } 107 | 108 | // interpret clipping parameters 109 | pftcode = *(INTEGER_POINTER(pft)); 110 | switch(pftcode) { 111 | case 1: 112 | filltype = pftEvenOdd; 113 | break; 114 | case 2: 115 | filltype = pftNonZero; 116 | break; 117 | case 3: 118 | filltype = pftPositive; 119 | break; 120 | case 4: 121 | filltype = pftNegative; 122 | break; 123 | default: 124 | ERREUR("polyclip: unrecognised code for fill type A"); 125 | } 126 | 127 | // simplify polygon; 128 | Paths result; 129 | SimplifyPolygons(polyA, result, filltype); 130 | 131 | // number of polygons 132 | m = result.size(); 133 | 134 | // initialise output list 135 | PROTECT(out = NEW_LIST(m)); 136 | 137 | // copy data 138 | if(m > 0) { 139 | for(i = 0; i < m; i++) { 140 | mi = result[i].size(); 141 | // Allocate space for output 142 | PROTECT(outi = NEW_LIST(2)); 143 | PROTECT(xouti = NEW_NUMERIC(mi)); 144 | PROTECT(youti = NEW_NUMERIC(mi)); 145 | xx = NUMERIC_POINTER(xouti); 146 | yy = NUMERIC_POINTER(youti); 147 | // copy to output space 148 | ScaleFromPath(result[i], xx, yy, mi, &mitrue, x0, y0, eps); 149 | // Put vectors into list 150 | SET_VECTOR_ELT(outi, 0, xouti); 151 | SET_VECTOR_ELT(outi, 1, youti); 152 | SET_VECTOR_ELT(out, i, outi); 153 | } 154 | } 155 | 156 | UNPROTECT(6 + 3*m); // 5 arguments + out + m * (outi, xouti, youti) 157 | return(out); 158 | } 159 | } 160 | 161 | // ----------------------------------------------------------------- 162 | 163 | extern "C" { 164 | SEXP Cclipbool(SEXP A, 165 | SEXP B, 166 | SEXP pftA, 167 | SEXP pftB, 168 | SEXP ct, 169 | SEXP X0, 170 | SEXP Y0, 171 | SEXP Eps, 172 | SEXP clo // whether paths in A are closed 173 | ){ 174 | int nA, nB, i, n, m, mi, mitrue; 175 | bool closed; 176 | double *x, *y, *xx, *yy; 177 | SEXP Ai = R_NilValue, Bi = R_NilValue; 178 | SEXP out, outi, xouti, youti; 179 | ClipType cliptype; 180 | PolyFillType filltypeA, filltypeB; 181 | int ctcode, pftAcode, pftBcode; 182 | double x0, y0, eps; 183 | 184 | // protect arguments from garbage collector 185 | PROTECT(A = AS_LIST(A)); 186 | PROTECT(B = AS_LIST(B)); 187 | PROTECT(clo = AS_LOGICAL(clo)); 188 | PROTECT(ct = AS_INTEGER(ct)); 189 | PROTECT(pftA = AS_INTEGER(pftA)); 190 | PROTECT(pftB = AS_INTEGER(pftB)); 191 | PROTECT(X0 = AS_NUMERIC(X0)); 192 | PROTECT(Y0 = AS_NUMERIC(Y0)); 193 | PROTECT(Eps = AS_NUMERIC(Eps)); 194 | 195 | // lengths of lists 196 | nA = LENGTH(A); 197 | nB = LENGTH(B); 198 | 199 | // Initialise object containing n polygons 200 | Paths polyA(nA), polyB(nB); 201 | closed = *(LOGICAL_POINTER(clo)); 202 | 203 | // Get scale parameters 204 | x0 = *(NUMERIC_POINTER(X0)); 205 | y0 = *(NUMERIC_POINTER(Y0)); 206 | eps = *(NUMERIC_POINTER(Eps)); 207 | 208 | // copy data 209 | for(i = 0; i < nA; i++) { 210 | Ai = VECTOR_ELT(A, i); 211 | n = LENGTH(VECTOR_ELT(Ai, 0)); 212 | x = NUMERIC_POINTER(VECTOR_ELT(Ai, 0)); 213 | y = NUMERIC_POINTER(VECTOR_ELT(Ai, 1)); 214 | ScaleToPath(x, y, n, polyA[i], x0, y0, eps); 215 | } 216 | for(i = 0; i < nB; i++) { 217 | Bi = VECTOR_ELT(B, i); 218 | n = LENGTH(VECTOR_ELT(Bi, 0)); 219 | x = NUMERIC_POINTER(VECTOR_ELT(Bi, 0)); 220 | y = NUMERIC_POINTER(VECTOR_ELT(Bi, 1)); 221 | ScaleToPath(x, y, n, polyB[i], x0, y0, eps); 222 | } 223 | 224 | // interpret clipping parameters 225 | ctcode = *(INTEGER_POINTER(ct)); 226 | pftAcode = *(INTEGER_POINTER(pftA)); 227 | pftBcode = *(INTEGER_POINTER(pftB)); 228 | switch(ctcode) { 229 | case 1: 230 | cliptype = ctIntersection; 231 | break; 232 | case 2: 233 | cliptype = ctUnion; 234 | break; 235 | case 3: 236 | cliptype = ctDifference; 237 | break; 238 | case 4: 239 | cliptype = ctXor; 240 | break; 241 | default: 242 | ERREUR("polyclip: unrecognised code for cliptype"); 243 | } 244 | switch(pftAcode) { 245 | case 1: 246 | filltypeA = pftEvenOdd; 247 | break; 248 | case 2: 249 | filltypeA = pftNonZero; 250 | break; 251 | case 3: 252 | filltypeA = pftPositive; 253 | break; 254 | case 4: 255 | filltypeA = pftNegative; 256 | break; 257 | default: 258 | ERREUR("polyclip: unrecognised code for fill type A"); 259 | } 260 | switch(pftBcode) { 261 | case 1: 262 | filltypeB = pftEvenOdd; 263 | break; 264 | case 2: 265 | filltypeB = pftNonZero; 266 | break; 267 | case 3: 268 | filltypeB = pftPositive; 269 | break; 270 | case 4: 271 | filltypeB = pftNegative; 272 | break; 273 | default: 274 | ERREUR("polyclip: unrecognised code for fill type B"); 275 | } 276 | 277 | // perform clipping operation 278 | Clipper c; 279 | Paths result; 280 | c.AddPaths(polyA, ptSubject, closed); 281 | c.AddPaths(polyB, ptClip, true); 282 | 283 | if (closed) { 284 | c.Execute(cliptype, result, filltypeA, filltypeB); 285 | } else { 286 | PolyTree polyTreeResult; 287 | c.Execute(cliptype, polyTreeResult, filltypeA, filltypeB); 288 | OpenPathsFromPolyTree(polyTreeResult, result); 289 | } 290 | 291 | // number of polygons 292 | m = result.size(); 293 | 294 | // initialise output list 295 | PROTECT(out = NEW_LIST(m)); 296 | 297 | // copy data 298 | if(m > 0) { 299 | for(i = 0; i < m; i++) { 300 | mi = result[i].size(); 301 | // Allocate space for output 302 | PROTECT(outi = NEW_LIST(2)); 303 | PROTECT(xouti = NEW_NUMERIC(mi)); 304 | PROTECT(youti = NEW_NUMERIC(mi)); 305 | xx = NUMERIC_POINTER(xouti); 306 | yy = NUMERIC_POINTER(youti); 307 | // copy to output space 308 | ScaleFromPath(result[i], xx, yy, mi, &mitrue, x0, y0, eps); 309 | // Put vectors into list 310 | SET_VECTOR_ELT(outi, 0, xouti); 311 | SET_VECTOR_ELT(outi, 1, youti); 312 | SET_VECTOR_ELT(out, i, outi); 313 | } 314 | } 315 | 316 | UNPROTECT(10 + 3*m); // 9 arguments + out + m * (outi, xouti, youti) 317 | return(out); 318 | } 319 | } 320 | 321 | // offset (dilation) operation for closed polygons 322 | 323 | extern "C" { 324 | SEXP Cpolyoffset(SEXP A, 325 | SEXP del, 326 | SEXP jt, 327 | SEXP mlim, 328 | SEXP atol, 329 | SEXP X0, 330 | SEXP Y0, 331 | SEXP Eps 332 | ){ 333 | int nA, i, n, m, mi, mitrue; 334 | double *x, *y, *xx, *yy; 335 | SEXP Ai = R_NilValue; 336 | SEXP out, outi, xouti, youti; 337 | JoinType jointype; 338 | int jtcode; 339 | double delta, miterlimit, arctolerance; 340 | double x0, y0, eps; 341 | 342 | // protect arguments from garbage collector 343 | PROTECT(A = AS_LIST(A)); 344 | PROTECT(del = AS_NUMERIC(del)); 345 | PROTECT(jt = AS_INTEGER(jt)); 346 | PROTECT(mlim = AS_NUMERIC(mlim)); 347 | PROTECT(atol = AS_NUMERIC(atol)); 348 | PROTECT(X0 = AS_NUMERIC(X0)); 349 | PROTECT(Y0 = AS_NUMERIC(Y0)); 350 | PROTECT(Eps = AS_NUMERIC(Eps)); 351 | 352 | // length of list 353 | nA = LENGTH(A); 354 | 355 | // Initialise object containing nA polygons 356 | Paths polyA(nA); 357 | 358 | // Get scale parameters 359 | x0 = *(NUMERIC_POINTER(X0)); 360 | y0 = *(NUMERIC_POINTER(Y0)); 361 | eps = *(NUMERIC_POINTER(Eps)); 362 | 363 | // copy data 364 | for(i = 0; i < nA; i++) { 365 | Ai = VECTOR_ELT(A, i); 366 | n = LENGTH(VECTOR_ELT(Ai, 0)); 367 | x = NUMERIC_POINTER(VECTOR_ELT(Ai, 0)); 368 | y = NUMERIC_POINTER(VECTOR_ELT(Ai, 1)); 369 | ScaleToPath(x, y, n, polyA[i], x0, y0, eps); 370 | } 371 | 372 | // interpret offset parameters 373 | jtcode = *(INTEGER_POINTER(jt)); 374 | switch(jtcode) { 375 | case 1: 376 | jointype = jtSquare; 377 | break; 378 | case 2: 379 | jointype = jtRound; 380 | break; 381 | case 3: 382 | jointype = jtMiter; 383 | break; 384 | default: 385 | ERREUR("polyclip: unrecognised code for jointype"); 386 | } 387 | 388 | // get parameters 389 | delta = *(NUMERIC_POINTER(del)); // absolute distance 390 | miterlimit = *(NUMERIC_POINTER(mlim)); // multiple of 'delta' 391 | arctolerance = *(NUMERIC_POINTER(atol)); // absolute distance 392 | // rescale 393 | delta = delta/eps; 394 | arctolerance = arctolerance/eps; 395 | 396 | // perform offset operation 397 | ClipperOffset co; 398 | Paths result; 399 | co.AddPaths(polyA, jointype, etClosedPolygon); 400 | co.MiterLimit = miterlimit; 401 | co.ArcTolerance = arctolerance; 402 | co.Execute(result, delta); 403 | 404 | // number of polygons 405 | m = result.size(); 406 | 407 | // initialise output list 408 | PROTECT(out = NEW_LIST(m)); 409 | 410 | // copy data 411 | if(m > 0) { 412 | for(i = 0; i < m; i++) { 413 | mi = result[i].size(); 414 | // Allocate space for output 415 | PROTECT(outi = NEW_LIST(2)); 416 | PROTECT(xouti = NEW_NUMERIC(mi)); 417 | PROTECT(youti = NEW_NUMERIC(mi)); 418 | xx = NUMERIC_POINTER(xouti); 419 | yy = NUMERIC_POINTER(youti); 420 | // copy to output space 421 | ScaleFromPath(result[i], xx, yy, mi, &mitrue, x0, y0, eps); 422 | // Put vectors into list 423 | SET_VECTOR_ELT(outi, 0, xouti); 424 | SET_VECTOR_ELT(outi, 1, youti); 425 | SET_VECTOR_ELT(out, i, outi); 426 | } 427 | } 428 | 429 | UNPROTECT(9 + 3*m); // 8 arguments + out + m * (outi, xouti, youti) 430 | return(out); 431 | } 432 | } 433 | 434 | 435 | // offset (dilation) operation for polygonal lines 436 | 437 | extern "C" { 438 | SEXP Clineoffset(SEXP A, 439 | SEXP del, 440 | SEXP jt, 441 | SEXP et, 442 | SEXP mlim, 443 | SEXP atol, 444 | SEXP X0, 445 | SEXP Y0, 446 | SEXP Eps 447 | ){ 448 | int nA, i, n, m, mi, mitrue; 449 | double *x, *y, *xx, *yy; 450 | SEXP Ai = R_NilValue; 451 | SEXP out, outi, xouti, youti; 452 | JoinType jointype; 453 | EndType endtype; 454 | int jtcode, etcode; 455 | double delta, miterlimit, arctolerance; 456 | double x0, y0, eps; 457 | 458 | // protect arguments from garbage collector 459 | PROTECT(A = AS_LIST(A)); 460 | PROTECT(del = AS_NUMERIC(del)); 461 | PROTECT(jt = AS_INTEGER(jt)); 462 | PROTECT(et = AS_INTEGER(et)); 463 | PROTECT(mlim = AS_NUMERIC(mlim)); 464 | PROTECT(atol = AS_NUMERIC(atol)); 465 | PROTECT(X0 = AS_NUMERIC(X0)); 466 | PROTECT(Y0 = AS_NUMERIC(Y0)); 467 | PROTECT(Eps = AS_NUMERIC(Eps)); 468 | 469 | // length of list 470 | nA = LENGTH(A); 471 | 472 | // Initialise object containing nA polygonal lines 473 | Paths polyA(nA); 474 | 475 | // Get scale parameters 476 | x0 = *(NUMERIC_POINTER(X0)); 477 | y0 = *(NUMERIC_POINTER(Y0)); 478 | eps = *(NUMERIC_POINTER(Eps)); 479 | 480 | // copy data 481 | for(i = 0; i < nA; i++) { 482 | Ai = VECTOR_ELT(A, i); 483 | n = LENGTH(VECTOR_ELT(Ai, 0)); 484 | x = NUMERIC_POINTER(VECTOR_ELT(Ai, 0)); 485 | y = NUMERIC_POINTER(VECTOR_ELT(Ai, 1)); 486 | ScaleToPath(x, y, n, polyA[i], x0, y0, eps); 487 | } 488 | 489 | // interpret offset parameters 490 | jtcode = *(INTEGER_POINTER(jt)); 491 | switch(jtcode) { 492 | case 1: 493 | jointype = jtSquare; 494 | break; 495 | case 2: 496 | jointype = jtRound; 497 | break; 498 | case 3: 499 | jointype = jtMiter; 500 | break; 501 | default: 502 | ERREUR("polyclip: unrecognised code for jointype"); 503 | } 504 | etcode = *(INTEGER_POINTER(et)); 505 | switch(etcode) { 506 | case 1: 507 | endtype = etClosedPolygon; 508 | break; 509 | case 2: 510 | endtype = etClosedLine; 511 | break; 512 | case 3: 513 | endtype = etOpenButt; 514 | break; 515 | case 4: 516 | endtype = etOpenSquare; 517 | break; 518 | case 5: 519 | endtype = etOpenRound; 520 | break; 521 | default: 522 | ERREUR("polyclip: unrecognised code for endtype"); 523 | } 524 | 525 | // get parameters 526 | delta = *(NUMERIC_POINTER(del)); // absolute distance 527 | miterlimit = *(NUMERIC_POINTER(mlim)); // multiple of 'delta' 528 | arctolerance = *(NUMERIC_POINTER(atol)); // absolute distance 529 | // rescale 530 | delta = delta/eps; 531 | arctolerance = arctolerance/eps; 532 | 533 | // perform offset operation 534 | ClipperOffset co; 535 | Paths result; 536 | co.AddPaths(polyA, jointype, endtype); 537 | co.MiterLimit = miterlimit; 538 | co.ArcTolerance = arctolerance; 539 | co.Execute(result, delta); 540 | 541 | // number of polygons 542 | m = result.size(); 543 | 544 | // initialise output list 545 | PROTECT(out = NEW_LIST(m)); 546 | 547 | // copy data 548 | if(m > 0) { 549 | for(i = 0; i < m; i++) { 550 | mi = result[i].size(); 551 | // Allocate space for output 552 | PROTECT(outi = NEW_LIST(2)); 553 | PROTECT(xouti = NEW_NUMERIC(mi)); 554 | PROTECT(youti = NEW_NUMERIC(mi)); 555 | xx = NUMERIC_POINTER(xouti); 556 | yy = NUMERIC_POINTER(youti); 557 | // copy to output space 558 | ScaleFromPath(result[i], xx, yy, mi, &mitrue, x0, y0, eps); 559 | // Put vectors into list 560 | SET_VECTOR_ELT(outi, 0, xouti); 561 | SET_VECTOR_ELT(outi, 1, youti); 562 | SET_VECTOR_ELT(out, i, outi); 563 | } 564 | } 565 | 566 | UNPROTECT(10 + 3*m); // 9 arguments + out + m * (outi, xouti, youti) 567 | return(out); 568 | } 569 | } 570 | 571 | // Minkowski sum of polygon with **path(s)** 572 | 573 | extern "C" { 574 | SEXP Cminksum(SEXP A, // list(list(x,y)) : polygon 575 | SEXP B, // list(list(x,y), list(x,y), ....) 576 | SEXP clo, // whether paths in B are closed 577 | SEXP X0, 578 | SEXP Y0, 579 | SEXP Eps) { 580 | int nB, i, nBi, nA0, m, mi, mitrue; 581 | double *x, *y, *xx, *yy; 582 | SEXP A0 = R_NilValue; 583 | SEXP Bi = R_NilValue; 584 | SEXP out, outi, xouti, youti; 585 | bool closed; 586 | double x0, y0, eps; 587 | Path pathA; 588 | 589 | // protect arguments from garbage collector 590 | PROTECT(A = AS_LIST(A)); 591 | PROTECT(B = AS_LIST(B)); 592 | PROTECT(clo = AS_LOGICAL(clo)); 593 | PROTECT(X0 = AS_NUMERIC(X0)); 594 | PROTECT(Y0 = AS_NUMERIC(Y0)); 595 | PROTECT(Eps = AS_NUMERIC(Eps)); 596 | // that's 6 arguments 597 | 598 | // Get scale parameters 599 | x0 = *(NUMERIC_POINTER(X0)); 600 | y0 = *(NUMERIC_POINTER(Y0)); 601 | eps = *(NUMERIC_POINTER(Eps)); 602 | 603 | // logical value specifying whether paths in B should be closed 604 | closed = *(LOGICAL_POINTER(clo)); 605 | 606 | // copy data from A 607 | A0 = VECTOR_ELT(A, 0); 608 | nA0 = LENGTH(VECTOR_ELT(A0, 0)); 609 | x = NUMERIC_POINTER(VECTOR_ELT(A0, 0)); 610 | y = NUMERIC_POINTER(VECTOR_ELT(A0, 1)); 611 | ScaleToPath(x, y, nA0, pathA, x0, y0, eps); 612 | 613 | // number of polygons in B 614 | nB = LENGTH(B); 615 | // Initialise object representing nB polygons 616 | Paths pathsB(nB); 617 | 618 | // copy data from B 619 | for(i = 0; i < nB; i++) { 620 | Bi = VECTOR_ELT(B, i); 621 | nBi = LENGTH(VECTOR_ELT(Bi, 0)); 622 | x = NUMERIC_POINTER(VECTOR_ELT(Bi, 0)); 623 | y = NUMERIC_POINTER(VECTOR_ELT(Bi, 1)); 624 | ScaleToPath(x, y, nBi, pathsB[i], x0, y0, eps); 625 | } 626 | 627 | // hit it 628 | Paths result; 629 | MinkowskiSum(pathA, pathsB, result, closed); 630 | 631 | // number of polygons 632 | m = result.size(); 633 | 634 | // initialise output list 635 | PROTECT(out = NEW_LIST(m)); 636 | 637 | // adjust origin: (x0,y0) were subtracted from both A and B 638 | x0 = 2.0 * x0; 639 | y0 = 2.0 * y0; 640 | 641 | // copy data 642 | if(m > 0) { 643 | for(i = 0; i < m; i++) { 644 | mi = result[i].size(); 645 | // Allocate space for output 646 | PROTECT(outi = NEW_LIST(2)); 647 | PROTECT(xouti = NEW_NUMERIC(mi)); 648 | PROTECT(youti = NEW_NUMERIC(mi)); 649 | xx = NUMERIC_POINTER(xouti); 650 | yy = NUMERIC_POINTER(youti); 651 | // copy to output space 652 | ScaleFromPath(result[i], xx, yy, mi, &mitrue, x0, y0, eps); 653 | // Put vectors into list 654 | SET_VECTOR_ELT(outi, 0, xouti); 655 | SET_VECTOR_ELT(outi, 1, youti); 656 | SET_VECTOR_ELT(out, i, outi); 657 | } 658 | } 659 | 660 | UNPROTECT(7 + 3*m); // 6 arguments + out + m * (outi, xouti, youti) 661 | return(out); 662 | } 663 | } 664 | 665 | // point in polygon test /////////////////// 666 | 667 | extern "C" { 668 | SEXP Cpiptest(SEXP P, // test points, list(x, y) 669 | SEXP A, // single polygon, list(x,y) 670 | SEXP X0, 671 | SEXP Y0, 672 | SEXP Eps 673 | ){ 674 | int na, np, i; 675 | double *xa, *ya, *xp, *yp; 676 | double x0, y0, eps; 677 | int *result; 678 | SEXP out; 679 | 680 | // protect arguments from garbage collector 681 | PROTECT(P = AS_LIST(P)); 682 | PROTECT(A = AS_LIST(A)); 683 | PROTECT(X0 = AS_NUMERIC(X0)); 684 | PROTECT(Y0 = AS_NUMERIC(Y0)); 685 | PROTECT(Eps = AS_NUMERIC(Eps)); 686 | 687 | // Get test point coordinates 688 | np = LENGTH(VECTOR_ELT(P, 0)); 689 | xp = NUMERIC_POINTER(VECTOR_ELT(P, 0)); 690 | yp = NUMERIC_POINTER(VECTOR_ELT(P, 1)); 691 | 692 | // Get polygon coordinates 693 | na = LENGTH(VECTOR_ELT(A, 0)); 694 | xa = NUMERIC_POINTER(VECTOR_ELT(A, 0)); 695 | ya = NUMERIC_POINTER(VECTOR_ELT(A, 1)); 696 | 697 | // Get scale parameters 698 | x0 = *(NUMERIC_POINTER(X0)); 699 | y0 = *(NUMERIC_POINTER(Y0)); 700 | eps = *(NUMERIC_POINTER(Eps)); 701 | 702 | // copy and scale polygon 703 | ClipperLib::Path pathA; 704 | ScaleToPath(xa, ya, na, pathA, x0, y0, eps); 705 | 706 | // Allocate space for output 707 | PROTECT(out = NEW_INTEGER(np)); 708 | result = INTEGER_POINTER(out); 709 | 710 | // handle one point at a time 711 | ClipperLib::IntPoint pti; 712 | for(i = 0; i < np; i++) { 713 | ScaleToPoint(xp[i], yp[i], pti, x0, y0, eps); 714 | result[i] = PointInPolygon(pti, pathA); 715 | } 716 | 717 | UNPROTECT(6); 718 | return(out); 719 | } 720 | } 721 | --------------------------------------------------------------------------------