Use a tube factory
JavaDoc: TubeFactory JavaDoc: PolygonalTubeFactory
The jReality tube factories put tubes around curves. TubeFactory itself is an interface; the simplest implementing class is PolygonalTubeFactory.
Simple example
Source file: TubeFactory01
Here's a simple example showing how to use a PolygonalTubeFactory to put a tube around a curve.
- The curve is provided by the method Primitives.discreteTorusKnot(). You can use any IndexedLineSet as input to the constructor.
- The second argument to the constructor is an integer identifying which edge in the IndexedLineSet should be tubed.
If the tubes look familiar, it's because the default line shader uses the same method to generate tubes when you specify that edges should be drawn with tubes. But notice that the line shader always disables display of points and lines when it displays such tubes. (Otherwise it would fall into an infinite regress, drawing tubes around the edges of the tubes around the edges around the tubes ...).
... public class TubeFactory01 { public static void main(String[] args) { SceneGraphComponent torussgc = SceneGraphUtility.createFullSceneGraphComponent("torus knot"); DefaultGeometryShader dgs = (DefaultGeometryShader) ShaderUtility.createDefaultGeometryShader(torussgc.getAppearance(), true); dgs.setShowLines(true); dgs.setShowPoints(false); DefaultPolygonShader dps = (DefaultPolygonShader) dgs.createPolygonShader("default"); dps.setDiffuseColor(Color.white); DefaultLineShader dls = (DefaultLineShader) dgs.createLineShader("default"); dls.setDiffuseColor(Color.yellow); dls.setTubeRadius(.01); IndexedLineSet torus1 = Primitives.discreteTorusKnot(1, .25, 2, 9, 250); PolygonalTubeFactory ptf = new PolygonalTubeFactory(torus1, 0); ptf.setClosed(true); ptf.setRadius(.1); ptf.setGenerateEdges(true); ptf.update(); IndexedFaceSet torus1Tubes = ptf.getTube(); torussgc.setGeometry(torus1Tubes); JRViewer.display(torussgc); } }
Advanced example
Source file: TubeFactory02
This second example shows a more sophisticated usage.
- The default cross section is replaced by a customized, star-shaped one.
- The vertex colors of the tube are inherited from the vertex colors of the line set.
- The tube is twisted, an effect which is only noticeable since the cross section is no longer circular.
And, for those who like to change parameters interactively, the example shows how to add a ShrinkPanel
plugin to a ViewerApp, in this case, one that allows you to control the three different radii which determine the geometry of the example.
... torussgc = SceneGraphUtility .createFullSceneGraphComponent("torus knot"); dgs = (DefaultGeometryShader) ShaderUtility .createDefaultGeometryShader(torussgc.getAppearance(), true); dgs.setShowLines(drawEdges); dgs.setShowPoints(false); dps = (DefaultPolygonShader) dgs.createPolygonShader("default"); dps.setSmoothShading(isSmooth); updateGeometry(); JRViewer v = new JRViewer(); v.addBasicUI(); v.addContentSupport(ContentType.Raw); v.registerPlugin(new ContentAppearance()); v.setContent(torussgc); SimpleController c = v.getController(); Plugin p = createShrinkPanel(getInspector(), "inspector"); c.registerPlugin(p); v.startup(); Viewer viewer = v.getPlugin(View.class).getViewer(); CameraUtility.encompass(viewer); addKeyListener(viewer); } private static void updateGeometry() { IndexedFaceSet tubedTorusKnot = tubedTorusKnot(R, r, tubeRadius); torussgc.setGeometry(tubedTorusKnot); } private static IndexedFaceSet tubedTorusKnot(double R, double r, double tubeRadius) { IndexedLineSet torus1 = Primitives.discreteTorusKnot(R,r, 2, 9, 250);// double[][] verts = new double[250][3]; colorVertices(torus1, new double[] {1,0,0}, new double[] {0,1,0}); // create a non-circular cross section for the tube int size = 16; double scale = 1; double[][] mysection = new double[size][3]; for (int i = 0; i<size; ++i) { double angle = (i/(size-1.0)) * Math.PI * 2; mysection[i][0] = scale * Math.cos(angle) *(1.5+Math.cos(4*angle)); mysection[i][1] = scale * Math.sin(angle) *(1.5+Math.cos(4*angle)); mysection[i][2] = 0.0; } PolygonalTubeFactory ptf = new PolygonalTubeFactory(torus1, 0); ptf.setClosed(true); ptf.setVertexColorsEnabled(true); ptf.setRadius(tubeRadius); ptf.setCrossSection(mysection); ptf.setTwists(6); double[][] vcolors = torus1.getVertexAttributes(Attribute.COLORS).toDoubleArrayArray(null); ptf.setVertexColors(vcolors); ptf.setGenerateEdges(true); ptf.update(); IndexedFaceSet torus1Tubes = ptf.getTube(); return torus1Tubes; } public static void colorVertices(IndexedLineSet ils, double[] color1, double[] color2) { int nPts = ils.getNumPoints(); double[][] colors = new double[nPts][3]; double[][] vertices = ils.getVertexAttributes(Attribute.COORDINATES).toDoubleArrayArray(null); for (int i = 1; i<nPts-1; ++i) { double[] v1 = Rn.subtract(null, vertices[i], vertices[i-1]); double t = 10 * Math.sqrt(Math.abs( v1[0]*v1[0] + v1[1] * v1[1])); // t = 10 * Rn.euclideanDistance(vertices[i], vertices[i-1]); t = t - ((int) t); Rn.linearCombination(colors[i],t, color1, 1-t, color2); } System.arraycopy(colors[1], 0, colors[0], 0, 3); System.arraycopy(colors[nPts-2], 0, colors[nPts-1], 0, 3); ils.setVertexAttributes(Attribute.COLORS, StorageModel.DOUBLE_ARRAY.array(3).createReadOnly(colors)); } private static Component getInspector() { Box container = Box.createVerticalBox(); final TextSlider RSlider = new TextSlider.Double("R", SwingConstants.HORIZONTAL, 0.0, 2, R); RSlider.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { R = RSlider.getValue().doubleValue(); updateGeometry(); } }); container.add(RSlider); final TextSlider rSlider = new TextSlider.Double("r", SwingConstants.HORIZONTAL, 0.0, 1, r); rSlider.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { r = rSlider.getValue().doubleValue(); updateGeometry(); } }); container.add(rSlider); final TextSlider rtSlider = new TextSlider.Double("tube radius", SwingConstants.HORIZONTAL, 0.0, 1, tubeRadius); rtSlider.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { tubeRadius = rtSlider.getValue().doubleValue(); updateGeometry(); } }); container.add(rtSlider); container.setName("Parameters"); return container; } }
The shrink panel is created in the following code:
public Plugin createShrinkPanel( final Component c, final String title) { ShrinkPanelPlugin p = new ShrinkPanelPlugin() { { GridLayout gl = new GridLayout(); gl.setRows(1); setInitialPosition(SHRINKER_RIGHT); shrinkPanel.setName(title); shrinkPanel.setLayout(gl); shrinkPanel.add(c); } @Override public Class<? extends SideContainerPerspective> getPerspectivePluginClass() { return View.class; } @Override public PluginInfo getPluginInfo() { return new PluginInfo(title); } }; return p; }
|