Improve efficiency of 2D projection

This commit is contained in:
SiboVG 2024-02-12 23:22:43 +01:00
parent 77844f80a2
commit 69e604c389

View File

@ -3,6 +3,7 @@ package net.sf.openrocket.file.wavefrontobj;
import de.javagl.obj.FloatTuple; import de.javagl.obj.FloatTuple;
import de.javagl.obj.ObjFace; import de.javagl.obj.ObjFace;
import de.javagl.obj.ObjGroup; import de.javagl.obj.ObjGroup;
import net.sf.openrocket.util.MathUtil;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.GeometryFactory;
@ -70,8 +71,8 @@ public abstract class TriangulationHelper {
public static List<ObjFace> generateCDTFaces(DefaultObj obj, DefaultObjFace face) { public static List<ObjFace> generateCDTFaces(DefaultObj obj, DefaultObjFace face) {
PolygonWithOriginalIndices polygonWithIndices = createProjectedPolygon(obj, face); PolygonWithOriginalIndices polygonWithIndices = createProjectedPolygon(obj, face);
Polygon polygon = polygonWithIndices.getPolygon(); Polygon polygon = polygonWithIndices.polygon();
Map<Coordinate3D, Integer> vertexIndexMap = polygonWithIndices.getVertexIndexMap(); Map<Coordinate3D, Integer> vertexIndexMap = polygonWithIndices.vertexIndexMap();
ConstrainedDelaunayTriangulator triangulator = new ConstrainedDelaunayTriangulator(polygon); ConstrainedDelaunayTriangulator triangulator = new ConstrainedDelaunayTriangulator(polygon);
List<Tri> triangles = triangulator.getTriangles(); List<Tri> triangles = triangulator.getTriangles();
@ -116,7 +117,7 @@ public abstract class TriangulationHelper {
for (int vertexIndex : face.getVertexIndices()) { for (int vertexIndex : face.getVertexIndices()) {
FloatTuple vertex = obj.getVertex(vertexIndex); FloatTuple vertex = obj.getVertex(vertexIndex);
Coordinate3D originalCoord = new Coordinate3D(vertexToCoordinate(vertex)); Coordinate3D originalCoord = new Coordinate3D(vertexToCoordinate(vertex));
Coordinate projectedCoord = projectVertexOntoPlane(originalCoord, normal); Coordinate projectedCoord = projectVertexOntoXYPlane(originalCoord, normal);
projectedCoord = new Coordinate(projectedCoord.x, projectedCoord.y); projectedCoord = new Coordinate(projectedCoord.x, projectedCoord.y);
projectedCoords.add(projectedCoord); projectedCoords.add(projectedCoord);
@ -141,47 +142,23 @@ public abstract class TriangulationHelper {
* @param normal The normal vector of the polygon's face. * @param normal The normal vector of the polygon's face.
* @return The projected 2D coordinate of the vertex. * @return The projected 2D coordinate of the vertex.
*/ */
private static Coordinate projectVertexOntoPlane(Coordinate3D vertex, Coordinate normal) { private static Coordinate projectVertexOntoXYPlane(Coordinate3D vertex, Coordinate normal) {
Coordinate zAxis = new Coordinate(0, 0, 1); // If the normal is a zero vector, the polygon is degenerate and cannot be projected
Coordinate rotationAxis = crossProduct(normal, zAxis); if (MathUtil.equals(normal.x, 0) && MathUtil.equals(normal.y, 0) && MathUtil.equals(normal.z, 0)) {
double rotationAngle = Math.acos(dotProduct(normal, zAxis) / (magnitude(normal) * magnitude(zAxis))); throw new IllegalArgumentException("Cannot project a degenerate polygon onto a 2D plane");
}
// Normalize the rotation axis // If the normal is parallel to the Z-axis, the polygon is already 2D
double axisLength = magnitude(rotationAxis); if (MathUtil.equals(normal.x, 0) && MathUtil.equals(normal.y, 0)) {
if (axisLength > 0) { return new Coordinate(vertex.coordinate().x, vertex.coordinate().y);
rotationAxis.x /= axisLength;
rotationAxis.y /= axisLength;
rotationAxis.z /= axisLength;
} }
// Use Rodrigues' rotation formula or a rotation matrix to rotate the vertex Coordinate u = crossProduct(normal, new Coordinate(0, 0, 1));
Coordinate rotatedVertex = rotateVertex(vertex, rotationAxis, rotationAngle); Coordinate w = crossProduct(normal, u);
return new Coordinate(rotatedVertex.x, rotatedVertex.y); // Projected vertex double x2D = dotProduct(vertex.coordinate(), u);
} double y2D = dotProduct(vertex.coordinate(), w);
/** return new Coordinate(x2D, y2D);
* Rotates a vertex around a given axis by a specified angle.
*
* @param vertex3D The vertex to rotate.
* @param axis The axis of rotation.
* @param angle The angle of rotation in radians.
* @return The rotated vertex.
*/
private static Coordinate rotateVertex(Coordinate3D vertex3D, Coordinate axis, double angle) {
Coordinate vertex = vertex3D.getCoordinate();
double cosTheta = Math.cos(angle);
double sinTheta = Math.sin(angle);
double x = vertex.x, y = vertex.y, z = vertex.z;
double u = axis.x, v = axis.y, w = axis.z;
// Apply Rodrigues' rotation formula
double v1 = u * x + v * y + w * z;
double xPrime = u * v1 * (1d - cosTheta) + x * cosTheta + (-w * y + v * z) * sinTheta;
double yPrime = v * v1 * (1d - cosTheta) + y * cosTheta + (w * x - u * z) * sinTheta;
double zPrime = w * v1 * (1d - cosTheta) + z * cosTheta + (-v * x + u * y) * sinTheta;
return new Coordinate(xPrime, yPrime, zPrime);
} }
/** /**
@ -206,7 +183,7 @@ public abstract class TriangulationHelper {
private static int getNearbyValue(Map<Coordinate3D, Integer> vertexIndexMap, Coordinate coord) { private static int getNearbyValue(Map<Coordinate3D, Integer> vertexIndexMap, Coordinate coord) {
for (Map.Entry<Coordinate3D, Integer> entry : vertexIndexMap.entrySet()) { for (Map.Entry<Coordinate3D, Integer> entry : vertexIndexMap.entrySet()) {
Coordinate key = entry.getKey().getCoordinate(); Coordinate key = entry.getKey().coordinate();
if (key.equals3D(coord)) { if (key.equals3D(coord)) {
return entry.getValue(); return entry.getValue();
} }
@ -225,7 +202,7 @@ public abstract class TriangulationHelper {
} }
private static Coordinate normalize(Coordinate vector) { private static Coordinate normalize(Coordinate vector) {
double magnitude = Math.sqrt(dotProduct(vector, vector)); // Calculate magnitude double magnitude = magnitude(vector);
if (magnitude == 0) { if (magnitude == 0) {
// Handle potential divide by zero if the vector is a zero vector // Handle potential divide by zero if the vector is a zero vector
return new Coordinate(0, 0, 0); return new Coordinate(0, 0, 0);
@ -245,36 +222,10 @@ public abstract class TriangulationHelper {
return normalize(crossProduct(u, v)); return normalize(crossProduct(u, v));
} }
private static class PolygonWithOriginalIndices { private record PolygonWithOriginalIndices(Polygon polygon, Map<Coordinate3D, Integer> vertexIndexMap) { }
private final Polygon polygon;
private final Map<Coordinate3D, Integer> vertexIndexMap;
public PolygonWithOriginalIndices(Polygon polygon, Map<Coordinate3D, Integer> vertexIndexMap) {
this.polygon = polygon;
this.vertexIndexMap = vertexIndexMap;
}
public Polygon getPolygon() {
return polygon;
}
public Map<Coordinate3D, Integer> getVertexIndexMap() {
return vertexIndexMap;
}
}
// Helper class to wrap Coordinate and override equals and hashCode to account for all 3 dimensions // Helper class to wrap Coordinate and override equals and hashCode to account for all 3 dimensions
private static class Coordinate3D { private record Coordinate3D(Coordinate coordinate) {
private Coordinate coordinate;
public Coordinate3D(Coordinate coordinate) {
this.coordinate = coordinate;
}
public Coordinate getCoordinate() {
return coordinate;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;