001/** 002 * PointArrayShape3D -- A Java 3D Shape3D representation of a PointArray. 003 * 004 * Copyright (C) 2009-2025, by 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 Library 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 org.jogamp.java3d.utils.geometry.GeometryInfo; 022import org.jogamp.java3d.utils.geometry.NormalGenerator; 023import org.jogamp.java3d.utils.geometry.Stripifier; 024import geomss.geom.*; 025import geomss.geom.PointArray; 026import java.util.List; 027import static java.util.Objects.requireNonNull; 028import java.util.ResourceBundle; 029import javax.measure.quantity.Dimensionless; 030import javax.measure.unit.SI; 031import org.jogamp.java3d.*; 032import org.jogamp.vecmath.*; 033 034/** 035 * A Shape3D object based on a {@link geomss.geom.PointArray PointArray}. The default 036 * appearance of the geometry is solid filled grey with lighting enabled. 037 * 038 * <p> Modified by: Joseph A. Huwaldt </p> 039 * 040 * @author Joseph A. Huwaldt, Date: April 5, 2009 041 * @version February 17, 2025 042 */ 043public class PointArrayShape3D extends GeomShape3D { 044 045 /** 046 * The resource bundle for this class. 047 */ 048 private static final ResourceBundle RB = J3DGeomGroup.RB; 049 050 /** 051 * Construct a PointArrayShape3D using the specified GeomElement as a reference. 052 * 053 * @param parent The GeomSS geometry element that this Shape3D is associated with. 054 */ 055 private PointArrayShape3D(GeomElement parent) { 056 super(parent); 057 } 058 059 /** 060 * Construct a PointArrayShape3D object from the specified {@link PointArray}. 061 * 062 * @param parent The GeomSS geometry element that this Shape3D is associated with. 063 * @param array The PointArray to be converted into a J3D Shape3D object. 064 * @throws IllegalArgumentException if array.size() < 2 065 */ 066 public PointArrayShape3D(GeomElement parent, PointArray<? extends GeomPoint> array) { 067 super(parent); 068 069 // Check inputs. 070 if (array.size() < 2) 071 throw new IllegalArgumentException(RB.getString("notEnoughStrings")); 072 int numPoints = array.get(0).size(); 073 for (PointString string : array) { 074 if (string.size() < 2) 075 throw new IllegalArgumentException(RB.getString("notEnoughPoints")); 076 if (string.size() != numPoints) 077 throw new IllegalArgumentException(RB.getString("inconsistantStrings")); 078 } 079 080 setGeometry(createGeometry(array, null, false)); 081 } 082 083 /** 084 * Construct a PointArrayShape3D object from the specified {@link PointArray}. 085 * 086 * @param parent The GeomSS geometry element that this Shape3D is associated with. 087 * @param array The PointArray to be converted into a J3D Shape3D object. 088 * @param appearance The Appearance to use when rendering the point array. 089 * @throws IllegalArgumentException if array.size() < 2 090 */ 091 public PointArrayShape3D(GeomElement parent, PointArray<? extends GeomPoint> array, Appearance appearance) { 092 this(parent, array); 093 setAppearance(requireNonNull(appearance)); 094 } 095 096 /** 097 * Construct a PointArrayShape3D object from the specified {@link PointArray}. 098 * 099 * @param parent The GeomSS geometry element that this Shape3D is associated with. 100 * @param array The PointArray to be converted into a J3D Shape3D object. 101 * @param appearance The Appearance to use when rendering the point array. 102 * @param stripify Flag indicating if the array of quadrilateral panels should be 103 * converted into triangle strips for rendering or not. 104 * @throws IllegalArgumentException if array.size() < 2 105 */ 106 public PointArrayShape3D(GeomElement parent, PointArray<? extends GeomPoint> array, 107 Appearance appearance, boolean stripify) { 108 super(parent); 109 110 // Check inputs. 111 if (array.size() < 2) 112 throw new IllegalArgumentException(RB.getString("notEnoughStrings")); 113 int numPoints = array.get(0).size(); 114 for (PointString string : array) { 115 if (string.size() < 2) 116 throw new IllegalArgumentException(RB.getString("notEnoughPoints")); 117 if (string.size() != numPoints) 118 throw new IllegalArgumentException(RB.getString("inconsistantStrings")); 119 } 120 121 setGeometry(createGeometry(array, null, stripify)); 122 setAppearance(requireNonNull(appearance)); 123 } 124 125 /** 126 * Construct a PointArrayShape3D object from the specified {@link PointArray}. 127 * 128 * @param parent The GeomSS geometry element that this Shape3D is associated with. 129 * @param array The PointArray to be converted into a J3D Shape3D object. 130 * @param normals A list of lists of surface normals, one for each point in 131 * "array". If <code>null</code> is provided, the surface normals 132 * will be automatically estimated from the vertex data. 133 * @param appearance The Appearance to use when rendering the point array. 134 * @param stripify Flag indicating if the array of quadrilateral panels should be 135 * converted into triangle strips for rendering or not. 136 * @throws IllegalArgumentException if array.size() < 2 137 */ 138 public PointArrayShape3D(GeomElement parent, PointArray<? extends GeomPoint> array, 139 List<List<GeomVector<Dimensionless>>> normals, Appearance appearance, boolean stripify) { 140 super(parent); 141 142 // Check inputs. 143 if (array.size() < 2) 144 throw new IllegalArgumentException(RB.getString("notEnoughStrings")); 145 int numPoints = array.get(0).size(); 146 for (PointString string : array) { 147 if (string.size() < 2) 148 throw new IllegalArgumentException(RB.getString("notEnoughPoints")); 149 if (string.size() != numPoints) 150 throw new IllegalArgumentException(RB.getString("inconsistantStrings")); 151 } 152 153 setGeometry(createGeometry(array, requireNonNull(normals), stripify)); 154 setAppearance(requireNonNull(appearance)); 155 } 156 157 /** 158 * Used to create a new instance of the node. 159 * 160 * @param forceDuplicate when set to <code>true</code>, causes the 161 * <code>duplicateOnCloneTree</code> flag to be ignored. When 162 * <code>false</code>, the value of each node's 163 * <code>duplicateOnCloneTree</code> variable determines whether 164 * NodeComponent data is duplicated or copied. 165 * @return A new instance of this Java3D node. 166 */ 167 @Override 168 public Node cloneNode(boolean forceDuplicate) { 169 PointArrayShape3D usc = new PointArrayShape3D(this.getGeometryElement()); 170 usc.duplicateNode(this, forceDuplicate); 171 return usc; 172 } 173 174 /** 175 * Convert the PointArray, and an optional array of vertex normals, into a J3D 176 * Geometry object here. 177 */ 178 private Geometry createGeometry(PointArray<? extends GeomPoint> array, 179 List<List<GeomVector<Dimensionless>>> normals, boolean stripify) { 180 181 int numCols = array.size(); 182 int numPanelCols = numCols - 1; 183 int numRows = array.get(0).size(); 184 int numPanelRows = numRows - 1; 185 186 // Convert PointArray objects into J3D quadralaterals. 187 int vertexCount = numRows * numCols; 188 int indexCount = numPanelRows * numPanelCols * 4; 189 190 // Load in the geometry into required 1D arrays. 191 Point3f[] coordinates = new Point3f[vertexCount]; 192 Vector3f[] normalsArr = null; 193 if (normals != null) 194 normalsArr = new Vector3f[vertexCount]; 195 196 int vertex = 0; 197 int dim = array.getPhyDimension(); 198 for (int i = 0; i < numCols; ++i) { 199 PointString<?> string = array.get(i); 200 201 for (int j = 0; j < numRows; ++j) { 202 GeomPoint point = string.get(j); 203 // Convert all geometry to meters. 204 double x = point.getValue(0, SI.METER); 205 double y = (dim > 1 ? point.getValue(1, SI.METER) : 0); 206 double z = (dim > 2 ? point.getValue(2, SI.METER) : 0); 207 coordinates[vertex] = new Point3f((float)x, (float)y, (float)z); 208 209 if (normals != null) { 210 // If normal vectors supplied, create an array of normals in Java3D format. 211 GeomVector n = normals.get(i).get(j); 212 x = n.getValue(0); 213 y = (dim > 1 ? n.getValue(1) : 0); 214 z = (dim > 2 ? n.getValue(2) : 0); 215 normalsArr[vertex] = new Vector3f((float)x, (float)y, (float)z); 216 } 217 218 ++vertex; 219 } 220 } 221 222 // Set up the quad vertex indices (panel verticies). 223 int[] coordIndices = new int[indexCount]; 224 int index = 0; 225 vertex = 0; 226 for (int i = 0; i < numPanelCols; ++i) { 227 for (int j = 0; j < numPanelRows; ++j) { 228 coordIndices[index++] = vertex; 229 coordIndices[index++] = vertex + 1; 230 coordIndices[index++] = vertex + 1 + numRows; 231 coordIndices[index++] = vertex + numRows; 232 ++vertex; 233 } 234 ++vertex; 235 } 236 237 // Create a GeomInfo object for our geometry. 238 GeometryInfo geomInfo = new GeometryInfo(GeometryInfo.QUAD_ARRAY); 239 geomInfo.setCoordinates(coordinates); 240 geomInfo.setCoordinateIndices(coordIndices); 241 242 if (normals != null) { 243 // Use the supplied vertex normals. 244 geomInfo.setNormals(normalsArr); 245 geomInfo.setNormalIndices(coordIndices); 246 247 } else { 248 // Generate vertex normals automatically. 249 NormalGenerator ng = new NormalGenerator(); 250 ng.generateNormals(geomInfo); 251 } 252 253 if (stripify) { 254 // Stripify the geometry for increased performance. 255 Stripifier st = new Stripifier(); 256 st.stripify(geomInfo); 257 } 258 259 return geomInfo.getGeometryArray(); 260 } 261}