de.jreality.geometry

## Class TubeFactory

• Direct Known Subclasses:
PolygonalTubeFactory

```public class TubeFactory
extends Object```
This class calculates tubes around curves. A tube of radius r around a curve C can be defined as the locus of points which lie at distant r from the curve (ignoring endpoints). We are interested only in imbedded tubes, that is, tubes which do not intersect themselves.

Example: in euclidean space, a tube around a line is an infinite cylinder. And, a tube around a line segment is a finite cylinder.

Complications arise in calculating tubes in various ways, some of which this class attempts to take into account:

• The ambient space may be hyperbolic or elliptic/spherical space, not euclidean space.
• A curve represented by a finite set of points can be thought of as truly a discrete curve, or as an approximation to a hypothetical smooth curve.

If the curve is considered to be discrete, then the tubing problem is not well-posed near the joints of the curve, since there the curvature approaches infinity and any finite tube will intersect itself.

This factory provides the basic foundation. It does not provide any geometry output; it is useful mainly on account of the method `#makeFrameField(double[][], int, int)`. See its documentation for a more precise statement of the mathematical implementation. Subclassses which generate actual geometric tubes can take different strategies to the challenges posed above, working with the same basic data provided in this class.

The basic data which determine the tube are:

• A curve of points in 3-space.
• Whether the curve is closed,
• The radius of the tube,
• A cross-section curve in 3-space.
• An ambient metric (specified by a metric (see `Pn`)
• A list of edge colors (optional),
• A list of vertex colors (optional),
• The type of frame family to use (Frenet or parallel),
• Whether to generate texture coordinates,
• Whether to parametrize the texture coordinates to constant speed,

These data are set using the instance methods below.

Calling `update()` will update the current state of the factory to the values set since the last call to `update()`. This is mostly of interest in the subclasses.

TODO adjust the parameter which determines how the profile at the vertices of the curve are "pulled back" towards the mid-segment profiles.

Author:
Charles Gunn
• ### Field Summary

Fields
Modifier and Type Field and Description
`boolean` `closedCurve`
`double[][]` `crossSection`
`double[][]` `edgeColors`
`boolean` `extendAtEnds`
`FrameFieldType` `frameFieldType`
`boolean` `framesDirty`
`boolean` `generateTextureCoordinates`
`int` `metric`
`double[]` `radii`
`double` `radius`
`double[][]` `theCurve`
`int` `twists`
`double[][]` `userBinormals`
`double[][]` `userTangents`
`double[][]` `vertexColors`
`boolean` `vertexColorsEnabled`
• ### Constructor Summary

Constructors
Constructor and Description
`TubeFactory()`
`TubeFactory(double[][] curve)`
• ### Method Summary

All Methods
Modifier and Type Method and Description
`TubeUtility.FrameInfo[]` `getFrameField()`
`SceneGraphComponent` `getFramesSceneGraphRepresentation()`
`double[]` `getInitialBinormal()`
`static SceneGraphComponent` `getSceneGraphRepresentation(TubeUtility.FrameInfo[] frames)`
`static SceneGraphComponent` ```getSceneGraphRepresentation(TubeUtility.FrameInfo[] frames, double scale)```
`double[][]` `getTangents()`
`double[][]` `getUserBinormals()`
`static SceneGraphComponent` `getXYZAxes()`
`boolean` `isArcLengthTextureCoordinates()`
`boolean` `isExtendAtEnds()`
`boolean` `isGenerateEdges()`
`boolean` `isGenerateTextureCoordinates()`
`boolean` `isMatchClosedTwist()`
`boolean` `isRemoveDuplicates()`
`TubeUtility.FrameInfo[]` ```makeFrameField(double[][] polygon, FrameFieldType type, int metric)```
The primary method in the tube-generating process.
`void` `setArcLengthTextureCoordinates(boolean arcLengthTextureCoordinates)`
If true, force the generated v- texture coordinates to run from 0 to 1 proportional to the (discrete) arc length of the curve.
`void` `setClosed(boolean closedCurve)`
Set whether the curve should be considered a closed loop.
`void` `setCrossSection(double[][] crossSection)`
A 2D curve in the (x,y) plane (with 3D coordinates!) to be used as the cross section of the tube.
`void` `setEdgeColors(double[][] edgeColors)`
Apply colors to faces of output tube, one for each tube segment.
`void` `setExtendAtEnds(boolean extendAtEnds)`
`void` `setFrameField(TubeUtility.FrameInfo[] frames)`
`void` `setFrameFieldType(FrameFieldType frameFieldType)`
Should the underlying frame field be generated by `TubeUtility#PARALLEL` or `TubeUtility#FRENET` displacement? Default: `TubeUtility#PARALLEL`
`void` `setGenerateEdges(boolean generateEdges)`
`void` `setGenerateTextureCoordinates(boolean generateTextureCoordinates)`
Whether the output geometry should have automatic texture coordinates.
`void` `setInitialBinormal(double[] initialBinormal)`
`void` `setMatchClosedTwist(boolean matchClosedTwist)`
`void` `setMetric(int metric)`
Set the metric of the ambient space.
`void` `setRadii(double[] radii)`
`void` `setRadius(double radius)`
Set the radius of the tube.
`void` `setRemoveDuplicates(boolean removeDuplicates)`
`void` `setTangents(double[][] tangents)`
`void` `setTwists(int twists)`
Set an integer number of twists to apply to the cross section as it is swept along the curve.
`void` `setUserBinormals(double[][] userBinormals)`
`void` `setVertexColors(double[][] vertexColors)`
Apply colors to vertices of output tube, one for each tube cross section.
`void` `setVertexColorsEnabled(boolean vertexColorsEnabled)`
Whether to apply vertex colors to the output tube.
`void` `update()`
`void` `updateFrames()`
• ### Methods inherited from class java.lang.Object

`equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`
• ### Field Detail

• #### theCurve

`public double[][] theCurve`
• #### userTangents

`public double[][] userTangents`
• #### userBinormals

`public double[][] userBinormals`
• #### vertexColors

`public double[][] vertexColors`
• #### edgeColors

`public double[][] edgeColors`
• #### crossSection

`public double[][] crossSection`

`public double[] radii`

`public double radius`
• #### frameFieldType

`public FrameFieldType frameFieldType`
• #### metric

`public int metric`
• #### twists

`public int twists`
• #### generateTextureCoordinates

`public boolean generateTextureCoordinates`
• #### extendAtEnds

`public boolean extendAtEnds`
• #### framesDirty

`public boolean framesDirty`
• #### closedCurve

`public boolean closedCurve`
• #### vertexColorsEnabled

`public boolean vertexColorsEnabled`
• ### Constructor Detail

• #### TubeFactory

`public TubeFactory()`
• #### TubeFactory

`public TubeFactory(double[][] curve)`
• ### Method Detail

• #### updateFrames

`public void updateFrames()`
• #### getFrameField

`public TubeUtility.FrameInfo[] getFrameField()`
• #### setClosed

`public void setClosed(boolean closedCurve)`
Set whether the curve should be considered a closed loop. Default: false.
Parameters:
`closedCurve` -
• #### setCrossSection

`public void setCrossSection(double[][] crossSection)`
A 2D curve in the (x,y) plane (with 3D coordinates!) to be used as the cross section of the tube. For flexibility: unless the first and last points are equal, the curve is considered to be open and the resulting tube will not close up. Default: an octagon lying on the unit circle.
Parameters:
`crossSection` -
• #### getTangents

`public double[][] getTangents()`
• #### setTangents

`public void setTangents(double[][] tangents)`
• #### getUserBinormals

`public double[][] getUserBinormals()`
• #### setUserBinormals

`public void setUserBinormals(double[][] userBinormals)`
• #### setFrameField

`public void setFrameField(TubeUtility.FrameInfo[] frames)`
• #### setFrameFieldType

`public void setFrameFieldType(FrameFieldType frameFieldType)`
Should the underlying frame field be generated by `TubeUtility#PARALLEL` or `TubeUtility#FRENET` displacement? Default: `TubeUtility#PARALLEL`
Parameters:
`frameFieldType` -

`public void setRadius(double radius)`
Set the radius of the tube. To be exact, this is applied as a scale factor to the cross section before it is swept out to make the tube.
Parameters:
`radius` -

`public void setRadii(double[] radii)`
• #### setMetric

`public void setMetric(int metric)`
Set the metric of the ambient space. See `Pn`.
Parameters:
`metric` -
• #### setTwists

`public void setTwists(int twists)`
Set an integer number of twists to apply to the cross section as it is swept along the curve. This twisting is done proportional to the arc length of the curve. For creating exotic shapes. Default: 0
Parameters:
`twists` -
• #### setVertexColorsEnabled

`public void setVertexColorsEnabled(boolean vertexColorsEnabled)`
Whether to apply vertex colors to the output tube. See `setVertexColors(double[][])`.
Parameters:
`vertexColorsEnabled` -
• #### setEdgeColors

`public void setEdgeColors(double[][] edgeColors)`
Apply colors to faces of output tube, one for each tube segment.
Parameters:
`edgeColors` - double[n][] where n is number of segments in curve
• #### setVertexColors

`public void setVertexColors(double[][] vertexColors)`
Apply colors to vertices of output tube, one for each tube cross section.
Parameters:
`vertexColors` - double[n][] where n is number of vertices in curve
• #### isGenerateTextureCoordinates

`public boolean isGenerateTextureCoordinates()`
• #### setGenerateTextureCoordinates

`public void setGenerateTextureCoordinates(boolean generateTextureCoordinates)`
Whether the output geometry should have automatic texture coordinates. If true, texture coordinates will be based on the tubing parameters: u is the cross section parameter, v is the length of the tube.
`setArcLengthTextureCoordinates(boolean)`
• #### isArcLengthTextureCoordinates

`public boolean isArcLengthTextureCoordinates()`
• #### setArcLengthTextureCoordinates

`public void setArcLengthTextureCoordinates(boolean arcLengthTextureCoordinates)`
If true, force the generated v- texture coordinates to run from 0 to 1 proportional to the (discrete) arc length of the curve.
Parameters:
`arcLengthTextureCoordinates` -
• #### isExtendAtEnds

`public boolean isExtendAtEnds()`
• #### setExtendAtEnds

`public void setExtendAtEnds(boolean extendAtEnds)`
• #### isRemoveDuplicates

`public boolean isRemoveDuplicates()`
• #### setRemoveDuplicates

`public void setRemoveDuplicates(boolean removeDuplicates)`
• #### isGenerateEdges

`public boolean isGenerateEdges()`
• #### setGenerateEdges

`public void setGenerateEdges(boolean generateEdges)`
• #### isMatchClosedTwist

`public boolean isMatchClosedTwist()`
• #### setMatchClosedTwist

`public void setMatchClosedTwist(boolean matchClosedTwist)`
• #### getInitialBinormal

`public double[] getInitialBinormal()`
• #### setInitialBinormal

`public void setInitialBinormal(double[] initialBinormal)`
• #### getFramesSceneGraphRepresentation

`public SceneGraphComponent getFramesSceneGraphRepresentation()`
• #### update

`public void update()`
• #### makeFrameField

```public TubeUtility.FrameInfo[] makeFrameField(double[][] polygon,
FrameFieldType type,
int metric)```
The primary method in the tube-generating process.

This is an instance method, since there is quite a bit of state which is expensive to compute and so can be saved in the instance.

The code is complicated by dealing with euclidean, hyperbolic, and elliptic cases simultaneously and But at the same time the code is a interesting example of how euclidean objects can be generalized to non-euclidean setting.

Explanation of the algorithm: Assume that polygon is an array of length n

The input curve polygon is assumed to have an initial and terminal point with the following properties: 1) if the curve is closed, polygon[0]=polygon[n-2] and polygon[n-1]=polygon[1] 2) if the curve is not closed, then polygon[0] is the mirror of polygon[2] wrt polygon[1], similarly for polygon[n-1]. The tubing factories perform this setup automatically before calling this method, but if you want to use this method directly, you'll need to do this setup yourself.

The returned frame array (of type `TubeUtility.FrameInfo` is of length n-2.

The input curve can consist of either 3- or 4-d vectors; in the latter case they are assumed to be homogeneous projective coordinates.

The basic idea is: first calculate the Frenet frame for the curve. That is, in the best case, a simple matter at each point P[i] of the curve:

• Calculate the polar ("tangent") plane at P.
• In the euclidean case this is the plane at infinity (w=0), i.e., tangent vectors are distinguished by having fourth coordinate 0.
• In non-euclidean case, the polar plane is uniquely defined by P.
• All vectors in the frame for P belong to this tangent plane.
• Calculate the osculating plane for the curve at P. This is the plane spanned by Pi-1i, Pi+1. When polarized, this gives the binormal vector
• In the euclidean case, polarizing a plane means "set the 4th coordinate to 0". This yields the binormal vector.
• In the non-euclidean case, with full symmetric duality, one also gets the binormal vector.
• Calculate the tangent vector at Pi. (Geometrically the tangent is not defined since the curve is not differentiable here. What follows is a reasonable guess.)
• First calculate the angle bisector or mid-plane of the curve at Pi: that is the plane spanned by the directions at Pi which lie at an equal angle to the two curve segments meeting there.
• Define the tangent direction at Pi< to be the direction orthogonal to the angle bisector
• The tangent vector is then the polar point of the mid-plane.
• Calculate the normal vector.
• Calculate the plane spanned by Pi, the binormal, and the normal vector.
• The normal vector is the polar point of this plane.
This algorithm calculates the Frenet frame. To get the parallel frame, first calculate the Frenet frame. Then the normal vector N has to be parallel transported along the curve:
• At the first point P0, define the parallel field to be the same as the Frenet field.
• At each further point Pi, calculate the plane spanned by Pi-1, Pi, and the previous parallel normal vector.
• Calculate the point of intersection of this plane, the polar plane (tangent plane) of Pi, and the mid-plane of Pi (both the latter two have already been calculated above).
• The result is the desired parallel normal vector N.
• The rest of the frame at Pi is gotten by rotating the Frenet frame so that the tangent vector is fixed and the Frenet normal rotates into the parallel normal.
At points where three or more consecutive curve vertices are collinear, \ some of the steps of the above algorithm aren't well-defined. In that case, the correct behavior is basically to tube as if these points had been deleted before the processing began.

Parameters:
`polygon` - the curve to frame (as array of 3- or 4-d points)
`type` - `TubeUtility.PARALLEL` or {TubeUtility.FRENET}
`metric` - the metric metric `Pn`
Returns:
an array of length (n-2) of type `TubeUtility.FrameInfo` containing an orthonormal frame for each internal point in the initial polygon array.
• #### getSceneGraphRepresentation

`public static SceneGraphComponent getSceneGraphRepresentation(TubeUtility.FrameInfo[] frames)`
• #### getSceneGraphRepresentation

```public static SceneGraphComponent getSceneGraphRepresentation(TubeUtility.FrameInfo[] frames,
double scale)```
• #### getXYZAxes

`public static SceneGraphComponent getXYZAxes()`