001/**
002 * J3DCurve -- A Java3D node that represents a Curve in a J3D scene graph.
003 * 
004 * Copyright (C) 2009-2023, Joseph A. Huwaldt.
005 * All rights reserved.
006 * 
007 * This library is free software; you can redistribute it and/or modify it under the terms
008 * of the GNU Lesser General Public License as published by the Free Software Foundation;
009 * either version 2.1 of the License, or (at your option) any later version.
010 * 
011 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public License along with
016 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
017 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html
018 */
019package geomss.j3d;
020
021import geomss.app.GeomSSCanvas3D;
022import geomss.geom.Curve;
023import geomss.geom.GeomPoint;
024import geomss.geom.PointString;
025import geomss.geom.SubrangePoint;
026import static java.util.Objects.requireNonNull;
027import javax.measure.unit.SI;
028import org.jogamp.java3d.*;
029import org.jogamp.vecmath.*;
030import javolution.context.StackContext;
031
032/**
033 * A Java 3D node that represents a Curve in a Java 3D scene graph.
034 *
035 * <p> Modified by: Joseph A. Huwaldt </p>
036 *
037 * @author Joseph A. Huwaldt, Date: May 16, 2009
038 * @version June 4, 2023
039 */
040public class J3DCurve extends J3DGeomGroup<Curve> {
041
042    //  The switch for main or mirrored geometry.
043    private Switch _symmSG;
044
045    //  Store the Shape3D so it could potentially be used again in the future.
046    private Shape3D _oldShape3D;
047
048    /**
049     * Construct a new J3DCurve using the specified Curve as a reference.
050     *
051     * @param canvas   The canvas that the geometry is being rendered into.
052     * @param geometry A new J3DCurve using the specified Curve as a reference.
053     */
054    public J3DCurve(GeomSSCanvas3D canvas, Curve geometry) {
055        super(requireNonNull(canvas), requireNonNull(geometry));
056    }
057
058    /**
059     * Set the display of a mirrored copy of this geometry. This is called from
060     * "setDisplayed()" to turn on and off the display of mirrored geometry without
061     * changing the "mirrored state" of the object. This call does not affect the output
062     * of "isMirrored()".
063     *
064     * @param mirrored Flag indicating if the mirrored geometry should be displayed or
065     *                 not.
066     */
067    @Override
068    protected void internalSetMirrored(boolean mirrored) {
069        if (mirrored)
070            _symmSG.setWhichChild(Switch.CHILD_ALL);
071        else
072            _symmSG.setWhichChild(0);
073    }
074
075    /**
076     * Create a new Java 3D <code>Group</code> that contains the geometry contained in
077     * this object. This method is called from <code>createSceneGraph</code>. Sub-classes
078     * must over-ride this method to provide geometry specific implementations.
079     *
080     * @return New Java 3D Group that contains the geometry in this object.
081     * @see #createSceneGraph
082     */
083    @Override
084    protected Group createGeometry() {
085
086        J3DRenderingPrefs drawPrefs = getRenderingPrefs();
087        Shape3D lineShape = null;
088
089        //  Try to recycle a previous rendering of this geometry element if at all possible.
090        J3DCurve oldGroup = (J3DCurve)getOldJ3DGeomGroup();
091        if (oldGroup != null) {
092            J3DRenderingPrefs oldPrefs = oldGroup.getRenderingPrefs();
093            if (oldPrefs.getDrawTolerance().equals(drawPrefs.getDrawTolerance())) {
094                //System.out.println("Re-using lineShape");
095                //  Draw tolerance is unchanged, so re-use the existing Shape3D.
096                lineShape = (Shape3D)oldGroup._oldShape3D.cloneNode(true);
097
098                //  Has the line color changed?
099                if (!oldPrefs.getLineColor().equals(drawPrefs.getLineColor())) {
100                    //System.out.println("Changing line color");
101                    Color4f lineColor = drawPrefs.getLineColorJ3D();
102                    LineArray lineA = (LineArray)lineShape.getGeometry();
103                    int size = lineA.getVertexCount();
104                    for (int i = 0; i < size; ++i) {
105                        lineA.setColor(i, lineColor);
106                    }
107                }
108
109            }
110        }
111
112        //  If we couldn't recycle the lineShape, then create a new one.
113        if (lineShape == null) {
114            Curve thisCurve = this.getGeomElement();
115            LineArray lineA;
116            StackContext.enter();
117            try {
118
119                //      Create a line and a point array.
120                PointString<SubrangePoint> points = thisCurve.gridToTolerance(drawPrefs.getDrawTolerance());
121                //System.out.println("points.size() = " + points.size());
122
123                int size = points.size();
124                int nVerts = 2 * (size - 1);
125                lineA = new LineArray(nVerts, LineArray.COORDINATES | LineArray.COLOR_4);
126
127                //      Set the coordinates of the line.
128                Color4f lineColor = drawPrefs.getLineColorJ3D();
129                Point3d oldPoint = null;
130                int lineIndex = 0;
131                for (int i = 0; i < size; ++i) {
132                    GeomPoint point = points.get(i);
133                    //  Convert all geometry to meters.
134                    int dims = point.getPhyDimension();
135                    double x = point.getValue(0, SI.METER);
136                    double y = (dims > 1 ? point.getValue(1, SI.METER) : 0);
137                    double z = (dims > 2 ? point.getValue(2, SI.METER) : 0);
138                    Point3d point3d = new Point3d(x, y, z);
139
140                    if (i != 0) {
141                        lineA.setCoordinate(lineIndex, oldPoint);               //      Store start of line segment.
142                        lineA.setColor(lineIndex++, lineColor);
143                        lineA.setCoordinate(lineIndex, point3d);                //      Store end of line segment.
144                        lineA.setColor(lineIndex++, lineColor);
145                    }
146
147                    oldPoint = point3d;
148                }
149
150            } finally {
151                StackContext.exit();
152            }
153
154            //  Create a 3D shape from the line array
155            lineShape = new GeomShape3D(thisCurve, lineA);
156        }
157        _oldShape3D = (Shape3D)lineShape.cloneNode(true);   //  Save off for potential re-use in the future.
158
159        //      Define the appearance of the line.
160        Appearance lineAppearance = new Appearance();
161        LineAttributes lineAttrib = new LineAttributes();
162        lineAttrib.setLineWidth(drawPrefs.getLineWidth());
163        lineAppearance.setLineAttributes(lineAttrib);
164        lineShape.setAppearance(lineAppearance);
165
166        //      Add the basic unmirrored geometry to the symmetry switch.
167        _symmSG = new Switch();
168        _symmSG.setCapability(Switch.ALLOW_SWITCH_READ);
169        _symmSG.setCapability(Switch.ALLOW_SWITCH_WRITE);
170        _symmSG.addChild(lineShape);
171
172        //      Clone the basic geometry to make the mirrored geometry.
173        Node mirrored = lineShape.cloneTree();
174
175        //      Create a mirror across the XZ plane of symmetry transform.
176        Transform3D symmT = new Transform3D();
177        symmT.setScale(new Vector3d(1, -1, 1));
178        TransformGroup symmTG = new TransformGroup(symmT);
179
180        //      Add the mirrored geometry to the symmetry transform group.
181        symmTG.addChild(mirrored);
182
183        //      Add the mirrored geometry to the symmetry switch.
184        _symmSG.addChild(symmTG);
185
186        //      By default, display only the main geometry (not the mirrored).
187        _symmSG.setWhichChild(0);
188
189        return _symmSG;
190    }
191
192    /**
193     * Creates a new instance of the node. This routine is called by
194     * <code>cloneTree</code> to duplicate the current node.
195     *
196     * @param forceDuplicate when set to <code>true</code>, causes the
197     *                       <code>duplicateOnCloneTree</code> flag to be ignored. When
198     *                       <code>false</code>, the value of each node's
199     *                       <code>duplicateOnCloneTree</code> variable determines whether
200     *                       NodeComponent data is duplicated or copied.
201     * @return A new instance of this Java3D node.
202     */
203    @Override
204    public Node cloneNode(boolean forceDuplicate) {
205        J3DCurve node = new J3DCurve(this.getCanvas3D(), this.getGeomElement());
206        node.duplicateNode(this, forceDuplicate);
207        return node;
208    }
209
210}