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

• 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()
• 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)
• 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 -

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:

• 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=polygon[n-2] and polygon[n-1]=polygon 2) if the curve is not closed, then polygon is the mirror of polygon wrt polygon, 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.