JavaCAD
PolygonUtil.java
Go to the documentation of this file.
1 /*
2  * PolygonUtil.java
3  *
4  * Copyright 2014-2014 Michael Hoffer info@michaelhoffer.de. All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY Michael Hoffer info@michaelhoffer.de "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL Michael Hoffer info@michaelhoffer.de OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * The views and conclusions contained in the software and documentation are
30  * those of the authors and should not be interpreted as representing official
31  * policies, either expressed or implied, of Michael Hoffer
32  * info@michaelhoffer.de.
33  */
34 package eu.mihosoft.vrl.v3d.ext.org.poly2tri;
35 
36 import eu.mihosoft.vrl.v3d.Debug3dProvider;
37 import eu.mihosoft.vrl.v3d.Extrude;
38 import eu.mihosoft.vrl.v3d.Plane;
39 import eu.mihosoft.vrl.v3d.Polygon;
40 import eu.mihosoft.vrl.v3d.Transform;
41 import eu.mihosoft.vrl.v3d.Vector3d;
42 import eu.mihosoft.vrl.v3d.Vertex;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.List;
46 
47 import org.locationtech.jts.geom.Coordinate;
48 import org.locationtech.jts.geom.Geometry;
49 import org.locationtech.jts.geom.GeometryFactory;
50 import org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator;
51 
52 // TODO: Auto-generated Javadoc
58 public class PolygonUtil {
59 
63  private PolygonUtil() {
64  throw new AssertionError("Don't instantiate me!", null);
65  }
66 
67 // /**
68 // * Converts a CSG polygon to a poly2tri polygon (including holes).
69 // *
70 // * @param polygon the polygon to convert
71 // * @return a CSG polygon to a poly2tri polygon (including holes)
72 // */
73 // public static eu.mihosoft.vrl.v3d.ext.org.poly2tri.Polygon fromCSGPolygon(eu.mihosoft.vrl.v3d.Polygon polygon) {
74 //
75 // // convert polygon
76 // List<PolygonPoint> points = new ArrayList<>();
77 // for (Vertex v : polygon.vertices) {
78 // PolygonPoint vp = new PolygonPoint(v.pos.x, v.pos.y, v.pos.z);
79 // points.add(vp);
80 // }
81 //
82 // eu.mihosoft.vrl.v3d.ext.org.poly2tri.Polygon result = new eu.mihosoft.vrl.v3d.ext.org.poly2tri.Polygon(points);
83 //
84 // // convert holes
85 // Optional<List<eu.mihosoft.vrl.v3d.Polygon>> holesOfPresult = polygon.getStorage()
86 // .getValue(eu.mihosoft.vrl.v3d.Edge.KEY_POLYGON_HOLES);
87 // if (holesOfPresult.isPresent()) {
88 // List<eu.mihosoft.vrl.v3d.Polygon> holesOfP = holesOfPresult.get();
89 //
90 // holesOfP.stream().forEach((hP) -> {
91 // result.addHole(fromCSGPolygon(hP));
92 // });
93 // }
94 //
95 // return result;
96 // }
97 
104  public static List<eu.mihosoft.vrl.v3d.Polygon> concaveToConvex(eu.mihosoft.vrl.v3d.Polygon incoming) {
105  return concaveToConvex(incoming,false);
106  }
107  public static List<eu.mihosoft.vrl.v3d.Polygon> concaveToConvex(eu.mihosoft.vrl.v3d.Polygon incoming, boolean strictTriangulation) {
108  //incoming = pruneDuplicatePoints(incoming);
109  List<Polygon> result = new ArrayList<>();
110 
111  if (incoming == null)
112  return result;
113  if (incoming.vertices.size() < 3)
114  return result;
115  eu.mihosoft.vrl.v3d.Polygon concave= incoming;;
116  Vector3d normalOfPlane = incoming.plane.normal;
117  boolean reorent = normalOfPlane.z < 1.0-Plane.EPSILON;
118  Transform orentationInv = null;
119  boolean debug = false;
120  Vector3d normal2;
121  if (reorent) {
122  double degreesToRotate = Math.toDegrees(Math.atan2(normalOfPlane.x,normalOfPlane.z));
123  Transform orentation = new Transform().roty(degreesToRotate);
124 
125  eu.mihosoft.vrl.v3d.Polygon tmp = incoming.transformed(orentation);
126 
127  Vector3d normal = tmp.plane.normal;
128  double degreesToRotate2 =90+Math.toDegrees(Math.atan2(normal.z,normal.y));
129  Transform orentation2 = orentation.rotx(degreesToRotate2);// th triangulation function needs
130  // the polygon on the xy plane
131  if (debug) {
133  Debug3dProvider.addObject(incoming);
134  }
135  concave = incoming.transformed(orentation2);
136  normal2 = concave.plane.normal;
137  orentationInv = orentation2.inverse();
138  if(concave.plane.normal.z <0) {
139  Transform orentation3 = orentation2.rotx(180);
140  concave = incoming.transformed(orentation3);
141  orentationInv = orentation3.inverse();
142  }
143 
144 
145  //System.out.println("New vectors "+normal2+" "+normal);
146  }
147 // if(concave.plane.normal.z < 0.999) {
148 // result.add(incoming);
149 // return result;
150 // //throw new RuntimeException("Orentaion of plane misaligned for triangulation "+concave.plane.normal.z);
151 // }
152 
153 
154  Vector3d normal = concave.plane.normal.clone();
155 
156  boolean cw = !Extrude.isCCW(concave);
157  //concave = Extrude.toCCW(concave);
158  if (debug) {
160  Debug3dProvider.addObject(concave);
161  //Debug3dProvider.clearScreen();
162  }
163 
164  Coordinate[] coordinates = new Coordinate[concave.vertices.size()+1];
165  double zplane =concave.vertices.get(0).pos.z;
166  for(int i=0;i<concave.vertices.size();i++) {
167  Vector3d v = concave.vertices.get(i).pos;
168  coordinates[i]=new Coordinate(v.x,v.y,zplane);
169  }
170  Vector3d v = concave.vertices.get(0).pos;
171  coordinates[concave.vertices.size()]=new Coordinate(v.x,v.y,zplane);
172  // use the default factory, which gives full double-precision
173  //System.out.println("Triangulating\n"+geom.toText());
174  Geometry triangles;
175  try {
176  Geometry geom = new GeometryFactory().createPolygon(coordinates);
177  triangles= ConstrainedDelaunayTriangulator.triangulate(geom);
178  //System.out.println("Triangulation result\n"+triangles.toText());
179  }catch(Exception ex) {
180  ex.printStackTrace();
181  throw ex;
182  }
183 // eu.mihosoft.vrl.v3d.ext.org.poly2tri.LegacyPolygon p = fromCSGPolygon(concave);
184 // //System.out.println("Triangulating "+p);
185 // eu.mihosoft.vrl.v3d.ext.org.poly2tri.Poly2Tri.triangulate(p);
186 //
187 // List<DelaunayTriangle> triangles = p.getTriangles();
188 
189  ArrayList<Vertex> triPoints = new ArrayList<>();
190 
191  for (int i=0;i<triangles.getNumGeometries();i++) {
192  Geometry tri = triangles.getGeometryN(i);
193  Coordinate[] coords = tri.getCoordinates();
194  int counter = 0;
195  if(coords.length!=4)
196  throw new RuntimeException("Failed to triangulate");
197  for (int j=0;j<3;j++) {
198  Coordinate tp = coords[j];
199  Vector3d pos = new Vector3d(tp.getX(), tp.getY(), zplane);
200  triPoints.add(new Vertex(pos, normal));
201 
202  if (counter == 2) {
203  if (!cw) {
204  Collections.reverse(triPoints);
205  }
206  eu.mihosoft.vrl.v3d.Polygon poly = new eu.mihosoft.vrl.v3d.Polygon(triPoints, concave.getStorage(),true);
207  //poly = Extrude.toCCW(poly);
208  poly.plane.normal = concave.plane.normal;
209  boolean b = !Extrude.isCCW(poly);
210  if (cw != b) {
211  // System.out.println("Triangle not matching incoming");
212  Collections.reverse(triPoints);
213  poly = new eu.mihosoft.vrl.v3d.Polygon(triPoints, concave.getStorage(),true);
214  b = !Extrude.isCCW(poly);
215  if (cw != b) {
216  System.out.println("Error, polygon is reversed!");
217  }
218  }
219  if (debug) {
220  //Debug3dProvider.clearScreen();
221  //Debug3dProvider.addObject(concave);
223  }
224 
225  if (reorent) {
226  poly = poly.transform(orentationInv);
227  }
228  poly.plane.normal = normalOfPlane;
229  //poly.setDegenerate(t.isDegenerate());
230  // System.out.println("Updating the normal to " + clone);
231 // if (debug) {
232 // Debug3dProvider.addObject(incoming);
233 // Debug3dProvider.addObject(poly);
234 // }
235  result.add(poly);
236  counter = 0;
237  triPoints = new ArrayList<>();
238 
239  } else {
240  counter++;
241  }
242  }
243  }
244 
245  return result;
246  }
247 
248 // /**
249 // * Concave to convex.
250 // *
251 // * @param concave the concave
252 // * @return the list
253 // */
254 // public static List<eu.mihosoft.vrl.v3d.Polygon> concaveToConvex(eu.mihosoft.vrl.v3d.Polygon incoming) {
255 // //incoming = pruneDuplicatePoints(incoming);
256 // if (incoming == null)
257 // return new ArrayList<>();
258 // if (incoming.vertices.size() < 3)
259 // return new ArrayList<>();
260 // eu.mihosoft.vrl.v3d.Polygon concave;
261 // Vector3d normalOfPlane = incoming.plane.normal;
262 // boolean reorent = Math.abs(normalOfPlane.z) < 1.0-Plane.EPSILON;
263 // Transform orentationInv = null;
264 // boolean debug = false;
265 // Vector3d normal2;
266 // if (reorent) {
267 // //debug = true;
268 // double degreesToRotate = Math.toDegrees(Math.atan2(normalOfPlane.x,normalOfPlane.z));
269 // Transform orentation = new Transform().roty(degreesToRotate);
270 //
271 // eu.mihosoft.vrl.v3d.Polygon tmp = incoming.transformed(orentation);
272 //
273 // Vector3d normal = tmp.plane.normal;
274 // double degreesToRotate2 =90+Math.toDegrees(Math.atan2(normal.z,normal.y));
275 // Transform orentation2 = orentation.rotx(degreesToRotate2);// th triangulation function needs
276 // // the polygon on the xy plane
277 // orentationInv = orentation2.inverse();
278 //
279 // if (debug) {
280 // Debug3dProvider.clearScreen();
281 // Debug3dProvider.addObject(incoming);
282 // }
283 // concave = incoming.transformed(orentation2);
284 // normal2 = concave.plane.normal;
285 // //System.out.println("New vectors "+normal2+" "+normal);
286 // } else
287 // concave = incoming;
288 // if(Math.abs(concave.plane.normal.z) < 1.0-Plane.EPSILON) {
289 // throw new RuntimeException("Orentaion of plane misaligned for triangulation");
290 // }
291 //
292 // List<eu.mihosoft.vrl.v3d.Polygon> result = new ArrayList<>();
293 //
294 // Vector3d normal = concave.vertices.get(0).normal.clone();
295 //
296 // boolean cw = !Extrude.isCCW(concave);
297 // concave = Extrude.toCCW(concave);
298 // if (reorent) {
299 // // Debug3dProvider.addObject(concave);
300 // }
301 //
302 // eu.mihosoft.vrl.v3d.ext.org.poly2tri.Polygon p = fromCSGPolygon(concave);
303 //
304 // eu.mihosoft.vrl.v3d.ext.org.poly2tri.Poly2Tri.triangulate(p);
305 //
306 // List<DelaunayTriangle> triangles = p.getTriangles();
307 //
308 // List<Vertex> triPoints = new ArrayList<>();
309 //
310 // for (DelaunayTriangle t : triangles) {
311 //
312 // int counter = 0;
313 // for (TriangulationPoint tp : t.points) {
314 //
315 // triPoints.add(new Vertex(new Vector3d(tp.getX(), tp.getY(), tp.getZ()), normal));
316 //
317 // if (counter == 2) {
318 // if (!cw) {
319 // Collections.reverse(triPoints);
320 // }
321 // eu.mihosoft.vrl.v3d.Polygon poly = new eu.mihosoft.vrl.v3d.Polygon(triPoints, concave.getStorage(),true);
322 //
323 // poly.plane.normal = concave.plane.normal;
324 // // Debug3dProvider.addObject(poly);
325 // if (reorent) {
326 // poly = poly.transform(orentationInv);
327 // if (reorent) {
328 // // Debug3dProvider.addObject(poly);
329 // }
330 // }
331 // Vector3d clone = normalOfPlane.clone();
332 //
333 // // System.out.println("Updating the normal to " + clone);
334 // poly.plane.normal = clone;
335 // // Debug3dProvider.addObject(poly);
336 // result.add(poly);
337 // counter = 0;
338 // triPoints = new ArrayList<>();
339 //
340 // } else {
341 // counter++;
342 // }
343 // }
344 // }
345 //
346 // return result;
347 // }
348 
349  public static Polygon pruneDuplicatePoints(Polygon incoming) {
350  ArrayList<Vertex> newPoints = new ArrayList<Vertex>();
351  for (int i = 0; i < incoming.vertices.size(); i++) {
352  Vertex v = incoming.vertices.get(i);
353  boolean duplicate = false;
354  for (Vertex vx : newPoints) {
355  if (vx.pos.test(v.pos, Plane.EPSILON_duplicate)) {
356  duplicate = true;
357  }
358  }
359  if (!duplicate) {
360  newPoints.add(v);
361  }
362 
363  }
364  if(newPoints.size()<3)
365  return null;
366 
367  return new Polygon(newPoints);
368 
369  }
370 }
static boolean isCCW(Polygon polygon)
Definition: Extrude.java:258
static final double EPSILON
Definition: Plane.java:55
static final double EPSILON_duplicate
Definition: Plane.java:57
final List< Vertex > vertices
Definition: Polygon.java:54
Transform roty(Number degreesToRotate)
Definition: Transform.java:705
Transform rotx(Number degreesToRotate)
Definition: Transform.java:716
static Vector3d y(double y)
Definition: Vector3d.java:484
static Vector3d z(double z)
Definition: Vector3d.java:494
static Vector3d x(double x)
Definition: Vector3d.java:474
static List< eu.mihosoft.vrl.v3d.Polygon > concaveToConvex(eu.mihosoft.vrl.v3d.Polygon incoming)
static Polygon pruneDuplicatePoints(Polygon incoming)
static List< eu.mihosoft.vrl.v3d.Polygon > concaveToConvex(eu.mihosoft.vrl.v3d.Polygon incoming, boolean strictTriangulation)