diff --git a/src/main/java/eu/mihosoft/vrl/v3d/CSG.java b/src/main/java/eu/mihosoft/vrl/v3d/CSG.java index a6d3b5e4..fc5cdf70 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/CSG.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/CSG.java @@ -136,7 +136,7 @@ @SuppressWarnings("restriction") public class CSG implements IuserAPI, Serializable { - transient private static final double POINTS_CONTACT_DISTANCE = 0.00001; + transient private static final double POINTS_CONTACT_DISTANCE = 0.0001; transient private static int MinPolygonsForOffloading = 200; transient private static final long serialVersionUID = 4071874097772427063L; transient private static IDebug3dProvider providerOf3d = null; diff --git a/src/main/java/eu/mihosoft/vrl/v3d/IPolygonRepairTool.java b/src/main/java/eu/mihosoft/vrl/v3d/IPolygonRepairTool.java index aec99b2a..4d95f9d1 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/IPolygonRepairTool.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/IPolygonRepairTool.java @@ -1,5 +1,10 @@ package eu.mihosoft.vrl.v3d; +import java.util.List; + +import javafx.scene.paint.Color; + public interface IPolygonRepairTool { - Polygon repairOverlappingEdges(Polygon concave) throws ColinearPointsException; + List repairOverlappingEdges(List vertices, PropertyStorage shared, + boolean allowDegenerate, Plane p, Color c) throws ColinearPointsException; } diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Node.java b/src/main/java/eu/mihosoft/vrl/v3d/Node.java index 6e16d3b6..085a9ae8 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/Node.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/Node.java @@ -271,8 +271,7 @@ private static int add(List l, int polygonIndex, int[] polygonStartInde return 1; } try { - testAddPolygon(l, orderedPoints, polygonPointX, polygonPointY, polygonPointZ, polygon, polygonBase, size, - false); + testAddPolygon(l, orderedPoints, polygonPointX, polygonPointY, polygonPointZ, polygon, polygonBase, size); return 1; } catch (Exception ex) { ex.printStackTrace(); @@ -282,7 +281,7 @@ private static int add(List l, int polygonIndex, int[] polygonStartInde } private static void testAddPolygon(List l, ArrayList orderedPoints, double[] polygonPointX, - double[] polygonPointY, double[] polygonPointZ, Polygon polygon, int polygonBase, int size, boolean test) { + double[] polygonPointY, double[] polygonPointZ, Polygon polygon, int polygonBase, int size) { List f = new ArrayList<>(); for (int i = polygonBase; i < polygonBase + size; i++) { if (i < orderedPoints.size()) { @@ -296,7 +295,7 @@ private static void testAddPolygon(List l, ArrayList orderedPoi } } - add(l, f, polygon, test); + add(l, f, polygon); } private static boolean addPoint(List f, Vertex v) { @@ -311,24 +310,23 @@ private static boolean addPoint(List f, Vertex v) { return f.add(v); } - private static void add(List l, List f, Polygon polygon) { - add(l, f, polygon, false); - } +// private static void add(List l, List f, Polygon polygon) { +// add(l, f, polygon, false); +// } - private static void add(List l, List f, Polygon polygon, boolean test) { + private static void add(List l, List f, Polygon polygon) { if (f.size() < 3) return; try { - if(!Extrude.isCCW(f, polygon.getPlane().getNormal())) { + if(polygon.getPlane().checkNormal(f) == NormalState.FLIPPED) { Collections.reverse(f); } Polygon fpoly = new Polygon(f, polygon.getStorage(), true, polygon.getPlane()) .setColor(polygon.getColor()); - if (!test) - l.add(fpoly); + l.add(fpoly); }catch(ColinearPointsException ex) { //ex.printStackTrace(); - System.err.println(ex.getMessage()+" Pruned Collinear polygon "+f ); + System.err.println("Pruned Collinear polygon "+f+" "+ex.getMessage() ); } } public static String getOsName() { @@ -934,21 +932,8 @@ private void splitSinglePolygon(Polygon polygon,List coplanarFront, Lis // search for the epsilon values of the incoming plane double posEpsilon = Plane.getEPSILON(); int size = polygon.getVertices().size(); - Vector3d normal = polygon.getPlane().getNormal(); - for (int i = 0; i < size; i++) { - Vector3d pos = polygon.getVertices().get(i).pos; - double dot = normal.dot(pos); - double t = Math.abs(dot - polygon.getPlane().getDist()); - if(t>0.01) { - throw new RuntimeException("A plane epsilon of "+t+" is impossible"); - } - if (t > posEpsilon) { - // com.neuronrobotics.sdk.common.Log.error("Non flat polygon, increasing - // positive epsilon "+t); - posEpsilon = t; - } + //Vector3d normal = polygon.getPlane().getNormal(); - } int polygonType = 0; List types = new ArrayList<>(); // boolean someF =false; @@ -982,7 +967,7 @@ private void splitSinglePolygon(Polygon polygon,List coplanarFront, Lis // Put the polygon in the correct list, splitting it when necessary. switch (polygonType) { case COPLANAR: - double cp = getThisNodePlane().getNormal().dot(normal); + double cp = getThisNodePlane().getNormal().dot(polygon.getPlane().getNormal()); (cp > 0 ? coplanarFront : coplanarBack).add(polygon); break; case FRONT: @@ -1042,6 +1027,7 @@ private void splitSinglePolygon(Polygon polygon,List coplanarFront, Lis // therefor the intersection point is halfway between i and j double t = (d / dotMinus); if (!Double.isFinite(t) || t < 0 || t > 1.0) { + new RuntimeException("ERROR in interpolation!").printStackTrace(); continue; } diff --git a/src/main/java/eu/mihosoft/vrl/v3d/NonFlatPolygonException.java b/src/main/java/eu/mihosoft/vrl/v3d/NonFlatPolygonException.java new file mode 100644 index 00000000..26bc0a39 --- /dev/null +++ b/src/main/java/eu/mihosoft/vrl/v3d/NonFlatPolygonException.java @@ -0,0 +1,11 @@ +package eu.mihosoft.vrl.v3d; + +public class NonFlatPolygonException extends Exception { + + public NonFlatPolygonException(String string) { + super(string); + } + + private static final long serialVersionUID = -7968568496236268642L; + +} diff --git a/src/main/java/eu/mihosoft/vrl/v3d/NormalState.java b/src/main/java/eu/mihosoft/vrl/v3d/NormalState.java new file mode 100644 index 00000000..9c5f4887 --- /dev/null +++ b/src/main/java/eu/mihosoft/vrl/v3d/NormalState.java @@ -0,0 +1,5 @@ +package eu.mihosoft.vrl.v3d; + +public enum NormalState { +SAME,FLIPPED,DIVERGENT; +} diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Plane.java b/src/main/java/eu/mihosoft/vrl/v3d/Plane.java index 7156feed..39d8545f 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/Plane.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/Plane.java @@ -101,10 +101,15 @@ public Plane(Vector3d normal, double dist) { * @throws ColinearPointsException */ public Plane(List vertices, Vector3d testNorm) throws ColinearPointsException { - Vector3d a = vertices.get(0).pos; + Vector3d n = computeNormal(vertices, testNorm); this.setNormal(n); - this.setDist(n.dot(a)); + double distAvg = 0; + for(int i=0;i vertices, Vector3d testNorm) throws ColinearPointsExce */ public Plane(Vector3d normal, List vertices) { this.setNormal(normal.normalized()); - this.setDist(normal.dot(vertices.get(0).pos)); + double distAvg = 0; + for(int i=0;i vertex) { + public NormalState checkNormal(List vertex) { Plane p = null; try { p = Plane.createFromPoints(vertex, getNormal()); @@ -203,84 +213,21 @@ public boolean checkNormal(ArrayList vertex) { if (p != null) { Vector3d normal = p.getNormal(); Vector3d normal2 = getNormal(); - double dot = 1-Math.abs(normal.dot(normal2)); + double dot2 = normal.dot(normal2); + double dot = 1-dot2; + double dFlipped = 2-dot2; // check for actual misallignment double d = Plane.getEPSILON(); - if(dot d - || e4 > d - || e5 > d) { - double e = Math.abs(normal.x) - Math.abs(normal2.x); - double e2 = Math.abs(normal.y) - Math.abs(normal2.y); - double f = Math.abs(normal.z) - Math.abs(normal2.z); - if (e > d - || e2 > d - || f > d) { - return false; - } - return false; - } + if(dot>d) + if(dFlipped>d) + return NormalState.DIVERGENT; + else + return NormalState.FLIPPED; + } - return true; + return NormalState.SAME; } - public static Vector3d computeNormalCrossProduct(List verts) { - int n = verts.size(); - if (n < 3) - return new Vector3d(0, 0, 1); - - // 1. Build all edge vectors - List edges = new ArrayList<>(); - - for (int j = 0; j < n; j++) { - Vector3d e = verts.get((j+1)%n).pos.minus(verts.get(j).pos); - edges.add(e); - - } - - - // 2. Find pair with smallest |dot| / (|e1||e2|) - double bestScore = Double.POSITIVE_INFINITY; - Vector3d bestE1 = null, bestE2 = null; - for (int i = 0; i < edges.size(); i++) { - Vector3d e1 = edges.get(i); - double len1 = e1.length(); - for (int j = i + 1; j < edges.size(); j++) { - Vector3d e2 = edges.get(j); - double len2 = e2.length(); - double score = Math.abs(e1.dot(e2)) / (len1 * len2); - if (score < bestScore) { - bestScore = score; - bestE1 = e1; - bestE2 = e2; - } - } - } - - // 3. Fallback: use first three vertices if no good pair found - if (bestE1 == null) { - Vector3d v0 = verts.get(0).pos; - bestE1 = verts.get(1).pos.minus(v0); - bestE2 = verts.get(2).pos.minus(v0); - } - - // 4. Compute normal - Vector3d normal = bestE1.cross(bestE2); - if (normal.magnitude() < Plane.getEPSILON()) { - throw new RuntimeException("Fail! Normal can not be computed"); - } - normal.normalize(); - Vector3d v0 = verts.get(0).pos; - Vector3d std = verts.get(1).pos.minus(v0).cross(verts.get(2).pos.minus(v0)).normalized(); - if (normal.dot(std) < 0) { - normal = normal.negated(); - } - return normal; - } diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java b/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java index 02891593..d5963b13 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java @@ -88,7 +88,7 @@ void setStorage(PropertyStorage storage) { * @param points the points that define the polygon * @return the decomposed concave polygon (list of convex polygons) */ - public static List fromConcavePoints(Vector3d... points) throws ColinearPointsException { + public static List fromConcavePoints(Vector3d... points) throws ColinearPointsException,NonFlatPolygonException { Polygon p = fromPoints(points); return PolygonUtil.triangulatePolygon(p); @@ -100,7 +100,7 @@ public static List fromConcavePoints(Vector3d... points) throws Colinea * @param points the points that define the polygon * @return the decomposed concave polygon (list of convex polygons) */ - public static List fromConcavePoints(List points)throws ColinearPointsException { + public static List fromConcavePoints(List points)throws ColinearPointsException,NonFlatPolygonException { Polygon p = fromPoints(points); return PolygonUtil.triangulatePolygon(p); @@ -115,7 +115,7 @@ public static List fromConcavePoints(List points)throws Colin * @param vertices polygon vertices * @param shared shared property */ - public Polygon(List vertices, PropertyStorage shared, boolean allowDegenerate, Plane p) throws ColinearPointsException { + public Polygon(List vertices, PropertyStorage shared, boolean allowDegenerate, Plane p) throws ColinearPointsException,NonFlatPolygonException { this.setVertices(pruneDuplicatePoints(vertices)); this.shared = shared; if (p != null) @@ -133,7 +133,7 @@ public Polygon(List vertices, PropertyStorage shared, boolean allowDegen * @param vertices polygon vertices * @param shared shared property */ - public Polygon(List vertices, PropertyStorage shared) throws ColinearPointsException { + public Polygon(List vertices, PropertyStorage shared) throws ColinearPointsException,NonFlatPolygonException { this(vertices, shared, true, null); } @@ -145,7 +145,7 @@ public Polygon(List vertices, PropertyStorage shared) throws ColinearPoi * * @param vertices polygon vertices */ - public Polygon(List vertices) throws ColinearPointsException { + public Polygon(List vertices) throws ColinearPointsException,NonFlatPolygonException { this(vertices, new PropertyStorage(), true, null); } @@ -155,7 +155,7 @@ public static ArrayList pruneDuplicatePoints(List incoming) { for (int i = 0; i < incoming.size(); i++) { Vertex v = incoming.get(i); for(Vertex vt:newPoints) { - if(vt.pos.test(v.pos,Plane.getEPSILON()/2.0)) { + if(vt.pos.test(v.pos,Plane.getEPSILON())) { v=null; break; } @@ -170,27 +170,17 @@ public static ArrayList pruneDuplicatePoints(List incoming) { } } - private void validateAndInit(boolean fixInversions) throws ColinearPointsException { + private void validateAndInit(boolean fixInversions) throws ColinearPointsException,NonFlatPolygonException { ArrayList vertices = pruneDuplicatePoints(this.vertices); Plane p = Plane.createFromPoints(vertices); if (getPlane() == null) { setPlane(p); } - - - //if(fixInversions) { - Vector3d minus = getPlane().getNormal().minus(p.getNormal()); - double magnitude = minus.magnitude(); - if (Math.abs( magnitude)>2-(Plane.getEPSILON()*2) ) { - Collections.reverse(vertices); - } - //} - if (!getPlane().checkNormal(vertices)) { - if(p.getLengthSquared()>Plane.getEPSILON()) - setPlane(p); - else - if (getPlane() == null) - throw new ColinearPointsException("Failed! the normal provided mismatched to calculated normal"); + if (getPlane().checkNormal(vertices)==NormalState.FLIPPED ) { + Collections.reverse(vertices); + } + if (getPlane().checkNormal(vertices)!=NormalState.SAME) { + throw new ColinearPointsException("Failed! the normal provided mismatched to calculated normal"); } this.vertices=vertices; @@ -198,10 +188,27 @@ private void validateAndInit(boolean fixInversions) throws ColinearPointsExcepti if (getVertices().size() < 3) { throw new ColinearPointsException("Invalid polygon: at least 3 vertices expected, got: " + getVertices().size()); } + Vector3d normal = getPlane().getNormal(); + + boolean adusted = false; + for (int i = 0; i < getVertices().size(); i++) { + double dist = getPlane().getDist(); + Vector3d pos = getVertices().get(i).pos; + double dot = normal.dot(pos); + double a = dot - dist; + double t = Math.abs(a); + if(t>0.01) { + throw new RuntimeException("A plane epsilon of "+t+" is impossible"); + } + if (t > Plane.getEPSILON()) { + throw new NonFlatPolygonException("Failed because polygon is not flat"); + } + } + if( !areAllPointsCollinear()) return; - new ColinearPointsException("This polygon is colinear"); + throw new ColinearPointsException("This polygon is colinear"); } @@ -219,7 +226,7 @@ public void rotatePoints() { * @param vertices polygon vertices * */ - public Polygon(Vertex... vertices) throws ColinearPointsException { + public Polygon(Vertex... vertices) throws ColinearPointsException,NonFlatPolygonException { this(Arrays.asList(vertices)); } @@ -257,9 +264,10 @@ public Polygon flip() { // } catch (ColinearPointsException e) { // plane.flip(); // } - if (!getPlane().checkNormal(vertices)) { + NormalState checkNormal = getPlane().checkNormal(vertices); + if (checkNormal!=NormalState.SAME) { // getPlane().checkNormal(vertices); - new RuntimeException("Failed! the normal provided mismatched to calculated normal").printStackTrace(); + throw new RuntimeException("Failed! the normal provided mismatched to calculated normal "+checkNormal); } return this; @@ -420,7 +428,7 @@ public Polygon transformed(Transform transform) throws ColinearPointsException { * @param shared shared property storage * @return a polygon defined by the specified point list */ - public static Polygon fromPoints(List points, PropertyStorage shared) throws ColinearPointsException { + public static Polygon fromPoints(List points, PropertyStorage shared) throws ColinearPointsException,NonFlatPolygonException { return fromPoints(points, shared, null, true); } @@ -430,7 +438,7 @@ public static Polygon fromPoints(List points, PropertyStorage shared) * @param points the points that define the polygon * @return a polygon defined by the specified point list */ - public static Polygon fromPoints(List points)throws ColinearPointsException { + public static Polygon fromPoints(List points)throws ColinearPointsException,NonFlatPolygonException { return fromPoints(points, new PropertyStorage(), null, true); } @@ -440,11 +448,11 @@ public static Polygon fromPoints(List points)throws ColinearPointsExce * @param points the points that define the polygon * @return a polygon defined by the specified point list */ - public static Polygon fromPoints(Vector3d... points)throws ColinearPointsException { + public static Polygon fromPoints(Vector3d... points)throws ColinearPointsException,NonFlatPolygonException { return fromPoints(Arrays.asList(points), new PropertyStorage(), null, true); } - public static Polygon fromPointsAllowDegenerate(List vertices2) throws ColinearPointsException { + public static Polygon fromPointsAllowDegenerate(List vertices2) throws ColinearPointsException,NonFlatPolygonException { return fromPoints(vertices2, new PropertyStorage(), null, true); } @@ -457,7 +465,7 @@ public static Polygon fromPointsAllowDegenerate(List vertices2) throws * @return a polygon defined by the specified point list */ public static Polygon fromPoints(List points, PropertyStorage shared, Plane plane, - boolean allowDegenerate)throws ColinearPointsException { + boolean allowDegenerate)throws ColinearPointsException,NonFlatPolygonException { List vertices = new ArrayList<>(); for (Vector3d p : points) { Vector3d vec = p.clone(); diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Vector3d.java b/src/main/java/eu/mihosoft/vrl/v3d/Vector3d.java index b3649b5f..9322885d 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/Vector3d.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/Vector3d.java @@ -51,9 +51,35 @@ * * @author Michael Hoffer <info@michaelhoffer.de> */ -public class Vector3d extends javax.vecmath.Vector3d { +public class Vector3d +{ + public double x,y,z; + /** + * Returns the length of this vector. + * @return the length of this vector + */ + public final double length() + { + return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z); + } + /** + * Normalizes this vector in place. + */ + public final void normalize() + { + double norm; + + norm = 1.0/Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z); + this.x *= norm; + this.y *= norm; + this.z *= norm; + } + public boolean epsilonEquals(Vector3d in, double ep) { + return test(in, ep); + } + private static String exportString = "%.10f"; private static double EXPORTEPSILON =1.0e-10; @@ -86,9 +112,9 @@ public class Vector3d extends javax.vecmath.Vector3d { * @param z z value */ public Vector3d(double x, double y, double z) { -// if(!Double.isFinite(x)||!Double.isFinite(y)||!Double.isFinite(z)) { -// throw new NumberFormatException("Vectors must be real "+x+" "+y+" "+z); -// } + if(!Double.isFinite(x)||!Double.isFinite(y)||!Double.isFinite(z)) { + throw new NumberFormatException("Vectors must be real "+x+" "+y+" "+z); + } this.x = x; this.y = y; this.z = z; diff --git a/src/main/java/eu/mihosoft/vrl/v3d/ext/imagej/STLLoader.java b/src/main/java/eu/mihosoft/vrl/v3d/ext/imagej/STLLoader.java index a8f6a142..fb03a7b4 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/ext/imagej/STLLoader.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/ext/imagej/STLLoader.java @@ -178,6 +178,7 @@ private void parseBinary(File f, ArrayList polygons,int triangles) { try { pl = new Plane(normal, vertices); }catch(NumberFormatException ex) { + System.out.println(" STL has bad Normal "+normal); pl=Plane.createFromPoints(vertices); } polygons.add(new Polygon(vertices, null, true, pl)); diff --git a/src/main/java/eu/mihosoft/vrl/v3d/ext/org/poly2tri/PolygonUtil.java b/src/main/java/eu/mihosoft/vrl/v3d/ext/org/poly2tri/PolygonUtil.java index f0906ad7..8309250a 100644 --- a/src/main/java/eu/mihosoft/vrl/v3d/ext/org/poly2tri/PolygonUtil.java +++ b/src/main/java/eu/mihosoft/vrl/v3d/ext/org/poly2tri/PolygonUtil.java @@ -39,8 +39,10 @@ import eu.mihosoft.vrl.v3d.Edge; import eu.mihosoft.vrl.v3d.Extrude; import eu.mihosoft.vrl.v3d.IPolygonRepairTool; +import eu.mihosoft.vrl.v3d.NonFlatPolygonException; import eu.mihosoft.vrl.v3d.Plane; import eu.mihosoft.vrl.v3d.Polygon; +import eu.mihosoft.vrl.v3d.PropertyStorage; import eu.mihosoft.vrl.v3d.Transform; import eu.mihosoft.vrl.v3d.Vector3d; import eu.mihosoft.vrl.v3d.Vertex; @@ -62,7 +64,6 @@ import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator; - //import earcut4j.Earcut; /** @@ -72,12 +73,13 @@ */ public class PolygonUtil { public static final double triangleScale = 1; - private static IPolygonRepairTool repair = concave1 -> { + private static IPolygonRepairTool repair = (List vertices, PropertyStorage shared, boolean allowDegenerate, + Plane p, Color color) -> { ArrayList edges = new ArrayList(); ArrayList toRemove = new ArrayList(); HashMap replace = new HashMap<>(); - List v1 = concave1.getVertices(); + List v1 = vertices; ArrayList modifiable = new ArrayList(); for (int i = 0; i < v1.size(); i++) { @@ -182,11 +184,13 @@ public class PolygonUtil { modifiable.remove(vr); if (modifiable.size() > 2) { try { - Polygon polygon = new Polygon(modifiable, concave1.getStorage(), false, concave1.getPlane()); - return polygon; + Polygon polygon = new Polygon(modifiable, shared, allowDegenerate, p); + polygon.setColor(color); + return Arrays.asList(polygon); } catch (ColinearPointsException e) { - System.out.println(" Pruning polygon in repair " + concave1 + " to " + modifiable); - + System.out.println(" Pruning polygon in repair " + vertices + " to " + modifiable); + } catch (NonFlatPolygonException e) { + return triangulatePolygon(modifiable, shared, allowDegenerate, p, color); } } throw new ColinearPointsException("Fix failed!"); @@ -504,6 +508,12 @@ private PolygonUtil() { // return result; // } + /** + * Calculates a quaternion-based transform that rotates `from` vector to align + * with (0,0,1). + * + * @throws ColinearPointsException + */ /** * Calculates a quaternion-based transform that rotates `from` vector to align * with (0,0,1). @@ -511,8 +521,22 @@ private PolygonUtil() { * @throws ColinearPointsException */ public static Transform calculateNormalTransform(Polygon concave) throws ColinearPointsException { + Transform transform = calculateNormalTransform(concave.getPlane()); + Polygon test = concave.transformed(transform); + Vector3d normal = test.plane.getNormal(); + double abs = Math.abs(normal.z); + if (1 - abs > 0.1) { + System.out.println("Error with " + test + " normal " + normal); + // Plane p = Plane.createFromPoints(test.getVertices()); + new ColinearPointsException("Failed to reorent the polygon for processing! z off by " + abs + " " + normal) + .printStackTrace(); + } + return transform; + } + + public static Transform calculateNormalTransform(Plane p) throws ColinearPointsException { // Normalize inputs - Vector3d u = concave.getPlane().getNormal(); + Vector3d u = p.getNormal(); Vector3d pureXVect = new Vector3d(1, 0, 0); Vector3d pureYVect = new Vector3d(0, 1, 0); Vector3d pureZVect = new Vector3d(0, 0, 1); @@ -539,40 +563,28 @@ public static Transform calculateNormalTransform(Polygon concave) throws Colinea // return new Transform().rotX(-90); // } double aboutZ = Math.toDegrees(Math.atan2(u.y, u.x)); - if(Double.isNaN(aboutZ)) + if (Double.isNaN(aboutZ)) throw new ColinearPointsException("Failed to creat a rotation angle"); Transform transform1 = new Transform().rotZ(aboutZ); Vector3d u2 = u.transformed(transform1); Transform transform; double aboutY = Math.toDegrees(Math.atan2(u2.x, u2.z)); - if(Double.isNaN(aboutY)) + if (Double.isNaN(aboutY)) throw new ColinearPointsException("Failed to creat a rotation angle"); - + Transform rotY = new Transform().rotY(aboutY); transform = rotY.copy().apply(transform1); - - - Vector3d u3 = u.transformed(transform).normalized(); - Polygon test = concave.transformed(transform); - Vector3d normal = test.plane.getNormal(); - double abs = Math.abs(normal.z); - if (1 - abs > 0.1) { - System.out.println("Error with " + test+" normal "+normal); - // Plane p = Plane.createFromPoints(test.getVertices()); - new ColinearPointsException("Failed to reorent the polygon for processing! z off by "+abs+" "+normal).printStackTrace(); - } - Matrix4d rotation = transform.getInternalMatrix(); Quat4d q1 = transform.getQuat(); javax.vecmath.Vector3d t1 = new javax.vecmath.Vector3d(); rotation.get(t1); List asList = Arrays.asList(t1.x, t1.y, t1.z, q1.w, q1.x, q1.y, q1.z); - for(Double d:asList){ - if(Double.isInfinite(d)||Double.isNaN(d)) + for (Double d : asList) { + if (Double.isInfinite(d) || Double.isNaN(d)) throw new ColinearPointsException("Failed to produce a matrix "); } - + return transform; } @@ -583,142 +595,90 @@ public static Transform calculateNormalTransform(Polygon concave) throws Colinea * @return the list * @throws ColinearPointsException */ - public static ArrayList triangulatePolygon(Polygon incoming) throws ColinearPointsException { - ArrayList result = new ArrayList<>(); - if (incoming == null) + public static ArrayList triangulatePolygon(Polygon p) throws ColinearPointsException { + ArrayList result = new ArrayList<>(); + if (p == null) return result; - if (incoming.getVertices().size() < 3) + if (p.getVertices().size() < 3) return result; - Polygon tmp = incoming; - Vector3d normalOfPlane = incoming.getPlane().getNormal().clone(); + return triangulatePolygon(p.getVertices(), p.getStorage(), false, p.getPlane(), p.getColor()); + } + + public static List transformed(List vertices, Transform transform) { + List tmp = new ArrayList(); + tmp.addAll(vertices); + tmp.stream().forEach((v) -> { + v.clone().transform(transform); + }); + return tmp; + } + + public static ArrayList triangulatePolygon(List vertices, PropertyStorage shared, + boolean allowDegenerate, Plane p, Color c) throws ColinearPointsException { + ArrayList result = new ArrayList<>(); + Vector3d normalOfPlane = p.getNormal().clone(); normalOfPlane.normalize(); boolean reorient = Math.abs(normalOfPlane.z - 1.0) > Plane.getEPSILON(); Transform orientationInv = null; boolean debug = false; - + List tmp = vertices; if (reorient) { - Transform orientation = calculateNormalTransform(incoming); - tmp = incoming.transformed(orientation); + Polygon incoming; + Transform orientation = calculateNormalTransform(p); + tmp = transformed(vertices, orientation); orientationInv = orientation.inverse(); } boolean cw = false; -// if(!Extrude.isCCW(tmp)) { -// ArrayList v =new ArrayList(tmp.getVertices()); -// Collections.reverse(v); -// tmp = new Polygon(v, tmp.getStorage(), false, null); -// } - Polygon concave = tmp; - double zplane = concave.getVertices().get(0).pos.z; -// for (Vector3d v : concave.getPoints()) { -// double abs = Math.abs(zplane - v.z); -// if (abs > 0.1) { -// new RuntimeException("Failed to triangulate, points must be coplainer, delta: " + abs) -// .printStackTrace(); -// } -// } - try { - if (concave.size() == 3) { - result.add(concave); - } else - makeTriangles(concave, cw, result, zplane, normalOfPlane, debug, orientationInv, reorient, - incoming.getColor()); - } catch (java.lang.IllegalStateException ex) { - - Polygon repaired = repairOverlappingEdges(concave); - int end = repaired.getVertices().size(); - if (end == 3) { - result.add(repaired); - } else { - try { - makeTriangles(repaired, cw, result, zplane, normalOfPlane, debug, orientationInv, reorient, - incoming.getColor()); - } catch (Exception e) { - makeTrianglesInternal(repaired, cw, result, zplane, normalOfPlane, debug, orientationInv, reorient, - incoming.getColor()); - } - } - - if (reorient) { - repaired = repaired.transform(orientationInv); + double zplane = vertices.get(0).pos.z; + + if (vertices.size() == 3) { + try { + result.add(new Polygon(vertices, shared, allowDegenerate, p)); + } catch (ColinearPointsException e) { + e.printStackTrace(); + } catch (NonFlatPolygonException e) { + e.printStackTrace(); } - incoming.setVertices(repaired.getVertices()); + } else + try { + makeTriangles(vertices, shared, allowDegenerate, p, cw, result, zplane, normalOfPlane, debug, + orientationInv, reorient, c); + } catch (java.lang.IllegalStateException ex) { + + List repairedList = repairOverlappingEdges(vertices, shared, allowDegenerate, p, c); + for (Polygon repaired : repairedList) { + int end = repaired.getVertices().size(); + if (end == 3) { + result.add(repaired); + } else { + try { + makeTriangles(repaired.getVertices(),repaired.getStorage(),false,repaired.getPlane(), cw, result, zplane, normalOfPlane, debug, orientationInv, reorient, + c); + } catch (Exception e) { + makeTrianglesInternal(repaired, cw, result, zplane, normalOfPlane, debug, orientationInv, + reorient, c); + } + } - } + if (reorient) { + repaired = repaired.transform(orientationInv); + } + vertices.clear(); + vertices.addAll(repaired.getVertices()); + } + } return result; } - private static Polygon repairOverlappingEdges(Polygon concave) throws ColinearPointsException { + private static List repairOverlappingEdges(List vertices, PropertyStorage shared, + boolean allowDegenerate, Plane p, Color c) throws ColinearPointsException { - return getRepair().repairOverlappingEdges(concave); + return getRepair().repairOverlappingEdges(vertices, shared, false, p, c); } -// private static void fourPointSpecialCase(Polygon concave, boolean cw, List result, double zplane, -// Vector3d normal, boolean debug, Transform orentationInv, boolean reorent, Color color) { -// List points = concave.getPoints(); -// int size = points.size(); -// for (int i = 0; i < size; i++) { -// // Get first two points to establish a direction vector -// Vector3d p1 = points.get(i); -// Vector3d p2 = points.get((i + 1) % size); -// -// // Calculate the direction vector between first two points -// Vector3d direction = p1.minus(p2); -// // Normalize the direction vector -// double length = direction.length(); -// double ep = Plane.getEPSILON(); -// if (length < ep) { // If points are effectively identical -// continue; -// } -// direction.normalize(); -// Vector3d p3 = points.get((i + 2) % size); -// -// // Calculate cross product -// Vector3d cross = direction.cross(p1.minus(p3)); -// // Calculate magnitude of cross product -// double magnitude = Math.abs(cross.length()); -// -// // If magnitude is not close to zero, points are not collinear -// if (magnitude > ep) { -// -// Plane normal2 = concave.plane; -// try { -// Polygon one = new Polygon( -// new ArrayList(Arrays.asList(new Vertex(p1), new Vertex(p2), new Vertex(p3))), -// concave.getStorage(), true, normal2); -// if (reorent) { -// one = one.transform(orentationInv); -// } -// one.setColor(color); -// result.add(one); -// } catch (ColinearPointsException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// try { -// Polygon two = new Polygon( -// new ArrayList(Arrays.asList(new Vertex(points.get((i + 3) % size)), -// new Vertex(points.get((i + 4) % size)), new Vertex(points.get((i + 5) % size)))), -// concave.getStorage(), true, normal2); -// if (reorent) { -// two = two.transform(orentationInv); -// } -// two.setColor(color); -// result.add(two); -// } catch (ColinearPointsException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// -// -// return; -// } -// } -// -// } - private static void makeTrianglesInternal(Polygon concave, boolean cw, List result, double zplane, Vector3d normal, boolean debug, Transform orentationInv, boolean reorent, Color color) throws ColinearPointsException { @@ -753,7 +713,7 @@ private static void makeTrianglesInternal(Polygon concave, boolean cw, List 0.1) { - new RuntimeException("Failed to triangulate, points must be coplainer").printStackTrace(); + new RuntimeException("Failed to triangulate, points must be coplainer").printStackTrace(); } // Calculate cross product @@ -779,6 +739,9 @@ private static void makeTrianglesInternal(Polygon concave, boolean cw, List result, double zplane, Vector3d normal, - boolean debug, Transform orentationInv, boolean reorent, Color color) { + private static void makeTriangles(List vertices, PropertyStorage shared, boolean allowDegenerate, Plane p, + boolean cw, List result, double zplane, Vector3d normal, boolean debug, Transform orentationInv, + boolean reorent, Color color) { - Polygon toTri = concave; + // Polygon toTri = concave; - Coordinate[] coordinates = new Coordinate[toTri.getVertices().size() + 1]; - for (int i = 0; i < toTri.getVertices().size(); i++) { - Vector3d v = toTri.getVertices().get(i).pos; + Coordinate[] coordinates = new Coordinate[vertices.size() + 1]; + for (int i = 0; i < vertices.size(); i++) { + Vector3d v = vertices.get(i).pos; coordinates[i] = new Coordinate(v.x * triangleScale, v.y * triangleScale, v.z * triangleScale); } - Vector3d v = toTri.getVertices().get(0).pos; - coordinates[toTri.getVertices().size()] = new Coordinate(v.x * triangleScale, v.y * triangleScale, - v.z * triangleScale); + Vector3d v = vertices.get(0).pos; + coordinates[vertices.size()] = new Coordinate(v.x * triangleScale, v.y * triangleScale, v.z * triangleScale); // use the default factory, which gives full double-precision Geometry geom = new GeometryFactory().createPolygon(coordinates); Geometry triangles = ConstrainedDelaunayTriangulator.triangulate(geom); ArrayList triPoints = new ArrayList<>(); - Plane p1 = concave.getPlane().clone(); + Plane p1 = p; for (int i = 0; i < triangles.getNumGeometries(); i++) { Geometry tri = triangles.getGeometryN(i); Coordinate[] coords = tri.getCoordinates(); @@ -827,19 +791,19 @@ private static void makeTriangles(Polygon concave, boolean cw, List res Coordinate tp = coords[j]; Vector3d pos = new Vector3d(tp.getX() / triangleScale, tp.getY() / triangleScale, zplane); Vertex e = null;// new Vertex(pos); - for (int x = 0; x < toTri.getVertices().size(); x++) { - Vector3d test = toTri.getVertices().get(x).pos; - double diffX = Math.abs( test.x-pos.x); - double diffY = Math.abs(test.y-pos.y); - if (diffY res Collections.reverse(triPoints); } Polygon poly; - poly = new Polygon(triPoints, concave.getStorage(), true, p1); + poly = new Polygon(triPoints, shared, true, p1); // poly = Extrude.toCCW(poly); // poly.getPlane().setNormal(concave.getPlane().getNormal()); @@ -869,6 +833,9 @@ private static void makeTriangles(Polygon concave, boolean cw, List res result.add(poly); } catch (ColinearPointsException ex) { System.out.println(ex.getMessage() + " Pruned new triangle as colinear " + triPoints); + } catch (NonFlatPolygonException e1) { + System.err.println("Impossible! it should not be possible to have anon-flat triangle"); + e1.printStackTrace(); } counter = 0; diff --git a/src/test/java/eu/mihosoft/vrl/v3d/HoleDetectionTest.java b/src/test/java/eu/mihosoft/vrl/v3d/HoleDetectionTest.java index 1dce702a..b630ecb5 100644 --- a/src/test/java/eu/mihosoft/vrl/v3d/HoleDetectionTest.java +++ b/src/test/java/eu/mihosoft/vrl/v3d/HoleDetectionTest.java @@ -22,9 +22,10 @@ public class HoleDetectionTest { /** * Hole detection test. * @throws ColinearPointsException + * @throws NonFlatPolygonException */ @Test - public void holeDetectionTest() throws ColinearPointsException { + public void holeDetectionTest() throws ColinearPointsException, NonFlatPolygonException { // one polygon with one hole Polygon p1 = Polygon.fromPoints( diff --git a/src/test/java/eu/mihosoft/vrl/v3d/PolygonClassificationTest.java b/src/test/java/eu/mihosoft/vrl/v3d/PolygonClassificationTest.java index dd5feab9..fadc5e92 100644 --- a/src/test/java/eu/mihosoft/vrl/v3d/PolygonClassificationTest.java +++ b/src/test/java/eu/mihosoft/vrl/v3d/PolygonClassificationTest.java @@ -140,9 +140,10 @@ public void testPolygon6_ValidWithLargeYZDifferences() { * Test to verify that truly degenerate polygons ARE correctly rejected * * @throws ColinearPointsException + * @throws NonFlatPolygonException */ @Test(expected = ColinearPointsException.class) - public void testDegeneratePolygon_DuplicatePoints() throws ColinearPointsException { + public void testDegeneratePolygon_DuplicatePoints() throws ColinearPointsException, NonFlatPolygonException { // This SHOULD fail - duplicate points List vertices = Arrays.asList( new Vertex(new Vector3d(10.5175167546, 116.6176943528, 83.1832321598)), @@ -158,9 +159,10 @@ public void testDegeneratePolygon_DuplicatePoints() throws ColinearPointsExcepti /** * Test to verify that collinear points ARE correctly rejected * @throws ColinearPointsException + * @throws NonFlatPolygonException */ @Test(expected = ColinearPointsException.class) - public void testDegeneratePolygon_Collinear() throws ColinearPointsException { + public void testDegeneratePolygon_Collinear() throws ColinearPointsException, NonFlatPolygonException { // This SHOULD fail - all points on same line (same Y coordinate, collinear in XZ) List vertices = Arrays.asList( new Vertex(new Vector3d(10.5068817145, 98.4507751465, 35.0306403108)), diff --git a/src/test/java/eu/mihosoft/vrl/v3d/StlLoadTest.java b/src/test/java/eu/mihosoft/vrl/v3d/StlLoadTest.java index 35cec2ad..bf84a31f 100644 --- a/src/test/java/eu/mihosoft/vrl/v3d/StlLoadTest.java +++ b/src/test/java/eu/mihosoft/vrl/v3d/StlLoadTest.java @@ -20,7 +20,6 @@ public void init() { JavaFXInitializer.go(); } @Test - @Ignore public void tower() throws IOException { String filename = "fixedTower.STL"; File file = new File(filename); diff --git a/src/test/java/eu/mihosoft/vrl/v3d/SvgExportTest.java b/src/test/java/eu/mihosoft/vrl/v3d/SvgExportTest.java index 9009f914..17ee8170 100644 --- a/src/test/java/eu/mihosoft/vrl/v3d/SvgExportTest.java +++ b/src/test/java/eu/mihosoft/vrl/v3d/SvgExportTest.java @@ -45,7 +45,7 @@ public void slicetest() throws IOException, ColinearPointsException { } @Test - public void test() throws IOException, ColinearPointsException { + public void test() throws IOException, ColinearPointsException, NonFlatPolygonException { List polygons = new ArrayList(); @@ -71,7 +71,7 @@ public void test() throws IOException, ColinearPointsException { } @Test - public void testSlices() throws IOException, ColinearPointsException { + public void testSlices() throws IOException, ColinearPointsException, NonFlatPolygonException { List polygons = new ArrayList();