Skip to content

Commit

Permalink
add support for Minkowski Sums
Browse files Browse the repository at this point in the history
  • Loading branch information
pmur002 committed Dec 6, 2022
1 parent 9884693 commit 0d414bf
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 19 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Authors@R: c(person("Paul", "Murrell", role = c("aut", "cre"),
Description: Functions for performing polygon geometry with 'grid' grobs.
This allows complex shapes to be defined by combining simpler
shapes.
URL: https://github.com/pmur002/gridgeometry, https://stattech.wordpress.fos.auckland.ac.nz/2019/03/04/2019-01-a-geometry-engine-interface-for-grid/, https://stattech.blogs.auckland.ac.nz/2022/06/01/2022-02-constructive-geometry-for-complex-grobs
URL: https://github.com/pmur002/gridgeometry, https://stattech.wordpress.fos.auckland.ac.nz/2019/03/04/2019-01-a-geometry-engine-interface-for-grid/, https://stattech.blogs.auckland.ac.nz/2022/06/01/2022-02-constructive-geometry-for-complex-grobs/
Depends: R (>= 3.6.0), grid
Imports: grDevices, polyclip (>= 1.10-0)
Suggests: graphics, lattice
Expand Down
56 changes: 39 additions & 17 deletions R/minkowski.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,62 +10,84 @@ polyminkowski.default <- function(A, B, ...) {
polyclip::polyminkowski(A, B, ...)
}

polyminkowskiGrob <- function(A, B, reduceA, reduceB, ...) {
if (inherits(B, "gPath") || is.character(B)) {
B <- grid.get(B, ...)
}
polyminkowskiGridGrob <- function(A, B, closed, reduceA, reduceB, ...) {
if (!(inherits(B, "grob") || inherits(B, "gList")))
stop("Argument 'B' must be a grob")
polyA <- xyListFromGrob(A, op = reduceA, closed = TRUE, ...)
polyB <- xyListFromGrob(B, op = reduceB, closed = TRUE, ...)
polyclip::polyminkowski(polyA, polyB, ...)
polyB <- xyListFromGrob(B, op = reduceB, closed = closed, ...)
polyclip::polyminkowski(polyA, polyB, closed = closed, ...)
}

polyminkowski.grob <- function(A, B,
polyminkowski.grob <- function(A, B, closed=isClosedShape(B),
reduceA = "union",
reduceB = "union",
...) {
polyminkowskiGrob(A, B, reduceA, reduceB, ...)
polyminkowskiGridGrob(A, B, closed, reduceA, reduceB, ...)
}

polyminkowski.gList <- function(A, B,
polyminkowski.gList <- function(A, B, closed=isClosedShape(B),
reduceA = "union",
reduceB = "union",
...) {
polyminkowskiGrob(A, B, reduceA, reduceB, ...)
polyminkowskiGridGrob(A, B, closed, reduceA, reduceB, ...)
}

polyminkowski.gPath <- function(A, B,
polyminkowski.gPath <- function(A, B, closed,
strict=FALSE, grep=FALSE, global=FALSE,
reduceA = "union",
reduceB = "union",
...) {
A <- grid.get(A, strict, grep, global)
polyminkowskiGrob(A, B, reduceA, reduceB, ...)
if (inherits(B, "gPath") || is.character(B)) {
B <- grid.get(B, ...)
}
if (missing(closed))
closed <- isClosedShape(B)
polyminkowskiGridGrob(A, B, closed, reduceA, reduceB, ...)
}

polyminkowski.character <- function(A, B,
polyminkowski.character <- function(A, B, closed,
strict=FALSE, grep=FALSE, global=FALSE,
reduceA = "union",
reduceB = "union",
...) {
A <- grid.get(A, strict, grep, global)
polyminkowskiGrob(A, B, reduceA, reduceB, ...)
if (inherits(B, "gPath") || is.character(B)) {
B <- grid.get(B, ...)
}
if (missing(closed))
closed <- isClosedShape(B)
polyminkowskiGridGrob(A, B, closed, reduceA, reduceB, ...)
}

################################################################################
## High level grob interface
makeContent.minkowskiGrob <- function(x) {
offsetpts <- do.call(polyminkowski, c(list(A=x$A, B=x$B), x$minkowskiArgs))
setChildren(x, gList(xyListToPath(offsetpts)))
children <- vector("list", 2)
closedPaths <- do.call(polyminkowski,
c(list(A=x$A, B=x$B, closed=TRUE),
x$polyclipArgs))
if (length(closedPaths)) {
children[[1]] <- x$grobFn(closedPaths,
name=paste0(x$name, ".closed"))
}
openPaths <- do.call(polyminkowski,
c(list(A=x$A, B=x$B, closed=FALSE),
x$polyclipArgs))
if (length(openPaths)) {
children[[2]] <- x$grobFn(openPaths,
name=paste0(x$name, ".open"))
}
setChildren(x, do.call(gList, children[!is.null(children)]))
}

minkowskiGrob <- function(A, B,
grobFn=xyListToPath,
name=NULL, gp=gpar(),
...) {
if (!(grobArg(A) && grobArg(B)))
stop("Invalid argument")
gTree(A=A, B=B,
gTree(A=A, B=B, grobFn=grobFn,
polyclipArgs=list(...),
gp=gp, name=name, cl="minkowskiGrob")
}
Expand Down
5 changes: 4 additions & 1 deletion inst/NEWS.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
\section{Changes in version 0.4-0}{
\itemize{
\item New functions \code{grid.polyoffset()} and
\code{grid.polylineoffset()}.
\code{grid.polylineoffset()} for generating offset regions.

\item New function \code{grid.minkowski()} for generating
Minkowski sums of grobs.
}
}

Expand Down
71 changes: 71 additions & 0 deletions man/grid.minkowski.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
\name{grid.minkowski}
\alias{grid.minkowski}
\alias{minkowskiGrob}
\title{
Generate Minkowski Sums of Grobs
}
\description{
Given a polygonal \dfn{pattern} and a polygonal \dfn{path},
generate the Minkowski Sum by adding the pattern to the path.
}
\usage{
minkowskiGrob(A, B,
grobFn=xyListToPath,
name=NULL, gp=gpar(), ...)
grid.minkowski(A, B, ...)
}
\arguments{
\item{A}{
A grob, gList, or gTree, or a gPath or a character value
identifying a grob that has already been drawn. This is known as the
\dfn{pattern} grob.
}
\item{B}{
A grob, gList, or gTree, or a gPath or a character value
identifying a grob that has already been drawn. This is known as the
\dfn{path} grob.
}
\item{grobFn}{
The function that is used to create the final grob result.
Predefined options are: \code{\link{xyListToPath}},
\code{\link{xyListToPolygon}}, and
\code{\link{xyListToLine}}.
}
\item{name}{
A name for the resulting grob.
}
\item{gp}{
Graphical parameter settings for the resulting grob.
}
\item{\dots}{
For \code{minkowskiGrob}, arguments passed on to
\code{polyclip::polyminkowski}.
}
}
\details{
Both \code{A} and \code{B} should not contain self-intersections,
though they can be non-convex.
}
\value{
\code{minkowskiGrob} returns a gTree.

\code{grid.minkowski} is only used for its side-effect of drawing
on the current graphics device.
}
\author{
Jack Wong
}
\seealso{
\code{\link{xyListToPath}},
\code{\link{xyListToPolygon}},
\code{\link{xyListToLine}},
\code{\link{polyminkowski}}
}
\examples{
pattern <- circleGrob(x = 0, y = 0, r = .1)
path <- rectGrob(width = 0.5, height = 0.5)
minkowski <- minkowskiGrob(pattern, path)
grid.draw(minkowski)
}
\keyword{ dplot }
\keyword{ aplot }
89 changes: 89 additions & 0 deletions man/polyminkowski.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
\name{polyminkowski}
\alias{polyminkowski}
\alias{polyminkowski.grob}
\alias{polyminkowski.gList}
\alias{polyminkowski.gPath}
\alias{polyminkowski.character}
\title{
Generate Minkowski Sums on Coordinates
}
\description{
This function generates the Minkowski Sum of two sets of coordinates.
}
\usage{
polyminkowski(A, B, ...)
\method{polyminkowski}{grob}(A, B, closed=isClosedShape(B),
reduceA = "union",
reduceB = "union",
...)
\method{polyminkowski}{gList}(A, B, closed=isClosedShape(B),
reduceA = "union",
reduceB = "union",
...)
\method{polyminkowski}{gPath}(A, B, closed,
strict=FALSE, grep=FALSE, global=FALSE,
reduceA = "union",
reduceB = "union",
...)
\method{polyminkowski}{character}(A, B, closed,
strict=FALSE, grep=FALSE, global=FALSE,
reduceA = "union",
reduceB = "union",
...)
}
\arguments{
\item{A}{
A set of coordinates describing a
\dfn{pattern} shape.
Or a grob, gList, or a gPath (or a character value)
identifying a grob that has already been drawn from which
coordinates are generated.
}
\item{B}{
A set of coordinates describing a
\dfn{path} shape.
Or a grob, gList, or a gPath (or a character value)
identifying a grob that has already been drawn from which
coordinates are generated.
}
\item{closed}{
A logical value indicating whether the \code{B} coordinates describe
a closed shape or an open shape.
}
\item{reduceA, reduceB}{
A character value describing the operation to be used if either
\code{A} or \code{B} need to be reduced to a single set of
coordinates. One of
\code{"intersection"}, \code{"minus"}, \code{"union"}, or
\code{"xor"}, in which case \code{polyminkowski} is used to
reduce multiple shapes, or \code{"flatten"}, in which case
coordinates for all shapes are returned.
}
\item{strict, grep, global}{
Arguments controlling the interpretation of the gPath
(passed to \code{grid.get}).
}
\item{\dots}{
Arguments used by methods.
}
}
\details{
The shape described by the pattern coordinates is added
to the shape described by the path coordinates.
}
\value{
The result is a new set of coordinates.
}
\author{
Paul Murrell
}
\seealso{
\code{\link{grid.minkowski}}
}
\examples{
c <- circleGrob(x=0, y=0, r=.1)
r <- rectGrob(width=.5, height=.5)
polyminkowski(c, r)
}
\keyword{ dplot }
\keyword{ aplot }

0 comments on commit 0d414bf

Please sign in to comment.