Title: | Contouring of Non-Regular Three-Dimensional Data |
---|---|
Description: | Create contour lines for a non regular series of points, potentially from a non-regular canvas. |
Authors: | Nicholas Hamilton |
Maintainer: | Nicholas Hamilton <[email protected]> |
License: | GPL (>= 2) |
Version: | 1.0.5 |
Built: | 2024-10-31 22:27:07 UTC |
Source: | CRAN |
Create contour lines for a non regular series of points, potentially from a non-regular canvas.
The contoureR
package executes linear interpolation on a delaunay triangulated
mesh strung between three-dimensional (3D) points supplied by the user. Contours are calculated
across the surface constrained by the convex hull of the supplied data.
Usually, the well known functions such as contourLines
from the grDevices
package, expect (or rather, require) data to be regular, this means that a rectangular array or matrix of
x
and y
coordinate pairs, each with a corresponding z
value is to be modelled – that
is to say the cartesian product of a numeric vector of x
values of length n
,
with a numeric vector of y
values having length m
, used to produce a set of
(m x n)
unique points that have been concurrently provided with exactly (m x n) z values
.
By restricting values to the above format, this in turn limits the region of analysis to square/rectangular
canvasses (ie plane defined by geometric and orthogonal vectors parallel to the x
and y
axes and range bound by the [xmin,xmax]
and [ymin,ymax]
in the above x
and y
input numeric vectors, respectively).
This restriction, from time-to-time, can be very inconvenient, and is a primary objective and purpose for
the creation of this package.
As suggested in the previous paragraph, the contoureR
package, on the other hand, has no such
orthogonality / regularity requirement and can therefore be applied over obscurely shaped regions such as
triangles, circles, polygons and the like. To demonstrate this, in the example
provided on the current page, an equation is contoured, where firstly the x
and y
data is
randomly selected (non regular), and then the set of values is subsequently constrained by a
bounding (limiting) circle.
Note, for the moment, the only restriction is that for polygon-type regions to be modelled, then these regions must not have holes, since these will be filled coarsely when the Deleaunaymesh gets generated, however, in future revisions, this obstacle should be easily addressed via parameter defining a manual exclusion list of points.
getContourLines
, contourLinesR
and contourWalker
# Contour Lines for a Function, Constrained to a limited domain # Example of the provision of non-regular data library(contoureR) library(ggplot2) a = -2; b = +2; n = 150 x = runif(n*n,a,b) y = runif(n*n,a,b) df = data.frame(x,y) df$z = with(df,-x*y*exp(-x^2-y^2)) df.sub = subset(df,x^2 + y^2 < 2) df.cnt = getContourLines(df.sub,nlevels=20) ggplot(data=df.cnt,aes(x,y,group=Group,colour=z)) + geom_path() + theme_bw()
# Contour Lines for a Function, Constrained to a limited domain # Example of the provision of non-regular data library(contoureR) library(ggplot2) a = -2; b = +2; n = 150 x = runif(n*n,a,b) y = runif(n*n,a,b) df = data.frame(x,y) df$z = with(df,-x*y*exp(-x^2-y^2)) df.sub = subset(df,x^2 + y^2 < 2) df.cnt = getContourLines(df.sub,nlevels=20) ggplot(data=df.cnt,aes(x,y,group=Group,colour=z)) + geom_path() + theme_bw()
A wrapper to the getContourLines
function, provided to ease the transition from
the contourLines
function as part of grDevices
contourLinesR(x, y, z, nlevels = 10, levels = pretty(range(z, na.rm = TRUE)), ...)
contourLinesR(x, y, z, nlevels = 10, levels = pretty(range(z, na.rm = TRUE)), ...)
x |
Numeric data for x and y coordinate, a single matrix or data-frame object can be provided for
|
y |
Numeric data for x and y coordinate, a single matrix or data-frame object can be provided for
|
z |
numeric Data for z coordinate (the coordinate to model) |
nlevels |
An integer number of bins to split the data into iff |
levels |
A numeric vector of the explicitly specified levels (z values) to contour, by specifying this argument,
it will override |
... |
any other parameters passed through to |
This function returns data in the same format/structure as contourLines
,
ie, list of lists, which is different from the (preferred) dataframe
object returned
by the getContourLines
function as part of the present work.
A list of contours is returned, Each contour is a list with the elements:
level |
The contour of the level |
x |
The x-coordinates of the contour |
y |
The y-coordinates of the contour |
library(contoureR) library(ggplot2) x = runif(100) y = runif(100) df = expand.grid(x=x,y=y) z = with(df,x+y) result = contourLinesR(df$x,df$y,z)
library(contoureR) library(ggplot2) x = runif(100) y = runif(100) df = expand.grid(x=x,y=y) z = with(df,x+y) result = contourLinesR(df$x,df$y,z)
This function is the R interface to the C++ core contour-walker function, totally essential for this package.
dm |
the n x 3 matrix representing the indexes of the vertices of the triangles in the delaunay mesh.
No values should be greater than the number of rows in |
xyz |
the m x 3 matrix of xyz coordinates for all the points in the data. |
levels |
a numeric vector of levels to contour. |
maximumPertubation |
the maximum pertubation amount (positive or negative) as a percentage. |
The underlying C++ routine establishes a pointer-driven adjacency-network within each of the triangles (Dels) in the supplied Delaunay mesh, that is to say that Two Del's are deemed to be 'networked' with each other if they share adjacent edges (Edges), which are drawn between two points (Nodes). Dels are deemed as 'fully-networked' if they hold reciprocating pointers with exactly three (3) other Dels. Dels can be partially networked if one or two of the available pointers remain unassigned, which may be the case if the particular Del is a participating member of the convex hull.
Because C++ uses zero-based indexing, whilst R uses 1-based indexing, should the Delaunay Mesh (dm
) be provided
where the minimum value is 1, then it will be deemed to be a 1-based set and reduced accordingly.
Prior to traversing the mesh, in order to prevent degeneracies, should any Dels in the mesh have vertices which are
of the same z
value, and/or equal to one of the intended contouring levels, then these nodes will be
pertubated (along the z
direction) by an infitesimally small amount. The consequences of this approach is
that when interpolating along the edges of the Del, the path will always leave at some point (if even trivially small)
inbetween two (2) nodes – the path will NEVER leave directly through a node, which would otherwise lead to potential
confusion as to the appropriate recipient of the path under such circumstance.
This function is not particularly convenient, and a more convenient wrapper has been produced, with all the usual
checks and balances, for further information, see the getContourLines
function.
matrix
with 6 columns: LevelID, GroupID, PathID, x, y
and z
n = 100 x = runif(n) y = runif(n) df = expand.grid(x,y); colnames(df) = c("x","y") df$z = df$x^2 + df$y^2 dm = getDelaunayMesh(df$x,df$y) res = contourWalker(dm,as.matrix(df),levels=pretty(df$z,n=20)) res = data.frame(res); colnames(res) = c('LID','GID','PID','x','y','z') res$Group = interaction(res$LID,res$GID) library(ggplot2) ggplot(res,aes(x,y,group=Group,colour=z)) + geom_path()
n = 100 x = runif(n) y = runif(n) df = expand.grid(x,y); colnames(df) = c("x","y") df$z = df$x^2 + df$y^2 dm = getDelaunayMesh(df$x,df$y) res = contourWalker(dm,as.matrix(df),levels=pretty(df$z,n=20)) res = data.frame(res); colnames(res) = c('LID','GID','PID','x','y','z') res$Group = interaction(res$LID,res$GID) library(ggplot2) ggplot(res,aes(x,y,group=Group,colour=z)) + geom_path()
This function is the R interface to the C++ implementation of Andrews Monotone, a well known algorithm for solving
the convex hull in O(nlogn)
time complexity.
convexHullAM_Indexes(x, y, includeColinear=FALSE,zeroBased = TRUE) convexHullAM_Points(x, y,includeColinear=FALSE)
convexHullAM_Indexes(x, y, includeColinear=FALSE,zeroBased = TRUE) convexHullAM_Points(x, y,includeColinear=FALSE)
x |
NumericVector of x values |
y |
NumericVector of y values |
includeColinear |
whether to inlude points that line ON the hull, by default this is set to FALSE, as this is the true definition of the convex hull. |
zeroBased |
Whether the return indexes should be zero based (true, for use in C++), or One-Based (false, for use in R). |
convexHullAM_Indexes
returns an integer vector of the indexes of the points,
whilst convexHullAM_Points
returns an n x 2
matrix of the points themselves.
library(contoureR) library(ggplot2) set.seed(1) x = runif(100) y = runif(100) ch = convexHullAM_Indexes(x,y,includeColinear=FALSE,zeroBased = FALSE) ggplot(data.frame(x,y),aes(x,y)) + geom_point() + geom_path(data=data.frame(x,y)[ch,],colour="red")
library(contoureR) library(ggplot2) set.seed(1) x = runif(100) y = runif(100) ch = convexHullAM_Indexes(x,y,includeColinear=FALSE,zeroBased = FALSE) ggplot(data.frame(x,y),aes(x,y)) + geom_point() + geom_path(data=data.frame(x,y)[ch,],colour="red")
The following routine produces contour lines for a set of non-regular x,y
and z
values.
via utilizing a Deleaunay Mesh strung between the supplied x,y
coordinates in order to produce
iso-contour data representing the third variable, z
. To this end, by using a Deleaunay mesh,
this routine does not require regular x
and y
data, although it can be expected to yield
'better' result, with regular / fine-grained data.
getContourLines(x, y, z, nlevels = 10, binwidth, levels, criticalRatio = 5)
getContourLines(x, y, z, nlevels = 10, binwidth, levels, criticalRatio = 5)
x , y
|
Numeric data for x and y coordinate, a single matrix or data-frame object can be provided for
|
z |
numeric Data for z coordinate (the coordinate to model) |
nlevels |
An integer number of bins to split the data into iff |
binwidth |
The desired width of the bins, if specified, will override |
levels |
A numeric vector of the explicitly specified levels (z values) to contour, by specifying this argument,
it will override |
criticalRatio |
When producing the Delaunay Mesh, the quality of the mesh can be poor in the proximity to the convex hull, Del's that have an aspect ratio greater than this value are not considered when producing the contours. In this context, the aspect ratio is defined as the circumradius to twice its inradius, equilateral triangles have an aspect ratio of 1, everything else is larger |
For the function getContourLines(...)
, the return object is a data.frame
obect
representing the contours, assembled in such way to permit easy use within the ggplot2
paradigm.
Such data frame contains seven (7) columns:
LID |
A number representing the level |
GID |
Within each level, a number representing the contour group |
PID |
Within each group, a number representing the path/segment |
x |
The x-coordinates of the contour |
y |
The y-coordinates of the contour |
z |
The z-coordinates of the contour (ie value of the level) |
Group |
The unique identifyer for each independent contour path, calculated as being the
interaction between |
contourLinesR
, getDelaunayMesh
and getConvexHull
This is a wrapper to the C++
interface function, contourWalker
.
# Contour Lines for Volcano Data library(ggplot2) data(volcano) x = 1:nrow(volcano) y = 1:ncol(volcano) z = expand.grid(x=x,y=y); z$z = apply(z,1,function(xx){ volcano[ xx[1],xx[2] ]} ) df = getContourLines(z) ggplot(df,aes(x,y,group=Group,colour=z)) + geom_path() # Contour Lines for a Function library(ggplot2) a = -2; b = 2; n = 75 x = y = seq(a,b,by=diff(c(a,b))/(n+1)) df = expand.grid(x=x,y=y) df$z = with(df,-x*y*exp(-x^2-y^2)) df.cnt = getContourLines(df) ggplot(data=df.cnt,aes(x,y,group=Group,colour=z)) + geom_path()
# Contour Lines for Volcano Data library(ggplot2) data(volcano) x = 1:nrow(volcano) y = 1:ncol(volcano) z = expand.grid(x=x,y=y); z$z = apply(z,1,function(xx){ volcano[ xx[1],xx[2] ]} ) df = getContourLines(z) ggplot(df,aes(x,y,group=Group,colour=z)) + geom_path() # Contour Lines for a Function library(ggplot2) a = -2; b = 2; n = 75 x = y = seq(a,b,by=diff(c(a,b))/(n+1)) df = expand.grid(x=x,y=y) df$z = with(df,-x*y*exp(-x^2-y^2)) df.cnt = getContourLines(df) ggplot(data=df.cnt,aes(x,y,group=Group,colour=z)) + geom_path()
Returns the sequence of indexes within the supplied numeric vectors x
and y
, that describe the convex
hull containing those points. This is a (slightly modified) implementation of the Andrews Monotone Chain, which is
a well known algorithm that is able to solve the convex hull with O(nlogn)
complexity.
Typical computation time on a Macbook Air, 1.7Ghz I7, 8Gb Ram, using random points in the range [0,1]:
100K points 0.03 Seconds
1M points 0.3 seconds, and
10M points3.3 seconds.
getConvexHull(x, y, includeColinear = FALSE)
getConvexHull(x, y, includeColinear = FALSE)
x |
numeric vector of x values |
y |
numeric vector of y values of same length as x |
includeColinear |
keep or discard the points that lie ON the hull, default is to discard (ie dont keep colinear points), as this is the true definition of the convex hull. |
Returns a vector of integers that represent the '1-based' indexes of the points relative to the
x
and y
input arguments. The resulting vector represents the closed list, meaning that the
first index and the last index in the series will be the same.
https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
#Generate the Convex Hull of a Series of Points set.seed(1) x = runif(100) y = runif(100) ch = getConvexHull(x,y) #To demonstrate, Lets view the hull library(ggplot2) df = data.frame(x,y) ggplot(data=df,aes(x,y)) + geom_path(data=df[ch,]) + geom_point()
#Generate the Convex Hull of a Series of Points set.seed(1) x = runif(100) y = runif(100) ch = getConvexHull(x,y) #To demonstrate, Lets view the hull library(ggplot2) df = data.frame(x,y) ggplot(data=df,aes(x,y)) + geom_path(data=df[ch,]) + geom_point()
Retrieves the Delaunay Mesh for a series of x and y points in 2D.
With the exception of a few brief checks, is almost a direct wrapper to the delaunayn
function as part of the geometry package.
getDelaunayMesh(x, y)
getDelaunayMesh(x, y)
x |
numeric vector of x values |
y |
numeric vector of y values of same length as x |
matrix
object having three columns that represent the (1-based) indexes of each vertex
relative to the data in the x
and y
input parameters.
#Generate a sample Delaunay Mesh set.seed(1) x = runif(100) y = runif(100) dm = getDelaunayMesh(x,y) #To demonstrate, Lets view the mesh library(ggplot2) library(reshape) df = as.data.frame(dm); df$id = 1:nrow(df); df = melt(df,id="id") df = cbind(df,data.frame(x,y)[df$value,]) ggplot(data=df,aes(x,y,group=id)) + geom_polygon(aes(fill=id),color="gray")
#Generate a sample Delaunay Mesh set.seed(1) x = runif(100) y = runif(100) dm = getDelaunayMesh(x,y) #To demonstrate, Lets view the mesh library(ggplot2) library(reshape) df = as.data.frame(dm); df$id = 1:nrow(df); df = melt(df,id="id") df = cbind(df,data.frame(x,y)[df$value,]) ggplot(data=df,aes(x,y,group=id)) + geom_polygon(aes(fill=id),color="gray")
Returns the indexes of supplied points, x
and y
, ordered either clockwise or
anticlockwise about another point, which by default is taken to be the non-weighted midpoint
of the supplied data
orderPoints(x, y, ..., xm = mean(range(x)), ym = mean(range(y)), clockwise = TRUE)
orderPoints(x, y, ..., xm = mean(range(x)), ym = mean(range(y)), clockwise = TRUE)
x |
numeric vector of x values |
y |
numeric vector of y values of same length as x |
... |
not used |
xm |
the x value of the reference point |
ym |
the y value of the reference point |
clockwise |
order in clockwise or anticlockwise manner |
#Generate a random set of points and put them clockwise order set.seed(1) x = runif(100) y = runif(100) op = orderPoints(x,y) #To demonstrate, Lets view the points in order library(ggplot2) df = data.frame(x,y) df = df[op,]; df$id = 1:nrow(df) ggplot(data=df,aes(x,y,colour=id)) + geom_path() + geom_point() + scale_colour_gradient(low="green",high="red")
#Generate a random set of points and put them clockwise order set.seed(1) x = runif(100) y = runif(100) op = orderPoints(x,y) #To demonstrate, Lets view the points in order library(ggplot2) df = data.frame(x,y) df = df[op,]; df$id = 1:nrow(df) ggplot(data=df,aes(x,y,colour=id)) + geom_path() + geom_point() + scale_colour_gradient(low="green",high="red")