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, ...)

Arguments

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 NULL.

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 TRUE, or a Frenet or user-specified frame if FALSE.

debug

If TRUE, plot the local Frenet coordinates at each point.

keepVars

If TRUE, return the local variables in attribute "vars".

...

Additional arguments to set as material properties.

Details

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.

Value

A "mesh3d" object holding the cylinder, possibly with attribute "vars" containing the local environment of the function.

Author

Duncan Murdoch

References

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.

Examples

# 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)))