Title: | 3D Visualization Using OpenGL |
---|---|
Description: | Provides medium to high level functions for 3D interactive graphics, including functions modelled on base graphics (plot3d(), etc.) as well as functions for constructing representations of geometric objects (cube3d(), etc.). Output may be on screen using OpenGL, or to various standard 3D file formats including WebGL, PLY, OBJ, STL as well as 2D image formats, including PNG, Postscript, SVG, PGF. |
Authors: | Duncan Murdoch [aut, cre], Daniel Adler [aut], Oleg Nenadic [ctb], Simon Urbanek [ctb], Ming Chen [ctb], Albrecht Gebhardt [ctb], Ben Bolker [ctb], Gabor Csardi [ctb], Adam Strzelecki [ctb], Alexander Senger [ctb], The R Core Team [ctb, cph], Dirk Eddelbuettel [ctb], The authors of Shiny [cph], The authors of knitr [cph], Jeroen Ooms [ctb], Yohann Demont [ctb], Joshua Ulrich [ctb], Xavier Fernandez i Marin [ctb], George Helffrich [ctb], Ivan Krylov [ctb], Michael Sumner [ctb], Mike Stein [ctb], Jonathon Love [ctb] |
Maintainer: | Duncan Murdoch <[email protected]> |
License: | GPL |
Version: | 1.3.14 |
Built: | 2024-11-25 16:31:39 UTC |
Source: | CRAN |
3D real-time rendering system.
RGL is a 3D real-time rendering system for R.
Multiple windows are managed at a time. Windows
may be divided into “subscenes”, where one has the current focus
that receives instructions from the R command-line.
The device design is oriented towards the R device metaphor. If you send
scene management instructions, and there's no device open, it will be opened
automatically.
Opened devices automatically get the current device focus. The focus may be
changed by using set3d()
or useSubscene3d()
.
RGL provides medium to high level functions for 3D interactive graphics, including
functions modelled on base graphics (plot3d()
, etc.) as well as functions for
constructing geometric objects (cube3d()
, etc.). Output
may be on screen using OpenGL, or to various standard 3D file formats including
WebGL, PLY, OBJ, STL as well as 2D image formats, including PNG, Postscript, SVG, PGF.
The open3d()
function attempts to open a new RGL window,
using default settings specified by the user.
See the first example below to display the ChangeLog.
r3d for a description of the *3d
interface;
par3d
for a description of scene properties and the rendering
pipeline; rgl.useNULL
for a description of how to
use RGL on a system with no graphics support.
if (!in_pkgdown_example()) file.show(system.file("NEWS", package = "rgl")) example(surface3d) example(plot3d)
if (!in_pkgdown_example()) file.show(system.file("NEWS", package = "rgl")) example(surface3d) example(plot3d)
Mostly for internal use, this function returns the current device number if one exists, or opens a new device and returns that.
.check3d()
.check3d()
The device number of an RGL device.
Duncan Murdoch
rgl.dev.list() .check3d() rgl.dev.list() .check3d() rgl.dev.list() close3d()
rgl.dev.list() .check3d() rgl.dev.list() .check3d() rgl.dev.list() close3d()
This adds mathematical lines to a scene. Their intersection with the current bounding box will be drawn.
abclines3d(x, y = NULL, z = NULL, a, b = NULL, c = NULL, ...)
abclines3d(x, y = NULL, z = NULL, a, b = NULL, c = NULL, ...)
x , y , z
|
Coordinates of points through which each line passes. |
a , b , c
|
Coordinates of the direction vectors for the lines. |
... |
Material properties. |
Draws the segment of a line that intersects the current
bounding box of the scene using the parametrization
where
is a real number.
Any reasonable way of defining the
coordinates x, y, z
and a, b, c
is acceptable.
See the function xyz.coords
for details.
A shape ID of the object is returned invisibly.
planes3d
for mathematical planes.
segments3d
draws sections of lines that
do not adapt to the bounding box.
plot3d(rnorm(100), rnorm(100), rnorm(100)) abclines3d(0, 0, 0, a = diag(3), col = "gray")
plot3d(rnorm(100), rnorm(100), rnorm(100)) abclines3d(0, 0, 0, a = diag(3), col = "gray")
This generic function adds normals at each of the vertices of a polyhedron by averaging the normals of each incident face. This has the effect of making the surface of the object appear smooth rather than faceted when rendered.
addNormals(x, ...) ## S3 method for class 'mesh3d' addNormals(x, angleWeighted = TRUE, ...)
addNormals(x, ...) ## S3 method for class 'mesh3d' addNormals(x, angleWeighted = TRUE, ...)
x |
An object to which to add normals. |
... |
Additional parameters which will be passed to the methods. |
angleWeighted |
See Details below. |
Currently methods are supplied for "mesh3d"
and
"shapelist3d"
classes.
These methods work by averaging the normals on the faces incident at
each vertex. By default these are weighted according to the angle
in the polygon at that vertex.
If angleWeighted = FALSE
, a slightly faster
but less accurate weighting by the triangle area is used.
Prior to rgl version 0.104.12 an incorrect weighting was used;
it can be partially reproduced by using angleWeighted = NA
,
but not all the bugs in that scheme will be kept.
A new object of the same class as x
, with normals added.
Duncan Murdoch
open3d() y <- subdivision3d(tetrahedron3d(col = "red"), depth = 3) shade3d(y) # No normals y <- addNormals(y) shade3d(translate3d(y, x = 1, y = 0, z = 0)) # With normals
open3d() y <- subdivision3d(tetrahedron3d(col = "red"), depth = 3) shade3d(y) # No normals y <- addNormals(y) shade3d(translate3d(y, x = 1, y = 0, z = 0)) # With normals
This is a function to produce actions in response to
a playwidget
or Shiny input control. The
mental model is that each of the vertices of some object has
a certain birth time; a control sets the current time, so that
vertices have ages depending on the control setting.
Attributes of those vertices can then be changed.
ageControl(births, ages, objids = tagged3d(tags), tags, value = 0, colors = NULL, alpha = NULL, radii = NULL, vertices = NULL, normals = NULL, origins = NULL, texcoords = NULL, x = NULL, y = NULL, z = NULL, red = NULL, green = NULL, blue = NULL)
ageControl(births, ages, objids = tagged3d(tags), tags, value = 0, colors = NULL, alpha = NULL, radii = NULL, vertices = NULL, normals = NULL, origins = NULL, texcoords = NULL, x = NULL, y = NULL, z = NULL, red = NULL, green = NULL, blue = NULL)
births |
Numeric birth times of vertices. |
ages |
Chosen ages at which the following attributes will apply. |
objids |
Object ids to which the changes apply. |
tags |
Alternate way to specify |
value |
Initial value; typically overridden by input. |
colors , alpha , radii , vertices , normals , origins , texcoords
|
Attributes of the vertices that can be changed. There should be
one entry or row for each entry in |
x , y , z , red , green , blue
|
These one-dimensional components of vertices and colors are provided for convenience. |
All attributes must have the same number of entries (rows for the matrices) as the ages vector. The births vector must have the same number of entries as the number of vertices in the object.
Not all objects contain all attributes; if one is chosen that is
not a property of the corresponding object, a Javascript alert()
will be
generated. (This restriction may be removed in the future by attempting
to add the attribute when it makes sense.)
If a births
entry is NA
, no change will be made to that
vertex.
A list of class "rglControl"
of cleaned up parameter
values, to be used in an RGL widget.
Duncan Murdoch
saveopts <- options(rgl.useNULL = TRUE) theta <- seq(0, 4*pi, length.out = 100) xyz <- cbind(sin(theta), cos(theta), sin(theta/2)) lineid <- plot3d(xyz, type="l", alpha = 0, lwd = 5, col = "blue")["data"] widget <- rglwidget() %>% playwidget(ageControl(births = theta, ages = c(-4*pi, -4*pi, 1-4*pi, 0, 0, 1), objids = lineid, alpha = c(0, 1, 0, 0, 1, 0)), start = 0, stop = 4*pi, step = 0.1, rate = 4) if (interactive() || in_pkgdown_example()) widget options(saveopts)
saveopts <- options(rgl.useNULL = TRUE) theta <- seq(0, 4*pi, length.out = 100) xyz <- cbind(sin(theta), cos(theta), sin(theta/2)) lineid <- plot3d(xyz, type="l", alpha = 0, lwd = 5, col = "blue")["data"] widget <- rglwidget() %>% playwidget(ageControl(births = theta, ages = c(-4*pi, -4*pi, 1-4*pi, 0, 0, 1), objids = lineid, alpha = c(0, 1, 0, 0, 1, 0)), start = 0, stop = 4*pi, step = 0.1, rate = 4) if (interactive() || in_pkgdown_example()) widget options(saveopts)
These functions allow comparison of mesh3d objects, ignoring irrelevant differences.
compare_proxy.mesh3d
can function as a compare_proxy
method for the
waldo package, by stripping out NULL
components
and ordering other components alphabetically by name.
all.equal.mesh3d
compares mesh3d objects by using
compare_proxy.mesh3d
to standardize them, then
using the regular all.equal
function to
compare them.
## S3 method for class 'mesh3d' all.equal(target, current, ...) compare_proxy.mesh3d(x, path = "x")
## S3 method for class 'mesh3d' all.equal(target, current, ...) compare_proxy.mesh3d(x, path = "x")
target , current
|
Two mesh3d objects to compare. |
x |
A single mesh3d object to standardize. |
path |
The string to use in a waldo display of this object. |
... |
Additional parameters to pass to |
all.equal.mesh3d
returns TRUE
, or a character
vector describing (some of) the differences.
compare_proxy.mesh3d
returns a list containing two
components:
a copy of x
with
relevant components in alphabetical order.
a modification of the path label for x
waldo is not an installation requirement for rgl
and rgl will never cause it to be loaded.
The compare_proxy.mesh3d
function will only be
registered as a method for waldo::compare_proxy
if you load waldo before rgl, as would normally
happen during testing using testthat, or if you load
it before calling mesh3d
,
as might happen if you are doing manual tests.
Given starting and ending points on a sphere and the center of the sphere, draw the great circle arc between the starting and ending points. If the starting and ending points have different radii, a segment of a logarithmic spiral will join them, unless they are in the same direction, in which case a straight line will join them.
arc3d(from, to, center, radius, n, circle = 50, base = 0, plot = TRUE, ...)
arc3d(from, to, center, radius, n, circle = 50, base = 0, plot = TRUE, ...)
from |
One or more points from which to start arcs. |
to |
One or more destination points. |
center |
One or more center points. |
radius |
If not missing, a vector of length |
n |
If not missing, how many segments to use between the first and last point. If missing, a value will be calculated based on the angle between starting and ending points as seen from the center. |
circle |
How many segments would be used if the arc went completely around a circle. |
base |
See Details below. |
plot |
Should the arcs be plotted, or returned as a matrix? |
... |
Additional parameters to pass to |
If any of from
, to
or center
is an n by 3
matrix with n > 1, multiple arcs will be drawn by recycling
each of these parameters to the number of rows of the longest
one.
If the vector lengths of from - center
and
to - center
differ, then instead of a spherical
arc, the function will draw a segment of a logarithmic spiral joining
the two points.
By default, the arc is drawn along the shortest great
circle path from from
to to
, but the
base
parameter can be used to modify this.
If base = 1
is used, the longer arc will be followed.
Larger positive integer values will result in base - 1
loops in that direction completely around the sphere.
Negative values will draw the curve in the same direction
as the shortest arc, but with abs(base)
full loops.
It doesn't make much sense to ask for such loops unless
the radii of from
and to
differ, because
spherical arcs would overlap.
Normally the base
parameter is left at its default
value of 0
.
When base
is non-zero, the curve will be constructed in multiple
pieces, between from
, to
, -from
and -to
,
for as many steps as necessary. If n
is specified, it
will apply to each of these pieces.
If plot = TRUE
, called mainly for the side effect of drawing arcs. Invisibly
returns the object ID of the collection of arcs.
If plot = FALSE
, returns a 3 column matrix containing
the points that would be drawn as the arcs.
Duncan Murdoch
normalize <- function(v) v/sqrt(sum(v^2)) # These vectors all have the same length from <- t(apply(matrix(rnorm(9), ncol = 3), 1, normalize)) to <- normalize(rnorm(3)) center <- c(0, 0, 0) open3d() spheres3d(center, radius = 1, col = "white", alpha = 0.2) arc3d(from, to, center, col = "red") arc3d(from, 2*to, center, col = "blue") text3d(rbind(from, to, center, 2*to), texts = c(paste0("from", 1:3), "to", "center", "2*to"), depth_mask = FALSE, depth_test = "always")
normalize <- function(v) v/sqrt(sum(v^2)) # These vectors all have the same length from <- t(apply(matrix(rnorm(9), ncol = 3), 1, normalize)) to <- normalize(rnorm(3)) center <- c(0, 0, 0) open3d() spheres3d(center, radius = 1, col = "white", alpha = 0.2) arc3d(from, to, center, col = "red") arc3d(from, 2*to, center, col = "blue") text3d(rbind(from, to, center, 2*to), texts = c(paste0("from", 1:3), "to", "center", "2*to"), depth_mask = FALSE, depth_test = "always")
Draws various types of arrows in a scene.
arrow3d(p0 = c(1, 1, 1), p1 = c(0, 0, 0), barblen, s = 1/3, theta = pi/12, type = c("extrusion", "lines", "flat", "rotation"), n = 3, width = 1/3, thickness = 0.618 * width, spriteOrigin = NULL, plot = TRUE, ...)
arrow3d(p0 = c(1, 1, 1), p1 = c(0, 0, 0), barblen, s = 1/3, theta = pi/12, type = c("extrusion", "lines", "flat", "rotation"), n = 3, width = 1/3, thickness = 0.618 * width, spriteOrigin = NULL, plot = TRUE, ...)
p0 |
The base of the arrow. |
p1 |
The head of the arrow. |
barblen |
The length of the barbs (in display coordinates).
Default given by |
s |
The length of the barbs as a fraction of line length. Ignored if |
theta |
Opening angle of barbs |
type |
Type of arrow to draw. Choose one from the list of defaults. Can be abbreviated. See below. |
n |
Number of barbs. |
width |
Width of shaft as fraction of barb width. |
thickness |
Thickness of shaft as fraction of barb width. |
spriteOrigin |
If arrow is to be replicated as sprites, the origins relative to which the sprites are drawn. |
plot |
If |
... |
Material properties passed to |
Four types of arrows can be drawn. The shapes
of all of them are affected by p0
, p1
, barblen
,
s
, theta
, material properties
in ...
, and spriteOrigin
. Other parameters
only affect some of the types, as shown.
"extrusion"
(default) A 3-dimensional
flat arrow, drawn with shade3d
. Affected by width
, thickness
and
smooth
.
"lines"
Drawn with lines, similar to arrows
, drawn with segments3d
. Affected by n
.
"flat"
A flat arrow, drawn with polygon3d
. Affected by width
and smooth
.
"rotation"
A solid of rotation,
drawn with shade3d
. Affected by
n
and width
.
Normally this function draws just one arrow from
p0
to p1
, but
if spriteOrigin
is given (in any form
that xyz.coords(spriteOrigin)
can
handle), arrows will be drawn for each point
specified, with p0
and p1
interpreted relative to those origins. The
arrows will be drawn as 3D sprites which will
maintain their orientation as the scene is rotated, so this is a good way to indicate
particular locations of interest in the scene.
If plot = TRUE
(the default), this is
called mainly for the side effect of drawing
the arrow; invisibly returns the id(s) of
the objects drawn.
If plot = FALSE
, the data that would be
used in the plot (not including material
properties) is returned.
Design based on heplots::arrow3d
, which contains modifications by Michael Friendly
to a function posted by Barry Rowlingson to R-help on 1/10/2010. Additions by Duncan Murdoch.
xyz <- matrix(rnorm(300), ncol = 3) plot3d(xyz) arrow3d(xyz[1,], xyz[2,], type = "extrusion", col = "red") arrow3d(xyz[3,], xyz[4,], type = "flat", col = "blue") arrow3d(xyz[5,], xyz[6,], type = "rotation", col = "green") arrow3d(xyz[7,], xyz[8,], type = "lines", col = "black") arrow3d(spriteOrigin = xyz[9:12,], col = "purple")
xyz <- matrix(rnorm(300), ncol = 3) plot3d(xyz) arrow3d(xyz[1,], xyz[2,], type = "extrusion", col = "red") arrow3d(xyz[3,], xyz[4,], type = "flat", col = "blue") arrow3d(xyz[5,], xyz[6,], type = "rotation", col = "green") arrow3d(xyz[7,], xyz[8,], type = "lines", col = "black") arrow3d(spriteOrigin = xyz[9:12,], col = "purple")
The as.mesh3d
generic function converts various objects
to mesh3d
objects.
The default method takes takes a matrix of vertices
as input and (optionally) merges repeated vertices, producing a mesh3d
object as output. It will contain either triangles or quads or segments or points
according to the type
argument.
If the generic is called without any argument, it will pass
all RGL ids from the current scene to the
as.mesh3d.rglId
method.
as.mesh3d(x, ...) ## Default S3 method: as.mesh3d(x, y = NULL, z = NULL, type = c("triangles", "quads", "segments", "points"), smooth = FALSE, tolerance = sqrt(.Machine$double.eps), notEqual = NULL, merge = TRUE, ..., triangles)
as.mesh3d(x, ...) ## Default S3 method: as.mesh3d(x, y = NULL, z = NULL, type = c("triangles", "quads", "segments", "points"), smooth = FALSE, tolerance = sqrt(.Machine$double.eps), notEqual = NULL, merge = TRUE, ..., triangles)
x , y , z
|
For the generic, |
type |
What type of things should be in the mesh? Tries this list in order until it finds one that works. |
smooth |
If |
tolerance |
The numerical tolerance to be used in |
notEqual |
If not |
merge |
Should apparently equal vertices be merged? |
... |
|
triangles |
Deprecated. If present, |
The motivation for this function is the following problem: I was asked whether RGL could render a surface made up of triangles or quadrilaterals to look smooth. It can do that, but needs normals at each vertex; they should be the average of the normals for each polygon sharing that vertex. Then OpenGL will interpolate the normals across the polygons and give the illusion of smoothness.
To do this, it needs to know which polygons share each vertex. If the
surface is described as a list of triangles or quadrilaterals, that
means identifying vertices that are in multiple polygons, and converting
the representation to a "mesh3d"
object (which is a matrix of vertices
and a matrix of vertex numbers making up triangles or quads). Then the
addNormals
function will add the normals.
Sometimes two polygons will share vertices (within numerical
tolerance) without the user wanting them to be considered internal to
the surface, or might want one sharp edge in an otherwise smooth
surface. This means I needed a way to declare that two vertices from
the original list of vertices in the triangles or quads are "not equal",
even when they test numerically equal. That's what the notEqual
matrix specifies.
A "mesh3d"
object with the same faces as in the
input, but (if merge=TRUE
) with vertices that test equal to
within tolerance
merged.
Duncan Murdoch
xyz <- matrix(c(-1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1), byrow = TRUE, ncol = 3) mesh <- as.mesh3d(xyz, type = "quads", col = "red") mesh$vb mesh$ib open3d() shade3d(mesh) # Stop vertices 2 and 5 from being merged notEQ <- matrix(FALSE, 12, 12) notEQ[2, 5] <- TRUE mesh <- as.mesh3d(xyz, type = "quads", notEqual = notEQ) mesh$vb mesh$ib
xyz <- matrix(c(-1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1), byrow = TRUE, ncol = 3) mesh <- as.mesh3d(xyz, type = "quads", col = "red") mesh$vb mesh$ib open3d() shade3d(mesh) # Stop vertices 2 and 5 from being merged notEQ <- matrix(FALSE, 12, 12) notEQ[2, 5] <- TRUE mesh <- as.mesh3d(xyz, type = "quads", notEqual = notEQ) mesh$vb mesh$ib
The alphashape3d::ashape3d
function computes the
3D -shape of a cloud of points. This is an approximation
to the visual outline of the cloud. It may include isolated
points, line segments, and triangular faces: this function
converts the triangular faces to an RGL
tmesh3d
object.
## S3 method for class 'ashape3d' as.mesh3d(x, alpha = x$alpha[1], tri_to_keep = 2L, col = "gray", smooth = FALSE, normals = NULL, texcoords = NULL, ...)
## S3 method for class 'ashape3d' as.mesh3d(x, alpha = x$alpha[1], tri_to_keep = 2L, col = "gray", smooth = FALSE, normals = NULL, texcoords = NULL, ...)
x |
An object of class |
alpha |
Which |
tri_to_keep |
Which triangles to keep. Expert use only: see |
col |
The surface colour. |
smooth |
Whether to attempt to add normals to make the surface look smooth. See the Details below. |
normals , texcoords
|
Normals and texture coordinates at each vertex can be specified. |
... |
Additional arguments to pass to use as |
Edelsbrunner and Mucke's (1994) -shape algorithm is
intended to compute a surface of a general cloud of points.
Unlike the convex hull, the cloud may have voids, isolated points, and other oddities. This function is designed to
work in the case where the surface is made up of
simple polygons.
If smooth = TRUE
, this method attempts to orient all
of the triangles in the surface consistently and add normals
at each vertex by averaging the triangle normals.
However, for some point clouds, the -shape will contain
sheets of polygons with a few solid polyhedra embedded.
This does not allow a consistent definition of "inside"
and outside. If this is detected, a warning is issued
and the resulting mesh will likely contain boundaries
where the assumed orientation of triangles changes, resulting
in ugly dark lines through the shape. Larger values
of
alpha
in the call to alphashape3d::ashape3d
may help.
Methods for plot3d
and persp3d
are also defined: they call the as.mesh3d
method and then plot the result.
A "mesh3d"
object, suitable for plotting.
Duncan Murdoch
Edelsbrunner, H., Mucke, E. P. (1994). Three-Dimensional Alpha Shapes. ACM Transactions on Graphics, 13(1), pp.43-72.
Lafarge, T. and Pateiro-Lopez, B. (2017). alphashape3d: Implementation of the 3D Alpha-Shape for the Reconstruction of 3D Sets from a Point Cloud. R package version 1.3.
if (requireNamespace("alphashape3d", quietly = TRUE)) { set.seed(123) n <- 400 # 1000 gives a nicer result, but takes longer xyz <- rbind(cbind(runif(n), runif(n), runif(n)), cbind(runif(n/8, 1, 1.5), runif(n/8, 0.25, 0.75), runif(n/8, 0.25, 0.75))) ash <- suppressMessages(alphashape3d::ashape3d(xyz, alpha = 0.2)) m <- as.mesh3d(ash, smooth = TRUE) open3d() mfrow3d(1, 2, sharedMouse = TRUE) plot3d(xyz, size = 1) plot3d(m, col = "red", alpha = 0.5) points3d(xyz, size = 1) }
if (requireNamespace("alphashape3d", quietly = TRUE)) { set.seed(123) n <- 400 # 1000 gives a nicer result, but takes longer xyz <- rbind(cbind(runif(n), runif(n), runif(n)), cbind(runif(n/8, 1, 1.5), runif(n/8, 0.25, 0.75), runif(n/8, 0.25, 0.75))) ash <- suppressMessages(alphashape3d::ashape3d(xyz, alpha = 0.2)) m <- as.mesh3d(ash, smooth = TRUE) open3d() mfrow3d(1, 2, sharedMouse = TRUE) plot3d(xyz, size = 1) plot3d(m, col = "red", alpha = 0.5) points3d(xyz, size = 1) }
This method attempts to read the attributes of objects in the rgl display and construct a mesh3d object to approximate them.
## S3 method for class 'rglId' as.mesh3d(x, type = NA, subscene = NA, ...)
## S3 method for class 'rglId' as.mesh3d(x, type = NA, subscene = NA, ...)
x |
A vector of RGL identifiers of objects in the specified subscene. |
type |
A vector of names of types of shapes to convert. Other shapes will be ignored. |
subscene |
Which subscene to look in; the default |
... |
Ignored. |
This function attempts to construct a triangle mesh to approximate
one or more objects from the current display. It can handle objects of types from c("points", "lines", "linestrip", "triangles", "quads", "planes", "surface")
.
Since this method only produces meshes containing points, segments and triangles, they won't necessarily be an exact match to the original object.
If the generic as.mesh3d
is called with no x
argument, this method will be called with x
set to the
ids in the current scene.
A mesh object.
Duncan Murdoch
as.triangles3d.rglId
for extracting the triangles,
clipMesh3d
to apply complex clipping to a mesh object.
# volcano example taken from "persp" # data(volcano) z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(y) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d(useNULL = TRUE) surface3d(x, y, z, color = col) m <- as.mesh3d() close3d() open3d() shade3d(m)
# volcano example taken from "persp" # data(volcano) z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(y) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d(useNULL = TRUE) surface3d(x, y, z, color = col) m <- as.mesh3d() close3d() open3d() shade3d(m)
rglscene
object.
This is a placeholder generic function, to allow other
packages to create "rglscene"
objects compatible with
the objects produced by scene3d
.
No methods are currently defined in rgl.
as.rglscene(x, ...)
as.rglscene(x, ...)
x |
Object to convert. |
... |
Other parameters to pass to methods. |
Converts the quads in a mesh version of an object to triangles by splitting them up. Optionally drops any point or segment components.
as.tmesh3d(x, ...) ## Default S3 method: as.tmesh3d(x, drop = FALSE, ...) ## S3 method for class 'mesh3d' as.tmesh3d(x, drop = FALSE, keepTags = FALSE, ...)
as.tmesh3d(x, ...) ## Default S3 method: as.tmesh3d(x, drop = FALSE, ...) ## S3 method for class 'mesh3d' as.tmesh3d(x, drop = FALSE, keepTags = FALSE, ...)
x |
An object from which to create a triangular mesh object. |
drop |
If |
keepTags |
Whether to include the |
... |
Ignored in the |
The default method simply calls as.mesh3d(x, ...)
and passes the result to the "mesh3d"
method.
A "mesh3d"
object containing no quads. If drop = TRUE
, it will only contain triangles.
If keepTags = TRUE
, a "tags"
element will be
added to the result. For details, see the clipMesh3d
help page.
Older versions of rgl had a "tmesh3d"
class
for meshes of triangles. That class is no longer used:
as.tmesh3d
and tmesh3d
both produce
"mesh3d"
objects.
Duncan Murdoch
as.triangles3d
to get just the coordinates.
x <- cuboctahedron3d() x # has quads and triangles as.tmesh3d(x) # has only triangles
x <- cuboctahedron3d() x # has quads and triangles as.tmesh3d(x) # has only triangles
This generic and its methods extract or creates a matrix of coordinates
of triangles from an object, suitable for passing to triangles3d
.
as.triangles3d(obj, ...) ## S3 method for class 'rglId' as.triangles3d(obj, attribute = c("vertices", "normals", "texcoords", "colors"), subscene = NA, ...)
as.triangles3d(obj, ...) ## S3 method for class 'rglId' as.triangles3d(obj, attribute = c("vertices", "normals", "texcoords", "colors"), subscene = NA, ...)
obj |
The object to convert. |
attribute |
Which attribute of an RGL object to extract? |
subscene |
Which subscene is this object in? |
... |
Additional arguments used by the methods. |
The method for "rglId"
objects can extract several different
attributes, organizing them as it would organize the vertices for
the triangles.
An n x 3
matrix containing the vertices of triangles making
up the object. Each successive 3 rows of the matrix corresponds to
a triangle.
If the attribute doesn't exist, NULL
will be returned.
Duncan Murdoch
as.mesh3d
to also capture material properties.
open3d() x <- surface3d(x = 1:10, y = 1:10, z = rnorm(100), col = "red") tri <- as.triangles3d(x) open3d() triangles3d(tri, col = "blue")
open3d() x <- surface3d(x = 1:10, y = 1:10, z = rnorm(100), col = "red") tri <- as.triangles3d(x) open3d() triangles3d(tri, col = "blue")
This function sets the apparent ratios of the x, y, and z axes of the current bounding box.
aspect3d(x, y = NULL, z = NULL)
aspect3d(x, y = NULL, z = NULL)
x |
The ratio for the x axis, or all three ratios, or |
y |
The ratio for the y axis |
z |
The ratio for the z axis |
If the ratios are all 1, the bounding box will be displayed as a cube approximately filling the display.
Values may be set larger or smaller as desired. Aspect "iso"
signifies that the
coordinates should all be displayed at the same scale, i.e. the bounding box should not be
rescaled. (This corresponds to the default display before aspect3d
has been called.)
Partial matches to "iso"
are allowed.
aspect3d
works by modifying par3d("scale")
.
The previous value of the scale is returned invisibly.
Duncan Murdoch
x <- rnorm(100) y <- rnorm(100)*2 z <- rnorm(100)*3 open3d() plot3d(x, y, z) aspect3d(1, 1, 0.5) highlevel() # To trigger display open3d() plot3d(x, y, z) aspect3d("iso") highlevel()
x <- rnorm(100) y <- rnorm(100)*2 z <- rnorm(100)*3 open3d() plot3d(x, y, z) aspect3d(1, 1, 0.5) highlevel() # To trigger display open3d() plot3d(x, y, z) aspect3d("iso") highlevel()
The asRow
function arranges objects in a row in the display;
the getWidgetId
function extracts the HTML element ID
from an HTML widget.
asRow(..., last = NA, height = NULL, colsize = 1) getWidgetId(widget)
asRow(..., last = NA, height = NULL, colsize = 1) getWidgetId(widget)
... |
Either a single |
last |
If not |
height |
An optional height for the resulting row. This is normally specified in pixels, but will be rescaled as necessary to fit the display. |
colsize |
A vector of relative widths for the columns in the row. |
widget |
A single HTML widget from which to extract the HTML element ID. |
Using asRow
requires that the manipulateWidget
package is installed.
asRow
produces a "combineWidgets"
object which
is a single column whose last element is another "combineWidgets"
object which is a single row.
If n
objects are given as input and last
is given a value
less than n
, the first n - last
objects will be displayed
in a column above the row containing the last
objects.
asRow
returns a single "combineWidgets"
object suitable for display or nesting
within a more complicated display.
getWidgetId
returns a character string containing the
HTML element ID of the widget.
Duncan Murdoch
pipe for the %>%
operator.
if (requireNamespace("manipulateWidget", quietly = TRUE) && require("crosstalk", quietly = TRUE)) { sd <- SharedData$new(mtcars) ids <- plot3d(sd$origData(), col = mtcars$cyl, type = "s") # Copy the key and group from existing shared data rglsd <- rglShared(ids["data"], key = sd$key(), group = sd$groupName()) w <- rglwidget(shared = rglsd) %>% asRow("Mouse mode: ", rglMouse(getWidgetId(.)), "Subset: ", filter_checkbox("cylinderselector", "Cylinders", sd, ~ cyl, inline = TRUE), last = 4, colsize = c(1,2,1,2), height = 60) if (interactive() || in_pkgdown_example()) w }
if (requireNamespace("manipulateWidget", quietly = TRUE) && require("crosstalk", quietly = TRUE)) { sd <- SharedData$new(mtcars) ids <- plot3d(sd$origData(), col = mtcars$cyl, type = "s") # Copy the key and group from existing shared data rglsd <- rglShared(ids["data"], key = sd$key(), group = sd$groupName()) w <- rglwidget(shared = rglsd) %>% asRow("Mouse mode: ", rglMouse(getWidgetId(.)), "Subset: ", filter_checkbox("cylinderselector", "Cylinders", sd, ~ cyl, inline = TRUE), last = 4, colsize = c(1,2,1,2), height = 60) if (interactive() || in_pkgdown_example()) w }
These functions draw axes, boxes and text outside the range of the data.
axes3d
, box3d
and title3d
are the higher level functions;
normally the others need not be called directly by users.
axes3d(edges = "bbox", labels = TRUE, tick = TRUE, nticks = 5, box = FALSE, expand = 1.03, ...) box3d(...) title3d(main = NULL, sub = NULL, xlab = NULL, ylab = NULL, zlab = NULL, line = NA, level = NA, floating = NULL, ...) axis3d(edge, at = NULL, labels = TRUE, tick = TRUE, line = 0, pos = NULL, nticks = 5, ...) mtext3d(text, edge, at = NULL, line = 0, level = 0, floating = FALSE, pos = NA, ...)
axes3d(edges = "bbox", labels = TRUE, tick = TRUE, nticks = 5, box = FALSE, expand = 1.03, ...) box3d(...) title3d(main = NULL, sub = NULL, xlab = NULL, ylab = NULL, zlab = NULL, line = NA, level = NA, floating = NULL, ...) axis3d(edge, at = NULL, labels = TRUE, tick = TRUE, line = 0, pos = NULL, nticks = 5, ...) mtext3d(text, edge, at = NULL, line = 0, level = 0, floating = FALSE, pos = NA, ...)
edges |
a code to describe which edge(s) of the box to use; see Details below |
labels |
whether to label the axes, or (for |
tick |
whether to use tick marks |
nticks |
suggested number of ticks |
box |
draw the full box if |
expand |
how much to expand the box around the data |
main |
the main title for the plot |
sub |
the subtitle for the plot |
xlab , ylab , zlab
|
the axis labels for the plot |
line , level
|
the "line" of the plot margin to draw the label on, and "level" above or below it |
floating |
which mode of axis labels? One of
|
edge , pos
|
the position at which to draw the axis or text |
text |
the text to draw |
at |
the value of a coordinate at which to draw the axis or labels. |
... |
additional parameters which are passed to |
The rectangular prism holding the 3D plot has 12 edges. They are identified
using 3 character strings. The first character (‘x’, ‘y’, or ‘z’) selects
the direction of the axis. The next two characters are each ‘-’ or ‘+’,
selecting the lower or upper end of one of the other coordinates. If only
one or two characters are given, the remaining characters normally default to ‘-’ (but with mtext3d(..., floating = TRUE)
the default is ‘+’; see below).
For example edge = 'x+'
draws an x-axis at the high level of y and the
low level of z.
By default, axes3d
uses the bbox3d
function to draw the axes.
The labels will move so that they do not obscure the data. Alternatively,
a vector of arguments as described above may be used, in which case
fixed axes are drawn using axis3d
.
As of rgl version 0.106.21, axis drawing has changed
significantly. Text drawn in the margins
will
adapt to the margins (see bbox3d
). The
edge
and floating
parameters will be recorded
in the margin
and floating
material
properties for the object.
If floating = FALSE
, they will be drawn on the
specified edge.
If floating = TRUE
, they will
move as the axis labels move when the scene is rotated.
The signs on the edge specification are interpreted as
agreeing with the axis ticks ‘+’ or disagreeing ‘-’.
For example, "x++"
will draw text on the x axis
in the same edge as the ticks, while "x--"
will draw
on the opposite edge.
The final possible value for floating
in mtext3d
is
NA
, which reproduces legacy rgl
behaviour. In
this case the labels are not tied to the bounding box,
so they should be drawn last, or they could appear inside
the box, overlapping the data.
In title3d
floating = NULL
(the default)
indicates the main title
and subtitle will be fixed while the axis labels will be
floating. The default locations for title and subtitle
are line = 2
and
level = 2
on edges "x++"
and "x--"
respectively. The axis labels float at line = 4
and level = 1
on the same edge as the ticks.
The at
parameter in axis3d
is the location
of the ticks, defaulting to pretty
locations.
In mtext3d
the at
parameter is the location
on the specified axis
at which to draw the text, defaulting to the middle of the
bounding box.
The line
parameter is the line counting out
from the box in the same direction as the axis ticks,
and level
is the line out in the orthogonal
direction. The ticks run from line = 0
to line = 1
, and the
tick labels are drawn at line = 2
. Both are drawn at level 0.
The pos
parameter is only supported in legacy mode. If it is a numeric vector of length 3, edge
determines
the direction of the axis and the tick marks, and the values of the
other two coordinates in pos
determine the position.
The level
parameter is ignored in legacy mode.
For mtext3d
in floating = TRUE
or floating = FALSE
mode,
there are 3 special values for the at
parameter: it may
be -Inf
, NA
or +Inf
, referring
to the bottom, middle or top of the given axis respectively.
These functions are called for their side effects. They return the object IDs of objects added to the scene.
mtext3d
is a wrapper for text3d
that sets the margin
and floating
material
properties. In fact, these properties can be set for many
kinds of objects (most kinds where it would make sense),
with the effect that the object will be drawn
in the margin, with x
coordinate corresponding to
at
, y
corresponding to line
, and z
corresponding to level
.
Duncan Murdoch
Classic graphics functions axis
, box
,
title
, mtext
are related.
See RGL functions bbox3d
for drawing the
box around the plot, and
setAxisCallbacks
for customized axes.
open3d() points3d(rnorm(10), rnorm(10), rnorm(10)) # First add standard axes axes3d() # and one in the middle (the NA will be ignored, a number would # do as well) axis3d('x', pos = c(NA, 0, 0)) # add titles title3d('main', 'sub', 'xlab', 'ylab', 'zlab') # Use a log scale for z open3d() x <- rnorm(10) y <- rnorm(10) z <- exp(rnorm(10, mean = 3, sd = 2)) logz <- log10(z) zticks <- axisTicks(range(logz), log = TRUE) zat <- log10(zticks) plot3d(x, y, logz, zlab = "z") axes3d(zat = zat, zlab = zticks, box = TRUE)
open3d() points3d(rnorm(10), rnorm(10), rnorm(10)) # First add standard axes axes3d() # and one in the middle (the NA will be ignored, a number would # do as well) axis3d('x', pos = c(NA, 0, 0)) # add titles title3d('main', 'sub', 'xlab', 'ylab', 'zlab') # Use a log scale for z open3d() x <- rnorm(10) y <- rnorm(10) z <- exp(rnorm(10, mean = 3, sd = 2)) logz <- log10(z) zticks <- axisTicks(range(logz), log = TRUE) zat <- log10(zticks) plot3d(x, y, logz, zlab = "z") axes3d(zat = zat, zlab = zticks, box = TRUE)
Set up the bounding box decoration.
bbox3d(xat = NULL, yat = NULL, zat = NULL, xunit = "pretty", yunit = "pretty", zunit = "pretty", expand = 1.03, draw_front = FALSE, xlab=NULL, ylab=NULL, zlab=NULL, xlen=5, ylen=5, zlen=5, marklen=15.0, marklen.rel=TRUE, ...)
bbox3d(xat = NULL, yat = NULL, zat = NULL, xunit = "pretty", yunit = "pretty", zunit = "pretty", expand = 1.03, draw_front = FALSE, xlab=NULL, ylab=NULL, zlab=NULL, xlen=5, ylen=5, zlen=5, marklen=15.0, marklen.rel=TRUE, ...)
xat , yat , zat
|
vector specifying the tickmark positions |
xlab , ylab , zlab
|
character vector specifying the tickmark labeling |
xunit , yunit , zunit
|
value specifying the tick mark base for uniform tick mark layout |
xlen , ylen , zlen
|
value specifying the number of tickmarks |
marklen |
value specifying the length of the tickmarks |
marklen.rel |
logical, if TRUE tick mark length is calculated using 1/ |
expand |
value specifying how much to expand the bounding box around the data |
draw_front |
draw the front faces of the bounding box |
... |
Material properties (or other |
Four different types of tick mark layouts are possible.
This description applies to the X axis; other axes are similar:
If xat
is not NULL
, the ticks are set up at custom positions.
If xunit
is numeric but not zero, it defines the tick mark base. If
it is "pretty"
(the default in bbox3d
), ticks are set at pretty
locations.
If xlen
is not zero, it specifies the number of ticks
(a suggestion if xunit
is "pretty"
).
The first color specifies the bounding box, while the second one specifies the tick mark and font color.
bbox3d
defaults to pretty
locations for the axis labels and a slightly larger
box, whereas rgl.bbox
covers the exact range.
axes3d
offers more flexibility in the specification of the axes, but
they are static, unlike those drawn by bbox3d
.
This function is called for the side effect of setting the bounding box decoration.
A shape ID is returned to allow pop3d
to delete it.
open3d() points3d(rnorm(100), rnorm(100), rnorm(100)) bbox3d(color = c("#333377", "black"), emission = "#333377", specular = "#3333FF", shininess = 5, alpha = 0.8)
open3d() points3d(rnorm(100), rnorm(100), rnorm(100)) bbox3d(color = c("#333377", "black"), emission = "#333377", specular = "#3333FF", shininess = 5, alpha = 0.8)
Set up the background of the scene.
bg3d(color, sphere=FALSE, back="lines", fogtype="none", fogScale = 1, col, ...)
bg3d(color, sphere=FALSE, back="lines", fogtype="none", fogScale = 1, col, ...)
color , col
|
See Details below. |
sphere |
logical: if |
back |
Specifies the fill style of the sphere geometry. See |
fogtype |
fog type:
Fog only applies to objects with |
fogScale |
Scaling for fog. See Details. |
... |
Additional material properties. See |
The background color is taken from color
or col
if color
is missing.
The first entry
is used for background clearing and as the fog color.
The second (if present) is used for background sphere geometry.
If color
and col
are both missing, the default is found in
the r3dDefaults$bg
list, or "white"
is used
if nothing is specified there.
If sphere
is set to TRUE
, an environmental
sphere enclosing the whole scene is drawn.
If not, but the material properties include a bitmap as a texture, the bitmap is drawn in the background of the scene. (The bitmap colors modify the general color setting.)
If neither a sphere nor a bitmap background is drawn, the background is filled with a solid color.
The fogScale
parameter should be a positive value
to change the density of the fog in the plot. For
fogtype = "linear"
it multiplies the density of the
fog; for the exponential fog types it multiplies the density
parameter used in the display.
See
the OpenGL 2.1 reference
for the formulas used in the fog calculations within R (though the
"exp2"
formula appears to be wrong, at least on my
system). In WebGL displays,
the following rules are used. They appear to match the
rules used in R on my system.
For "linear"
fog, the near clipping plane is
taken as , and the
far clipping plane is taken as
. The
amount of fog is
clamped to a 0 to 1
range, where
.
For "exp"
and "exp2"
fog, the observer location
is negative at a distance depending on the field of view.
The formula for the distance is
where .
We calculate
so runs from 0 at the observer to
1 at the far clipping plane.
For "exp"
fog, the amount of fog is
.
For "exp2"
fog, the amount of fog is
.
material3d
, bgplot3d
to add a 2D plot as background.
open3d() # a simple white background bg3d("white") # the holo-globe (inspired by star trek): bg3d(sphere = TRUE, color = c("black", "green"), lit = FALSE, back = "lines" ) # an environmental sphere with a nice texture. bg3d(sphere = TRUE, texture = system.file("textures/sunsleep.png", package = "rgl"), back = "filled" ) # The same texture as a fixed background open3d() bg3d(texture = system.file("textures/sunsleep.png", package = "rgl"), col = "white")
open3d() # a simple white background bg3d("white") # the holo-globe (inspired by star trek): bg3d(sphere = TRUE, color = c("black", "green"), lit = FALSE, back = "lines" ) # an environmental sphere with a nice texture. bg3d(sphere = TRUE, texture = system.file("textures/sunsleep.png", package = "rgl"), back = "filled" ) # The same texture as a fixed background open3d() bg3d(texture = system.file("textures/sunsleep.png", package = "rgl"), col = "white")
Add a 2D plot or a legend in the background of an RGL window.
bgplot3d(expression, bg.color = getr3dDefaults("bg", "color"), magnify = 1, ...) legend3d(...)
bgplot3d(expression, bg.color = getr3dDefaults("bg", "color"), magnify = 1, ...) legend3d(...)
expression |
Any plotting commands to produce a plot. |
bg.color |
The color to use for the background. |
magnify |
Multiplicative factor to apply to size of window when producing background plot. |
... |
For |
The bgplot3d
function opens a png
device and executes expression
,
producing a plot there. This plot is then used as a bitmap background for the current
RGL subscene.
The legend3d
function draws a standard 2D legend to the background of the current
subscene by calling bgplot3d
to open a device, and setting up a plot region there
to fill the whole display.
The bgplot3d
function invisibly returns the ID of the background object that was created,
with attribute "value"
holding the value returned when the expression
was
evaluated.
The legend3d
function does similarly. The "value"
attribute is the result
of the call to legend
. The scaling of the coordinates runs from 0 to 1
in X and Y.
Because the background plots are drawn as bitmaps, they do not resize very gracefully. It's best to size your window first, then draw the background at that size.
Duncan Murdoch
bg3d
for other background options.
x <- rnorm(100) y <- rnorm(100) z <- rnorm(100) open3d() # Needs to be a bigger window than the default par3d(windowRect = c(100, 100, 612, 612)) parent <- currentSubscene3d() mfrow3d(2, 2) plot3d(x, y, z) next3d(reuse = FALSE) bgplot3d(plot(y, z)) next3d(reuse = FALSE) bgplot3d(plot(x, z)) next3d(reuse = FALSE) legend3d("center", c("2D Points", "3D Points"), pch = c(1, 16)) useSubscene3d(parent)
x <- rnorm(100) y <- rnorm(100) z <- rnorm(100) open3d() # Needs to be a bigger window than the default par3d(windowRect = c(100, 100, 612, 612)) parent <- currentSubscene3d() mfrow3d(2, 2) plot3d(x, y, z) next3d(reuse = FALSE) bgplot3d(plot(y, z)) next3d(reuse = FALSE) bgplot3d(plot(x, z)) next3d(reuse = FALSE) legend3d("center", c("2D Points", "3D Points"), pch = c(1, 16)) useSubscene3d(parent)
These files typically have one buffer holding all the binary data for a scene.
new()
Buffer$new(json = NULL, binfile = NULL)
json
list read from glTF file.
binfile
optional External binary filename, or raw vector
load()
Load from file.
Buffer$load(uri, buf = 0)
uri
Which file to load.
buf
Which buffer number to load.
saveOpenBuffer()
Write open buffer to connection.
Buffer$saveOpenBuffer(con, buf = 0)
con
Output connection.
buf
Buffer number.
getBuffer()
Get buffer object.
Buffer$getBuffer(buf, default = list(byteLength = 0))
buf
Buffer number.
default
Default buffer object if buf
not found.
A list containing components described here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-buffer.
setBuffer()
Set buffer object.
Buffer$setBuffer(buf, buffer)
buf
Buffer number.
buffer
New value to insert.
openBuffer()
Open a connection for the data in a buffer.
Buffer$openBuffer(buf)
buf
Buffer number.
An open binary connection.
writeBuffer()
Write data to buffer.
Buffer$writeBuffer(values, type, size, buf = 0)
values
Values to write.
type
Type to write.
size
Byte size of each value.
buf
Which buffer to write to.
Byte offset of start of bytes written.
closeBuffer()
Close the connection in a buffer.
If there was a connection open, this will save the
contents in the raw vector bytes
within the buffer object.
Buffer$closeBuffer(buf)
buf
The buffer number.
closeBuffers()
Close any open buffers.
Call this after working with a GLTF file to avoid warnings from R about closing unused connections.
Buffer$closeBuffers()
getBufferview()
Get bufferView
object.
Buffer$getBufferview(bufv)
bufv
bufferView
number.
A list containing components described here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-bufferview.
addBufferView()
Add a new buffer view.
Buffer$addBufferView(values, type, size, target = NULL, buf = 0)
values
Values to put in the view.
type
Type of values.
size
Size of values in bytes.
target
Optional target use for values.
buf
Which buffer to write to.
New bufferView
number.
openBufferview()
Open a connection to a buffer view.
Buffer$openBufferview(bufv)
bufv
Which bufferView
.
A connection.
setBufferview()
Set bufferView
object.
Buffer$setBufferview(bufv, bufferView)
bufv
bufferView
number.
bufferView
New value to insert.
getAccessor()
Get accessor object
Buffer$getAccessor(acc)
acc
Accessor number
A list containing components described here: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-accessor
setAccessor()
Set accessor object.
Buffer$setAccessor(acc, accessor)
acc
Accessor number.
accessor
New value to insert.
readAccessor()
Read data given by accessor number.
Buffer$readAccessor(acc)
acc
Accessor number.
A vector or array as specified in the accessor. For the MATn
types, the 3rd index
indexes the element.
readAccessor0()
Read data given by accessor object.
Buffer$readAccessor0(accessor)
accessor
Accessor object
A vector or array as specified in the accessor. For the MATn
types, the 3rd index
indexes the element.
addAccessor()
Write values to accessor, not including min
and max
.
Buffer$addAccessor( values, target = NULL, types = "anyGLTF", normalized = FALSE )
values
Values to write.
target
Optional target use for values.
types
Which types can be used?
normalized
Are normalized integers allowed?
useDouble
Whether to write doubles or singles.
New accessor number
dataURI()
Convert buffer to data URI.
Buffer$dataURI(buf = 0)
buf
Buffer to convert.
String containing data URI.
as.list()
Convert to list.
Buffer$as.list()
List suitable for writing using JSON.
clone()
The objects of this class are cloneable with this method.
Buffer$clone(deep = FALSE)
deep
Whether to make a deep clone.
Set and get user callbacks on mouse events.
rgl.setMouseCallbacks(button, begin = NULL, update = NULL, end = NULL, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.getMouseCallbacks(button, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.setWheelCallback(rotate, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.getWheelCallback(dev = cur3d(), subscene = currentSubscene3d(dev))
rgl.setMouseCallbacks(button, begin = NULL, update = NULL, end = NULL, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.getMouseCallbacks(button, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.setWheelCallback(rotate, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.getWheelCallback(dev = cur3d(), subscene = currentSubscene3d(dev))
button |
Which button? Use 1 for left, 2 for right, 3 for middle, 4 for wheel. Use 0 to set an action when no button is pressed. |
begin |
Called when mouse down event occurs |
update |
Called when mouse moves |
end |
Called when mouse is released |
rotate |
Called when mouse wheel is rotated |
dev , subscene
|
The RGL device and subscene to work with |
The set functions set event handlers on mouse events that occur within the current RGL window.
The begin
and update
events should be functions taking two arguments; these
will be the mouse coordinates when the event occurs. The end
event handler
takes no arguments. The rotate
event takes a single argument, which will be
equal to 1
if the user pushes the wheel away by one click, and 2
if
the user pulls the wheel by one click.
Alternatively, the handlers may be set to NULL
, the default value, in which case
no action will occur.
If a subscene has multiple listeners, the user action will still only be called for the
subscene that received the mouse event. It should consult par3d("listeners")
if it makes sense to take action on the whole group of subscenes.
The get function retrieves the callbacks that are currently set.
The “no button” mouse handler may be set by
specifying button = 0
. The begin
function will be called the first time the mouse
moves within the subscene, and the update
function will be called repeatedly as it moves. The
end
function will never be called.
The set functions are called for the side effect of setting the mouse event handlers.
The rgl.getMouseCallbacks
function returns a list containing the callback functions or NULL
if no user callback is set.
The rgl.getWheelCallback
returns the callback function or NULL
.
Duncan Murdoch
par3d
to set built-in handlers,
setUserCallbacks
to work with rglwidget
.
pan3d <- function(button, dev = cur3d(), subscene = currentSubscene3d(dev)) { start <- list() begin <- function(x, y) { activeSubscene <- par3d("activeSubscene", dev = dev) start$listeners <<- par3d("listeners", dev = dev, subscene = activeSubscene) for (sub in start$listeners) { init <- par3d(c("userProjection","viewport"), dev = dev, subscene = sub) init$pos <- c(x/init$viewport[3], 1 - y/init$viewport[4], 0.5) start[[as.character(sub)]] <<- init } } update <- function(x, y) { for (sub in start$listeners) { init <- start[[as.character(sub)]] xlat <- 2*(c(x/init$viewport[3], 1 - y/init$viewport[4], 0.5) - init$pos) mouseMatrix <- translationMatrix(xlat[1], xlat[2], xlat[3]) par3d(userProjection = mouseMatrix %*% init$userProjection, dev = dev, subscene = sub ) } } rgl.setMouseCallbacks(button, begin, update, dev = dev, subscene = subscene) cat("Callbacks set on button", button, "of RGL device", dev, "in subscene", subscene, "\n") } open3d() shade3d(icosahedron3d(), col = "yellow") # This only works in the internal display... pan3d(1)
pan3d <- function(button, dev = cur3d(), subscene = currentSubscene3d(dev)) { start <- list() begin <- function(x, y) { activeSubscene <- par3d("activeSubscene", dev = dev) start$listeners <<- par3d("listeners", dev = dev, subscene = activeSubscene) for (sub in start$listeners) { init <- par3d(c("userProjection","viewport"), dev = dev, subscene = sub) init$pos <- c(x/init$viewport[3], 1 - y/init$viewport[4], 0.5) start[[as.character(sub)]] <<- init } } update <- function(x, y) { for (sub in start$listeners) { init <- start[[as.character(sub)]] xlat <- 2*(c(x/init$viewport[3], 1 - y/init$viewport[4], 0.5) - init$pos) mouseMatrix <- translationMatrix(xlat[1], xlat[2], xlat[3]) par3d(userProjection = mouseMatrix %*% init$userProjection, dev = dev, subscene = sub ) } } rgl.setMouseCallbacks(button, begin, update, dev = dev, subscene = subscene) cat("Callbacks set on button", button, "of RGL device", dev, "in subscene", subscene, "\n") } open3d() shade3d(icosahedron3d(), col = "yellow") # This only works in the internal display... pan3d(1)
Version 1.0-2 of deldir is not compatible with rgl. This allows code to avoid trying to call it.
checkDeldir(error = FALSE)
checkDeldir(error = FALSE)
error |
If |
Returns TRUE
if deldir is available in a compatible
version.
checkDeldir()
checkDeldir()
Modifies a mesh3d object so that values of a function are bounded.
clipMesh3d(mesh, fn, bound = 0, greater = TRUE, minVertices = 0, plot = FALSE, keepValues = FALSE, keepTags = FALSE) clipObj3d(ids = tagged3d(tags), fn, bound = 0, greater = TRUE, minVertices = 0, replace = TRUE, tags)
clipMesh3d(mesh, fn, bound = 0, greater = TRUE, minVertices = 0, plot = FALSE, keepValues = FALSE, keepTags = FALSE) clipObj3d(ids = tagged3d(tags), fn, bound = 0, greater = TRUE, minVertices = 0, replace = TRUE, tags)
mesh |
A |
fn |
A function used to determine clipping, or a vector of values from such a function, with one value per vertex. |
bound |
The value(s) of |
greater |
Logical; whether to keep |
minVertices |
See Details below. |
plot |
Logical; whether or not to plot the mesh. |
keepValues |
Logical; whether to save the function values at
each vertex when |
keepTags |
Whether to keep the |
ids |
The RGL id value(s) of objects to clip. |
tags |
Object tags; an alternate way to specify |
replace |
Should the |
These functions transform a mesh3d object or other
RGL objects by removing parts where fn
violates
the bound.
For clipMesh3d
the fn
argument can be any
of the following:
a character vector naming a function (with special
names "x"
, "y"
, and "z"
corresponding
to functions returning those coordinates)
a function
a numeric vector with one value per vertex
NULL
, indicating that the numeric values
are saved in mesh$values
For clipObj3d
any of the above except NULL
may be used.
If fn
is a numeric vector, with one value per vertex, those values will be
used in the test.
If it is a function with formal arguments x
,
y
and z
, it will receive the coordinates of
vertices in those arguments, otherwise it will receive
the coordinates in a single n x 3 matrix. The function
should be vectorized and return one value per vertex,
to check against the bound.
These operations are performed on the mesh:
First, all quads are converted to triangles.
Next, each vertex is checked against the condition.
Modifications to triangles depend
on how many of the vertices satisfy the condition
(fn >= bound
or fn <= bound
, depending on greater
)
for inclusion.
If no vertices in a triangle satisfy the condition, the triangle is omitted.
If one vertex satisfies the condition, the other two vertices
in that triangle are shrunk towards it by assuming fn
is locally linear.
If two vertices satisfy the condition, the third vertex is shrunk along each edge towards each other vertex, forming a quadrilateral made of two new triangles.
If all vertices satisfy the condition, they are included with no modifications.
Modifications to line segments are similar: the segment will be shortened if it crosses the boundary, or omitted if it is entirely out of bounds. Points, spheres, text and sprites will just be kept or rejected.
The minVertices
argument is used to improve the
approximation to the boundary when fn
is a non-linear
function. In that case, the interpolation described above
can be inaccurate. If minVertices
is set to a
positive
number (e.g. 10000
), then each object is modified
by subdivision to have at least that number of vertices,
so that pieces are smaller and the linear interpolation
is more accurate. In the clipObj3d
function,
minVertices
can be a vector, with entries corresponding
to each of the entries in ids
.
If plot = FALSE
,
clipMesh3d
returns new mesh3d object in which all vertices (approximately) satisfy the
clipping condition. Note that the order of vertices will likely
differ from the original order, and new vertices will be added near
the boundary (and if minVertices > 0
, in the
interior). If in addition keepValues = TRUE
,
a component named "values"
will be added to the
mesh containing the values for each vertex.
If keepTags = TRUE
, the tags
component
described below will be added to the output mesh.
If plot = TRUE
, the result will be
plotted with shade3d
and its result returned.
clipObj3d
is called for the side effect of modifying
the scene. It returns a list of new RGL id values
corresponding to the ids
passed as arguments.
keepTags
argumentIf keepTags = TRUE
, a "tags"
element will be
added to the result. It will be a vector with one entry per
point, segment, triangle and quad in the output mesh. (These tags are not related
to the tags used to identify rgl objects.)
The mesh tags may be used to show the correspondence between
the parts of
the input mesh and output mesh.
By default, the tags are constructed as a numerical sequence
over points,
segments, triangles and
quads in the input mesh, in that order, starting from one. This is the same order
used for colours when
shading with
meshColor == "faces"
.
For example, start with a mesh with one point, two segments, three
triangles and four
quads, but no tags
member.
It would implicitly tag the parts from one to ten as
c(1, # the point
2:3, # the two segments
4:6, # the three triangles
7:10) # the four quads
If clipping deleted the segments and the first triangle, the output would contain the seven element result
mesh$tags <- c(1, # the point remains
# no segments now
5:6, # the two remaining triangles
# were previously items 5 and 6
7:10) # the four quads
The tags
output may contain repetitions. For example,
when a triangle is partially clipped and replaced by several
smaller triangles, entries for all of them will contain the value
corresponding to the original triangle.
The mesh$tags
component may be supplied as part of the input
mesh as any type of vector;
the output will propagate values to the new mesh. The input length
must match the total number of points, segments, triangles and
quads in the input mesh or an error will be raised.
Duncan Murdoch
See https://stackoverflow.com/q/56242470/2554330 and https://laustep.github.io/stlahblog/posts/MeshClipping.html for a motivating example.
See contourLines3d
and
filledContour3d
for ways to display function
values without clipping.
# Show the problem that minVertices solves: cube <- cube3d(col = rainbow(6), meshColor = "faces") # This function only has one argument, so it will # be passed x, y and z in columns of a matrix vecnorm <- function(vals) apply(vals, 1, function(row) sqrt(sum(row^2))) open3d() mfrow3d(2, 2, sharedMouse = TRUE) id1 <- shade3d(cube) # All vertices have norm sqrt(3), so this clips nothing: clipObj3d(id1, fn = vecnorm, bound = sqrt(2)) next3d() id2 <- wire3d(cube, lit = FALSE) clipObj3d(id2, fn = vecnorm, bound = sqrt(2)) # This subdivides the cube, and does proper clipping: next3d() id3 <- shade3d(cube) clipObj3d(id3, fn = vecnorm, bound = sqrt(2), minVertices = 200) next3d() id4 <- wire3d(cube, lit = FALSE) clipObj3d(id4, fn = vecnorm, bound = sqrt(2), minVertices = 200)
# Show the problem that minVertices solves: cube <- cube3d(col = rainbow(6), meshColor = "faces") # This function only has one argument, so it will # be passed x, y and z in columns of a matrix vecnorm <- function(vals) apply(vals, 1, function(row) sqrt(sum(row^2))) open3d() mfrow3d(2, 2, sharedMouse = TRUE) id1 <- shade3d(cube) # All vertices have norm sqrt(3), so this clips nothing: clipObj3d(id1, fn = vecnorm, bound = sqrt(2)) next3d() id2 <- wire3d(cube, lit = FALSE) clipObj3d(id2, fn = vecnorm, bound = sqrt(2)) # This subdivides the cube, and does proper clipping: next3d() id3 <- shade3d(cube) clipObj3d(id3, fn = vecnorm, bound = sqrt(2), minVertices = 200) next3d() id4 <- wire3d(cube, lit = FALSE) clipObj3d(id4, fn = vecnorm, bound = sqrt(2), minVertices = 200)
This is a function to produce actions in a web display. A
playwidget
or Shiny
input control (e.g. a sliderInput
control)
sets a value which controls attributes of one or more clipping
planes.
clipplaneControl(a = NULL, b = NULL, c = NULL, d = NULL, plane = 1, clipplaneids = tagged3d(tag), tag, ...)
clipplaneControl(a = NULL, b = NULL, c = NULL, d = NULL, plane = 1, clipplaneids = tagged3d(tag), tag, ...)
a , b , c , d
|
Parameter values for the clipping planes. |
plane |
Which plane in the clipplane object? |
clipplaneids |
The id of the clipplane object. |
tag |
Select clipplane with matching tag. Ignored
if |
... |
Other parameters passed to |
A list of class "rglControl"
of cleaned up parameter
values, to be used in an RGL widget.
Duncan Murdoch
open3d() saveopts <- options(rgl.useNULL = TRUE) xyz <- matrix(rnorm(300), ncol = 3) id <- plot3d(xyz, type="s", col = "blue", zlim = c(-3,3))["clipplanes"] dvals <- c(3, -3) widget <- rglwidget() %>% playwidget(clipplaneControl(d = dvals, clipplaneids = id), start = 0, stop = 1, step = 0.01, rate = 0.5) if (interactive() || in_pkgdown_example()) widget options(saveopts)
open3d() saveopts <- options(rgl.useNULL = TRUE) xyz <- matrix(rnorm(300), ncol = 3) id <- plot3d(xyz, type="s", col = "blue", zlim = c(-3,3))["clipplanes"] dvals <- c(3, -3) widget <- rglwidget() %>% playwidget(clipplaneControl(d = dvals, clipplaneids = id), start = 0, stop = 1, step = 0.01, rate = 0.5) if (interactive() || in_pkgdown_example()) widget options(saveopts)
contourLines3d
draws contour lines on a surface;
filledContour3d
draws filled contours on it.
contourLines3d(obj, ...) ## S3 method for class 'rglId' contourLines3d(obj, ...) ## S3 method for class 'mesh3d' contourLines3d(obj, fn = "z", nlevels = 10, levels = NULL, minVertices = 0, plot = TRUE, ... ) filledContour3d(obj, ...) ## S3 method for class 'rglId' filledContour3d(obj, plot = TRUE, replace = plot, ...) ## S3 method for class 'mesh3d' filledContour3d(obj, fn = "z", nlevels = 20, levels = pretty(range(values), nlevels), color.palette = function(n) hcl.colors(n, "YlOrRd", rev = TRUE), col = color.palette(length(levels) - 1), minVertices = 0, plot = TRUE, keepValues = FALSE, ... )
contourLines3d(obj, ...) ## S3 method for class 'rglId' contourLines3d(obj, ...) ## S3 method for class 'mesh3d' contourLines3d(obj, fn = "z", nlevels = 10, levels = NULL, minVertices = 0, plot = TRUE, ... ) filledContour3d(obj, ...) ## S3 method for class 'rglId' filledContour3d(obj, plot = TRUE, replace = plot, ...) ## S3 method for class 'mesh3d' filledContour3d(obj, fn = "z", nlevels = 20, levels = pretty(range(values), nlevels), color.palette = function(n) hcl.colors(n, "YlOrRd", rev = TRUE), col = color.palette(length(levels) - 1), minVertices = 0, plot = TRUE, keepValues = FALSE, ... )
obj |
The object(s) on which to draw contour lines. |
fn |
The function(s) to be contoured. See Details. |
nlevels |
Suggested number of contour levels if |
levels |
Specified contour values. |
minVertices |
See Details below. |
plot |
Whether to draw the lines or return them in a dataframe. |
... |
For the |
replace |
Whether to delete the objects that are being contoured. |
color.palette |
a color palette function to assign colors in the plot |
col |
the actual colors to use in the plot. |
keepValues |
whether to save the function values at
each vertex when |
For contourLines3d
, the fn
argument can be any
of the following:
a character vector naming one or more functions
a function
a numeric vector with one value per vertex
NULL
, indicating that the numeric values
are saved in obj$values
a list containing any of the above.
For filledContour3d
, only one function can be specified.
The special names "x", "y", "z"
may be used in
fn
to specify functions returning one of those
coordinates. (If you have existing functions x()
, y()
or z()
they will be masked by this choice; specify
such functions by value rather than name, e.g. fn = x
instead of fn = "x"
.)
Functions in fn
with formal arguments x
,
y
and z
will receive the coordinates of
vertices in those arguments, otherwise they will receive
the coordinates in a single n x 3 matrix. They should
be vectorized and return one value per vertex.
Each of the functions will be evaluated at each vertex
of the surface specified by obj
, and contours will
be drawn assuming the function is linear between vertices.
If contours of a nonlinear function are needed, you may
want to increase minVertices
as described below.
If levels
is not specified, values will be set
separately for each entry in fn
, using
pretty(range(values, na.rm = TRUE), nlevels)
where
values
are the values on the vertices.
The minVertices
argument is used to improve the
approximation to the contour when the function is non-linear.
In that case, the interpolation between vertices
can be inaccurate. If minVertices
is set to a
positive
number (e.g. 10000
), then the mesh is modified
by subdivision to have at least that number of vertices,
so that pieces are smaller and the linear interpolation
is more accurate.
For both contourLines3d
and filledContour3d
the "rglId"
method converts the given id values to
a mesh, and calls the "mesh3d"
method.
The "mesh3d"
method returns an object of class
"rglId"
corresponding to what was
drawn if plot
is TRUE
,
If plot
is FALSE
, contourLines3d
returns a dataframe containing
columns c("x", "y", "z", "fn", "level")
giving
the coordinates of the endpoints of each line segment,
the name (or index) of the function for this contour, and the
level of the contour.
If plot
is FALSE
, filledContour3d
returns a "mesh3d"
object holding the result.
If keepValues
is TRUE
, the mesh
will contain the values corresponding to each vertex
(with linear approximations at the boundaries).
To draw contours on a surface, the surface should be drawn
with material property polygon_offset = 1
(or perhaps
some larger positive value) so that the lines of the contour are not
obscured by the surface.
In R versions prior to 3.6.0, the default color.palette
is grDevices::cm.colors
.
Duncan Murdoch
The misc3d package contains the function contour3d
to draw contour surfaces in space instead of contour lines
on surfaces.
# Add contourlines in "z" to a persp plot z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) open3d() id <- persp3d(x, y, z, aspect = "iso", axes = FALSE, box = FALSE, polygon_offset = 1) contourLines3d(id) # "z" is the default function filledContour3d(id, polygon_offset = 1, nlevels = 10, replace = TRUE) # Draw longitude and latitude lines on a globe lat <- matrix(seq(90, -90, length.out = 50)*pi/180, 50, 50, byrow = TRUE) long <- matrix(seq(-180, 180, length.out = 50)*pi/180, 50, 50) r <- 6378.1 # radius of Earth in km x <- r*cos(lat)*cos(long) y <- r*cos(lat)*sin(long) z <- r*sin(lat) open3d() ids <- persp3d(x, y, z, col = "white", texture = system.file("textures/worldsmall.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z, polygon_offset = 1) contourLines3d(ids, list(latitude = function(x, y, z) asin(z/sqrt(x^2+y^2+z^2))*180/pi, longitude = function(x, y, z) atan2(y, x)*180/pi))
# Add contourlines in "z" to a persp plot z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) open3d() id <- persp3d(x, y, z, aspect = "iso", axes = FALSE, box = FALSE, polygon_offset = 1) contourLines3d(id) # "z" is the default function filledContour3d(id, polygon_offset = 1, nlevels = 10, replace = TRUE) # Draw longitude and latitude lines on a globe lat <- matrix(seq(90, -90, length.out = 50)*pi/180, 50, 50, byrow = TRUE) long <- matrix(seq(-180, 180, length.out = 50)*pi/180, 50, 50) r <- 6378.1 # radius of Earth in km x <- r*cos(lat)*cos(long) y <- r*cos(lat)*sin(long) z <- r*sin(lat) open3d() ids <- persp3d(x, y, z, col = "white", texture = system.file("textures/worldsmall.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z, polygon_offset = 1) contourLines3d(ids, list(latitude = function(x, y, z) asin(z/sqrt(x^2+y^2+z^2))*180/pi, longitude = function(x, y, z) atan2(y, x)*180/pi))
A collection of sample mesh objects.
cube3d(trans = identityMatrix(), ...) tetrahedron3d(trans = identityMatrix(), ...) octahedron3d(trans = identityMatrix(), ...) icosahedron3d(trans = identityMatrix(), ...) dodecahedron3d(trans = identityMatrix(), ...) cuboctahedron3d(trans = identityMatrix(), ...) oh3d(trans = identityMatrix(), ...) # an 'o' object
cube3d(trans = identityMatrix(), ...) tetrahedron3d(trans = identityMatrix(), ...) octahedron3d(trans = identityMatrix(), ...) icosahedron3d(trans = identityMatrix(), ...) dodecahedron3d(trans = identityMatrix(), ...) cuboctahedron3d(trans = identityMatrix(), ...) oh3d(trans = identityMatrix(), ...) # an 'o' object
trans |
transformation to apply to objects |
... |
additional parameters to pass to |
These sample objects optionally take a 4x4 matrix transformation trans
as
an argument. This transformation is applied to all vertices of the default shape.
The default is an identity transformation.
Objects of class c("mesh3d",
"shape3d")
.
# render all of the Platonic solids open3d() shade3d( translate3d( tetrahedron3d(col = "red"), 0, 0, 0) ) shade3d( translate3d( cube3d(col = "green"), 3, 0, 0) ) shade3d( translate3d( octahedron3d(col = "blue"), 6, 0, 0) ) shade3d( translate3d( dodecahedron3d(col = "cyan"), 9, 0, 0) ) shade3d( translate3d( icosahedron3d(col = "magenta"), 12, 0, 0) )
# render all of the Platonic solids open3d() shade3d( translate3d( tetrahedron3d(col = "red"), 0, 0, 0) ) shade3d( translate3d( cube3d(col = "green"), 3, 0, 0) ) shade3d( translate3d( octahedron3d(col = "blue"), 6, 0, 0) ) shade3d( translate3d( dodecahedron3d(col = "cyan"), 9, 0, 0) ) shade3d( translate3d( icosahedron3d(col = "magenta"), 12, 0, 0) )
This function converts a description of a space curve into a "mesh3d"
object forming a cylindrical tube around the curve.
cylinder3d(center, radius = 1, twist = 0, e1 = NULL, e2 = NULL, e3 = NULL, sides = 8, section = NULL, closed = 0, rotationMinimizing = is.null(e2) && is.null(e3), debug = FALSE, keepVars = FALSE, ...)
cylinder3d(center, radius = 1, twist = 0, e1 = NULL, e2 = NULL, e3 = NULL, sides = 8, section = NULL, closed = 0, rotationMinimizing = is.null(e2) && is.null(e3), debug = FALSE, keepVars = FALSE, ...)
center |
An n by 3 matrix whose columns are the x, y and z coordinates of the space curve. |
radius |
The radius of the cross-section of the tube at each point in the center. |
twist |
The amount by which the polygon forming the tube is twisted at each point. |
e1 , e2 , e3
|
The local coordinates to use at each point on the space curve. These default to a rotation minimizing frame or Frenet coordinates. |
sides |
The number of sides in the polygon cross section. |
section |
The polygon cross section as a two-column matrix, or |
closed |
Whether to treat the first and last points of the space curve as identical, and close the curve, or put caps on the ends. See the Details. |
rotationMinimizing |
Use a rotation minimizing local
frame if |
debug |
If |
keepVars |
If |
... |
Additional arguments to set as material properties. |
The number of points in the space curve is determined by the vector lengths in center
,
after using xyz.coords
to convert it to a list. The other arguments
radius
, twist
, e1
, e2
, and e3
are extended to the same
length.
The closed
argument controls how the ends of the cylinder are
handled. If closed > 0
, it represents the number of points of
overlap in the coordinates. closed == TRUE
is the same as
closed = 1
. If closed > 0
but the ends don't actually
match, a warning will be given and results will be somewhat
unpredictable.
Negative values of closed
indicate that caps should be put on the
ends of the cylinder. If closed == -1
, a cap will be put on the
end corresponding to center[1, ]
. If closed == -2
, caps
will be put on both ends.
If section
is NULL
(the default), a regular
sides
-sided polygon is used, and radius
measures the
distance from the center of the cylinder to each vertex. If not
NULL
, sides
is ignored (and set internally to
nrow(section)
), and radius
is used as a multiplier to
the vertex coordinates. twist
specifies the rotation of the
polygon. Both radius
and twist
may be vectors, with
values recycled to the number of rows in center
, while
sides
and section
are the same at every point along the
curve.
The three optional arguments e1
, e2
, and e3
determine the local coordinate system used to create the vertices at
each point in center
. If missing, they are computed by simple
numerical approximations. e1
should be the tangent coordinate,
giving the direction of the curve at the point. The cross-section of
the polygon will be orthogonal to e1
. When rotationMinimizing
is TRUE
, e2
and e3
are chosen to give
a rotation minimizing frame (see Wang et al., 2008). When it is
FALSE
, e2
defaults to an
approximation to the normal or curvature vector; it is used as the
image of the y
axis of the polygon cross-section. e3
defaults to an approximation to the binormal vector, to which the
x
axis of the polygon maps. The vectors are orthogonalized and
normalized at each point.
A "mesh3d"
object holding the cylinder, possibly with
attribute "vars"
containing the local environment of the function.
Duncan Murdoch
Wang, W., Jüttler, B., Zheng, D. and Liu, Y. (2008). Computation of rotation minimizing frames. ACM Transactions on Graphics, Vol. 27, No. 1, Article 2.
# A trefoil knot open3d() theta <- seq(0, 2*pi, length.out = 25) knot <- cylinder3d( center = cbind( sin(theta) + 2*sin(2*theta), 2*sin(3*theta), cos(theta) - 2*cos(2*theta)), e1 = cbind( cos(theta) + 4*cos(2*theta), 6*cos(3*theta), sin(theta) + 4*sin(2*theta)), radius = 0.8, closed = TRUE, color = "green") shade3d(addNormals(subdivision3d(knot, depth = 2)))
# A trefoil knot open3d() theta <- seq(0, 2*pi, length.out = 25) knot <- cylinder3d( center = cbind( sin(theta) + 2*sin(2*theta), 2*sin(3*theta), cos(theta) - 2*cos(2*theta)), e1 = cbind( cos(theta) + 4*cos(2*theta), 6*cos(3*theta), sin(theta) + 4*sin(2*theta)), radius = 0.8, closed = TRUE, color = "green") shade3d(addNormals(subdivision3d(knot, depth = 2)))
decorate3d
adds the usual decorations to a plot: labels, axes, etc.
decorate3d(xlim = NULL, ylim = NULL, zlim = NULL, xlab = "x", ylab = "y", zlab = "z", box = TRUE, axes = TRUE, main = NULL, sub = NULL, top = TRUE, aspect = FALSE, expand = 1.03, tag = material3d("tag"), ...)
decorate3d(xlim = NULL, ylim = NULL, zlim = NULL, xlab = "x", ylab = "y", zlab = "z", box = TRUE, axes = TRUE, main = NULL, sub = NULL, top = TRUE, aspect = FALSE, expand = 1.03, tag = material3d("tag"), ...)
xlim , ylim , zlim
|
These are used for the labels. |
xlab , ylab , zlab
|
labels for the coordinates. |
box , axes
|
whether to draw a box and axes. |
main , sub
|
main title and subtitle. |
top |
whether to bring the window to the top when done. |
aspect |
either a logical indicating whether to adjust the aspect ratio, or a new ratio. |
expand |
how much to expand the box around the data, if it is drawn. |
tag |
optional label for objects being produced. |
... |
ignored. |
The RGL id values for those items.
open3d() shade3d(tetrahedron3d(), col = "red") decorate3d(main = "A Tetrahedron")
open3d() shade3d(tetrahedron3d(), col = "red") decorate3d(main = "A Tetrahedron")
Project a line onto the surface in a scene so that it appears to drape itself onto the surface.
drape3d(obj, ...) ## S3 method for class 'mesh3d' drape3d(obj, x, y = NULL, z = NULL, plot = TRUE, up = c(0, 0, 1), P = projectDown(up), ...) ## Default S3 method: drape3d(obj, ...)
drape3d(obj, ...) ## S3 method for class 'mesh3d' drape3d(obj, x, y = NULL, z = NULL, plot = TRUE, up = c(0, 0, 1), P = projectDown(up), ...) ## Default S3 method: drape3d(obj, ...)
obj |
The object(s) upon which to drape lines. |
x , y , z
|
Coordinates of the line segments
to be draped. Any reasonable way of defining the
coordinates is acceptable. See the function |
plot |
Should the result be plotted, or returned as a data frame? |
up |
The direction to consider as “up”. |
P |
The projection to use for draping, a 4x4 matrix. |
... |
For the |
The default method converts obj
to a mesh
using as.mesh3d
, then uses the "mesh3d"
method.
The current implementation constructs the segments to drape
across the surface using the same method as
lines3d
uses: each successive point is
joined to the previous one. Use NA
coordinates to
indicate breaks in the line.
The P
matrix is used to project points to a plane as
follows: They are transformed by P
in
homogeneous coordinates, then only first two (Euclidean)
coordinates are kept.
If plot = TRUE
,
plots the result and invisibly returns the object ID of the collection of segments.
If plot = FALSE
, returns a matrix containing "x", "y" and "z"
values for the line(s)
(for use with segments3d
),
George Helffrich and Duncan Murdoch
# # volcano example taken from "persp" # z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(z) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d() id <- surface3d(x, y, z, color = col, polygon_offset = 1) segs <- data.frame(x = range(x) + c(100, -100), y = range(y) + c(150, -100), z = 325) drape3d(id, segs, col = 'yellow', lwd = 3) lines3d(segs, col='red', lwd=3) p <- c(350, 205) # (x,y) of strike & dip reading off <- 20*c(-1, +1) # X-marks-the-spot offset segs <- data.frame( x = c(p[1] + off, NA, p[1] + off), y = c(p[2] + off, NA, p[2] - off), z = rep(350, 5) ) drape3d(id, segs, col = "yellow", lwd = 3)
# # volcano example taken from "persp" # z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(z) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d() id <- surface3d(x, y, z, color = col, polygon_offset = 1) segs <- data.frame(x = range(x) + c(100, -100), y = range(y) + c(150, -100), z = 325) drape3d(id, segs, col = 'yellow', lwd = 3) lines3d(segs, col='red', lwd=3) p <- c(350, 205) # (x,y) of strike & dip reading off <- 20*c(-1, +1) # X-marks-the-spot offset segs <- data.frame( x = c(p[1] + off, NA, p[1] + off), y = c(p[2] + off, NA, p[2] - off), z = rep(350, 5) ) drape3d(id, segs, col = "yellow", lwd = 3)
The rglwidget
control is designed to work in the
htmlwidgets framework. Older RGL web pages that
used the deprecated writeWebGL
or knitr used a different
method of linking the controls to the scene. This is a partial
bridge between the two systems. You should adopt the new system, not use this function.
elementId2Prefix(elementId, prefix = elementId)
elementId2Prefix(elementId, prefix = elementId)
elementId |
An element identifier from a |
prefix |
The prefix to use in the old-style control. |
This function generates Javascript code, so it should be
used in an results = "asis"
block in a knitr document.
Duncan Murdoch
A generic function and several methods returning an ellipsoid or other outline of a confidence region for three parameters.
ellipse3d(x, ...) ## Default S3 method: ellipse3d(x, scale = c(1, 1, 1), centre = c(0, 0, 0), level = 0.95, t = sqrt(qchisq(level, 3)), which = 1:3, subdivide = 3, smooth = TRUE, ...) ## S3 method for class 'lm' ellipse3d(x, which = 1:3, level = 0.95, t = sqrt(3 * qf(level, 3, x$df.residual)), ...) ## S3 method for class 'glm' ellipse3d(x, which = 1:3, level = 0.95, t, dispersion, ...) ## S3 method for class 'nls' ellipse3d(x, which = 1:3, level = 0.95, t = sqrt(3 * qf(level, 3, s$df[2])), ...)
ellipse3d(x, ...) ## Default S3 method: ellipse3d(x, scale = c(1, 1, 1), centre = c(0, 0, 0), level = 0.95, t = sqrt(qchisq(level, 3)), which = 1:3, subdivide = 3, smooth = TRUE, ...) ## S3 method for class 'lm' ellipse3d(x, which = 1:3, level = 0.95, t = sqrt(3 * qf(level, 3, x$df.residual)), ...) ## S3 method for class 'glm' ellipse3d(x, which = 1:3, level = 0.95, t, dispersion, ...) ## S3 method for class 'nls' ellipse3d(x, which = 1:3, level = 0.95, t = sqrt(3 * qf(level, 3, s$df[2])), ...)
x |
An object. In the default method the parameter |
... |
Additional parameters to pass to the default method or to |
scale |
If |
centre |
The centre of the ellipse will be at this position. |
level |
The confidence level of a simultaneous confidence region. The default is 0.95, for a 95% region. This is used to control the size of the ellipsoid. |
t |
The size of the ellipse may also be controlled by specifying the value of a t-statistic on its boundary. This defaults to the appropriate value for the confidence region. |
which |
This parameter selects which variables from the object will be plotted. The default is the first 3. |
subdivide |
This controls the number of subdivisions (see |
smooth |
If |
dispersion |
The value of dispersion to use. If specified, it is treated as fixed,
and chi-square limits for |
A mesh3d
object representing the ellipsoid.
# Plot a random sample and an ellipsoid of concentration corresponding to a 95% # probability region for a # trivariate normal distribution with mean 0, unit variances and # correlation 0.8. if (requireNamespace("MASS", quietly = TRUE)) { Sigma <- matrix(c(10, 3, 0, 3, 2, 0, 0, 0, 1), 3, 3) Mean <- 1:3 x <- MASS::mvrnorm(1000, Mean, Sigma) open3d() plot3d(x, box = FALSE) plot3d( ellipse3d(Sigma, centre = Mean), col = "green", alpha = 0.5, add = TRUE) } # Plot the estimate and joint 90% confidence region for the displacement and cylinder # count linear coefficients in the mtcars dataset data(mtcars) fit <- lm(mpg ~ disp + cyl , mtcars) open3d() plot3d(ellipse3d(fit, level = 0.90), col = "blue", alpha = 0.5, aspect = TRUE)
# Plot a random sample and an ellipsoid of concentration corresponding to a 95% # probability region for a # trivariate normal distribution with mean 0, unit variances and # correlation 0.8. if (requireNamespace("MASS", quietly = TRUE)) { Sigma <- matrix(c(10, 3, 0, 3, 2, 0, 0, 0, 1), 3, 3) Mean <- 1:3 x <- MASS::mvrnorm(1000, Mean, Sigma) open3d() plot3d(x, box = FALSE) plot3d( ellipse3d(Sigma, centre = Mean), col = "green", alpha = 0.5, add = TRUE) } # Plot the estimate and joint 90% confidence region for the displacement and cylinder # count linear coefficients in the mtcars dataset data(mtcars) fit <- lm(mpg ~ disp + cyl , mtcars) open3d() plot3d(ellipse3d(fit, level = 0.90), col = "blue", alpha = 0.5, aspect = TRUE)
Gets the current scene using scene3d
, and
compares the result to a saved value, optionally closing
the window afterwards.
expect_known_scene(name, close = TRUE, file = paste0("testdata/", name, ".rds"), ...)
expect_known_scene(name, close = TRUE, file = paste0("testdata/", name, ".rds"), ...)
name |
By default, the base name of the file to save results in.
Not used if |
close |
Whether to close the rgl window after the comparison. |
file |
The file in which to save the result. |
... |
Other arguments which will be passed to |
This function uses expect_known_value
to save a representation of the scene. During the comparison, the scene
is modified so that non-reproducible aspects are standardized
or omitted:
object ids are changed to start at 1.
system-specific font names and texture names are deleted.
the window is shifted to the top left of the screen.
Calls to expect_known_scene()
enable testthat::local_edition(2)
for the duration of the call, so it will work in testthat “3rd edition”.
A value describing the changes to the saved object,
suitable for use in test_that()
.
## Not run: # These lines can be included in testthat::test_that() code. plot3d(1:10, 1:10, 1:10) expect_known_scene("plot") ## End(Not run)
## Not run: # These lines can be included in testthat::test_that() code. plot3d(1:10, 1:10, 1:10) expect_known_scene("plot") ## End(Not run)
Given a two-dimensional polygon, this generates a three-dimensional extrusion of the shape by triangulating the polygon and creating a cylinder with that shape as the end faces.
extrude3d(x, y = NULL, thickness = 1, smooth = FALSE, ...)
extrude3d(x, y = NULL, thickness = 1, smooth = FALSE, ...)
x , y
|
A polygon description in one of the forms supported by |
thickness |
The extrusion will have this thickness. |
smooth |
logical; should normals be added so that the edges of the extrusion appear smooth? |
... |
Other parameters to pass to |
The extrusion is always constructed with the polygon in the xy plane at
z = 0
and another copy at z = thickness
. Use
the transformation functions (e.g. rotate3d
) to
obtain other orientations and placements.
A mesh object containing a triangulation of the polygon for each face, and quadrilaterals for the sides.
Duncan Murdoch
polygon3d
for a simple polygon, triangulate
for
the triangulation, turn3d
for a solid of rotation.
x <- c(1:10, 10:1) y <- rev(c(rep(c(0, 2), 5), rep(c(1.5, -0.5), 5))) plot(x, y, type = "n") polygon(x, y) open3d() shade3d( extrude3d(x, y), col = "red" )
x <- c(1:10, 10:1) y <- rev(c(rep(c(0, 2), 5), rep(c(1.5, -0.5), 5))) plot(x, y, type = "n") polygon(x, y) open3d() shade3d( extrude3d(x, y), col = "red" )
facing3d
subsets an object by converting it to a triangle mesh,
then subsetting to those triangles that are counterclockwise
(for front = TRUE
) when projected into a plane.
projectDown
computes a projection that “looks down” the specified direction.
facing3d(obj, up = c(0, 0, 1), P = projectDown(up), front = TRUE, strict = TRUE) projectDown(up)
facing3d(obj, up = c(0, 0, 1), P = projectDown(up), front = TRUE, strict = TRUE) projectDown(up)
obj |
An object that can be converted to a triangular mesh object. |
up |
The direction that is to be considered “up”. It may be either a 3 vector in Euclidean coordinates or a 4 vector in homogeneous coordinates. |
P |
The projection to use for draping, a 4x4 matrix. See |
front |
If |
strict |
If |
By default
the returned subset will be those triangles whose upper side
matches front
. Change up
or use an
arbitrary projection for different subsets.
drape3d
and shadow3d
project objects onto meshes; these functions can be used to
project only onto the top or front.
facing3d
returns a mesh object made of those triangles which face in the
desired direction.
projectDown
computes a 4x4 matrix. The first two
coordinates of asEuclidean(x %*% projectDown(up))
give a projection of x
from above into a plane, where
up
determines which direction is taken to be “up”.
open3d() d <- rnorm(3) d <- d/sqrt(sum(d^2)) shade3d( facing3d( icosahedron3d(), up = d, strict = FALSE), col = "yellow") wire3d( facing3d( icosahedron3d(), up = d, front = FALSE), col = "black") # Show the direction: arrow3d(-2*d , -d)
open3d() d <- rnorm(3) d <- d/sqrt(sum(d^2)) shade3d( facing3d( icosahedron3d(), up = d, strict = FALSE), col = "yellow") wire3d( facing3d( icosahedron3d(), up = d, front = FALSE), col = "black") # Show the direction: arrow3d(-2*d , -d)
In an R Markdown document, figure dimensions are normally
specified in inches; these are translated into pixel
dimensions when HTML output is requested and rglwidget
is used. These functions reproduce that
translation.
figWidth() figHeight()
figWidth() figHeight()
When used in an R Markdown document, these functions
return the requested current dimensions of figures in pixels. Outside such a document, NULL
is returned.
Duncan Murdoch
# No useful return value outside of R Markdown: figWidth() figHeight()
# No useful return value outside of R Markdown: figWidth() figHeight()
Constructs a mesh of line segments corresponding to non-shared (i.e. boundary) edges of triangles or quads in the original mesh.
getBoundary3d(mesh, sorted = FALSE, simplify = TRUE, ...)
getBoundary3d(mesh, sorted = FALSE, simplify = TRUE, ...)
mesh |
A mesh object. |
sorted |
Whether the result should have the segments sorted in sequential order. |
simplify |
Whether to simplify the resulting mesh, dropping all unused
vertices. If |
... |
Material properties to apply to the mesh. |
A "mesh3d"
object containing 0 or more segments.
Duncan Murdoch
x <- cube3d(col = "blue") x$ib <- x$ib[,-(1:2)] b <- getBoundary3d(x, sorted = TRUE, col = "black") open3d() shade3d(x, alpha=0.2) shade3d(b) # Show edge vertices in sequence: text3d(t(b$vb), texts = 1:ncol(b$vb), adj = 0) c(b$is[1,1], b$is[2,])
x <- cube3d(col = "blue") x$ib <- x$ib[,-(1:2)] b <- getBoundary3d(x, sorted = TRUE, col = "black") open3d() shade3d(x, alpha=0.2) shade3d(b) # Show edge vertices in sequence: text3d(t(b$vb), texts = 1:ncol(b$vb), adj = 0) c(b$is[1,1], b$is[2,])
The glTF specification packs data into buffers, labelling
the type of each part with an integer. The first six values
in gltfTypes
are the
integers used there, with "int"
and "double"
added for completeness; those values are taken from
OpenGL header files.
gltfTypes
gltfTypes
gltfTypes
is simply a named vector containing integer values.
These are used in the Buffer
object.
https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#_accessor_componenttype
gltfTypes
gltfTypes
Generate a 3x3 orthogonal matrix using the Gram-Schmidt algorithm.
GramSchmidt(v1, v2, v3, order = 1:3)
GramSchmidt(v1, v2, v3, order = 1:3)
v1 , v2 , v3
|
Three length 3 vectors (taken as row vectors). |
order |
The precedence order for the vectors; see Details. |
This function orthogonalizes the matrix rbind(v1, v2, v3)
using the Gram-Schmidt algorithm. It can handle rank 2 matrices
(returning a rank 3 matrix). If the original is rank 1, it is likely
to fail.
The order
vector determines the precedence of the original
vectors. For example, if it is c(i, j, k)
, then row i
will be unchanged (other than normalization); row j
will
normally be transformed within the span of rows i
and j
.
Row k
will be transformed orthogonally to the span of
the others.
A 3x3 matrix whose rows are the orthogonalization of the original row vectors.
Duncan Murdoch
# Proceed through the rows in order print(A <- matrix(rnorm(9), 3, 3)) GramSchmidt(A[1, ], A[2, ], A[3, ]) # Keep the middle row unchanged print(A <- matrix(c(rnorm(2), 0, 1, 0, 0, rnorm(3)), 3, 3, byrow = TRUE)) GramSchmidt(A[1, ], A[2, ], A[3, ], order = c(2, 1, 3))
# Proceed through the rows in order print(A <- matrix(rnorm(9), 3, 3)) GramSchmidt(A[1, ], A[2, ], A[3, ]) # Keep the middle row unchanged print(A <- matrix(c(rnorm(2), 0, 1, 0, 0, rnorm(3)), 3, 3, byrow = TRUE)) GramSchmidt(A[1, ], A[2, ], A[3, ], order = c(2, 1, 3))
This function adds a reference grid to an RGL plot.
grid3d(side, at = NULL, col = "gray", lwd = 1, lty = 1, n = 5)
grid3d(side, at = NULL, col = "gray", lwd = 1, lty = 1, n = 5)
side |
Where to put the grid; see the Details section. |
at |
How to draw the grid; see the Details section. |
col |
The color of the grid lines. |
lwd |
The line width of the grid lines. (Currently only
|
lty |
The line type of the grid lines. |
n |
Suggested number of grid lines; see the Details section. |
This function is similar to grid
in classic graphics,
except that it draws a 3D grid in the plot.
The grid is drawn in a plane perpendicular to the coordinate axes. The
first letter of the side
argument specifies the direction of
the plane: "x"
, "y"
or "z"
(or uppercase
versions) to specify the coordinate which is constant on the plane.
If at = NULL
(the default), the grid is drawn at the limit of
the box around the data. If the second letter of the side
argument
is "-"
or is not present, it is the lower limit; if "+"
then at the upper limit. The grid lines are drawn at values
chosen by pretty
with n
suggested locations.
The default locations should match those chosen by axis3d
with nticks = n
.
If at
is a numeric vector, the grid lines are drawn at those values.
If at
is a list, then the "x"
component is used to
specify the x location, the "y"
component specifies the y location, and
the "z"
component specifies the z location. Missing components
are handled using the default as for at = NULL
.
Multiple grids may be drawn by specifying multiple values for side
or for the component of at
that specifies the grid location.
A vector or matrix of object ids is returned invisibly.
If the scene is resized, the grid will not be resized; use abclines3d
to draw grid lines that will automatically resize.
Ben Bolker and Duncan Murdoch
x <- 1:10 y <- 1:10 z <- matrix(outer(x - 5, y - 5) + rnorm(100), 10, 10) open3d() persp3d(x, y, z, col = "red", alpha = 0.7, aspect = c(1, 1, 0.5)) grid3d(c("x", "y+", "z"))
x <- 1:10 y <- 1:10 z <- matrix(outer(x - 5, y - 5) + rnorm(100), 10, 10) open3d() persp3d(x, y, z, col = "red", alpha = 0.7, aspect = c(1, 1, 0.5)) grid3d(c("x", "y+", "z"))
This adds text to identify points within a plot when the mouse is near them.
hover3d(x, y = NULL, z = NULL, labeller = NULL, tolerance = 20, persist = c("no", "one", "yes"), labels = seq_along(x), adj = c(-0.2, 0.5), scene = scene3d(minimal = FALSE), applyToScene = TRUE, ...)
hover3d(x, y = NULL, z = NULL, labeller = NULL, tolerance = 20, persist = c("no", "one", "yes"), labels = seq_along(x), adj = c(-0.2, 0.5), scene = scene3d(minimal = FALSE), applyToScene = TRUE, ...)
x , y , z
|
Coordinates of point to identify. Any reasonable way of defining the
coordinates is acceptable. See the function |
labeller |
A function to display information about identified points. |
tolerance |
How close (in pixels) the mouse should be to a point to display the information. |
persist |
Should the label persist? If |
labels |
If the default |
adj |
If the default |
scene , applyToScene
|
Arguments to pass to |
... |
Additional arguments that will be passed to the labeller. |
If specified, the labeller
argument should specify a
function with arguments
compatible with function(index, ...)
. It will be called with
index
being the index of the point that was selected. It should
plot the label, and return the rgl ids of the objects that were
plotted.
When applyToScene
is TRUE
, all labels
or labelling objects will be created and attached to the scene. You may want to
delete them (using the ids returned in idverts
and
idtexts
) once rglwidget
has been called,
as they serve no purpose in the current device.
Only one hover handler is supported per scene or device.
A lowlevel
vector of ids is returned invisibly.
If applyToScene
is TRUE
, it will contain the
ids of the temporary objects created for Javascript.
It will also have these attributes:
oldPar |
Values of |
oldDev |
The value of |
Duncan Murdoch
identify3d
and selectpoints3d
work in the rgl device and return information
about the selections. setUserCallbacks
is the underlying function used by hover3d
.
# Create a labeller to show the coordinates of the selected point. labelLocation <- function(x, y = NULL, z = NULL) { xyz <- xyz.coords(x, y, z) function(sel, ...) { p <- with(xyz, matrix(c(x[sel], y[sel], z[sel]), ncol = 3)) c(text3d(p, texts = sprintf("x:%.2f", p[1]), adj = c(-0.2, -0.6), ...), text3d(p, texts = sprintf("y:%.2f", p[2]), adj = c(-0.2, 0.5), ...), text3d(p, texts = sprintf("z:%.2f", p[3]), adj = c(-0.2, 1.6), ...)) } } xyz <- matrix(rnorm(30), ncol = 3) open3d() ids <- plot3d(xyz) hover3d(xyz, labeller = labelLocation(xyz), col = "red", cex = 0.8) # The same thing using the data id: # hover3d(ids["data"], # labeller = labelLocation(rgl.attrib(ids["data"], "vertices")), # col = "red", cex = 0.8)
# Create a labeller to show the coordinates of the selected point. labelLocation <- function(x, y = NULL, z = NULL) { xyz <- xyz.coords(x, y, z) function(sel, ...) { p <- with(xyz, matrix(c(x[sel], y[sel], z[sel]), ncol = 3)) c(text3d(p, texts = sprintf("x:%.2f", p[1]), adj = c(-0.2, -0.6), ...), text3d(p, texts = sprintf("y:%.2f", p[2]), adj = c(-0.2, 0.5), ...), text3d(p, texts = sprintf("z:%.2f", p[3]), adj = c(-0.2, 1.6), ...)) } } xyz <- matrix(rnorm(30), ncol = 3) open3d() ids <- plot3d(xyz) hover3d(xyz, labeller = labelLocation(xyz), col = "red", cex = 0.8) # The same thing using the data id: # hover3d(ids["data"], # labeller = labelLocation(rgl.attrib(ids["data"], "vertices")), # col = "red", cex = 0.8)
Identify points in a plot, similarly to the identify
function
in base graphics.
identify3d(x, y = NULL, z = NULL, labels = seq_along(x), n = length(x), plot = TRUE, adj = c(-0.1, 0.5), tolerance = 20, buttons = c("right", "middle"))
identify3d(x, y = NULL, z = NULL, labels = seq_along(x), n = length(x), plot = TRUE, adj = c(-0.1, 0.5), tolerance = 20, buttons = c("right", "middle"))
x , y , z
|
coordinates of points in a scatter plot. Alternatively, any
object which defines coordinates (see |
labels |
an optional character vector giving labels for the
points. Will be coerced using |
n |
the maximum number of points to be identified. |
plot |
logical: if |
adj |
numeric vector to use as |
tolerance |
the maximal distance (in pixels) for the pointer to be ‘close enough’ to a point. |
buttons |
a length 1 or 2 character vector giving the buttons to use for selection and quitting. |
If buttons
is length 1, the user can quit by reaching n
selections, or by hitting the
escape key, but the result will be lost if escape is used.
A vector of selected indices.
Duncan Murdoch
identify
for base graphics, select3d
for selecting regions.
This object is imported from magrittr. Follow the link to its documentation.
Pipes can be used to string together
rglwidget
calls and playwidget
calls. See ageControl
for an example.
This is mainly for internal use to decide whether results should be automatically included in a pkgdown web page.
in_pkgdown_example()
in_pkgdown_example()
TRUE
or FALSE
in_pkgdown_example()
in_pkgdown_example()
add a light source to the scene.
light3d(theta=0, phi=15, x=NULL, y = NULL, z = NULL, viewpoint.rel = TRUE, ambient = "#FFFFFF", diffuse = "#FFFFFF", specular = "#FFFFFF")
light3d(theta=0, phi=15, x=NULL, y = NULL, z = NULL, viewpoint.rel = TRUE, ambient = "#FFFFFF", diffuse = "#FFFFFF", specular = "#FFFFFF")
theta , phi
|
direction to infinitely distant light |
x , y , z
|
position of finitely distant light |
viewpoint.rel |
logical, if TRUE light is a viewpoint light that is positioned relative to the current viewpoint |
ambient , diffuse , specular
|
light color values used for lighting calculation |
Up to 8 light sources are supported. They are positioned either in
world space or relative to the camera. By providing polar
coordinates to theta
and phi
a directional light source is used. If
numerical values are given to x, y and z, a point-like light source with
finite distance to the objects in the scene is set up.
If x
is non-null, xyz.coords
will
be used to form the location values, so all three coordinates
can be specified in x
.
If no lights have been added to a subscene, lights from the parent subscene will be used.
See material3d
for a discussion of how
the components of the light affect the display of objects.
This function is called for the side effect of adding a light. A light ID is
returned to allow pop3d
to remove it.
# # a lightsource moving through the scene # data(volcano) z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(z) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d() bg3d("gray50") surface3d(x, y, z, color = col, back = "lines") r <- max(y) - mean(y) lightid <- spheres3d(1, 1, 1, alpha = 0) frame <- function(time) { a <- pi*(time - 1) save <- par3d(skipRedraw = TRUE) clear3d(type = "lights") pop3d(id = lightid) xyz <- matrix(c(r*sin(a) + mean(x), r*cos(a) + mean(y), max(z)), ncol = 3) light3d(x = xyz, diffuse = "gray75", specular = "gray75", viewpoint.rel = FALSE) light3d(diffuse = "gray10", specular = "gray25") lightid <<- spheres3d(xyz, emission = "white", radius = 4) par3d(save) Sys.sleep(0.02) NULL } play3d(frame, duration = 2)
# # a lightsource moving through the scene # data(volcano) z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(z) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d() bg3d("gray50") surface3d(x, y, z, color = col, back = "lines") r <- max(y) - mean(y) lightid <- spheres3d(1, 1, 1, alpha = 0) frame <- function(time) { a <- pi*(time - 1) save <- par3d(skipRedraw = TRUE) clear3d(type = "lights") pop3d(id = lightid) xyz <- matrix(c(r*sin(a) + mean(x), r*cos(a) + mean(y), max(z)), ncol = 3) light3d(x = xyz, diffuse = "gray75", specular = "gray75", viewpoint.rel = FALSE) light3d(diffuse = "gray10", specular = "gray25") lightid <<- spheres3d(xyz, emission = "white", radius = 4) par3d(save) Sys.sleep(0.02) NULL } play3d(frame, duration = 2)
A utility function to help in development of internal Javascript code, this function processes the Javascript to minify it and report on errors and bad style.
makeDependency(name, src, script = NULL, package, version = packageVersion(package), minifile = paste0(basename(src), ".min.js"), debugging = FALSE, ...)
makeDependency(name, src, script = NULL, package, version = packageVersion(package), minifile = paste0(basename(src), ".min.js"), debugging = FALSE, ...)
name , src , script , package , version , ...
|
Arguments to pass to |
minifile |
Basename of minified file. |
debugging |
See details below. |
This is a utility function used by RGL
to process its Javascript code used when displaying rglwidget
values. It may be helpful in other packages to use
in their own installation.
If the js package version 1.2 or greater is installed,
the Javascript code will be minified and stored in the file named
by minifile
in the src
directory. Syntax errors
in the code will stop the process; unused variables will be
reported.
If debugging
is TRUE
, the locations of Javascript syntax
errors will be reported, along with hints about improvements,
and the original files will be used in the dependency object
that is created.
If debugging
is FALSE
(the default),
the minified file will be used in the dependency object,
hints won't be given, and syntax errors will lead to an
uninformative failure to minify.
An object that can be included in a list of dependencies passed to htmltools::attachDependencies
.
Duncan Murdoch
## Not run: # This is a slightly simplified version of the code used to # produce one of the dependencies for rglwidget(). # It writes to the system library copy of rgl so # has been marked not to run in the example code. makeDependency("rglwidgetClass", src = "htmlwidgets/lib/rglClass", script = c("rglClass.src.js", "utils.src.js", "buffer.src.js", "subscenes.src.js", "shaders.src.js", "textures.src.js", "projection.src.js", "mouse.src.js", "init.src.js", "pieces.src.js", "draw.src.js", "controls.src.js", "selection.src.js", "rglTimer.src.js", "pretty.src.js", "axes.src.js", "animation.src.js"), stylesheet = "rgl.css", package = "rgl", debugging = isTRUE(as.logical(Sys.getenv("RGL_DEBUGGING", "FALSE")))) ## End(Not run)
## Not run: # This is a slightly simplified version of the code used to # produce one of the dependencies for rglwidget(). # It writes to the system library copy of rgl so # has been marked not to run in the example code. makeDependency("rglwidgetClass", src = "htmlwidgets/lib/rglClass", script = c("rglClass.src.js", "utils.src.js", "buffer.src.js", "subscenes.src.js", "shaders.src.js", "textures.src.js", "projection.src.js", "mouse.src.js", "init.src.js", "pieces.src.js", "draw.src.js", "controls.src.js", "selection.src.js", "rglTimer.src.js", "pretty.src.js", "axes.src.js", "animation.src.js"), stylesheet = "rgl.css", package = "rgl", debugging = isTRUE(as.logical(Sys.getenv("RGL_DEBUGGING", "FALSE")))) ## End(Not run)
Get or set material properties for geometry appearance.
material3d(..., id = NULL) rgl.material.names rgl.material.readonly
material3d(..., id = NULL) rgl.material.names rgl.material.readonly
... |
Material properties to set or query. |
id |
the rgl id of an object to query, or |
In an rgl scene, each object has “material properties” that control how it is rendered and (in the case of tag
)
that can be used to store a
label or other information. material3d
sets defaults
for these properties and queries the defaults or specific
values for an individual object.
To set values, use name = value
settings, e.g.
material3d(color = "red")
. To query values,
specify the property or properties in a character vector,
e.g. material3d("color")
.
Only one side at a time can be culled.
The material
member of the r3dDefaults
list may be used to
set default values for material properties.
material3d()
returns values similarly
to par3d
:
When setting properties, it returns the previous values
invisibly in a named list. When querying multiple values, a
named list is returned. When a single value is queried it is
returned directly.
The rgl.material.names
variable contains the
full list of material names.
The following read-write material properties control
the appearance
of objects in an rgl scene.
vector of R color characters. Represents the diffuse component in case of lighting calculation (lit = TRUE), otherwise it describes the solid color characteristics.
logical, specifying if lighting calculation should take place on geometry
properties for lighting calculation. ambient, specular, emission are R color character string values; shininess represents a numerical.
vector of alpha values between 0.0 (fully transparent) .. 1.0 (opaque).
logical, specifying whether smooth shading or flat shading should be used. For smooth shading, Gouraud shading is used in rgl windows, while Phong shading is used in WebGL.
path to a texture image file. See the Textures section below for details.
specifies what is defined with the pixmap
alpha values
luminance
luminance and alpha
color
color and alpha texture
Note that support for these modes is slightly different
in the display within R versus the WebGL display using
rglwidget()
. In particular, in WebGL
textype = "alpha"
will always take the alpha value from
the luminance (i.e. the average of the R, G and B channels)
of the texture, whereas the R display bases
the choice on the internal format of the texture file.
specifies how the texture interacts with the existing color
texture value replaces existing value
default; texture value multiplies existing value
for textype = "rgba"
, texture
is mixed with existing value
uses the texture to blend the existing value with black
adds the texture value to the existing. May not be available in the R display with very old OpenGL drivers.
Logical, specifies if the texture should be mipmapped.
specifies the magnification filtering type (sorted by ascending quality):
texel nearest to the center of the pixel
weighted linear average of a 2x2 array of texels
specifies the minification filtering type (sorted by ascending quality):
texel nearest to the center of the pixel
weighted linear average of a 2x2 array of texels
low quality mipmapping
medium quality mipmapping
medium quality mipmapping
high quality mipmapping
logical, specifies if auto-generated texture coordinates for environment-mapping should be performed on geometry.
Determines the polygon mode for the specified side:
filled polygon
wireframed polygon
point polygon
culled (hidden) polygon
numeric, specifying the size of points in pixels
numeric, specifying the line width in pixels
logical, specifying if fog effect should be applied on the corresponding shape. Fog type is set in bg3d
.
logical, specifying if points should be round and lines should be antialiased, but see Note below.
logical, specifying whether the object's depth should be stored.
Determines which depth test is used to see if this
object is visible, depending on its apparent depth in the scene
compared to the stored depth. Possible values are "never"
,
"less"
(the default), "equal"
, "lequal"
(less than or equal), "greater"
, "notequal"
,
"gequal"
(greater than or equal), "always"
.
A one or two element
vector giving the ‘factor’ and ‘units’ values
to use in a glPolygonOffset()
call in OpenGL. If
only one value is given, it is used for both elements.
The ‘units’ value is added to the depth of all pixels in
a filled polygon,
and the ‘factor’ value is multiplied by an estimate of
the slope of the polygon and then added to the depth. Positive values “push” polygons back slightly for the purpose
of depth testing, to allow points, lines or other polygons
to be drawn on the surface without being obscured due
to rounding error. Negative values pull the object forward.
A typical value to use is 1
(which
is automatically expanded to c(1,1)
).
If values are too large, objects which should be behind
the polygon will show through, and if values are too small,
the objects on the surface will be partially obscured.
Experimentation may be needed to get it right. The first
example in ?persp3d
uses this property to add
grid lines to a surface.
Used mainly for text to
draw annotations in the margins, but supported by
most kinds of objects: see mtext3d
.
A length 1 string value. These may be used to identify objects, or encode other meta data about the object.
Two string values from the list below describing how transparent objects are blended with colors behind them. The first determines the coefficient applied to the color of the current object (the source); the second determines the coefficient applied to the existing color (the destination). The resulting color will be the sum of the two resulting colors. The allowed strings correspond to OpenGL constants:
Zero; color has no effect.
One; color is added to the other term.
Multiply by source color or its opposite.
Multiply by destination color or its opposite.
Multiply by source alpha or its opposite. Default values.
Multiply by destination alpha or its opposite.
These are allowed, but to be useful they require other settings which rgl doesn't support.
An allowed abbreviation of color
.
The rgl.material.readonly
variable contains
the subset of material properties that are read-only
so they can be queried
but not set.
Currently there is only one:
Is the current color transparent?
The texture
material property may be NULL
or the name of
a bitmap file to be displayed on the surface being rendered.
Currently only PNG format files are supported.
By default, the colors in the bitmap will modify the color of the
object being plotted. If the color is black (a common default), you
won't see anything, so a warning may be issued. You can
suppress the warning by specifying
the color explicitly, or calling
options{rgl.warnBlackTexture = FALSE}
.
Other aspects of texture display are controlled by the material
properties textype, texmode, texmipmap, texmagfilter,
texminfilter
and texenvmap
described above.
For an extensive discussion of textures, see the Textures section of the rgl Overview vignette.
Object display colors are determined as follows:
If lit = FALSE
, an element of the color
vector property is displayed without modification. See documentation for individual objects for information on which element is chosen.
If lit = TRUE
, the color is determined as follows.
The color is set to the emission
property
of the object.
For each defined light, the following are added:
the product of the ambient
color
of the light and the ambient
color of the object is added.
the color
of the
object is multiplied by the diffuse
color
of the light and by a constant depending on
the angle between the surface and the direction to
the light, and added.
the specular
color of the object
is multiplied by the specular
color of the
light and a constant depending on the shininess
of the object and the direction to the light, and
added. The shininess
property mainly
determines the size of the shiny highlight; adjust
one or both of the specular
colors to change
its brightness.
If point_antialias
is TRUE
, points will be drawn as circles in WebGL; otherwise, they
will be drawn as squares. Within R, the behaviour depends
on your graphics hardware: for example, I see circles for
both settings on my laptop.
Within R, lines tend to appear heavier with line_antialias == TRUE
. There's no difference at all
in WebGL.
save <- material3d("color") material3d(color = "red") material3d("color") material3d(color = save) # this illustrates the effect of depth_test x <- c(1:3); xmid <- mean(x) y <- c(2, 1, 3); ymid <- mean(y) z <- 1 open3d() tests <- c("never", "less", "equal", "lequal", "greater", "notequal", "gequal", "always") for (i in 1:8) { triangles3d(x, y, z + i, col = heat.colors(8)[i]) texts3d(xmid, ymid, z + i, paste(i, tests[i], sep = ". "), depth_test = tests[i]) } highlevel() # To trigger display # this illustrates additive blending open3d() bg3d("darkgray") quad <- cbind(c(-1, 1, 1, -1), 1, c(-1, -1, 1, 1)) quads3d(rbind(translate3d(quad, -0.5, 0, -0.5), translate3d(quad, 0.5, 0.5, -0.5), translate3d(quad, 0, 1, 0.5)), col = rep(c("red", "green", "blue"), each = 4), alpha = 0.5, blend = c("src_alpha", "one"))
save <- material3d("color") material3d(color = "red") material3d("color") material3d(color = save) # this illustrates the effect of depth_test x <- c(1:3); xmid <- mean(x) y <- c(2, 1, 3); ymid <- mean(y) z <- 1 open3d() tests <- c("never", "less", "equal", "lequal", "greater", "notequal", "gequal", "always") for (i in 1:8) { triangles3d(x, y, z + i, col = heat.colors(8)[i]) texts3d(xmid, ymid, z + i, paste(i, tests[i], sep = ". "), depth_test = tests[i]) } highlevel() # To trigger display # this illustrates additive blending open3d() bg3d("darkgray") quad <- cbind(c(-1, 1, 1, -1), 1, c(-1, -1, 1, 1)) quads3d(rbind(translate3d(quad, -0.5, 0, -0.5), translate3d(quad, 0.5, 0.5, -0.5), translate3d(quad, 0, 1, 0.5)), col = rep(c("red", "green", "blue"), each = 4), alpha = 0.5, blend = c("src_alpha", "one"))
These functions construct 4x4 matrices for transformations in the homogeneous coordinate system used by OpenGL, and translate vectors between homogeneous and Euclidean coordinates.
identityMatrix() scaleMatrix(x, y, z) translationMatrix(x, y, z) rotationMatrix(angle, x, y, z, matrix) asHomogeneous(x) asEuclidean(x) asHomogeneous2(x) asEuclidean2(x) scale3d(obj, x, y, z, ...) translate3d(obj, x, y, z, ...) rotate3d(obj, angle, x, y, z, matrix, ...) transform3d(obj, matrix, ...)
identityMatrix() scaleMatrix(x, y, z) translationMatrix(x, y, z) rotationMatrix(angle, x, y, z, matrix) asHomogeneous(x) asEuclidean(x) asHomogeneous2(x) asEuclidean2(x) scale3d(obj, x, y, z, ...) translate3d(obj, x, y, z, ...) rotate3d(obj, angle, x, y, z, matrix, ...) transform3d(obj, matrix, ...)
x , y , z , angle , matrix
|
See details |
obj |
An object to be transformed |
... |
Additional parameters to be passed to methods |
OpenGL uses homogeneous coordinates to handle perspective and affine
transformations. The homogeneous point (x, y, z, w)
corresponds
to the Euclidean point (x/w, y/w, z/w)
. The matrices produced by
the functions scaleMatrix
, translationMatrix
, and rotationMatrix
are to be left-multiplied by a row vector
of homogeneous coordinates; alternatively, the transpose of the result
can be right-multiplied by a column vector. The generic functions
scale3d
, translate3d
and rotate3d
apply these transformations
to the obj
argument. The transform3d
function is a synonym
for rotate3d(obj, matrix = matrix)
.
By default, it is assumed that obj
is a row vector
(or a matrix of row vectors) which will be multiplied on the right by
the corresponding matrix, but users may write methods for these generics
which operate differently. Methods are supplied for mesh3d
objects.
To compose transformations, use matrix multiplication. The effect is to apply the matrix on the left first, followed by the one on the right.
identityMatrix
returns an identity matrix.
scaleMatrix
scales each coordinate by the given factor. In Euclidean
coordinates, (u, v, w)
is transformed to (x*u, y*v, z*w)
.
translationMatrix
translates each coordinate by the given translation, i.e.
(u, v, w)
is transformed to (u + x, v + y, w + z)
.
rotationMatrix
can be called in three ways. With
arguments angle, x, y, z
it represents a rotation
of angle
radians about the axis
x, y, z
. If matrix
is a 3x3 rotation matrix,
it will be converted into the corresponding matrix in 4x4 homogeneous
coordinates. Finally, if a 4x4 matrix is given, it will be returned unchanged.
(The latter behaviour is used to allow transform3d
to act like a
generic function, even though it is not.)
Use asHomogeneous(x)
to convert the Euclidean vector x
to
homogeneous coordinates, and asEuclidean(x)
for the reverse transformation. These functions accept the following
inputs:
n x 3 matrices: rows are assumed to be Euclidean
n x 4 matrices: rows are assumed to be homogeneous
vectors of length 3n or 4n: assumed to be vectors
concatenated. For the ambiguous case
of vectors that are length 12n (so both 3n and 4n are possible),
the assumption is that the conversion is necessary: asEuclidean
assumes the vectors are homogeneous,
and asHomogeneous
assumes the vectors are Euclidean.
Outputs are n x 4 or n x 3 matrices for asHomogeneous
and asEuclidean
respectively.
The functions asHomogeneous2
and asEuclidean2
act similarly, but they assume inputs are 3 x n or 4 x n
and outputs are in similar shapes.
identityMatrix
,
scaleMatrix
, translationMatrix
, and rotationMatrix
produce
a 4x4 matrix representing the requested transformation
in homogeneous coordinates.
scale3d
, translate3d
and rotate3d
transform the object
and produce a new object of the same class.
Duncan Murdoch
par3d
for a description of how RGL uses matrices in
rendering.
# A 90 degree rotation about the x axis: rotationMatrix(pi/2, 1, 0, 0) # Find what happens when you rotate (2, 0, 0) by 45 degrees about the y axis: x <- asHomogeneous(c(2, 0, 0)) y <- x %*% rotationMatrix(pi/4, 0, 1, 0) asEuclidean(y) # or more simply... rotate3d(c(2, 0, 0), pi/4, 0, 1, 0)
# A 90 degree rotation about the x axis: rotationMatrix(pi/2, 1, 0, 0) # Find what happens when you rotate (2, 0, 0) by 45 degrees about the y axis: x <- asHomogeneous(c(2, 0, 0)) y <- x %*% rotationMatrix(pi/4, 0, 1, 0) asEuclidean(y) # or more simply... rotate3d(c(2, 0, 0), pi/4, 0, 1, 0)
Attempts to merge "mesh3d"
objects. Objects need
to be similar enough; see Details.
## S3 method for class 'mesh3d' merge(x, y, ..., attributesMustMatch = FALSE)
## S3 method for class 'mesh3d' merge(x, y, ..., attributesMustMatch = FALSE)
x , y
|
|
... |
Optional additional objects. |
attributesMustMatch |
See Details. |
To allow objects to be merged, they need to be similar enough in terms of having the same list of material properties, normals, texture coordinates, etc.
If attributesMustMatch
is TRUE
, it is an error
to have attributes in one mesh but not in another, and
those attributes that only specify a single value must have equal
values in all meshes.
If attributesMustMatch
is FALSE
, any non-matching
attributes will be dropped from the final result.
A single "mesh3d"
object merging the contents
of the arguments.
Duncan Murdoch
open3d() # Notice that the alpha setting for the cube is dropped, because # the other shapes don't specify alpha. shade3d(merge(cube3d(col="red", alpha = 0.5), translate3d(tetrahedron3d(col="green"), 2, 0, 0), translate3d(octahedron3d(col="blue"), 4, 0, 0)))
open3d() # Notice that the alpha setting for the cube is dropped, because # the other shapes don't specify alpha. shade3d(merge(cube3d(col="red", alpha = 0.5), translate3d(tetrahedron3d(col="green"), 2, 0, 0), translate3d(octahedron3d(col="blue"), 4, 0, 0)))
A mesh object can have the same vertex listed twice. Each copy is
allowed to have separate normals, texture coordinates, and color.
However, it is more efficient to have just a single copy if those
differences aren't needed. For automatic smoothing using
addNormals
, triangles and quads need to share vertices.
This function merges identical (or similar) vertices to achieve this.
mergeVertices(mesh, notEqual = NULL, attribute = "vertices", tolerance = sqrt(.Machine$double.eps))
mergeVertices(mesh, notEqual = NULL, attribute = "vertices", tolerance = sqrt(.Machine$double.eps))
mesh |
A |
notEqual |
A logical matrix indicating that certain pairs should not be merged even if they appear identical. |
attribute |
Which attribute(s) should be considered in comparing vertices?
A vector chosen from |
tolerance |
When comparing vertices using |
A new mesh object.
Duncan Murdoch
as.mesh3d.rglId
, which often constructs mesh
objects containing a lot of duplication.
open3d() (mesh1 <- cuboctahedron3d(col = rainbow(14), meshColor = "face")) id <- shade3d(mesh1) (mesh2 <- as.mesh3d(id)) shade3d(translate3d(mesh2, 3, 0, 0)) (mesh3 <- mergeVertices(mesh2)) shade3d(translate3d(mesh3, 6, 0, 0))
open3d() (mesh1 <- cuboctahedron3d(col = rainbow(14), meshColor = "face")) id <- shade3d(mesh1) (mesh2 <- as.mesh3d(id)) shade3d(translate3d(mesh2, 3, 0, 0)) (mesh3 <- mergeVertices(mesh2)) shade3d(translate3d(mesh3, 6, 0, 0))
Creates meshes containing points, segments, triangles and quads.
mesh3d( x, y = NULL, z = NULL, vertices, material = NULL, normals = NULL, texcoords = NULL, points = NULL, segments = NULL, triangles = NULL, quads = NULL, meshColor = c("vertices", "edges", "faces", "legacy")) qmesh3d(vertices, indices, homogeneous = TRUE, material = NULL, normals = NULL, texcoords = NULL, meshColor = c("vertices", "edges", "faces", "legacy")) tmesh3d(vertices, indices, homogeneous = TRUE, material = NULL, normals = NULL, texcoords = NULL, meshColor = c("vertices", "edges", "faces", "legacy"))
mesh3d( x, y = NULL, z = NULL, vertices, material = NULL, normals = NULL, texcoords = NULL, points = NULL, segments = NULL, triangles = NULL, quads = NULL, meshColor = c("vertices", "edges", "faces", "legacy")) qmesh3d(vertices, indices, homogeneous = TRUE, material = NULL, normals = NULL, texcoords = NULL, meshColor = c("vertices", "edges", "faces", "legacy")) tmesh3d(vertices, indices, homogeneous = TRUE, material = NULL, normals = NULL, texcoords = NULL, meshColor = c("vertices", "edges", "faces", "legacy"))
x , y , z
|
coordinates. Any reasonable way of defining the
coordinates is acceptable. See the function |
vertices |
A 4 row matrix of homogeneous coordinates; takes
precedence over |
material |
material properties for later rendering |
normals |
normals at each vertex |
texcoords |
texture coordinates at each vertex |
points |
vector of indices of vertices to draw as points |
segments |
2 x n matrix of indices of vertices to draw as segments |
triangles |
3 x n matrix of indices of vertices to draw as triangles |
quads |
4 x n matrix of indices of vertices to draw as quads |
indices |
(obsolete) 3 or 4 x n matrix of vertex indices |
homogeneous |
(obsolete) should |
meshColor |
how should colours be interpreted? See details in |
These functions create mesh3d
objects, which consist of a matrix
of vertex coordinates together with a matrices of indices indicating how the vertices should be displayed, and
material properties.
The "shape3d"
class is a general class for shapes that can be plotted
by dot3d
, wire3d
or shade3d
.
The "mesh3d"
class is a class of objects that form meshes: the vertices
are in member vb
, as a 4 by n
matrix using
homogeneous coordinates. Indices of these vertices are
contained in optional components ip
for points,
is
for line segments, it
for triangles, and
ib
for quads.
Individual meshes may have any combination of these.
The functions tmesh3d
and qmesh3d
are included
for back-compatibility; they produce meshes of triangles and
quads respectively.
Objects of class c("mesh3d",
"shape3d")
.
See points3d
for a discussion of texture coordinates.
shade3d
, shapelist3d
for multiple shapes
# generate a quad mesh object vertices <- c( -1.0, -1.0, 0, 1.0, -1.0, 0, 1.0, 1.0, 0, -1.0, 1.0, 0 ) indices <- c( 1, 2, 3, 4 ) open3d() wire3d( mesh3d(vertices = vertices, quads = indices) )
# generate a quad mesh object vertices <- c( -1.0, -1.0, 0, 1.0, -1.0, 0, 1.0, 1.0, 0, -1.0, 1.0, 0 ) indices <- c( 1, 2, 3, 4 ) open3d() wire3d( mesh3d(vertices = vertices, quads = indices) )
The mfrow3d
and layout3d
functions provide functionality in RGL
similar to par("mfrow")
and layout
in classic R graphics.
subsceneList(value, window = cur3d()) mfrow3d(nr, nc, byrow = TRUE, parent = NA, sharedMouse = FALSE, ...) layout3d(mat, widths = rep.int(1, ncol(mat)), heights = rep.int(1, nrow(mat)), parent = NA, sharedMouse = FALSE, ...) next3d(current = NA, clear = TRUE, reuse = TRUE) clearSubsceneList(delete = currentSubscene3d() %in% subsceneList(), window = cur3d())
subsceneList(value, window = cur3d()) mfrow3d(nr, nc, byrow = TRUE, parent = NA, sharedMouse = FALSE, ...) layout3d(mat, widths = rep.int(1, ncol(mat)), heights = rep.int(1, nrow(mat)), parent = NA, sharedMouse = FALSE, ...) next3d(current = NA, clear = TRUE, reuse = TRUE) clearSubsceneList(delete = currentSubscene3d() %in% subsceneList(), window = cur3d())
value |
A new subscene list to set. If missing, return the current one (or |
window |
Which window to operate on. |
nr , nc
|
Number of rows and columns of figures. |
byrow |
Whether figures progress by row (as with |
mat , widths , heights
|
Layout parameters; see |
parent |
The parent subscene. |
sharedMouse |
Whether to make all subscenes |
... |
Additional parameters to pass to |
current |
The subscene to move away from. |
clear |
Whether the newly entered subscene should be cleared upon entry. |
reuse |
Whether to skip advancing if the current subscene has no objects in it. |
delete |
If |
rgl can maintain a list of subscenes; the mfrow3d
and layout3d
functions create that list.
When the list is in place, next3d
causes RGL to move to the next scene in the list, or cycle
back to the first one.
Unlike the classic R graphics versions of these functions, these functions are completely compatible with each other. You can mix them within a single RGL window.
In the default case where parent
is missing, mfrow3d
and layout3d
will
call clearSubsceneList()
at the start.
By default clearSubsceneList()
checks
whether the current subscene is in the current subscene list; if so, it will delete
all subscenes in the list, and call gc3d
to delete any objects that are no
longer shown. The subscene list will be set to a previous value if one was recorded,
or NULL
if not.
If parent
is specified in mfrow3d
or layout3d
(even as NA
), the new subscenes will be created within
the parent.
The next3d()
function first finds out if the current
subscene is in the current list. If not, it moves to the
previous list, and looks there. Once it finds a list
containing the current subscene, it moves to the next entry
in that list. If it can't find one, it creates a length
one list containing just the current subscene.
mfrow3d
and layout3d
return a vector of subscene id values that have just been
created. If a previous subscene list was in effect and was not automatically cleared,
it is attached as an attribute "prev"
.
Duncan Murdoch
shapes <- list(Tetrahedron = tetrahedron3d(), Cube = cube3d(), Octahedron = octahedron3d(), Icosahedron = icosahedron3d(), Dodecahedron = dodecahedron3d(), Cuboctahedron = cuboctahedron3d()) col <- rainbow(6) open3d() mfrow3d(3, 2) for (i in 1:6) { next3d() # won't advance the first time, since it is empty shade3d(shapes[[i]], col = col[i]) } highlevel(integer()) # To trigger display as rglwidget open3d() mat <- matrix(1:4, 2, 2) mat <- rbind(mat, mat + 4, mat + 8) layout3d(mat, height = rep(c(3, 1), 3), sharedMouse = TRUE) for (i in 1:6) { next3d() shade3d(shapes[[i]], col = col[i]) next3d() text3d(0, 0, 0, names(shapes)[i]) } highlevel(integer())
shapes <- list(Tetrahedron = tetrahedron3d(), Cube = cube3d(), Octahedron = octahedron3d(), Icosahedron = icosahedron3d(), Dodecahedron = dodecahedron3d(), Cuboctahedron = cuboctahedron3d()) col <- rainbow(6) open3d() mfrow3d(3, 2) for (i in 1:6) { next3d() # won't advance the first time, since it is empty shade3d(shapes[[i]], col = col[i]) } highlevel(integer()) # To trigger display as rglwidget open3d() mat <- matrix(1:4, 2, 2) mat <- rbind(mat, mat + 4, mat + 8) layout3d(mat, height = rep(c(3, 1), 3), sharedMouse = TRUE) for (i in 1:6) { next3d() shade3d(shapes[[i]], col = col[i]) next3d() text3d(0, 0, 0, names(shapes)[i]) } highlevel(integer())
This function sets the location of the viewer.
observer3d(x, y = NULL, z = NULL, auto = FALSE)
observer3d(x, y = NULL, z = NULL, auto = FALSE)
x , y , z
|
The location as a 3 vector, using the usual |
auto |
If |
This function
sets the location of the viewer relative to the scene, after the model transformations
(scaling, rotation) have been done, but before lighting or projection have been
applied. (See par3d
for details on the rendering pipeline.)
The coordinate system is a slightly strange one: the X coordinate
moves the observer location from left to right, and the Y coordinate moves up
and down. The Z coordinate changes the depth from the viewer. All are measured
relative to the center of the bounding box (par("bbox")
) of the subscene.
The observer always looks in the positive Z direction
after the model rotation have been done. The coordinates are in post-scaling
units.
Invisibly returns the previous value.
This function is likely to change in future versions of RGL, to allow more flexibility in the specification of the observer's location and orientation.
Duncan Murdoch
example(surface3d) # The volcano data observer3d(0, 0, 440) # Viewed from very close up
example(surface3d) # The volcano data observer3d(0, 0, 440) # Viewed from very close up
open3d
opens a new RGL window; cur3d
returns the device number of the current
window; close3d
closes one or more windows.
open3d(..., params = getr3dDefaults(), useNULL = rgl.useNULL(), silent = FALSE) close3d(dev = cur3d(), silent = TRUE) cur3d() rgl.dev.list() set3d(dev, silent = FALSE) getr3dDefaults(class = NULL, value = NULL) r3dDefaults rgl.quit()
open3d(..., params = getr3dDefaults(), useNULL = rgl.useNULL(), silent = FALSE) close3d(dev = cur3d(), silent = TRUE) cur3d() rgl.dev.list() set3d(dev, silent = FALSE) getr3dDefaults(class = NULL, value = NULL) r3dDefaults rgl.quit()
... |
arguments in |
params |
a list of graphical parameters |
useNULL |
whether to use the null graphics device |
dev |
which device to close or use |
silent |
whether report on what was done |
class , value
|
names of components to retrieve |
open3d
opens a new RGL device, and sets the parameters as
requested. The r3dDefaults
list returned by the
getr3dDefaults
function will be used as default
values for parameters. As installed this sets the point of view to
'world coordinates' (i.e. x running from left to right, y from front
to back, z from bottom to top), the mouseMode
to
(zAxis, zoom, fov)
, and the field of view to 30 degrees. useFreeType
defaults to FALSE
on
Windows; on other systems it indicates the availability
of FreeType.
Users may create their own variable named r3dDefaults
in the global
environment and it will override the installed one. If there
is a bg
element in the list or the arguments, it should be
a list of arguments to pass to the bg3d
function to
set the background.
The arguments to open3d
may include material
, a list
of material properties as in r3dDefaults
, but note
that high level functions such as plot3d
normally use
the r3dDefaults
values in preference to this setting.
If useNULL
is TRUE
, RGL will use a “null”
device. This device records objects as they are plotted, but
displays nothing. It is intended for use with rglwidget
.
The open3d
function returns the device that
was opened. If
silent = TRUE
, it is returned invisibly.
The cur3d
function returns the current device,
or the value 0 if there isn't one. rgl.dev.list
returns a vector of all open devices. Items are named
according to the type of device: null
for a hidden
null device,
wgl
for a Windows device, and glX
for an X windows device.
set3d
returns the device number of the
previously active device.
The close3d
function returns the new current
device, invisibly.
The r3dDefaults
variable is a list containing default
settings. The getr3dDefaults
function searches the user's
global environment for r3dDefaults
and returns the
one in the RGL namespace if it was not found there.
The components of the list may include any settable par3d
parameter, or "material"
, which should include a list
of default material3d
properties, or "bg"
,
which is a
list of defaults to pass to the bg3d
function.
rgl.quit
attempts to unload rgl and then returns
NULL
invisibly.
rgl.useNULL
for default usage of null device.
r3dDefaults open3d() shade3d(cube3d(color = rainbow(6), meshColor = "faces")) cur3d()
r3dDefaults open3d() shade3d(cube3d(color = rainbow(6), meshColor = "faces")) cur3d()
par3d
can be used to set or query graphical parameters in RGL.
Parameters can be set by specifying them as arguments to par3d
in
name = value
form, or by passing them as a list of named
values.
par3d(..., no.readonly = FALSE, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.par3d.names rgl.par3d.readonly
par3d(..., no.readonly = FALSE, dev = cur3d(), subscene = currentSubscene3d(dev)) rgl.par3d.names rgl.par3d.readonly
... |
arguments in |
no.readonly |
logical; if |
dev |
integer; the RGL device. |
subscene |
integer; the subscene. |
Parameters are queried by giving one or more character vectors to
par3d
.
par3d()
(no arguments) or par3d(no.readonly = TRUE)
is used to
get all the graphical parameters (as a named list).
By default, queries and modifications apply to the current subscene
on the current device; specify dev
and/or subscene
to
change this. Some parameters apply to the device as a whole;
these are marked in the list below.
When parameters are set, their former values are returned in an
invisible named list. Such a list can be passed as an argument to
par3d
to restore the parameter values.
Use par3d(no.readonly = TRUE)
for the full list of parameters
that can be restored.
When just one parameter is queried, its value is returned directly. When two or more parameters are queried, the result is a list of values, with the list names giving the parameters.
Note the inconsistency: setting one parameter returns a list, but querying one parameter returns an object.
The rgl.par3d.names
variable contains the
full list of names of par3d
properties.
rgl.par3d.readonly
contains the list of
read-only properties.
In the list below, R.O. indicates the read-only arguments: These may only be used in queries, they do not set anything.
activeSubscene
R.O. integer. Used
with rgl.setMouseCallbacks
: during a
callback, indicates the id of the subscene that was clicked.
antialias
R.O. in par3d
, may be
set in open3d
. The (requested) number of hardware
antialiasing planes to use (with multisample antialiasing). The
OpenGL driver may not support the requested number, in which case
par3d("antialias")
will report what was actually set. Applies to the whole device.
cex
real. The default size for text.
family
character. The default device independent family name; see text3d
.
Applies to the whole device.
font
integer. The default font number (from 1 to 4; see text3d
).
Applies to the whole device.
useFreeType
logical. Should FreeType fonts be used? Applies to the whole device.
fontname
R.O.; the system-dependent name of the current font. Applies to the whole device.
FOV
real. The field of view, from 0 to 179 degrees. This controls
the degree of parallax in the perspective view.
Isometric perspective corresponds to FOV = 0
.
ignoreExtent
logical. Set to TRUE
so that subsequently plotted objects
will be ignored in calculating the bounding box of the scene. Applies to the
whole device.
maxClipPlanes
R.O.; an integer giving the maximum number of clip planes that can be defined in the current system. Applies to the whole device.
modelMatrix
R.O.; a 4 by 4 matrix describing the position of the user data. See the Note below.
listeners
integer. A vector of subscene id values. If a subscene receives
a mouse event (see mouseMode
just below), the same action will be carried out
on all subscenes in this list. (The subscene itself is normally listed as a listener.
If it is not listed, it will not respond to its own mouse events.)
mouseMode
character. A vector of 5 strings describing
mouse actions. The 5 entries are named
c("none", "left", "right", "middle", "wheel")
, corresponding to actions for no button,
the left, right or middle button, and the
mouse wheel.
Partial matching to action names is used. Possible values for the actions are:
"none"
No action for this button.
"trackball"
Mouse acts as a virtual trackball, rotating the scene.
"xAxis"
Similar to "trackball"
, but restricted to X axis rotation.
"yAxis"
Y axis rotation.
"zAxis"
Z axis rotation.
"polar"
Mouse rotates the scene by moving in polar coordinates.
"selecting"
Mouse is used for selection. This is not normally set by the
user, but is used internally by the select3d
function.
"zoom"
Mouse is used to zoom the display.
"fov"
Mouse changes the field of view of the display.
"user"
Used when a user handler is set by
rgl.setMouseCallbacks
.
Possible values for the last entry corresponding to the mouse wheel also include
"pull"
Pulling on the mouse wheel increases magnification, i.e. “pulls the scene closer”.
"push"
Pulling on the mouse wheel decreases magnification, i.e. “pushes the scene away”.
"user2"
Used when a user handler is set by
rgl.setWheelCallback
.
A common default on Mac OSX is to convert a two finger drag on a trackpad to a mouse wheel rotation.
The first entry is for actions to take when no mouse button is pressed. Legal values are the same as for the mouse buttons.
The first entry was added after rgl version 0.106.8. For back compatibility, if the vector
of actions is less than 5 entries, "none"
will be added at the start of it.
observer
R.O.; the position of the observer relative to
the model. Set by observer3d
. See the Note below.
projMatrix
R.O.; a 4 by 4 matrix describing the current projection of the scene.
scale
real. A vector of 3 values indicating the amount by which to
rescale each axis before display. Set by aspect3d
.
skipRedraw
whether to update the display. Set to TRUE
to suspend
updating while making multiple changes to the scene. See demo(hist3d)
for an example.
Applies to the whole device.
userMatrix
a 4 by 4 matrix describing user actions to display the scene.
userProjection
a 4 by 4 matrix describing changes to the projection.
viewport
real. A vector giving the dimensions of the
window in pixels. The entries are taken to be c(x, y, width, height)
where
c(x, y)
are the coordinates in pixels of the lower left corner within the window.
zoom
real. A positive value indicating the current magnification of the scene.
bbox
R.O.; real. A vector of six values indicating the current values of the bounding box of the scene (xmin, xmax, ymin, ymax, zmin, zmax)
windowRect
integer. A vector of four values indicating the left, top, right and bottom of the displayed window (in pixels). Applies to the whole device.
The parameters returned by par3d
are sufficient to determine where RGL would render
a point on the screen. Given a column vector (x, y, z)
in a subscene s
,
it performs the equivalent of
the following operations:
It converts the point to homogeneous coordinates by appending w = 1
, giving
the vector v = (x, y, z, 1)
.
It calculates the M = par3d("modelMatrix")
as a product from right to left
of several matrices:
A matrix to translate the centre of the bounding box to the origin.
A matrix to rescale according to par3d("scale")
.
The par3d("userMatrix")
as set by the user.
A matrix which may be set by mouse movements.
The description above applies to the usual case where there
is just one subscene, or where the subscene's "model"
is set to
"replace"
. If it is set to "modify"
, the first step is
skipped, and at the end the procedure is followed for the parent subscene. If it is set to "inherit"
only the parent settings
are used.
It multiplies the point by M
giving u = M %*% v
.
It multiplies that point by a matrix based on the observer position to translate the origin to the centre of the viewing region.
Using this location and information on the normals (which have been similarly transformed), it performs lighting calculations.
It obtains the projection matrix P = par3d("projMatrix")
based on the bounding box and field of view
or observer location, multiplies that by the userProjection
matrix to give P
. It multiplies the point
by it giving P %*% u = (x2, y2, z2, w2)
.
It converts back to Euclidean coordinates by dividing the first 3 coordinates by w2
.
The new value z2/w2
represents the depth into the scene of the point. Depending
on what has already been plotted, this depth might be obscured, in which case nothing more is plotted.
If the point is not culled due to depth, the x2
and y2
values are used
to determine the point in the image. The par3d("viewport")
values are used to translate
from the range (-1, 1)
to pixel locations, and the point is plotted.
If hardware antialiasing is enabled, then the whole process is repeated multiple times (at least conceptually) with different locations in each pixel sampled to determine what is plotted there, and then the images are combined into what is displayed.
See ?matrices for more information on homogeneous and Euclidean coordinates.
Note that many of these calculations are done on the graphics card using single precision; you will likely see signs of rounding error if your scene requires more than 4 or 5 digit precision to distinguish values in any coordinate.
The "xAxis"
, "yAxis"
and "zAxis"
mouse modes rotate
relative to the coordinate system of the data, regardless of the current
orientation of the scene.
When multiple parameters are set, they are set in the order given. In some
cases this may lead to warnings and ignored values; for example, some font
families only support cex = 1
, so changing both cex
and
family
needs to be done in the right order. For example, when using the
"bitmap"
family on Windows, par3d(family = "sans", cex = 2)
will work, but par3d(cex = 2, family = "sans")
will leave cex
at 1 (with a warning that the "bitmap"
family only supports that size).
Although par3d("viewport")
names the entries of the reported vector,
names are ignored when setting the viewport and entries must be specified
in the standard order.
In rgl versions 0.94.x the modelMatrix
entry had a changed meaning;
before and after that it contains a copy of the OpenGL MODELVIEW matrix.
As of version 0.100.32, when changing the "windowRect"
parameter, the
"viewport"
for the root (or specified) subscene
is changed immediately. This fixes a bug where in earlier
versions it would only be changed when the window was redrawn, potentially after another command making use of the value.
Default values are not described here, as several of them
are changed by the r3dDefaults
variable when
the window is opened by open3d
.
OpenGL Architecture Review Board (1997). OpenGL Programming Guide. Addison-Wesley.
view3d
to set FOV
and zoom
.
open3d
for how to open a new window
with default settings for these parameters.
open3d() shade3d(cube3d(color = rainbow(6), meshColor = "faces")) save <- par3d(userMatrix = rotationMatrix(90*pi/180, 1, 0, 0)) highlevel() # To trigger display save par3d("userMatrix") par3d(save) highlevel() par3d("userMatrix")
open3d() shade3d(cube3d(color = rainbow(6), meshColor = "faces")) save <- par3d(userMatrix = rotationMatrix(90*pi/180, 1, 0, 0)) highlevel() # To trigger display save par3d("userMatrix") par3d(save) highlevel() par3d("userMatrix")
Returns a function which interpolates par3d
parameter values,
suitable for use in animations.
par3dinterp(times = NULL, userMatrix, scale, zoom, FOV, method = c("spline", "linear"), extrapolate = c("oscillate", "cycle", "constant", "natural"), dev = cur3d(), subscene = par3d("listeners", dev = dev))
par3dinterp(times = NULL, userMatrix, scale, zoom, FOV, method = c("spline", "linear"), extrapolate = c("oscillate", "cycle", "constant", "natural"), dev = cur3d(), subscene = par3d("listeners", dev = dev))
times |
Times at which values are recorded or a list; see below |
userMatrix |
Values of |
scale |
Values of |
zoom |
Values of |
FOV |
Values of |
method |
Method of interpolation |
extrapolate |
How to extrapolate outside the time range |
dev |
Which RGL device to use |
subscene |
Which subscene to use |
This function is intended to be used in constructing animations. It produces
a function that returns a list suitable to pass to par3d
, to set
the viewpoint at a given point in time.
All of the parameters are optional. Only those par3d
parameters that are
specified will be returned.
The input values other than times
may each be specified as
lists, giving the parameter value settings at a fixed time, or as
matrices or arrays. If not lists, the following formats should be
used: userMatrix
can be a 4 x 4 x n
array, or a 4
x 4n
matrix; scale
should be an n x 3
matrix;
zoom
and FOV
should be length n
vectors.
An alternative form of input is to put all of the above arguments into
a list (i.e. a list of lists, or a list of arrays/matrices/vectors),
and pass it as the first argument. This is the most convenient way to
use this function with the function
tkpar3dsave
.
Interpolation is by cubic spline or linear interpolation in an appropriate coordinate-wise fashion. Extrapolation may oscillate (repeat the sequence forward, backward, forward, etc.), cycle (repeat it forward), be constant (no repetition outside the specified time range), or be natural (linear on an appropriate scale). In the case of cycling, the first and last specified values should be equal, or the last one will be dropped. Natural extrapolation is only supported with spline interpolation.
A function is returned. The function takes one argument, and returns a list of
par3d
settings interpolated to that time.
Prior to rgl version 0.95.1476, the
subscene
argument defaulted to the
current subscene, and any additional entries would
be ignored by play3d
. The current
default value of par3d("listeners", dev = dev)
means that all subscenes that share
mouse responses will also share modifications
by this function.
Duncan Murdoch
play3d
to play the animation.
f <- par3dinterp( zoom = c(1, 2, 3, 1) ) f(0) f(1) f(0.5) ## Not run: play3d(f) ## End(Not run)
f <- par3dinterp( zoom = c(1, 2, 3, 1) ) f(0) f(1) f(0.5) ## Not run: play3d(f) ## End(Not run)
This control works with playwidget
to
change settings in a WebGL display in the same way
as par3dinterp
does within R.
par3dinterpControl(fn, from, to, steps, subscene = NULL, omitConstant = TRUE, ...)
par3dinterpControl(fn, from, to, steps, subscene = NULL, omitConstant = TRUE, ...)
fn |
A function returned from |
from , to , steps
|
Values where |
subscene |
Which subscene's properties should be modified? |
omitConstant |
If |
... |
Additional parameters which will be passed
to |
par3dinterpSetter
sets parameters corresponding to values produced by the result of
par3dinterp
.
Returns controller data in a list of class "rglControl".
Duncan Murdoch
example(plot3d) M <- r3dDefaults$userMatrix fn <- par3dinterp(times = (0:2)*0.75, userMatrix = list(M, rotate3d(M, pi/2, 1, 0, 0), rotate3d(M, pi/2, 0, 1, 0)), scale = c(0.5, 1, 2)) control <- par3dinterpControl(fn, 0, 3, steps = 15) control if (interactive() || in_pkgdown_example()) rglwidget(width = 500, height = 250) %>% playwidget(control, step = 0.01, loop = TRUE, rate = 0.5)
example(plot3d) M <- r3dDefaults$userMatrix fn <- par3dinterp(times = (0:2)*0.75, userMatrix = list(M, rotate3d(M, pi/2, 1, 0, 0), rotate3d(M, pi/2, 0, 1, 0)), scale = c(0.5, 1, 2)) control <- par3dinterpControl(fn, 0, 3, steps = 15) control if (interactive() || in_pkgdown_example()) rglwidget(width = 500, height = 250) %>% playwidget(control, step = 0.01, loop = TRUE, rate = 0.5)
This function plots symbols similarly to
what the base graphics function points
does when pch
is specified.
pch3d(x, y = NULL, z = NULL, pch = 1, bg = material3d("color")[1], cex = 1, radius, color = "black", lit = FALSE, ...)
pch3d(x, y = NULL, z = NULL, pch = 1, bg = material3d("color")[1], cex = 1, radius, color = "black", lit = FALSE, ...)
x , y , z
|
The locations at which to plot in a form
suitable for use in |
pch |
A vector of integers or single characters describing the symbols to plot. |
bg |
The fill color(s) to use for |
cex |
A relative size of the symbol to plot. |
radius |
An absolute size of the symbol to plot in user coordinates. |
color |
The color(s) to use for symbols. |
lit |
Whether the object responds to lighting or just shows the displayed color directly. |
... |
Other material properties. |
The list of symbols encoded by numerical
pch
values is given in the points
help page.
A vector of object id values is returned invisibly. Separate objects will be drawn
for each different combination of pch
value from
0 to 25, color
and bg
, and another holding all the character
symbols.
This function is not a perfect match to how the points
function works due
to limitations in RGL and OpenGL. In
particular:
Symbols with numbers from 1 to 25 are drawn
as 3D sprites (see sprites3d
),
so they will resize as the window is zoomed.
Letters and numbers from 32 to 255 (which are
mapped to letters) are drawn using text3d
,
so they maintain a fixed size.
A calculation somewhat like the one in plot3d
that sets the size
of spheres is used to choose the size of
sprites based on cex
and the
current scaling. This will likely need manual
tweaking. Use the radius
argument
for a fixed size.
No special handling is done for the case of
pch = "."
. Use points3d
for
small dots.
As of rgl version 0.100.10, background and foreground colors can vary from symbol to symbol.
Duncan Murdoch
points3d
, text3d
and
sprites3d
for other ways to label points,
points
for the base graphics symbol
definitions.
open3d() i <- 0:25; x <- i %% 5; y <- rep(0, 26); z <- i %/% 5 pch3d(x, y, z, pch = i, bg = "gray", color = rainbow(26)) text3d(x, y, z + 0.3, i) pch3d(x + 5, y, z, pch = i+65) text3d(x + 5, y, z + 0.3, i+65)
open3d() i <- 0:25; x <- i %% 5; y <- rep(0, 26); z <- i %/% 5 pch3d(x, y, z, pch = i, bg = "gray", color = rainbow(26)) text3d(x, y, z + 0.3, i) pch3d(x + 5, y, z, pch = i+65) text3d(x + 5, y, z + 0.3, i+65)
This function draws plots of surfaces in 3-space. persp3d
is a generic function.
persp3d(x, ...) ## Default S3 method: persp3d(x = seq(0, 1, length.out = nrow(z)), y = seq(0, 1, length.out = ncol(z)), z, xlim = NULL, ylim = NULL, zlim = NULL, xlab = NULL, ylab = NULL, zlab = NULL, add = FALSE, aspect = !add, forceClipregion = FALSE, ...)
persp3d(x, ...) ## Default S3 method: persp3d(x = seq(0, 1, length.out = nrow(z)), y = seq(0, 1, length.out = ncol(z)), z, xlim = NULL, ylim = NULL, zlim = NULL, xlab = NULL, ylab = NULL, zlab = NULL, add = FALSE, aspect = !add, forceClipregion = FALSE, ...)
x , y , z
|
points to plot on surface. See Details below. |
xlim , ylim , zlim
|
x-, y- and z-limits. If present, the plot is clipped to this region. |
xlab , ylab , zlab
|
titles for the axes. N.B. These must be character strings; expressions are not accepted. Numbers will be coerced to character strings. |
add |
whether to add the points to an existing plot. |
aspect |
either a logical indicating whether to adjust the aspect ratio, or a new ratio. |
forceClipregion |
force a clipping region to be used, whether or not limits are given. |
... |
additional material parameters to be passed to |
The default method plots a surface defined as a grid of (x,y,z)
locations in space. The grid may be specified in several ways:
As with persp
, x
and y
may be given as vectors in ascending order, with z
given as a matrix. There
should be one x
value for each row of z
and
one y
value for each column. The surface drawn will
have x
constant across rows and y
constant
across columns. This is the most convenient format when
z
is a function of x
and y
which are
measured on a regular grid.
x
and y
may also be given as matrices, in which
case they should have the same dimensions as z
. The
surface will combine corresponding points in each matrix
into locations (x,y,z)
and draw the surface through those. This
allows general surfaces to be drawn, as in the example of a spherical Earth shown below.
If x
is a list
, its components x$x
, x$y
and x$z
are used for x
, y
and
z
respectively, though an explicitly specified
z
value will have priority.
One difference from persp
is that colors are specified on each
vertex, rather than on each facet of the surface. To emulate the persp
color handling, you need to do the following. First, convert the color vector to
an (nx - 1)
by (ny - 1)
matrix; then add an extra row before row 1,
and an extra column after the last column, to convert it to nx
by ny
.
(These extra colors will not be used). For example,
col <- rbind(1, cbind(matrix(col, nx - 1, ny - 1), 1))
.
Finally, call persp3d
with material property smooth = FALSE
.
See the “Clipping” section in plot3d
for more details on xlim, ylim, zlim
and forceClipregion
.
This function is called for the side effect of drawing the plot. A vector of shape IDs is returned invisibly.
Duncan Murdoch
plot3d
, persp
. There is
a persp3d.function
method for drawing functions, and persp3d.deldir
can be used
to draw surfaces defined by an irregular collection of points. A formula method persp3d.formula
draws
surfaces using this method.
The surface3d
function is used to draw the surface without the axes etc.
# (1) The Obligatory Mathematical surface. # Rotated sinc function. x <- seq(-10, 10, length.out = 20) y <- x f <- function(x, y) { r <- sqrt(x^2 + y^2); 10 * sin(r)/r } z <- outer(x, y, f) z[is.na(z)] <- 1 open3d() # Draw the surface twice: the first draws the solid part, # the second draws the grid. Offset the first so it doesn't # obscure the lines. persp3d(x, y, z, aspect = c(1, 1, 0.5), col = "lightblue", xlab = "X", ylab = "Y", zlab = "Sinc( r )", polygon_offset = 1) persp3d(x, y, z, front = "lines", back = "lines", lit = FALSE, add = TRUE) highlevel() # trigger the plot # (2) Add to existing persp plot: xE <- c(-10, 10); xy <- expand.grid(xE, xE) points3d(xy[, 1], xy[, 2], 6, col = "red") lines3d(x, y = 10, z = 6 + sin(x), col = "green") phi <- seq(0, 2*pi, length.out = 201) r1 <- 7.725 # radius of 2nd maximum xr <- r1 * cos(phi) yr <- r1 * sin(phi) lines3d(xr, yr, f(xr, yr), col = "pink", lwd = 2) # (3) Visualizing a simple DEM model z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) open3d() invisible(bg3d("slategray")) # suppress display material3d(col = "black") persp3d(x, y, z, col = "green3", aspect = "iso", axes = FALSE, box = FALSE) # (4) A globe lat <- matrix(seq(90, -90, length.out = 50)*pi/180, 50, 50, byrow = TRUE) long <- matrix(seq(-180, 180, length.out = 50)*pi/180, 50, 50) r <- 6378.1 # radius of Earth in km x <- r*cos(lat)*cos(long) y <- r*cos(lat)*sin(long) z <- r*sin(lat) open3d() persp3d(x, y, z, col = "white", texture = system.file("textures/worldsmall.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z) ## Not run: # This looks much better, but is slow because the texture is very big persp3d(x, y, z, col = "white", texture = system.file("textures/world.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z) ## End(Not run)
# (1) The Obligatory Mathematical surface. # Rotated sinc function. x <- seq(-10, 10, length.out = 20) y <- x f <- function(x, y) { r <- sqrt(x^2 + y^2); 10 * sin(r)/r } z <- outer(x, y, f) z[is.na(z)] <- 1 open3d() # Draw the surface twice: the first draws the solid part, # the second draws the grid. Offset the first so it doesn't # obscure the lines. persp3d(x, y, z, aspect = c(1, 1, 0.5), col = "lightblue", xlab = "X", ylab = "Y", zlab = "Sinc( r )", polygon_offset = 1) persp3d(x, y, z, front = "lines", back = "lines", lit = FALSE, add = TRUE) highlevel() # trigger the plot # (2) Add to existing persp plot: xE <- c(-10, 10); xy <- expand.grid(xE, xE) points3d(xy[, 1], xy[, 2], 6, col = "red") lines3d(x, y = 10, z = 6 + sin(x), col = "green") phi <- seq(0, 2*pi, length.out = 201) r1 <- 7.725 # radius of 2nd maximum xr <- r1 * cos(phi) yr <- r1 * sin(phi) lines3d(xr, yr, f(xr, yr), col = "pink", lwd = 2) # (3) Visualizing a simple DEM model z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) open3d() invisible(bg3d("slategray")) # suppress display material3d(col = "black") persp3d(x, y, z, col = "green3", aspect = "iso", axes = FALSE, box = FALSE) # (4) A globe lat <- matrix(seq(90, -90, length.out = 50)*pi/180, 50, 50, byrow = TRUE) long <- matrix(seq(-180, 180, length.out = 50)*pi/180, 50, 50) r <- 6378.1 # radius of Earth in km x <- r*cos(lat)*cos(long) y <- r*cos(lat)*sin(long) z <- r*sin(lat) open3d() persp3d(x, y, z, col = "white", texture = system.file("textures/worldsmall.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z) ## Not run: # This looks much better, but is slow because the texture is very big persp3d(x, y, z, col = "white", texture = system.file("textures/world.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z) ## End(Not run)
The deldir()
function in the deldir package computes a Delaunay triangulation of a set
of points. These functions display it as a surface.
## S3 method for class 'deldir' plot3d(x, ...) ## S3 method for class 'deldir' persp3d(x, ..., add = FALSE) ## S3 method for class 'deldir' as.mesh3d(x, col = "gray", coords = c("x", "y", "z"), smooth = TRUE, normals = NULL, texcoords = NULL, ...)
## S3 method for class 'deldir' plot3d(x, ...) ## S3 method for class 'deldir' persp3d(x, ..., add = FALSE) ## S3 method for class 'deldir' as.mesh3d(x, col = "gray", coords = c("x", "y", "z"), smooth = TRUE, normals = NULL, texcoords = NULL, ...)
x |
A |
add |
Whether to add surface to existing plot ( |
col |
Colors to apply to each vertex in the triangulation. Will be recycled as needed. |
coords |
See Details below. |
smooth |
Whether to average normals at vertices for a smooth appearance. |
normals |
User-specified normals at each vertex. Requires |
texcoords |
Texture coordinates at each vertex. |
... |
See Details below. |
These functions construct a mesh3d
object
corresponding to the triangulation in x
. The
plot3d
and persp3d
methods plot it.
The coords
parameter allows surfaces to be
plotted over any coordinate plane. It should be
a permutation of the column names c("x", "y", "z")
from the "deldir"
object. The first will be used
as the x coordinate, the second as the y coordinate,
and the third as the z coordinate.
The ...
parameters in plot3d.deldir
are passed to persp3d.deldir
; in persp3d.deldir
they are
passed to both as.mesh3d.deldir
and persp3d.mesh3d
;
in as.mesh3d.deldir
they are used as material parameters
in a tmesh3d
call.
x <- rnorm(200, sd = 5) y <- rnorm(200, sd = 5) r <- sqrt(x^2 + y^2) z <- 10 * sin(r)/r col <- cm.colors(20)[1 + round(19*(z - min(z))/diff(range(z)))] save <- options(rgl.meshColorWarning = FALSE) # This code is awkward: to work with demo(rglExamples), # we need auto-printing of the plots. This means we # have to repeat the test for deldir. haveDeldir <- checkDeldir() if (haveDeldir) { dxyz <- deldir::deldir(x, y, z = z, suppressMsge = TRUE) persp3d(dxyz, col = col) } if (haveDeldir) { open3d() # Do it without smoothing and with a different orientation. persp3d(dxyz, col = col, coords = c("z", "x", "y"), smooth = FALSE) } options(save)
x <- rnorm(200, sd = 5) y <- rnorm(200, sd = 5) r <- sqrt(x^2 + y^2) z <- 10 * sin(r)/r col <- cm.colors(20)[1 + round(19*(z - min(z))/diff(range(z)))] save <- options(rgl.meshColorWarning = FALSE) # This code is awkward: to work with demo(rglExamples), # we need auto-printing of the plots. This means we # have to repeat the test for deldir. haveDeldir <- checkDeldir() if (haveDeldir) { dxyz <- deldir::deldir(x, y, z = z, suppressMsge = TRUE) persp3d(dxyz, col = col) } if (haveDeldir) { open3d() # Do it without smoothing and with a different orientation. persp3d(dxyz, col = col, coords = c("z", "x", "y"), smooth = FALSE) } options(save)
Plot a function z(x, y)
or a parametric function
(x(s, t), y(s, t), z(s, t))
.
## S3 method for class ''function'' persp3d(x, xlim = c(0, 1), ylim = c(0, 1), slim = NULL, tlim = NULL, n = 101, xvals = seq.int(min(xlim), max(xlim), length.out = n[1]), yvals = seq.int(min(ylim), max(ylim), length.out = n[2]), svals = seq.int(min(slim), max(slim), length.out = n[1]), tvals = seq.int(min(tlim), max(tlim), length.out = n[2]), xlab, ylab, zlab, col = "gray", otherargs = list(), normal = NULL, texcoords = NULL, ...) ## S3 method for class ''function'' plot3d(x, ...)
## S3 method for class ''function'' persp3d(x, xlim = c(0, 1), ylim = c(0, 1), slim = NULL, tlim = NULL, n = 101, xvals = seq.int(min(xlim), max(xlim), length.out = n[1]), yvals = seq.int(min(ylim), max(ylim), length.out = n[2]), svals = seq.int(min(slim), max(slim), length.out = n[1]), tvals = seq.int(min(tlim), max(tlim), length.out = n[2]), xlab, ylab, zlab, col = "gray", otherargs = list(), normal = NULL, texcoords = NULL, ...) ## S3 method for class ''function'' plot3d(x, ...)
x |
A function of two arguments. See the details below. |
xlim , ylim
|
By default, the range of x and y values. For a parametric surface, if these are not missing, they are used as limits on the displayed x and y values. |
slim , tlim
|
If not |
n |
A one or two element vector giving the number of steps in the x and y (or s and t) grid. |
xvals , yvals
|
The values at which to evaluate x and y. Ignored for
a parametric surface. If used, |
svals , tvals
|
The values at which to evaluate s and t for a parametric
surface. Only used if
|
xlab , ylab , zlab
|
The axis labels. See the details below for the defaults. |
col |
The color to use for the plot. See the details below. |
otherargs |
Additional arguments to pass to the function. |
normal , texcoords
|
Functions to set surface normals or texture coordinates. See the details below. |
... |
Additional arguments to pass to |
The "function"
method for plot3d
simply passes
all arguments to persp3d
. Thus this description applies
to both.
The first argument x
is required to be a function. It
is named x
only because of the requirements of the S3
system; in the remainder of this help page, we will assume
that the assignment f <- x
has been made, and will
refer to the function f()
.
persp3d.function
evaluates f()
on a two-dimensional
grid of values, and displays the resulting surface. The values
on the grid will be passed in as vectors in the
first two arguments to the function, so f()
needs
to be vectorized. Other optional arguments to f()
can be specified in the otherargs
list.
In the default form where slim
and tlim
are both
NULL
, it is assumed that f(x, y)
returns heights,
which will be plotted in the z coordinate. The default axis labels
will be taken from the argument names to f()
and the
expression passed as argument x
to this function.
If slim
or tlim
is specified, a parametric
surface is plotted. The function
f(s, t)
must return a 3-column matrix, giving x, y and z
coordinates of points on the surface. The default axis
labels will be the column names if those are present.
In this case xlim
, ylim
and zlim
are used to define a clipping region only if specified;
the defaults are ignored.
The color of the surface may be specified as the name of a color, or a vector or matrix of color names. In this case the colors will be recycled across the points on the grid of values.
Alternatively, a function may be given: it should be a function
like rainbow
that takes an integer argument and
returns a vector of colors. In this case the colors are mapped
to z values.
The normal
argument allows specification of a function
to compute normal vectors to the surface. This function is
passed the same arguments as f()
(including otherargs
if present), and should produce a 3-column matrix containing the
x, y and z coordinates of the normals.
The texcoords
argument is a function similar to normal
, but
it produces a 2-column matrix containing texture coordinates.
Both normal
and texcoords
may also contain matrices,
with 3 and 2 columns respectively, and rows corresponding to the
points that were passed to f()
.
This function constructs a call to persp3d
and returns the value from that function.
Duncan Murdoch
The curve
function in base graphics does
something similar for functions of one variable. See the
example below for space curves.
# (1) The Obligatory Mathematical surface. # Rotated sinc function, with colors f <- function(x, y) { r <- sqrt(x^2 + y^2) ifelse(r == 0, 10, 10 * sin(r)/r) } open3d() plot3d(f, col = colorRampPalette(c("blue", "white", "red")), xlab = "X", ylab = "Y", zlab = "Sinc( r )", xlim = c(-10, 10), ylim = c(-10, 10), aspect = c(1, 1, 0.5)) # (2) A cylindrical plot f <- function(s, t) { r <- 1 + exp( -pmin( (s - t)^2, (s - t - 1)^2, (s - t + 1)^2 )/0.01 ) cbind(r*cos(t*2*pi), r*sin(t*2*pi), s) } open3d() plot3d(f, slim = c(0, 1), tlim = c(0, 1), col = "red", alpha = 0.8) # Add a curve to the plot, fixing s at 0.5. plot3d(f(0.5, seq.int(0, 1, length.out = 100)), type = "l", add = TRUE, lwd = 3, depth_test = "lequal")
# (1) The Obligatory Mathematical surface. # Rotated sinc function, with colors f <- function(x, y) { r <- sqrt(x^2 + y^2) ifelse(r == 0, 10, 10 * sin(r)/r) } open3d() plot3d(f, col = colorRampPalette(c("blue", "white", "red")), xlab = "X", ylab = "Y", zlab = "Sinc( r )", xlim = c(-10, 10), ylim = c(-10, 10), aspect = c(1, 1, 0.5)) # (2) A cylindrical plot f <- function(s, t) { r <- 1 + exp( -pmin( (s - t)^2, (s - t - 1)^2, (s - t + 1)^2 )/0.01 ) cbind(r*cos(t*2*pi), r*sin(t*2*pi), s) } open3d() plot3d(f, slim = c(0, 1), tlim = c(0, 1), col = "red", alpha = 0.8) # Add a curve to the plot, fixing s at 0.5. plot3d(f(0.5, seq.int(0, 1, length.out = 100)), type = "l", add = TRUE, lwd = 3, depth_test = "lequal")
The tri.mesh()
functions in the
interp and tripack packages compute a Delaunay triangulation of a set
of points. These functions display it as a surface.
## S3 method for class 'triSht' plot3d(x, z, ...) ## S3 method for class 'triSht' persp3d(x, z, ..., add = FALSE) ## S3 method for class 'triSht' as.mesh3d(x, z, col = "gray", coords = c("x", "y", "z"), smooth = TRUE, normals = NULL, texcoords = NULL, ...) ## S3 method for class 'tri' plot3d(x, z, ...) ## S3 method for class 'tri' persp3d(x, z, ..., add = FALSE) ## S3 method for class 'tri' as.mesh3d(x, z, col = "gray", coords = c("x", "y", "z"), smooth = TRUE, normals = NULL, texcoords = NULL, ...)
## S3 method for class 'triSht' plot3d(x, z, ...) ## S3 method for class 'triSht' persp3d(x, z, ..., add = FALSE) ## S3 method for class 'triSht' as.mesh3d(x, z, col = "gray", coords = c("x", "y", "z"), smooth = TRUE, normals = NULL, texcoords = NULL, ...) ## S3 method for class 'tri' plot3d(x, z, ...) ## S3 method for class 'tri' persp3d(x, z, ..., add = FALSE) ## S3 method for class 'tri' as.mesh3d(x, z, col = "gray", coords = c("x", "y", "z"), smooth = TRUE, normals = NULL, texcoords = NULL, ...)
x |
A |
z |
z coordinate values corresponding to each of the nodes in |
add |
Whether to add surface to existing plot ( |
col |
Colors to apply to each vertex in the triangulation. Will be recycled as needed. |
coords |
See Details below. |
smooth |
Whether to average normals at vertices for a smooth appearance. |
normals |
User-specified normals at each vertex. Requires |
texcoords |
Texture coordinates at each vertex. |
... |
See Details below. |
These functions construct a mesh3d
object
corresponding to the triangulation in x
. The
plot3d
and persp3d
methods plot it.
The coords
parameter allows surfaces to be
plotted over any coordinate plane. It should be
a permutation of the column names c("x", "y", "z")
.
The first will be used
as the x coordinate, the second as the y coordinate,
and the third as the z coordinate.
The ...
parameters in plot3d.triSht
and plot3d.tri
are passed to persp3d
; in persp3d.triSht
and persp3d.tri
they are
passed to both as.mesh3d
and persp3d.mesh3d
;
in as.mesh3d.triSht
and as.mesh3d.tri
they are used as material parameters
in a tmesh3d
call.
"tri"
objects may contain constraints. These appear
internally as extra nodes, representing either the inside
or outside of boundaries on the region being triangulated.
Each of these nodes should also have a z
value, but
triangles corresponding entirely to constraint nodes will not
be drawn. In this way complex, non-convex regions can
be triangulated. See the second example below.
If there are duplicate points, the tri.mesh()
functions
will optionally delete some of them. If you choose this option,
the z
values must correspond to the nodes after
deletion, not before.
x <- rnorm(200, sd = 5) y <- rnorm(200, sd = 5) r <- sqrt(x^2 + y^2) z <- 10 * sin(r)/r col <- cm.colors(20)[1 + round(19*(z - min(z))/diff(range(z)))] save <- NULL if ((haveinterp <- requireNamespace("interp", quietly = TRUE))) { save <- options(rgl.meshColorWarning = FALSE) dxy <- interp::tri.mesh(x, y) open3d() persp3d(dxy, z, col = col, meshColor = "vertices") } if (haveinterp) { open3d() # Do it without smoothing and with a different orientation. persp3d(dxy, z, col = col, coords = c("z", "x", "y"), smooth = FALSE) } if (requireNamespace("tripack", quietly = TRUE)) { if (is.null(save)) save <- options(rgl.meshColorWarning = FALSE) # Leave a circular hole around (3, 0) theta <- seq(0, 2*pi, length.out = 30)[-1] cx <- 2*cos(theta) + 3 cy <- 2*sin(theta) keep <- (x - 3)^2 + y^2 > 4 dxy2 <- tripack::tri.mesh(x[keep], y[keep]) dxy2 <- tripack::add.constraint(dxy2, cx, cy) z <- dxy2$x^2 - dxy2$y^2 col <- terrain.colors(20)[1 + round(19*(z - min(z))/diff(range(z)))] open3d() persp3d(dxy2, z, col = col) } options(save)
x <- rnorm(200, sd = 5) y <- rnorm(200, sd = 5) r <- sqrt(x^2 + y^2) z <- 10 * sin(r)/r col <- cm.colors(20)[1 + round(19*(z - min(z))/diff(range(z)))] save <- NULL if ((haveinterp <- requireNamespace("interp", quietly = TRUE))) { save <- options(rgl.meshColorWarning = FALSE) dxy <- interp::tri.mesh(x, y) open3d() persp3d(dxy, z, col = col, meshColor = "vertices") } if (haveinterp) { open3d() # Do it without smoothing and with a different orientation. persp3d(dxy, z, col = col, coords = c("z", "x", "y"), smooth = FALSE) } if (requireNamespace("tripack", quietly = TRUE)) { if (is.null(save)) save <- options(rgl.meshColorWarning = FALSE) # Leave a circular hole around (3, 0) theta <- seq(0, 2*pi, length.out = 30)[-1] cx <- 2*cos(theta) + 3 cy <- 2*sin(theta) keep <- (x - 3)^2 + y^2 > 4 dxy2 <- tripack::tri.mesh(x[keep], y[keep]) dxy2 <- tripack::add.constraint(dxy2, cx, cy) z <- dxy2$x^2 - dxy2$y^2 col <- terrain.colors(20)[1 + round(19*(z - min(z))/diff(range(z)))] open3d() persp3d(dxy2, z, col = col) } options(save)
planes3d
adds mathematical planes
to a scene. Their intersection with the current
bounding box will be drawn. clipplanes3d
adds clipping planes to a scene.
planes3d(a, b = NULL, c = NULL, d = 0, ...) clipplanes3d(a, b = NULL, c = NULL, d = 0)
planes3d(a, b = NULL, c = NULL, d = 0, ...) clipplanes3d(a, b = NULL, c = NULL, d = 0)
a , b , c
|
Coordinates of the normal to the plane. Any reasonable way of defining the
coordinates is acceptable. See the function |
d |
Coordinates of the "offset". See the details. |
... |
Material properties. See |
planes3d
draws planes using the parametrization
. Multiple planes may be specified
by giving multiple values for any of
a, b, c, d
; the other
values will be recycled as necessary.
clipplanes3d
defines clipping planes
using the same equations. Clipping planes suppress the display of
other objects (or parts of them) in the subscene, based on their coordinates.
Points (or parts of lines or surfaces) where the coordinates x, y, z
satisfy will be suppressed.
The number of clipping planes
supported by the OpenGL driver is implementation dependent; use
par3d("maxClipPlanes")
to find the limit.
A shape ID of the planes or clipplanes object is returned invisibly.
abclines3d
for mathematical lines.
triangles3d
or the corresponding
functions for quadrilaterals may be used to draw sections of planes that
do not adapt to the bounding box.
The example in subscene3d shows how to combine clipping planes to suppress complex shapes.
# Show regression plane with z as dependent variable open3d() x <- rnorm(100) y <- rnorm(100) z <- 0.2*x - 0.3*y + rnorm(100, sd = 0.3) fit <- lm(z ~ x + y) plot3d(x, y, z, type = "s", col = "red", size = 1) coefs <- coef(fit) a <- coefs["x"] b <- coefs["y"] c <- -1 d <- coefs["(Intercept)"] planes3d(a, b, c, d, alpha = 0.5) open3d() ids <- plot3d(x, y, z, type = "s", col = "red", size = 1, forceClipregion = TRUE) oldid <- useSubscene3d(ids["clipregion"]) clipplanes3d(a, b, c, d) useSubscene3d(oldid)
# Show regression plane with z as dependent variable open3d() x <- rnorm(100) y <- rnorm(100) z <- 0.2*x - 0.3*y + rnorm(100, sd = 0.3) fit <- lm(z ~ x + y) plot3d(x, y, z, type = "s", col = "red", size = 1) coefs <- coef(fit) a <- coefs["x"] b <- coefs["y"] c <- -1 d <- coefs["(Intercept)"] planes3d(a, b, c, d, alpha = 0.5) open3d() ids <- plot3d(x, y, z, type = "s", col = "red", size = 1, forceClipregion = TRUE) oldid <- useSubscene3d(ids["clipregion"]) clipplanes3d(a, b, c, d) useSubscene3d(oldid)
play3d
calls a function repeatedly, passing it the elapsed
time in seconds, and using the result of the function to
reset the viewpoint. movie3d
does the same, but
records each frame to a file to make a movie.
play3d(f, duration = Inf, dev = cur3d(), ..., startTime = 0) movie3d(f, duration, dev = cur3d(), ..., fps = 10, movie = "movie", frames = movie, dir = tempdir(), convert = NULL, clean = TRUE, verbose = TRUE, top = !rgl.useNULL(), type = "gif", startTime = 0, webshot = TRUE)
play3d(f, duration = Inf, dev = cur3d(), ..., startTime = 0) movie3d(f, duration, dev = cur3d(), ..., fps = 10, movie = "movie", frames = movie, dir = tempdir(), convert = NULL, clean = TRUE, verbose = TRUE, top = !rgl.useNULL(), type = "gif", startTime = 0, webshot = TRUE)
f |
A function returning a list that may be passed to |
duration |
The duration of the animation |
dev |
Which RGL device to select |
... |
Additional parameters to pass to |
startTime |
Initial time at which to start the animation |
fps |
Number of frames per second |
movie |
The base of the output filename, not including .gif |
frames |
The base of the name for each frame |
dir |
A directory in which to create temporary files for each frame of the movie |
convert |
How to convert to a GIF movie; see Details |
clean |
If |
verbose |
Whether to report the |
top |
Whether to call |
type |
What type of movie to create. See Details. |
webshot |
Whether to use the webshot2 package
for snapshots of frames. See |
The function f
will be called in a loop with the first argument
being the startTime
plus the time in seconds since the start
(where the start is measured after all arguments have been evaluated).
play3d
is likely to place a high load
on the CPU; if this is a problem, calls to Sys.sleep
should be made within the function to release time to other processes.
play3d
will run for the specified duration
(in seconds), but
can be interrupted by pressing ESC
while the RGL window has the focus.
movie3d
saves each frame to disk in a filename of the form ‘framesXXX.png’, where
XXX is the frame number, starting from 0.
If convert
is NULL
(the default) and the
magick package is installed, it will be used
to convert the frames to a GIF movie (or other format if supported). If
magick is not installed or
convert
is TRUE
, movie3d
will attempt
to use the external ImageMagick
program to convert the
frames to a movie.
The newer magick
executable is tried
first, then convert
if that fails.
The type
argument will be passed to ImageMagick
to use as a file extension
to choose the file type.
Finally, convert
can be a template for a command to
execute in the standard shell (wildcards are allowed). The template is
converted to a command using sprintf(convert, fps, frames, movie, type, duration, dir)
For example, convert = TRUE
uses the template "magick -delay 1x%d %s*.png %s.%s"
. All work
is done in the directory dir
, so paths should not be needed in
the command. (Note that sprintf
does not require all
arguments to be used, and supports formats that use them in an
arbitrary order.)
The top = TRUE
default is designed to work around an OpenGL limitation:
in some implementations, rgl.snapshot
will fail if the window
is not topmost.
As of rgl version 0.94, the dev
argument is not needed: the
function f
can specify its device, as spin3d
does,
for example. However, if dev
is specified, it will be selected
as the current device as each update is played.
As of rgl version 0.95.1476, f
can
include multiple values in a "subscene"
component, and par3d()
will be called
for each of them.
play3d
is called for the side effect of its repeated calls to f
.
It returns NULL
invisibly.
movie3d
is also normally called for the side effect
of producing the output movie. It invisibly returns
Duncan Murdoch, based on code by Michael Friendly
spin3d
and par3dinterp
return functions suitable
to use as f
. See demo(flag)
for an example that modifies the
scene in f
.
open3d() plot3d( cube3d(col = "green") ) M <- par3d("userMatrix") if (!rgl.useNULL() && interactive()) play3d( par3dinterp(times = (0:2)*0.5, userMatrix = list(M, rotate3d(M, pi/2, 1, 0, 0), rotate3d(M, pi/2, 0, 1, 0) ) ), duration = 2 ) ## Not run: movie3d( spin3d(), duration = 5 ) ## End(Not run)
open3d() plot3d( cube3d(col = "green") ) M <- par3d("userMatrix") if (!rgl.useNULL() && interactive()) play3d( par3dinterp(times = (0:2)*0.5, userMatrix = list(M, rotate3d(M, pi/2, 1, 0, 0), rotate3d(M, pi/2, 0, 1, 0) ) ), duration = 2 ) ## Not run: movie3d( spin3d(), duration = 5 ) ## End(Not run)
This is a widget that can be put in a web page to allow animations with or without Shiny.
playwidget(sceneId, controls, start = 0, stop = Inf, interval = 0.05, rate = 1, components = c("Reverse", "Play", "Slower", "Faster", "Reset", "Slider", "Label"), loop = TRUE, step = 1, labels = NULL, precision = 3, elementId = NULL, respondTo = NULL, reinit = NULL, buttonLabels = components, pause = "Pause", height = 40, ...)
playwidget(sceneId, controls, start = 0, stop = Inf, interval = 0.05, rate = 1, components = c("Reverse", "Play", "Slower", "Faster", "Reset", "Slider", "Label"), loop = TRUE, step = 1, labels = NULL, precision = 3, elementId = NULL, respondTo = NULL, reinit = NULL, buttonLabels = components, pause = "Pause", height = 40, ...)
sceneId |
The HTML id of the RGL scene being controlled, or an object. See the Details below. |
controls |
A single |
start , stop
|
The starting and stopping values of the animation. If
|
interval |
The requested interval (in seconds) between updates. Updates may occur at longer intervals. |
rate |
The number of units of “nominal” time per real world second. |
components |
Which components should be displayed? See Details below. |
loop |
When the player reaches the end of the interval, should it loop back to the beginning? |
step |
Step size in the slider. |
labels |
Optional labels to use, corresponding to slider steps. Set to
|
precision |
If |
elementId |
The HTML id of the generated widget, containing buttons, slider, etc. |
respondTo |
The HTML ID of a Shiny input control (e.g. a |
reinit |
A vector of ids that will need re-initialization before being drawn again. |
buttonLabels , pause
|
These are the labels that will be shown on the buttons if they are displayed. |
height |
The height of the widget in pixels. In a pipe, this is a relative height. |
... |
Additional arguments to pass to
to |
The components
are buttons to control the animation,
a slider for manual control, and a label to show the current
value. They will be displayed in the order given in components
. Not all need be included.
The buttons have the following behaviour:
Reverse the direction.
Play the animation.
Decrease the playing speed.
Increase the playing speed.
Stop the animation and reset to the start value.
If respondTo
is used, no components
are shown, as it is assumed Shiny (or whatever control is being referenced) will provide the UI components.
The sceneId
component can be another playwidget
, a rglwidget
result, or a result of
htmltools::tags
or htmltools::tagList
. This allows you
to use a magrittr-style
“pipe” command to join an rglwidget
with one or more playwidget
s. If a playwidget
comes
first, sceneId
should be set to NA
. If
the rglwidget
does not come first,
previous values should be piped into its controllers
argument. Other HTML code (including other widgets)
can be used in the chain if wrapped in htmltools::tagList
.
Each control should inherit from "rglControl"
. They
can have the following components in addition to any
private ones:
labels
default labels for the slider.
param
values to include on the slider.
dependencies
additional HTML dependencies to
include, after the default rglwidgetClass
.
A widget suitable for use in an Rmarkdown-generated web page, or elsewhere.
The appearance of the controls is set by the stylesheet
in system.file("htmlwidgets/lib/rglClass/rgl.css")
.
The overall widget is of class rglPlayer
, with id
set according to elementId
.
The buttons are of HTML class rgl-button
, the
slider is of class rgl-slider
, and the label is of
class rgl-label
. Each element has an id prefixed
by the widget id, e.g. elementId-button-Reverse
,
elementId-slider
, etc. (where elementId
should be replaced by the actual id).
The reinit
parameter handles the case where
an object needs re-initialization after each change. For
example, plane objects may need this if their intersection
with the bounding box changes shape. Note that
re-initialization is generally incompatible with
the vertexControl
as it modifies values
which are set during initialization.
Duncan Murdoch
subsetControl
,
propertyControl
, ageControl
and
vertexControl
are possible controls to use.
toggleWidget
is a wrapper for
playwidget
and subsetControl
to insert a single button to toggle some elements in a display.
saveopts <- options(rgl.useNULL = TRUE) objid <- plot3d(1:10, 1:10, rnorm(10), col=c("red", "red"), type = "s")["data"] control <- ageControl(value=0, births=1:10, ages = c(-5,0,5), colors = c("green", "yellow", "red"), objids = objid) # This example uses explicit names rglwidget(elementId = "theplot", controllers = "theplayer", height = 300, width = 300) playwidget("theplot", control, start = -5, stop = 5, rate = 3, elementId = "theplayer", components = c("Play", "Slider")) # This example uses pipes, and can skip the names widget <- rglwidget(height = 300, width = 300) %>% playwidget(control, start = -5, stop = 5, rate = 3, components = c("Play", "Slider")) if (interactive() || in_pkgdown_example()) widget options(saveopts)
saveopts <- options(rgl.useNULL = TRUE) objid <- plot3d(1:10, 1:10, rnorm(10), col=c("red", "red"), type = "s")["data"] control <- ageControl(value=0, births=1:10, ages = c(-5,0,5), colors = c("green", "yellow", "red"), objids = objid) # This example uses explicit names rglwidget(elementId = "theplot", controllers = "theplayer", height = 300, width = 300) playwidget("theplot", control, start = -5, stop = 5, rate = 3, elementId = "theplayer", components = c("Play", "Slider")) # This example uses pipes, and can skip the names widget <- rglwidget(height = 300, width = 300) %>% playwidget(control, start = -5, stop = 5, rate = 3, components = c("Play", "Slider")) if (interactive() || in_pkgdown_example()) widget options(saveopts)
Draws a 3D scatterplot.
plot3d(x, ...) ## Default S3 method: plot3d(x, y, z, xlab, ylab, zlab, type = "p", col, size, lwd, radius, add = FALSE, aspect = !add, xlim = NULL, ylim = NULL, zlim = NULL, forceClipregion = FALSE, decorate = !add, ...) ## S3 method for class 'mesh3d' plot3d(x, xlab = "x", ylab = "y", zlab = "z", type = c("shade", "wire", "dots"), add = FALSE, aspect = !add, ...)
plot3d(x, ...) ## Default S3 method: plot3d(x, y, z, xlab, ylab, zlab, type = "p", col, size, lwd, radius, add = FALSE, aspect = !add, xlim = NULL, ylim = NULL, zlim = NULL, forceClipregion = FALSE, decorate = !add, ...) ## S3 method for class 'mesh3d' plot3d(x, xlab = "x", ylab = "y", zlab = "z", type = c("shade", "wire", "dots"), add = FALSE, aspect = !add, ...)
x , y , z
|
vectors of points to be plotted. Any reasonable way of defining the
coordinates is acceptable. See the function |
xlab , ylab , zlab
|
labels for the coordinates. |
type |
For the default method, a single character indicating the type of item to plot.
Supported types are: 'p' for points, 's' for spheres,
'l' for lines, 'h' for line segments
from |
col |
the color to be used for plotted items. |
size |
the size for plotted points. |
lwd |
the line width for plotted items. |
radius |
the radius of spheres: see Details below. |
add |
whether to add the points to an existing plot. |
aspect |
either a logical indicating whether to adjust the aspect ratio, or a new ratio. |
xlim , ylim , zlim
|
If not |
forceClipregion |
Force a clipping region to be used, whether or not limits are given. |
decorate |
Whether to add bounding axes and other decorations. |
... |
additional parameters which will be passed to |
plot3d
is a partial 3D analogue of plot.default.
Missing values in the data are skipped, as in standard graphics.
If aspect
is TRUE
, aspect ratios of c(1, 1, 1)
are passed to
aspect3d
. If FALSE
, no aspect adjustment is done. In other
cases, the value is passed to aspect3d
.
With type = "s"
, spheres are drawn centered at the specified locations.
The radius may be controlled by size
(specifying the size relative
to the plot display, with the default size = 3
giving a radius
about 1/20 of the plot region) or radius
(specifying it on the data scale
if an isometric aspect ratio is chosen, or on an average scale
if not).
plot3d
is called for the side effect of drawing the plot; a vector
of object IDs is returned.
If any of xlim
, ylim
or zlim
are specified,
they should be length two vectors giving lower and upper
clipping limits for the corresponding coordinate. NA
limits will be ignored.
If any clipping limits are given, then the data will be
plotted in a newly created subscene within the current one;
otherwise plotting will take place directly in the current
subscene. This subscene is named "clipregion"
in the results. This may affect the appearance of transparent
objects if some are drawn in the plot3d
call and others after,
as RGL will not attempt to depth-sort objects if they are
in different subscenes. It is best to draw all overlapping
transparent objects in the same subscene. See the example
in planes3d
. It will also affect the use
of clipplanes3d
; clipping planes need to be in the
same subscene as the objects being clipped.
Use forceClipregion = TRUE
to force creation of this
subscene even without specifying limits.
Duncan Murdoch
plot.default
,
open3d
, par3d
.
There are plot3d.function
and plot3d.deldir
methods for plotting surfaces.
open3d() x <- sort(rnorm(1000)) y <- rnorm(1000) z <- rnorm(1000) + atan2(x, y) plot3d(x, y, z, col = rainbow(1000))
open3d() x <- sort(rnorm(1000)) y <- rnorm(1000) z <- rnorm(1000) + atan2(x, y) plot3d(x, y, z, col = rainbow(1000))
These functions provide a simple formula-based interface
to plot3d
and persp3d
.
## S3 method for class 'formula' plot3d(x, data = NULL, xlab, ylab, zlab, ...) ## S3 method for class 'formula' persp3d(x, data = NULL, xlab, ylab, zlab, ...)
## S3 method for class 'formula' plot3d(x, data = NULL, xlab, ylab, zlab, ...) ## S3 method for class 'formula' persp3d(x, data = NULL, xlab, ylab, zlab, ...)
x |
A formula like |
data |
An optional dataframe or list in which to find the components of the formula. |
xlab , ylab , zlab
|
Optional axis labels to override the ones automatically obtained from the formula. |
... |
Additional arguments to pass to the default |
Only simple formulas (the ones handled by the xyz.coords
function) are supported: a single variable on the left hand side (which will be plotted on the Z axis),
and a sum of two variables on the right hand side (which
will be the X and Y axis variables in the plot.)
These functions are called for the side effect of drawing
the plots. The plot3d
method draws a scatterplot. The persp3d
method draws a surface plot.
Return values are as given by the plot3d.default
method or the persp3d.deldir
methods.
The persp3d
method requires that the suggested package deldir is installed.
Duncan Murdoch
open3d() mfrow3d(1, 2, sharedMouse = TRUE) plot3d(mpg ~ wt + qsec, data = mtcars) if (checkDeldir()) persp3d(mpg ~ wt + qsec, data = mtcars)
open3d() mfrow3d(1, 2, sharedMouse = TRUE) plot3d(mpg ~ wt + qsec, data = mtcars) if (checkDeldir()) persp3d(mpg ~ wt + qsec, data = mtcars)
This function provides several plots of the result of fitting a two-predictor model.
## S3 method for class 'lm' plot3d(x, which = 1, plane.col = "gray", plane.alpha = 0.5, sharedMouse = TRUE, use_surface3d, do_grid = TRUE, grid.col = "black", grid.alpha = 1, grid.steps = 5, sub.steps = 4, vars = get_all_vars(terms(x), x$model), clip_to_density = 0, ...)
## S3 method for class 'lm' plot3d(x, which = 1, plane.col = "gray", plane.alpha = 0.5, sharedMouse = TRUE, use_surface3d, do_grid = TRUE, grid.col = "black", grid.alpha = 1, grid.steps = 5, sub.steps = 4, vars = get_all_vars(terms(x), x$model), clip_to_density = 0, ...)
x |
An object inheriting from class |
which |
Which plot to show? See Details below. |
plane.col , plane.alpha
|
These parameters control the colour and transparency of a plane or surface. |
sharedMouse |
If multiple plots are requested, should they share mouse controls, so that they move in sync? |
use_surface3d |
Use the |
do_grid |
Plot a grid. |
grid.col , grid.alpha , grid.steps
|
Characteristics of the grid. |
sub.steps |
If |
vars |
A dataframe containing the variables to plot in the first three columns, with the response assumed to be in column 1. See the Note below. |
clip_to_density |
If positive, the surface, plane or grid will be clipped to a region with sufficient data. |
... |
Other parameters to pass to the default |
Three plots are possible, depending on the value(s) in which
:
(default) Show the points and the fitted plane or surface.
Show the residuals and the plane at z = 0
.
Show the predicted values on the fitted plane or surface.
If clip_to_density
is positive, then the
surface, plane or grid will be clipped to
the region where a non-parametric density estimate
(using MASS::kde2d
), normalized to have a
maximum value of 1, is greater than the given value.
This will suppress parts of the plot that aren't
supported by the observed data.
Called for the side effect of drawing one or more plots.
Invisibly returns a high-level vector of object ids. Names of object ids have the plot number (in drawing order) appended.
The default value for the vars
argument will handle
simple linear models with a response and two predictors, and
some models with functions of those two predictors. For
models that fail (e.g. models using poly
), you
can include the observed values as in the third example below.
If clip_to_density > 0
,
The clipping is approximate, so it may not agree perfectly between surfaces, planes and grids.
This option requires the suggested packages MASS and interp, and will be ignored with a warning if either is not installed.
Duncan Murdoch
open3d() ids <- plot3d(lm(mpg ~ wt + qsec, data = mtcars), which = 1:3) names(ids) open3d() plot3d(lm(mpg ~ wt + I(wt^2) + qsec, data = mtcars)) open3d() # Specify vars in the order: response, pred1, pred2. plot3d(lm(mpg ~ poly(wt, 3) + qsec, data = mtcars), vars = mtcars[,c("mpg", "wt", "qsec")]) open3d() # Clip parts of the plot with few (wt, qsec) points plot3d(lm(mpg ~ poly(wt, 3) + qsec, data = mtcars), vars = mtcars[,c("mpg", "wt", "qsec")], clip_to_density = 0.1)
open3d() ids <- plot3d(lm(mpg ~ wt + qsec, data = mtcars), which = 1:3) names(ids) open3d() plot3d(lm(mpg ~ wt + I(wt^2) + qsec, data = mtcars)) open3d() # Specify vars in the order: response, pred1, pred2. plot3d(lm(mpg ~ poly(wt, 3) + qsec, data = mtcars), vars = mtcars[,c("mpg", "wt", "qsec")]) open3d() # Clip parts of the plot with few (wt, qsec) points plot3d(lm(mpg ~ poly(wt, 3) + qsec, data = mtcars), vars = mtcars[,c("mpg", "wt", "qsec")], clip_to_density = 0.1)
To plot mathematical text, this function uses base graphics functions to plot it to a ‘.png’ file, then uses that file as a texture in a sprite.
plotmath3d(x, y = NULL, z = NULL, text, cex = par("cex"), adj = 0.5, pos = NULL, offset = 0.5, fixedSize = TRUE, startsize = 480, initCex = 5, margin = "", floating = FALSE, tag = "", ...)
plotmath3d(x, y = NULL, z = NULL, text, cex = par("cex"), adj = 0.5, pos = NULL, offset = 0.5, fixedSize = TRUE, startsize = 480, initCex = 5, margin = "", floating = FALSE, tag = "", ...)
x , y , z
|
coordinates. Any reasonable way of defining the
coordinates is acceptable. See the function |
text |
A character vector or expression. See
|
cex |
Character size expansion. |
adj |
one value specifying the horizontal adjustment, or two, specifying horizontal and vertical adjustment respectively, or three, for depth as well. |
pos , offset
|
alternate way to specify |
fixedSize |
Should the resulting sprite behave like the default ones, and resize with the scene, or like text, and stay at a fixed size? |
startsize , initCex
|
These parameters are unlikely to be needed by users.
|
margin , floating , tag
|
|
... |
Additional arguments to pass to
|
Called for the side effect of displaying the sprites. The shape ID of the displayed object is returned.
The text3d
function passes calls to this
function if its usePlotmath
argument is TRUE
.
This is the default value if its
texts
argument looks like an expression.
Duncan Murdoch
open3d() plotmath3d(1:3, 1:3, 1:3, expression(x[1] == 1, x[2] == 2, x[3] == 3)) # This lets the text resize with the plot text3d(4, 4, 4, "resizeable text", usePlotmath = TRUE, fixedSize = FALSE)
open3d() plotmath3d(1:3, 1:3, 1:3, expression(x[1] == 1, x[2] == 2, x[3] == 3)) # This lets the text resize with the plot text3d(4, 4, 4, "resizeable text", usePlotmath = TRUE, fixedSize = FALSE)
This function takes a description of a flat polygon in x, y and z coordinates, and draws it in three dimensions.
polygon3d(x, y = NULL, z = NULL, fill = TRUE, plot = TRUE, coords, random = TRUE, ...)
polygon3d(x, y = NULL, z = NULL, fill = TRUE, plot = TRUE, coords, random = TRUE, ...)
x , y , z
|
Vertices of the polygon in a form accepted by |
fill |
logical; should the polygon be filled? |
plot |
logical; should the polygon be displayed? |
coords |
Which two coordinates ( |
random |
Should a random triangulation be used? |
... |
Other parameters to pass to |
The function triangulates the two dimensional polygon described by coords
, then
applies the triangulation to all three coordinates. No check is made that the polygon
is actually all in one plane, but the results may be somewhat unpredictable
(especially if random = TRUE
) if it is not.
Polygons need not be simple; use NA
to indicate separate closed pieces. For
fill = FALSE
there are no other restrictions on the pieces, but for
fill = TRUE
the resulting two-dimensional polygon needs to be one that
triangulate
can handle.
If plot = TRUE
, the id number of the lines (for fill = FALSE
) or triangles
(for fill = TRUE
) that have been plotted.
If plot = FALSE
, then for fill = FALSE
, a vector of indices into
the XYZ matrix that could be used to draw the polygon. For fill = TRUE
,
a triangular mesh object representing the triangulation.
Duncan Murdoch
extrude3d
for a solid extrusion of a polygon, triangulate
for
the triangulation.
theta <- seq(0, 4*pi, length.out = 50) r <- theta + 1 r <- c(r[-50], rev(theta*0.8) + 1) theta <- c(theta[-50], rev(theta)) x <- r*cos(theta) y <- r*sin(theta) open3d() plot(x, y, type = "n") polygon(x, y) polygon3d(x, y, x + y, col = "blue")
theta <- seq(0, 4*pi, length.out = 50) r <- theta + 1 r <- c(r[-50], rev(theta*0.8) + 1) theta <- c(theta[-50], rev(theta)) x <- r*cos(theta) y <- r*sin(theta) open3d() plot(x, y, type = "n") polygon(x, y) polygon3d(x, y, x + y, col = "blue")
Adds a shape node to the current scene.
points3d(x, y = NULL, z = NULL, ...) lines3d(x, y = NULL, z = NULL, ...) segments3d(x, y = NULL, z = NULL, ...) triangles3d(x, y = NULL, z = NULL, ...) quads3d(x, y = NULL, z = NULL, ...)
points3d(x, y = NULL, z = NULL, ...) lines3d(x, y = NULL, z = NULL, ...) segments3d(x, y = NULL, z = NULL, ...) triangles3d(x, y = NULL, z = NULL, ...) quads3d(x, y = NULL, z = NULL, ...)
x , y , z
|
coordinates. Any reasonable way of defining the
coordinates is acceptable. See the function |
... |
Material properties (see |
The functions points3d
, lines3d
, segments3d
,
triangles3d
and quads3d
add points, joined lines, line segments,
filled triangles or quadrilaterals to the plots. They correspond to the OpenGL types
GL_POINTS, GL_LINE_STRIP, GL_LINES, GL_TRIANGLES
and GL_QUADS
respectively.
Points are taken in pairs by segments3d
, triplets as the vertices
of the triangles, and quadruplets for the quadrilaterals. Colors are applied vertex by vertex;
if different at each end of a line segment, or each vertex of a polygon, the colors
are blended over the extent of the object. Polygons
must be non-degenerate and quadrilaterals must be entirely
in one plane and convex, or the results are undefined.
The appearance of the new objects are defined by the material properties.
See material3d
for details.
For triangles and quads, the normals at each vertex may be specified
using normals
. These may be given in any way that would be
acceptable as a single argument to xyz.coords
.
These need not match the actual normals to the polygon:
curved surfaces can be simulated by using other choices of normals.
Texture coordinates may also be specified. These may be given in
any way that would be acceptable as a single argument to
xy.coords
, and are interpreted in terms
of the bitmap specified as the material texture, with (0, 0)
at the lower left, (1, 1)
at the upper right. The texture
is used to modulate the color of the polygon.
All of these functions support an argument called
indices
, which allows vertices (and other attributes)
to be re-used, as they are in objects created by mesh3d
and related functions. This is
intended to be used on smooth surfaces, where each shared
vertex has just one value for normals, colors and texture
coordinates.
For shapes with flat-looking faces (e.g. polyhedra like cube3d
),
the vertices must be duplicated
to be rendered properly.
Each function returns the integer object ID of the shape that
was added to the scene. These can be passed to pop3d
to remove the object from the scene.
Ming Chen and Duncan Murdoch
# Show 12 random vertices in various ways. M <- matrix(rnorm(36), 3, 12, dimnames = list(c('x', 'y', 'z'), rep(LETTERS[1:4], 3))) # Force 4-tuples to be convex in planes so that quads3d works. for (i in c(1, 5, 9)) { quad <- as.data.frame(M[, i + 0:3]) coeffs <- runif(2, 0, 3) if (mean(coeffs) < 1) coeffs <- coeffs + 1 - mean(coeffs) quad$C <- with(quad, coeffs[1]*(B - A) + coeffs[2]*(D - A) + A) M[, i + 0:3] <- as.matrix(quad) } open3d() # Rows of M are x, y, z coords; transpose to plot M <- t(M) shift <- matrix(c(-3, 3, 0), 12, 3, byrow = TRUE) points3d(M) lines3d(M + shift) segments3d(M + 2*shift) triangles3d(M + 3*shift, col = 'red') quads3d(M + 4*shift, col = 'green') text3d(M + 5*shift, texts = 1:12) # Add labels shift <- outer(0:5, shift[1, ]) shift[, 1] <- shift[, 1] + 3 text3d(shift, texts = c('points3d', 'lines3d', 'segments3d', 'triangles3d', 'quads3d', 'text3d'), adj = 0) rgl.bringtotop()
# Show 12 random vertices in various ways. M <- matrix(rnorm(36), 3, 12, dimnames = list(c('x', 'y', 'z'), rep(LETTERS[1:4], 3))) # Force 4-tuples to be convex in planes so that quads3d works. for (i in c(1, 5, 9)) { quad <- as.data.frame(M[, i + 0:3]) coeffs <- runif(2, 0, 3) if (mean(coeffs) < 1) coeffs <- coeffs + 1 - mean(coeffs) quad$C <- with(quad, coeffs[1]*(B - A) + coeffs[2]*(D - A) + A) M[, i + 0:3] <- as.matrix(quad) } open3d() # Rows of M are x, y, z coords; transpose to plot M <- t(M) shift <- matrix(c(-3, 3, 0), 12, 3, byrow = TRUE) points3d(M) lines3d(M + shift) segments3d(M + 2*shift) triangles3d(M + 3*shift, col = 'red') quads3d(M + 4*shift, col = 'green') text3d(M + 5*shift, texts = 1:12) # Add labels shift <- outer(0:5, shift[1, ]) shift[, 1] <- shift[, 1] + 3 text3d(shift, texts = c('points3d', 'lines3d', 'segments3d', 'triangles3d', 'quads3d', 'text3d'), adj = 0) rgl.bringtotop()
These are setter functions to produce actions in a Shiny app, or in an animation.
subsetControl(value = 1, subsets, subscenes = NULL, fullset = Reduce(union, subsets), accumulate = FALSE) propertyControl(value = 0, entries, properties, objids = tagged3d(tags), tags, values = NULL, param = seq_len(NROW(values)) - 1, interp = TRUE)
subsetControl(value = 1, subsets, subscenes = NULL, fullset = Reduce(union, subsets), accumulate = FALSE) propertyControl(value = 0, entries, properties, objids = tagged3d(tags), tags, values = NULL, param = seq_len(NROW(values)) - 1, interp = TRUE)
value |
The value to use for input (typically |
subsets |
A list of vectors of object identifiers; the value will choose among them. |
fullset |
Objects in the subscene which are not in |
subscenes |
The subscenes to be controlled. If |
accumulate |
If |
entries , properties , objids
|
Which properties to set. |
tags |
Select objects with matching tags. Ignored
if |
values |
Values to set. |
param |
Parameter values corresponding to the rows of |
interp |
Whether to use linear interpolation between |
subsetControl
produces data for playwidget
to
display subsets of the object in one or more subscenes. This code will not
touch objects in the
subscenes if they
are not in fullset
. fullset
defaults to the union of
all the object ids mentioned in subsets
, so by default if an id is
not mentioned in one of the subsets, it will not be controlled by the slider. If value
is specified in R code, it will be a 1-based
index into the subsets
list; when specified internally
in Javascript, 0-based indexing into the corresponding array
will be used.
propertyControl
sets individual properties. Here the row
of values
is determined by the position of value
in param
.
These functions return controller data in a list of class
"rglControl"
.
Duncan Murdoch
subsetSetter
for a way to embed a pure Javascript
control, and playwidget
for a way to use these
in animations (including Shiny), rglShared
for linking using the crosstalk package.
Generic 3D interface for 3D rendering and computational geometry.
R3d is a design for an interface for 3d rendering and computation without dependency
on a specific rendering implementation. R3d includes a collection
of 3D objects and geometry algorithms.
All r3d interface functions are named *3d
. They represent generic functions that delegate
to implementation functions.
The interface can be grouped into 8 categories: Scene Management, Primitive Shapes, High-level Shapes, Geometry Objects, Visualization, Interaction, Transformation, Subdivision.
The rendering interface gives an abstraction to the underlying rendering model. It can be grouped into four categories:
A 3D scene consists of shapes, lights and background environment.
Generic primitive 3D graphics shapes such as points, lines, triangles, quadrangles and texts.
Generic high-level 3D graphics shapes such as spheres, sprites and terrain.
Generic interface to select points in 3D space using the pointer device.
In this package we include an implementation of r3d using the underlying rgl.*
functions.
3D computation is supported through the use of object structures that live entirely in R.
Geometry and mesh objects allow to define high-level geometry for computational purpose such as triangle or
quadrangle meshes (see mesh3d
).
Generic interface to transform 3d objects.
Generic rendering of 3d objects such as dotted, wired or shaded.
Generic subdivision of 3d objects.
At present, the main practical differences between the r3d functions
and the rgl.*
functions are as follows.
The r3d functions call
open3d
if there is no device open, and the
rgl.*
functions call rgl.open
. By default
open3d
sets the initial orientation of the coordinate
system in 'world coordinates', i.e. a right-handed coordinate system
in which the x-axis increases from left to right, the y-axis
increases with depth into the scene, and the z-axis increases from
bottom to top of the screen. rgl.*
functions, on the other
hand, use a right-handed coordinate system similar to that used in
OpenGL. The x-axis matches that of r3d, but the y-axis increases
from bottom to top, and the z-axis decreases with depth into the
scene. Since the user can manipulate the scene, either system can
be rotated into the other one.
The r3d functions also preserve the rgl.material
setting across
calls (except for texture elements, in the current implementation), whereas
the deprecated rgl.*
functions leave it as set by the last call.
The example code below illustrates the two coordinate systems.
points3d
,
lines3d
,
segments3d
,
triangles3d
,
quads3d
,
text3d
,
spheres3d
,
sprites3d
,
terrain3d
,
select3d
,
dot3d
,
wire3d
,
shade3d
,
transform3d
,
rotate3d
,
subdivision3d
,
mesh3d
,
cube3d
,
rgl
x <- c(0, 1, 0, 0) y <- c(0, 0, 1, 0) z <- c(0, 0, 0, 1) labels <- c("Origin", "X", "Y", "Z") i <- c(1, 2, 1, 3, 1, 4) # *3d interface open3d() text3d(x, y, z, labels) text3d(1, 1, 1, "*3d coordinates") segments3d(x[i], y[i], z[i])
x <- c(0, 1, 0, 0) y <- c(0, 0, 1, 0) z <- c(0, 0, 0, 1) labels <- c("Origin", "X", "Y", "Z") i <- c(1, 2, 1, 3, 1, 4) # *3d interface open3d() text3d(x, y, z, labels) text3d(1, 1, 1, "*3d coordinates") segments3d(x[i], y[i], z[i])
These functions read and write STL files. This is a simple file format that
is commonly used in 3D printing. It does not represent text,
only triangles. The writeSTL
function converts some RGL object
types to triangles.
readSTL(con, ascii = NA, plot = TRUE, ...) writeSTL(con, ascii = FALSE, pointRadius = 0.005, pointShape = icosahedron3d(), lineRadius = pointRadius, lineSides = 20, ids = tagged3d(tags), tags = NULL)
readSTL(con, ascii = NA, plot = TRUE, ...) writeSTL(con, ascii = FALSE, pointRadius = 0.005, pointShape = icosahedron3d(), lineRadius = pointRadius, lineSides = 20, ids = tagged3d(tags), tags = NULL)
con |
A connection or filename. |
ascii |
Whether to use the ASCII format or the binary format. The
default |
plot |
On reading, should the object be plotted? |
... |
If plotting, other parameters to pass to |
pointRadius , lineRadius
|
The radius of points and lines relative to the overall scale of the figure. |
pointShape |
A mesh shape to use for points. It is scaled by the |
lineSides |
Lines are rendered as cylinders with this many sides. |
ids |
The identifiers (from |
tags |
Alternate way to specify |
The current implementation is limited. For reading, it ignores normals and color information. For writing, it only outputs triangles, quads, planes, spheres, points, line segments, line strips and surfaces, and does not write color information. Lines and points are rendered in an isometric scale: if your data scales vary, they will look strange.
Since the STL format only allows one object per file, all RGL objects are combined into a single object when output.
The output file is readable by Blender and Meshlab; the latter can write in a number of other formats, including U3D, suitable for import into a PDF document.
readSTL
invisibly returns the object id if plot = TRUE
, or (visibly)
a matrix of vertices of the triangles if not.
writeSTL
invisibly returns the name of the connection to which the
data was written.
Duncan Murdoch
The file format was found on Wikipedia on October 25, 2012. I learned
about the STL file format from David Smith's blog reporting on
Ian Walker's r2stl
function.
scene3d
saves a copy of a scene to an R variable;
rglwidget
, writeASY
, writePLY
, writeOBJ
and writeSTL
write the scene to a file in various other formats.
filename <- tempfile(fileext = ".stl") open3d() shade3d( icosahedron3d(col = "magenta") ) writeSTL(filename) open3d() readSTL(filename, col = "red")
filename <- tempfile(fileext = ".stl") open3d() shade3d( icosahedron3d(col = "magenta") ) writeSTL(filename) open3d() readSTL(filename, col = "red")
Retrieves information about the shapes in a scene.
rgl.attrib(id, attrib, first = 1, last = rgl.attrib.count(id, attrib))
rgl.attrib(id, attrib, first = 1, last = rgl.attrib.count(id, attrib))
id |
A shape identifier, as returned by |
attrib |
An attribute of a shape. Currently supported: one of |
first , last
|
Specify these to retrieve only those rows of the result. |
If the identifier is not found or is not a shape that has the given attribute,
zero will be returned by rgl.attrib.count
, and an empty matrix
will be returned by rgl.attrib
.
The first four attrib
names correspond to the usual OpenGL
properties; "dim"
is used just for surfaces, defining the rows
and columns in the rectangular grid; "cex"
, "adj"
,
"family"
, "font"
and "pos"
apply only to text objects.
rgl.attrib
returns the values of the attribute. Attributes
are mostly real-valued, with the following sizes:
"vertices" |
3 values | x, y, z |
"normals" |
3 values | x, y, z |
"centers" |
3 values | x, y, z |
"colors" |
4 values | r, g, b, a |
"texcoords" |
2 values | s, t |
"dim" |
2 values | r, c |
"cex" |
1 value | cex |
"adj" |
2 values | x, y |
"radii" |
1 value | r |
"ids" |
1 value | id |
"usermatrix" |
4 values | x, y, z, w |
"texts" |
1 value | text |
"types" |
1 value | type |
"flags" |
1 value | flag |
"family" |
1 value | family |
"font" |
1 value | font |
"pos" |
1 value | pos |
The "texts"
, "types"
and "family"
attributes are character-valued; the "flags"
attribute is logical valued, with named rows.
These are returned as matrices with the row count equal to the count for the attribute, and the columns as listed above.
Duncan Murdoch
p <- plot3d(rnorm(100), rnorm(100), rnorm(100), type = "s", col = "red") rgl.attrib(p["data"], "vertices", last = 10)
p <- plot3d(rnorm(100), rnorm(100), rnorm(100), type = "s", col = "red") rgl.attrib(p["data"], "vertices", last = 10)
These functions give information about the attributes of
RGL objects. rgl.attrib.info
is the more
“user-friendly” function; rgl.attrib.count
is a
lower-level function more likely to be used in programming.
rgl.attrib.info(id = ids3d("all", 0)$id, attribs = NULL, showAll = FALSE) rgl.attrib.count(id, attrib)
rgl.attrib.info(id = ids3d("all", 0)$id, attribs = NULL, showAll = FALSE) rgl.attrib.count(id, attrib)
id |
One or more RGL object ids. |
attribs |
A character vector of one or more attribute names. |
showAll |
Should attributes with zero entries be shown? |
attrib |
A single attribute name. |
See the first example below to get the full list of attribute names.
A dataframe containing the following columns:
id |
The id of the object. |
attrib |
The full name of the attribute. |
nrow , ncol
|
The size of matrix that would be returned
by |
Duncan Murdoch
rgl.attrib
to obtain the attribute values.
open3d() id <- points3d(rnorm(100), rnorm(100), rnorm(100), col = "green") rgl.attrib.info(id, showAll = TRUE) rgl.attrib.count(id, "vertices") merge(rgl.attrib.info(), ids3d("all"))
open3d() id <- points3d(rnorm(100), rnorm(100), rnorm(100), col = "green") rgl.attrib.info(id, showAll = TRUE) rgl.attrib.count(id, "vertices") merge(rgl.attrib.info(), ids3d("all"))
'rgl.bringtotop' brings the current RGL window to the front of the window stack (and gives it focus).
rgl.bringtotop(stay = FALSE)
rgl.bringtotop(stay = FALSE)
stay |
whether to make the window stay on top. |
If stay
is TRUE
, then the window will stay on top
of normal windows.
not completely implemented for X11 graphics (stay not implemented; window managers such as KDE may block this action (set "Focus stealing prevention level" to None in Control Center/Window Behavior/Advanced)). Not currently implemented under OS/X.
Ming Chen/Duncan Murdoch
open3d() points3d(rnorm(1000), rnorm(1000), rnorm(1000), color = heat.colors(1000)) rgl.bringtotop(stay = TRUE)
open3d() points3d(rnorm(1000), rnorm(1000), rnorm(1000), color = heat.colors(1000)) rgl.bringtotop(stay = TRUE)
This function gets a user-defined axis labelling callback in R.
rgl.getAxisCallback(axis, dev = cur3d(), subscene = currentSubscene3d(dev))
rgl.getAxisCallback(axis, dev = cur3d(), subscene = currentSubscene3d(dev))
axis |
Which axis? Can be value from |
dev , subscene
|
The RGL device and subscene to work with. |
The callback function.
Duncan Murdoch
setAxisCallbacks
to
work with rglwidget
.
This function is mainly for internal use. It simply increments the internal object ID number and returns the new value. Negative values have no effect.
rgl.incrementID(n = 1L)
rgl.incrementID(n = 1L)
n |
An integer increment to use. |
The resulting ID value.
# Get the current ID value rgl.incrementID(0) # Increment it rgl.incrementID()
# Get the current ID value rgl.incrementID(0) # Increment it rgl.incrementID()
Initializing the RGL system.
rgl.init(initValue = 0, onlyNULL = FALSE, debug = getOption("rgl.debug", FALSE))
rgl.init(initValue = 0, onlyNULL = FALSE, debug = getOption("rgl.debug", FALSE))
initValue |
value for internal use only |
onlyNULL |
only initialize the null (no display) device |
debug |
enable some debugging messages |
If useNULL
is TRUE
, RGL will use a “null”
device. This device records objects as they are plotted, but
displays nothing. It is intended for use with rglwidget
and similar functions.
Currently debug
only controls messages printed by the OpenGL
library during initialization. In future debug = TRUE
may
become more verbose.
For display within an OpenGL window in R, RGL requires the OpenGL
system to be installed and available. If there is a problem
initializing it, you may see the message
'rgl.init' failed, running with 'rgl.useNULL = TRUE'.
There
are several causes and remedies:
On any system, the OpenGL libraries need to be present for RGL to be able to start an OpenGL device.
On macOS, you need to install XQuartz. It is available from https://www.xquartz.org.
On Linux, you need to install Mesa 3D. One of these commands
may work, depending on your system:
zypper source-install --build-deps-only Mesa # openSUSE/SLED/SLES
yum-builddep mesa # yum Fedora, OpenSuse(?)
dnf builddep mesa # dnf Fedora
apt-get build-dep mesa # Debian, Ubuntu and related
Windows should have OpenGL installed by default.
On Unix-alike systems (macOS and Linux, for example), RGL normally uses the GLX system for creating displays. If the graphic is created on a remote machine, it may need to use “Indirect GLX” (IGLX). Due to security concerns, this is often disabled by default. See https://www.x.org/wiki/Development/Security/Advisory-2014-12-09/ for a discussion of the security issues, and https://unix.stackexchange.com/q/317954 for ways to re-enable IGLX.
The https://www.virtualgl.org project is intended to be a way to avoid IGLX, by rendering remotely and sending bitmaps to the local machine. It's not a simple install...
If you don't need to see RGL displays on screen, you
can use the “NULL device”. See rgl.useNULL
.
If you can't build the rgl package with OpenGL support, you can disable it and use the NULL device. (This may happen automatically during configuration, but you'll get a tested result if you specify it explicitly.) See the instructions in the ‘README’ file in the source tarball.
Normally the user doesn't call rgl.init
at all: it
is called when the package is loaded. It returns no
useful value.
This function extracts single components of the pixel information from the topmost window.
rgl.pixels(component = c("red", "green", "blue"), viewport = par3d("viewport"), top = TRUE)
rgl.pixels(component = c("red", "green", "blue"), viewport = par3d("viewport"), top = TRUE)
component |
Which component(s)? |
viewport |
Lower left corner and size of desired region. |
top |
Whether to bring window to top before reading. |
The possible components are "red"
, "green"
, "blue"
,
"alpha"
, "depth"
, and "luminance"
(the sum of the three
colors). All are scaled from 0 to 1.
Note that the luminance is kept below 1 by truncating the sum; this is the
definition used for the GL_LUMINANCE
component in OpenGL.
A vector, matrix or array containing the desired components. If one component is requested, a vector or matrix will be returned depending on the size of block requested (length 1 dimensions are dropped); if more, an array, whose last dimension is the list of components.
Duncan Murdoch
rgl.snapshot
to write a copy to a file,
demo("stereo")
for functions that make use of this to draw
a random dot stereogram and an anaglyph.
example(surface3d) depth <- rgl.pixels(component = "depth") if (length(depth) && is.matrix(depth)) # Protect against empty or single pixel windows contour(depth)
example(surface3d) depth <- rgl.pixels(component = "depth") if (length(depth) && is.matrix(depth)) # Protect against empty or single pixel windows contour(depth)
Saves the screenshot to a file in PostScript or other vector graphics format.
rgl.postscript( filename, fmt = "eps", drawText = TRUE )
rgl.postscript( filename, fmt = "eps", drawText = TRUE )
filename |
full path to filename. |
fmt |
export format, currently supported: ps, eps, tex, pdf, svg, pgf |
drawText |
logical, whether to draw text |
Animations can be created in a loop modifying the scene and saving a screenshot to a file. (See example below)
This function is a wrapper for the GL2PS library by Christophe Geuzaine, and has the same limitations as that library: not all OpenGL features are supported, and some are only supported in some formats. See the reference for full details.
Christophe Geuzaine / Albrecht Gebhardt
GL2PS: an OpenGL to PostScript printing library by Christophe Geuzaine, https://www.geuz.org/gl2ps/, version 1.4.2.
# Create new files in tempdir savedir <- setwd(tempdir()) x <- y <- seq(-10, 10, length.out = 20) z <- outer(x, y, function(x, y) x^2 + y^2) persp3d(x, y, z, col = 'lightblue') title3d("Using LaTeX text", col = 'red', line = 3) rgl.postscript("persp3da.ps", "ps", drawText = FALSE) rgl.postscript("persp3da.pdf", "pdf", drawText = FALSE) rgl.postscript("persp3da.tex", "tex") pop3d() title3d("Using ps/pdf text", col = 'red', line = 3) rgl.postscript("persp3db.ps", "ps") rgl.postscript("persp3db.pdf", "pdf") rgl.postscript("persp3db.tex", "tex", drawText = FALSE) setwd(savedir) ## Not run: # # create a series of frames for an animation # open3d() shade3d(oh3d(), color = "red") view3d(0, 20) for (i in 1:45) { view3d(i, 20) filename <- paste("pic", formatC(i, digits = 1, flag = "0"), ".eps", sep = "") rgl.postscript(filename, fmt = "eps") } ## End(Not run)
# Create new files in tempdir savedir <- setwd(tempdir()) x <- y <- seq(-10, 10, length.out = 20) z <- outer(x, y, function(x, y) x^2 + y^2) persp3d(x, y, z, col = 'lightblue') title3d("Using LaTeX text", col = 'red', line = 3) rgl.postscript("persp3da.ps", "ps", drawText = FALSE) rgl.postscript("persp3da.pdf", "pdf", drawText = FALSE) rgl.postscript("persp3da.tex", "tex") pop3d() title3d("Using ps/pdf text", col = 'red', line = 3) rgl.postscript("persp3db.ps", "ps") rgl.postscript("persp3db.pdf", "pdf") rgl.postscript("persp3db.tex", "tex", drawText = FALSE) setwd(savedir) ## Not run: # # create a series of frames for an animation # open3d() shade3d(oh3d(), color = "red") view3d(0, 20) for (i in 1:45) { view3d(i, 20) filename <- paste("pic", formatC(i, digits = 1, flag = "0"), ".eps", sep = "") rgl.postscript(filename, fmt = "eps") } ## End(Not run)
Mostly for internal use, this function temporarily installs a handler on a button of the mouse that will return the mouse coordinates of one click and drag rectangle.
rgl.select(button = c("left", "middle", "right"), dev = cur3d(), subscene = currentSubscene3d(dev))
rgl.select(button = c("left", "middle", "right"), dev = cur3d(), subscene = currentSubscene3d(dev))
button |
Which button to use? |
dev , subscene
|
The RGL device and subscene to work with |
A vector of four coordinates: the X and Y coordinates of the start and end of the dragged rectangle.
Duncan Murdoch
select3d
, a version that allows the selection region to be used to
select points in the scene.
As of R 2.13.0, it is possible to include RGL graphics into a Sweave document. These functions support that integration.
Sweave.snapshot() rgl.Sweave(name, width, height, options, ...) rgl.Sweave.off()
Sweave.snapshot() rgl.Sweave(name, width, height, options, ...) rgl.Sweave.off()
name , width , height , options , ...
|
These arguments are passed by |
The rgl.Sweave
function is not normally called by the user. The user
specifies it as the graphics driver when opening the code chunk, e.g. by
using
<<fig = TRUE, pdf = FALSE, grdevice = rgl.Sweave, resolution = 100>>=
When the RGL device is closed at the end of the code chunk,
rgl.Sweave.off()
will be called automatically. It
will save a snapshot
of the last image (by default in ‘.png’ format) for inclusion in the
Sweave document and (by default) close the device.
Alternatively, the Sweave.snapshot()
function
can be called to save the image before the end of the chunk. Only one
snapshot will be taken per chunk.
Several chunk options are used by the rgl.Sweave
device:
(default FALSE
). If TRUE
then the RGL
device will not be closed at the end of the chunk,
instead a call to Sweave.snapshot()
will be used if it has not been called explicitly. Subsequent chunks can add
to the scene.
(default png
). The output may be specified
as outputtype = pdf
or outputtype = eps
instead, in which case
the rgl.postscript
function will be used to write output in the
specified format. Note that rgl.postscript
has limitations
and does not always render scenes correctly.
(default 0.1). After creating the display window, Sys.sleep
will be called to delay this many seconds, to allow the display system to
initialize. This is needed in X11 systems which open the display
asynchronously. If the default time is too short, rgl.Sweave
may
falsely report that the window is too large to open.
These functions are called for their side effects.
We recommend turning off all other graphics drivers in a chunk that uses
grdevice = rgl.Sweave
. The RGL functions do not write to
a standard graphics device.
The rgl package relies on your graphics hardware to render OpenGL scenes,
and the default ‘.png’ output copies a bitmap from the hardware device. All
such devices have limitations on the size of the bitmap, but they do not
always signal these limitations in a way that RGL will detect. If you find
that images are not being produced properly, try reducing the size using
the resolution
, width
or height
chunk options.
Duncan Murdoch
RweaveLatex
for a description of alternate graphics drivers
in Sweave, and standard options that can be used in code chunks.
hook_rgl
and hook_webgl
allow fixed or interactive RGL
scenes to be embedded in knitr
documents.
This function checks the "rgl.useNULL"
option if present, or the
RGL_USE_NULL environment variable if it is not. If the value is
TRUE
or a string which matches “yes” or “true”
in a case-insensitive test, TRUE
is returned.
rgl.useNULL()
rgl.useNULL()
A logical value indicating the current default for use of the null device.
This function is checked by the initialization code when the rgl package
is loaded. Thus if you want to run RGL on a system where there is no
graphics support, you should run options(rgl.useNULL = TRUE)
or set the environment variable RGL_USE_NULL=TRUE
*before*
calling library(rgl)
(or other code that
loads rgl), and it will not fail in its attempt at initialization.
Duncan Murdoch
rgl.useNULL()
rgl.useNULL()
This function converts from 3-dimensional user coordinates to 3-dimensional window coordinates.
rgl.user2window(x, y = NULL, z = NULL, projection = rgl.projection()) rgl.window2user(x, y = NULL, z = 0, projection = rgl.projection()) rgl.projection(dev = cur3d(), subscene = currentSubscene3d(dev))
rgl.user2window(x, y = NULL, z = NULL, projection = rgl.projection()) rgl.window2user(x, y = NULL, z = 0, projection = rgl.projection()) rgl.projection(dev = cur3d(), subscene = currentSubscene3d(dev))
x , y , z
|
Input coordinates. Any reasonable way of defining the
coordinates is acceptable. See the function |
projection |
The RGL projection to use |
dev , subscene
|
The RGL device and subscene to work with |
These functions convert between user coordinates and window coordinates.
Window coordinates run from 0 to 1 in X, Y, and Z. X runs from 0 on the left to 1 on the right; Y runs from 0 at the bottom to 1 at the top; Z runs from 0 foremost to 1 in the background. RGL does not currently display vertices plotted outside of this range, but in normal circumstances will automatically resize the display to show them. In the example below this has been suppressed.
The coordinate conversion functions produce a matrix with columns corresponding to the X, Y, and Z coordinates.
rgl.projection()
returns a list containing the following components:
model |
the modelview matrix |
projection |
the projection matrix |
viewport |
the viewport vector |
See par3d
for more details.
Ming Chen / Duncan Murdoch
open3d() points3d(rnorm(100), rnorm(100), rnorm(100)) if (interactive() || !.Platform$OS == "unix") { # Calculate a square in the middle of the display and plot it square <- rgl.window2user(c(0.25, 0.25, 0.75, 0.75, 0.25), c(0.25, 0.75, 0.75, 0.25, 0.25), 0.5) par3d(ignoreExtent = TRUE) lines3d(square) par3d(ignoreExtent = FALSE) }
open3d() points3d(rnorm(100), rnorm(100), rnorm(100)) if (interactive() || !.Platform$OS == "unix") { # Calculate a square in the middle of the display and plot it square <- rgl.window2user(c(0.25, 0.25, 0.75, 0.75, 0.25), c(0.25, 0.75, 0.75, 0.25, 0.25), 0.5) par3d(ignoreExtent = TRUE) lines3d(square) par3d(ignoreExtent = FALSE) }
This function uses the extrafont package to help register system fonts for use with FreeType in rgl.
rglExtrafonts(..., quiet = TRUE)
rglExtrafonts(..., quiet = TRUE)
... |
Vectors of fonts to try. See the Details. |
quiet |
Whether to print information on progress. |
The extrafont
package collects information on
installed fonts from the system. When you first
install extrafont, or after new fonts have been installed on your
system, run extrafont::font_import()
to build
its database of system fonts.
Fonts can be installed in rgl using
rglExtrafonts(rglname = familyname)
or rglExtrafonts(familyname)
. In this call
familyname
is a vector of family names to look
for in the extrafont database using extrafont::choose_font(familyname)
; the first one found
will be registered with rgl. The optional name rglname
will also be usable to refer to the font family.
If none of the given family names is found, no change will be made to the registered fonts in rgl.
During startup, rgl detects whether extrafont
is installed, and if so runs
rglExtrafonts(sans = c("Helvetica", "Arial"),
serif = c("Times", "Times New Roman"),
mono = c("Courier", "Courier New"))
to attempt to set up the default fonts.
Fonts found by extrafont can also be used in some other graphics devices besides rgl; see the extrafont documentation for details.
Invisibly returns a vector giving the rgl name and the family name for the newly installed font.
Each font in a display needs a unique rgl name; if the associated font for a given name is changed, all previously plotted text will also change.
Currently rglwidget
displays will not
respect the new definitions.
Duncan Murdoch
if (requireNamespace("extrafont") && !in_pkgdown_example()) { open3d() text3d(1,1,1, "Default", family = "sans", cex = 2) # Attempt to register new sans-serif font: newfamily <- rglExtrafonts(newsans = c("Comic Sans MS", "Impact", "Verdana", "Tahoma")) text3d(2,2,2, newfamily, family = "newsans", cex = 2) }
if (requireNamespace("extrafont") && !in_pkgdown_example()) { open3d() text3d(1,1,1, "Default", family = "sans", cex = 2) # Attempt to register new sans-serif font: newfamily <- rglExtrafonts(newsans = c("Comic Sans MS", "Impact", "Verdana", "Tahoma")) text3d(2,2,2, newfamily, family = "newsans", cex = 2) }
Specify FreeType fonts for use in rgl graphics.
rglFonts(...)
rglFonts(...)
... |
Device dependent font definitions for use with FreeType. |
FreeType fonts are specified using the rglFonts
function. This function
takes a vector of four filenames of TrueType font files which
will be used for the four styles regular, bold, italic and bold italic. The
vector is passed with a name to be used as the family name, e.g.
rglFonts(sans = c("/path/to/FreeSans.ttf", ...))
. In order to
limit the file size, the rgl package ships with just 3 font files, for
regular versions of the serif
, sans
and mono
families.
Additional free font files were available in the past from the
Amaya project, though currently the
rglExtrafonts
function provides an easier way
to register new fonts.
On Windows the system fonts are acceptable and are used
when useFreeType = FALSE
(the current default in
r3dDefaults
). Mappings to family
names
are controlled by the grDevices::windowsFonts()
function.
Full pathnames should normally be used to specify font files. If relative paths are used, they are interpreted differently by platform. Currently Windows fonts are looked for in the Windows fonts folder, while other platforms use the current working directory.
If FreeType fonts are not used, then bitmapped fonts will be used instead. On Windows these will be based on the fonts specified using the
windowsFonts
function, and are resizable.
Other platforms will use the default bitmapped font which is not
resizable.
Bitmapped fonts have a limited number of characters supported; if any unsupported characters are used, an error will be thrown.
the current set of font definitions.
## Not run: # These FreeType fonts are available from the Amaya project, and are not shipped # with rgl. You would normally install them to the rgl/fonts directory # and use fully qualified pathnames, e.g. # system.file("fonts/FreeSerif.ttf", package = "rgl") rglFonts(serif = c("FreeSerif.ttf", "FreeSerifBold.ttf", "FreeSerifItalic.ttf", "FreeSerifBoldItalic.ttf"), sans = c("FreeSans.ttf", "FreeSansBold.ttf", "FreeSansOblique.ttf", "FreeSansBoldOblique.ttf"), mono = c("FreeMono.ttf", "FreeMonoBold.ttf", "FreeMonoOblique.ttf", "FreeMonoBoldOblique.ttf"), symbol= c("ESSTIX10.TTF", "ESSTIX12.TTF", "ESSTIX9_.TTF", "ESSTIX11.TTF")) ## End(Not run)
## Not run: # These FreeType fonts are available from the Amaya project, and are not shipped # with rgl. You would normally install them to the rgl/fonts directory # and use fully qualified pathnames, e.g. # system.file("fonts/FreeSerif.ttf", package = "rgl") rglFonts(serif = c("FreeSerif.ttf", "FreeSerifBold.ttf", "FreeSerifItalic.ttf", "FreeSerifBoldItalic.ttf"), sans = c("FreeSans.ttf", "FreeSansBold.ttf", "FreeSansOblique.ttf", "FreeSansBoldOblique.ttf"), mono = c("FreeMono.ttf", "FreeMonoBold.ttf", "FreeMonoOblique.ttf", "FreeMonoBoldOblique.ttf"), symbol= c("ESSTIX10.TTF", "ESSTIX12.TTF", "ESSTIX9_.TTF", "ESSTIX11.TTF")) ## End(Not run)
All objects in an RGL scene have a
numerical id. These ids are normally stored
in vectors of class c("rglIds", "numeric")
, which
will also have class "rglHighlevel"
or "rglLowlevel"
depending on
whether a high level function like
plot3d
or persp3d
, or a low level function created the objects.
rglId(ids = integer()) lowlevel(ids = integer()) highlevel(ids = integer()) ## S3 method for class 'rglId' print(x, rglwidget = getOption("rgl.printRglwidget", FALSE), ...)
rglId(ids = integer()) lowlevel(ids = integer()) highlevel(ids = integer()) ## S3 method for class 'rglId' print(x, rglwidget = getOption("rgl.printRglwidget", FALSE), ...)
ids |
A vector of object ids. |
x |
An |
rglwidget |
Whether to create and print an RGL widget. If false, nothing is printed. |
... |
Other arguments which will be passed to
|
These functions and classes are intended to allow
RGL scenes to be automatically
displayed in R Markdown documents. See setupKnitr
for details on enabling auto-printing.
Note that all objects in the current
scene will be printed by default, not just
the ids in x
. (One reason for this
is that lights are also objects; printing
objects without lights would rarely make
sense.)
Objects of class "rglId"
, c("rglHighlevel", "rglId",
"numeric")
or c("rglLowlevel", "rglId",
"numeric")
for rglId
, lowlevel
or highlevel
respectively.
Duncan Murdoch
x <- matrix(rnorm(30), ncol = 3, dimnames = list(NULL, c("x", "y", "z"))) p <- plot3d(x, type = "s") str(p) if (interactive() || in_pkgdown_example()) print(p, rglwidget = TRUE)
x <- matrix(rnorm(30), ncol = 3, dimnames = list(NULL, c("x", "y", "z"))) p <- plot3d(x, type = "s") str(p) if (interactive() || in_pkgdown_example()) print(p, rglwidget = TRUE)
This generates an HTML select
element to
choose among the mouse modes supported by rglwidget
.
rglMouse(sceneId, choices = c("trackball", "selecting", "xAxis", "yAxis", "zAxis", "polar", "zoom", "fov", "none"), labels = choices, button = 1, dev = cur3d(), subscene = currentSubscene3d(dev), default = par3d("mouseMode", dev = dev, subscene = subscene)[button + 1], stayActive = FALSE, height = 40, ...)
rglMouse(sceneId, choices = c("trackball", "selecting", "xAxis", "yAxis", "zAxis", "polar", "zoom", "fov", "none"), labels = choices, button = 1, dev = cur3d(), subscene = currentSubscene3d(dev), default = par3d("mouseMode", dev = dev, subscene = subscene)[button + 1], stayActive = FALSE, height = 40, ...)
sceneId |
Either an |
choices |
Which mouse modes to support? |
labels |
How to label each mouse mode. |
button |
Which mouse button is being controlled. |
dev |
The RGL device used for defaults. |
subscene |
Which subscene is being modified. |
default |
What is the default entry to show in the control. |
stayActive |
Whether a selection brush should stay active if the mouse mode is changed. |
height |
The (relative) height of the item in the output display. |
... |
Additional arguments to pass to |
A result of an rglwidget
call can be passed
as the sceneId
argument. This allows the widget
to be “piped” into the rglMouse
call.
The widget will appear first, the selector next in
a tagList
.
If the sceneId
is a character string, it should be
the elementId
of a separately constructed
rglwidget
result.
Finally, the sceneId
can be omitted. In this case
the rglMouse
result needs to be passed into an
rglwidget
call as part of the
controllers
argument. This will place the selector before the
widget on the resulting display.
If the mouse mode is changed while brushing the scene,
by default the brush will be removed (and so the selection
will be cleared too). If this is not desired, set
stayActive = TRUE
.
A browsable value to put in a web page.
Duncan Murdoch
if (interactive() || in_pkgdown_example()) { open3d() xyz <- matrix(rnorm(300), ncol = 3) id <- plot3d(xyz, col = "red", type = "s")["data"] par3d(mouseMode = "selecting") share <- rglShared(id) # This puts the selector below the widget. rglwidget(shared = share, width = 300, height = 300) %>% rglMouse() # This puts the selector above the widget. rglMouse() %>% rglwidget(shared = share, width = 300, height = 300, controllers = .) }
if (interactive() || in_pkgdown_example()) { open3d() xyz <- matrix(rnorm(300), ncol = 3) id <- plot3d(xyz, col = "red", type = "s")["data"] par3d(mouseMode = "selecting") share <- rglShared(id) # This puts the selector below the widget. rglwidget(shared = share, width = 300, height = 300) %>% rglMouse() # This puts the selector above the widget. rglMouse() %>% rglwidget(shared = share, width = 300, height = 300, controllers = .) }
These functions take a user orientation matrix from an RGL scene and approximate the parameters to either lattice or base graphics functions.
rglToLattice(rotm = par3d("userMatrix")) rglToBase(rotm = par3d("userMatrix"))
rglToLattice(rotm = par3d("userMatrix")) rglToBase(rotm = par3d("userMatrix"))
rotm |
A matrix in homogeneous coordinates to convert. |
The lattice package can use Euler angles in
the ZYX scheme to describe the rotation of a scene
in its wireframe
or
cloud
functions. The
rglToLattice
function computes these angles
based on rotm
, which defaults to the current
user matrix. This allows RGL to be used
to interactively find a decent viewpoint and
then reproduce it in lattice.
The base graphics persp
function does
not use full Euler angles; it
uses a viewpoint angle, and assume the z axis
remains vertical. The rglToBase
function
computes the viewpoint angle accurately if the
RGL scene is displayed with a vertical
z axis, and does an approximation otherwise.
rglToLattice
returns a list suitable to
be used as the screen
argument to
wireframe
.
rglToBase
returns a list containing
theta
and phi
components which
can be used as corresponding arguments in persp
.
Duncan Murdoch
persp3d(volcano, col = "green") if ((hasorientlib <- requireNamespace("orientlib", quietly = TRUE)) && requireNamespace("lattice", quietly = TRUE)) lattice::wireframe(volcano, screen = rglToLattice()) if (hasorientlib) { angles <- rglToBase() persp(volcano, col = "green", border = NA, shade = 0.5, theta = angles$theta, phi = angles$phi) }
persp3d(volcano, col = "green") if ((hasorientlib <- requireNamespace("orientlib", quietly = TRUE)) && requireNamespace("lattice", quietly = TRUE)) lattice::wireframe(volcano, screen = rglToLattice()) if (hasorientlib) { angles <- rglToBase() persp(volcano, col = "green", border = NA, shade = 0.5, theta = angles$theta, phi = angles$phi) }
The htmlwidgets package provides a framework for embedding graphical displays in HTML documents of various types. This function provides the necessities to embed an RGL scene in one.
rglwidget(x = scene3d(minimal), width = figWidth(), height = figHeight(), controllers = NULL, elementId = NULL, reuse = FALSE, webGLoptions = list(preserveDrawingBuffer = TRUE), shared = NULL, minimal = TRUE, webgl, snapshot, shinyBrush = NULL, altText = "3D plot", ..., oldConvertBBox = FALSE, fastTransparency = getOption("rgl.fastTransparency", TRUE))
rglwidget(x = scene3d(minimal), width = figWidth(), height = figHeight(), controllers = NULL, elementId = NULL, reuse = FALSE, webGLoptions = list(preserveDrawingBuffer = TRUE), shared = NULL, minimal = TRUE, webgl, snapshot, shinyBrush = NULL, altText = "3D plot", ..., oldConvertBBox = FALSE, fastTransparency = getOption("rgl.fastTransparency", TRUE))
x |
An RGL scene produced by the |
width , height
|
The width and height of the display in pixels. |
controllers |
Names of |
snapshot , webgl
|
Control of mode of display of scene. See Details below. |
elementId |
The id to use on the HTML |
reuse |
Ignored. See Details below. |
webGLoptions |
A list of options to pass to WebGL when the drawing context is created. See the Details below. |
shared |
An object produced by |
minimal |
Should attributes be skipped if they currently have
no effect? See |
shinyBrush |
The name of a Shiny |
altText |
Text to include for screen-readers or browsers that don't handle WebGL. See Details below. |
oldConvertBBox , fastTransparency
|
See Details below. |
... |
Additional arguments
to pass to |
This produces a WebGL version of an RGL scene using the htmlwidgets framework. This allows display of the scene in the RStudio IDE, a browser, an rmarkdown document or in a shiny app.
options(rgl.printRglwidget = TRUE)
will cause
rglwidget()
to be called and displayed
when the result of an RGL call that changes the
scene is printed.
In RMarkdown or in standalone code, you can use a magrittr-style
“pipe” command to join an rglwidget
with a
playwidget
or toggleWidget
. If the control widget comes
first, it should be piped into the controllers
argument. If the rglwidget
comes first, it
can be piped into the first argument of playwidget
or toggleWidget
.
In earlier versions, the reuse
argument let one output scene share data from earlier ones. This is no longer supported.
If elementId
is NULL
and we are not in a Shiny app,
elementId
is set to a random value to facilitate re-use
of information.
To save the display to a file, use htmlwidgets::saveWidget
.
This requires pandoc
to be installed.
For a snapshot, you can use
htmltools::save_html(img(src=rglwidget(snapshot=TRUE)), file = ...)
.
The webGLoptions
argument is a list which will
be passed when the WebGL context is created. See
the WebGL 1.0 specification on https://registry.khronos.org/webgl/ for possible
settings. The default in rglwidget
differs
from the WebGL default by setting preserveDrawingBuffer = TRUE
in order to allow other tools to read
the image, but please note that some implementations
of WebGL contain bugs with this setting. We have
attempted to work around them, but may change our
default in the future if this proves unsatisfactory.
The webgl
argument controls
whether a dynamic plot is displayed in HTML. In LaTeX
and some other formats
dynamic plots can't be
displayed, so if the snapshot
argument is TRUE
,
webgl
must be FALSE
. (In previous versions
of the rgl package, both webgl
and snapshot
could be
TRUE
; that hasn't worked for a while and is no longer
allowed as of version 0.105.6.)
The snapshot
argument controls whether a snapshot is
displayed: it must be !webgl
if both are specified.
Prior to rgl 0.106.21, rglwidget
converted
bounding box decorations into separate objects: a box, text
for the labels, segments for the ticks. By default it now
generates these in Javascript, allowing axis labels to move as
they do in the display in R. If you prefer the old conversion,
set oldConvertBBox = TRUE
.
In version 1.3.4, the handling of transparent objects was
changed to match the rgl device more closely. The new
method of rendering is quite a bit faster, though sometimes
less accurate. To get the older drawing method set
fastTransparency = FALSE
.
An object of class "htmlwidget"
(or "shiny.tag.list"
if pipes are used) that will intelligently print itself into
HTML in a variety of contexts including the R console, within R Markdown
documents, and within Shiny output bindings.
If objects are passed in the shared
argument,
then the widget will respond to selection and filtering
applied to those as shared datasets. See rglShared
for more details and an example.
In an R Markdown document, you would normally call
setupKnitr(autoprint = TRUE)
and would not
make explicit calls to rglwidget()
. If you do
make such calls, the graphics will be inserted into the
document.
In knitr versions greater than 1.42.5,
the altText
argument will be ignored and the alternate
text will be set from chunk option fig.alt
or
fig.cap
as with other graphics.
This widget is designed to work with Shiny for interactive displays linked to a server running R.
In a Shiny app, there will often be one or more
playwidget
objects in the app, taking input from
the user. In order to be sure that the initial value of the user control
is reflected in the scene, you should list all players in the
controllers
argument. See the sample application in
system.file("shinyDemo", package = "rglwidget")
for an example.
In Shiny, it is possible to find out information about mouse selections
by specifying the name of an input
item in the
shinyBrush
argument. For example, with
shinyBrush = "brush3d"
, each change
to the mouse selection will send data to input$brush3d
in an
object of class "rglMouseSelection"
with the
following components:
The ID of the subscene where the mouse is selecting.
Either "changing"
or "inactive"
.
The coordinates of the corners of the selected region in the window,
in order c(x1, y1, x2, y2)
.
The model matrix, projection matrix and viewport in effect at that location.
This object can be used as the first argument to
selectionFunction3d
to produce a test function for
whether a particular location is in the selected region. If the
brush becomes inactive, an object containing only the state
field will be sent, with value "inactive"
.
The appearance of the display is set by the stylesheet
in system.file("htmlwidgets/lib/rglClass/rgl.css")
.
The widget is of class rglWebGL
, with id
set according to elementId
. (As of this writing,
no special settings are given for class rglWebGL
,
but you can add your own.)
Duncan Murdoch
hook_webgl
for an earlier approach to this problem. rglwidgetOutput
for Shiny details.
save <- options(rgl.useNULL=TRUE) example("plot3d", "rgl") widget <- rglwidget() if (interactive() || in_pkgdown_example()) widget if (interactive() && !in_pkgdown_example()) { # Save it to a file. This requires pandoc filename <- tempfile(fileext = ".html") htmlwidgets::saveWidget(rglwidget(), filename) browseURL(filename) } options(save)
save <- options(rgl.useNULL=TRUE) example("plot3d", "rgl") widget <- rglwidget() if (interactive() || in_pkgdown_example()) widget if (interactive() && !in_pkgdown_example()) { # Save it to a file. This requires pandoc filename <- tempfile(fileext = ".html") htmlwidgets::saveWidget(rglwidget(), filename) browseURL(filename) } options(save)
The dev.off
function in grDevices doesn't restore
the previous graphics device when called. This function does.
safe.dev.off(which = dev.cur(), prev = dev.prev())
safe.dev.off(which = dev.cur(), prev = dev.prev())
which |
Which device to close. |
prev |
Which device to set as current after closing. |
This function closes device which
if it is not device 1,
then calls dev.set(prev)
if there are any devices still
open.
The number and name of the new active device. It will not
necessarily be prev
if that device isn't already open.
Duncan Murdoch
https://bugs.r-project.org/show_bug.cgi?id=18604
# Open a graphics device dev.new() first <- dev.cur() # Open a second graphics device dev.new() second <- dev.cur() second # Open another one, and close it using dev.off() dev.new() dev.off() dev.cur() == second # Not the same as second! # Try again with safe.dev.off() dev.set(second) dev.new() safe.dev.off() dev.cur() == second # Close the other two devs safe.dev.off() safe.dev.off()
# Open a graphics device dev.new() first <- dev.cur() # Open a second graphics device dev.new() second <- dev.cur() second # Open another one, and close it using dev.off() dev.new() dev.off() dev.cur() == second # Not the same as second! # Try again with safe.dev.off() dev.set(second) dev.new() safe.dev.off() dev.cur() == second # Close the other two devs safe.dev.off() safe.dev.off()
Clear shapes, lights, bbox
clear3d( type = c("shapes", "bboxdeco", "material"), defaults, subscene = 0 ) pop3d( type = "shapes", id = 0, tag = NULL) ids3d( type = "shapes", subscene = NA, tags = FALSE )
clear3d( type = c("shapes", "bboxdeco", "material"), defaults, subscene = 0 ) pop3d( type = "shapes", id = 0, tag = NULL) ids3d( type = "shapes", subscene = NA, tags = FALSE )
type |
Select subtype(s):
|
defaults |
default values to use after clearing |
subscene |
which subscene to work with. |
id |
vector of ID numbers of items to remove |
tag |
override |
tags |
logical; whether to return |
RGL holds several lists of objects in each scene.
There are lists for shapes, lights, bounding box decorations, subscenes, etc.
clear3d
clears the specified stack, or restores
the defaults for the bounding box (not visible) or viewpoint.
With id = 0
pop3d
removes
the last added node on the list (except for subscenes: there it
removes the active subscene). The id
argument
may be used to specify arbitrary item(s) to remove; if id != 0
,
the type
argument is ignored.
clear3d
may also be used to clear material properties
back to their defaults.
clear3d
has an optional defaults
argument, which defaults to
r3dDefaults
. Only the materials
component of this argument
is currently used by clear3d
.
ids3d
returns a dataframe containing the IDs in the currently active subscene
by default, or a specified subscene, or if subscene = 0
, in the whole
rgl window along with an indicator of their type and if tags = TRUE
, the
tag
value for each.
Note that clearing the light stack leaves the scene in darkness; it should normally
be followed by a call to light3d
.
rgl
,
bbox3d
,
light3d
,
open3d
to open a new window.
x <- rnorm(100) y <- rnorm(100) z <- rnorm(100) p <- plot3d(x, y, z, type = 's', tag = "plot") ids3d() lines3d(x, y, z) ids3d(tags = TRUE) if (interactive() && !rgl.useNULL() && !in_pkgdown_example()) { readline("Hit enter to change spheres") pop3d(id = p["data"]) spheres3d(x, y, z, col = "red", radius = 1/5) box3d() }
x <- rnorm(100) y <- rnorm(100) z <- rnorm(100) p <- plot3d(x, y, z, type = 's', tag = "plot") ids3d() lines3d(x, y, z) ids3d(tags = TRUE) if (interactive() && !rgl.useNULL() && !in_pkgdown_example()) { readline("Hit enter to change spheres") pop3d(id = p["data"]) spheres3d(x, y, z, col = "red", radius = 1/5) box3d() }
This function saves a large part of the RGL state associated with the current window to a variable.
scene3d(minimal = TRUE) ## S3 method for class 'rglscene' plot3d(x, add = FALSE, open3dParams = getr3dDefaults(), ...) ## S3 method for class 'rglobject' plot3d(x, ...) ## S3 method for class 'rglscene' print(x, ...) ## S3 method for class 'rglobject' print(x, ...)
scene3d(minimal = TRUE) ## S3 method for class 'rglscene' plot3d(x, add = FALSE, open3dParams = getr3dDefaults(), ...) ## S3 method for class 'rglobject' plot3d(x, ...) ## S3 method for class 'rglscene' print(x, ...) ## S3 method for class 'rglobject' print(x, ...)
minimal |
Should attributes be skipped if they currently have no effect? See Details. |
x |
An object of class |
add |
Whether to open a new window, or add to the existing one. |
open3dParams |
Default parameters for |
... |
Additional parameters passed to |
The components saved are: the par3d
settings, the material3d
settings, the bg3d
settings, the lights and the objects in the scene.
In most cases, calling plot3d
on that variable will
duplicate the scene. (There are likely to be small differences, mostly internal, but
some aspects of the scene are not currently available.) If textures are used,
the name of the texture will be saved, rather than the contents of the
texture file.
Other than saving the code
to recreate a scene, saving the result of scene3d
to
a file will allow it to be reproduced later most accurately. In roughly decreasing order of fidelity,
writeWebGL
(now deprecated), writePLY
, writeOBJ
and writeSTL
write the scene to a file in formats readable by other software.
If minimal = TRUE
(the default), then attributes of objects
will not be saved if they currently have no effect on
the display, thereby reducing the file size.
Set minimal = FALSE
if the scene is intended
to be used in a context where the appearance could be changed.
Currently this only affects the inclusion of normals; with
minimal = TRUE
they are
omitted for objects when the material is not lit.
The scene3d
function returns an object of class
"rglscene"
. This is a list with some or all of the components:
material |
The results returned from a |
rootSubscene |
A list containing information about the main ("root") subscene. This may include:
|
objects |
A list containing the RGL lights, background and objects in the scene. |
The objects in the objects
component are of class
"rglobject"
. They are lists containing some or all of the
components
id |
The RGL identifier of the object in the original scene. |
type |
A character variable identifying the type of object. |
material |
Components of the material that differ from the scene material. |
vertices , normals , etc.
|
Any of the attributes of the object retrievable by |
ignoreExtent |
A logical value indicating whether this object contributes to the bounding box. Currently this may differ from the object in the original scene. |
objects |
Sprites may contain other objects; they will be stored here as a list of |
Lights in the scene are stored similarly, mixed into the objects
list.
The plot3d
methods invisibly return a vector of RGL object ids
that were plotted. The print
methods invisibly return the
object that was printed.
Duncan Murdoch
rglwidget
, writePLY
, writeOBJ
and writeSTL
write the scene to a file in various formats.
open3d() z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) persp3d(x, y, z, col = "green3", aspect = "iso") s <- scene3d() # Make it bigger s$par3d$windowRect <- 1.5*s$par3d$windowRect # and draw it again plot3d(s)
open3d() z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) persp3d(x, y, z, col = "green3", aspect = "iso") s <- scene3d() # Make it bigger s$par3d$windowRect <- 1.5*s$par3d$windowRect # and draw it again plot3d(s)
These functions allow Shiny apps to make relatively large changes to a scene, adding and removing objects from it.
sceneChange(elementId, x = scene3d(minimal), delete = NULL, add = NULL, replace = NULL, material = FALSE, rootSubscene = FALSE, delfromSubscenes = NULL, skipRedraw = FALSE, minimal = TRUE) registerSceneChange()
sceneChange(elementId, x = scene3d(minimal), delete = NULL, add = NULL, replace = NULL, material = FALSE, rootSubscene = FALSE, delfromSubscenes = NULL, skipRedraw = FALSE, minimal = TRUE) registerSceneChange()
elementId |
The id of the element holding the |
x |
The new scene to use as a source for objects to add. |
delete , add , replace
|
Object ids to modify in the scene. The |
material |
Logical to indicate whether default material should be updated. |
rootSubscene |
Logical to indicate whether root subscene should be updated. |
delfromSubscenes |
A vector of subscene ids that may have been changed by
deletions. By default, all subscenes in |
skipRedraw |
If |
minimal |
See |
registerSceneChange
must be called in the UI component
of a Shiny app to register the "sceneChange"
custom
message.
registerSceneChange
returns the HTML code to register the
message.
sceneChange
returns a list to be used as the "sceneChange"
message to change the scene. Use
shiny::session$sendCustomMessage
to send it.
Duncan Murdoch
playwidget
for a different approach to modifying
scenes that can be much faster, but may be less flexible. The
Shiny demo in this package makes use of all of these approaches.
## Not run: shinyUI(fluidPage( registerSceneChange(), actionButton("thebutton", "Change") )) shinyServer(function(input, output, session) { observeEvent(input$thebutton, { session$sendCustomMessage("sceneChange", sceneChange("thewidget", delete = deletes, add = adds)) }) }) ## End(Not run)
## Not run: shinyUI(fluidPage( registerSceneChange(), actionButton("thebutton", "Change") )) shinyServer(function(input, output, session) { observeEvent(input$thebutton, { session$sendCustomMessage("sceneChange", sceneChange("thewidget", delete = deletes, add = adds)) }) }) ## End(Not run)
This function allows the user to use the mouse to select a region in an RGL scene.
select3d(button = c("left", "middle", "right"), dev = cur3d(), subscene = currentSubscene3d(dev)) selectionFunction3d(proj, region = proj$region)
select3d(button = c("left", "middle", "right"), dev = cur3d(), subscene = currentSubscene3d(dev)) selectionFunction3d(proj, region = proj$region)
button |
Which button to use for selection. |
dev , subscene
|
The RGL device and subscene to work with |
proj |
An object returned from |
region |
Corners of a rectangular region in the display. |
select3d
selects 3-dimensional regions by allowing the
user to use a mouse to draw a rectangle showing
the projection of the region onto the screen. It returns
a function which tests points for inclusion in the selected region.
selectionFunction3d
constructs such a test function given
coordinates and current transformation matrices.
If the scene is later moved or rotated, the selected region will remain the same, though no longer corresponding to a rectangle on the screen.
These return a function f(x, y, z)
which tests whether each
of the points (x, y, z)
is in the selected region, returning
a logical vector. This function accepts input in a wide
variety of formats as it uses xyz.coords
to interpret its parameters.
Ming Chen / Duncan Murdoch
# Allow the user to select some points, and then redraw them # in a different color if (interactive() && !in_pkgdown_example()) { x <- rnorm(1000) y <- rnorm(1000) z <- rnorm(1000) open3d() points3d(x, y, z) f <- select3d() if (!is.null(f)) { keep <- f(x, y, z) pop3d() points3d(x[keep], y[keep], z[keep], color = 'red') points3d(x[!keep], y[!keep], z[!keep]) } }
# Allow the user to select some points, and then redraw them # in a different color if (interactive() && !in_pkgdown_example()) { x <- rnorm(1000) y <- rnorm(1000) z <- rnorm(1000) open3d() points3d(x, y, z) f <- select3d() if (!is.null(f)) { keep <- f(x, y, z) pop3d() points3d(x[keep], y[keep], z[keep], color = 'red') points3d(x[!keep], y[!keep], z[!keep]) } }
This function uses the select3d
function to allow the user to choose a
point or region in the scene, then reports on all the vertices in or near that selection.
selectpoints3d(objects = ids3d()$id, value = TRUE, closest = TRUE, multiple = FALSE, ...)
selectpoints3d(objects = ids3d()$id, value = TRUE, closest = TRUE, multiple = FALSE, ...)
objects |
A vector of object id values to use for the search. |
value |
If |
closest |
If |
multiple |
If |
... |
Other parameters to pass to |
The multiple
argument may be a logical value or a function. If logical,
it controls whether multiple selections will be performed. If
multiple
is FALSE
, a single selection will be performed;
it might contain multiple points. If TRUE
, multiple selections
will occur and the results will be combined into a single matrix.
If multiple
is a function, it should take a single argument.
This function will be called with the argument set to a matrix
containing newly added rows to the value, i.e.
it will contain coordinates of the newly selected points (if
value = TRUE
), or the indices of the points (if value =
FALSE
). It should return a logical value, TRUE
to indicate
that selection should continue, FALSE
to indicate that it
should stop.
In either case, if multiple selections are being performed, the ESC
key will
stop the process.
If value
is TRUE
, a 3-column matrix giving the coordinates of the
selected points. All rows in the matrix will be unique even if multiple vertices
have the same coordinates.
If value
is FALSE
, a 2-column matrix containing columns:
id |
The object id containing the point. |
index |
The index of the point within |
This function selects points, not areas. For example, if the selection region is in the interior of the triangle, that will count as a miss for all of the triangle's vertices.
Duncan Murdoch
select3d
to return a selection function.
xyz <- cbind(rnorm(20), rnorm(20), rnorm(20)) ids <- plot3d( xyz ) if (interactive() && !in_pkgdown_example()) { # Click near a point to select it and put a sphere there. # Press ESC to quit... # This version returns coordinates selectpoints3d(ids["data"], multiple = function(x) { spheres3d(x, color = "red", alpha = 0.3, radius = 0.2) TRUE }) # This one returns indices selectpoints3d(ids["data"], value = FALSE, multiple = function(ids) { spheres3d(xyz[ids[, "index"], , drop = FALSE], color = "blue", alpha = 0.3, radius = 0.2) TRUE }) }
xyz <- cbind(rnorm(20), rnorm(20), rnorm(20)) ids <- plot3d( xyz ) if (interactive() && !in_pkgdown_example()) { # Click near a point to select it and put a sphere there. # Press ESC to quit... # This version returns coordinates selectpoints3d(ids["data"], multiple = function(x) { spheres3d(x, color = "red", alpha = 0.3, radius = 0.2) TRUE }) # This one returns indices selectpoints3d(ids["data"], value = FALSE, multiple = function(ids) { spheres3d(xyz[ids[, "index"], , drop = FALSE], color = "blue", alpha = 0.3, radius = 0.2) TRUE }) }
This function sets user callbacks to construct axes in R or
rglwidget
displays.
setAxisCallbacks(axes, fns, javascript = NULL, subscene = scene$rootSubscene$id, scene = scene3d(minimal = FALSE), applyToScene = TRUE, applyToDev = missing(scene))
setAxisCallbacks(axes, fns, javascript = NULL, subscene = scene$rootSubscene$id, scene = scene3d(minimal = FALSE), applyToScene = TRUE, applyToDev = missing(scene))
axes |
Which axes? Specify as number in |
fns |
Function or list of functions or character vector giving names of functions. |
javascript |
Optional block of Javascript code to be included (at the global level). |
subscene |
Which subscene do these callbacks apply to? |
scene |
Which scene? |
applyToScene |
Should these changes apply to the scene object? |
applyToDev |
Should these changes apply to the current device? |
If applyToScene
is TRUE
, this function adds Javascript
callbacks to the scene
object.
If applyToDev
is TRUE
, it adds R
callbacks to the current RGL device.
For Javascript,
the callbacks are specified as strings; these will be
evaluated within the browser in the global context to define the functions,
which will then be called with the Javascript
this
object set to the current
rglwidgetClass
object.
For R, they may be strings or R functions.
Both options may be TRUE
, in which case the
callbacks must be specified as strings which are
both valid Javascript and valid R. The usual way to
do this is to give just a function name, with the
function defined elsewhere, as in the Example below.
The functions should have a header of the form
function(margin)
. The margin
argument
will be a string like "x++"
indicating which margin
would be chosen by R. If RGL would not choose to draw any
axis annotations (which happens with rglwidget
, though
not currently in R itself), only the letter will be passed,
e.g. "x"
.
Invisibly returns an rglScene
object. This
object will record the changes if applyToScene
is TRUE
.
If applyToDev
is TRUE
, it will also
have the side effect of attempting to install the
callbacks.
Duncan Murdoch
setUserCallbacks
for mouse callbacks.
# Draw arrows instead of tick marks on axes arrowAxis <- local({ ids <- c(NA, NA, NA) bbox <- c(NA, NA, NA, NA, NA, NA) function(margin) { dim <- if (grepl("x", margin)) 1 else if (grepl("y", margin)) 2 else 3 inds <- 2*dim + (-1):0 range <- par3d("bbox")[inds] if (!identical(bbox[inds], range)) { if (!is.na(ids[dim])) pop3d(id = ids[dim]) bbox[inds] <<- range center <- mean(range) from <- mean(c(range[1], center)) to <- mean(c(center, range[2])) # margin should agree with suggestion, so use "x++" etc. margin <- gsub("-", "+", margin) ids[dim] <<- arrow3d(p0 = c(from, 1, 1), p1 = c(to, 1, 1), n = 4, type = "lines", margin = margin, floating = TRUE) } } }) # Define the Javascript function with the same name to use in WebGL # Since Javascript won't change the bounding box, this function # doesn't need to do anything. js <- " window.arrowAxis = function(margin) {} ; " xyz <- matrix(rnorm(60), ncol = 3) plot3d(xyz, xlab = "x", ylab = "y", zlab = "z") setAxisCallbacks(1:3, "arrowAxis", javascript = js) rglwidget()
# Draw arrows instead of tick marks on axes arrowAxis <- local({ ids <- c(NA, NA, NA) bbox <- c(NA, NA, NA, NA, NA, NA) function(margin) { dim <- if (grepl("x", margin)) 1 else if (grepl("y", margin)) 2 else 3 inds <- 2*dim + (-1):0 range <- par3d("bbox")[inds] if (!identical(bbox[inds], range)) { if (!is.na(ids[dim])) pop3d(id = ids[dim]) bbox[inds] <<- range center <- mean(range) from <- mean(c(range[1], center)) to <- mean(c(center, range[2])) # margin should agree with suggestion, so use "x++" etc. margin <- gsub("-", "+", margin) ids[dim] <<- arrow3d(p0 = c(from, 1, 1), p1 = c(to, 1, 1), n = 4, type = "lines", margin = margin, floating = TRUE) } } }) # Define the Javascript function with the same name to use in WebGL # Since Javascript won't change the bounding box, this function # doesn't need to do anything. js <- " window.arrowAxis = function(margin) {} ; " xyz <- matrix(rnorm(60), ncol = 3) plot3d(xyz, xlab = "x", ylab = "y", zlab = "z") setAxisCallbacks(1:3, "arrowAxis", javascript = js) rglwidget()
This function is mainly for internal use, to work around a bug in macOS Catalina: if base plotting happens too quickly after opening RGL and the first call to quartz, R crashes.
This inserts a delay after the first call to open the
graphics device. The default is
no delay, unless on Catalina with no graphics device
currently open but the quartz
device set
as the default, when a 1 second delay will be added.
Use environment variable "RGL_SLOW_DEV = value" to set
a different default delay.
It works by changing the value of options("device")
,
so explicit calls to the device will not be affected.
It is called automatically when the rgl package is loaded.
setGraphicsDelay(delay = Sys.getenv("RGL_SLOW_DEV", 0), unixos = "none")
setGraphicsDelay(delay = Sys.getenv("RGL_SLOW_DEV", 0), unixos = "none")
delay |
Number of seconds for the delay. |
unixos |
The name of the Unix OS. If set to |
Called for the side effect of adding the delay to the first opening of the graphics device.
These functions allow RGL graphics to be embedded in knitr documents.
The simplest method is to run setupKnitr(autoprint = TRUE)
early in the document. That way RGL commands act a lot
like base graphics commands: plots will be automatically inserted
where appropriate, according to the fig.keep
chunk option.
By default (fig.keep = "high"
), only high-level plots
are kept, after low-level changes have been merged into them.
See the knitr documentation
https://yihui.org/knitr/options/#plots for more details.
To suppress auto-printing, the RGL calls
can be wrapped in invisible()
.
Similarly to grid graphics (used by lattice
and ggplot2), automatic inclusion requires the object
to be printed: only the last statement in a code block
in braces is automatically printed. Unlike those
packages, auto-printing is the only way to get this to
work: calling print
explicitly doesn't
work.
Other functions allow embedding either as bitmaps (hook_rgl
with format "png"
),
fixed vector graphics (hook_rgl
with format "eps"
, "pdf"
or
"postscript"
), or interactive WebGL graphics (hook_webgl
). hook_rglchunk
is not normally invoked by the
user; it is the hook that supports automatic creation and
deletion of RGL scenes.
setupKnitr(autoprint = FALSE, rgl.newwindow = autoprint, rgl.closewindows = autoprint) hook_rgl(before, options, envir) hook_webgl(before, options, envir) hook_rglchunk(before, options, envir)
setupKnitr(autoprint = FALSE, rgl.newwindow = autoprint, rgl.closewindows = autoprint) hook_rgl(before, options, envir) hook_webgl(before, options, envir) hook_rglchunk(before, options, envir)
autoprint |
If true, RGL commands
automatically plot (with low level plots suppressed by
the default value of the |
rgl.newwindow , rgl.closewindows
|
Default values for the knitr chunk options. |
before , options , envir
|
Standard knitr hook function arguments. |
The setupKnitr()
function needs to be called once
at the start of the document to install the knitr hooks.
If it is called twice in the same session the second call
will override the first.
The following chunk options are supported:
rgl.newwindow
: Whether to open a new window for the chunk. Default is set by setupKnitr
argument.
rgl.closewindows
: Whether
to close windows at the end of the chunk. Default is set by setupKnitr
argument.
rgl.margin
(default 100): number of pixels by which to indent the
WebGL window.
snapshot
: Logical value: when autoprinting
in HTML, should a snapshot be used instead of the dynamic
WebGL display? Corresponds to rglwidget(snapshot = TRUE, webgl = FALSE)
. Ignored in LaTeX, where a snapshot
will always be produced (unless fig.keep
specifies
no figure at all).
dpi
, fig.retina
, fig.width
, fig.height
: standard knitr chunk
options used to set the size of the output.
fig.keep
, fig.hold
, fig.beforecode
: standard knitr chunk options used to control the
display of plots.
dev
: used by hook_rgl
to set
the output format. May be "eps"
, "postscript"
,
"pdf"
or "png"
(default: "png"
).
rgl.keepopen
: no longer used. Ignored
with a warning.
fig.alt
is partially supported: rgl
will always use the first entry if fig.alt
is a vector.
Other graphics types match the entries in fig.alt
to successive plots within the chunk.
(This is due to a limitation in knitr, and may
change in the future.)
A string to be embedded into the output, or NULL
if called
when no output is available.
The setupKnitr(autoprint = TRUE)
method assumes
all printing of RGL objects happens through
auto-printing of objects produced by the lowlevel
or highlevel
functions. All RGL functions that
produce graphics do this, but functions in other packages that
call them may not return values appropriately.
Mixing explicit calls
to rglwidget
with auto-printing is likely to lead to failure of some
scenes to display. To avoid this, set options(rgl.printRglwidget = FALSE)
before using such explicit calls. Similarly,
use that option before calling the example
function
in a code chunk if the example prints RGL objects.
The hook*
functions are originally by Yihui Xie in the knitr package; and have been modified by
Duncan Murdoch. Some parts of the setupKnitr
function
duplicate source code from knitr.
This function sets user mouse callbacks in R or
rglwidget
displays.
setUserCallbacks(button, begin = NULL, update = NULL, end = NULL, rotate = NULL, javascript = NULL, subscene = scene$rootSubscene$id, scene = scene3d(minimal = FALSE), applyToScene = TRUE, applyToDev = missing(scene))
setUserCallbacks(button, begin = NULL, update = NULL, end = NULL, rotate = NULL, javascript = NULL, subscene = scene$rootSubscene$id, scene = scene3d(minimal = FALSE), applyToScene = TRUE, applyToDev = missing(scene))
button |
Which button should this callback apply to? Can
be numeric from |
begin , update , end , rotate
|
Functions to call when events occur. See Details. |
javascript |
Optional block of Javascript code to be included (at the global level). |
subscene |
Which subscene do these callbacks apply to? |
scene |
Which scene? |
applyToScene |
Should these changes apply to the scene object? |
applyToDev |
Should these changes apply to the current device? |
If applyToScene
is TRUE
, this function adds Javascript
callbacks to the scene
object.
If applyToDev
is TRUE
, it adds R
callbacks to the current RGL device.
For Javascript,
the callbacks are specified as strings; these will be
evaluated within the browser in the global context to define the functions,
which will then be called with the Javascript
this
object set to the current
rglwidgetClass
object.
For R, they may be strings or R functions.
Both options may be TRUE
, in which case the
callbacks must be specified as strings which are
both valid Javascript and valid R. The usual way to
do this is to give just a function name, with the
function defined elsewhere, as in the Example below.
The begin
and update
functions should be
of the form
function(x, y) { ... }
. The end
function
will be called with no arguments.
The rotate
callback can only be set on the
mouse wheel. It is called when the mouse
wheel is rotated. It should be of the form
function(away)
, where away
will be 1
while rotating the wheel “away” from you,
and 2 while rotating it towards you. If rotate
is not set but other callbacks are set on the wheel
“button”, then each click of the mouse wheel
will trigger all start
, update
,
then end
calls in sequence.
The javascript
argument is an optional block
of code which will be evaluated once during the
initialization of the widget. It can define functions
and assign them as members of the window
object,
and then the names of those functions can be given
in the callback arguments; this allows the callbacks
to share information.
Invisibly returns an rglScene
object. This
object will record the changes if applyToScene
is TRUE
.
If applyToDev
is TRUE
, it will also
have the side effect of attempting to install the
callbacks using rgl.setMouseCallbacks
and rgl.setWheelCallback
.
Duncan Murdoch
setAxisCallbacks
for user defined axes.
# This example identifies points in both the rgl window and # in WebGL verts <- cbind(rnorm(11), rnorm(11), rnorm(11)) idverts <- plot3d(verts, type = "s", col = "blue")["data"] # Plot some invisible text; the Javascript will move it idtext <- text3d(verts[1,,drop = FALSE], texts = 1, adj = c(0.5, -1.5), alpha = 0) # Define the R functions to use within R fns <- local({ idverts <- idverts idtext <- idtext closest <- -1 update <- function(x, y) { save <- par3d(skipRedraw = TRUE) on.exit(par3d(save)) rect <- par3d("windowRect") size <- rect[3:4] - rect[1:2] x <- x / size[1]; y <- 1 - y / size[2]; verts <- rgl.attrib(idverts, "vertices") # Put in window coordinates vw <- rgl.user2window(verts) dists <- sqrt((x - vw[,1])^2 + (y - vw[,2])^2) newclosest <- which.min(dists) if (newclosest != closest) { if (idtext > 0) pop3d(id = idtext) closest <<- newclosest idtext <<- text3d(verts[closest,,drop = FALSE], texts = closest, adj = c(0.5, -1.5)) } } end <- function() { if (idtext > 0) pop3d(id = idtext) closest <<- -1 idtext <<- -1 } list(rglupdate = update, rglend = end) }) rglupdate <- fns$rglupdate rglend <- fns$rglend # Define the Javascript functions with the same names to use in WebGL js <- ' var idverts = %id%, idtext = %idtext%, closest = -1, subid = %subid%; window.rglupdate = function(x, y) { var obj = this.getObj(idverts), i, newdist, dist = Infinity, pt, newclosest; x = x/this.canvas.width; y = y/this.canvas.height; for (i = 0; i < obj.vertices.length; i++) { pt = obj.vertices[i].concat(1); pt = this.user2window(pt, subid); pt[0] = x - pt[0]; pt[1] = y - pt[1]; pt[2] = 0; newdist = rglwidgetClass.vlen(pt); if (newdist < dist) { dist = newdist; newclosest = i; } } if (newclosest !== closest) { closest = newclosest var text = this.getObj(idtext); text.vertices[0] = obj.vertices[closest]; text.colors[0][3] = 1; // alpha is here! text.texts[0] = (closest + 1).toString(); text.initialized = false; this.drawScene(); } }; window.rglend = function() { var text = this.getObj(idtext); closest = -1; text.colors[0][3] = 0; text.initialized = false; this.drawScene(); }' js <- sub("%id%", idverts, js) js <- sub("%subid%", subsceneInfo()$id, js) js <- sub("%idtext%", idtext, js) # Install both setUserCallbacks("left", begin = "rglupdate", update = "rglupdate", end = "rglend", javascript = js) rglwidget() # This example doesn't affect the rgl window, it only modifies # the scene object to implement panning # Define the Javascript functions to use in WebGL js <- ' window.subid = %subid%; window.panbegin = function(x, y) { var activeSub = this.getObj(subid), viewport = activeSub.par3d.viewport, activeModel = this.getObj(this.useid(activeSub.id, "model")), l = activeModel.par3d.listeners, i; this.userSave = {x:x, y:y, viewport:viewport, cursor:this.canvas.style.cursor}; for (i = 0; i < l.length; i++) { activeSub = this.getObj(l[i]); activeSub.userSaveMat = new CanvasMatrix4(activeSub.par3d.userMatrix); } this.canvas.style.cursor = "grabbing"; }; window.panupdate = function(x, y) { var objects = this.scene.objects, activeSub = this.getObj(subid), activeModel = this.getObj(this.useid(activeSub.id, "model")), l = activeModel.par3d.listeners, viewport = this.userSave.viewport, par3d, i, zoom; if (x === this.userSave.x && y === this.userSave.y) return; x = (x - this.userSave.x)/this.canvas.width; y = (y - this.userSave.y)/this.canvas.height; for (i = 0; i < l.length; i++) { activeSub = this.getObj(l[i]); par3d = activeSub.par3d; /* NB: The right amount of zoom depends on the scaling of the data and the position of the observer. This might need tweaking. */ zoom = rglwidgetClass.vlen(par3d.observer)*par3d.zoom; activeSub.par3d.userMatrix.load(objects[l[i]].userSaveMat); activeSub.par3d.userMatrix.translate(zoom*x, zoom*y, 0); } this.drawScene(); }; window.panend = function() { this.canvas.style.cursor = this.userSave.cursor; }; ' js <- sub("%subid%", subsceneInfo()$id, js) scene <- setUserCallbacks("left", begin = "panbegin", update = "panupdate", end = "panend", applyToDev = FALSE, javascript = js) rglwidget(scene)
# This example identifies points in both the rgl window and # in WebGL verts <- cbind(rnorm(11), rnorm(11), rnorm(11)) idverts <- plot3d(verts, type = "s", col = "blue")["data"] # Plot some invisible text; the Javascript will move it idtext <- text3d(verts[1,,drop = FALSE], texts = 1, adj = c(0.5, -1.5), alpha = 0) # Define the R functions to use within R fns <- local({ idverts <- idverts idtext <- idtext closest <- -1 update <- function(x, y) { save <- par3d(skipRedraw = TRUE) on.exit(par3d(save)) rect <- par3d("windowRect") size <- rect[3:4] - rect[1:2] x <- x / size[1]; y <- 1 - y / size[2]; verts <- rgl.attrib(idverts, "vertices") # Put in window coordinates vw <- rgl.user2window(verts) dists <- sqrt((x - vw[,1])^2 + (y - vw[,2])^2) newclosest <- which.min(dists) if (newclosest != closest) { if (idtext > 0) pop3d(id = idtext) closest <<- newclosest idtext <<- text3d(verts[closest,,drop = FALSE], texts = closest, adj = c(0.5, -1.5)) } } end <- function() { if (idtext > 0) pop3d(id = idtext) closest <<- -1 idtext <<- -1 } list(rglupdate = update, rglend = end) }) rglupdate <- fns$rglupdate rglend <- fns$rglend # Define the Javascript functions with the same names to use in WebGL js <- ' var idverts = %id%, idtext = %idtext%, closest = -1, subid = %subid%; window.rglupdate = function(x, y) { var obj = this.getObj(idverts), i, newdist, dist = Infinity, pt, newclosest; x = x/this.canvas.width; y = y/this.canvas.height; for (i = 0; i < obj.vertices.length; i++) { pt = obj.vertices[i].concat(1); pt = this.user2window(pt, subid); pt[0] = x - pt[0]; pt[1] = y - pt[1]; pt[2] = 0; newdist = rglwidgetClass.vlen(pt); if (newdist < dist) { dist = newdist; newclosest = i; } } if (newclosest !== closest) { closest = newclosest var text = this.getObj(idtext); text.vertices[0] = obj.vertices[closest]; text.colors[0][3] = 1; // alpha is here! text.texts[0] = (closest + 1).toString(); text.initialized = false; this.drawScene(); } }; window.rglend = function() { var text = this.getObj(idtext); closest = -1; text.colors[0][3] = 0; text.initialized = false; this.drawScene(); }' js <- sub("%id%", idverts, js) js <- sub("%subid%", subsceneInfo()$id, js) js <- sub("%idtext%", idtext, js) # Install both setUserCallbacks("left", begin = "rglupdate", update = "rglupdate", end = "rglend", javascript = js) rglwidget() # This example doesn't affect the rgl window, it only modifies # the scene object to implement panning # Define the Javascript functions to use in WebGL js <- ' window.subid = %subid%; window.panbegin = function(x, y) { var activeSub = this.getObj(subid), viewport = activeSub.par3d.viewport, activeModel = this.getObj(this.useid(activeSub.id, "model")), l = activeModel.par3d.listeners, i; this.userSave = {x:x, y:y, viewport:viewport, cursor:this.canvas.style.cursor}; for (i = 0; i < l.length; i++) { activeSub = this.getObj(l[i]); activeSub.userSaveMat = new CanvasMatrix4(activeSub.par3d.userMatrix); } this.canvas.style.cursor = "grabbing"; }; window.panupdate = function(x, y) { var objects = this.scene.objects, activeSub = this.getObj(subid), activeModel = this.getObj(this.useid(activeSub.id, "model")), l = activeModel.par3d.listeners, viewport = this.userSave.viewport, par3d, i, zoom; if (x === this.userSave.x && y === this.userSave.y) return; x = (x - this.userSave.x)/this.canvas.width; y = (y - this.userSave.y)/this.canvas.height; for (i = 0; i < l.length; i++) { activeSub = this.getObj(l[i]); par3d = activeSub.par3d; /* NB: The right amount of zoom depends on the scaling of the data and the position of the observer. This might need tweaking. */ zoom = rglwidgetClass.vlen(par3d.observer)*par3d.zoom; activeSub.par3d.userMatrix.load(objects[l[i]].userSaveMat); activeSub.par3d.userMatrix.translate(zoom*x, zoom*y, 0); } this.drawScene(); }; window.panend = function() { this.canvas.style.cursor = this.userSave.cursor; }; ' js <- sub("%subid%", subsceneInfo()$id, js) scene <- setUserCallbacks("left", begin = "panbegin", update = "panupdate", end = "panend", applyToDev = FALSE, javascript = js) rglwidget(scene)
setUserShaders
sets user-defined shaders (programs written in GLSL)
for customized display of RGL objects. Currently
only supported in WebGL displays, as the regular
displays do not support GLSL. getShaders
gets
the user defined shader, or if it is not present, the
automatically generated one.
setUserShaders(ids, vertexShader = NULL, fragmentShader = NULL, attributes = NULL, uniforms = NULL, textures = NULL, scene = scene3d(minimal), minimal = TRUE) getShaders(id, scene = scene3d(minimal), minimal = TRUE)
setUserShaders(ids, vertexShader = NULL, fragmentShader = NULL, attributes = NULL, uniforms = NULL, textures = NULL, scene = scene3d(minimal), minimal = TRUE) getShaders(id, scene = scene3d(minimal), minimal = TRUE)
ids , id
|
Which objects should receive the shaders, or which object should be queried? |
vertexShader , fragmentShader
|
The vertex and fragment shader source code. If |
attributes |
A named list of “attributes” to attach to each vertex. |
uniforms |
A named list of “uniforms”. |
textures |
A named list of textures. |
scene |
A |
minimal |
See |
Modern versions of OpenGL work with “shaders”, programs written to run on the graphics processor. The vertex shader does the calculations to move vertices and set their intrinsic colours. The fragment shader computes how each pixel in the display will be shown, taking into account lighting, material properties, etc. (More precisely, it does the computation for each “fragment”; a fragment is a pixel within an object to display. There may be many objects at a particular location, and each will result in a fragment calculation unless culled by z-buffering or being discarded in some other way.)
Normally the WebGL Javascript code uses the default
shaders stored in
system.file("htmlwidgets/lib/rglClass/shaders",
package = "rgl")
. This function allows them to be written
by hand, for testing new features, hand optimization, etc.
The defines used by the default shaders will also be prepended
to user shaders, which can use them for customization on
an object-by-object basis.
The names used for the attributes
, uniforms
and textures
should match names in the shaders
for corresponding variables. (The texture names should be
names of uniform sampler2D
variables.)
A modified version of the scene
.
The getShaders
function requires the V8 package
to extract auto-generated shaders, since the defines
are generated by Javascript code.
Duncan Murdoch
rglwidget
for display of the scene in WebGL.
open3d() id <- shade3d(octahedron3d(), col = "red") # For each triangle, set weights on the 3 vertices. # This will be replicated to the appropriate size in Javascript. wts <- diag(3) # This leaves out the centres of each face vs <- " attribute vec3 aPos; attribute vec4 aCol; uniform mat4 mvMatrix; uniform mat4 prMatrix; varying vec4 vCol; varying vec4 vPosition; attribute vec3 aNorm; uniform mat4 normMatrix; varying vec3 vNormal; attribute vec3 wts; varying vec3 vwts; void main(void) { vPosition = mvMatrix * vec4(aPos, 1.); gl_Position = prMatrix * vPosition; vCol = aCol; vNormal = normalize((normMatrix * vec4(aNorm, 1.)).xyz); vwts = wts; } " fs <- " #ifdef GL_ES precision highp float; #endif varying vec4 vCol; // carries alpha varying vec4 vPosition; varying vec3 vNormal; uniform mat4 mvMatrix; uniform vec3 emission; uniform float shininess; uniform vec3 ambient[NLIGHTS]; uniform vec3 specular[NLIGHTS]; // light*material uniform vec3 diffuse[NLIGHTS]; uniform vec3 lightDir[NLIGHTS]; uniform bool viewpoint[NLIGHTS]; uniform bool finite[NLIGHTS]; varying vec3 vwts; uniform vec2 wtrange; void main(void) { float minwt = min(vwts.x, min(vwts.y, vwts.z)); if (minwt < wtrange.x || minwt > wtrange.y) discard; vec3 eye = normalize(-vPosition.xyz); vec3 lightdir; vec4 colDiff; vec3 halfVec; vec4 lighteffect = vec4(emission, 0.); vec3 col; float nDotL; vec3 n = normalize(vNormal); n = -faceforward(n, n, eye); colDiff = vec4(vCol.rgb * diffuse[0], vCol.a); lightdir = lightDir[0]; if (!viewpoint[0]) lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz; if (!finite[0]) { halfVec = normalize(lightdir + eye); } else { lightdir = normalize(lightdir - vPosition.xyz); halfVec = normalize(lightdir + eye); } col = ambient[0]; nDotL = dot(n, lightdir); col = col + max(nDotL, 0.) * colDiff.rgb; col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[0]; lighteffect = lighteffect + vec4(col, colDiff.a); gl_FragColor = lighteffect; } " x <- setUserShaders(id, vs, fs, attributes = list(wts=wts), uniforms = list(wtrange = c(-0.01, 0.15))) if (interactive() || in_pkgdown_example()) rglwidget(x)
open3d() id <- shade3d(octahedron3d(), col = "red") # For each triangle, set weights on the 3 vertices. # This will be replicated to the appropriate size in Javascript. wts <- diag(3) # This leaves out the centres of each face vs <- " attribute vec3 aPos; attribute vec4 aCol; uniform mat4 mvMatrix; uniform mat4 prMatrix; varying vec4 vCol; varying vec4 vPosition; attribute vec3 aNorm; uniform mat4 normMatrix; varying vec3 vNormal; attribute vec3 wts; varying vec3 vwts; void main(void) { vPosition = mvMatrix * vec4(aPos, 1.); gl_Position = prMatrix * vPosition; vCol = aCol; vNormal = normalize((normMatrix * vec4(aNorm, 1.)).xyz); vwts = wts; } " fs <- " #ifdef GL_ES precision highp float; #endif varying vec4 vCol; // carries alpha varying vec4 vPosition; varying vec3 vNormal; uniform mat4 mvMatrix; uniform vec3 emission; uniform float shininess; uniform vec3 ambient[NLIGHTS]; uniform vec3 specular[NLIGHTS]; // light*material uniform vec3 diffuse[NLIGHTS]; uniform vec3 lightDir[NLIGHTS]; uniform bool viewpoint[NLIGHTS]; uniform bool finite[NLIGHTS]; varying vec3 vwts; uniform vec2 wtrange; void main(void) { float minwt = min(vwts.x, min(vwts.y, vwts.z)); if (minwt < wtrange.x || minwt > wtrange.y) discard; vec3 eye = normalize(-vPosition.xyz); vec3 lightdir; vec4 colDiff; vec3 halfVec; vec4 lighteffect = vec4(emission, 0.); vec3 col; float nDotL; vec3 n = normalize(vNormal); n = -faceforward(n, n, eye); colDiff = vec4(vCol.rgb * diffuse[0], vCol.a); lightdir = lightDir[0]; if (!viewpoint[0]) lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz; if (!finite[0]) { halfVec = normalize(lightdir + eye); } else { lightdir = normalize(lightdir - vPosition.xyz); halfVec = normalize(lightdir + eye); } col = ambient[0]; nDotL = dot(n, lightdir); col = col + max(nDotL, 0.) * colDiff.rgb; col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[0]; lighteffect = lighteffect + vec4(col, colDiff.a); gl_FragColor = lighteffect; } " x <- setUserShaders(id, vs, fs, attributes = list(wts=wts), uniforms = list(wtrange = c(-0.01, 0.15))) if (interactive() || in_pkgdown_example()) rglwidget(x)
Draws 3D mesh objects in full, or just the edges, or just the vertices.
dot3d(x, ...) # draw dots at the vertices of an object ## S3 method for class 'mesh3d' dot3d(x, ..., front = "points", back = "points") wire3d(x, ...) # draw a wireframe object ## S3 method for class 'mesh3d' wire3d(x, ..., front = "lines", back = "lines") shade3d(x, ...) # draw a shaded object ## S3 method for class 'mesh3d' shade3d(x, override = TRUE, meshColor = c("vertices", "edges", "faces", "legacy"), texcoords = NULL, ..., front = "filled", back = "filled")
dot3d(x, ...) # draw dots at the vertices of an object ## S3 method for class 'mesh3d' dot3d(x, ..., front = "points", back = "points") wire3d(x, ...) # draw a wireframe object ## S3 method for class 'mesh3d' wire3d(x, ..., front = "lines", back = "lines") shade3d(x, ...) # draw a shaded object ## S3 method for class 'mesh3d' shade3d(x, override = TRUE, meshColor = c("vertices", "edges", "faces", "legacy"), texcoords = NULL, ..., front = "filled", back = "filled")
x |
a |
... |
additional rendering parameters, or for
|
override |
should the parameters specified here override those stored in the object? |
meshColor |
how should colours be interpreted? See details below |
texcoords |
texture coordinates at each vertex. |
front , back
|
Material properties for rendering. |
The meshColor
argument controls how material colours and textures are interpreted. This parameter
was added in rgl version 0.100.1 (0.100.27 for dot3d
). Possible values are:
"vertices"
Colours and texture coordinates are applied by vertex, in the order
they appear in the x$vb
matrix.
"edges"
Colours are applied to each edge: first to the segments in the x$is
matrix, then
the
3 edges of each triangle in the x$it
matrix, then the 4
edges of each quad in the x$ib
matrix. This mode
is only supported if both front and back materials are
"lines"
, and the mesh contains no points.
"faces"
Colours are applied to each object in the mesh: first to the points, then the segments, triangles and finally quads. The entire whole face (or point or segment) receives one colour from the specified colours.
"legacy"
Colours and textures are applied in the same way as in rgl versions earlier than 0.100.1.
Unique partial matches of these values will be recognized.
If colours are specified but meshColor
is not
and options(rgl.meshColorWarning = TRUE)
,
a warning will be given that their
interpretation may have changed. In versions 0.100.1 to 0.100.26
of rgl, the default
was to give the warning; now the default is for no warning.
Note that since version 0.102.10, meshColor =
"edges"
is only allowed when drawing lines (the
wire3d
default), and it may draw
edges more than once. In general, if any rendering
draws twice at the same location, which copy is visible
depends on the order of drawing and the
material3d("depth_test")
setting.
Whether points, lines or solid faces are drawn is determined in 3 steps:
If arguments "front"
or "back"
are
specified in the call, those are used.
If one or both of those arguments are not specified, but the material properties are present in the object, those are used.
If values are not specified in either of those
places, shade3d
draws filled surfaces,
wire3d
draws lines, and dot3d
draws points.
Note: For some versions of rgl up to version 0.107.15, rule 2 above was not respected.
dot3d
, wire3d
, and shade3d
are called for their side effect
of drawing an object into the scene; they return an object ID (or vector of IDs) invisibly.
See primitives for a discussion of texture coordinates.
mesh3d
, par3d
, shapelist3d
for multiple shapes
# generate a quad mesh object vertices <- c( -1.0, -1.0, 0, 1.0, -1.0, 0, 1.0, 1.0, 0, -1.0, 1.0, 0 ) indices <- c( 1, 2, 3, 4 ) open3d() wire3d( mesh3d(vertices = vertices, quads = indices) ) # render 4 meshes vertically in the current view open3d() bg3d("gray") l0 <- oh3d(tran = par3d("userMatrix"), color = "green" ) shade3d( translate3d( l0, -6, 0, 0 )) l1 <- subdivision3d( l0 ) shade3d( translate3d( l1 , -2, 0, 0 ), color = "red", override = FALSE ) l2 <- subdivision3d( l1 ) shade3d( translate3d( l2 , 2, 0, 0 ), color = "red", override = TRUE ) l3 <- subdivision3d( l2 ) shade3d( translate3d( l3 , 6, 0, 0 ), color = "red" ) # render all of the Platonic solids open3d() shade3d( translate3d( tetrahedron3d(col = "red"), 0, 0, 0) ) shade3d( translate3d( cube3d(col = "green"), 3, 0, 0) ) shade3d( translate3d( octahedron3d(col = "blue"), 6, 0, 0) ) shade3d( translate3d( dodecahedron3d(col = "cyan"), 9, 0, 0) ) shade3d( translate3d( icosahedron3d(col = "magenta"), 12, 0, 0) )
# generate a quad mesh object vertices <- c( -1.0, -1.0, 0, 1.0, -1.0, 0, 1.0, 1.0, 0, -1.0, 1.0, 0 ) indices <- c( 1, 2, 3, 4 ) open3d() wire3d( mesh3d(vertices = vertices, quads = indices) ) # render 4 meshes vertically in the current view open3d() bg3d("gray") l0 <- oh3d(tran = par3d("userMatrix"), color = "green" ) shade3d( translate3d( l0, -6, 0, 0 )) l1 <- subdivision3d( l0 ) shade3d( translate3d( l1 , -2, 0, 0 ), color = "red", override = FALSE ) l2 <- subdivision3d( l1 ) shade3d( translate3d( l2 , 2, 0, 0 ), color = "red", override = TRUE ) l3 <- subdivision3d( l2 ) shade3d( translate3d( l3 , 6, 0, 0 ), color = "red" ) # render all of the Platonic solids open3d() shade3d( translate3d( tetrahedron3d(col = "red"), 0, 0, 0) ) shade3d( translate3d( cube3d(col = "green"), 3, 0, 0) ) shade3d( translate3d( octahedron3d(col = "blue"), 6, 0, 0) ) shade3d( translate3d( dodecahedron3d(col = "cyan"), 9, 0, 0) ) shade3d( translate3d( icosahedron3d(col = "magenta"), 12, 0, 0) )
Project a mesh onto a surface in a scene so that it appears to cast a shadow onto the surface.
shadow3d(obj, mesh, plot = TRUE, up = c(0, 0, 1), P = projectDown(up), outside = FALSE, ...)
shadow3d(obj, mesh, plot = TRUE, up = c(0, 0, 1), P = projectDown(up), outside = FALSE, ...)
obj |
The target object which will show the shadow. |
mesh |
The mesh which will cast the shadow. |
plot |
Whether to plot the result. |
up |
Which direction is “up”? |
P |
The projection to use for draping, a 4x4 matrix. See |
outside |
Should the function compute and (possibly) plot the region outside of the shadow? |
... |
Other arguments to pass to |
shadow3d
internally constructs a function that
is zero on the boundary of the shadow and positive inside,
then draws filled contours of that function. Because the
function is nonlinear, the boundaries will be approximate,
with the best approximation resulting from a large
value of filledContour3d
parameter minVertices
.
If outside = TRUE
, the first color used by
filledContour3d
will indicate the inside
of the shadow, and the second color will indicate the exterior.
The returned value from filledContour3d
.
Duncan Murdoch
open3d() obj <- translate3d(scale3d(oh3d(), 0.3, 0.3, 0.3), 0,0,2) shade3d(obj, col = "red") target <- icosahedron3d() # We offset the target using polygon_offset = 1 so that the # shadow on its surface will appear clearly. shade3d(target, col = "white", polygon_offset = 1) # minVertices = 1000 leaves noticeable artifacts on the edges # of the shadow. A larger value gives a better result, but is # slower. # We use facing3d(target) so the shadow and outside part only # appear on the upper side of the target shadow3d(facing3d(target), obj, minVertices = 1000, plot=TRUE, col = c("yellow", "blue"), outside = TRUE)
open3d() obj <- translate3d(scale3d(oh3d(), 0.3, 0.3, 0.3), 0,0,2) shade3d(obj, col = "red") target <- icosahedron3d() # We offset the target using polygon_offset = 1 so that the # shadow on its surface will appear clearly. shade3d(target, col = "white", polygon_offset = 1) # minVertices = 1000 leaves noticeable artifacts on the edges # of the shadow. A larger value gives a better result, but is # slower. # We use facing3d(target) so the shadow and outside part only # appear on the upper side of the target shadow3d(facing3d(target), obj, minVertices = 1000, plot=TRUE, col = c("yellow", "blue"), outside = TRUE)
These functions create and plot a list of shapes.
shapelist3d(shapes, x = 0, y = NULL, z = NULL, size = 1, matrix = NULL, override = TRUE, ..., plot = TRUE)
shapelist3d(shapes, x = 0, y = NULL, z = NULL, size = 1, matrix = NULL, override = TRUE, ..., plot = TRUE)
shapes |
A single |
x , y , z
|
Translation(s) to apply |
size |
Scaling(s) to apply |
matrix |
A single matrix transformation, or a list of them. |
override |
Whether the material properties should override the ones in the shapes. |
... |
Material properties to apply. |
plot |
Whether to plot the result. |
shapelist3d
is a quick way to create a complex object made up of simpler ones.
Each of the arguments shapes
through override
may be a vector of
values (a list in the case of shapes
or matrix
). All values
will be recycled to produce a list of shapes as long as the longest of them.
The xyz.coords
function will be used to process the x
,
y
and z
arguments, so a matrix may be used as x
to
specify all three. If a vector is used for x
but y
or z
is missing, default values of 0
will be used.
The "shapelist3d"
class is simply a list of "shape3d"
objects.
Methods for dot3d
, wire3d
, shade3d
,
translate3d
, scale3d
, and rotate3d
are defined for these objects.
An object of class c("shapelist3d", "shape3d")
.
Duncan Murdoch
open3d() shapelist3d(icosahedron3d(), x = rnorm(10), y = rnorm(10), z = rnorm(10), col = 1:5, size = 0.3)
open3d() shapelist3d(icosahedron3d(), x = rnorm(10), y = rnorm(10), z = rnorm(10), col = 1:5, size = 0.3)
These functions allow an RGL scene to be embedded in a Shiny app.
rglwidgetOutput(outputId, width = "512px", height = "512px") renderRglwidget(expr, env = parent.frame(), quoted = FALSE, outputArgs = list()) playwidgetOutput(outputId, width = "0px", height = "0px") renderPlaywidget(expr, env = parent.frame(), quoted = FALSE, outputArgs = list())
rglwidgetOutput(outputId, width = "512px", height = "512px") renderRglwidget(expr, env = parent.frame(), quoted = FALSE, outputArgs = list()) playwidgetOutput(outputId, width = "0px", height = "0px") renderPlaywidget(expr, env = parent.frame(), quoted = FALSE, outputArgs = list())
outputId |
The name for the control. |
width , height
|
Width and height to display the control. |
expr |
An R expression returning
a |
env |
The environment in which to evaluate |
quoted |
Is the expression already quoted? |
outputArgs |
A list containing arguments; see details below. |
Use rglwidgetOutput
or playwidgetOutput
as an output
object in a Shiny user
interface section; use renderRglwidget
or renderPlaywidget
as the render
function in the server section.
In a dynamic R Markdown document with runtime: shiny
, you
only call the render function, and may optionally pass width
and height
to the output function by putting them in
a list in outputArgs
. See the example below.
Used internally by Shiny.
Duncan Murdoch
## Not run: # This could be used in a dynamic R Markdown document. See # demo("shinyDemo") and demo("simpleShinyRgl") for Shiny apps. inputPanel( sliderInput("n", label = "n", min = 10, max = 100, value = 10, step = 10) ) renderRglwidget({ n <- input$n try(close3d()) plot3d(rnorm(n), rnorm(n), rnorm(n)) rglwidget() }, outputArgs = list(width = "auto", height = "300px")) ## End(Not run)
## Not run: # This could be used in a dynamic R Markdown document. See # demo("shinyDemo") and demo("simpleShinyRgl") for Shiny apps. inputPanel( sliderInput("n", label = "n", min = 10, max = 100, value = 10, step = 10) ) renderRglwidget({ n <- input$n try(close3d()) plot3d(rnorm(n), rnorm(n), rnorm(n)) rglwidget() }, outputArgs = list(width = "auto", height = "300px")) ## End(Not run)
These functions allow Shiny apps to read and write the par3d
settings that may have been modified by user interaction in the browser.
shinyGetPar3d(parameters, session, subscene = currentSubscene3d(cur3d()), tag = "") shinySetPar3d(..., session, subscene = currentSubscene3d(cur3d())) shinyResetBrush(session, brush)
shinyGetPar3d(parameters, session, subscene = currentSubscene3d(cur3d()), tag = "") shinySetPar3d(..., session, subscene = currentSubscene3d(cur3d())) shinyResetBrush(session, brush)
parameters |
A character vector naming the parameters to get. |
session |
The Shiny session object. |
subscene |
The subscene to which the parameters apply. Defaults to the currently active subscene in the R session. |
tag |
An arbitrary string or value which will be sent as part of the response. |
... |
A number of |
brush |
The name of a Shiny input element corresponding to
the |
Requesting information from the browser is a complicated process.
The shinyGetPar3d
function doesn't return the requested value,
it just submits a request for the value to be returned later in
input$par3d
, a reactive input. No action will result
except when a reactive observer depends on input$par3d
.
See the example code below.
The shinySetPar3d
function sends a message to the browser
asking it to change a particular parameter. The change will be
made immediately, without sending the full scene to the browser,
so should be reasonably fast.
These functions are called for their side effects, and don't return useful values.
The side effect of shinyGetPar3d
is to cause input$par3d
to be updated sometime later.
Besides the requested parameter values, input$par3d
will
contain a copy of the subscene
and tag
arguments.
The side effect of shinySetPar3d
is to send a message to the
browser to update its copy of the par3d
parameters
immediately.
R and the browser don't maintain a perfect match between the way
parameters are stored internally. The browser version of parameters
will be returned by shinyGetPar3d
and should be supplied to
shinySetPar3d
.
Duncan Murdoch
https://shiny.rstudio.com/articles/communicating-with-js.html describes the underlying mechanisms used by these two functions.
The rglwidget
argument shinySelectionInput
allows information about mouse selections
to be returned to R.
if (interactive() && !in_pkgdown_example() && requireNamespace("shiny")) { save <- options(rgl.useNULL = TRUE) xyz <- matrix(rnorm(300), ncol = 3) app = shiny::shinyApp( ui = shiny::bootstrapPage( shiny::actionButton("redraw", "Redraw"), rglwidgetOutput("rglPlot") ), server = function(input, output, session) { # This waits until the user to click on the "redraw" # button, then sends a request for the current userMatrix shiny::observeEvent(input$redraw, { shinyGetPar3d("userMatrix", session) }) # This draws the plot whenever input$par3d changes, # i.e. whenever a response to the request above is # received. output$rglPlot <- renderRglwidget({ if (length(rgl.dev.list())) close3d() col <- sample(colors(), 1) plot3d(xyz, col = col, type = "s", main = col) par3d(userMatrix = input$par3d$userMatrix) rglwidget() }) }) shiny::runApp(app) options(save) }
if (interactive() && !in_pkgdown_example() && requireNamespace("shiny")) { save <- options(rgl.useNULL = TRUE) xyz <- matrix(rnorm(300), ncol = 3) app = shiny::shinyApp( ui = shiny::bootstrapPage( shiny::actionButton("redraw", "Redraw"), rglwidgetOutput("rglPlot") ), server = function(input, output, session) { # This waits until the user to click on the "redraw" # button, then sends a request for the current userMatrix shiny::observeEvent(input$redraw, { shinyGetPar3d("userMatrix", session) }) # This draws the plot whenever input$par3d changes, # i.e. whenever a response to the request above is # received. output$rglPlot <- renderRglwidget({ if (length(rgl.dev.list())) close3d() col <- sample(colors(), 1) plot3d(xyz, col = col, type = "s", main = col) par3d(userMatrix = input$par3d$userMatrix) rglwidget() }) }) shiny::runApp(app) options(save) }
This function uses a bitmap of a standard 2D graphics plot as a texture on a quadrilateral. Default arguments are set up so that it will appear on the face of the bounding box of the current 3D plot, but optional arguments allow it to be placed anywhere in the scene.
show2d(expression, face = "z-", line = 0, reverse = FALSE, rotate = 0, x = NULL, y = NULL, z = NULL, width = 480, height = 480, filename = NULL, ignoreExtent = TRUE, color = "white", specular = "black", lit = FALSE, texmipmap = TRUE, texminfilter = "linear.mipmap.linear", expand = 1.03, texcoords = matrix(c(0, 1, 1, 0, 0, 0, 1, 1), ncol = 2), ...)
show2d(expression, face = "z-", line = 0, reverse = FALSE, rotate = 0, x = NULL, y = NULL, z = NULL, width = 480, height = 480, filename = NULL, ignoreExtent = TRUE, color = "white", specular = "black", lit = FALSE, texmipmap = TRUE, texminfilter = "linear.mipmap.linear", expand = 1.03, texcoords = matrix(c(0, 1, 1, 0, 0, 0, 1, 1), ncol = 2), ...)
expression |
Any plotting commands to produce a plot in standard graphics.
Ignored if |
face |
A character string defining which face of the bounding box to use. See Details below. |
line |
How far out from the bounding box should the quadrilateral
be placed? Uses same convention as |
reverse , rotate
|
Should the image be reversed or rotated? See Details below. |
x , y , z
|
Specific values to use to override |
width , height
|
Parameters to pass to |
filename |
A ‘.png’ file image to use as the texture. |
ignoreExtent |
Whether the quadrilateral should be ignored when computing the bounding box of the scene. |
color , specular , lit , texmipmap , texminfilter , ...
|
Material properties to use for the quadrilateral. |
expand |
Amount by which the quadrilateral is expanded outside the bounding box of the data. |
texcoords |
Coordinates on the image. Lower left
of the bitmap is |
The default arguments are chosen to make it easy to place
a 2D image on the face of the bounding box. If x
,
y
and z
are NULL
(the defaults),
face
will be used as a code for one of the
six faces of the bounding box. The first letter should
be "x"
, "y"
or "z"
; this defines
the axis perpendicular to the desired face. If the
second letter is "-"
or is missing, the face
will be chosen to be the face with the lower value
on that axis. Any other letter will use the opposite
face.
If any of x
, y
or z
is given, the
specified value will be used to replace the value calculated
above. Usually four values should be given, corresponding to
the coordinates of the lower left, lower right, upper right
and upper left of the destination for the image before
reverse
and rotate
are used. Fewer values
can be used for one or two coordinates; cbind
will be used to put together all 3 coordinates into
a 4 by 3 matrix (which will be returned as an attribute
of the result).
The bitmap plot will by default be oriented so that it is
properly oriented when viewed from the
direction of the higher values of the perpendicular coordinate,
and its lower left corner is at the lower value of the two
remaining coordinates. The argument reverse
causes
the orientation to be mirrored, and rotate
causes it
to be rotated by multiples of 90 degrees. rotate
should be an integer,
with 0
for no rotation, 1
for a 90 degree
counter-clockwise rotation, etc.
The width
and height
arguments control the shape
and resolution of the bitmap. The defaults give a square
bitmap, which is appropriate with the usual c(1,1,1)
aspect ratios (see aspect3d
). Some tuning may be
needed to choose the resolution. The plot will look best when
displayed at its original size; shrinking it smaller tends to make
it look faded, while expanding it bigger will make it look
blurry. If filename
is given, the width and height
will be taken from the file, and width
and height
arguments will be ignored.
Invisibly returns the id value of the quadrilateral, with the following attributes:
value |
The value returned by |
xyz |
A 4 by 3 matrix giving the coordinates of the corners as used in plotting. |
texcoords |
A 4 by 2 matrix giving the texture coordinates of the image. |
filename |
The filename for the temporary file holding the bitmap image. |
Duncan Murdoch
bgplot3d
uses a plot as the background for the window.
example(plot3d, ask = FALSE) show2d({ par(mar=c(0,0,0,0)) plot(x, y, col = rainbow(1000), axes=FALSE) })
example(plot3d, ask = FALSE) show2d({ par(mar=c(0,0,0,0)) plot(x, y, col = rainbow(1000), axes=FALSE) })
Saves the screenshot to a file.
rgl.snapshot( filename, fmt = "png", top = TRUE ) snapshot3d( filename = tempfile(fileext = ".png"), fmt = "png", top = TRUE, ..., scene, width = NULL, height = NULL, webshot = as.logical(Sys.getenv("RGL_USE_WEBSHOT", "TRUE")) )
rgl.snapshot( filename, fmt = "png", top = TRUE ) snapshot3d( filename = tempfile(fileext = ".png"), fmt = "png", top = TRUE, ..., scene, width = NULL, height = NULL, webshot = as.logical(Sys.getenv("RGL_USE_WEBSHOT", "TRUE")) )
filename |
path to file to save. |
fmt |
image export format, currently supported: png. Ignored if |
top |
whether to call |
... |
arguments to pass to |
scene |
|
width , height
|
optional specifications of output size in pixels |
webshot |
Use the webshot2 package to take the snapshot |
rgl.snapshot()
is a low-level function
that copies the current RGL window from the screen. Users
should usually use snapshot3d()
instead; it is more flexible,
and (if webshot2 is installed) can take images even if
no window is showing, and they can be larger than the physical
screen. On some systems webshot2 doesn't work reliably; if
you find snapshot3d()
failing or taking a very long time
I'd recommend using snapshot3d(..., webshot = FALSE)
. See
the note below about RGL_USE_WEBSHOT
to make this the default.
Animations can be created in a loop modifying the scene and saving
each screenshot to a file. Various graphics programs (e.g. ImageMagick)
can put these together into a single animation. (See movie3d
or
the example below.)
These functions are mainly called for the side effects. The filename of the saved file is returned invisibly.
When rgl.useNULL()
is TRUE
, only webshot = TRUE
will produce a snapshot. It requires the webshot2
package and a Chrome browser. If no suitable browser is
found, snapshot3d()
will revert to rgl.snapshot()
.
To override the automatic search, set
environment variable CHROMOTE_CHROME
to the path to a suitable browser.
rgl.snapshot
works by taking an image from the displayed
window on-screen.
On some systems, the snapshot
will include content from other windows if they cover the active RGL
window. Setting top = TRUE
(the default) will use
rgl.bringtotop
before the snapshot
to avoid this.
There are likely limits to how large width
and
height
can be set based on the display hardware; if these
are exceeded the results are undefined. A typical result
is that the snapshot will still be made but at a smaller
size.
There are slight differences between the displays with
webshot = TRUE
and webshot = FALSE
, as the
former are rendered using WebGL while the latter are rendered
using OpenGL. Often the webshot = TRUE
displays
have better quality, but they are usually slower to
produce, sometimes drastically so.
Set the environment
variable RGL_USE_WEBSHOT to "FALSE"
if
you want rgl.snapshot
to be used by default.
if (interactive() && !in_pkgdown_example()) { saveopts <- options(rgl.useNULL = TRUE) plot3d(matrix(rnorm(300), ncol = 3, dimnames = list(NULL, c("x", "y", "z"))), col = "red") options(saveopts) browseURL(snapshot3d()) } ## Not run: # # create animation # shade3d(oh3d(), color = "red") rgl.bringtotop() view3d(0, 20) olddir <- setwd(tempdir()) for (i in 1:45) { view3d(i, 20) filename <- paste("pic", formatC(i, digits = 1, flag = "0"), ".png", sep = "") snapshot3d(filename) } ## Now run ImageMagick in tempdir(). Use 'convert' instead of 'magick' ## if you have an older version of ImageMagick: ## magick -delay 10 *.png -loop 0 pic.gif setwd(olddir) ## End(Not run)
if (interactive() && !in_pkgdown_example()) { saveopts <- options(rgl.useNULL = TRUE) plot3d(matrix(rnorm(300), ncol = 3, dimnames = list(NULL, c("x", "y", "z"))), col = "red") options(saveopts) browseURL(snapshot3d()) } ## Not run: # # create animation # shade3d(oh3d(), color = "red") rgl.bringtotop() view3d(0, 20) olddir <- setwd(tempdir()) for (i in 1:45) { view3d(i, 20) filename <- paste("pic", formatC(i, digits = 1, flag = "0"), ".png", sep = "") snapshot3d(filename) } ## Now run ImageMagick in tempdir(). Use 'convert' instead of 'magick' ## if you have an older version of ImageMagick: ## magick -delay 10 *.png -loop 0 pic.gif setwd(olddir) ## End(Not run)
Adds a sphere set shape node to the scene
spheres3d(x, y = NULL, z = NULL, radius = 1, fastTransparency = TRUE, ...)
spheres3d(x, y = NULL, z = NULL, radius = 1, fastTransparency = TRUE, ...)
x , y , z
|
Numeric vector of point coordinates corresponding to
the center of each sphere. Any reasonable way of defining the
coordinates is acceptable. See the function |
radius |
Vector or single value defining the sphere radius/radii |
fastTransparency |
logical value indicating whether fast sorting should be used for transparency. See the Details. |
... |
Material properties. See |
If a non-isometric aspect ratio is chosen, these functions will still draw
objects that appear to the viewer to be spheres. Use ellipse3d
to draw shapes that are spherical in the data scale.
When the scale is not isometric, the radius is measured in an average scale. In this case the bounding box calculation is iterative, since rescaling the plot changes the shape of the spheres in user-coordinates, which changes the bounding box. Versions of rgl prior to 0.92.802 did not do this iterative adjustment.
If any coordinate or radius is NA
, the sphere is not plotted.
If a texture is used, its bitmap is wrapped around the sphere, with the top edge at the maximum y coordinate, and the left-right edges joined at the maximum in the z coordinate, centred in x.
If the alpha
material value of the spheres is less than
the default 1
, they need to be drawn in order from
back to front. When fastTransparency
is TRUE
, this is approximated by sorting the centers and drawing
complete spheres in that order. This produces acceptable
results in most cases, but artifacts may be visible,
especially if the radius
values vary, or they
intersect other transparent objects. Setting
fastTransparency = FALSE
will cause the sorting
to apply to each of the 480 facets of individual spheres.
This is much slower, but may produce better
output.
A shape ID of the spheres object is returned.
material3d
, aspect3d
for setting non-isometric scales
open3d() spheres3d(rnorm(10), rnorm(10), rnorm(10), radius = runif(10), color = rainbow(10))
open3d() spheres3d(rnorm(10), rnorm(10), rnorm(10), radius = runif(10), color = rainbow(10))
This creates a function to use with play3d
to
spin an RGL scene at a fixed rate.
spin3d(axis = c(0, 0, 1), rpm = 5, dev = cur3d(), subscene = par3d("listeners", dev = dev))
spin3d(axis = c(0, 0, 1), rpm = 5, dev = cur3d(), subscene = par3d("listeners", dev = dev))
axis |
The desired axis of rotation |
rpm |
The rotation speed in rotations per minute |
dev |
Which RGL device to use |
subscene |
Which subscene to use |
A function with header function(time, base = M)
, where M
is the result of par3d("userMatrix")
at the time the function
is created. This function
calculates and returns a list containing userMatrix
updated by
spinning the base matrix for time
seconds at rpm
revolutions per
minute about the specified axis
.
Prior to rgl version 0.95.1476, the
subscene
argument defaulted to the
current subscene, and any additional entries would
be ignored by play3d
. The current
default value of par3d("listeners", dev = dev)
means that all subscenes that share
mouse responses will also share modifications
by this function.
Duncan Murdoch
play3d
to play the animation
# Spin one object open3d() plot3d(oh3d(col = "lightblue", alpha = 0.5)) if (!rgl.useNULL() && interactive()) play3d(spin3d(axis = c(1, 0, 0), rpm = 30), duration = 2) # Show spinning sprites, and rotate the whole view open3d() spriteid <- NULL spin1 <- spin3d(rpm = 4.5 ) # the scene spinner spin2 <- spin3d(rpm = 9 ) # the sprite spinner f <- function(time) { par3d(skipRedraw = TRUE) # stops intermediate redraws on.exit(par3d(skipRedraw = FALSE)) # redraw at the end pop3d(id = spriteid) # delete the old sprite cubeid <- shade3d(cube3d(), col = "red") spriteid <<- sprites3d(0:1, 0:1, 0:1, shape = cubeid, userMatrix = spin2(time, base = spin1(time)$userMatrix)$userMatrix) spin1(time) } if (!rgl.useNULL() && interactive()) play3d(f, duration = 2)
# Spin one object open3d() plot3d(oh3d(col = "lightblue", alpha = 0.5)) if (!rgl.useNULL() && interactive()) play3d(spin3d(axis = c(1, 0, 0), rpm = 30), duration = 2) # Show spinning sprites, and rotate the whole view open3d() spriteid <- NULL spin1 <- spin3d(rpm = 4.5 ) # the scene spinner spin2 <- spin3d(rpm = 9 ) # the sprite spinner f <- function(time) { par3d(skipRedraw = TRUE) # stops intermediate redraws on.exit(par3d(skipRedraw = FALSE)) # redraw at the end pop3d(id = spriteid) # delete the old sprite cubeid <- shade3d(cube3d(), col = "red") spriteid <<- sprites3d(0:1, 0:1, 0:1, shape = cubeid, userMatrix = spin2(time, base = spin1(time)$userMatrix)$userMatrix) spin1(time) } if (!rgl.useNULL() && interactive()) play3d(f, duration = 2)
Adds a sprite set shape node to the scene.
sprites3d(x, y = NULL, z = NULL, radius = 1, shapes = NULL, userMatrix, fixedSize = FALSE, adj = 0.5, pos = NULL, offset = 0.25, rotating = FALSE, ...) particles3d(x, y = NULL, z = NULL, radius = 1, ...)
sprites3d(x, y = NULL, z = NULL, radius = 1, shapes = NULL, userMatrix, fixedSize = FALSE, adj = 0.5, pos = NULL, offset = 0.25, rotating = FALSE, ...) particles3d(x, y = NULL, z = NULL, radius = 1, ...)
x , y , z
|
point coordinates. Any reasonable way of defining the
coordinates is acceptable. See the function |
radius |
vector or single value defining the sprite radius |
shapes |
|
userMatrix |
if |
fixedSize |
should sprites remain at a fixed size, or resize with the scene? |
adj , pos , offset
|
positioning arguments; see Details |
rotating |
should sprites remain at a fixed orientation, or rotate with the scene? |
... |
material properties when |
Simple sprites (used when shapes
is NULL
) are 1 by 1 squares
that are directed towards the viewpoint. Their primary use is for
fast (and faked) atmospherical effects, e.g. particles and clouds
using alpha blended textures. Particles are sprites using an alpha-blended
particle texture giving the illusion of clouds and gases.
The centre of each square will by default be at the
coordinates given by x, y, z
. This may be adjusted
using the adj
or pos
parameters.
adj
and pos
are treated similarly to the same
parameters for text3d
. adj
has 3
entries, for adjustment to the x
, y
and
z
coordinates respectively. For x
, a value
of 0 puts the sprite to the right of the specified point,
0.5 centers it there, and 1 puts it to the left. The other
coordinates are similar. By default, each value is 0.5 and
the sprites are centered at the points given by (x, y, z)
.
The pos
parameter overrides adj
. It should
be an integer or vector of integers (one per point),
interpreted as in text3d
to position the
sprite relative to the (x, y, z)
point: 0 is centered on it, 1 is below,
2 is to the left, 3 is above, 4 is to the right, 5 is in
front, and 6 is behind. offset
is the
fraction of the sprite size to separate it from the point.
When shapes
is not NULL
, it should be a vector of
identifiers of objects to plot in the scene (e.g. as returned by
plotting functions or by ids3d
), or a list of such vectors. The referenced objects will
be removed from the scene and duplicated as sprite images in a
constant orientation, as specified by userMatrix
. By default the
origin (0, 0, 0)
will be plotted at the coordinates given by (x, y, z)
, perhaps modified by adj
or pos
.
If shapes
is a vector all entries in it will be plotted at every
location. If shapes
is a list, different shapes
will be plotted at each location. All entries in list
entry 1 will be plotted
at location 1, all entries in entry 2 at location 2, etc.
Entries will be recycled as needed.
The userMatrix
argument is ignored for shapes = NULL
. For
shapes, sprites3d
defaults the matrix to r3dDefaults$userMatrix
.
If any coordinate is NA
, the sprite is not plotted.
The id values of the shapes may be retrieved after plotting
using rgl.attrib(id, "ids")
, the associated entry
in shapes
is retrievable in rgl.attrib(id, "shapenum")
, and
the user matrix is retrieved using rgl.attrib(id, "usermatrix")
.
These functions are called for the side effect of displaying the sprites. The shape ID of the displayed object is returned.
While any rgl objects can be used as 3D sprites, spheres
produced by spheres3d
and other objects that
adapt to the coordinate system
may not render properly. To plot spheres, construct them
as mesh objects as shown in the example.
open3d() particles3d( rnorm(100), rnorm(100), rnorm(100), color = rainbow(100) ) # is the same as sprites3d( rnorm(100), rnorm(100), rnorm(100), color = rainbow(100), lit = FALSE, alpha = .2, textype = "alpha", texture = system.file("textures/particle.png", package = "rgl") ) sprites3d( rnorm(10) + 6, rnorm(10), rnorm(10), shape = shade3d(tetrahedron3d(), col = "red") ) open3d() # Since the symbols are objects in the scene, they need # to be added to the scene after calling plot3d() plot3d(iris, type = "n") # Use list(...) to apply different symbols to different points. symbols <- list(shade3d(cube3d(), col = "red"), shade3d(tetrahedron3d(), col = "blue"), # Construct spheres shade3d(addNormals(subdivision3d(icosahedron3d(), 2)), col = "yellow")) sprites3d(iris, shape = symbols[iris$Species], radius = 0.1)
open3d() particles3d( rnorm(100), rnorm(100), rnorm(100), color = rainbow(100) ) # is the same as sprites3d( rnorm(100), rnorm(100), rnorm(100), color = rainbow(100), lit = FALSE, alpha = .2, textype = "alpha", texture = system.file("textures/particle.png", package = "rgl") ) sprites3d( rnorm(10) + 6, rnorm(10), rnorm(10), shape = shade3d(tetrahedron3d(), col = "red") ) open3d() # Since the symbols are objects in the scene, they need # to be added to the scene after calling plot3d() plot3d(iris, type = "n") # Use list(...) to apply different symbols to different points. symbols <- list(shade3d(cube3d(), col = "red"), shade3d(tetrahedron3d(), col = "blue"), # Construct spheres shade3d(addNormals(subdivision3d(icosahedron3d(), 2)), col = "yellow")) sprites3d(iris, shape = symbols[iris$Species], radius = 0.1)
The subdivision surface algorithm divides and refines (deforms) a given mesh recursively to certain degree (depth). The mesh3d algorithm consists of two stages: divide and deform. The divide step generates for each triangle or quad four new triangles or quads, the deform step drags the points (refinement step).
subdivision3d( x, ...) ## S3 method for class 'mesh3d' subdivision3d( x, depth = 1, normalize = FALSE, deform = TRUE, keepTags = FALSE, ... ) divide.mesh3d(mesh, vb = mesh$vb, ib = mesh$ib, it = mesh$it, is = mesh$is, keepTags = FALSE) normalize.mesh3d(mesh) deform.mesh3d(mesh, vb = mesh$vb, ib = mesh$ib, it = mesh$it, is = mesh$is)
subdivision3d( x, ...) ## S3 method for class 'mesh3d' subdivision3d( x, depth = 1, normalize = FALSE, deform = TRUE, keepTags = FALSE, ... ) divide.mesh3d(mesh, vb = mesh$vb, ib = mesh$ib, it = mesh$it, is = mesh$is, keepTags = FALSE) normalize.mesh3d(mesh) deform.mesh3d(mesh, vb = mesh$vb, ib = mesh$ib, it = mesh$it, is = mesh$is)
x |
3d geometry mesh |
mesh |
3d geometry mesh |
depth |
recursion depth |
normalize |
normalize mesh3d coordinates after division if |
deform |
deform mesh |
keepTags |
if |
is |
indices for segments |
it |
indices for triangular faces |
ib |
indices for quad faces |
vb |
matrix of vertices: 4 x n matrix (rows x, y, z, h) or equivalent vector, where h indicates scaling of each plotted quad |
... |
other arguments (unused) |
subdivision3d
takes
a mesh object and replaces each segment with two new ones, and
each triangle or quad with 4 new ones
by adding vertices half-way along the edges (and one in the
centre of a quad). The positions of the vertices are
deformed so that the resulting surface is smoother than the original. These operations are repeated depth
times.
The other functions do the individual steps of the subdivision.
divide.mesh3d
adds the extra vertices. deform.mesh3d
does the smoothing by replacing each vertex with the average of each of its neighbours. normalize.mesh3d
normalizes the homogeneous coordinates, by setting the
4th coordinate to 1. (The 4th coordinate is used as a
weight in the deform step.)
A modified mesh3d
object. If keepTags
is
TRUE
, it will contain a tags
component. For
details, see the clipMesh3d
help topic.
open3d() shade3d( subdivision3d( cube3d(), depth = 3 ), color = "red", alpha = 0.5 )
open3d() shade3d( subdivision3d( cube3d(), depth = 3 ), color = "red", alpha = 0.5 )
This creates a new subscene, or selects one by id
value, or adds objects to one.
newSubscene3d(viewport = "replace", projection = "replace", model = "replace", mouseMode = "inherit", parent = currentSubscene3d(), copyLights = TRUE, copyShapes = FALSE, copyBBoxDeco = copyShapes, copyBackground = FALSE, newviewport, ignoreExtent) currentSubscene3d(dev = cur3d()) useSubscene3d(subscene) addToSubscene3d(ids = tagged3d(tags), tags, subscene = currentSubscene3d()) delFromSubscene3d(ids = tagged3d(tags), tags, subscene = currentSubscene3d()) gc3d(protect = NULL)
newSubscene3d(viewport = "replace", projection = "replace", model = "replace", mouseMode = "inherit", parent = currentSubscene3d(), copyLights = TRUE, copyShapes = FALSE, copyBBoxDeco = copyShapes, copyBackground = FALSE, newviewport, ignoreExtent) currentSubscene3d(dev = cur3d()) useSubscene3d(subscene) addToSubscene3d(ids = tagged3d(tags), tags, subscene = currentSubscene3d()) delFromSubscene3d(ids = tagged3d(tags), tags, subscene = currentSubscene3d()) gc3d(protect = NULL)
viewport , projection , model , mouseMode
|
How should the new subscene be embedded? Possible values are
|
parent |
The parent subscene (defaults to the current subscene). |
copyLights , copyShapes , copyBBoxDeco , copyBackground
|
Whether lights, shapes, bounding box decorations and background should be copied to the new subscene. |
newviewport |
Optionally specify the new subscene's viewport (in pixels). |
ignoreExtent |
Whether to ignore the subscene's bounding box when calculating the parent bounding
box. Defaults to |
dev |
Which RGL device to query for the current subscene. |
subscene |
Which subscene to use or modify. |
ids |
A vector of integer object ids to add to the subscene. |
tags |
Alternate way to specify |
protect |
Object ids to protect from this garbage collection. |
The rgl package allows multiple windows to be open; each one corresponds to a “scene”. Within each scene there are one or more “subscenes”. Each subscene corresponds to a rectangular region in the window, and may have its own projection, transformation and behaviour in response to the mouse.
There is always a current subscene: most graphic operations make changes there, e.g. by adding an object to it.
The scene “owns” objects; addToSubscene3d
and delFromSubscene3d
put their ids into or remove them from the list being displayed within a particular subscene.
The gc3d
function deletes objects from the scene if they are not visible in any
subscene, unless they are protected by having their id included in protect
.
The viewport
, projection
and model
parameters each have three possible settings:
c("inherit", "modify", "replace")
. "inherit"
means that the corresponding value
from the parent subscene will be used. "replace"
means that the new subscene will
have its own value of the value, independent of its parent. "modify"
means that the
child value will be applied first, and then the parent value will be applied. For viewport, this
means that if the parent viewport is changed, the child will maintain its relative position. For
the two matrices, "modify"
is unlikely to give satisfactory results, but it is available
for possible use.
The mouseMode
parameter
can only be one of c("inherit", "replace")
. If
it is "inherit"
, the subscene will use the mouse
controls of the parent, and any change to them will
affect the parent and all children that inherit from it.
This is the behaviour that was present before rgl
version 0.100.13. If it is "replace"
, then
it will receive a copy of the parent mouse controls,
but modifications to them will affect only this
subscene, not the parent. Note that this is orthogonal
to the par3d("listeners")
setting:
if another subscene is listed as a listener, it will respond
to mouse actions using the same mode as the one receiving
them.
The viewport
parameter controls the rectangular region in which the subscene is displayed.
It is specified using newviewport
(in pixels relative to the whole window), or set to
match the parent viewport.
The projection
parameter controls settings corresponding to the observer. These
include the field of view and the zoom; they also include the position of the observer relative to
the model. The par3d("projMatrix")
matrix is determined by the projection.
The model
parameter controls settings corresponding to the model. Mouse rotations affect
the model, as does scaling. The par3d("modelMatrix")
matrix is determined by these as
well as by the position of the observer (since OpenGL assumes that the observer is at
(0, 0, 0) after the MODELVIEW transformation). Only those parts concerning the model are
inherited when model
specifies inheritance, the observer setting is controlled by
projection
.
If copyBackground
is TRUE
, the background of the newly created child will
overwrite anything displayed in the parent subscene, regardless of depth.
If successful, each function returns the object id of the subscene, with the exception of
gc3d
, which returns the count of objects which have been deleted,
and useSubscene3d
, which returns the previously active subscene id.
Duncan Murdoch and Fang He.
subsceneInfo
for information about a subscene,
mfrow3d
and layout3d
to set up
multiple panes of subscenes.
# Show the Earth with a cutout by using clipplanes in subscenes lat <- matrix(seq(90, -90, length.out = 50)*pi/180, 50, 50, byrow = TRUE) long <- matrix(seq(-180, 180, length.out = 50)*pi/180, 50, 50) r <- 6378.1 # radius of Earth in km x <- r*cos(lat)*cos(long) y <- r*cos(lat)*sin(long) z <- r*sin(lat) open3d() obj <- surface3d(x, y, z, col = "white", texture = system.file("textures/worldsmall.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z) cols <- c(rep("chocolate4", 4), rep("burlywood1", 4), "darkgoldenrod1") rs <- c(6350, 5639, 4928.5, 4207, 3486, (3486 + 2351)/2, 2351, (2351 + 1216)/2, 1216) for (i in seq_along(rs)) obj <- c(obj, spheres3d(0, 0, col = cols[i], radius = rs[i])) root <- currentSubscene3d() newSubscene3d("inherit", "inherit", "inherit", copyShapes = TRUE, parent = root) clipplanes3d(1, 0, 0, 0) newSubscene3d("inherit", "inherit", "inherit", copyShapes = TRUE, parent = root) clipplanes3d(0, 1, 0, 0) newSubscene3d("inherit", "inherit", "inherit", copyShapes = TRUE, parent = root) clipplanes3d(0, 0, 1, 0) # Now delete the objects from the root subscene, to reveal the clipping planes useSubscene3d(root) delFromSubscene3d(obj)
# Show the Earth with a cutout by using clipplanes in subscenes lat <- matrix(seq(90, -90, length.out = 50)*pi/180, 50, 50, byrow = TRUE) long <- matrix(seq(-180, 180, length.out = 50)*pi/180, 50, 50) r <- 6378.1 # radius of Earth in km x <- r*cos(lat)*cos(long) y <- r*cos(lat)*sin(long) z <- r*sin(lat) open3d() obj <- surface3d(x, y, z, col = "white", texture = system.file("textures/worldsmall.png", package = "rgl"), specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "", normal_x = x, normal_y = y, normal_z = z) cols <- c(rep("chocolate4", 4), rep("burlywood1", 4), "darkgoldenrod1") rs <- c(6350, 5639, 4928.5, 4207, 3486, (3486 + 2351)/2, 2351, (2351 + 1216)/2, 1216) for (i in seq_along(rs)) obj <- c(obj, spheres3d(0, 0, col = cols[i], radius = rs[i])) root <- currentSubscene3d() newSubscene3d("inherit", "inherit", "inherit", copyShapes = TRUE, parent = root) clipplanes3d(1, 0, 0, 0) newSubscene3d("inherit", "inherit", "inherit", copyShapes = TRUE, parent = root) clipplanes3d(0, 1, 0, 0) newSubscene3d("inherit", "inherit", "inherit", copyShapes = TRUE, parent = root) clipplanes3d(0, 0, 1, 0) # Now delete the objects from the root subscene, to reveal the clipping planes useSubscene3d(root) delFromSubscene3d(obj)
This function retrieves information about the tree of subscenes shown in the active window.
subsceneInfo(id = NA, embeddings, recursive = FALSE)
subsceneInfo(id = NA, embeddings, recursive = FALSE)
id |
Which subscene to report on; |
embeddings |
Optional new setting for the embeddings for this subscene. |
recursive |
Whether to report on children recursively. |
In RGL, each window contains a tree of “subscenes”, each containing views of a subset of the objects defined in the window.
Rendering in each subscene depends on the viewport, the projection, and the model
transformation. Each of these characteristics may be inherited from the parent
(embedding[i] = "inherit"
), may modify the parent (embedding[i] = "modify"
),
or may replace the parent (embedding[i] == "replace"
). All three must be
specified if embeddings
is used.
id |
The object id of the subscene |
parent |
The object id of the parent subscene, if any |
children |
If |
embedding |
A vector of 3 components describing how this subscene is embedded in its parent. |
Duncan Murdoch
example(plot3d) subsceneInfo()
example(plot3d) subsceneInfo()
Adds a surface to the current scene. The surface is defined by a matrix defining the height of each grid point and two vectors or matrices defining the grid.
surface3d(x, y, z, ..., normal_x = NULL, normal_y = NULL, normal_z = NULL, texture_s=NULL, texture_t=NULL, flip = FALSE)
surface3d(x, y, z, ..., normal_x = NULL, normal_y = NULL, normal_z = NULL, texture_s=NULL, texture_t=NULL, flip = FALSE)
x , y , z
|
vectors or matrices of values. See Details. |
... |
Material properties. See |
normal_x , normal_y , normal_z
|
matrices giving the coordinates of normals at each grid point |
texture_s , texture_t
|
matrices giving the texture coordinates at each grid point |
flip |
flip definition of “up” |
Adds a surface mesh to the current scene. The surface is
typically defined by a matrix of height values in z
(as in persp
),
but any of x
, y
, or z
may be matrices or
vectors, as long as at least one is a matrix. (One
historical exception is allowed: if all are vectors but
the length of z
is the product of the lengths of
x
and y
, z
is converted to a matrix.)
Dimensions of all matrices must match.
If any of the coordinates are vectors, they are interpreted as follows:
If x
is a vector, it corresponds to rows of the matrix.
If y
is a vector, it corresponds to columns
of the matrix.
If z
is a vector, it corresponds to columns
unless y
is also a vector, in which case it corresponds
to rows.
If the normals are not supplied, they will be calculated automatically based on neighbouring points.
Texture coordinates run from 0 to 1 over each dimension of the texture bitmap. If texture coordinates are not supplied, they will be calculated to render the texture exactly once over the grid. Values greater than 1 can be used to repeat the texture over the surface.
surface3d
always tries to draw the surface with the ‘front’ upwards
(typically towards higher z
values). This can be used to render
the top and bottom differently; see material3d
and
the example below. If you don't like its choice, set
flip = TRUE
to use the opposition definition.
NA
values in the height matrix are not drawn.
See persp3d
for a higher level interface.
# # volcano example taken from "persp" # z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(z) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d() surface3d(x, y, z, color = col, back = "lines")
# # volcano example taken from "persp" # z <- 2 * volcano # Exaggerate the relief x <- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y <- 10 * (1:ncol(z)) # 10 meter spacing (E to W) zlim <- range(z) zlen <- zlim[2] - zlim[1] + 1 colorlut <- terrain.colors(zlen) # height color lookup table col <- colorlut[ z - zlim[1] + 1 ] # assign colors to heights for each point open3d() surface3d(x, y, z, color = col, back = "lines")
Objects with material properties may have an arbitrary string set as a tag. This function retrieves the id values associated with a given tag, or the tags set on given ids.
tagged3d(tags = NULL, ids = NULL, full = FALSE, subscene = 0)
tagged3d(tags = NULL, ids = NULL, full = FALSE, subscene = 0)
tags |
A vector of tags to use for selection. |
ids |
A vector of ids to report the tags on. |
full |
logical; whether to return
a dataframe containing |
subscene |
Where to look: by default, the whole scene is searched.
|
Exactly one of tags
and ids
must be specified.
A dataframe is constructed with columns
id |
item id |
type |
item type |
tag |
item tag |
matching the specified tags
or ids
value.
If full = TRUE
, the full dataframe is returned, otherwise
just the requested ids or tags.
If ids
is specified, the return value will be
in the same order
as ids
).
Duncan Murdoch
open3d() ids <- plot3d(rnorm(10), rnorm(10), rnorm(10), tag = "plot") unclass(ids) tagged3d("plot") tagged3d(ids = ids, full = TRUE)
open3d() ids <- plot3d(rnorm(10), rnorm(10), rnorm(10), tag = "plot") unclass(ids) tagged3d("plot") tagged3d(ids = ids, full = TRUE)
Adds text to the scene. The text is positioned in 3D space. Text is always oriented towards the camera.
text3d(x, y = NULL, z = NULL, texts, adj = 0.5, pos = NULL, offset = 0.5, usePlotmath = is.language(texts), family = par3d("family"), font = par3d("font"), cex = par3d("cex"), useFreeType = par3d("useFreeType"), ...) texts3d(x, y = NULL, z = NULL, texts, adj = 0.5, pos = NULL, offset = 0.5, usePlotmath = is.language(texts), family = par3d("family"), font = par3d("font"), cex = par3d("cex"), useFreeType = par3d("useFreeType"), ...)
text3d(x, y = NULL, z = NULL, texts, adj = 0.5, pos = NULL, offset = 0.5, usePlotmath = is.language(texts), family = par3d("family"), font = par3d("font"), cex = par3d("cex"), useFreeType = par3d("useFreeType"), ...) texts3d(x, y = NULL, z = NULL, texts, adj = 0.5, pos = NULL, offset = 0.5, usePlotmath = is.language(texts), family = par3d("family"), font = par3d("font"), cex = par3d("cex"), useFreeType = par3d("useFreeType"), ...)
x , y , z
|
point coordinates. Any reasonable way of defining the
coordinates is acceptable. See the function |
texts |
text character vector to draw |
adj |
one value specifying the horizontal adjustment, or two, specifying horizontal and vertical adjustment respectively, or three, specifying adjustment in all three directions. |
pos |
a position specifier for the text. If specified, this
overrides any |
offset |
when |
usePlotmath |
logical. Should |
family |
A device-independent font family name, or "" |
font |
A numeric font number from 1 to 4 |
cex |
A numeric character expansion value |
useFreeType |
logical. Should FreeType be used to draw text? (See details below.) |
... |
Material properties; see |
The adj
parameter determines the position of the text relative to the
specified coordinate. Use adj = c(0, 0)
to place the left bottom corner at
(x, y, z)
, adj = c(0.5, 0.5)
to center the text there, and adj = c(1, 1)
to put the right top corner there. The optional second coordinate for vertical
adjustment defaults to 0.5
. Placement is done using the "advance" of
the string and the "ascent" of the font relative to the baseline, when these metrics
are known.
text3d
and texts3d
draw text using the r3d
conventions. These are synonyms; the former is singular to be
consistent with the classic 2-D graphics functions, and the latter is
plural to be consistent with all the other graphics primitives. Take
your choice!
If any coordinate or text is NA
, that text is not plotted.
If usePlotmath
is TRUE
, the work will be done
by the plotmath3d
function. This is the default
if the texts
parameter is “language”, e.g. the result of
a call to expression
or quote
.
The text drawing functions return the object ID of the text object (or sprites, in case of usePlotmath = TRUE
)
invisibly.
Fonts are specified using the family
, font
, cex
,
and useFreeType
arguments. Defaults for the currently active
device may be set using par3d
, or for future devices
using r3dDefaults
.
The family
specification is the same as for standard graphics, i.e.
families c("serif", "sans", "mono", "symbol")
are normally
available, but users may add additional families. font
numbers
are restricted to the range 1 to 4 for standard, bold, italic and bold italic
respectively. Font 5 is recoded as family "symbol"
font 1, but that is not supported unless specifically
installed, so should be avoided.
Using an unrecognized value for "family"
will result in
the system standard font as used in RGL up to version 0.76. That font
is not resizable and font
values are ignored.
If useFreeType
is TRUE
, then RGL will use the FreeType
anti-aliased fonts for drawing. This is generally desirable, and it is the
default on non-Windows systems if RGL was built to support FreeType.
FreeType fonts are specified using the rglFonts
function.
r3d
, plotmath3d
, rglExtrafonts
for adding fonts
open3d() famnum <- rep(1:3, 8) family <- c("serif", "sans", "mono")[famnum] font <- rep(rep(1:4, each = 3), 2) cex <- rep(1:2, each = 12) text3d(font, cex, famnum, texts = paste(family, font), adj = 0.5, color = "blue", family = family, font = font, cex = cex)
open3d() famnum <- rep(1:3, 8) family <- c("serif", "sans", "mono")[famnum] font <- rep(rep(1:4, each = 3), 2) cex <- rep(1:2, each = 12) text3d(font, cex, famnum, texts = paste(family, font), adj = 0.5, color = "blue", family = family, font = font, cex = cex)
Internally, rgl works with PNG files for textures. If a texture is requested using a different format, a temporary PNG file of the image will be saved. This function allows you to retrieve the original expression used to produce the texture.
textureSource(texture)
textureSource(texture)
texture |
The filename of a texture file. If missing, the directory where texture files are stored will be returned. |
rgl creates a new file in the temporary directory
whenever a non-PNG texture is used. It will delete them
when it knows there are no references and
at the end of the session, but conceivably there will be
situations where you need to delete them earlier. Calling
textureSource()
with no arguments will give
you the directory holding the textures so that they can
be deleted sooner.
If texture
is specified and it is the name of a
temporary PNG texture file produced by rgl, the
expression used to specify the texture will be returned.
If it is the name of some other file, texture
will
be returned.
If no argument is given, the session-specific directory holding the temporary texture files will be returned.
xyz <- cbind(c(0,1,1,0), c(0,0,1,1), c(0,0,0,0)) st <- xyz[,1:2] open3d() id <- quads3d(xyz, texcoords = st, texture = as.raster(matrix(colors()[1:120], ncol = 10)), col="white") material3d(id = id, "texture") textureSource(material3d(id = id, "texture"))
xyz <- cbind(c(0,1,1,0), c(0,0,1,1), c(0,0,0,0)) st <- xyz[,1:2] open3d() id <- quads3d(xyz, texcoords = st, texture = as.raster(matrix(colors()[1:120], ncol = 10)), col="white") material3d(id = id, "texture") textureSource(material3d(id = id, "texture"))
Jim Lemon's thigmophobe
function in the
plotrix
package
computes good directions for labels in a 2D plot. This function
does the same for a particular projection in a 3D plot by
projecting down to 2D and calling his function.
thigmophobe3d(x, y = NULL, z = NULL, P = par3d("projMatrix"), M = par3d("modelMatrix"), windowRect = par3d("windowRect"))
thigmophobe3d(x, y = NULL, z = NULL, P = par3d("projMatrix"), M = par3d("modelMatrix"), windowRect = par3d("windowRect"))
x , y , z
|
point coordinates. Any reasonable way of defining
the coordinates is acceptable. See the function |
P , M , windowRect
|
The projection and modelview matrices, and the size and position of the display in pixels. |
Since thigmophobe3d
projects using fixed P
and M
,
it will not necessarily choose good directions if the user rotates
the display or makes any other change to the projection.
A vector of values from 1 to 4 to be used as the pos
argument
in text3d
.
The example below shows how to update the directions during an animation; I find that the moving labels are distracting, and prefer to live with fixed ones.
Duncan Murdoch
if (requireNamespace("plotrix", quietly = TRUE)) { # Simulate some data xyz <- matrix(rnorm(30), ncol = 3) # Plot the data first, to establish the projection plot3d(xyz) # Now thigmophobe3d can choose directions textid <- text3d(xyz, texts = 1:10, pos = thigmophobe3d(xyz)) # Update the label positions during an animation if (interactive() && !rgl.useNULL()) { spin <- spin3d(rpm = 5) f <- function(time) { par3d(skipRedraw = TRUE) on.exit(par3d(skipRedraw = FALSE)) pop3d(id = textid) # Need to rotate before thigmophobe3d is called result <- spin(time) par3d(userMatrix = result$userMatrix) textid <<- text3d(xyz, texts = 1:10, pos = thigmophobe3d(xyz)) result } play3d(f, duration = 5) } else textid # just print the static display }
if (requireNamespace("plotrix", quietly = TRUE)) { # Simulate some data xyz <- matrix(rnorm(30), ncol = 3) # Plot the data first, to establish the projection plot3d(xyz) # Now thigmophobe3d can choose directions textid <- text3d(xyz, texts = 1:10, pos = thigmophobe3d(xyz)) # Update the label positions during an animation if (interactive() && !rgl.useNULL()) { spin <- spin3d(rpm = 5) f <- function(time) { par3d(skipRedraw = TRUE) on.exit(par3d(skipRedraw = FALSE)) pop3d(id = textid) # Need to rotate before thigmophobe3d is called result <- spin(time) par3d(userMatrix = result$userMatrix) textid <<- text3d(xyz, texts = 1:10, pos = thigmophobe3d(xyz)) result } play3d(f, duration = 5) } else textid # just print the static display }
This function opens a TCL/TK modal dialog to allow particular views of an RGL scene to be saved.
tkpar3dsave(params = c("userMatrix", "scale", "zoom", "FOV"), times = FALSE, dev = cur3d(), ...)
tkpar3dsave(params = c("userMatrix", "scale", "zoom", "FOV"), times = FALSE, dev = cur3d(), ...)
params |
Which parameters to save |
times |
Should times be saved as well? |
dev |
Which RGL device to work with |
... |
Additional parameters to pass to |
This opens a TCL/TK modal dialog box with Record
and Quit
buttons. Each time
Record
is clicked, a snapshot is taken of current par3d
settings. When
Quit
is clicked, the dialog closes and the values are returned in a list.
If times == TRUE
, then the times at which the views are recorded will also be saved, so that
the play3d
function will play back with the same timing.
A list of the requested components. Each one will consist of a list of values that were
current when the Record
button was clicked. These are suitable to be passed directly
to the par3dinterp
function.
Duncan Murdoch
if (interactive() && !in_pkgdown_example()) { # Record a series of positions, and then play them back immediately # at evenly spaced times, in an oscillating loop example(plot3d) play3d( par3dinterp( tkpar3dsave() ) ) # As above, but preserve the click timings # play3d( par3dinterp( tkpar3dsave(times=TRUE) ) ) }
if (interactive() && !in_pkgdown_example()) { # Record a series of positions, and then play them back immediately # at evenly spaced times, in an oscillating loop example(plot3d) play3d( par3dinterp( tkpar3dsave() ) ) # As above, but preserve the click timings # play3d( par3dinterp( tkpar3dsave(times=TRUE) ) ) }
Functions from the former tkrgl package.
The tkrgl package contained functions to use TCL/TK to control an RGL scene on screen. These functions have now been merged into the rgl package, and the tkrgl package has been archived.
To avoid conflicts with RGL names and to indicate the TCL/TK
nature of these functions, they have all been prefixed with tk
:
tkpar3dsave
Formerly tkrgl::par3dsave
, allows interactive
saving of scene parameters.
tkspin3d, tkspinControl
Formerly
tkrgl::spin3d
and tkrgl::spinControl
, create buttons
to spin the scene.
History:
0.2-2 | First public release |
0.3 | Added possibility to control multiple windows |
0.4 | Compatibility with 2.0.0 tcltk package |
0.5 | Added continuous rotation |
0.6 | Added par3dsave |
0.7 | Added parameters to tkspinControl , fixed startup |
0.8 | Minor fixes to pass checks |
0.9 | Merge functions into rgl |
This function creates a TCL/TK window containing buttons to spin and resize one or more RGL windows.
tkspin3d(dev = cur3d(), ...)
tkspin3d(dev = cur3d(), ...)
dev |
A vector of one or more RGL device numbers to control |
... |
Named parameters in that match named formal arguments
to |
Ming Chen and Duncan Murdoch
if (interactive() && !in_pkgdown_example()) { open3d() points3d(rnorm(100), rnorm(100), rnorm(100), size=3) axes3d() box3d() tkspin3d() }
if (interactive() && !in_pkgdown_example()) { open3d() points3d(rnorm(100), rnorm(100), rnorm(100), size=3) axes3d() box3d() tkspin3d() }
This function may be used to embed a spin control in a TCL/TK window.
tkspinControl(base, dev = cur3d(), continue=FALSE, speed=30, scale=100, ... )
tkspinControl(base, dev = cur3d(), continue=FALSE, speed=30, scale=100, ... )
base |
The TCL/TK frame in which to insert this control. |
dev |
A vector of one or more RGL device numbers to control. |
continue |
Initial setting for continuous rotation checkbox. |
speed |
Initial setting for speed slider. |
scale |
Initial setting for scale slider. |
... |
Additional parameters to pass to |
Ming Chen and Duncan Murdoch
if (interactive() && !in_pkgdown_example()) { library(tcltk) open3d() win1 <- cur3d() plot3d(rexp(100), rexp(100), rexp(100), size=3, col='green') open3d() win2 <- cur3d() plot3d(rt(100,2), rt(100,2), rt(100, 2), size=3, col='yellow') open3d() win3 <- cur3d() plot3d(rexp(100), rexp(100), rexp(100), size=3, col='red') open3d() win4 <- cur3d() plot3d(rbinom(100,10,0.5), rbinom(100,10,0.5), rbinom(100,10,0.5), size=3, col='cyan') base <- tktoplevel() tkwm.title(base, "Spinners") con1 <- tkspinControl(base, dev=c(win1,win2)) con2 <- tkspinControl(base, dev=c(win3,win4)) tkpack(con1, con2) }
if (interactive() && !in_pkgdown_example()) { library(tcltk) open3d() win1 <- cur3d() plot3d(rexp(100), rexp(100), rexp(100), size=3, col='green') open3d() win2 <- cur3d() plot3d(rt(100,2), rt(100,2), rt(100, 2), size=3, col='yellow') open3d() win3 <- cur3d() plot3d(rexp(100), rexp(100), rexp(100), size=3, col='red') open3d() win4 <- cur3d() plot3d(rbinom(100,10,0.5), rbinom(100,10,0.5), rbinom(100,10,0.5), size=3, col='cyan') base <- tktoplevel() tkwm.title(base, "Spinners") con1 <- tkspinControl(base, dev=c(win1,win2)) con2 <- tkspinControl(base, dev=c(win3,win4)) tkpack(con1, con2) }
This function produces a button in an HTML scene that will toggle the display of items in the scene.
toggleWidget(sceneId, ids = tagged3d(tags), tags = NULL, hidden = integer(), subscenes = NULL, label, ...)
toggleWidget(sceneId, ids = tagged3d(tags), tags = NULL, hidden = integer(), subscenes = NULL, label, ...)
sceneId |
The HTML id of the RGL scene being controlled,
or an object as in |
ids ,
|
The RGL id numbers of the objects to toggle. Those in |
tags |
Alternate way to specify |
subscenes |
The subscenes in which to toggle the objects. |
label |
The label to put on the button. The default is set
from the expression passed to |
... |
Additional arguments to pass to |
Like playwidget
, this function is designed
to work within the htmlwidgets framework.
If the value is printed, the button will be inserted
into the output.
It is also designed to work with magrittr-style
pipes: the result of rglwidget
or
other widgets can be piped into it to add it
to a display. It can also appear first in the
pipeline, if sceneId
is set to NA
.
A widget suitable for use in an Rmarkdown-generated web page, or elsewhere.
Duncan Murdoch
toggleButton
for the older style
of HTML control.
theplot <- plot3d(rnorm(100), rnorm(100), rnorm(100), col = "red") widget <- rglwidget(height = 300, width = 300) %>% toggleWidget(theplot["data"], hidden = theplot[c("xlab", "ylab", "zlab")], label = "Points") if (interactive() || in_pkgdown_example()) widget
theplot <- plot3d(rnorm(100), rnorm(100), rnorm(100), col = "red") widget <- rglwidget(height = 300, width = 300) %>% toggleWidget(theplot["data"], hidden = theplot[c("xlab", "ylab", "zlab")], label = "Points") if (interactive() || in_pkgdown_example()) widget
This algorithm decomposes a general polygon into simple polygons and uses the “ear-clipping” algorithm to triangulate it. Polygons with holes are supported.
triangulate(x, y = NULL, z = NULL, random = TRUE, plot = FALSE, partial = NA)
triangulate(x, y = NULL, z = NULL, random = TRUE, plot = FALSE, partial = NA)
x , y , z
|
Coordinates of a two-dimensional polygon in a format supported by |
random |
Whether to use a random or deterministic triangulation. |
plot |
Whether to plot the triangulation; mainly for debugging purposes. |
partial |
If the triangulation fails, should partial results be returned? |
Normally triangulate
looks only at the x
and y
coordinates. However, if one of those is constant, it is replaced
with the z
coordinate if present.
The algorithm works as follows. First, it breaks the polygon into
pieces separated by NA
values in x
or y
.
Each of these pieces should be a simple, non-self-intersecting
polygon, separate from the other pieces.
(Though some minor exceptions to this rule may work, none
are guaranteed). The nesting of these pieces is determined.
The “outer” polygon(s) are then merged with the polygons that they immediately contain, and each of these pieces is triangulated using the ear-clipping algorithm.
Finally, all the triangulated pieces are put together into one result.
A three-by-n array giving the indices of the vertices of each triangle. (No vertices are added; only the original vertices are used in the triangulation.)
The array has an integer vector attribute "nextvert"
with one entry per vertex, giving the index of the next
vertex to proceed counter-clockwise around outer
polygon boundaries, clockwise around inner boundaries.
Not all inputs will succeed, even when a triangulation is
possible. Generally using random = TRUE
will find
a successful triangulation if one exists, but it may
occasionally take more than one try.
Duncan Murdoch
See the Wikipedia article “polygon triangulation” for a description of the ear-clipping algorithm.
extrude3d
for a solid extrusion of a polygon, polygon3d
for
a flat display; both use triangulate
.
theta <- seq(0, 2*pi, length.out = 25)[-25] theta <- c(theta, NA, theta, NA, theta, NA, theta, NA, theta) r <- c(rep(1.5, 24), NA, rep(0.5, 24), NA, rep(0.5, 24), NA, rep(0.3, 24), NA, rep(0.1, 24)) dx <- c(rep(0, 24), NA, rep(0.6, 24), NA, rep(-0.6, 24), NA, rep(-0.6, 24), NA, rep(-0.6, 24)) x <- r*cos(theta) + dx y <- r*sin(theta) plot(x, y, type = "n") polygon(x, y) triangulate(x, y, plot = TRUE) open3d() polygon3d(x, y, x - y, col = "red")
theta <- seq(0, 2*pi, length.out = 25)[-25] theta <- c(theta, NA, theta, NA, theta, NA, theta, NA, theta) r <- c(rep(1.5, 24), NA, rep(0.5, 24), NA, rep(0.5, 24), NA, rep(0.3, 24), NA, rep(0.1, 24)) dx <- c(rep(0, 24), NA, rep(0.6, 24), NA, rep(-0.6, 24), NA, rep(-0.6, 24), NA, rep(-0.6, 24)) x <- r*cos(theta) + dx y <- r*sin(theta) plot(x, y, type = "n") polygon(x, y) triangulate(x, y, plot = TRUE) open3d() polygon3d(x, y, x - y, col = "red")
This function “turns” the curve (as on a lathe) to form a solid of rotation along the x axis.
turn3d(x, y = NULL, n = 12, smooth = FALSE, ...)
turn3d(x, y = NULL, n = 12, smooth = FALSE, ...)
x , y
|
Points on the curve, in a form suitable for |
n |
How many steps in the rotation? |
smooth |
logical; whether to add normals for a smooth appearance. |
... |
Additional parameters to pass to |
A mesh object containing triangles and/or quadrilaterals.
Fang He and Duncan Murdoch
x <- 1:10 y <- rnorm(10)^2 open3d() shade3d(turn3d(x, y), col = "green")
x <- 1:10 y <- rnorm(10)^2 open3d() shade3d(turn3d(x, y), col = "green")
This is a function to produce actions in a web display. A
playwidget
or Shiny
input control (e.g. a sliderInput
control)
sets a value which controls attributes of a selection of vertices.
vertexControl(value = 0, values = NULL, vertices = 1, attributes, objid = tagged3d(tag), tag, param = seq_len(NROW(values)) - 1, interp = TRUE)
vertexControl(value = 0, values = NULL, vertices = 1, attributes, objid = tagged3d(tag), tag, param = seq_len(NROW(values)) - 1, interp = TRUE)
value |
The value to use for input (typically |
values |
A matrix of values, each row corresponding to an input value. |
vertices |
Which vertices are being controlled? Specify |
attributes |
A vector of attributes of a vertex,
from |
objid |
A single RGL object id. |
tag |
An alternate way to specify |
param |
Parameter values corresponding to each row of |
interp |
Whether to interpolate between rows of |
This function
modifies attributes of vertices in a single
object. The attributes
are properties of each vertex
in a scene; not all are applicable to all objects. In order,
they are: coordinates of the vertex "x", "y", "z"
, color
of the vertex "red", "green", "blue", "alpha"
, normal at the vertex
"nx", "ny", "nz"
, radius of a sphere at the vertex
"radius"
, origin within a texture "ox", "oy"
and
perhaps "oz"
, texture coordinates "ts", "tt"
.
Planes are handled specially. The coefficients a, b, c
in the planes3d
or clipplanes3d
specification are controlled
using "nx", "ny", "nz"
, and d
is handled as
"offset"
. The vertices
argument is interpreted
as the indices of the planes when these attributes are set.
If only one attribute of one vertex is specified, values
may
be given as a vector and will be treated as a one-column matrix. Otherwise
values
must be given as a matrix with ncol(values) == max(length(vertices),
length(attributes))
. The vertices
and attributes
vectors will be recycled to the same length, and entries from column j
of values
will be applied to
vertex vertices[j]
, attribute attributes[j]
.
The value
argument is translated into a row (or two rows if
interp = TRUE
) of values
by finding its location in param
.
A list of class "rglControl"
of cleaned up parameter
values, to be used in an RGL widget.
Duncan Murdoch
saveopts <- options(rgl.useNULL = TRUE) theta <- seq(0, 6*pi, length.out = 100) xyz <- cbind(sin(theta), cos(theta), theta) plot3d(xyz, type="l") id <- spheres3d(xyz[1,,drop=FALSE], col="red") widget <- rglwidget(width=500, height=300) %>% playwidget(vertexControl(values=xyz, attributes=c("x", "y", "z"), objid = id, param=1:100), start = 1, stop = 100, rate=10) if (interactive() || in_pkgdown_example()) widget options(saveopts)
saveopts <- options(rgl.useNULL = TRUE) theta <- seq(0, 6*pi, length.out = 100) xyz <- cbind(sin(theta), cos(theta), theta) plot3d(xyz, type="l") id <- spheres3d(xyz[1,,drop=FALSE], col="red") widget <- rglwidget(width=500, height=300) %>% playwidget(vertexControl(values=xyz, attributes=c("x", "y", "z"), objid = id, param=1:100), start = 1, stop = 100, rate=10) if (interactive() || in_pkgdown_example()) widget options(saveopts)
Set the viewpoint orientation.
view3d( theta = 0, phi = 15, fov = 60, zoom = 1, scale = par3d("scale"), interactive = TRUE, userMatrix, type = c("userviewpoint", "modelviewpoint") )
view3d( theta = 0, phi = 15, fov = 60, zoom = 1, scale = par3d("scale"), interactive = TRUE, userMatrix, type = c("userviewpoint", "modelviewpoint") )
theta , phi
|
polar coordinates in degrees. |
fov |
field-of-view angle in degrees |
zoom |
zoom factor |
scale |
real length 3 vector specifying the rescaling to apply to each axis |
interactive |
logical, specifying if interactive navigation is allowed |
userMatrix |
4x4 matrix specifying user point of view |
type |
which viewpoint to set? |
The data model can be rotated using the polar coordinates theta
and phi
. Alternatively, it can be set in a completely general way using the 4x4 matrix
userMatrix
. If userMatrix
is specified, theta
and phi
are ignored.
The pointing device of your graphics user-interface can also be used to set the viewpoint interactively. With the pointing device the buttons are by default set as follows:
adjust viewpoint position
adjust field of view angle
adjust zoom factor
The user's view can be set with fov
and zoom
.
If the fov
angle is set to 0, a parallel or orthogonal
projection is used. Small non-zero values (e.g. 0.01 or less, but not
0.0) are likely to lead to rendering errors due to OpenGL limitations.
Prior to version 0.94, all of these characteristics were stored in
one viewpoint object. With that release the characteristics are split
into those that affect the projection (the user viewpoint) and those that
affect the model (the model viewpoint). By default, this function
sets both, but the type
argument can be used to limit the effect.
## Not run: # animated round trip tour for 10 seconds open3d() shade3d(oh3d(), color = "red") start <- proc.time()[3] while ((i <- 36*(proc.time()[3] - start)) < 360) { view3d(i, i/4); } ## End(Not run)
## Not run: # animated round trip tour for 10 seconds open3d() shade3d(oh3d(), color = "red") start <- proc.time()[3] while ((i <- 36*(proc.time()[3] - start)) < 360) { view3d(i, i/4); } ## End(Not run)
Asymptote is a language for 3D graphics that is highly integrated with LaTeX. This is an experimental function to write an Asymptote program to approximate an RGL scene.
writeASY(scene = scene3d(), title = "scene", outtype = c("pdf", "eps", "asy", "latex", "pdflatex"), prc = TRUE, runAsy = "asy %filename%", defaultFontsize = 12, width = 7, height = 7, ppi = 100, ids = tagged3d(tags), tags = NULL, version = "2.65")
writeASY(scene = scene3d(), title = "scene", outtype = c("pdf", "eps", "asy", "latex", "pdflatex"), prc = TRUE, runAsy = "asy %filename%", defaultFontsize = 12, width = 7, height = 7, ppi = 100, ids = tagged3d(tags), tags = NULL, version = "2.65")
scene |
RGL scene object |
outtype |
What type of file to write? See Details. |
prc |
Whether to produce an interactive PRC scene. |
title |
The base of the filename to produce. |
runAsy |
Code to run the Asymptote program. |
defaultFontsize |
The default fontsize for text. |
width , height
|
Width and height of the output image, in inches. |
ppi |
“Pixels per inch” to assume when converting line widths and point sizes (which RGL measures in pixels). |
ids |
If not |
tags |
Alternate way to specify |
version |
Asymptote version 2.44 had a definition for its “light()” function
that was incompatibly changed in versions 2.47 and 2.50.
The current code has been tested with version 2.65.
If you are using an older version, set |
Asymptote is both a language describing a 2D or 3D graphic, and a program to interpret that language and produce output in a variety of formats including EPS, PDF (interactive or static), etc.
The interactive scene produced with prc = TRUE
requires outtype = "pdf"
, and (as of this
writing) has a number of limitations:
As far as we know, only Adobe Acrobat Reader of a sufficiently recent version can display these scenes.
Current versions ignore lighting settings.
The filename of the output file is returned invisibly.
This function is currently under development and limited in the quality of output it produces. Arguments will likely change.
There are a number of differences between the interactive display in Asymptote and the display in RGL. In particular, many objects that are a fixed size in RGL will scale with the image in Asymptote. Defaults have been chosen somewhat arbitrarily; tweaking will likely be needed.
Material properties of surfaces are not yet implemented.
On some systems, the program asy
used
to process the output has bugs and may fail. Run the example
at your own risk!
Duncan Murdoch
J. C. Bowman and A. Hammerlindl (2008). Asymptote: A vector graphics language, TUGBOAT: The Communications of the TeX Users Group, 29:2, 288-294.
scene3d
saves a copy of a scene to an R variable; rglwidget
,
writePLY
, writeOBJ
and writeSTL
write the scene to a file in various other formats.
## Not run: # On some systems, the program "asy" used # to process the output has bugs, so this may fail. x <- rnorm(20) y <- rnorm(20) z <- rnorm(20) plot3d(x, y, z, type = "s", col = "red") olddir <- setwd(tempdir()) writeASY(title = "interactive") # Produces interactive.pdf writeASY(title = "noninteractive", prc = FALSE) # Produces noninteractive.pdf setwd(olddir) ## End(Not run)
## Not run: # On some systems, the program "asy" used # to process the output has bugs, so this may fail. x <- rnorm(20) y <- rnorm(20) z <- rnorm(20) plot3d(x, y, z, type = "s", col = "red") olddir <- setwd(tempdir()) writeASY(title = "interactive") # Produces interactive.pdf writeASY(title = "noninteractive", prc = FALSE) # Produces noninteractive.pdf setwd(olddir) ## End(Not run)
writeOBJ
writes OBJ files. This is a file format that
is commonly used in 3D graphics applications. It does not represent text,
but does represent points, lines, polygons (and many other types that
RGL doesn't support). readOBJ
reads only some parts
of OBJ files.
writeOBJ(con, pointRadius = 0.005, pointShape = icosahedron3d(), lineRadius = pointRadius, lineSides = 20, pointsAsPoints = FALSE, linesAsLines = FALSE, withNormals = TRUE, withTextures = TRUE, separateObjects = TRUE, ids = tagged3d(tags), tags = NULL) readOBJ(con, ...)
writeOBJ(con, pointRadius = 0.005, pointShape = icosahedron3d(), lineRadius = pointRadius, lineSides = 20, pointsAsPoints = FALSE, linesAsLines = FALSE, withNormals = TRUE, withTextures = TRUE, separateObjects = TRUE, ids = tagged3d(tags), tags = NULL) readOBJ(con, ...)
con |
A connection or filename. |
pointRadius , lineRadius
|
The radius of points and lines relative to the overall scale of the figure, if they are converted to polyhedra. |
pointShape |
A mesh shape to use for points if they are converted. It is scaled by the |
lineSides |
Lines are rendered as cylinders with this many sides. |
pointsAsPoints , linesAsLines
|
Whether to convert points and lines to “point” and “line” records in the OBJ output. |
withNormals |
Whether to output vertex normals for smooth shading. |
separateObjects |
Whether to mark each RGL object as a separate object in the file. |
withTextures |
Whether to output texture coordinates. |
ids |
The identifiers (from |
tags |
Alternate way to specify |
... |
Additional arguments (typically just |
The current writeOBJ
implementation only outputs triangles, quads, planes, spheres,
points, line segments, line strips and surfaces. It does not output material
properties such as colors, since the OBJ format does not support the per-vertex
colors that RGL uses.
The readOBJ
implementation can
read faces, normals, and textures coordinates, but ignores
material properties (including the specification of the texture
file to use). To read a file that uses a single texture,
specify it in the material
argument, e.g.
readOBJ("model.OBJ", material = list(color = "white", texture = "texture.png"))
. There is no support for files that use
multiple textures.
The defaults for pointsAsPoints
and linesAsLines
have been
chosen because Blender (https://www.blender.org) does not import
points or lines, only polygons. If you are exporting to other software you
may want to change them.
If present, texture coordinates are output by default, but the textures themselves are not.
Individual RGL objects are output as separate objects in the file when
separateObjects = TRUE
, the default.
The output file should be readable by Blender and Meshlab; the latter can write in a number of other formats, including U3D, suitable for import into a PDF document.
writeObj
invisibly returns the name of the connection to which the
data was written.
readObj
returns a mesh object constructed
from the input file.
Duncan Murdoch
The file format was found at http://www.martinreddy.net/gfx/3d/OBJ.spec on November 11, 2012.
scene3d
saves a copy of a scene to an R variable; rglwidget
, writeASY
,
writePLY
and writeSTL
write the scene to a file in various other formats.
filename <- tempfile(fileext = ".obj") open3d() shade3d( icosahedron3d() ) writeOBJ(filename) # The motivation for writing readObj() was to read a shape # file of Comet 67P/Churyumov-Gerasimenko, from the ESA. # The file no longer appears to be online, but may still be # available on archive.org. Here was the original URL: # cometurl <- "http://sci.esa.int/science-e/www/object/doc.cfm?fobjectid=54726" # This code would read and display it: # open3d() # shade3d(readOBJ(url(cometurl), # material = list(col = "gray"))) # Textures are used in a realistic hand image available from # https://free3d.com/3d-model/freerealsichand-85561.html # Thanks to Monte Shaffer for pointing this out. # Decompress the files into the current directory, convert # hand_mapNew.jpg to hand_mapNew.png, then use ## Not run: open3d() shade3d(readOBJ("hand.OBJ", material = list(color = "white", shininess = 1, texture = "hand_mapNew.png"))) ## End(Not run)
filename <- tempfile(fileext = ".obj") open3d() shade3d( icosahedron3d() ) writeOBJ(filename) # The motivation for writing readObj() was to read a shape # file of Comet 67P/Churyumov-Gerasimenko, from the ESA. # The file no longer appears to be online, but may still be # available on archive.org. Here was the original URL: # cometurl <- "http://sci.esa.int/science-e/www/object/doc.cfm?fobjectid=54726" # This code would read and display it: # open3d() # shade3d(readOBJ(url(cometurl), # material = list(col = "gray"))) # Textures are used in a realistic hand image available from # https://free3d.com/3d-model/freerealsichand-85561.html # Thanks to Monte Shaffer for pointing this out. # Decompress the files into the current directory, convert # hand_mapNew.jpg to hand_mapNew.png, then use ## Not run: open3d() shade3d(readOBJ("hand.OBJ", material = list(color = "white", shininess = 1, texture = "hand_mapNew.png"))) ## End(Not run)
This function writes PLY files. This is a simple file format that
is commonly used in 3D printing. It does not represent text,
only edges and polygons. The writePLY
function
does the necessary conversions.
writePLY(con, format = c("little_endian", "big_endian", "ascii"), pointRadius = 0.005, pointShape = icosahedron3d(), lineRadius = pointRadius, lineSides = 20, pointsAsEdges = FALSE, linesAsEdges = pointsAsEdges, withColors = TRUE, withNormals = !(pointsAsEdges || linesAsEdges), ids = tagged3d(tags), tags = NULL)
writePLY(con, format = c("little_endian", "big_endian", "ascii"), pointRadius = 0.005, pointShape = icosahedron3d(), lineRadius = pointRadius, lineSides = 20, pointsAsEdges = FALSE, linesAsEdges = pointsAsEdges, withColors = TRUE, withNormals = !(pointsAsEdges || linesAsEdges), ids = tagged3d(tags), tags = NULL)
con |
A connection or filename. |
format |
Which output format. Defaults to little-endian binary. |
pointRadius , lineRadius
|
The radius of points and lines relative to the overall scale of the figure, if they are converted to polyhedra. |
pointShape |
A mesh shape to use for points if they are converted. It is scaled by the |
lineSides |
Lines are rendered as cylinders with this many sides. |
pointsAsEdges , linesAsEdges
|
Whether to convert points and lines to “Edge” records in the PLY output. |
withColors |
Whether to output vertex color information. |
withNormals |
Whether to output vertex normals for smooth shading. |
ids |
The identifiers (from |
tags |
Select objects with matching tags. Ignored
if |
The current implementation only outputs triangles, quads, planes, spheres, points, line segments, line strips and surfaces.
The defaults for pointsAsEdges
and linesAsEdges
have been
chosen because Blender (https://www.blender.org) does not import
lines, only polygons. If you are exporting to other software you
may want to change them.
Since the PLY format only allows one object per file, all RGL objects are combined into a single object when output.
The output file is readable by Blender and Meshlab; the latter can write in a number of other formats, including U3D, suitable for import into a PDF document.
Invisibly returns the name of the connection to which the data was written.
Duncan Murdoch
The file format was found on www.mathworks.com
on November 10, 2012
at a URL that no longer exists; currently the
format is described at www.mathworks.com/help/vision/ug/the-ply-format.html
.
scene3d
saves a copy of a scene to an R variable;
rglwidget
, writeASY
, writeOBJ
and writeSTL
write the scene to a file in various other formats.
filename <- tempfile(fileext = ".ply") open3d() shade3d( icosahedron3d(col = "magenta") ) writePLY(filename)
filename <- tempfile(fileext = ".ply") open3d() shade3d( icosahedron3d(col = "magenta") ) writePLY(filename)