001/**
002 * J3DGeomPlane -- A Java3D node that represents a GeomPlane 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.*;
023import static java.util.Objects.requireNonNull;
024import javax.measure.quantity.Dimensionless;
025import javax.measure.unit.SI;
026import org.jogamp.java3d.*;
027import org.jogamp.java3d.PointArray;
028import org.jogamp.vecmath.*;
029import javolution.context.StackContext;
030
031/**
032 * A Java 3D node that represents a <code>GeomPlane</code> in a Java 3D scene graph.
033 *
034 * <p> Modified by: Joseph A. Huwaldt </p>
035 *
036 * @author Joseph A. Huwaldt, Date: June 14, 2009
037 * @version June 4, 2023
038 */
039public class J3DGeomPlane extends J3DGeomGroup<GeomPlane> {
040
041    //  The switch for main or mirrored geometry.
042    private Switch _symmSG;
043
044    /**
045     * Construct a J3DGeomPlane using the specified GeomPlane as a reference.
046     * 
047     * @param canvas   The canvas that the geometry is being rendered into.
048     * @param geometry The GeomSS plane to be turned into a Java3D node.
049     */
050    public J3DGeomPlane(GeomSSCanvas3D canvas, GeomPlane geometry) {
051        super(requireNonNull(canvas), requireNonNull(geometry));
052    }
053
054    /**
055     * Set the display of a mirrored copy of this geometry. This is called from
056     * "setDisplayed()" to turn on and off the display of mirrored geometry without
057     * changing the "mirrored state" of the object. This call does not affect the output
058     * of "isMirrored()".
059     */
060    @Override
061    protected void internalSetMirrored(boolean mirrored) {
062        if (mirrored)
063            _symmSG.setWhichChild(Switch.CHILD_ALL);
064        else
065            _symmSG.setWhichChild(0);
066    }
067
068    /**
069     * Create a new Java 3D <code>Group</code> that contains the geometry contained in
070     * this object. This method is called from <code>createSceneGraph</code>. Sub-classes
071     * must over-ride this method to provide geometry specific implementations.
072     *
073     * @return New Java 3D Group that contains the geometry in this object.
074     * @see #createSceneGraph
075     */
076    @Override
077    protected Group createGeometry() {
078
079        StackContext.enter();
080        try {
081            //  Create a line and a point array.
082            GeomPlane thisPlane = this.getGeomElement();
083            GeomVector normalV = thisPlane.getNormal();
084
085            //  Set the coordinates of the line.
086            J3DRenderingPrefs drawPrefs = getRenderingPrefs();
087            Color4f pointColor = drawPrefs.getPointColorJ3D();
088            Color4f lineColor = drawPrefs.getLineColorJ3D();
089
090            //  *** First create the vector display elements (a point and a line).
091            GeomPoint o = normalV.getOrigin().to(SI.METER);     //      Convert all geometry to meters.
092            int dims = o.getPhyDimension();
093            double x = o.getValue(0);
094            double y = (dims > 1 ? o.getValue(1) : 0);
095            double z = (dims > 2 ? o.getValue(2) : 0);
096            Point3d point3d = new Point3d(x, y, z);
097
098            LineArray lineA = new LineArray(2, LineArray.COORDINATES | LineArray.COLOR_4);
099            PointArray pointA = new PointArray(1, PointArray.COORDINATES | PointArray.COLOR_4);
100            lineA.setCoordinate(0, point3d);                    //      Store start of line segment.
101            lineA.setColor(0, lineColor);
102            pointA.setCoordinate(0, point3d);
103            pointA.setColor(0, pointColor);
104
105            //  Create a point representing the other end of the vector.
106            if (!normalV.getUnit().equals(Dimensionless.UNIT))
107                normalV = (GeomVector)normalV.to(SI.METER);                     //      Convert all geometry to meters.
108
109            x += normalV.getValue(0);
110            y += (dims > 1 ? normalV.getValue(1) : 0);
111            z += (dims > 2 ? normalV.getValue(2) : 0);
112            point3d = new Point3d(x, y, z);
113
114            lineA.setCoordinate(1, point3d);                    //      Store start of line segment.
115            lineA.setColor(1, lineColor);
116
117            //  Create a 3D shape from the line array
118            Shape3D vectorLineShape = new GeomShape3D(thisPlane, lineA);
119
120            //  Define the appearance of the line.
121            Appearance lineAppearance = new Appearance();
122            LineAttributes lineAttrib = new LineAttributes();
123            lineAttrib.setLineWidth(drawPrefs.getLineWidth());
124            lineAppearance.setLineAttributes(lineAttrib);
125            vectorLineShape.setAppearance(lineAppearance);
126
127            //  Create a 3D shape from the point array
128            Shape3D vectorPointShape = new GeomShape3D(thisPlane, pointA);
129            Appearance appearance = new Appearance();
130            vectorPointShape.setAppearance(appearance);
131            appearance.setPointAttributes(new PointAttributes(drawPrefs.getPointSize(), true));
132
133            //  *** Now create a box around the vector that is in the plane.
134            GeomVector<Dimensionless> yhat = GeomUtil.calcYHat(normalV);
135            GeomVector<Dimensionless> xhat = GeomUtil.calcXHat(normalV, yhat);
136            lineA = new LineArray(8, LineArray.COORDINATES | LineArray.COLOR_4);
137
138            //  Set 0 (upper right) and 2 (lower left) points.
139            Point p = Point.valueOf(xhat.plus(yhat)).plus(o);
140            x = p.getValue(0);
141            y = (dims > 1 ? p.getValue(1) : 0);
142            z = (dims > 2 ? p.getValue(2) : 0);
143            point3d = new Point3d(x, y, z);
144            lineA.setCoordinate(0, point3d);
145            lineA.setCoordinate(7, point3d);
146            lineA.setColor(0, lineColor);
147            lineA.setColor(7, lineColor);
148
149            p = Point.valueOf(xhat.plus(yhat)).opposite().plus(o);
150            x = p.getValue(0);
151            y = (dims > 1 ? p.getValue(1) : 0);
152            z = (dims > 2 ? p.getValue(2) : 0);
153            point3d = new Point3d(x, y, z);
154            lineA.setCoordinate(3, point3d);
155            lineA.setCoordinate(4, point3d);
156            lineA.setColor(3, lineColor);
157            lineA.setColor(4, lineColor);
158
159            //  Set 1 (upper left) and 3 (lower right) points.
160            p = Point.valueOf(xhat.opposite().plus(yhat)).plus(o);
161            x = p.getValue(0);
162            y = (dims > 1 ? p.getValue(1) : 0);
163            z = (dims > 2 ? p.getValue(2) : 0);
164            point3d = new Point3d(x, y, z);
165            lineA.setCoordinate(1, point3d);
166            lineA.setCoordinate(2, point3d);
167            lineA.setColor(1, lineColor);
168            lineA.setColor(2, lineColor);
169
170            p = Point.valueOf(xhat.opposite().plus(yhat)).opposite().plus(o);
171            x = p.getValue(0);
172            y = (dims > 1 ? p.getValue(1) : 0);
173            z = (dims > 2 ? p.getValue(2) : 0);
174            point3d = new Point3d(x, y, z);
175            lineA.setCoordinate(5, point3d);
176            lineA.setCoordinate(6, point3d);
177            lineA.setColor(5, lineColor);
178            lineA.setColor(6, lineColor);
179
180            //  Create a 3D shape from the line array
181            Shape3D planeLineShape = new GeomShape3D(thisPlane, lineA);
182            planeLineShape.setAppearance(lineAppearance);
183
184            //  Create a group for the main geometry (unmirrored)
185            //  that contains each part of the display properties of a GeomPlane.
186            Group renderGroup = new Switch(Switch.CHILD_ALL);
187            renderGroup.setCapability(Switch.ALLOW_SWITCH_READ);
188            renderGroup.setCapability(Switch.ALLOW_SWITCH_WRITE);
189
190            //  Add the geometry to the display switch.
191            renderGroup.addChild(vectorLineShape);
192            renderGroup.addChild(vectorPointShape);
193            renderGroup.addChild(planeLineShape);
194
195            //  Add the basic unmirrored geometry to the symmetry switch.
196            _symmSG = new Switch();
197            _symmSG.setCapability(Switch.ALLOW_SWITCH_READ);
198            _symmSG.setCapability(Switch.ALLOW_SWITCH_WRITE);
199            _symmSG.addChild(renderGroup);
200
201            //  Clone the basic geometry to make the mirrored geometry.
202            Node mirrored = renderGroup.cloneTree();
203
204            //  Create a mirror across the XZ plane of symmetry transform.
205            Transform3D symmT = new Transform3D();
206            symmT.setScale(new Vector3d(1, -1, 1));
207            TransformGroup symmTG = new TransformGroup(symmT);
208
209            //  Add the mirrored geometry to the symmetry transform group.
210            symmTG.addChild(mirrored);
211
212            //  Add the mirrored geometry to the symmetry switch.
213            _symmSG.addChild(symmTG);
214
215            //  By default, display only the main geometry (not the mirrored).
216            _symmSG.setWhichChild(0);
217
218            return _symmSG;
219
220        } finally {
221            StackContext.exit();
222        }
223    }
224
225    /**
226     * Creates a new instance of the node. This routine is called by
227     * <code>cloneTree</code> to duplicate the current node.
228     *
229     * @param forceDuplicate when set to <code>true</code>, causes the
230     *                       <code>duplicateOnCloneTree</code> flag to be ignored. When
231     *                       <code>false</code>, the value of each node's
232     *                       <code>duplicateOnCloneTree</code> variable determines whether
233     *                       NodeComponent data is duplicated or copied.
234     * @return A new instance of this Java3D node.
235     */
236    @Override
237    public Node cloneNode(boolean forceDuplicate) {
238        J3DGeomPlane node = new J3DGeomPlane(this.getCanvas3D(), this.getGeomElement());
239        node.duplicateNode(this, forceDuplicate);
240        return node;
241    }
242}