diff --git a/.gitignore b/.gitignore
index e4913633..b6fdc661 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,7 @@
/testSpline.svg
/CSGdatabase.json
/*.stl
-/*.svg
+/Test.svg
out
/*.mtl
@@ -26,3 +26,4 @@ html
/handmade.step
/test-export-2.step
/test-export.step
+/Part-Num-0.svg.png
diff --git a/Part-Num-0.svg b/Part-Num-0.svg
new file mode 100644
index 00000000..fc988110
--- /dev/null
+++ b/Part-Num-0.svg
@@ -0,0 +1,136 @@
+
+
diff --git a/src/main/java/com/piro/bezier/BezierPath.java b/src/main/java/com/piro/bezier/BezierPath.java
index 82fa752f..c65be77d 100644
--- a/src/main/java/com/piro/bezier/BezierPath.java
+++ b/src/main/java/com/piro/bezier/BezierPath.java
@@ -6,6 +6,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import eu.mihosoft.vrl.v3d.Plane;
import eu.mihosoft.vrl.v3d.Vector3d;
public class BezierPath {
@@ -14,7 +15,7 @@ public class BezierPath {
BezierListProducer path;
- private ArrayList pointList = new ArrayList();
+ private ArrayList plInternal = new ArrayList();
double resolution = 0.075;
/** Creates a new instance of Animate */
@@ -60,45 +61,45 @@ protected void parsePathList(String list) {
x = nextFloat(tokens);
y = nextFloat(tokens);
path.movetoAbs(x, y);
- pointList.add(new Vector3d(x, y, 0));
+ setThePoint(new Vector3d(x, y, 0));
curCmd = 'L';
break;
case 'm':
x = nextFloat(tokens);
y = nextFloat(tokens);
path.movetoRel(x, y);
- pointList.add(new Vector3d(x, y, 0));
+ setThePoint(new Vector3d(x, y, 0));
curCmd = 'l';
break;
case 'L':
path.linetoAbs(nextFloat(tokens), nextFloat(tokens));
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
+ setThePoint(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
break;
case 'l':
path.linetoRel(nextFloat(tokens), nextFloat(tokens));
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
+ setThePoint(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
break;
case 'H':
path.linetoHorizontalAbs(nextFloat(tokens));
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
+ setThePoint(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
break;
case 'h':
path.linetoHorizontalRel(nextFloat(tokens));
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
+ setThePoint(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
break;
case 'V':
path.linetoVerticalAbs(nextFloat(tokens));
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
+ setThePoint(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
break;
case 'v':
path.linetoVerticalAbs(nextFloat(tokens));
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
+ setThePoint(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(1));
break;
case 'A':
case 'a':
@@ -106,51 +107,51 @@ protected void parsePathList(String list) {
case 'Q':
path.curvetoQuadraticAbs(nextFloat(tokens), nextFloat(tokens), nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 'q':
path.curvetoQuadraticAbs(nextFloat(tokens), nextFloat(tokens), nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 'T':
path.curvetoQuadraticSmoothAbs(nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 't':
path.curvetoQuadraticSmoothRel(nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 'C':
path.curvetoCubicAbs(nextFloat(tokens), nextFloat(tokens), nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 'c':
path.curvetoCubicRel(nextFloat(tokens), nextFloat(tokens), nextFloat(tokens), nextFloat(tokens),
nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 'S':
path.curvetoCubicSmoothAbs(nextFloat(tokens), nextFloat(tokens), nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 's':
path.curvetoCubicSmoothRel(nextFloat(tokens), nextFloat(tokens), nextFloat(tokens), nextFloat(tokens));
for (double i = resolution; i < 1; i += resolution) {
- pointList.add(path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i));
+ addingPoint(i);
}
break;
case 'Z':
@@ -167,6 +168,19 @@ protected void parsePathList(String list) {
}
}
+ private boolean addingPoint(double i) {
+ Vector3d eval = path.bezierSegs.get(path.bezierSegs.size() - 1).eval(i);
+ return setThePoint(eval);
+ }
+
+ private boolean setThePoint(Vector3d eval) {
+ for(Vector3d v:plInternal) {
+ if(Math.abs(v.minus(eval).magnitude()) l) {
String s = l.removeFirst();
return Float.parseFloat(s);
@@ -206,7 +220,7 @@ public Vector3d eval(float interp) {
*/
public ArrayList evaluate() {
- return pointList;
+ return plInternal;
}
}
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Edge.java b/src/main/java/eu/mihosoft/vrl/v3d/Edge.java
index 5a7d068c..3d17c6c9 100644
--- a/src/main/java/eu/mihosoft/vrl/v3d/Edge.java
+++ b/src/main/java/eu/mihosoft/vrl/v3d/Edge.java
@@ -125,7 +125,7 @@ public static Polygon toPolygon(List points, Plane plane) {
Polygon p = Polygon.fromPoints(points);
p.vertices.stream().forEachOrdered((vertex) -> {
- vertex.normal = plane.normal.clone();
+ vertex.normal = plane.getNormal().clone();
});
// // we try to detect wrong orientation by comparing normals
@@ -897,8 +897,8 @@ private static List> searchPlaneGroups(List polygons) {
continue;
}
- Vector3d nOuter = pOuter.plane.normal;
- Vector3d nInner = pInner.plane.normal;
+ Vector3d nOuter = pOuter.plane.getNormal();
+ Vector3d nInner = pInner.plane.getNormal();
double angle = nOuter.angle(nInner);
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Extrude.java b/src/main/java/eu/mihosoft/vrl/v3d/Extrude.java
index afd7736d..50909875 100644
--- a/src/main/java/eu/mihosoft/vrl/v3d/Extrude.java
+++ b/src/main/java/eu/mihosoft/vrl/v3d/Extrude.java
@@ -90,6 +90,7 @@ private CSG monotoneExtrude(Vector3d dir, Polygon polygon1) {
Polygon polygon2 = polygon1.translated(dir);
int numvertices = polygon1.vertices.size();
+ System.out.println("Building Polygon "+polygon1.getPoints().size());
for (int i = 0; i < numvertices; i++) {
int nexti = (i + 1) % numvertices;
@@ -98,11 +99,18 @@ private CSG monotoneExtrude(Vector3d dir, Polygon polygon1) {
Vector3d topV1 = polygon2.vertices.get(i).pos;
Vector3d bottomV2 = polygon1.vertices.get(nexti).pos;
Vector3d topV2 = polygon2.vertices.get(nexti).pos;
-
+ double distance = bottomV1.minus(bottomV2).magnitude();
+ if(Math.abs(distance) pPoints = Arrays.asList(bottomV2, topV2, topV1, bottomV1);
-
- newPolygons.add(Polygon.fromPoints(pPoints, polygon1.getStorage()));
-
+ try {
+ newPolygons.add(Polygon.fromPoints(pPoints, polygon1.getStorage()));
+ }catch(Exception ex) {
+ System.out.println("Polygon has problems: ");
+ ex.printStackTrace();
+ }
}
polygon2 = polygon2.flipped();
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Plane.java b/src/main/java/eu/mihosoft/vrl/v3d/Plane.java
index 89b71680..d6934d7d 100644
--- a/src/main/java/eu/mihosoft/vrl/v3d/Plane.java
+++ b/src/main/java/eu/mihosoft/vrl/v3d/Plane.java
@@ -45,190 +45,245 @@
public class Plane {
private static IPolygonDebugger debugger = null;
private static boolean useDebugger = false;
- /**
- * EPSILON is the tolerance used by {@link #splitPolygon(eu.mihosoft.vrl.v3d.Polygon, java.util.List, java.util.List, java.util.List, java.util.List)
- * } to decide if a point is on the plane.
- * public static final double EPSILON = 0.00000001;
- */
-
- public static final double EPSILON = 1.0e-9;
- public static final double EPSILON_Point = EPSILON;
- public static final double EPSILON_duplicate = 1.0e-4;
- /**
- * XY plane.
- */
- public static final Plane XY_PLANE = new Plane(Vector3d.Z_ONE, 1);
- /**
- * XZ plane.
- */
- public static final Plane XZ_PLANE = new Plane(Vector3d.Y_ONE, 1);
- /**
- * YZ plane.
- */
- public static final Plane YZ_PLANE = new Plane(Vector3d.X_ONE, 1);
-
- /**
- * Normal vector.
- */
- public Vector3d normal;
- /**
- * Distance to origin.
- */
- public double dist;
-
- /**
- * Constructor. Creates a new plane defined by its normal vector and the
- * distance to the origin.
- *
- * @param normal plane normal
- * @param dist distance from origin
- */
- public Plane(Vector3d normal, double dist) {
- this.normal = normal.normalized();
- this.dist = dist;
- }
-
- /**
- * Creates a plane defined by the the specified points.
- *
- * @param a first point
- * @param b second point
- * @param c third point
- * @return a plane
- */
- public static Plane createFromPoints(Vector3d a, Vector3d b, Vector3d c) {
- Vector3d n = b.minus(a).cross(c.minus(a)).normalized();
- return new Plane(n, n.dot(a));
- }
-
- /* (non-Javadoc)
- * @see java.lang.Object#clone()
- */
- @Override
- public Plane clone() {
- return new Plane(normal.clone(), dist);
- }
-
- /**
- * Flips this plane.
- */
- public void flip() {
- normal = normal.negated();
- dist = -dist;
- }
-
- /**
- * Splits a {@link Polygon} by this plane if needed. After that it puts the
- * polygons or the polygon fragments in the appropriate lists
- * ({@code front}, {@code back}). Coplanar polygons go into either
- * {@code coplanarFront}, {@code coplanarBack} depending on their
- * orientation with respect to this plane. Polygons in front or back of this
- * plane go into either {@code front} or {@code back}.
- *
- * @param polygon polygon to split
- * @param coplanarFront "coplanar front" polygons
- * @param coplanarBack "coplanar back" polygons
- * @param front front polygons
- * @param back back polgons
- */
- public void splitPolygon(
- Polygon polygon,
- List coplanarFront,
- List coplanarBack,
- List front,
- List back) {
- final int COPLANAR = 0;
- final int FRONT = 1;
- final int BACK = 2;
- final int SPANNING = 3; // == some in the FRONT + some in the BACK
- if(debugger!=null && useDebugger) {
+ /**
+ * EPSILON is the tolerance used by
+ * {@link #splitPolygon(eu.mihosoft.vrl.v3d.Polygon, java.util.List, java.util.List, java.util.List, java.util.List) }
+ * to decide if a point is on the plane. public static final double EPSILON =
+ * 0.00000001;
+ */
+
+ public static final double EPSILON = 1.0e-9;
+ public static final double EPSILON_Point = EPSILON;
+ public static final double EPSILON_duplicate = 1.0e-4;
+ /**
+ * XY plane.
+ */
+ public static final Plane XY_PLANE = new Plane(Vector3d.Z_ONE, 1);
+ /**
+ * XZ plane.
+ */
+ public static final Plane XZ_PLANE = new Plane(Vector3d.Y_ONE, 1);
+ /**
+ * YZ plane.
+ */
+ public static final Plane YZ_PLANE = new Plane(Vector3d.X_ONE, 1);
+
+ /**
+ * Normal vector.
+ */
+ private Vector3d normal;
+ /**
+ * Distance to origin.
+ */
+ private double dist;
+
+ /**
+ * Constructor. Creates a new plane defined by its normal vector and the
+ * distance to the origin.
+ *
+ * @param normal plane normal
+ * @param dist distance from origin
+ */
+ public Plane(Vector3d normal, double dist) {
+ this.setNormal(normal.normalized());
+ this.setDist(dist);
+ }
+
+ /**
+ * Creates a plane defined by the the specified points.
+ *
+ * @param a first point
+ * @param b second point
+ * @param c third point
+ * @return a plane
+ */
+ public static Plane createFromPoints(List vertices) {
+ Vector3d a = vertices.get(0).pos;
+ Vector3d n = computeNormal(vertices);
+ return new Plane(n, n.dot(a));
+ }
+
+ public static Vector3d computeNormal(List vertices) {
+ Vector3d normal = new Vector3d(0, 0, 0);
+ int n = vertices.size();
+
+ for (int i = 0; i < n; i++) {
+ Vector3d current = vertices.get(i).pos;
+ Vector3d next = vertices.get((i + 1) % n).pos;
+
+ normal.x += (current.y - next.y) * (current.z + next.z);
+ normal.y += (current.z - next.z) * (current.x + next.x);
+ normal.z += (current.x - next.x) * (current.y + next.y);
+ }
+
+ Vector3d normalized = normal.normalized();
+ // If Newell's method fails, try finding three non-collinear points
+ double lengthSquared = normal.lengthSquared();
+ double d = EPSILON * EPSILON;
+ if (lengthSquared < d) { // Adjust this epsilon as needed
+ for (int i = 0; i < n - 2; i++) {
+ Vector3d a = vertices.get(i).pos;
+ for (int j = i + 1; j < n - 1; j++) {
+ Vector3d b = vertices.get(j).pos;
+ for (int k = j + 1; k < n; k++) {
+ Vector3d c = vertices.get(k).pos;
+ normal = b.minus(a).cross(c.minus(a));
+ lengthSquared = normal.lengthSquared();
+ if (lengthSquared > d) { // Non-zero normal found
+ return normal.normalized();
+ }
+ }
+ }
+ }
+ }
+
+ // If all else fails, return a default normal (e.g., in the z direction)
+ lengthSquared = normal.lengthSquared();
+
+ if (lengthSquared < Double.MIN_VALUE*10) {
+ throw new NumberFormatException("This set of points is not a valid polygon");
+ }
+ if(normalized.lengthSquared() coplanarFront, List coplanarBack,
+ List front, List back) {
+ final int COPLANAR = 0;
+ final int FRONT = 1;
+ final int BACK = 2;
+ final int SPANNING = 3; // == some in the FRONT + some in the BACK
+ if (debugger != null && useDebugger) {
// debugger.display(polygon);
// debugger.display(coplanarFront);
// debugger.display(coplanarBack);
// debugger.display(front);
// debugger.display(back);
- }
- // search for the epsilon values of the incoming plane
- double negEpsilon = -Plane.EPSILON;
- double posEpsilon = Plane.EPSILON;
- for (int i = 0; i < polygon.vertices.size(); i++) {
- double t = polygon.plane.normal.dot(polygon.vertices.get(i).pos) - polygon.plane.dist;
- if(t>posEpsilon) {
- //System.err.println("Non flat polygon, increasing positive epsilon "+t);
- posEpsilon=t+Plane.EPSILON;
- }
- if(t types = new ArrayList<>();
- boolean somePointsInfront = false;
- boolean somePointsInBack = false;
- for (int i = 0; i < polygon.vertices.size(); i++) {
- double t = this.normal.dot(polygon.vertices.get(i).pos) - this.dist;
- int type = (t < negEpsilon) ? BACK : (t > posEpsilon) ? FRONT : COPLANAR;
- if(type==BACK)
- somePointsInBack=true;
- if(type==FRONT)
- somePointsInfront = true;
- types.add(type);
- }
- if(somePointsInBack && somePointsInfront)
- polygonType=SPANNING;
- else if(somePointsInBack) {
- polygonType=BACK;
- }else if(somePointsInfront)
- polygonType=FRONT;
-
- // Put the polygon in the correct list, splitting it when necessary.
- switch (polygonType) {
- case COPLANAR:
- (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).add(polygon);
- break;
- case FRONT:
- front.add(polygon);
- break;
- case BACK:
- back.add(polygon);
- break;
- case SPANNING:
- List f = new ArrayList<>();
- List b = new ArrayList<>();
- for (int i = 0; i < polygon.vertices.size(); i++) {
- int j = (i + 1) % polygon.vertices.size();
- int ti = types.get(i);
- int tj = types.get(j);
- Vertex vi = polygon.vertices.get(i);
- Vertex vj = polygon.vertices.get(j);
- if (ti != BACK) {
- f.add(vi);
- }
- if (ti != FRONT) {
- b.add(ti != BACK ? vi.clone() : vi);
- }
- if ((ti | tj) == SPANNING) {
- double t = (this.dist - this.normal.dot(vi.pos))
- / this.normal.dot(vj.pos.minus(vi.pos));
- Vertex v = vi.interpolate(vj, t);
- f.add(v);
- b.add(v.clone());
- }
- }
- if (f.size() >= 3) {
- front.add(new Polygon(f, polygon.getStorage()).setColor(polygon.getColor()));
- } else {
- System.out.println("Front Clip Fault!");
- }
- if (b.size() >= 3) {
- back.add(new Polygon(b, polygon.getStorage()).setColor(polygon.getColor()));
- } else {
- System.out.println("Back Clip Fault!");
- }
- break;
- }
- }
+ }
+ // search for the epsilon values of the incoming plane
+ double negEpsilon = -Plane.EPSILON;
+ double posEpsilon = Plane.EPSILON;
+ for (int i = 0; i < polygon.vertices.size(); i++) {
+ double t = polygon.plane.getNormal().dot(polygon.vertices.get(i).pos) - polygon.plane.getDist();
+ if (t > posEpsilon) {
+ // System.err.println("Non flat polygon, increasing positive epsilon "+t);
+ posEpsilon = t + Plane.EPSILON;
+ }
+ if (t < negEpsilon) {
+ // System.err.println("Non flat polygon, decreasing negative epsilon "+t);
+ negEpsilon = t - Plane.EPSILON;
+ }
+ }
+ int polygonType = 0;
+ List types = new ArrayList<>();
+ boolean somePointsInfront = false;
+ boolean somePointsInBack = false;
+ for (int i = 0; i < polygon.vertices.size(); i++) {
+ double t = this.getNormal().dot(polygon.vertices.get(i).pos) - this.getDist();
+ int type = (t < negEpsilon) ? BACK : (t > posEpsilon) ? FRONT : COPLANAR;
+ if (type == BACK)
+ somePointsInBack = true;
+ if (type == FRONT)
+ somePointsInfront = true;
+ types.add(type);
+ }
+ if (somePointsInBack && somePointsInfront)
+ polygonType = SPANNING;
+ else if (somePointsInBack) {
+ polygonType = BACK;
+ } else if (somePointsInfront)
+ polygonType = FRONT;
+
+ // Put the polygon in the correct list, splitting it when necessary.
+ switch (polygonType) {
+ case COPLANAR:
+ (this.getNormal().dot(polygon.plane.getNormal()) > 0 ? coplanarFront : coplanarBack).add(polygon);
+ break;
+ case FRONT:
+ front.add(polygon);
+ break;
+ case BACK:
+ back.add(polygon);
+ break;
+ case SPANNING:
+ List f = new ArrayList<>();
+ List b = new ArrayList<>();
+ for (int i = 0; i < polygon.vertices.size(); i++) {
+ int j = (i + 1) % polygon.vertices.size();
+ int ti = types.get(i);
+ int tj = types.get(j);
+ Vertex vi = polygon.vertices.get(i);
+ Vertex vj = polygon.vertices.get(j);
+ if (ti != BACK) {
+ f.add(vi);
+ }
+ if (ti != FRONT) {
+ b.add(ti != BACK ? vi.clone() : vi);
+ }
+ if ((ti | tj) == SPANNING) {
+ double t = (this.getDist() - this.getNormal().dot(vi.pos))
+ / this.getNormal().dot(vj.pos.minus(vi.pos));
+ Vertex v = vi.interpolate(vj, t);
+ f.add(v);
+ b.add(v.clone());
+ }
+ }
+ if (f.size() >= 3) {
+ try {
+ front.add(new Polygon(f, polygon.getStorage()).setColor(polygon.getColor()));
+ } catch (NumberFormatException ex) {
+ //ex.printStackTrace();
+ // skip adding broken polygon here
+ }
+ } else {
+ System.out.println("Front Clip Fault!");
+ }
+ if (b.size() >= 3) {
+ try {
+ back.add(new Polygon(b, polygon.getStorage()).setColor(polygon.getColor()));
+ } catch (NumberFormatException ex) {
+ //ex.printStackTrace();
+ // skip adding broken polygon here
+ }
+ } else {
+ System.out.println("Back Clip Fault!");
+ }
+ break;
+ }
+ }
public static IPolygonDebugger getDebugger() {
return debugger;
@@ -245,4 +300,34 @@ public static boolean isUseDebugger() {
public static void setUseDebugger(boolean useDebugger) {
Plane.useDebugger = useDebugger;
}
+
+ public Vector3d getNormal() {
+ return normal;
+ }
+
+ public void setNormal(Vector3d normal) {
+ if (Double.isFinite(normal.x) && Double.isFinite(normal.y) && Double.isFinite(normal.z))
+ this.normal = normal;
+ else {
+
+ NumberFormatException numberFormatException = new NumberFormatException();
+ // numberFormatException.printStackTrace();
+ throw numberFormatException;
+ }
+ }
+
+ public double getDist() {
+ return dist;
+ }
+
+ public void setDist(double dist) {
+ if (Double.isFinite(dist))
+ this.dist = dist;
+ else {
+
+ NumberFormatException numberFormatException = new NumberFormatException();
+ // numberFormatException.printStackTrace();
+ throw numberFormatException;
+ }
+ }
}
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java b/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java
index 5fc18f7a..12e0a838 100644
--- a/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java
+++ b/src/main/java/eu/mihosoft/vrl/v3d/Polygon.java
@@ -62,7 +62,7 @@ public final class Polygon {
*
* Note: uses first three vertices to define the plane.
*/
- public final Plane plane;
+ public Plane plane;
/**
@@ -111,10 +111,15 @@ public static List fromConcavePoints(List points) {
public Polygon(List vertices, PropertyStorage shared, boolean allowDegenerate) {
this.vertices = pruneDuplicatePoints(vertices);
this.shared = shared;
- this.plane = Plane.createFromPoints(
- vertices.get(0).pos,
- vertices.get(1).pos,
- vertices.get(2).pos);
+// try {
+ this.plane = Plane.createFromPoints(
+ vertices);
+// }catch(java.lang.NumberFormatException nf) {
+// System.out.println("plane failed to load");
+// nf.printStackTrace();
+// this.plane = Plane.createFromPoints(
+// vertices);
+// }
validateAndInit(allowDegenerate);
}
/**
@@ -142,9 +147,7 @@ public Polygon(List vertices, PropertyStorage shared) {
public Polygon(List vertices) {
this.vertices = pruneDuplicatePoints(vertices);
this.plane = Plane.createFromPoints(
- vertices.get(0).pos,
- vertices.get(1).pos,
- vertices.get(2).pos);
+ vertices);
validateAndInit(true);
}
public static List pruneDuplicatePoints(List incoming) {
@@ -171,10 +174,10 @@ public static List pruneDuplicatePoints(List incoming) {
}
private void validateAndInit( boolean allowDegenerate) {
for (Vertex v : vertices) {
- v.normal = plane.normal;
+ v.normal = plane.getNormal();
}
setDegenerate(true);
- if (Vector3d.ZERO.equals(plane.normal)) {
+ if (Vector3d.ZERO.equals(plane.getNormal())) {
valid = false;
throw new RuntimeException(
"Normal is zero! Probably, duplicate points have been specified!\n\n" + toStlString());
@@ -279,7 +282,7 @@ public StringBuilder toStlString(StringBuilder sb) {
for (int i = 0; i < this.vertices.size() - 2; i++) {
sb.
append(" facet normal ").append(
- this.plane.normal.toStlString()).append("\n").
+ this.plane.getNormal().toStlString()).append("\n").
append(" outer loop\n").
append(" ").append(firstVertexStl).append("\n").
append(" ");
@@ -311,7 +314,7 @@ public Polygon translate(Vector3d v) {
Vector3d b = this.vertices.get(1).pos;
Vector3d c = this.vertices.get(2).pos;
- this.plane.normal = b.minus(a).cross(c.minus(a));
+ this.plane.setNormal(b.minus(a).cross(c.minus(a)));
return this;
}
@@ -348,11 +351,9 @@ public Polygon transform(Transform transform) {
);
Vector3d a = this.vertices.get(0).pos;
- Vector3d b = this.vertices.get(1).pos;
- Vector3d c = this.vertices.get(2).pos;
- this.plane.normal = b.minus(a).cross(c.minus(a)).normalized();
- this.plane.dist = this.plane.normal.dot(a);
+ this.plane.setNormal(Plane.computeNormal(this.vertices));
+ this.plane.setDist(this.plane.getNormal().dot(a));
if (transform.isMirror()) {
// the transformation includes mirroring. flip polygon
@@ -361,7 +362,21 @@ public Polygon transform(Transform transform) {
}
return this;
}
-
+// public Vector3d computeNormal(List vertices) {
+// Vector3d normal = new Vector3d(0, 0, 0);
+// int n = vertices.size();
+//
+// for (int i = 0; i < n; i++) {
+// Vector3d current = vertices.get(i).pos;
+// Vector3d next = vertices.get((i + 1) % n).pos;
+//
+// normal.x += (current.y - next.y) * (current.z + next.z);
+// normal.y += (current.z - next.z) * (current.x + next.x);
+// normal.z += (current.x - next.x) * (current.y + next.y);
+// }
+//
+// return normal.normalized();
+// }
/**
* Returns a transformed copy of this polygon.
*
@@ -423,7 +438,7 @@ private static Polygon fromPoints(
List points, PropertyStorage shared, Plane plane, boolean allowDegenerate) {
Vector3d normal
- = (plane != null) ? plane.normal.clone() : new Vector3d(0, 0, 0);
+ = (plane != null) ? plane.getNormal().clone() : new Vector3d(0, 0, 0);
List vertices = new ArrayList<>();
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/Transform.java b/src/main/java/eu/mihosoft/vrl/v3d/Transform.java
index eeb31902..01c873e2 100644
--- a/src/main/java/eu/mihosoft/vrl/v3d/Transform.java
+++ b/src/main/java/eu/mihosoft/vrl/v3d/Transform.java
@@ -328,10 +328,10 @@ public Transform mirror(Plane plane) {
System.err.println("WARNING: I'm too dumb to implement the mirror() operation correctly. Please fix me!");
- double nx = plane.normal.x;
- double ny = plane.normal.y;
- double nz = plane.normal.z;
- double w = plane.dist;
+ double nx = plane.getNormal().x;
+ double ny = plane.getNormal().y;
+ double nz = plane.getNormal().z;
+ double w = plane.getDist();
double elemenents[] = { (1.0 - 2.0 * nx * nx), (-2.0 * ny * nx), (-2.0 * nz * nx), 0, (-2.0 * nx * ny),
(1.0 - 2.0 * ny * ny), (-2.0 * nz * ny), 0, (-2.0 * nx * nz), (-2.0 * ny * nz), (1.0 - 2.0 * nz * nz),
0, (-2.0 * nx * w), (-2.0 * ny * w), (-2.0 * nz * w), 1 };
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 297de03a..0f131b9d 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
@@ -108,7 +108,7 @@ public static List concaveToConvex(Polygon incoming) {
if (incoming.vertices.size() < 3)
return result;
Polygon concave = incoming;
- Vector3d normalOfPlane = incoming.plane.normal;
+ Vector3d normalOfPlane = incoming.plane.getNormal();
boolean reorent = normalOfPlane.z < 1.0-Plane.EPSILON;
Transform orentationInv = null;
boolean debug = false;
@@ -118,7 +118,7 @@ public static List concaveToConvex(Polygon incoming) {
Polygon tmp = incoming.transformed(orentation);
- Vector3d normal = tmp.plane.normal;
+ Vector3d normal = tmp.plane.getNormal();
double degreesToRotate2 =90+Math.toDegrees(Math.atan2(normal.z,normal.y));
Transform orentation2 = orentation.rotx(degreesToRotate2);// th triangulation function needs
// the polygon on the xy plane
@@ -128,7 +128,7 @@ public static List concaveToConvex(Polygon incoming) {
}
concave = incoming.transformed(orentation2);
orentationInv = orentation2.inverse();
- if(concave.plane.normal.z <0) {
+ if(concave.plane.getNormal().z <0) {
Transform orentation3 = orentation2.rotx(180);
concave = incoming.transformed(orentation3);
orentationInv = orentation3.inverse();
@@ -136,7 +136,7 @@ public static List concaveToConvex(Polygon incoming) {
}
- Vector3d normal = concave.plane.normal.clone();
+ Vector3d normal = concave.plane.getNormal().clone();
boolean cw = !Extrude.isCCW(concave);
//concave = Extrude.toCCW(concave);
@@ -183,7 +183,7 @@ public static List concaveToConvex(Polygon incoming) {
}
Polygon poly = new Polygon(triPoints, concave.getStorage(), true);
//poly = Extrude.toCCW(poly);
- poly.plane.normal = concave.plane.normal;
+ poly.plane.setNormal(concave.plane.getNormal());
boolean b = !Extrude.isCCW(poly);
if (cw != b) {
// System.out.println("Triangle not matching incoming");
@@ -203,7 +203,7 @@ public static List concaveToConvex(Polygon incoming) {
if (reorent) {
poly = poly.transform(orentationInv);
}
- poly.plane.normal = normalOfPlane;
+ poly.plane.setNormal(normalOfPlane);
poly.setColor(incoming.getColor());
result.add(poly);
counter = 0;
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/svg/SVGLoad.java b/src/main/java/eu/mihosoft/vrl/v3d/svg/SVGLoad.java
index 34da0ff3..1c6d6da9 100644
--- a/src/main/java/eu/mihosoft/vrl/v3d/svg/SVGLoad.java
+++ b/src/main/java/eu/mihosoft/vrl/v3d/svg/SVGLoad.java
@@ -584,11 +584,13 @@ private void loadComposite(String code, double resolution, Transform startingFra
// println "Single path found"
// setHolePolarity(true);
- try {
+// try {
loadSingle(code, resolution, startingFrame,encapsulatingLayer, c);
- } catch (Exception ex) {
- // BowlerStudio.printStackTrace(ex);
- }
+// } catch (Exception ex) {
+// System.out.println("Polygon failed to load!");
+// ex.printStackTrace();
+// // BowlerStudio.printStackTrace(ex);
+// }
} else {
// setHolePolarity(false);
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/thumbnail/ThumbnailImage.java b/src/main/java/eu/mihosoft/vrl/v3d/thumbnail/ThumbnailImage.java
new file mode 100644
index 00000000..dc716c82
--- /dev/null
+++ b/src/main/java/eu/mihosoft/vrl/v3d/thumbnail/ThumbnailImage.java
@@ -0,0 +1,209 @@
+package eu.mihosoft.vrl.v3d.thumbnail;
+
+
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import eu.mihosoft.vrl.v3d.Bounds;
+import eu.mihosoft.vrl.v3d.CSG;
+import eu.mihosoft.vrl.v3d.Vector3d;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.SceneAntialiasing;
+import javafx.scene.SnapshotParameters;
+import javafx.scene.image.WritableImage;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.PhongMaterial;
+import javafx.scene.shape.CullFace;
+import javafx.scene.shape.MeshView;
+import javafx.scene.transform.Transform;
+import javafx.scene.PerspectiveCamera;
+import javafx.embed.swing.SwingFXUtils;
+import javafx.scene.transform.Affine;
+import javafx.scene.transform.Transform;
+import javafx.scene.transform.Rotate;
+import javafx.geometry.Rectangle2D;
+
+public class ThumbnailImage {
+ private static CullFace cullFaceValue = CullFace.BACK;
+
+ public static Bounds getSellectedBounds(List incoming) {
+ Vector3d min = null;
+ Vector3d max = null;
+ for (CSG c : incoming) {
+ if(c.isHide())
+ continue;
+ if(c.isInGroup())
+ continue;
+ Vector3d min2 = c.getBounds().getMin().clone();
+ Vector3d max2 = c.getBounds().getMax().clone();
+ if (min == null)
+ min = min2;
+ if (max == null)
+ max = max2;
+ if (min2.x < min.x)
+ min.x = min2.x;
+ if (min2.y < min.y)
+ min.y = min2.y;
+ if (min2.z < min.z)
+ min.z = min2.z;
+ if (max.x < max2.x)
+ max.x = max2.x;
+ if (max.y < max2.y)
+ max.y = max2.y;
+ if (max.z < max2.z)
+ max.z = max2.z;
+ }
+ if(max==null)
+ max=new Vector3d(0,0,0);
+ if(min==null)
+ min=new Vector3d(0,0,0);
+ return new Bounds(min, max);
+ }
+
+ public static WritableImage get(List c) {
+ ArrayList csgList=new ArrayList() ;
+ for(CSG cs:c) {
+ if(cs.getManipulator()!=null) {
+ csgList.add(cs.transformed(TransformConverter.fromAffine(cs.getManipulator())).syncProperties(cs));
+ }else
+ csgList.add(cs);
+ }
+ // Create a group to hold all the meshes
+ Group root = new Group();
+
+ // Add all meshes to the group
+ Bounds b = getSellectedBounds(csgList);
+
+ double yOffset = (b.getMax().y-b.getMin().y)/2;
+ double xOffset =(b.getMax().x -b.getMin().x)/2;
+ double zCenter = (b.getMax().z -b.getMin().z)/2;
+ for (CSG csg : csgList) {
+ if(csg.isHide())
+ continue;
+ if(csg.isInGroup())
+ continue;
+ MeshView meshView = csg.movez(-zCenter).getMesh();
+ if (csg.isHole()) {
+ PhongMaterial material = new PhongMaterial();
+ material.setDiffuseColor(new Color(0.25, 0.25, 0.25, 0.75));
+ material.setSpecularColor(javafx.scene.paint.Color.WHITE);
+ meshView.setMaterial(material);
+ meshView.setOpacity(0.25);
+ }
+ meshView.setCullFace(getCullFaceValue());
+ root.getChildren().add(meshView);
+ }
+
+ // Calculate the bounds of all CSGs combined
+ double totalz = b.getMax().z - b.getMin().z;
+ double totaly = b.getMax().y - b.getMin().y;
+ double totalx = b.getMax().x - b.getMin().x;
+
+ // Create a perspective camera
+ PerspectiveCamera camera = new PerspectiveCamera(true);
+
+ // Calculate camera position to fit all objects in view
+ double maxDimension = Math.max(totalx, Math.max(totaly, totalz));
+ double cameraDistance = (maxDimension / Math.tan(Math.toRadians(camera.getFieldOfView() / 2)))*0.8 ;
+
+// TransformNR camoffset = new TransformNR(xOffset, yOffset, 0);
+// TransformNR camDist = new TransformNR(0, 0, -cameraDistance);
+// TransformNR rot = new TransformNR(new RotationNR(-150, 45, 0));
+//
+// Affine af = TransformFactory.nrToAffine(camoffset.times(rot.times(camDist)));
+ Affine camDist = new Affine();
+ camDist.setTz(-cameraDistance);
+ Rotate rot1 =new Rotate(45, Rotate.Z_AXIS);
+ Rotate rot2 =new Rotate(-150, Rotate.Y_AXIS);
+ Affine camoffset = new Affine();
+ camoffset.setTx(xOffset);
+ camoffset.setTy(yOffset);
+ camera.getTransforms().add(camoffset );
+ camera.getTransforms().add(rot2 );
+ camera.getTransforms().add(rot1 );
+ camera.getTransforms().add(camDist );
+ //
+
+
+ // Position the camera
+// camera.setTranslateX();
+// camera.setTranslateY();
+// camera.setTranslateZ();
+// // Apply rotations to the root group instead of the camera
+// root.getTransforms().addAll(
+// new Rotate(-5, Rotate.Y_AXIS),
+// new Rotate(-45, Rotate.X_AXIS)
+// );
+ // Create a scene with the group and camera
+ int i = 1000;
+ Scene scene = new Scene(root, i, i, true, SceneAntialiasing.BALANCED);
+ scene.setFill(Color.TRANSPARENT);
+ scene.setCamera(camera);
+
+ // Set up snapshot parameters
+ SnapshotParameters params = new SnapshotParameters();
+ params.setFill(Color.TRANSPARENT);
+ params.setCamera(camera);
+ params.setDepthBuffer(true);
+ params.setTransform(Transform.scale(1, 1));
+ // Set the near and far clip
+ camera.setNearClip(0.1); // Set the near clip plane
+ camera.setFarClip(9000.0); // Set the far clip plane
+
+
+ // Create the WritableImage first
+ WritableImage snapshot = new WritableImage(i, i);
+
+ root.snapshot(params, snapshot);
+
+ return snapshot;
+ }
+
+ public static Thread writeImage(ArrayList incoming, File toPNG) {
+ Thread t= new Thread(new Runnable() {
+ WritableImage img=null;
+ @Override
+ public void run() {
+ File image = toPNG;
+ javafx.application.Platform.runLater(() -> img=ThumbnailImage.get(incoming));
+ while (img == null)
+ try {
+ Thread.sleep(16);
+ // System.out.println("Waiting for image to write");
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ break;
+ }
+ BufferedImage bufferedImage = SwingFXUtils.fromFXImage(img, null);
+
+ try {
+ ImageIO.write(bufferedImage, "png", image);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ t.setUncaughtExceptionHandler((thread, throwable) -> {
+ throwable.printStackTrace();
+ thread.interrupt();
+ });
+ t.start();
+ return t;
+ }
+
+ public static CullFace getCullFaceValue() {
+ return cullFaceValue;
+ }
+
+ public static void setCullFaceValue(CullFace cullFaceValue) {
+ ThumbnailImage.cullFaceValue = cullFaceValue;
+ }
+}
diff --git a/src/main/java/eu/mihosoft/vrl/v3d/thumbnail/TransformConverter.java b/src/main/java/eu/mihosoft/vrl/v3d/thumbnail/TransformConverter.java
new file mode 100644
index 00000000..33349046
--- /dev/null
+++ b/src/main/java/eu/mihosoft/vrl/v3d/thumbnail/TransformConverter.java
@@ -0,0 +1,34 @@
+package eu.mihosoft.vrl.v3d.thumbnail;
+
+import javax.vecmath.Matrix4d;
+
+import eu.mihosoft.vrl.v3d.Transform;
+
+public class TransformConverter {
+ public static Transform fromAffine(javafx.scene.transform.Affine rotations) {
+ double[] v = new double[]{
+ rotations.getMxx(),
+ rotations.getMxy(),
+ rotations.getMxz(),
+ 0,
+ rotations.getMyx(),
+ rotations.getMyy(),
+ rotations.getMyz(),
+ 0,
+ rotations.getMzx(),
+ rotations.getMzy(),
+ rotations.getMzz(),
+ 0,
+ rotations.getTx(),
+ rotations.getTy(),
+ rotations.getTz(),
+ 1
+ };
+ Matrix4d rotation = new Matrix4d(v );
+ //eu.mihosoft.vrl.v3d.Transform transform = new eu.mihosoft.vrl.v3d.Transform(rotation);
+ eu.mihosoft.vrl.v3d.Transform transform = new eu.mihosoft.vrl.v3d.Transform();
+ System.out.println("Incoming "+rotations);
+ System.out.println("Converted to "+transform);
+ return transform;
+ }
+}
diff --git a/src/test/java/eu/mihosoft/vrl/v3d/SVGLoadTest.java b/src/test/java/eu/mihosoft/vrl/v3d/SVGLoadTest.java
index e859540c..89383220 100644
--- a/src/test/java/eu/mihosoft/vrl/v3d/SVGLoadTest.java
+++ b/src/test/java/eu/mihosoft/vrl/v3d/SVGLoadTest.java
@@ -11,38 +11,65 @@
import org.junit.Test;
import eu.mihosoft.vrl.v3d.svg.SVGLoad;
+import eu.mihosoft.vrl.v3d.thumbnail.ThumbnailImage;
+import javafx.scene.shape.CullFace;
public class SVGLoadTest {
+ @Test
+ public void adversarial() throws IOException {
+ JavaFXInitializer.go();
+ File svg = new File("Part-Num-0.svg");
+ if (!svg.exists())
+ throw new RuntimeException("Test file missing!" + svg.getAbsolutePath());
+ SVGLoad s = new SVGLoad(svg.toURI());
+ ArrayListparts =run(s);
+ try {
+ ThumbnailImage.setCullFaceValue(CullFace.NONE);
+ ThumbnailImage.writeImage(parts,new File(svg.getAbsolutePath()+".png")).join();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ // fail("Not yet implemented");
+ }
@Test
public void test() throws IOException {
File svg = new File("Test.SVG");
- if(!svg.exists())
- throw new RuntimeException("Test file missing!"+svg.getAbsolutePath());
+ if (!svg.exists())
+ throw new RuntimeException("Test file missing!" + svg.getAbsolutePath());
SVGLoad s = new SVGLoad(svg.toURI());
run(s);
- //fail("Not yet implemented");
+ // fail("Not yet implemented");
}
- private ArrayList