All Downloads are FREE. Search and download functionalities are using the official Maven repository.

us.ihmc.scs2.definition.visual.TriangleMesh3DFactories Maven / Gradle / Ivy

package us.ihmc.scs2.definition.visual;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.DoubleFunction;

import gnu.trove.list.array.TIntArrayList;
import us.ihmc.commons.MathTools;
import us.ihmc.euclid.Axis3D;
import us.ihmc.euclid.axisAngle.AxisAngle;
import us.ihmc.euclid.geometry.interfaces.ConvexPolygon2DReadOnly;
import us.ihmc.euclid.geometry.interfaces.LineSegment3DReadOnly;
import us.ihmc.euclid.geometry.tools.EuclidGeometryTools;
import us.ihmc.euclid.matrix.RotationMatrix;
import us.ihmc.euclid.orientation.interfaces.Orientation3DReadOnly;
import us.ihmc.euclid.shape.convexPolytope.interfaces.ConvexPolytope3DReadOnly;
import us.ihmc.euclid.shape.convexPolytope.interfaces.Face3DReadOnly;
import us.ihmc.euclid.shape.convexPolytope.interfaces.HalfEdge3DReadOnly;
import us.ihmc.euclid.shape.convexPolytope.interfaces.Vertex3DReadOnly;
import us.ihmc.euclid.shape.primitives.Box3D;
import us.ihmc.euclid.shape.primitives.Ramp3D;
import us.ihmc.euclid.shape.primitives.interfaces.BoxPolytope3DView;
import us.ihmc.euclid.tools.EuclidCoreTools;
import us.ihmc.euclid.transform.interfaces.RigidBodyTransformReadOnly;
import us.ihmc.euclid.tuple2D.Point2D32;
import us.ihmc.euclid.tuple2D.interfaces.Point2DReadOnly;
import us.ihmc.euclid.tuple3D.Point3D;
import us.ihmc.euclid.tuple3D.Point3D32;
import us.ihmc.euclid.tuple3D.UnitVector3D;
import us.ihmc.euclid.tuple3D.Vector3D;
import us.ihmc.euclid.tuple3D.Vector3D32;
import us.ihmc.euclid.tuple3D.interfaces.Point3DReadOnly;
import us.ihmc.euclid.tuple3D.interfaces.Tuple3DReadOnly;
import us.ihmc.euclid.tuple3D.interfaces.Vector3DBasics;
import us.ihmc.euclid.tuple3D.interfaces.Vector3DReadOnly;
import us.ihmc.log.LogTools;
import us.ihmc.scs2.definition.geometry.ArcTorus3DDefinition;
import us.ihmc.scs2.definition.geometry.Box3DDefinition;
import us.ihmc.scs2.definition.geometry.Capsule3DDefinition;
import us.ihmc.scs2.definition.geometry.Cone3DDefinition;
import us.ihmc.scs2.definition.geometry.ConvexPolytope3DDefinition;
import us.ihmc.scs2.definition.geometry.Cylinder3DDefinition;
import us.ihmc.scs2.definition.geometry.Ellipsoid3DDefinition;
import us.ihmc.scs2.definition.geometry.ExtrudedPolygon2DDefinition;
import us.ihmc.scs2.definition.geometry.GeometryDefinition;
import us.ihmc.scs2.definition.geometry.HemiEllipsoid3DDefinition;
import us.ihmc.scs2.definition.geometry.Polygon2DDefinition;
import us.ihmc.scs2.definition.geometry.Polygon3DDefinition;
import us.ihmc.scs2.definition.geometry.PyramidBox3DDefinition;
import us.ihmc.scs2.definition.geometry.Ramp3DDefinition;
import us.ihmc.scs2.definition.geometry.Sphere3DDefinition;
import us.ihmc.scs2.definition.geometry.Tetrahedron3DDefinition;
import us.ihmc.scs2.definition.geometry.Torus3DDefinition;
import us.ihmc.scs2.definition.geometry.TriangleMesh3DDefinition;
import us.ihmc.scs2.definition.geometry.TruncatedCone3DDefinition;

/**
 * This class provides factories to create generic meshes, i.e. {@code TriangleMesh3DDefinition}, to
 * represent a 3D shape.
 * 

* The generic mesh can be used to construct a triangle mesh in the format of the graphics engine in * which it will be rendered. *

*

* The construction methods assumes the following coordinate system convention: x is pointing * forward, y pointing left, z pointing upward. *

* * @author Sylvain Bertrand */ public class TriangleMesh3DFactories { private static final float TwoPi = 2.0f * (float) Math.PI; private static final float HalfPi = (float) Math.PI / 2.0f; private static final float SQRT3 = (float) Math.sqrt(3.0); private static final float SQRT6 = (float) Math.sqrt(6.0); private static final float HALF_SQRT3 = SQRT3 / 2.0f; private static final float THIRD_SQRT3 = SQRT3 / 3.0f; private static final float SIXTH_SQRT3 = SQRT3 / 6.0f; private static final float THIRD_SQRT6 = SQRT6 / 3.0f; private static final float FOURTH_SQRT6 = SQRT6 / 4.0f; private static final float ONE_THIRD = 1.0f / 3.0f; private TriangleMesh3DFactories() { // Prevent an object being generated. } /** * Tests the implementation of the given {@code description} and attempts to create the appropriate * triangle mesh. * * @param description the geometry to create the mesh for. * @return the generic triangle mesh or {@code null} if the given {@code description} is not * supported. */ public static TriangleMesh3DDefinition TriangleMesh(GeometryDefinition description) { if (description == null) return null; TriangleMesh3DDefinition mesh = null; if (description instanceof TriangleMesh3DDefinition) return (TriangleMesh3DDefinition) description; else if (description instanceof ArcTorus3DDefinition) mesh = ArcTorus((ArcTorus3DDefinition) description); else if (description instanceof Box3DDefinition) mesh = Box((Box3DDefinition) description); else if (description instanceof Capsule3DDefinition) mesh = Capsule((Capsule3DDefinition) description); else if (description instanceof Cone3DDefinition) mesh = Cone((Cone3DDefinition) description); else if (description instanceof ConvexPolytope3DDefinition) mesh = ConvexPolytope((ConvexPolytope3DDefinition) description); else if (description instanceof Cylinder3DDefinition) mesh = Cylinder((Cylinder3DDefinition) description); else if (description instanceof Ellipsoid3DDefinition) mesh = Ellipsoid((Ellipsoid3DDefinition) description); else if (description instanceof ExtrudedPolygon2DDefinition) mesh = ExtrudedPolygon((ExtrudedPolygon2DDefinition) description); else if (description instanceof HemiEllipsoid3DDefinition) mesh = HemiEllipsoid((HemiEllipsoid3DDefinition) description); else if (description instanceof Polygon2DDefinition) mesh = Polygon((Polygon2DDefinition) description); else if (description instanceof Polygon3DDefinition) mesh = Polygon((Polygon3DDefinition) description); else if (description instanceof PyramidBox3DDefinition) mesh = PyramidBox((PyramidBox3DDefinition) description); else if (description instanceof Sphere3DDefinition) mesh = Sphere((Sphere3DDefinition) description); else if (description instanceof Tetrahedron3DDefinition) mesh = Tetrahedron((Tetrahedron3DDefinition) description); else if (description instanceof Torus3DDefinition) mesh = Torus((Torus3DDefinition) description); else if (description instanceof TruncatedCone3DDefinition) mesh = TruncatedCone((TruncatedCone3DDefinition) description); else if (description instanceof Ramp3DDefinition) mesh = Ramp((Ramp3DDefinition) description); if (mesh == null) { LogTools.error("Unrecognized " + GeometryDefinition.class.getSimpleName() + ": " + description.getClass().getSimpleName()); return null; } else { if (description.getName() != null) mesh.setName(description.getName()); return mesh; } } /** * Creates a triangle mesh for a 3D sphere. *

* The sphere is centered at the origin and is a UV sphere, see * UV mapping. *

* * @param description the description holding the sphere's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Sphere(Sphere3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Sphere(description.getRadius(), description.getResolution(), description.getResolution()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D sphere. *

* The sphere is centered at the origin and is a UV sphere, see * UV mapping. *

* * @param radius the radius of the sphere. Each vertex is positioned at that distance * from the origin. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Sphere(double radius, int latitudeResolution, int longitudeResolution) { return Sphere((float) radius, latitudeResolution, longitudeResolution); } /** * Creates a triangle mesh for a 3D sphere. *

* The sphere is centered at the origin and is a UV sphere, see * UV mapping. *

* * @param radius the radius of the sphere. Each vertex is positioned at that distance * from the origin. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Sphere(float radius, int latitudeResolution, int longitudeResolution) { return Ellipsoid(radius, radius, radius, latitudeResolution, longitudeResolution); } /** * Creates a triangle mesh for a 3D ellipsoid. *

* The ellipsoid is centered at the origin and the algorithm is similar to a UV sphere, see * UV mapping. *

* * @param description the description holding the ellipsoid's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Ellipsoid(Ellipsoid3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Ellipsoid(description.getRadiusX(), description.getRadiusY(), description.getRadiusZ(), description.getResolution(), description.getResolution()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D ellipsoid. *

* The ellipsoid is centered at the origin and the algorithm is similar to a UV sphere, see * UV mapping. *

* * @param radiusX radius of the ellipsoid along the x-axis. * @param radiusY radius of the ellipsoid along the y-axis. * @param radiusZ radius of the ellipsoid along the z-axis. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Ellipsoid(double radiusX, double radiusY, double radiusZ, int latitudeResolution, int longitudeResolution) { return Ellipsoid((float) radiusX, (float) radiusY, (float) radiusZ, latitudeResolution, longitudeResolution); } /** * Creates a triangle mesh for a 3D ellipsoid. *

* The ellipsoid is centered at the origin and the algorithm is similar to a UV sphere, see * UV mapping. *

* * @param radiusX radius of the ellipsoid along the x-axis. * @param radiusY radius of the ellipsoid along the y-axis. * @param radiusZ radius of the ellipsoid along the z-axis. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Ellipsoid(float radiusX, float radiusY, float radiusZ, int latitudeResolution, int longitudeResolution) { if (longitudeResolution % 2 == 1) longitudeResolution += 1; int nPointsLongitude = longitudeResolution + 1; int nPointsLatitude = latitudeResolution + 1; // Reminder of longitude and latitude: http://www.geographyalltheway.com/ks3_geography/maps_atlases/longitude_latitude.htm Point3D32 points[] = new Point3D32[nPointsLatitude * nPointsLongitude]; Vector3D32[] normals = new Vector3D32[nPointsLatitude * nPointsLongitude]; Point2D32 textPoints[] = new Point2D32[nPointsLatitude * nPointsLongitude]; for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude; longitudeIndex++) { float longitudeAngle = TwoPi * ((float) longitudeIndex / (float) (nPointsLongitude - 1)); float cosLongitude = (float) Math.cos(longitudeAngle); float sinLongitude = (float) Math.sin(longitudeAngle); float textureX = (float) longitudeIndex / (float) (nPointsLongitude - 1); for (int latitudeIndex = 1; latitudeIndex < nPointsLatitude - 1; latitudeIndex++) { float latitudeAngle = (float) (-HalfPi + Math.PI * ((float) latitudeIndex / (nPointsLatitude - 1.0f))); float cosLatitude = (float) Math.cos(latitudeAngle); float sinLatitude = (float) Math.sin(latitudeAngle); int currentIndex = latitudeIndex * nPointsLongitude + longitudeIndex; float normalX = cosLongitude * cosLatitude; float normalY = sinLongitude * cosLatitude; float normalZ = sinLatitude; float vertexX = radiusX * normalX; float vertexY = radiusY * normalY; float vertexZ = radiusZ * normalZ; points[currentIndex] = new Point3D32(vertexX, vertexY, vertexZ); normals[currentIndex] = new Vector3D32(normalX, normalY, normalZ); float textureY = 0.5f * (1.0f - sinLatitude); textPoints[currentIndex] = new Point2D32(textureX, textureY); } textureX += 0.5f / (nPointsLongitude - 1.0f); // South pole int southPoleIndex = longitudeIndex; points[southPoleIndex] = new Point3D32(0.0f, 0.0f, -radiusZ); normals[southPoleIndex] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[southPoleIndex] = new Point2D32(textureX, 1.0f - 1.0f / 256.0f); // North pole int northPoleIndex = (nPointsLatitude - 1) * nPointsLongitude + longitudeIndex; points[northPoleIndex] = new Point3D32(0.0f, 0.0f, radiusZ); normals[northPoleIndex] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[northPoleIndex] = new Point2D32(textureX, 1.0f / 256.0f); } int numberOfTriangles = 2 * ((nPointsLatitude - 2) * nPointsLongitude - 1); int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; // Mid-latitude faces for (int latitudeIndex = 1; latitudeIndex < nPointsLatitude - 2; latitudeIndex++) { for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % nPointsLongitude; int nextLatitudeIndex = latitudeIndex + 1; // Lower triangles triangleIndices[index++] = latitudeIndex * nPointsLongitude + longitudeIndex; triangleIndices[index++] = latitudeIndex * nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * nPointsLongitude + longitudeIndex; // Upper triangles triangleIndices[index++] = latitudeIndex * nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * nPointsLongitude + longitudeIndex; } } // South pole faces for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % nPointsLongitude; triangleIndices[index++] = longitudeIndex; triangleIndices[index++] = nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nPointsLongitude + longitudeIndex; } // North pole faces for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % nPointsLongitude; triangleIndices[index++] = (nPointsLatitude - 1) * nPointsLongitude + longitudeIndex; triangleIndices[index++] = (nPointsLatitude - 2) * nPointsLongitude + longitudeIndex; triangleIndices[index++] = (nPointsLatitude - 2) * nPointsLongitude + nextLongitudeIndex; } return new TriangleMesh3DDefinition("Ellipsoid Factory", points, textPoints, normals, triangleIndices); } /** * Create a triangle mesh for the given polygon. * * @param description the description holding the polygon's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Polygon(Polygon2DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Polygon(null, description.getPolygonVertices(), description.isCounterClockwiseOrdered()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Create a triangle mesh for the given polygon. * * @param convexPolygon the polygon to create a mesh from. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Polygon(ConvexPolygon2DReadOnly convexPolygon) { return Polygon(null, convexPolygon); } /** * Create a triangle mesh for the given polygon. * * @param polygonPose the 3D pose of the polygon. Can be {@code null}, in which case no transform * is applied. * @param convexPolygon the polygon to create a mesh from. * @return the generic triangle mesh or {@code null} if {@code convexPolygon} is {@code null}. */ public static TriangleMesh3DDefinition Polygon(RigidBodyTransformReadOnly polygonPose, ConvexPolygon2DReadOnly convexPolygon) { if (convexPolygon == null) return null; else return Polygon(polygonPose, convexPolygon.getPolygonVerticesView(), !convexPolygon.isClockwiseOrdered()); } /** * Create a triangle mesh for the given polygon. *

* It is assumed that the polygon is convex and clockwise ordered. *

* * @param polygonPose the 3D pose of the polygon. Can be {@code null}, in which case no * transform is applied. * @param cwOrderedPolygonVertices the clockwise-ordered vertices of the polygon. * @return the generic triangle mesh or {@code null} if {@code cwOrderedPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition PolygonClockwise(RigidBodyTransformReadOnly polygonPose, List cwOrderedPolygonVertices) { return Polygon(polygonPose, cwOrderedPolygonVertices, false); } /** * Create a triangle mesh for the given polygon. *

* It is assumed that the polygon is convex and counter-clockwise ordered. *

* * @param polygonPose the 3D pose of the polygon. Can be {@code null}, in which case * no transform is applied. * @param ccwOrderedPolygonVertices the counter-clockwise-ordered vertices of the polygon. * @return the generic triangle mesh or {@code null} if {@code ccwOrderedPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition PolygonCounterClockwise(RigidBodyTransformReadOnly polygonPose, List ccwOrderedPolygonVertices) { return Polygon(polygonPose, ccwOrderedPolygonVertices, true); } /** * Create a triangle mesh for the given polygon. *

* It is assumed that the polygon is convex. *

* * @param polygonPose the 3D pose of the polygon. Can be {@code null}, in which case no * transform is applied. * @param polygonVertices the counter-clockwise-ordered vertices of the polygon. * @param counterClockwiseOrdered {@code true} to indicate that the vertices are counter-clockwise * ordered, {@code false} for clockwise ordered. * @return the generic triangle mesh or {@code null} if {@code convexPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition Polygon(RigidBodyTransformReadOnly polygonPose, List polygonVertices, boolean counterClockwiseOrdered) { if (polygonVertices == null) return null; int numberOfVertices = polygonVertices.size(); if (numberOfVertices < 3) return null; Point3D32[] vertices = new Point3D32[numberOfVertices]; Vector3D32[] normals = new Vector3D32[numberOfVertices]; Point2D32[] texturePoints = new Point2D32[numberOfVertices]; normals[0] = new Vector3D32(Axis3D.Z); if (polygonPose != null) polygonPose.transform(normals[0]); for (int i = 1; i < numberOfVertices; i++) normals[i] = new Vector3D32(normals[0]); float minX = polygonVertices.get(0).getX32(); float minY = polygonVertices.get(0).getY32(); float maxX = polygonVertices.get(0).getX32(); float maxY = polygonVertices.get(0).getY32(); for (int i = 1; i < numberOfVertices; i++) { Point2DReadOnly vertex2D; if (counterClockwiseOrdered) vertex2D = polygonVertices.get(i); else vertex2D = polygonVertices.get(numberOfVertices - 1 - i); if (vertex2D.getX32() > maxX) maxX = vertex2D.getX32(); else if (vertex2D.getX32() < minX) minX = vertex2D.getX32(); if (vertex2D.getY32() > maxY) maxY = vertex2D.getY32(); else if (vertex2D.getY32() < minY) minY = vertex2D.getY32(); } for (int i = 0; i < numberOfVertices; i++) { Point2DReadOnly vertex2D = polygonVertices.get(i); Point3D32 vertex3D = new Point3D32(); vertex3D.set(vertex2D); if (polygonPose != null) polygonPose.transform(vertex3D); vertices[i] = vertex3D; float textureX = 1.0f - (vertex2D.getY32() - minY) / (maxY - minY); float textureY = 1.0f - (vertex2D.getX32() - minX) / (maxX - minX); texturePoints[i] = new Point2D32(textureX, textureY); } if (!counterClockwiseOrdered) { reverse(vertices); reverse(texturePoints); } int numberOfTriangles = numberOfVertices - 2; // Do a naive way of splitting a polygon into triangles. Assumes convexity and ccw ordering. int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; for (int j = 2; j < vertices.length; j++) { triangleIndices[index++] = 0; triangleIndices[index++] = j - 1; triangleIndices[index++] = j; } return new TriangleMesh3DDefinition("Polygon Factory", vertices, texturePoints, normals, triangleIndices); } /** * Create a triangle mesh for the given polygon. * * @param description the description holding the polygon's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Polygon(Polygon3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Polygon(description.getPolygonVertices(), description.isCounterClockwiseOrdered()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Create a triangle mesh for the given polygon. *

* It is assumed that the polygon is convex and clockwise ordered. *

* * @param cwOrderedPolygonVertices the clockwise-ordered vertices of the polygon. * @return the generic triangle mesh or {@code null} if {@code cwOrderedPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition PolygonClockwise(List cwOrderedPolygonVertices) { return Polygon(cwOrderedPolygonVertices, false); } /** * Create a triangle mesh for the given polygon. *

* It is assumed that the polygon is convex and counter-clockwise ordered. *

* * @param ccwOrderedPolygonVertices the counter-clockwise-ordered vertices of the polygon. * @return the generic triangle mesh or {@code null} if {@code ccwOrderedPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition PolygonCounterClockwise(List ccwOrderedPolygonVertices) { return Polygon(ccwOrderedPolygonVertices, true); } /** * Create a triangle mesh for the given polygon. *

* It is assumed that the polygon is convex. *

* * @param convexPolygonVertices the vertices of the polygon. * @param counterClockwiseOrdered {@code true} to indicate that the vertices are counter-clockwise * ordered, {@code false} for clockwise ordered. * @return the generic triangle mesh or {@code null} if {@code convexPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition Polygon(List convexPolygonVertices, boolean counterClockwiseOrdered) { if (convexPolygonVertices == null) return null; int numberOfVertices = convexPolygonVertices.size(); if (numberOfVertices < 3) return null; int numberOfTriangles = numberOfVertices - 2; Vector3D32[] triangleNormals = new Vector3D32[numberOfTriangles]; for (int i = 0; i < numberOfTriangles; i++) { Vector3D32 triangleNormal = new Vector3D32(); EuclidGeometryTools.normal3DFromThreePoint3Ds(convexPolygonVertices.get(0), convexPolygonVertices.get(i + 1), convexPolygonVertices.get(i + 2), triangleNormal); triangleNormals[i] = triangleNormal; } boolean areAllNormalsEqual = true; Vector3D32 polygonNormal = new Vector3D32(); for (int i = 1; i < numberOfTriangles; i++) { if (areAllNormalsEqual && !triangleNormals[i - 1].epsilonEquals(triangleNormals[i], 1.0e-7)) { areAllNormalsEqual = false; } polygonNormal.add(triangleNormals[i]); } polygonNormal.normalize(); RotationMatrix polygonOrientation = new RotationMatrix(); EuclidGeometryTools.orientation3DFromZUpToVector3D(polygonNormal, polygonOrientation); Point2D32[] texturePoints = new Point2D32[numberOfVertices]; float minX = Float.POSITIVE_INFINITY; float minY = Float.POSITIVE_INFINITY; float maxX = Float.NEGATIVE_INFINITY; float maxY = Float.NEGATIVE_INFINITY; Point3D32 point = new Point3D32(); for (int i = 0; i < numberOfVertices; i++) { polygonOrientation.inverseTransform(convexPolygonVertices.get(i), point); Point2D32 texturePoint = new Point2D32(); texturePoint.set(-point.getY(), -point.getX()); texturePoints[i] = texturePoint; minX = Math.min(minX, texturePoint.getX32()); minY = Math.min(minY, texturePoint.getY32()); maxX = Math.max(maxX, texturePoint.getX32()); maxY = Math.max(maxY, texturePoint.getY32()); } for (int i = 0; i < numberOfVertices; i++) { texturePoints[i].sub(minX, minY); texturePoints[i].scale(1.0 / (maxX - minX), 1.0 / (maxY - minY)); } Point3D32[] vertices = convexPolygonVertices.stream().map(Point3D32::new).toArray(Point3D32[]::new); Vector3D32[] normals = new Vector3D32[numberOfVertices]; int[] triangleIndices = new int[3 * numberOfTriangles]; if (areAllNormalsEqual) { for (int i = 0; i < numberOfVertices; i++) { if (i < numberOfTriangles) normals[i] = triangleNormals[i]; else normals[i] = new Vector3D32(triangleNormals[0]); } } else { normals[0] = polygonNormal; normals[1] = triangleNormals[0]; for (int i = 1; i < numberOfVertices - 2; i++) { Vector3D32 normal = new Vector3D32(); normal.interpolate(triangleNormals[i - 1], triangleNormals[i], 0.5); normal.normalize(); normals[i] = normal; } Vector3D32 normal = new Vector3D32(); normal.interpolate(triangleNormals[numberOfTriangles - 2], triangleNormals[numberOfTriangles - 1], 0.5); normal.normalize(); normals[numberOfVertices - 2] = triangleNormals[numberOfTriangles - 1]; normals[numberOfVertices - 1] = triangleNormals[numberOfTriangles - 1]; } // Do a naive way of splitting a polygon into triangles. Assumes convexity and ccw ordering. int index = 0; for (int j = 2; j < numberOfVertices; j++) { triangleIndices[index++] = 0; triangleIndices[index++] = j - 1; triangleIndices[index++] = j; } return new TriangleMesh3DDefinition("Polygon Factory", vertices, texturePoints, normals, triangleIndices); } /** * Create a triangle mesh for the given polygon 2D and extrude it along the z-axis. * * @param description the description holding the polygon's properties. * @return the generic triangle mesh or {@code null} if {@code convexPolygon} is {@code null}. */ public static TriangleMesh3DDefinition ExtrudedPolygon(ExtrudedPolygon2DDefinition description) { TriangleMesh3DDefinition meshDataHolder = ExtrudedPolygon(description.getPolygonVertices(), description.isCounterClockwiseOrdered(), description.getTopZ(), description.getBottomZ()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Create a triangle mesh for the given polygon 2D and extrude it along the z-axis. * * @param convexPolygon the polygon to create a mesh from. * @param extrusionHeight thickness of the extrusion. If {@code extrusionHeight < 0}, the polygon is * extruded toward z negative. * @return the generic triangle mesh or {@code null} if {@code convexPolygon} is {@code null}. */ public static TriangleMesh3DDefinition ExtrudedPolygon(ConvexPolygon2DReadOnly convexPolygon, double extrusionHeight) { if (convexPolygon == null) return null; return ExtrudedPolygon(convexPolygon.getPolygonVerticesView(), false, extrusionHeight, 0.0); } /** * Create a triangle mesh for the given polygon 2D and extrude it along the z-axis. *

* It is assumed that the polygon is convex and counter-clockwise ordered. *

* * @param ccwOrderedPolygonVertices the counter-clockwise-ordered vertices of the polygon. * @param extrusionHeight thickness of the extrusion. The polygon is extruded upward along * the z-axis. * @return the generic triangle mesh or {@code null} if {@code ccwOrderedPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition ExtrudedPolygon(List ccwOrderedPolygonVertices, double extrusionHeight) { return ExtrudedPolygonCounterClockwise(ccwOrderedPolygonVertices, extrusionHeight, 0.0); } /** * Create a triangle mesh for the given polygon 2D and extrude it along the z-axis. *

* It is assumed that the polygon is convex and counter-clockwise ordered. *

* * @param ccwOrderedPolygonVertices the counter-clockwise-ordered vertices of the polygon. * @param topZ thickness of the extrusion. If {@code extrusionHeight < 0}, the * polygon is extruded toward z negative. * @param bottomZ offset along the z-axis that is applied on all the vertices of * the resulting mesh. * @return the generic triangle mesh or {@code null} if {@code ccwOrderedPolygonVertices} is * {@code null}. */ public static TriangleMesh3DDefinition ExtrudedPolygonCounterClockwise(List ccwOrderedPolygonVertices, double topZ, double bottomZ) { return ExtrudedPolygon(ccwOrderedPolygonVertices, true, topZ, bottomZ); } /** * Create a triangle mesh for the given polygon and extrude it along the z-axis. *

* It is assumed that the polygon is convex and counter-clockwise ordered. *

* * @param polygonVertices the counter-clockwise-ordered vertices of the polygon. * @param counterClockwiseOrdered {@code true} to indicate that the vertices are counter-clockwise * ordered, {@code false} for clockwise ordered. * @param topZ z-coordinate of the bottom face. * @param bottomZ z-coordinate of the top face. * @return the generic triangle mesh or {@code null} if {@code polygonVertices} is {@code null}. */ public static TriangleMesh3DDefinition ExtrudedPolygon(List polygonVertices, boolean counterClockwiseOrdered, double topZ, double bottomZ) { if (polygonVertices == null || polygonVertices.size() < 3) return null; int numberOfVertices = polygonVertices.size(); Point3D32 vertices[] = new Point3D32[6 * numberOfVertices + 4]; Vector3D32 normals[] = new Vector3D32[6 * numberOfVertices + 4]; Point2D32[] texturePoints = new Point2D32[6 * numberOfVertices + 4]; float minX = polygonVertices.get(0).getX32(); float minY = polygonVertices.get(0).getY32(); float maxX = polygonVertices.get(0).getX32(); float maxY = polygonVertices.get(0).getY32(); for (int i = 1; i < numberOfVertices; i++) { Point2DReadOnly vertex = polygonVertices.get(i); if (vertex.getX32() > maxX) maxX = vertex.getX32(); else if (vertex.getX32() < minX) minX = vertex.getX32(); if (vertex.getY32() > maxY) maxY = vertex.getY32(); else if (vertex.getY32() < minY) minY = vertex.getY32(); } for (int i = 0; i < numberOfVertices; i++) { Point2DReadOnly vertex; if (counterClockwiseOrdered) vertex = polygonVertices.get(i); else vertex = polygonVertices.get(numberOfVertices - 1 - i); float vertexX = vertex.getX32(); float vertexY = vertex.getY32(); // Vertices for bottom face vertices[i] = new Point3D32(vertexX, vertexY, (float) bottomZ); normals[i] = new Vector3D32(0.0f, 0.0f, -1.0f); texturePoints[i] = new Point2D32(0.5f - 0.5f * (vertexY - minY) / (maxY - minY) + 0.5f, 0.5f - 0.5f * (vertexX - minX) / (maxX - minX)); // Vertices for top face vertices[i + numberOfVertices] = new Point3D32(vertexX, vertexY, (float) (topZ)); normals[i + numberOfVertices] = new Vector3D32(0.0f, 0.0f, 1.0f); texturePoints[i + numberOfVertices] = new Point2D32(0.5f - 0.5f * (vertexY - minY) / (maxY - minY), 0.5f - 0.5f * (vertexX - minX) / (maxX - minX)); } double perimeter = 0.0; for (int i = 0; i < polygonVertices.size(); i++) { Point2DReadOnly vertex = polygonVertices.get(i); Point2DReadOnly nextVertex = polygonVertices.get((i + 1) % numberOfVertices); perimeter += vertex.distance(nextVertex); } double distanceAlongPerimeter = 0.0; double nextDistanceAlongPerimeter = 0.0; int nextIndex = counterClockwiseOrdered ? 0 : numberOfVertices - 1; Point2DReadOnly vertex; Point2DReadOnly nextVertex = polygonVertices.get(nextIndex); for (int i = 0; i < numberOfVertices + 1; i++) { vertex = nextVertex; if (counterClockwiseOrdered) { nextIndex++; nextIndex %= numberOfVertices; } else { nextIndex--; if (nextIndex < 0) nextIndex = numberOfVertices - 1; } nextVertex = polygonVertices.get(nextIndex); nextDistanceAlongPerimeter += nextVertex.distance(vertex); float vertexX = vertex.getX32(); float vertexY = vertex.getY32(); Point3D32 vertexBottom = new Point3D32(vertexX, vertexY, (float) bottomZ); Point3D32 vertexTop = new Point3D32(vertexX, vertexY, (float) (topZ)); Point3D32 nextVertexBottom = new Point3D32(nextVertex.getX32(), nextVertex.getY32(), (float) bottomZ); Point3D32 nextVertexTop = new Point3D32(nextVertex.getX32(), nextVertex.getY32(), (float) (topZ)); Vector3D normal = new Vector3D(); normal.sub(nextVertexTop, vertexTop); normal.cross(Axis3D.Z, normal); normal.negate(); normal.normalize(); int vertexBottomIndex = 2 * i + 2 * numberOfVertices; int nextVertexBottomIndex = vertexBottomIndex + 1; int vertexTopIndex = vertexBottomIndex + 2 * (numberOfVertices + 1); int nextVertexTopIndex = vertexTopIndex + 1; // Vertices for side faces // Bottom vertices[vertexBottomIndex] = vertexBottom; normals[vertexBottomIndex] = new Vector3D32(normal); texturePoints[vertexBottomIndex] = new Point2D32((float) (distanceAlongPerimeter / perimeter), 1.0f); vertices[nextVertexBottomIndex] = nextVertexBottom; normals[nextVertexBottomIndex] = new Vector3D32(normal); texturePoints[nextVertexBottomIndex] = new Point2D32((float) (nextDistanceAlongPerimeter / perimeter), 1.0f); // Top vertices[vertexTopIndex] = vertexTop; normals[vertexTopIndex] = new Vector3D32(normal); texturePoints[vertexTopIndex] = new Point2D32((float) (distanceAlongPerimeter / perimeter), 0.5f); vertices[nextVertexTopIndex] = nextVertexTop; normals[nextVertexTopIndex] = new Vector3D32(normal); texturePoints[nextVertexTopIndex] = new Point2D32((float) (nextDistanceAlongPerimeter / perimeter), 0.5f); distanceAlongPerimeter = nextDistanceAlongPerimeter; } int numberOfTriangles = 4 * numberOfVertices; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; for (int i = 1; i < numberOfVertices; i++) { // Bottom face triangleIndices[index++] = (i + 1) % numberOfVertices; triangleIndices[index++] = i; triangleIndices[index++] = 0; // Top face triangleIndices[index++] = numberOfVertices; triangleIndices[index++] = i + numberOfVertices; triangleIndices[index++] = (i + 1) % numberOfVertices + numberOfVertices; } // Side faces for (int i = 0; i < numberOfVertices + 1; i++) { // Lower triangle triangleIndices[index++] = 2 * i + 2 * numberOfVertices; triangleIndices[index++] = (2 * i + 1) % (2 * numberOfVertices + 2) + 2 * numberOfVertices; triangleIndices[index++] = 2 * i + 4 * numberOfVertices + 2; // Upper triangle triangleIndices[index++] = (2 * i + 1) % (2 * numberOfVertices + 2) + 2 * numberOfVertices; triangleIndices[index++] = (2 * i + 1) % (2 * numberOfVertices + 2) + 4 * numberOfVertices + 2; triangleIndices[index++] = 2 * i + 4 * numberOfVertices + 2; } return new TriangleMesh3DDefinition("ExtrudedPolygon Factory", vertices, texturePoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D hemi-ellipsoid, i.e. top half of an ellipsoid with the bottom * closed by a flat cap. * * @param description the description holding the hemi-ellipsoid's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition HemiEllipsoid(HemiEllipsoid3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = HemiEllipsoid(description.getRadiusX(), description.getRadiusY(), description.getRadiusZ(), description.getResolution(), description.getResolution()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D hemi-ellipsoid, i.e. top half of an ellipsoid with the bottom * closed by a flat cap. * * @param radiusX radius of the ellipsoid along the x-axis. * @param radiusY radius of the ellipsoid along the y-axis. * @param radiusZ radius of the ellipsoid along the z-axis. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition HemiEllipsoid(double radiusX, double radiusY, double radiusZ, int latitudeResolution, int longitudeResolution) { return HemiEllipsoid((float) radiusX, (float) radiusY, (float) radiusZ, latitudeResolution, longitudeResolution); } /** * Creates a triangle mesh for a 3D hemi-ellipsoid, i.e. top half of an ellipsoid with the bottom * closed by a flat cap. * * @param radiusX radius of the ellipsoid along the x-axis. * @param radiusY radius of the ellipsoid along the y-axis. * @param radiusZ radius of the ellipsoid along the z-axis. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition HemiEllipsoid(float radiusX, float radiusY, float radiusZ, int latitudeResolution, int longitudeResolution) { if (longitudeResolution % 2 == 1) longitudeResolution += 1; int nPointsLongitude = longitudeResolution + 1; int nPointsLatitude = latitudeResolution + 1; // Reminder of longitude and latitude: http://www.geographyalltheway.com/ks3_geography/maps_atlases/longitude_latitude.htm Point3D32 points[] = new Point3D32[(nPointsLatitude + 1) * nPointsLongitude]; Vector3D32[] normals = new Vector3D32[(nPointsLatitude + 1) * nPointsLongitude]; Point2D32 textPoints[] = new Point2D32[(nPointsLatitude + 1) * nPointsLongitude]; for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude; longitudeIndex++) { float longitudeAngle = TwoPi * ((float) longitudeIndex / (float) (nPointsLongitude - 1)); float cosLongitude = (float) Math.cos(longitudeAngle); float sinLongitude = (float) Math.sin(longitudeAngle); float textureX = (float) longitudeIndex / (float) (nPointsLongitude - 1); for (int latitudeIndex = 1; latitudeIndex < nPointsLatitude - 1; latitudeIndex++) { float latitudeAngle = HalfPi * ((float) (latitudeIndex - 1) / (float) (nPointsLatitude - 2)); float cosLatitude = (float) Math.cos(latitudeAngle); float sinLatitude = (float) Math.sin(latitudeAngle); int currentIndex = (latitudeIndex + 1) * nPointsLongitude + longitudeIndex; float normalX = cosLongitude * cosLatitude; float normalY = sinLongitude * cosLatitude; float normalZ = sinLatitude; float vertexX = radiusX * normalX; float vertexY = radiusY * normalY; float vertexZ = radiusZ * normalZ; points[currentIndex] = new Point3D32(vertexX, vertexY, vertexZ); normals[currentIndex] = new Vector3D32(normalX, normalY, normalZ); float textureY = 0.5f * (1.0f - sinLatitude); textPoints[currentIndex] = new Point2D32(textureX, textureY); } // Bottom side int currentIndex = nPointsLongitude + longitudeIndex; float vertexX = (float) (radiusX * Math.cos(longitudeAngle)); float vertexY = (float) (radiusY * Math.sin(longitudeAngle)); float vertexZ = 0.0f; points[currentIndex] = new Point3D32(vertexX, vertexY, vertexZ); normals[currentIndex] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[currentIndex] = new Point2D32(textureX, 0.5f); textureX += 0.5f / (nPointsLongitude - 1); // Bottom center int southPoleIndex = longitudeIndex; points[southPoleIndex] = new Point3D32(0.0f, 0.0f, 0.0f); normals[southPoleIndex] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[southPoleIndex] = new Point2D32(textureX, 1.0f - 1.0f / 256.0f); // North pole int northPoleIndex = nPointsLatitude * nPointsLongitude + longitudeIndex; points[northPoleIndex] = new Point3D32(0.0f, 0.0f, radiusZ); normals[northPoleIndex] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[northPoleIndex] = new Point2D32(textureX, 1.0f / 256.0f); } int numberOfTriangles = 2 * (nPointsLatitude - 1) * (nPointsLongitude - 1); int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; // Mid-latitude faces for (int latitudeIndex = 1; latitudeIndex < nPointsLatitude - 1; latitudeIndex++) { for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % nPointsLongitude; int nextLatitudeIndex = latitudeIndex + 1; // Lower triangles triangleIndices[index++] = latitudeIndex * nPointsLongitude + longitudeIndex; triangleIndices[index++] = latitudeIndex * nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * nPointsLongitude + longitudeIndex; // Upper triangles triangleIndices[index++] = latitudeIndex * nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * nPointsLongitude + longitudeIndex; } } // Bottom face for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % nPointsLongitude; triangleIndices[index++] = longitudeIndex; triangleIndices[index++] = nPointsLongitude + nextLongitudeIndex; triangleIndices[index++] = nPointsLongitude + longitudeIndex; } // North pole faces for (int longitudeIndex = 0; longitudeIndex < nPointsLongitude - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % nPointsLongitude; triangleIndices[index++] = (nPointsLatitude - 0) * nPointsLongitude + longitudeIndex; triangleIndices[index++] = (nPointsLatitude - 1) * nPointsLongitude + longitudeIndex; triangleIndices[index++] = (nPointsLatitude - 1) * nPointsLongitude + nextLongitudeIndex; } return new TriangleMesh3DDefinition("HemiEllipsoid Factory", points, textPoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D cylinder. *

* The cylinder's axis is aligned with the z-axis. When {@code centered} is true, the cylinder is * centered at the origin, when false its bottom face is centered at the origin. *

* * @param description the description holding the cylinder's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Cylinder(Cylinder3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Cylinder(description.getRadius(), description.getLength(), description.getResolution(), description.isCentered()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D cylinder. *

* The cylinder's axis is aligned with the z-axis. When {@code centered} is true, the cylinder is * centered at the origin, when false its bottom face is centered at the origin. *

* * @param radius the cylinder's radius. * @param height the cylinder's height or length. * @param resolution the resolution for the cylindrical part. * @param centered {@code true} to center the cylinder at the origin, {@code false} to center its * bottom face at the origin. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Cylinder(double radius, double height, int resolution, boolean centered) { return Cylinder((float) radius, (float) height, resolution, centered); } /** * Creates a triangle mesh for a 3D cylinder. *

* The cylinder's axis is aligned with the z-axis. When {@code centered} is true, the cylinder is * centered at the origin, when false its bottom face is centered at the origin. *

* * @param radius the cylinder's radius. * @param height the cylinder's height or length. * @param resolution the resolution for the cylindrical part. * @param centered {@code true} to center the cylinder at the origin, {@code false} to center its * bottom face at the origin. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Cylinder(float radius, float height, int resolution, boolean centered) { Point3D32 points[] = new Point3D32[4 * resolution + 2]; Vector3D32 normals[] = new Vector3D32[4 * resolution + 2]; Point2D32 texturePoints[] = new Point2D32[4 * resolution + 2]; float zTop = centered ? 0.5f * height : height; float zBottom = centered ? -0.5f * height : 0.0f; for (int i = 0; i < resolution; i++) { double angle = i * TwoPi / (resolution - 1.0); float cosAngle = (float) Math.cos(angle); float sinAngle = (float) Math.sin(angle); float vertexX = radius * cosAngle; float vertexY = radius * sinAngle; // Bottom vertices points[i] = new Point3D32(vertexX, vertexY, zBottom); normals[i] = new Vector3D32(0.0f, 0.0f, -1.0f); texturePoints[i] = new Point2D32(0.25f * (1f + sinAngle) + 0.5f, 0.25f * (1f - cosAngle)); // Top vertices points[i + resolution] = new Point3D32(vertexX, vertexY, zTop); normals[i + resolution] = new Vector3D32(0.0f, 0.0f, 1.0f); texturePoints[i + resolution] = new Point2D32(0.25f * (1f - sinAngle), 0.25f * (1f - cosAngle)); // Outer vertices // Bottom points[i + 2 * resolution] = new Point3D32(vertexX, vertexY, zBottom); normals[i + 2 * resolution] = new Vector3D32(cosAngle, sinAngle, 0.0f); texturePoints[i + 2 * resolution] = new Point2D32(i / (resolution - 1.0f), 1.0f); // Top points[i + 3 * resolution] = new Point3D32(vertexX, vertexY, zTop); normals[i + 3 * resolution] = new Vector3D32(cosAngle, sinAngle, 0.0f); texturePoints[i + 3 * resolution] = new Point2D32(i / (resolution - 1.0f), 0.5f); } // Center of bottom cap points[4 * resolution] = new Point3D32(0.0f, 0.0f, zBottom); normals[4 * resolution] = new Vector3D32(0.0f, 0.0f, -1.0f); texturePoints[4 * resolution] = new Point2D32(0.75f, 0.25f); // Center of top cap points[4 * resolution + 1] = new Point3D32(0.0f, 0.0f, zTop); normals[4 * resolution + 1] = new Vector3D32(0.0f, 0.0f, 1.0f); texturePoints[4 * resolution + 1] = new Point2D32(0.25f, 0.25f); int numberOfTriangles = 4 * resolution; int[] triangleIndices = new int[6 * numberOfTriangles]; int index = 0; for (int i = 0; i < resolution; i++) { // The bottom cap triangleIndices[index++] = (i + 1) % resolution; triangleIndices[index++] = i; triangleIndices[index++] = 4 * resolution; } for (int i = 0; i < resolution; i++) { // The top cap triangleIndices[index++] = 4 * resolution + 1; triangleIndices[index++] = i + resolution; triangleIndices[index++] = (i + 1) % resolution + resolution; } for (int i = 0; i < resolution; i++) { // The cylinder part // Lower triangle triangleIndices[index++] = i + 2 * resolution; triangleIndices[index++] = (i + 1) % resolution + 2 * resolution; triangleIndices[index++] = i + 3 * resolution; // Upper triangle triangleIndices[index++] = (i + 1) % resolution + 2 * resolution; triangleIndices[index++] = (i + 1) % resolution + 3 * resolution; triangleIndices[index++] = i + 3 * resolution; } return new TriangleMesh3DDefinition("Cylinder Factory", points, texturePoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D cone. *

* The cone's axis is aligned with the z-axis and is positioned such that the center of its bottom * face is at the origin. *

* * @param description the description holding the cone's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Cone(Cone3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Cone(description.getHeight(), description.getRadius(), description.getResolution()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D cone. *

* The cone's axis is aligned with the z-axis and is positioned such that the center of its bottom * face is at the origin. *

* * @param height the height of the cone. * @param radius the radius of the base. * @param resolution the resolution for the cylindrical part of the cone. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Cone(double height, double radius, int resolution) { return Cone((float) height, (float) radius, resolution); } /** * Creates a triangle mesh for a 3D cone. *

* The cone's axis is aligned with the z-axis and is positioned such that the center of its bottom * face is at the origin. *

* * @param height the height of the cone. * @param radius the radius of the base. * @param resolution the resolution for the cylindrical part of the cone. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Cone(float height, float radius, int resolution) { Point3D32[] vertices = new Point3D32[3 * resolution + 1]; Vector3D32[] normals = new Vector3D32[3 * resolution + 1]; Point2D32[] texturePoints = new Point2D32[3 * resolution + 1]; // This is equal to half of the opening angle of the cone at its top. Used to compute the normals. float slopeAngle = (float) Math.atan2(radius, height); float cosSlopeAngle = (float) Math.cos(slopeAngle); float sinSlopeAngle = (float) Math.sin(slopeAngle); for (int i = 0; i < resolution; i++) { double angle = i * TwoPi / (resolution - 1); float cosAngle = (float) Math.cos(angle); float sinAngle = (float) Math.sin(angle); float vertexX = radius * cosAngle; float vertexY = radius * sinAngle; // Vertices for the bottom part. vertices[i] = new Point3D32(vertexX, vertexY, 0.0f); normals[i] = new Vector3D32(0.0f, 0.0f, -1.0f); texturePoints[i] = new Point2D32(0.25f * (1f + sinAngle), 0.25f * (1f - cosAngle)); // Vertices for the side part. vertices[i + resolution] = new Point3D32(vertexX, vertexY, 0.0f); normals[i + resolution] = new Vector3D32(cosSlopeAngle * cosAngle, cosSlopeAngle * sinAngle, sinSlopeAngle); texturePoints[i + resolution] = new Point2D32(i / (resolution - 1.0f), 1.0f); vertices[i + 2 * resolution] = new Point3D32(0.0f, 0.0f, height); normals[i + 2 * resolution] = new Vector3D32(cosSlopeAngle * cosAngle, cosSlopeAngle * sinAngle, sinSlopeAngle); texturePoints[i + 2 * resolution] = new Point2D32(i / (resolution - 1.0f), 0.5f); } // The center of the bottom int bottomCenterIndex = 3 * resolution; vertices[bottomCenterIndex] = new Point3D32(0.0f, 0.0f, 0.0f); normals[bottomCenterIndex] = new Vector3D32(0.0f, 0.0f, -1.0f); texturePoints[bottomCenterIndex] = new Point2D32(0.25f, 0.25f); int numberOfTriangles = 2 * resolution; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; for (int i = 0; i < resolution; i++) { // The bottom face triangleIndices[index++] = bottomCenterIndex; triangleIndices[index++] = (i + 1) % resolution; triangleIndices[index++] = i; // The side faces triangleIndices[index++] = i + resolution; triangleIndices[index++] = (i + 1) % resolution + resolution; triangleIndices[index++] = i + 2 * resolution; } return new TriangleMesh3DDefinition("Cone Factory", vertices, texturePoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D truncated cone which base and top are ellipses. *

* The cone's axis is aligned with the z-axis and is positioned such that the center of its bottom * face is at the origin. *

* * @param description the description holding the cone's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition TruncatedCone(TruncatedCone3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = TruncatedCone(description.getHeight(), description.getBaseRadiusX(), description.getBaseRadiusY(), description.getTopRadiusX(), description.getTopRadiusY(), description.getResolution(), description.getCentered()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D truncated cone which base and top are ellipses. *

* The cone's axis is aligned with the z-axis and is positioned such that the center of its bottom * face is at the origin. *

* * @param height the cone's height. * @param baseRadiusX radius around the x-axis of the base ellipse. * @param baseRadiusY radius around the y-axis of the base ellipse. * @param topRadiusX radius around the x-axis of the top ellipse. * @param topRadiusY radius around the x-axis of the top ellipse. * @param resolution the resolution for the cylindrical part of the cone. * @param centered {@code true} to center the truncated cone at the origin, {@code false} to * center its bottom face at the origin. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition TruncatedCone(double height, double baseRadiusX, double baseRadiusY, double topRadiusX, double topRadiusY, int resolution, boolean centered) { return TruncatedCone((float) height, (float) baseRadiusX, (float) baseRadiusY, (float) topRadiusX, (float) topRadiusY, resolution, centered); } /** * Creates a triangle mesh for a 3D truncated cone which base and top are ellipses. *

* The cone's axis is aligned with the z-axis and is positioned such that the center of its bottom * face is at the origin. *

* * @param height the cone's height. * @param baseRadiusX radius around the x-axis of the base ellipse. * @param baseRadiusY radius around the y-axis of the base ellipse. * @param topRadiusX radius around the x-axis of the top ellipse. * @param topRadiusY radius around the x-axis of the top ellipse. * @param resolution the resolution for the cylindrical part of the cone. * @param centered {@code true} to center the truncated cone at the origin, {@code false} to * center its bottom face at the origin. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition TruncatedCone(float height, float baseRadiusX, float baseRadiusY, float topRadiusX, float topRadiusY, int resolution, boolean centered) { Point3D32 points[] = new Point3D32[4 * resolution + 2]; Vector3D32[] normals = new Vector3D32[4 * resolution + 2]; Point2D32[] textPoints = new Point2D32[4 * resolution + 2]; float topZ = centered ? 0.5f * height : height; float bottomZ = centered ? -0.5f * height : 0.0f; for (int i = 0; i < resolution; i++) { double angle = i * TwoPi / (resolution - 1); float cosAngle = (float) Math.cos(angle); float sinAngle = (float) Math.sin(angle); float baseX = baseRadiusX * cosAngle; float baseY = baseRadiusY * sinAngle; float topX = topRadiusX * cosAngle; float topY = topRadiusY * sinAngle; // Bottom face vertices points[i] = new Point3D32(baseX, baseY, bottomZ); normals[i] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[i] = new Point2D32(0.25f * (1f + sinAngle) + 0.5f, 0.25f * (1f - cosAngle)); // Top face vertices points[i + resolution] = new Point3D32(topX, topY, topZ); normals[i + resolution] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[i + resolution] = new Point2D32(0.25f * (1f - sinAngle), 0.25f * (1f - cosAngle)); // Cone face float currentBaseRadius = (float) Math.sqrt(baseX * baseX + baseY * baseY); float currentTopRadius = (float) Math.sqrt(topX * topX + topY * topY); float openingAngle = (float) Math.atan((currentBaseRadius - currentTopRadius) / height); float baseAngle = (float) Math.atan2(baseY, baseX); float topAngle = (float) Math.atan2(topY, topX); points[i + 2 * resolution] = new Point3D32(baseX, baseY, bottomZ); normals[i + 2 * resolution] = new Vector3D32((float) (Math.cos(baseAngle) * Math.cos(openingAngle)), (float) (Math.sin(baseAngle) * Math.cos(openingAngle)), (float) Math.sin(openingAngle)); textPoints[i + 2 * resolution] = new Point2D32(i / (resolution - 1.0f), 1.0f); points[i + 3 * resolution] = new Point3D32(topX, topY, topZ); normals[i + 3 * resolution] = new Vector3D32((float) (Math.cos(topAngle) * Math.cos(openingAngle)), (float) (Math.sin(topAngle) * Math.cos(openingAngle)), (float) Math.sin(openingAngle)); textPoints[i + 3 * resolution] = new Point2D32(i / (resolution - 1.0f), 0.5f); } // Bottom center points[4 * resolution] = new Point3D32(0.0f, 0.0f, bottomZ); normals[4 * resolution] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[4 * resolution] = new Point2D32(0.75f, 0.25f); // Top center points[4 * resolution + 1] = new Point3D32(0.0f, 0.0f, topZ); normals[4 * resolution + 1] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[4 * resolution + 1] = new Point2D32(0.25f, 0.25f); int numberOfTriangles = 4 * resolution; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; for (int i = 0; i < resolution; i++) { // Bottom face triangleIndices[index++] = 4 * resolution; triangleIndices[index++] = (i + 1) % resolution; triangleIndices[index++] = i; // Top face triangleIndices[index++] = 4 * resolution + 1; triangleIndices[index++] = i + resolution; triangleIndices[index++] = (i + 1) % resolution + resolution; //Cone face: lower triangle triangleIndices[index++] = i + 2 * resolution; triangleIndices[index++] = (i + 1) % resolution + 2 * resolution; triangleIndices[index++] = i + 3 * resolution; //Cone face: upper triangle triangleIndices[index++] = (i + 1) % resolution + 2 * resolution; triangleIndices[index++] = (i + 1) % resolution + 3 * resolution; triangleIndices[index++] = i + 3 * resolution; } return new TriangleMesh3DDefinition("TruncatedCone Factory", points, textPoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D torus. *

* The torus' axis is aligned with the z-axis and its centroid at the origin. *

* * @param description the description holding the torus's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Torus(Torus3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Torus(description.getMajorRadius(), description.getMinorRadius(), description.getResolution()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D torus. *

* The torus' axis is aligned with the z-axis and its centroid at the origin. *

* * @param majorRadius the radius from the torus centroid to the tube center. * @param minorRadius the radius of the tube. * @param resolution used to define the longitudinal and radial resolutions. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Torus(double majorRadius, double minorRadius, int resolution) { return Torus((float) majorRadius, (float) minorRadius, resolution); } /** * Creates a triangle mesh for a 3D torus. *

* The torus' axis is aligned with the z-axis and its centroid at the origin. *

* * @param majorRadius the radius from the torus centroid to the tube center. * @param minorRadius the radius of the tube. * @param resolution used to define the longitudinal and radial resolutions. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Torus(float majorRadius, float minorRadius, int resolution) { return ArcTorus(0.0f, (float) (2.0 * Math.PI), majorRadius, minorRadius, resolution); } /** * Creates a triangle mesh for an open partial 3D torus. *

* The torus' axis is aligned with the z-axis and its centroid at the origin. *

* * @param description the description holding the torus's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition ArcTorus(ArcTorus3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = ArcTorus(description.getStartAngle(), description.getEndAngle(), description.getMajorRadius(), description.getMinorRadius(), description.getResolution()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for an open partial 3D torus. *

* The torus' axis is aligned with the z-axis and its centroid at the origin. *

* * @param startAngle the angle at which the torus starts. The angle is in radians, it is expressed * with respect to the x-axis, and a positive angle corresponds to a * counter-clockwise rotation. * @param endAngle the angle at which the torus ends. If {@code startAngle == endAngle} the torus * will be closed. The angle is in radians, it is expressed with respect to the * x-axis, and a positive angle corresponds to a counter-clockwise rotation. * @param majorRadius the radius from the torus centroid to the tube center. * @param minorRadius the radius of the tube. * @param resolution used to define the longitudinal and radial resolutions. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition ArcTorus(double startAngle, double endAngle, double majorRadius, double minorRadius, int resolution) { return ArcTorus((float) startAngle, (float) endAngle, (float) majorRadius, (float) minorRadius, resolution); } /** * Creates a triangle mesh for an open partial 3D torus. *

* The torus' axis is aligned with the z-axis and its centroid at the origin. *

* * @param startAngle the angle at which the torus starts. The angle is in radians, it is expressed * with respect to the x-axis, and a positive angle corresponds to a * counter-clockwise rotation. * @param endAngle the angle at which the torus ends. If {@code startAngle == endAngle} the torus * will be closed. The angle is in radians, it is expressed with respect to the * x-axis, and a positive angle corresponds to a counter-clockwise rotation. * @param majorRadius the radius from the torus centroid to the tube center. * @param minorRadius the radius of the tube. * @param resolution used to define the longitudinal and radial resolutions. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition ArcTorus(float startAngle, float endAngle, float majorRadius, float minorRadius, int resolution) { startAngle = (float) EuclidCoreTools.shiftAngleInRange(startAngle, 0.0); endAngle = (float) EuclidCoreTools.shiftAngleInRange(endAngle, 0.0); if (EuclidCoreTools.epsilonEquals(endAngle, 0.0, 1.0e-6)) endAngle = TwoPi; float torusSpanAngle = endAngle - startAngle; boolean isClosed = MathTools.epsilonEquals(torusSpanAngle, TwoPi, 1.0e-3); // Make things a bit clearer. int majorN = resolution; // Make things a bit clearer. int minorN = resolution; float stepAngle = (endAngle - startAngle) / (majorN - 1); int numberOfVertices = isClosed ? majorN * minorN : majorN * minorN + 2 * (resolution + 1); Point3D32 points[] = new Point3D32[numberOfVertices]; Vector3D32[] normals = new Vector3D32[numberOfVertices]; Point2D32[] texturePoints = new Point2D32[numberOfVertices]; float centerX, centerY; float pX, pY, pZ; float textureY, textureX; // Core part of the torus for (int majorIndex = 0; majorIndex < majorN; majorIndex++) { float majorAngle = startAngle + majorIndex * stepAngle; float cosMajorAngle = (float) Math.cos(majorAngle); float sinMajorAngle = (float) Math.sin(majorAngle); centerX = majorRadius * cosMajorAngle; centerY = majorRadius * sinMajorAngle; textureY = (float) majorIndex / (float) (majorN - 1); for (int minorIndex = 0; minorIndex < minorN; minorIndex++) { int currentIndex = majorIndex * minorN + minorIndex; float minorAngle = minorIndex * 2.0f * (float) Math.PI / (minorN - 1.0f); float cosMinorAngle = (float) Math.cos(minorAngle); float sinMinorAngle = (float) Math.sin(minorAngle); pX = centerX + minorRadius * cosMajorAngle * cosMinorAngle; pY = centerY + minorRadius * sinMajorAngle * cosMinorAngle; pZ = minorRadius * sinMinorAngle; points[currentIndex] = new Point3D32(pX, pY, pZ); normals[currentIndex] = new Vector3D32(cosMajorAngle * cosMinorAngle, sinMajorAngle * cosMinorAngle, sinMinorAngle); textureX = (float) minorIndex / (float) (minorN - 1); if (!isClosed) textureX *= 0.5f; texturePoints[currentIndex] = new Point2D32(textureX, textureY); } } int lastMajorIndex = isClosed ? majorN : majorN - 1; int numberOfTriangles = 2 * lastMajorIndex * minorN; if (!isClosed) numberOfTriangles += 2 * minorN; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; // Torus core for (int majorIndex = 0; majorIndex < lastMajorIndex; majorIndex++) { for (int minorIndex = 0; minorIndex < minorN; minorIndex++) { int nextMajorIndex = (majorIndex + 1) % majorN; int nextMinorIndex = (minorIndex + 1) % minorN; triangleIndices[index++] = nextMajorIndex * minorN + minorIndex; triangleIndices[index++] = nextMajorIndex * minorN + nextMinorIndex; triangleIndices[index++] = majorIndex * minorN + nextMinorIndex; triangleIndices[index++] = nextMajorIndex * minorN + minorIndex; triangleIndices[index++] = majorIndex * minorN + nextMinorIndex; triangleIndices[index++] = majorIndex * minorN + minorIndex; } } // Close both ends when the torus is open if (!isClosed) { // First end float cosStartAngle = (float) Math.cos(startAngle); float sinStartAngle = (float) Math.sin(startAngle); centerX = majorRadius * cosStartAngle; centerY = majorRadius * sinStartAngle; for (int minorIndex = 0; minorIndex < minorN; minorIndex++) { int currentIndex = majorN * minorN + minorIndex; float minorAngle = minorIndex * 2.0f * (float) Math.PI / minorN; float cosMinorAngle = (float) Math.cos(minorAngle); float sinMinorAngle = (float) Math.sin(minorAngle); pX = centerX + minorRadius * cosStartAngle * cosMinorAngle; pY = centerY + minorRadius * sinStartAngle * cosMinorAngle; pZ = minorRadius * sinMinorAngle; points[currentIndex] = new Point3D32(pX, pY, pZ); normals[currentIndex] = new Vector3D32(sinStartAngle, -cosStartAngle, 0.0f); textureX = 0.75f + 0.25f * cosMinorAngle; textureY = 0.25f - 0.25f * sinMinorAngle; texturePoints[currentIndex] = new Point2D32(textureX, textureY); } // First end center int firstEndCenterIndex = numberOfVertices - 2; points[firstEndCenterIndex] = new Point3D32(centerX, centerY, 0.0f); normals[firstEndCenterIndex] = new Vector3D32(sinStartAngle, -cosStartAngle, 0.0f); texturePoints[firstEndCenterIndex] = new Point2D32(0.75f, 0.25f); // Second end float cosEndAngle = (float) Math.cos(endAngle); float sinEndAngle = (float) Math.sin(endAngle); centerX = majorRadius * cosEndAngle; centerY = majorRadius * sinEndAngle; for (int minorIndex = 0; minorIndex < minorN; minorIndex++) { int currentIndex = (majorN + 1) * minorN + minorIndex; float minorAngle = minorIndex * 2.0f * (float) Math.PI / minorN; float cosMinorAngle = (float) Math.cos(minorAngle); float sinMinorAngle = (float) Math.sin(minorAngle); pX = centerX + minorRadius * cosEndAngle * cosMinorAngle; pY = centerY + minorRadius * sinEndAngle * cosMinorAngle; pZ = minorRadius * sinMinorAngle; points[currentIndex] = new Point3D32(pX, pY, pZ); normals[currentIndex] = new Vector3D32(-sinEndAngle, cosEndAngle, 0.0f); textureX = 0.75f - 0.25f * cosMinorAngle; textureY = 0.75f - 0.25f * sinMinorAngle; texturePoints[currentIndex] = new Point2D32(textureX, textureY); } // Second end center int secondEndCenterIndex = numberOfVertices - 1; points[secondEndCenterIndex] = new Point3D32(centerX, centerY, 0.0f); normals[secondEndCenterIndex] = new Vector3D32(-sinEndAngle, cosEndAngle, 0.0f); texturePoints[secondEndCenterIndex] = new Point2D32(0.75f, 0.75f); // Setting up indices for (int minorIndex = 0; minorIndex < minorN; minorIndex++) { int nextMinorIndex = (minorIndex + 1) % minorN; triangleIndices[index++] = firstEndCenterIndex; triangleIndices[index++] = majorN * minorN + minorIndex; triangleIndices[index++] = majorN * minorN + nextMinorIndex; triangleIndices[index++] = secondEndCenterIndex; triangleIndices[index++] = (majorN + 1) * minorN + nextMinorIndex; triangleIndices[index++] = (majorN + 1) * minorN + minorIndex; } } return new TriangleMesh3DDefinition("ArcTorus Factory", points, texturePoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D box of size ({@code sizeX}, {@code sizeY}, {@code sizeZ}). * * @param description the description holding the box's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Box(Box3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Box(description.getSizeX(), description.getSizeY(), description.getSizeZ(), description.isCentered()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D box of size ({@code sizeX}, {@code sizeY}, {@code sizeZ}). * * @param sizeX box size along the x-axis. * @param sizeY box size along the y-axis. * @param sizeZ box size along the z-axis. * @param centered when {@code true} the generated mesh is centered at the origin, when * {@code false} the bottom face of the box is centered at the origin. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Box(double sizeX, double sizeY, double sizeZ, boolean centered) { return Box((float) sizeX, (float) sizeY, (float) sizeZ, centered); } /** * Creates a triangle mesh for a 3D box of size ({@code sizeX}, {@code sizeY}, {@code sizeZ}). * * @param sizeX box size along the x-axis. * @param sizeY box size along the y-axis. * @param sizeZ box size along the z-axis. * @param centered when {@code true} the generated mesh is centered at the origin, when * {@code false} the bottom face of the box is centered at the origin. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Box(float sizeX, float sizeY, float sizeZ, boolean centered) { Point3D32 points[] = new Point3D32[24]; Vector3D32[] normals = new Vector3D32[24]; Point2D32 textPoints[] = new Point2D32[24]; float zBottom = centered ? -sizeZ / 2f : 0; float zTop = centered ? sizeZ / 2f : sizeZ; // Bottom vertices for bottom face points[0] = new Point3D32(-sizeX / 2.0f, -sizeY / 2.0f, zBottom); normals[0] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[0] = new Point2D32(0.5f, 0.5f); points[1] = new Point3D32(sizeX / 2.0f, -sizeY / 2.0f, zBottom); normals[1] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[1] = new Point2D32(0.25f, 0.5f); points[2] = new Point3D32(sizeX / 2.0f, sizeY / 2.0f, zBottom); normals[2] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[2] = new Point2D32(0.25f, 0.25f); points[3] = new Point3D32(-sizeX / 2.0f, sizeY / 2.0f, zBottom); normals[3] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[3] = new Point2D32(0.5f, 0.25f); // Top vertices for top face points[4] = new Point3D32(-sizeX / 2.0f, -sizeY / 2.0f, zTop); normals[4] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[4] = new Point2D32(0.75f, 0.5f); points[5] = new Point3D32(sizeX / 2.0f, -sizeY / 2.0f, zTop); normals[5] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[5] = new Point2D32(1.0f, 0.5f); points[6] = new Point3D32(sizeX / 2.0f, sizeY / 2.0f, zTop); normals[6] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[6] = new Point2D32(1.0f, 0.25f); points[7] = new Point3D32(-sizeX / 2.0f, sizeY / 2.0f, zTop); normals[7] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[7] = new Point2D32(0.75f, 0.25f); // Left face vertices points[8] = new Point3D32(points[2]); normals[8] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[8] = new Point2D32(0.25f, 0.25f); points[9] = new Point3D32(points[3]); normals[9] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[9] = new Point2D32(0.5f, 0.25f); points[10] = new Point3D32(points[6]); normals[10] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[10] = new Point2D32(0.25f, 0.0f); points[11] = new Point3D32(points[7]); normals[11] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[11] = new Point2D32(0.5f, 0.0f); // Right face vertices points[12] = new Point3D32(points[0]); normals[12] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[12] = new Point2D32(0.5f, 0.5f); points[13] = new Point3D32(points[1]); normals[13] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[13] = new Point2D32(0.25f, 0.5f); points[14] = new Point3D32(points[4]); normals[14] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[14] = new Point2D32(0.5f, 0.75f); points[15] = new Point3D32(points[5]); normals[15] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[15] = new Point2D32(0.25f, 0.75f); // Front face vertices points[16] = new Point3D32(points[0]); normals[16] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[16] = new Point2D32(0.5f, 0.5f); points[17] = new Point3D32(points[3]); normals[17] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[17] = new Point2D32(0.5f, 0.25f); points[18] = new Point3D32(points[4]); normals[18] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[18] = new Point2D32(0.75f, 0.5f); points[19] = new Point3D32(points[7]); normals[19] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[19] = new Point2D32(0.75f, 0.25f); // Back face vertices points[20] = new Point3D32(points[1]); normals[20] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[20] = new Point2D32(0.25f, 0.5f); points[21] = new Point3D32(points[2]); normals[21] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[21] = new Point2D32(0.25f, 0.25f); points[22] = new Point3D32(points[5]); normals[22] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[22] = new Point2D32(0.0f, 0.5f); points[23] = new Point3D32(points[6]); normals[23] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[23] = new Point2D32(0.0f, 0.25f); int numberOfTriangles = 2 * 6; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; // Bottom face (face vertices 0, 1, 2, 3) triangleIndices[index++] = 2; triangleIndices[index++] = 1; triangleIndices[index++] = 0; triangleIndices[index++] = 3; triangleIndices[index++] = 2; triangleIndices[index++] = 0; // Top face (face vertices 4, 5, 6, 7) triangleIndices[index++] = 4; triangleIndices[index++] = 5; triangleIndices[index++] = 6; triangleIndices[index++] = 4; triangleIndices[index++] = 6; triangleIndices[index++] = 7; // Left face (face vertices 8, 9, 10, 11) triangleIndices[index++] = 8; triangleIndices[index++] = 11; triangleIndices[index++] = 10; triangleIndices[index++] = 8; triangleIndices[index++] = 9; triangleIndices[index++] = 11; // Right face (face vertices 12, 13, 14, 15) triangleIndices[index++] = 15; triangleIndices[index++] = 14; triangleIndices[index++] = 13; triangleIndices[index++] = 14; triangleIndices[index++] = 12; triangleIndices[index++] = 13; // Front face (face vertices 16, 17, 18, 19) triangleIndices[index++] = 16; triangleIndices[index++] = 19; triangleIndices[index++] = 17; triangleIndices[index++] = 16; triangleIndices[index++] = 18; triangleIndices[index++] = 19; // Back face (face vertices 20, 21, 22, 23) triangleIndices[index++] = 20; triangleIndices[index++] = 23; triangleIndices[index++] = 22; triangleIndices[index++] = 20; triangleIndices[index++] = 21; triangleIndices[index++] = 23; return new TriangleMesh3DDefinition("Box Factory", points, textPoints, normals, triangleIndices); } /** * Creates a triangle mesh for a flat horizontal rectangle. * * @param sizeX the rectangle size along the x-axis. * @param sizeY the rectangle size along the y-axis. * @param z the height of the rectangle. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition FlatRectangle(double sizeX, double sizeY, double z) { return FlatRectangle((float) sizeX, (float) sizeY, (float) z); } /** * Creates a triangle mesh for a flat horizontal rectangle. * * @param sizeX the rectangle size along the x-axis. * @param sizeY the rectangle size along the y-axis. * @param z the height of the rectangle. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition FlatRectangle(float sizeX, float sizeY, float z) { return FlatRectangle(-0.5f * sizeX, -0.5f * sizeY, 0.5f * sizeX, 0.5f * sizeY, z); } /** * Creates a triangle mesh for a flat horizontal rectangle. * * @param minX the rectangle's lower-bound along the x-axis. * @param maxX the rectangle's upper-bound along the x-axis. * @param minY the rectangle's lower-bound along the y-axis. * @param maxY the rectangle's upper-bound along the x-axis. * @param z the height of the rectangle. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition FlatRectangle(double minX, double minY, double maxX, double maxY, double z) { return FlatRectangle((float) minX, (float) minY, (float) maxX, (float) maxY, (float) z); } /** * Creates a triangle mesh for a flat horizontal rectangle. * * @param minX the rectangle's lower-bound along the x-axis. * @param maxX the rectangle's upper-bound along the x-axis. * @param minY the rectangle's lower-bound along the y-axis. * @param maxY the rectangle's upper-bound along the x-axis. * @param z the height of the rectangle. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition FlatRectangle(float minX, float minY, float maxX, float maxY, float z) { Point3D32[] points = new Point3D32[4]; Vector3D32[] normals = new Vector3D32[4]; Point2D32[] textPoints = new Point2D32[4]; points[0] = new Point3D32(minX, minY, z); points[1] = new Point3D32(maxX, minY, z); points[2] = new Point3D32(maxX, maxY, z); points[3] = new Point3D32(minX, maxY, z); textPoints[0] = new Point2D32(1.0f, 1.0f); textPoints[1] = new Point2D32(1.0f, 0.0f); textPoints[2] = new Point2D32(0.0f, 0.0f); textPoints[3] = new Point2D32(0.0f, 1.0f); normals[0] = new Vector3D32(0.0f, 0.0f, 1.0f); normals[1] = new Vector3D32(0.0f, 0.0f, 1.0f); normals[2] = new Vector3D32(0.0f, 0.0f, 1.0f); normals[3] = new Vector3D32(0.0f, 0.0f, 1.0f); int[] triangleIndices = new int[3 * 2]; int index = 0; triangleIndices[index++] = 0; triangleIndices[index++] = 1; triangleIndices[index++] = 3; triangleIndices[index++] = 1; triangleIndices[index++] = 2; triangleIndices[index++] = 3; return new TriangleMesh3DDefinition("FlatRectangle Factory", points, textPoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D ramp. *

* The ramp is positioned such that its bottom face is centered at the origin. *

* * @param description the description holding the ramp's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Ramp(Ramp3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Ramp(description.getSizeX(), description.getSizeY(), description.getSizeZ()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D ramp. *

* The ramp is positioned such that its bottom face is centered at the origin. *

* * @param sizeX ramp size along the x-axis. * @param sizeY ramp size along the y-axis. * @param sizeZ ramp size along the z-axis. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Ramp(double sizeX, double sizeY, double sizeZ) { return Ramp((float) sizeX, (float) sizeY, (float) sizeZ); } /** * Creates a triangle mesh for a 3D ramp. *

* The ramp is positioned such that its bottom face is centered at the origin. *

* * @param sizeX ramp size along the x-axis. * @param sizeY ramp size along the y-axis. * @param sizeZ ramp size along the z-axis. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Ramp(float sizeX, float sizeY, float sizeZ) { Point3D32[] points = new Point3D32[18]; Vector3D32[] normals = new Vector3D32[18]; Point2D32[] textPoints = new Point2D32[18]; float tex0 = 0.0f; float tex1 = 1.0f / 3.0f; float tex2 = 2.0f / 3.0f; float tex3 = 1.0f; // Bottom face vertices points[0] = new Point3D32(-sizeX / 2.0f, -sizeY / 2.0f, 0.0f); normals[0] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[0] = new Point2D32(tex2, tex2); points[1] = new Point3D32(sizeX / 2.0f, -sizeY / 2.0f, 0.0f); normals[1] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[1] = new Point2D32(tex1, tex2); points[2] = new Point3D32(sizeX / 2.0f, sizeY / 2.0f, 0.0f); normals[2] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[2] = new Point2D32(tex1, tex1); points[3] = new Point3D32(-sizeX / 2.0f, sizeY / 2.0f, 0.0f); normals[3] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[3] = new Point2D32(tex2, tex1); // Back face vertices points[4] = new Point3D32(sizeX / 2.0f, -sizeY / 2.0f, sizeZ); normals[4] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[4] = new Point2D32(tex0, tex2); points[5] = new Point3D32(sizeX / 2.0f, sizeY / 2.0f, sizeZ); normals[5] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[5] = new Point2D32(tex0, tex1); points[6] = new Point3D32(points[2]); normals[6] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[6] = new Point2D32(tex1, tex1); points[7] = new Point3D32(points[1]); normals[7] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[7] = new Point2D32(tex1, tex2); // Top face vertices float rampAngle = (float) Math.atan2(sizeZ, sizeX); points[8] = new Point3D32(points[0]); normals[8] = new Vector3D32(-(float) Math.sin(rampAngle), 0.0f, (float) Math.cos(rampAngle)); textPoints[8] = new Point2D32(tex2, tex2); points[9] = new Point3D32(points[4]); normals[9] = new Vector3D32(-(float) Math.sin(rampAngle), 0.0f, (float) Math.cos(rampAngle)); textPoints[9] = new Point2D32(tex3, tex2); points[10] = new Point3D32(points[5]); normals[10] = new Vector3D32(-(float) Math.sin(rampAngle), 0.0f, (float) Math.cos(rampAngle)); textPoints[10] = new Point2D32(tex3, tex1); points[11] = new Point3D32(points[3]); normals[11] = new Vector3D32(-(float) Math.sin(rampAngle), 0.0f, (float) Math.cos(rampAngle)); textPoints[11] = new Point2D32(tex2, tex1); // Right face vertices points[12] = new Point3D32(points[0]); normals[12] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[12] = new Point2D32(tex2, tex2); points[13] = new Point3D32(points[1]); normals[13] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[13] = new Point2D32(tex1, tex2); points[14] = new Point3D32(points[4]); normals[14] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[14] = new Point2D32(tex1, tex3); // Left face vertices points[15] = new Point3D32(points[2]); normals[15] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[15] = new Point2D32(tex1, tex1); points[16] = new Point3D32(points[3]); normals[16] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[16] = new Point2D32(tex2, tex1); points[17] = new Point3D32(points[5]); normals[17] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[17] = new Point2D32(tex1, tex0); int numberOfTriangles = 2 * 3 + 2; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; // Bottom face triangleIndices[index++] = 0; triangleIndices[index++] = 2; triangleIndices[index++] = 1; triangleIndices[index++] = 0; triangleIndices[index++] = 3; triangleIndices[index++] = 2; // Back face triangleIndices[index++] = 7; triangleIndices[index++] = 5; triangleIndices[index++] = 4; triangleIndices[index++] = 5; triangleIndices[index++] = 7; triangleIndices[index++] = 6; // Top face triangleIndices[index++] = 8; triangleIndices[index++] = 9; triangleIndices[index++] = 10; triangleIndices[index++] = 8; triangleIndices[index++] = 10; triangleIndices[index++] = 11; // Right face triangleIndices[index++] = 12; triangleIndices[index++] = 13; triangleIndices[index++] = 14; // Left face triangleIndices[index++] = 15; triangleIndices[index++] = 16; triangleIndices[index++] = 17; return new TriangleMesh3DDefinition("Ramp Factory", points, textPoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D box which bottom and top faces are extended with a pyramid. * * @param description the description holding the box's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition PyramidBox(PyramidBox3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = PyramidBox(description.getBoxSizeX(), description.getBoxSizeY(), description.getBoxSizeZ(), description.getPyramidHeight()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D box which bottom and top faces are extended with a pyramid. * * @param boxSizeX box size along the x-axis. * @param boxSizeY box size along the y-axis. * @param boxSizeZ box size along the z-axis. * @param pyramidHeight the height for each pyramid. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition PyramidBox(double boxSizeX, double boxSizeY, double boxSizeZ, double pyramidHeight) { return PyramidBox((float) boxSizeX, (float) boxSizeY, (float) boxSizeZ, (float) pyramidHeight); } /** * Creates a triangle mesh for a 3D box which bottom and top faces are extended with a pyramid. * * @param boxSizeX box size along the x-axis. * @param boxSizeY box size along the y-axis. * @param boxSizeZ box size along the z-axis. * @param pyramidHeight the height for each pyramid. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition PyramidBox(float boxSizeX, float boxSizeY, float boxSizeZ, float pyramidHeight) { Point3D32 points[] = new Point3D32[40]; Vector3D32[] normals = new Vector3D32[40]; Point2D32 textPoints[] = new Point2D32[40]; float totalHeight = 2.0f * pyramidHeight + boxSizeZ; // Box front face points[0] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[0] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[0] = new Point2D32(0.75f, 1.0f - pyramidHeight / totalHeight); points[1] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[1] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[1] = new Point2D32(0.75f, pyramidHeight / totalHeight); points[2] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[2] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[2] = new Point2D32(0.5f, pyramidHeight / totalHeight); points[3] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[3] = new Vector3D32(-1.0f, 0.0f, 0.0f); textPoints[3] = new Point2D32(0.5f, 1.0f - pyramidHeight / totalHeight); // Box back face points[4] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[4] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[4] = new Point2D32(0.0f, 1.0f - pyramidHeight / totalHeight); points[5] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[5] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[5] = new Point2D32(0.0f, pyramidHeight / totalHeight); points[6] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[6] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[6] = new Point2D32(0.25f, pyramidHeight / totalHeight); points[7] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[7] = new Vector3D32(1.0f, 0.0f, 0.0f); textPoints[7] = new Point2D32(0.25f, 1.0f - pyramidHeight / totalHeight); // Box left face points[8] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[8] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[8] = new Point2D32(0.5f, 1.0f - pyramidHeight / totalHeight); points[9] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[9] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[9] = new Point2D32(0.5f, pyramidHeight / totalHeight); points[10] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[10] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[10] = new Point2D32(0.25f, pyramidHeight / totalHeight); points[11] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[11] = new Vector3D32(0.0f, 1.0f, 0.0f); textPoints[11] = new Point2D32(0.25f, 1.0f - pyramidHeight / totalHeight); // Box right face points[12] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[12] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[12] = new Point2D32(0.75f, 1.0f - pyramidHeight / totalHeight); points[13] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[13] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[13] = new Point2D32(0.75f, pyramidHeight / totalHeight); points[14] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[14] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[14] = new Point2D32(1.0f, pyramidHeight / totalHeight); points[15] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[15] = new Vector3D32(0.0f, -1.0f, 0.0f); textPoints[15] = new Point2D32(1.0f, 1.0f - pyramidHeight / totalHeight); float frontBackAngle = (float) Math.atan2(boxSizeX / 2.0, pyramidHeight); float leftRightAngle = (float) Math.atan2(boxSizeY / 2.0, pyramidHeight); // Top pyramid // Front face points[16] = new Point3D32(0.0f, 0.0f, 0.5f * boxSizeZ + pyramidHeight); normals[16] = new Vector3D32(-(float) Math.cos(frontBackAngle), 0.0f, (float) Math.sin(frontBackAngle)); textPoints[16] = new Point2D32(0.625f, 0.0f); points[17] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[17] = new Vector3D32(-(float) Math.cos(frontBackAngle), 0.0f, (float) Math.sin(frontBackAngle)); textPoints[17] = new Point2D32(0.75f, pyramidHeight / totalHeight); points[18] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[18] = new Vector3D32(-(float) Math.cos(frontBackAngle), 0.0f, (float) Math.sin(frontBackAngle)); textPoints[18] = new Point2D32(0.5f, pyramidHeight / totalHeight); // Back face points[19] = new Point3D32(0.0f, 0.0f, 0.5f * boxSizeZ + pyramidHeight); normals[19] = new Vector3D32((float) Math.cos(frontBackAngle), 0.0f, (float) Math.sin(frontBackAngle)); textPoints[19] = new Point2D32(0.125f, 0.0f); points[20] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[20] = new Vector3D32((float) Math.cos(frontBackAngle), 0.0f, (float) Math.sin(frontBackAngle)); textPoints[20] = new Point2D32(0.0f, pyramidHeight / totalHeight); points[21] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[21] = new Vector3D32((float) Math.cos(frontBackAngle), 0.0f, (float) Math.sin(frontBackAngle)); textPoints[21] = new Point2D32(0.25f, pyramidHeight / totalHeight); // Left face points[22] = new Point3D32(0.0f, 0.0f, 0.5f * boxSizeZ + pyramidHeight); normals[22] = new Vector3D32(0.0f, (float) Math.cos(leftRightAngle), (float) Math.sin(leftRightAngle)); textPoints[22] = new Point2D32(0.375f, 0.0f); points[23] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[23] = new Vector3D32(0.0f, (float) Math.cos(leftRightAngle), (float) Math.sin(leftRightAngle)); textPoints[23] = new Point2D32(0.5f, pyramidHeight / totalHeight); points[24] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[24] = new Vector3D32(0.0f, (float) Math.cos(leftRightAngle), (float) Math.sin(leftRightAngle)); textPoints[24] = new Point2D32(0.25f, pyramidHeight / totalHeight); // Right face points[25] = new Point3D32(0.0f, 0.0f, 0.5f * boxSizeZ + pyramidHeight); normals[25] = new Vector3D32(0.0f, -(float) Math.cos(leftRightAngle), (float) Math.sin(leftRightAngle)); textPoints[25] = new Point2D32(0.875f, 0.0f); points[26] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[26] = new Vector3D32(0.0f, -(float) Math.cos(leftRightAngle), (float) Math.sin(leftRightAngle)); textPoints[26] = new Point2D32(0.75f, pyramidHeight / totalHeight); points[27] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, 0.5f * boxSizeZ); normals[27] = new Vector3D32(0.0f, -(float) Math.cos(leftRightAngle), (float) Math.sin(leftRightAngle)); textPoints[27] = new Point2D32(1.0f, pyramidHeight / totalHeight); // Bottom pyramid // Front face points[28] = new Point3D32(0.0f, 0.0f, -pyramidHeight - 0.5f * boxSizeZ); normals[28] = new Vector3D32(-(float) Math.cos(frontBackAngle), 0.0f, -(float) Math.sin(frontBackAngle)); textPoints[28] = new Point2D32(0.625f, 1.0f); points[29] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[29] = new Vector3D32(-(float) Math.cos(frontBackAngle), 0.0f, -(float) Math.sin(frontBackAngle)); textPoints[29] = new Point2D32(0.75f, 1.0f - pyramidHeight / totalHeight); points[30] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[30] = new Vector3D32(-(float) Math.cos(frontBackAngle), 0.0f, -(float) Math.sin(frontBackAngle)); textPoints[30] = new Point2D32(0.5f, 1.0f - pyramidHeight / totalHeight); // Back face points[31] = new Point3D32(0.0f, 0.0f, -pyramidHeight - 0.5f * boxSizeZ); normals[31] = new Vector3D32((float) Math.cos(frontBackAngle), 0.0f, -(float) Math.sin(frontBackAngle)); textPoints[31] = new Point2D32(0.125f, 1.0f); points[32] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[32] = new Vector3D32((float) Math.cos(frontBackAngle), 0.0f, -(float) Math.sin(frontBackAngle)); textPoints[32] = new Point2D32(0.0f, 1.0f - pyramidHeight / totalHeight); points[33] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[33] = new Vector3D32((float) Math.cos(frontBackAngle), 0.0f, -(float) Math.sin(frontBackAngle)); textPoints[33] = new Point2D32(0.25f, 1.0f - pyramidHeight / totalHeight); // Left face points[34] = new Point3D32(0.0f, 0.0f, -pyramidHeight - 0.5f * boxSizeZ); normals[34] = new Vector3D32(0.0f, (float) Math.cos(leftRightAngle), -(float) Math.sin(leftRightAngle)); textPoints[34] = new Point2D32(0.375f, 1.0f); points[35] = new Point3D32(-boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[35] = new Vector3D32(0.0f, (float) Math.cos(leftRightAngle), -(float) Math.sin(leftRightAngle)); textPoints[35] = new Point2D32(0.5f, 1.0f - pyramidHeight / totalHeight); points[36] = new Point3D32(boxSizeX / 2.0f, boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[36] = new Vector3D32(0.0f, (float) Math.cos(leftRightAngle), -(float) Math.sin(leftRightAngle)); textPoints[36] = new Point2D32(0.25f, 1.0f - pyramidHeight / totalHeight); // Right face points[37] = new Point3D32(0.0f, 0.0f, -pyramidHeight - 0.5f * boxSizeZ); normals[37] = new Vector3D32(0.0f, -(float) Math.cos(leftRightAngle), -(float) Math.sin(leftRightAngle)); textPoints[37] = new Point2D32(0.875f, 1.0f); points[38] = new Point3D32(-boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[38] = new Vector3D32(0.0f, -(float) Math.cos(leftRightAngle), -(float) Math.sin(leftRightAngle)); textPoints[38] = new Point2D32(0.75f, 1.0f - pyramidHeight / totalHeight); points[39] = new Point3D32(boxSizeX / 2.0f, -boxSizeY / 2.0f, -0.5f * boxSizeZ); normals[39] = new Vector3D32(0.0f, -(float) Math.cos(leftRightAngle), -(float) Math.sin(leftRightAngle)); textPoints[39] = new Point2D32(1.0f, 1.0f - pyramidHeight / totalHeight); int numberOfTriangles = 2 * 4 + 2 * 4; int[] polygonIndices = new int[3 * numberOfTriangles]; int index = 0; // Box front face polygonIndices[index++] = 0; polygonIndices[index++] = 1; polygonIndices[index++] = 2; polygonIndices[index++] = 0; polygonIndices[index++] = 2; polygonIndices[index++] = 3; // Box back face polygonIndices[index++] = 4; polygonIndices[index++] = 6; polygonIndices[index++] = 5; polygonIndices[index++] = 4; polygonIndices[index++] = 7; polygonIndices[index++] = 6; // Box left face polygonIndices[index++] = 8; polygonIndices[index++] = 9; polygonIndices[index++] = 10; polygonIndices[index++] = 8; polygonIndices[index++] = 10; polygonIndices[index++] = 11; // Box right face polygonIndices[index++] = 12; polygonIndices[index++] = 14; polygonIndices[index++] = 13; polygonIndices[index++] = 12; polygonIndices[index++] = 15; polygonIndices[index++] = 14; // Top pyramid front face polygonIndices[index++] = 16; polygonIndices[index++] = 18; polygonIndices[index++] = 17; // Top pyramid back face polygonIndices[index++] = 19; polygonIndices[index++] = 20; polygonIndices[index++] = 21; // Top pyramid left face polygonIndices[index++] = 22; polygonIndices[index++] = 24; polygonIndices[index++] = 23; // Top pyramid right face polygonIndices[index++] = 25; polygonIndices[index++] = 26; polygonIndices[index++] = 27; // Bottom pyramid front face polygonIndices[index++] = 28; polygonIndices[index++] = 29; polygonIndices[index++] = 30; // Bottom pyramid back face polygonIndices[index++] = 31; polygonIndices[index++] = 33; polygonIndices[index++] = 32; // Bottom pyramid left face polygonIndices[index++] = 36; polygonIndices[index++] = 34; polygonIndices[index++] = 35; // Bottom pyramid right face polygonIndices[index++] = 37; polygonIndices[index++] = 39; polygonIndices[index++] = 38; return new TriangleMesh3DDefinition("PyramidBox Factory", points, textPoints, normals, polygonIndices); } /** * Create a triangle mesh for a 3D line segment. *

* The line segment is implemented as an elongated 3D box. *

* * @param lineSegment used to define the mesh end points. * @param width the thickness of the line. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Line(LineSegment3DReadOnly lineSegment, double width) { return Line(lineSegment.getFirstEndpoint(), lineSegment.getSecondEndpoint(), width); } /** * Create a triangle mesh for a 3D line segment. *

* The line segment is implemented as an elongated 3D box. *

* * @param point0 the first endpoint of the line segment. * @param point1 the second endpoint of the line segment. * @param width the thickness of the line. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Line(Point3DReadOnly point0, Point3DReadOnly point1, double width) { return Line(point0.getX(), point0.getY(), point0.getZ(), point1.getX(), point1.getY(), point1.getZ(), width); } /** * Create a triangle mesh for a 3D line segment. *

* The line segment is implemented as an elongated 3D box. *

* * @param x0 the x-coordinate of the first endpoint for the line segment. * @param y0 the y-coordinate of the first endpoint for the line segment. * @param z0 the z-coordinate of the first endpoint for the line segment. * @param x1 the x-coordinate of the second endpoint for the line segment. * @param y1 the y-coordinate of the second endpoint for the line segment. * @param z1 the z-coordinate of the second endpoint for the line segment. * @param width the thickness of the line. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Line(double x0, double y0, double z0, double x1, double y1, double z1, double width) { return Line((float) x0, (float) y0, (float) z0, (float) x1, (float) y1, (float) z1, (float) width); } /** * Create a triangle mesh for a 3D line segment. *

* The line segment is implemented as an elongated 3D box. *

* * @param x0 the x-coordinate of the first endpoint for the line segment. * @param y0 the y-coordinate of the first endpoint for the line segment. * @param z0 the z-coordinate of the first endpoint for the line segment. * @param x1 the x-coordinate of the second endpoint for the line segment. * @param y1 the y-coordinate of the second endpoint for the line segment. * @param z1 the z-coordinate of the second endpoint for the line segment. * @param width the thickness of the line. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Line(float x0, float y0, float z0, float x1, float y1, float z1, float width) { Vector3D32 lineDirection = new Vector3D32(x1 - x0, y1 - y0, z1 - z0); float lineLength = (float) lineDirection.norm(); lineDirection.scale(1.0f / lineLength); TriangleMesh3DDefinition line = Box(width, width, lineLength, false); line.setName("Line Factory"); Point3D32[] vertices = line.getVertices(); Vector3D32[] normals = line.getNormals(); float yaw; float pitch; if (Math.abs(lineDirection.getZ()) < 1.0 - 1.0e-7) { yaw = (float) Math.atan2(lineDirection.getY(), lineDirection.getX()); double xyLength = Math.sqrt(lineDirection.getX() * lineDirection.getX() + lineDirection.getY() * lineDirection.getY()); pitch = (float) Math.atan2(xyLength, lineDirection.getZ()); } else { yaw = 0.0f; pitch = lineDirection.getZ() >= 0.0 ? 0.0f : (float) Math.PI; } float cYaw = (float) Math.cos(yaw); float sYaw = (float) Math.sin(yaw); float cPitch = (float) Math.cos(pitch); float sPitch = (float) Math.sin(pitch); float rxx = cYaw * cPitch; float rxy = -sYaw; float rxz = cYaw * sPitch; float ryx = sYaw * cPitch; float ryy = cYaw; float ryz = sYaw * sPitch; float rzx = -sPitch; float rzz = cPitch; for (int i = 0; i < vertices.length; i++) { Point3D32 vertex = vertices[i]; float vx = vertex.getX32(); float vy = vertex.getY32(); float vz = vertex.getZ32(); vertex.setX(x0 + rxx * vx + rxy * vy + rxz * vz); vertex.setY(y0 + ryx * vx + ryy * vy + ryz * vz); vertex.setZ(z0 + rzx * vx + rzz * vz); } for (int i = 0; i < normals.length; i++) { Vector3D32 normal = normals[i]; float vx = normal.getX32(); float vy = normal.getY32(); float vz = normal.getZ32(); normal.setX(rxx * vx + rxy * vy + rxz * vz); normal.setY(ryx * vx + ryy * vy + ryz * vz); normal.setZ(rzx * vx + rzz * vz); } return line; } /** * Creates a triangle mesh for a 3D capsule with its ends being half ellipsoids. *

* The capsule's axis is aligned with the z-axis and it is centered at the origin. *

* * @param description the description holding the capsule's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Capsule(Capsule3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Capsule(description.getLength(), description.getRadiusX(), description.getRadiusY(), description.getRadiusZ(), description.getResolution(), description.getResolution()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D capsule with its ends being half ellipsoids. *

* The capsule's axis is aligned with the z-axis and it is centered at the origin. *

* * @param height the capsule's height or length. Distance separating the center of the * two half ellipsoids. * @param radiusX radius of the capsule along the x-axis. * @param radiusY radius of the capsule along the y-axis. * @param radiusZ radius of the capsule along the z-axis. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Capsule(double height, double radiusX, double radiusY, double radiusZ, int latitudeResolution, int longitudeResolution) { return Capsule((float) height, (float) radiusX, (float) radiusY, (float) radiusZ, latitudeResolution, longitudeResolution); } /** * Creates a triangle mesh for a 3D capsule with its ends being half ellipsoids. *

* The capsule's axis is aligned with the z-axis and it is centered at the origin. *

* * @param height the capsule's height or length. Distance separating the center of the * two half ellipsoids. * @param radiusX radius of the capsule along the x-axis. * @param radiusY radius of the capsule along the y-axis. * @param radiusZ radius of the capsule along the z-axis. * @param latitudeResolution the resolution along the vertical axis, i.e. z-axis. * @param longitudeResolution the resolution around the vertical axis, i.e. the number of vertices * per latitude. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Capsule(float height, float radiusX, float radiusY, float radiusZ, int latitudeResolution, int longitudeResolution) { if (latitudeResolution % 2 != 0) latitudeResolution++; if (longitudeResolution % 2 != 1) longitudeResolution++; // Reminder of longitude and latitude: http://www.geographyalltheway.com/ks3_geography/maps_atlases/longitude_latitude.htm int numberOfVertices = latitudeResolution * longitudeResolution; Point3D32 points[] = new Point3D32[numberOfVertices]; Vector3D32[] normals = new Vector3D32[numberOfVertices]; Point2D32 textPoints[] = new Point2D32[numberOfVertices]; float texRatio = radiusZ / (2.0f * radiusZ + height); float halfHeight = 0.5f * height; for (int longitudeIndex = 0; longitudeIndex < longitudeResolution; longitudeIndex++) { float longitudeAngle = TwoPi * ((float) longitudeIndex / (float) (longitudeResolution - 1.0f)); float textureX = (float) longitudeIndex / (float) (longitudeResolution - 1); // Bottom hemi-ellipsoid for (int latitudeIndex = 1; latitudeIndex < latitudeResolution / 2; latitudeIndex++) { float latitudeAngle = (float) (-HalfPi + Math.PI * ((float) latitudeIndex / (latitudeResolution - 1.0f))); float cosLongitude = (float) Math.cos(longitudeAngle); float sinLongitude = (float) Math.sin(longitudeAngle); float cosLatitude = (float) Math.cos(latitudeAngle); float sinLatitude = (float) Math.sin(latitudeAngle); int currentIndex = latitudeIndex * longitudeResolution + longitudeIndex; float normalX = cosLongitude * cosLatitude; float normalY = sinLongitude * cosLatitude; float normalZ = sinLatitude; float vertexX = radiusX * normalX; float vertexY = radiusY * normalY; float vertexZ = radiusZ * normalZ - halfHeight; points[currentIndex] = new Point3D32(vertexX, vertexY, vertexZ); normals[currentIndex] = new Vector3D32(normalX, normalY, normalZ); float textureY = 1.0f - (1.0f + sinLatitude) * texRatio; textPoints[currentIndex] = new Point2D32(textureX, textureY); } // Top hemi-ellipsoid for (int latitudeIndex = 0; latitudeIndex < latitudeResolution / 2 - 1; latitudeIndex++) { float latitudeAngle = (float) (Math.PI * ((float) latitudeIndex / (latitudeResolution - 1.0f))); float cosLongitude = (float) Math.cos(longitudeAngle); float sinLongitude = (float) Math.sin(longitudeAngle); float cosLatitude = (float) Math.cos(latitudeAngle); float sinLatitude = (float) Math.sin(latitudeAngle); int currentIndex = (latitudeResolution / 2 + latitudeIndex) * longitudeResolution + longitudeIndex; float normalX = cosLongitude * cosLatitude; float normalY = sinLongitude * cosLatitude; float normalZ = sinLatitude; float vertexX = radiusX * normalX; float vertexY = radiusY * normalY; float vertexZ = radiusZ * normalZ + halfHeight; points[currentIndex] = new Point3D32(vertexX, vertexY, vertexZ); normals[currentIndex] = new Vector3D32(normalX, normalY, normalZ); float textureY = (1.0f - sinLatitude) * texRatio; textPoints[currentIndex] = new Point2D32(textureX, textureY); } textureX += 0.5f / (longitudeResolution - 1.0f); // South pole int southPoleIndex = longitudeIndex; points[southPoleIndex] = new Point3D32(0.0f, 0.0f, -radiusZ - halfHeight); normals[southPoleIndex] = new Vector3D32(0.0f, 0.0f, -1.0f); textPoints[southPoleIndex] = new Point2D32(textureX, 1.0f - 1.0f / 256.0f); // North pole int northPoleIndex = (latitudeResolution - 1) * longitudeResolution + longitudeIndex; points[northPoleIndex] = new Point3D32(0.0f, 0.0f, radiusZ + halfHeight); normals[northPoleIndex] = new Vector3D32(0.0f, 0.0f, 1.0f); textPoints[northPoleIndex] = new Point2D32(textureX, 1.0f / 256.0f); } int numberOfTriangles = 2 * latitudeResolution * longitudeResolution + 1 * longitudeResolution; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; // Top hemi-ellipsoid Mid-latitude faces for (int latitudeIndex = 1; latitudeIndex < latitudeResolution - 1; latitudeIndex++) { for (int longitudeIndex = 0; longitudeIndex < longitudeResolution; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % longitudeResolution; int nextLatitudeIndex = latitudeIndex + 1; // Lower triangles triangleIndices[index++] = latitudeIndex * longitudeResolution + longitudeIndex; triangleIndices[index++] = latitudeIndex * longitudeResolution + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * longitudeResolution + longitudeIndex; // Upper triangles triangleIndices[index++] = latitudeIndex * longitudeResolution + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * longitudeResolution + nextLongitudeIndex; triangleIndices[index++] = nextLatitudeIndex * longitudeResolution + longitudeIndex; } } // South pole faces for (int longitudeIndex = 0; longitudeIndex < longitudeResolution - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % longitudeResolution; triangleIndices[index++] = longitudeIndex; triangleIndices[index++] = longitudeResolution + nextLongitudeIndex; triangleIndices[index++] = longitudeResolution + longitudeIndex; } // North pole faces for (int longitudeIndex = 0; longitudeIndex < longitudeResolution - 1; longitudeIndex++) { int nextLongitudeIndex = (longitudeIndex + 1) % longitudeResolution; triangleIndices[index++] = (latitudeResolution - 1) * longitudeResolution + longitudeIndex; triangleIndices[index++] = (latitudeResolution - 2) * longitudeResolution + longitudeIndex; triangleIndices[index++] = (latitudeResolution - 2) * longitudeResolution + nextLongitudeIndex; } return new TriangleMesh3DDefinition("Capsule Factory", points, textPoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D regular tetrahedron. *

* Its base is centered at the origin. *

* * @param description the description holding the tetrahedron's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Tetrahedron(Tetrahedron3DDefinition description) { TriangleMesh3DDefinition meshDataHolder = Tetrahedron(description.getEdgeLength()); if (meshDataHolder != null) meshDataHolder.setName(description.getName()); return meshDataHolder; } /** * Creates a triangle mesh for a 3D regular tetrahedron. *

* The tetrahedron is centered at the origin. *

* * @param edgeLength length of the tetrahedron's edges. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Tetrahedron(double edgeLength) { return Tetrahedron((float) edgeLength); } private static final float TETRAHEDRON_FACE_EDGE_FACE_ANGLE = (float) Math.acos(ONE_THIRD); private static final float TETRAHEDRON_SINE_FACE_EDGE_FACE_ANGLE = (float) Math.sin(TETRAHEDRON_FACE_EDGE_FACE_ANGLE); /** * Creates a triangle mesh for a 3D regular tetrahedron. *

* The tetrahedron is centered at the origin. *

* * @param edgeLength length of the tetrahedron's edges. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition Tetrahedron(float edgeLength) { /* * @formatter:off * Base vertices ordering * 0 * / \ * / \ * / \ * 2 ----- 1 * @formatter:on */ float height = THIRD_SQRT6 * edgeLength; float topHeight = FOURTH_SQRT6 * edgeLength; float baseHeight = topHeight - height; float halfEdgeLength = 0.5f * edgeLength; float cosFaceEdgeFace = ONE_THIRD; float sinFaceEdgeFace = TETRAHEDRON_SINE_FACE_EDGE_FACE_ANGLE; float cosEdgeVertexEdge = 0.5f; float sinEdgeVertexEdge = HALF_SQRT3; Point3D32 topVertex = new Point3D32(0.0f, 0.0f, topHeight); Point3D32 baseVertex0 = new Point3D32(edgeLength * THIRD_SQRT3, 0.0f, baseHeight); Point3D32 baseVertex1 = new Point3D32(-edgeLength * SIXTH_SQRT3, halfEdgeLength, baseHeight); Point3D32 baseVertex2 = new Point3D32(-edgeLength * SIXTH_SQRT3, -halfEdgeLength, baseHeight); Vector3D32 frontNormal = new Vector3D32(-sinFaceEdgeFace, 0.0f, cosFaceEdgeFace); Vector3D32 rightNormal = new Vector3D32(sinFaceEdgeFace * sinEdgeVertexEdge, sinFaceEdgeFace * cosEdgeVertexEdge, cosFaceEdgeFace); Vector3D32 leftNormal = new Vector3D32(sinFaceEdgeFace * sinEdgeVertexEdge, -sinFaceEdgeFace * cosEdgeVertexEdge, cosFaceEdgeFace); Vector3D32 baseNormal = new Vector3D32(0.0f, 0.0f, -1.0f); int numberOfVertices = 12; Point3D32[] vertices = new Point3D32[numberOfVertices]; Vector3D32[] normals = new Vector3D32[numberOfVertices]; Point2D32[] texturePoints = new Point2D32[numberOfVertices]; // Front face vertices[0] = new Point3D32(baseVertex2); normals[0] = new Vector3D32(frontNormal); texturePoints[0] = new Point2D32(0.25f, 0.5f); vertices[1] = new Point3D32(baseVertex1); normals[1] = new Vector3D32(frontNormal); texturePoints[1] = new Point2D32(0.75f, 0.5f); vertices[2] = new Point3D32(topVertex); normals[2] = new Vector3D32(frontNormal); texturePoints[2] = new Point2D32(0.5f, 1.0f); // Right face vertices[3] = new Point3D32(baseVertex1); normals[3] = new Vector3D32(rightNormal); texturePoints[3] = new Point2D32(0.75f, 0.5f); vertices[4] = new Point3D32(baseVertex0); normals[4] = new Vector3D32(rightNormal); texturePoints[4] = new Point2D32(0.5f, 0.0f); vertices[5] = new Point3D32(topVertex); normals[5] = new Vector3D32(rightNormal); texturePoints[5] = new Point2D32(1.0f, 0.0f); // Left face vertices[6] = new Point3D32(baseVertex0); normals[6] = new Vector3D32(leftNormal); texturePoints[6] = new Point2D32(0.5f, 0.0f); vertices[7] = new Point3D32(baseVertex2); normals[7] = new Vector3D32(leftNormal); texturePoints[7] = new Point2D32(0.25f, 0.5f); vertices[8] = new Point3D32(topVertex); normals[8] = new Vector3D32(leftNormal); texturePoints[8] = new Point2D32(0.0f, 0.0f); // Bottom face vertices[9] = new Point3D32(baseVertex0); normals[9] = new Vector3D32(baseNormal); texturePoints[9] = new Point2D32(0.5f, 0.0f); vertices[10] = new Point3D32(baseVertex1); normals[10] = new Vector3D32(baseNormal); texturePoints[10] = new Point2D32(0.75f, 0.5f); vertices[11] = new Point3D32(baseVertex2); normals[11] = new Vector3D32(baseNormal); texturePoints[11] = new Point2D32(0.25f, 0.5f); int numberOfTriangles = 4; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; // Front face triangleIndices[index++] = 0; triangleIndices[index++] = 2; triangleIndices[index++] = 1; // Right face triangleIndices[index++] = 3; triangleIndices[index++] = 5; triangleIndices[index++] = 4; // Left face triangleIndices[index++] = 6; triangleIndices[index++] = 8; triangleIndices[index++] = 7; // Bottom face triangleIndices[index++] = 9; triangleIndices[index++] = 11; triangleIndices[index++] = 10; return new TriangleMesh3DDefinition("Tetrahedron Factory", vertices, texturePoints, normals, triangleIndices); } /** * Creates a triangle mesh for a 3D convex polytope. *

* The texture mapping is computed from the spherical coordinates of the vertices and may not be * appropriate depending on the polytope. *

* * @param description the description holding the polytope's properties. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition ConvexPolytope(ConvexPolytope3DDefinition description) { return ConvexPolytope(description.getConvexPolytope()); } /** * Creates a triangle mesh for a 3D convex polytope. *

* The texture mapping is computed from the spherical coordinates of the vertices and may not be * appropriate depending on the polytope. *

* * @param convexPolytope the polytope to create the triangle mesh for. Not modified. * @return the generic triangle mesh. */ public static TriangleMesh3DDefinition ConvexPolytope(ConvexPolytope3DReadOnly convexPolytope) { if (convexPolytope == null) return null; int numberOfVertices = convexPolytope.getFaces().stream().mapToInt(Face3DReadOnly::getNumberOfEdges).sum(); Point3D32[] vertices = new Point3D32[numberOfVertices]; Vector3D32[] normals = new Vector3D32[numberOfVertices]; Point2D32[] texturePoints = new Point2D32[numberOfVertices]; int numberOfTriangles = numberOfVertices - 2 * convexPolytope.getNumberOfFaces(); int[] triangleIndices = new int[3 * numberOfTriangles]; int vertexOffset = 0; int triangleOffset = 0; Vector3D direction = new Vector3D(); for (int faceIndex = 0; faceIndex < convexPolytope.getNumberOfFaces(); faceIndex++) { Face3DReadOnly face = convexPolytope.getFace(faceIndex); double minLongitude = Double.POSITIVE_INFINITY; double[] longitudes = new double[face.getNumberOfEdges()]; for (int vertexIndex = 0; vertexIndex < face.getNumberOfEdges(); vertexIndex++) { Vertex3DReadOnly vertex = face.getVertex(vertexIndex); vertices[vertexOffset + vertexIndex] = new Point3D32(vertex); normals[vertexOffset + vertexIndex] = new Vector3D32(face.getNormal()); direction.sub(vertex, convexPolytope.getCentroid()); direction.normalize(); double longitude = Math.atan2(direction.getY(), direction.getX()); longitudes[vertexIndex] = longitude; minLongitude = Math.min(longitude, minLongitude); float textureY = 0.5f * (1.0f - direction.getZ32()); texturePoints[vertexOffset + vertexIndex] = new Point2D32(0.0f, textureY); } for (int vertexIndex = 0; vertexIndex < face.getNumberOfEdges(); vertexIndex++) { double longitude = minLongitude + EuclidCoreTools.angleDifferenceMinusPiToPi(longitudes[vertexIndex], minLongitude); float textureX = (float) (0.5 * (longitude / Math.PI + 1.0)); texturePoints[vertexOffset + vertexIndex].setX(textureX); } for (int i = 2; i < face.getNumberOfEdges(); i++) { triangleIndices[triangleOffset++] = vertexOffset; triangleIndices[triangleOffset++] = vertexOffset + i; triangleIndices[triangleOffset++] = vertexOffset + i - 1; } vertexOffset += face.getNumberOfEdges(); } return new TriangleMesh3DDefinition("ConvexPolytope Factory", vertices, texturePoints, normals, triangleIndices); } /* * TODO: The following is for drawing STP shapes. Needs some cleanup. */ public static TriangleMesh3DDefinition toSTPBox3DMesh(RigidBodyTransformReadOnly pose, Tuple3DReadOnly size, double smallRadius, double largeRadius, boolean highlightLimits) { return toSTPBox3DMesh(pose, size.getX(), size.getY(), size.getZ(), smallRadius, largeRadius, highlightLimits); } public static TriangleMesh3DDefinition toSTPBox3DMesh(RigidBodyTransformReadOnly pose, double sizeX, double sizeY, double sizeZ, double smallRadius, double largeRadius, boolean highlightLimits) { return combine(true, false, toSTPBox3DMeshes(pose, sizeX, sizeY, sizeZ, smallRadius, largeRadius, highlightLimits)); } public static TriangleMesh3DDefinition[] toSTPBox3DMeshes(RigidBodyTransformReadOnly pose, double sizeX, double sizeY, double sizeZ, double smallRadius, double largeRadius, boolean highlightLimits) { Box3D stpBox3D = new Box3D(sizeX, sizeY, sizeZ); if (pose != null) stpBox3D.getPose().set(pose); BoxPolytope3DView boxPolytope = stpBox3D.asConvexPolytope(); return toSTPConvexPolytope3DMeshes(boxPolytope, smallRadius, largeRadius, highlightLimits); } public static TriangleMesh3DDefinition toSTPCapsule3DMesh(RigidBodyTransformReadOnly pose, double radius, double length, double smallRadius, double largeRadius, boolean highlightLimits) { return combine(true, false, toSTPCapsule3DMeshes(pose, radius, length, smallRadius, largeRadius, highlightLimits)); } public static TriangleMesh3DDefinition[] toSTPCapsule3DMeshes(RigidBodyTransformReadOnly pose, double radius, double length, double smallRadius, double largeRadius, boolean highlightLimits) { List faceMeshes = new ArrayList<>(); UnitVector3D axis = new UnitVector3D(Axis3D.Z); Point3D position = new Point3D(); if (pose != null) { pose.transform(axis); position.set(pose.getTranslation()); } Point3D topCenter = new Point3D(); topCenter.scaleAdd(0.5 * length, axis, position); Point3D bottomCenter = new Point3D(); bottomCenter.scaleAdd(-0.5 * length, axis, position); // Side face Vector3D axisOrthogonal = newOrthogonalVector(axis); double sphereOffset = EuclidGeometryTools.triangleIsoscelesHeight(largeRadius - smallRadius, length); Point3D sphereCenter = new Point3D(); sphereCenter.scaleAdd(-sphereOffset, axisOrthogonal, position); UnitVector3D startDirection = new UnitVector3D(); UnitVector3D endDirection = new UnitVector3D(); startDirection.sub(bottomCenter, sphereCenter); endDirection.sub(topCenter, sphereCenter); TriangleMesh3DDefinition arc = toArcPointsAndNormals(sphereCenter, largeRadius, startDirection, endDirection, 64); faceMeshes.add(applyRevolution(arc, position, axis, 0.0, TwoPi, 64, false)); if (highlightLimits) { double limitPositionOnAxis = 0.5 * length * largeRadius / (largeRadius - smallRadius); double limitRadius = sphereOffset * smallRadius / (largeRadius - smallRadius); TriangleMesh3DDefinition sideLimitMesh = ArcTorus(0.0, TwoPi, limitRadius, 0.001, 64); sideLimitMesh = rotate(sideLimitMesh, EuclidGeometryTools.axisAngleFromZUpToVector3D(axis)); sideLimitMesh = translate(sideLimitMesh, position); Arrays.asList(sideLimitMesh.getVertices()).forEach(v -> v.scaleAdd(limitPositionOnAxis, axis, v)); faceMeshes.add(sideLimitMesh.copy()); Arrays.asList(sideLimitMesh.getVertices()).forEach(v -> v.scaleAdd(-2.0 * limitPositionOnAxis, axis, v)); faceMeshes.add(sideLimitMesh); } // Cap faces startDirection.set(axis); arc = toArcPointsAndNormals(topCenter, smallRadius, endDirection, startDirection, 64); TriangleMesh3DDefinition capMesh = applyRevolution(arc, position, axis, 0.0, TwoPi, 64, false); faceMeshes.add(capMesh.copy()); // Flipping the meshes around to draw the bottom cap RotationMatrix flipRotation = new RotationMatrix(); flipRotation.setAxisAngle(axisOrthogonal.getX(), axisOrthogonal.getY(), axisOrthogonal.getZ(), Math.PI); Arrays.asList(capMesh.getVertices()).forEach(v -> { v.sub(position); flipRotation.transform(v); v.add(position); }); Arrays.asList(capMesh.getNormals()).forEach(n -> flipRotation.transform(n)); faceMeshes.add(capMesh); return new TriangleMesh3DDefinition[] {combine(true, false, faceMeshes)}; } public static TriangleMesh3DDefinition toSTPCylinder3DMesh(RigidBodyTransformReadOnly pose, double radius, double length, double smallRadius, double largeRadius, boolean highlightLimits) { return combine(true, false, toSTPCylinder3DMeshes(pose, radius, length, smallRadius, largeRadius, highlightLimits)); } public static TriangleMesh3DDefinition[] toSTPCylinder3DMeshes(RigidBodyTransformReadOnly pose, double radius, double length, double smallRadius, double largeRadius, boolean highlightLimits) { List faceMeshes = new ArrayList<>(); List edgeMeshes = new ArrayList<>(); UnitVector3D axis = new UnitVector3D(Axis3D.Z); Point3D position = new Point3D(); if (pose != null) { axis.applyTransform(pose); position.set(pose.getTranslation()); } Point3D topCenter = new Point3D(); topCenter.scaleAdd(0.5 * length, axis, position); Point3D bottomCenter = new Point3D(); bottomCenter.scaleAdd(-0.5 * length, axis, position); { // Side face Vector3D axisOrthogonal = newOrthogonalVector(axis); double sphereOffset = EuclidGeometryTools.triangleIsoscelesHeight(largeRadius - smallRadius, length); Point3D sphereCenter = new Point3D(); sphereCenter.scaleAdd(-sphereOffset + radius, axisOrthogonal, position); UnitVector3D startDirection = new UnitVector3D(); UnitVector3D endDirection = new UnitVector3D(); startDirection.scaleAdd(radius, axisOrthogonal, bottomCenter); startDirection.sub(sphereCenter); endDirection.scaleAdd(radius, axisOrthogonal, topCenter); endDirection.sub(sphereCenter); TriangleMesh3DDefinition arc = toArcPointsAndNormals(sphereCenter, largeRadius, startDirection, endDirection, 64); faceMeshes.add(applyRevolution(arc, position, axis, 0.0, TwoPi, 64, false)); if (highlightLimits) { double limitPositionOnAxis = 0.5 * length * largeRadius / (largeRadius - smallRadius); double limitRadius = radius + sphereOffset * smallRadius / (largeRadius - smallRadius); TriangleMesh3DDefinition sideLimitMesh = ArcTorus(0.0, TwoPi, limitRadius, 0.001, 64); sideLimitMesh = rotate(sideLimitMesh, EuclidGeometryTools.axisAngleFromZUpToVector3D(axis)); sideLimitMesh = translate(sideLimitMesh, position); Arrays.asList(sideLimitMesh.getVertices()).forEach(v -> v.scaleAdd(limitPositionOnAxis, axis, v)); faceMeshes.add(sideLimitMesh.copy()); Arrays.asList(sideLimitMesh.getVertices()).forEach(v -> v.scaleAdd(-2.0 * limitPositionOnAxis, axis, v)); faceMeshes.add(sideLimitMesh); } } { // Cap faces double sphereOffset = EuclidGeometryTools.triangleIsoscelesHeight(largeRadius - smallRadius, 2.0 * radius); Point3D sphereCenter = new Point3D(); sphereCenter.scaleAdd(-sphereOffset, axis, topCenter); Vector3D axisOrthogonal = newOrthogonalVector(axis); UnitVector3D boundaryDirection = new UnitVector3D(); boundaryDirection.scaleAdd(radius, axisOrthogonal, topCenter); boundaryDirection.sub(sphereCenter); TriangleMesh3DDefinition arc = toArcPointsAndNormals(sphereCenter, largeRadius, boundaryDirection, axis, 64); TriangleMesh3DDefinition capMesh = applyRevolution(arc, position, axis, 0.0, TwoPi, 64, false); faceMeshes.add(capMesh.copy()); // Flipping the meshes around to draw the bottom cap RotationMatrix flipRotation = new RotationMatrix(); flipRotation.setAxisAngle(axisOrthogonal.getX(), axisOrthogonal.getY(), axisOrthogonal.getZ(), Math.PI); Arrays.asList(capMesh.getVertices()).forEach(v -> { v.sub(position); flipRotation.transform(v); v.add(position); }); Arrays.asList(capMesh.getNormals()).forEach(n -> flipRotation.transform(n)); faceMeshes.add(capMesh); if (highlightLimits) { double limitPositionOnAxis = 0.5 * length + sphereOffset * smallRadius / (largeRadius - smallRadius); double limitRadius = radius * largeRadius / (largeRadius - smallRadius); TriangleMesh3DDefinition capLimitMesh = ArcTorus(0.0, TwoPi, limitRadius, 0.001, 64); capLimitMesh = rotate(capLimitMesh, EuclidGeometryTools.axisAngleFromZUpToVector3D(axis)); capLimitMesh = translate(capLimitMesh, position); Arrays.asList(capLimitMesh.getVertices()).forEach(v -> v.scaleAdd(limitPositionOnAxis, axis, v)); faceMeshes.add(capLimitMesh.copy()); Arrays.asList(capLimitMesh.getVertices()).forEach(v -> v.scaleAdd(-2.0 * limitPositionOnAxis, axis, v)); faceMeshes.add(capLimitMesh); } } { // Edges Vector3D axisOrthogonal = newOrthogonalVector(axis); Point3D arcCenter = new Point3D(); arcCenter.scaleAdd(radius, axisOrthogonal, topCenter); double capSphereOffset = EuclidGeometryTools.triangleIsoscelesHeight(largeRadius - smallRadius, 2.0 * radius); Point3D capSphereCenter = new Point3D(); capSphereCenter.scaleAdd(-capSphereOffset, axis, topCenter); double sideSphereOffset = EuclidGeometryTools.triangleIsoscelesHeight(largeRadius - smallRadius, length); Point3D sideSphereCenter = new Point3D(); sideSphereCenter.scaleAdd(-sideSphereOffset + radius, axisOrthogonal, position); UnitVector3D startDirection = new UnitVector3D(); UnitVector3D endDirection = new UnitVector3D(); startDirection.sub(arcCenter, sideSphereCenter); endDirection.sub(arcCenter, capSphereCenter); TriangleMesh3DDefinition arc = toArcPointsAndNormals(arcCenter, smallRadius, startDirection, endDirection, 32); TriangleMesh3DDefinition edgeMesh = applyRevolution(arc, position, axis, 0.0, TwoPi, 64, false); faceMeshes.add(edgeMesh.copy()); RotationMatrix flipRotation = new RotationMatrix(); flipRotation.setAxisAngle(axisOrthogonal.getX(), axisOrthogonal.getY(), axisOrthogonal.getZ(), Math.PI); Arrays.asList(edgeMesh.getVertices()).forEach(v -> { v.sub(position); flipRotation.transform(v); v.add(position); }); Arrays.asList(edgeMesh.getNormals()).forEach(n -> flipRotation.transform(n)); faceMeshes.add(edgeMesh); } return new TriangleMesh3DDefinition[] {combine(true, false, faceMeshes), combine(true, false, edgeMeshes)}; } public static TriangleMesh3DDefinition toSTPRamp3DMesh(RigidBodyTransformReadOnly pose, double sizeX, double sizeY, double sizeZ, double smallRadius, double largeRadius, boolean highlightLimits) { return combine(true, false, toSTPRamp3DMeshes(pose, sizeX, sizeY, sizeZ, smallRadius, largeRadius, highlightLimits)); } public static TriangleMesh3DDefinition[] toSTPRamp3DMeshes(RigidBodyTransformReadOnly pose, double sizeX, double sizeY, double sizeZ, double smallRadius, double largeRadius, boolean highlightLimits) { Ramp3D stpRamp3D = new Ramp3D(sizeX, sizeY, sizeZ); if (pose != null) stpRamp3D.getPose().set(pose); return toSTPConvexPolytope3DMeshes(stpRamp3D.asConvexPolytope(), smallRadius, largeRadius, highlightLimits); } public static TriangleMesh3DDefinition toSTPConvexPolytope3DMesh(ConvexPolytope3DReadOnly convexPolytope, double smallRadius, double largeRadius, boolean highlightLimits) { return combine(true, false, toSTPConvexPolytope3DMeshes(convexPolytope, smallRadius, largeRadius, highlightLimits)); } public static TriangleMesh3DDefinition[] toSTPConvexPolytope3DMeshes(ConvexPolytope3DReadOnly convexPolytope, double smallRadius, double largeRadius, boolean highlightLimits) { List faceMeshes = new ArrayList<>(); List edgeMeshes = new ArrayList<>(); List vertexMeshes = new ArrayList<>(); for (int faceIndex = 0; faceIndex < convexPolytope.getNumberOfFaces(); faceIndex++) { Face3DReadOnly face = convexPolytope.getFace(faceIndex); // Produce faces' big sphere mesh faceMeshes.addAll(toFaceSpheres(face, largeRadius, smallRadius, highlightLimits)); // Produce faces' inner-tori meshes (only for faces that are non-cyclic) edgeMeshes.addAll(toFaceInnerTori(face, largeRadius, smallRadius)); } Set processedHalfEdgeSet = new HashSet<>(); for (int edgeIndex = 0; edgeIndex < convexPolytope.getNumberOfHalfEdges(); edgeIndex++) { // Building the edges' torus HalfEdge3DReadOnly halfEdge = convexPolytope.getHalfEdge(edgeIndex); if (processedHalfEdgeSet.contains(halfEdge.getTwin())) continue; processedHalfEdgeSet.add(halfEdge); edgeMeshes.add(toHalfEdgeTorus(halfEdge, largeRadius, smallRadius)); } for (int vertexIndex = 0; vertexIndex < convexPolytope.getNumberOfVertices(); vertexIndex++) { // Building the vertices' small sphere Vertex3DReadOnly vertex = convexPolytope.getVertex(vertexIndex); vertexMeshes.addAll(toVertexSphere(vertex, largeRadius, smallRadius, false)); } return new TriangleMesh3DDefinition[] {combine(true, false, faceMeshes), combine(true, false, edgeMeshes), combine(true, false, vertexMeshes)}; } public static List toFaceSpheres(Face3DReadOnly face, double largeRadius, double smallRadius, boolean highlightLimits) { List meshes = new ArrayList<>(); boolean isFaceCyclicPolygon = isFaceCyclicPolygon(face, largeRadius, smallRadius); int startIndex = computeFaceStartIndex(face); Vertex3DReadOnly v0 = face.getVertex(startIndex); for (int indexOffset = 1; indexOffset < face.getNumberOfEdges() - 1; indexOffset++) { Vertex3DReadOnly v1 = face.getVertex((startIndex + indexOffset) % face.getNumberOfEdges()); Vertex3DReadOnly v2 = face.getVertex((startIndex + indexOffset + 1) % face.getNumberOfEdges()); meshes.addAll(toFaceSubSphere(face, v0, v1, v2, largeRadius, smallRadius, highlightLimits && !isFaceCyclicPolygon)); } if (highlightLimits && isFaceCyclicPolygon) { meshes.addAll(toCyclicFaceSphereLimits(face, largeRadius, smallRadius, 0.001)); } return meshes; } private static int computeFaceStartIndex(Face3DReadOnly face) { int startIndex = 0; double maxDistanceSquared = 0.0; for (int i = 0; i < face.getNumberOfEdges(); i++) { Vertex3DReadOnly v0 = face.getVertex(i); for (int j = 0; j < face.getNumberOfEdges(); j++) { Vertex3DReadOnly v1 = face.getVertex(j); double distanceSquared = v0.distanceSquared(v1); if (distanceSquared > maxDistanceSquared) { startIndex = i; maxDistanceSquared = distanceSquared; } } } return startIndex; } private static boolean isFaceCyclicPolygon(Face3DReadOnly face, double largeRadius, double smallRadius) { if (face.getNumberOfEdges() <= 3) return true; Point3D sphereCenter = new Point3D(); Vertex3DReadOnly v0 = face.getVertex(0); Vertex3DReadOnly v1 = face.getVertex(1); Vertex3DReadOnly v2 = face.getVertex(2); double radius = largeRadius - smallRadius; EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1, v2, radius, sphereCenter); double radiusSquared = EuclidCoreTools.square(radius); for (int vertexIndex = 3; vertexIndex < face.getNumberOfEdges(); vertexIndex++) { double distanceSquared = face.getVertex(vertexIndex).distanceSquared(sphereCenter); if (!EuclidCoreTools.epsilonEquals(radiusSquared, distanceSquared, 1.0e-12)) return false; } return true; } public static List toFaceSubSphere(Face3DReadOnly owner, Vertex3DReadOnly v0, Vertex3DReadOnly v1, Vertex3DReadOnly v2, double largeRadius, double smallRadius, boolean highlightLimits) { List meshes = new ArrayList<>(); Point3D sphereCenter = new Point3D(); Vector3D limitA = new Vector3D(); Vector3D limitB = new Vector3D(); Vector3D limitC = new Vector3D(); EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1, v2, largeRadius - smallRadius, sphereCenter); limitA.sub(v0, sphereCenter); limitB.sub(v1, sphereCenter); limitC.sub(v2, sphereCenter); meshes.add(toPartialSphereMesh(sphereCenter, limitA, limitB, limitC, largeRadius, 32)); if (highlightLimits) meshes.addAll(toFaceSubSphereLimits(owner, v0, v1, v2, largeRadius, smallRadius, 0.001)); return meshes; } public static List toCyclicFaceSphereLimits(Face3DReadOnly face, double largeRadius, double smallRadius, double lineThickness) { List meshes = new ArrayList<>(); Point3D arcCenter = new Point3D(); Vector3D arcNormal = new Vector3D(); Vector3D startDirection = new Vector3D(); Vector3D endDirection = new Vector3D(); Point3D endpoint = new Point3D(); Vertex3DReadOnly v0 = face.getVertex(0); Vertex3DReadOnly v1 = face.getVertex(1); Vertex3DReadOnly v2 = face.getVertex(2); double radius = largeRadius - smallRadius; EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1, v2, radius, arcCenter); for (int edgeIndex = 0; edgeIndex < face.getNumberOfEdges(); edgeIndex++) { // Creates arcs to highlight the limits of the big spheres. HalfEdge3DReadOnly edge = face.getEdge(edgeIndex); EuclidGeometryTools.normal3DFromThreePoint3Ds(arcCenter, edge.getFirstEndpoint(), edge.getSecondEndpoint(), arcNormal); startDirection.sub(edge.getFirstEndpoint(), arcCenter); endDirection.sub(edge.getSecondEndpoint(), arcCenter); meshes.addAll(toSegmentedLine3DMesh(arcCenter, arcNormal, largeRadius, lineThickness, startDirection, startDirection.angle(endDirection), 32, 8)); endpoint.scaleAdd(largeRadius / startDirection.norm(), startDirection, arcCenter); meshes.add(translate(Sphere(lineThickness, 8, 8), endpoint)); } return meshes; } private static List toFaceSubSphereLimits(Face3DReadOnly owner, Vertex3DReadOnly v0, Vertex3DReadOnly v1, Vertex3DReadOnly v2, double largeRadius, double smallRadius, double lineThickness) { List meshes = new ArrayList<>(); Point3D arcCenter = new Point3D(); Vector3D arcNormal = new Vector3D(); Vector3D startDirection = new Vector3D(); Vector3D endDirection = new Vector3D(); Point3D endpoint = new Point3D(); EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1, v2, largeRadius - smallRadius, arcCenter); Vertex3DReadOnly[] vertices = {v0, v1, v2}; for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++) { // Creates arcs to highlight the limits of the big spheres. Vertex3DReadOnly start = vertices[vertexIndex]; Vertex3DReadOnly end = vertices[(vertexIndex + 1) % 3]; EuclidGeometryTools.normal3DFromThreePoint3Ds(arcCenter, start, end, arcNormal); startDirection.sub(start, arcCenter); endDirection.sub(end, arcCenter); meshes.addAll(toSegmentedLine3DMesh(arcCenter, arcNormal, largeRadius, lineThickness, startDirection, startDirection.angle(endDirection), 32, 8)); endpoint.scaleAdd(largeRadius / startDirection.norm(), startDirection, arcCenter); meshes.add(translate(Sphere(lineThickness, 8, 8), endpoint)); } return meshes; } public static List toFaceInnerTori(Face3DReadOnly face, double largeRadius, double smallRadius) { if (isFaceCyclicPolygon(face, largeRadius, smallRadius)) return Collections.emptyList(); List meshes = new ArrayList<>(); Point3D prevTriangleSphere = new Point3D(); Point3D nextTriangleSphere = new Point3D(); Vector3D prevSphereToEdge = new Vector3D(); Vector3D nextSphereToEdge = new Vector3D(); Point3D edgeCenter = new Point3D(); UnitVector3D edgeAxis = new UnitVector3D(); Vector3D startDirection = new Vector3D(); Vector3D endDirection = new Vector3D(); int startIndex = computeFaceStartIndex(face); Vertex3DReadOnly v0 = face.getVertex(startIndex); for (int indexOffset = 2; indexOffset < face.getNumberOfEdges() - 1; indexOffset++) { Vertex3DReadOnly v1Prev = face.getVertex((startIndex + indexOffset - 1) % face.getNumberOfEdges()); Vertex3DReadOnly v2Prev = face.getVertex((startIndex + indexOffset) % face.getNumberOfEdges()); Vertex3DReadOnly v1Next = v2Prev; Vertex3DReadOnly v2Next = face.getVertex((startIndex + indexOffset + 1) % face.getNumberOfEdges()); EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1Prev, v2Prev, largeRadius - smallRadius, prevTriangleSphere); EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1Next, v2Next, largeRadius - smallRadius, nextTriangleSphere); edgeCenter.add(v0, v2Prev); edgeCenter.scale(0.5); prevSphereToEdge.sub(edgeCenter, prevTriangleSphere); nextSphereToEdge.sub(edgeCenter, nextTriangleSphere); edgeAxis.sub(v2Prev, v0); edgeAxis.negate(); double endRevolutionAngle = prevSphereToEdge.angle(nextSphereToEdge); startDirection.sub(v2Prev, nextTriangleSphere); endDirection.sub(v0, nextTriangleSphere); TriangleMesh3DDefinition arcData = toArcPointsAndNormals(nextTriangleSphere, largeRadius, startDirection, endDirection, 32); meshes.add(applyRevolution(arcData, edgeCenter, edgeAxis, 0.0, endRevolutionAngle, 16, false)); } return meshes; } public static TriangleMesh3DDefinition toHalfEdgeTorus(HalfEdge3DReadOnly halfEdge, double largeRadius, double smallRadius) { Vector3D startDirection = new Vector3D(); Vector3D endDirection = new Vector3D(); Vector3D sphereToEdgeA = new Vector3D(); Vector3D sphereToEdgeB = new Vector3D(); Point3D neighborSphereCenterA = computeNeighborFaceSubSphereCenter(halfEdge, largeRadius, smallRadius); Point3D neighborSphereCenterB = computeNeighborFaceSubSphereCenter(halfEdge.getTwin(), largeRadius, smallRadius); sphereToEdgeA.sub(halfEdge.midpoint(), neighborSphereCenterA); sphereToEdgeB.sub(halfEdge.midpoint(), neighborSphereCenterB); Vector3DBasics revolutionAxis = halfEdge.getDirection(true); revolutionAxis.negate(); double endRevolutionAngle = sphereToEdgeA.angle(sphereToEdgeB); startDirection.sub(halfEdge.getDestination(), neighborSphereCenterA); endDirection.sub(halfEdge.getOrigin(), neighborSphereCenterA); TriangleMesh3DDefinition arcData = toArcPointsAndNormals(neighborSphereCenterA, largeRadius, startDirection, endDirection, 32); return applyRevolution(arcData, halfEdge.midpoint(), revolutionAxis, 0.0, endRevolutionAngle, 16, false); } private static Point3D computeNeighborFaceSubSphereCenter(HalfEdge3DReadOnly edge, double largeRadius, double smallRadius) { Point3D neighorSubSphereCenter = new Point3D(); Face3DReadOnly neighbor = edge.getFace(); int edgeIndex = neighbor.getEdges().indexOf(edge); int neighborStartIndex = computeFaceStartIndex(neighbor); Vertex3DReadOnly v0 = neighbor.getVertex(neighborStartIndex); Vertex3DReadOnly v1; Vertex3DReadOnly v2; if (edgeIndex == neighborStartIndex) { v1 = neighbor.getVertex((neighborStartIndex + 1) % neighbor.getNumberOfEdges()); v2 = neighbor.getVertex((neighborStartIndex + 2) % neighbor.getNumberOfEdges()); } else { int neighborLastIndex = neighborStartIndex - 1; if (neighborLastIndex < 0) neighborLastIndex += neighbor.getNumberOfEdges(); if (edgeIndex == neighborLastIndex) { int neighborSecondToLastIndex = neighborStartIndex - 2; if (neighborSecondToLastIndex < 0) neighborSecondToLastIndex += neighbor.getNumberOfEdges(); v1 = neighbor.getVertex(neighborSecondToLastIndex); v2 = neighbor.getVertex(neighborLastIndex); } else { v1 = edge.getOrigin(); v2 = edge.getDestination(); } } EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1, v2, largeRadius - smallRadius, neighorSubSphereCenter); return neighorSubSphereCenter; } public static List toVertexSphere(Vertex3DReadOnly vertex, double largeRadius, double smallRadius, boolean addVerticesMesh) { List meshes = new ArrayList<>(); for (int edgeIndex = 0; edgeIndex < vertex.getNumberOfAssociatedEdges(); edgeIndex++) { meshes.add(toVertexPartialSphere(vertex, vertex.getAssociatedEdge(edgeIndex), largeRadius, smallRadius, addVerticesMesh)); meshes.addAll(toVertexPartialSpheres(vertex, vertex.getAssociatedEdge(edgeIndex).getFace(), largeRadius, smallRadius, addVerticesMesh)); } return meshes; } public static TriangleMesh3DDefinition toVertexPartialSphere(Vertex3DReadOnly vertex, HalfEdge3DReadOnly associatedEdge, double largeRadius, double smallRadius, boolean addVerticesMesh) { Vector3D limitC = new Vector3D(); for (int faceIndex = 0; faceIndex < vertex.getNumberOfAssociatedEdges(); faceIndex++) { limitC.add(directionNeighborSubSphereToVertex(vertex, vertex.getAssociatedEdge(faceIndex).getFace(), largeRadius, smallRadius)); } limitC.normalize(); return toVertexPartialSphere(vertex, limitC, associatedEdge.midpoint(), associatedEdge.length(), computeNeighborFaceSubSphereCenter(associatedEdge.getTwin(), largeRadius, smallRadius), computeNeighborFaceSubSphereCenter(associatedEdge, largeRadius, smallRadius), largeRadius, smallRadius, addVerticesMesh); } public static List toVertexPartialSpheres(Vertex3DReadOnly vertex, Face3DReadOnly neighbor, double largeRadius, double smallRadius, boolean addVerticesMesh) { if (isFaceCyclicPolygon(neighbor, largeRadius, smallRadius)) return Collections.emptyList(); Vector3D limitC = new Vector3D(); for (int faceIndex = 0; faceIndex < vertex.getNumberOfAssociatedEdges(); faceIndex++) { limitC.add(directionNeighborSubSphereToVertex(vertex, vertex.getAssociatedEdge(faceIndex).getFace(), largeRadius, smallRadius)); } limitC.normalize(); List meshes = new ArrayList<>(); Point3D prevTriangleSphere = new Point3D(); Point3D nextTriangleSphere = new Point3D(); Point3D edgeCenter = new Point3D(); int startIndex = computeFaceStartIndex(neighbor); Vertex3DReadOnly v0 = neighbor.getVertex(startIndex); for (int indexOffset = 2; indexOffset < neighbor.getNumberOfEdges() - 1; indexOffset++) { Vertex3DReadOnly v1Prev = neighbor.getVertex((startIndex + indexOffset - 1) % neighbor.getNumberOfEdges()); Vertex3DReadOnly v2Prev = neighbor.getVertex((startIndex + indexOffset) % neighbor.getNumberOfEdges()); Vertex3DReadOnly v1Next = v2Prev; Vertex3DReadOnly v2Next = neighbor.getVertex((startIndex + indexOffset + 1) % neighbor.getNumberOfEdges()); EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1Prev, v2Prev, largeRadius - smallRadius, prevTriangleSphere); EuclidGeometryTools.sphere3DPositionFromThreePoints(v0, v1Next, v2Next, largeRadius - smallRadius, nextTriangleSphere); edgeCenter.add(v0, v2Prev); edgeCenter.scale(0.5); meshes.add(toVertexPartialSphere(vertex, limitC, edgeCenter, v0.distance(v2Prev), prevTriangleSphere, nextTriangleSphere, largeRadius, smallRadius, addVerticesMesh)); } return meshes; } public static TriangleMesh3DDefinition toVertexPartialSphere(Vertex3DReadOnly vertex, Vector3DReadOnly limitC, Point3DReadOnly commonEdgeCenter, double commonEdgeLength, Point3DReadOnly sphereA, Point3DReadOnly sphereB, double largeRadius, double smallRadius, boolean addVerticesMesh) { Vector3D sphereAToEdge = new Vector3D(); sphereAToEdge.sub(commonEdgeCenter, sphereA); sphereAToEdge.normalize(); Vector3D sphereBToEdge = new Vector3D(); sphereBToEdge.sub(commonEdgeCenter, sphereB); sphereBToEdge.normalize(); Vector3D testWinding = new Vector3D(); testWinding.cross(sphereAToEdge, sphereBToEdge); boolean flip = testWinding.dot(limitC) > 0.0; double radius = EuclidGeometryTools.triangleIsoscelesHeight(largeRadius - smallRadius, commonEdgeLength); Vector3D sphereToEdge = new Vector3D(); Point3D sphereCenter = new Point3D(); Vector3D limitAB = new Vector3D(); DoubleFunction limitABFunction = alpha -> { if (flip) alpha = 1.0 - alpha; sphereToEdge.interpolate(sphereAToEdge, sphereBToEdge, alpha); sphereToEdge.scale(radius / sphereToEdge.norm()); sphereCenter.sub(commonEdgeCenter, sphereToEdge); limitAB.sub(vertex, sphereCenter); limitAB.normalize(); return limitAB; }; return toPartialSphereMesh(vertex, limitABFunction, limitC, smallRadius, 16, false); } private static Vector3DReadOnly directionNeighborSubSphereToVertex(Vertex3DReadOnly vertex, Face3DReadOnly neighbor, double largeRadius, double smallRadius) { int edgeIndex = neighbor.getVertices().indexOf(vertex); Vector3D direction = new Vector3D(); direction.sub(vertex, computeNeighborFaceSubSphereCenter(neighbor.getEdge(edgeIndex), largeRadius, smallRadius)); return direction; } public static List toSegmentedLine3DMesh(Point3DReadOnly arcCenter, Vector3DReadOnly arcNormal, double arcRadius, double thickness, Vector3DReadOnly startDirection, double angleSpan, int resolution, int radialResolution) { SegmentedLine3DTriangleMeshFactory generator = new SegmentedLine3DTriangleMeshFactory(resolution, radialResolution); AxisAngle axisAngle = new AxisAngle(arcNormal, 0.0); Vector3D direction = new Vector3D(); Point3D[] points = new Point3D[resolution]; for (int i = 0; i < resolution; i++) { axisAngle.setAngle(angleSpan * i / (resolution - 1.0)); axisAngle.transform(startDirection, direction); direction.normalize(); Point3D point = new Point3D(); point.scaleAdd(arcRadius, direction, arcCenter); points[i] = point; } generator.setLineRadius(thickness); generator.compute(points); return Arrays.asList(generator.getTriangleMesh3DDefinitions()); } public static TriangleMesh3DDefinition toPartialSphereMesh(Point3DReadOnly sphereCenter, Tuple3DReadOnly limitA, Tuple3DReadOnly limitB, Tuple3DReadOnly limitC, double sphereRadius, int resolution) { return toPartialSphereMesh(sphereCenter, limitA, limitB, limitC, sphereRadius, resolution, false); } public static TriangleMesh3DDefinition toPartialSphereMesh(Point3DReadOnly sphereCenter, Tuple3DReadOnly limitA, Tuple3DReadOnly limitB, Tuple3DReadOnly limitC, double sphereRadius, int resolution, boolean addVerticesMesh) { return toPartialSphereMesh(sphereCenter, alpha -> interpolateVector3D(limitA, limitB, alpha), alpha -> interpolateVector3D(limitB, limitC, alpha), alpha -> interpolateVector3D(limitC, limitA, alpha), sphereRadius, resolution, addVerticesMesh); } public static TriangleMesh3DDefinition toPartialSphereMesh(Point3DReadOnly sphereCenter, DoubleFunction limitABFunction, Tuple3DReadOnly limitC, double sphereRadius, int resolution, boolean addVerticesMesh) { Vector3D limitA = new Vector3D(limitABFunction.apply(0.0)); Vector3D limitB = new Vector3D(limitABFunction.apply(1.0)); limitA.normalize(); limitB.normalize(); return toPartialSphereMesh(sphereCenter, limitABFunction, alpha -> interpolateVector3D(limitB, limitC, alpha), alpha -> interpolateVector3D(limitC, limitA, alpha), sphereRadius, resolution, addVerticesMesh); } public static TriangleMesh3DDefinition toPartialSphereMesh(Point3DReadOnly sphereCenter, DoubleFunction limitABFunction, DoubleFunction limitBCFunction, DoubleFunction limitCAFunction, double sphereRadius, int resolution, boolean addVerticesMesh) { List points = new ArrayList<>(); List normals = new ArrayList<>(); List textPoints = new ArrayList<>(); Point3D limitAB = new Point3D(); for (int longitude = 0; longitude < resolution - 1; longitude++) { double longitudeAlpha = longitude / (resolution - 1.0); int latitudeResolution = (int) Math.round(EuclidCoreTools.interpolate(resolution, 1, longitudeAlpha)); { // latitude = 0, point is on AC limit Vector3D32 direction = new Vector3D32(); direction.set(limitCAFunction.apply(1.0 - longitudeAlpha)); direction.normalize(); Point3D32 point = new Point3D32(); point.scaleAdd(sphereRadius, direction, sphereCenter); points.add(point); normals.add(direction); textPoints.add(new Point2D32((float) longitudeAlpha, 0.0f)); } for (int latitude = 1; latitude < latitudeResolution - 1; latitude++) { double latitudeAlpha = latitude / (latitudeResolution - 1.0); limitAB.set(limitABFunction.apply(latitudeAlpha)); Vector3D32 direction = new Vector3D32(); direction.interpolate(limitAB, limitCAFunction.apply(0.0), longitudeAlpha); direction.normalize(); Point3D32 point = new Point3D32(); point.scaleAdd(sphereRadius, direction, sphereCenter); points.add(point); normals.add(direction); textPoints.add(new Point2D32((float) longitudeAlpha, (float) latitudeAlpha)); } { // latitude = latitudeResolution - 1, point is on BC limit Vector3D32 direction = new Vector3D32(); direction.set(limitBCFunction.apply(longitudeAlpha)); direction.normalize(); Point3D32 point = new Point3D32(); point.scaleAdd(sphereRadius, direction, sphereCenter); points.add(point); normals.add(direction); textPoints.add(new Point2D32((float) longitudeAlpha, 1.0f)); } } { // Last vertex lies on limitC Vector3D32 direction = new Vector3D32(); direction.set(limitCAFunction.apply(0.0)); direction.normalize(); Point3D32 point = new Point3D32(); point.scaleAdd(sphereRadius, direction, sphereCenter); points.add(point); normals.add(direction); textPoints.add(new Point2D32(1.0f, 1.0f)); } TIntArrayList triangleIndices = new TIntArrayList(); int nextLatitudeResolution = resolution; int longitudeStartIndex = 0; for (int longitude = 0; longitude < resolution - 1; longitude++) { int latitudeResolution = nextLatitudeResolution; nextLatitudeResolution = (int) Math.round(EuclidCoreTools.interpolate(resolution, 1, (longitude + 1.0) / (resolution - 1.0))); int nextLongitudeStartIndex = longitudeStartIndex + latitudeResolution; for (int latitude = 0; latitude < latitudeResolution - 1; latitude++) { if (latitude < nextLatitudeResolution) { triangleIndices.add(longitudeStartIndex + latitude); triangleIndices.add(nextLongitudeStartIndex + latitude); triangleIndices.add(longitudeStartIndex + latitude + 1); } if (latitude < nextLatitudeResolution - 1) { triangleIndices.add(nextLongitudeStartIndex + latitude); triangleIndices.add(nextLongitudeStartIndex + latitude + 1); triangleIndices.add(longitudeStartIndex + latitude + 1); } } longitudeStartIndex += latitudeResolution; } TriangleMesh3DDefinition partialSphereMesh = new TriangleMesh3DDefinition(points.toArray(new Point3D32[0]), textPoints.toArray(new Point2D32[0]), normals.toArray(new Vector3D32[0]), triangleIndices.toArray()); if (!addVerticesMesh) { return partialSphereMesh; } else { TriangleMesh3DDefinition[] meshes = new TriangleMesh3DDefinition[1 + points.size()]; meshes[0] = partialSphereMesh; for (int i = 0; i < points.size(); i++) meshes[i + 1] = translate(Tetrahedron(0.005), points.get(i)); return combine(true, false, meshes); } } public static TriangleMesh3DDefinition toArcPointsAndNormals(Point3DReadOnly arcPosition, double arcRadius, Vector3DReadOnly startDirection, Vector3DReadOnly endDirection, int resolution) { Point3D32[] points = new Point3D32[resolution]; Vector3D32[] normals = new Vector3D32[resolution]; for (int i = 0; i < resolution; i++) { double alpha = i / (resolution - 1.0); Vector3D32 direction = new Vector3D32(); direction.interpolate(startDirection, endDirection, alpha); direction.normalize(); Point3D32 point = new Point3D32(); point.scaleAdd(arcRadius, direction, arcPosition); points[i] = point; normals[i] = direction; } return new TriangleMesh3DDefinition(points, null, normals, null); } public static TriangleMesh3DDefinition applyRevolution(TriangleMesh3DDefinition subMesh, Point3DReadOnly rotationCenter, Vector3DReadOnly rotationAxis, double startAngle, double endAngle, int resolution, boolean addVerticesMesh) { int subMeshSize = subMesh.getVertices().length; Point3D32[] points = new Point3D32[resolution * subMeshSize]; Vector3D32[] normals = new Vector3D32[resolution * subMeshSize]; Point2D32[] textPoints = new Point2D32[resolution * subMeshSize]; AxisAngle rotation = new AxisAngle(); rotation.getAxis().set(rotationAxis); for (int revIndex = 0; revIndex < resolution; revIndex++) { double angle = EuclidCoreTools.interpolate(startAngle, endAngle, revIndex / (resolution - 1.0)); rotation.setAngle(angle); for (int meshIndex = 0; meshIndex < subMeshSize; meshIndex++) { Point3D32 point = new Point3D32(subMesh.getVertices()[meshIndex]); Vector3D32 normal = new Vector3D32(subMesh.getNormals()[meshIndex]); point.sub(rotationCenter); rotation.transform(point); rotation.transform(normal); point.add(rotationCenter); points[revIndex * subMeshSize + meshIndex] = point; normals[revIndex * subMeshSize + meshIndex] = normal; textPoints[revIndex * subMeshSize + meshIndex] = new Point2D32(revIndex / (resolution - 1.0f), meshIndex / (subMeshSize - 1.0f)); } } int numberOfTriangles = 2 * resolution * subMeshSize; int[] triangleIndices = new int[3 * numberOfTriangles]; int index = 0; for (int revIndex = 0; revIndex < resolution - 1; revIndex++) { for (int meshIndex = 0; meshIndex < subMeshSize - 1; meshIndex++) { int nextRevIndex = revIndex + 1; int nextMeshIndex = meshIndex + 1; triangleIndices[index++] = nextRevIndex * subMeshSize + meshIndex; triangleIndices[index++] = nextRevIndex * subMeshSize + nextMeshIndex; triangleIndices[index++] = revIndex * subMeshSize + nextMeshIndex; triangleIndices[index++] = nextRevIndex * subMeshSize + meshIndex; triangleIndices[index++] = revIndex * subMeshSize + nextMeshIndex; triangleIndices[index++] = revIndex * subMeshSize + meshIndex; } } TriangleMesh3DDefinition partialSphereMesh = new TriangleMesh3DDefinition(points, textPoints, normals, triangleIndices); if (!addVerticesMesh) { return partialSphereMesh; } else { TriangleMesh3DDefinition[] meshes = new TriangleMesh3DDefinition[1 + points.length]; meshes[0] = partialSphereMesh; for (int i = 0; i < points.length; i++) meshes[i + 1] = translate(Tetrahedron(0.005), points[i]); return combine(true, false, meshes); } } public static TriangleMesh3DDefinition rotate(TriangleMesh3DDefinition in, Orientation3DReadOnly rotation) { for (Point3D32 vertex : in.getVertices()) rotation.transform(vertex); for (Vector3D32 normal : in.getNormals()) rotation.transform(normal); return in; } public static TriangleMesh3DDefinition translate(TriangleMesh3DDefinition in, Tuple3DReadOnly translation) { for (Point3D32 vertex : in.getVertices()) vertex.add(translation); return in; } public static TriangleMesh3DDefinition combine(boolean adjustTriangleIndices, boolean deepCopy, TriangleMesh3DDefinition... definitions) { return combine(adjustTriangleIndices, deepCopy, Arrays.asList(definitions)); } public static TriangleMesh3DDefinition combine(boolean adjustTriangleIndices, boolean deepCopy, Collection definitions) { int outVertexSize = 0; int outTextureSize = 0; int outNormalSize = 0; int outIndexSize = 0; for (TriangleMesh3DDefinition definition : definitions) { outVertexSize += definition.getVertices() != null ? definition.getVertices().length : 0; outTextureSize += definition.getTextures() != null ? definition.getTextures().length : 0; outNormalSize += definition.getNormals() != null ? definition.getNormals().length : 0; outIndexSize += definition.getTriangleIndices() != null ? definition.getTriangleIndices().length : 0; } Point3D32[] outVertices = new Point3D32[outVertexSize]; Point2D32[] outTextures = new Point2D32[outTextureSize]; Vector3D32[] outNormals = new Vector3D32[outNormalSize]; int[] outIndices = new int[outIndexSize]; int vertexIndex = 0; int textureIndex = 0; int normalIndex = 0; int indexIndex = 0; for (TriangleMesh3DDefinition definition : definitions) { Point3D32[] inVertices = definition.getVertices(); Point2D32[] inTextures = definition.getTextures(); Vector3D32[] inNormals = definition.getNormals(); int[] inIndices = definition.getTriangleIndices(); if (inVertices != null) { if (deepCopy) { for (Point3D32 vertex : inVertices) outVertices[vertexIndex++] = new Point3D32(vertex); } else { System.arraycopy(inVertices, 0, outVertices, vertexIndex, inVertices.length); vertexIndex += inVertices.length; } } if (inTextures != null) { if (deepCopy) { for (Point2D32 texture : inTextures) outTextures[textureIndex++] = new Point2D32(texture); } else { System.arraycopy(inTextures, 0, outTextures, textureIndex, inTextures.length); textureIndex += inTextures.length; } } if (inNormals != null) { if (deepCopy) { for (Vector3D32 normal : inNormals) outNormals[normalIndex++] = new Vector3D32(normal); } else { System.arraycopy(inNormals, 0, outNormals, normalIndex, inNormals.length); normalIndex += inNormals.length; } } if (inIndices != null) { System.arraycopy(inIndices, 0, outIndices, indexIndex, inIndices.length); indexIndex += inIndices.length; } } if (adjustTriangleIndices && definitions.size() > 1) { Iterator iterator = definitions.iterator(); TriangleMesh3DDefinition definition = iterator.next(); int shift = definition.getVertices().length; int startIndex = definition.getTriangleIndices().length; while (iterator.hasNext()) { definition = iterator.next(); int endIndex = startIndex + definition.getTriangleIndices().length; for (int i = startIndex; i < endIndex; i++) { outIndices[i] += shift; } shift += definition.getVertices().length; startIndex = endIndex; } } return new TriangleMesh3DDefinition(outVertices, outTextures, outNormals, outIndices); } public static Vector3D newOrthogonalVector(Vector3DReadOnly referenceVector) { Vector3D orthogonal = new Vector3D(); // Purposefully picking a large tolerance to ensure sanity of the cross-product. if (Math.abs(referenceVector.getY()) > 0.1 || Math.abs(referenceVector.getZ()) > 0.1) orthogonal.set(1.0, 0.0, 0.0); else orthogonal.set(0.0, 1.0, 0.0); orthogonal.cross(referenceVector); orthogonal.normalize(); return orthogonal; } public static Vector3D interpolateVector3D(Tuple3DReadOnly tuple0, Tuple3DReadOnly tuplef, double alpha) { Vector3D interpolated = new Vector3D(); interpolated.interpolate(tuple0, tuplef, alpha); return interpolated; } /** * Reverses the order of the elements in the specified array. *

* This method runs in linear time. *

* * @param array the array whose elements are to be reversed. */ public static void reverse(Object[] array) { for (int i = 0, mid = array.length >> 1, j = array.length - 1; i < mid; i++, j--) { Object oldCoefficient_i = array[i]; array[i] = array[j]; array[j] = oldCoefficient_i; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy