Skip to content

Commit e9e3861

Browse files
committed
convert the splitPlane to a Node method and make the storage a mutable
list
1 parent 5a016e6 commit e9e3861

File tree

9 files changed

+443
-433
lines changed

9 files changed

+443
-433
lines changed

src/main/java/eu/mihosoft/vrl/v3d/CSG.java

Lines changed: 259 additions & 255 deletions
Large diffs are not rendered by default.

src/main/java/eu/mihosoft/vrl/v3d/Extrude.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public CSG extrude(Vector3d dir, Polygon polygon1) {
8282
return monotoneExtrude(dir, polygon1);
8383
}
8484
private CSG monotoneExtrude(Vector3d dir, Polygon polygon1) {
85-
List<Polygon> newPolygons = new ArrayList<>();
85+
ArrayList<Polygon> newPolygons = new ArrayList<>();
8686
CSG extrude;
8787
//polygon1=polygon1.flipped();
8888
// List<Vertex> newVertices = new ArrayList<>();
@@ -118,7 +118,7 @@ private CSG monotoneExtrude(Vector3d dir, Polygon polygon1) {
118118
}
119119
}
120120

121-
List<Polygon> topPolygons = PolygonUtil.concaveToConvex(polygon2);
121+
ArrayList<Polygon> topPolygons = PolygonUtil.concaveToConvex(polygon2);
122122

123123
newPolygons.addAll(topPolygons);
124124
extrude = CSG.fromPolygons(newPolygons);
@@ -151,7 +151,7 @@ public static CSG polygons(Polygon polygon1, Polygon polygon2) {
151151
// polygon2=Polygon.fromPoints(toCCW(polygon2.getPoints()));
152152
// }
153153

154-
List<Polygon> newPolygons = new ArrayList<>();
154+
ArrayList<Polygon> newPolygons = new ArrayList<>();
155155
CSG extrude;
156156
newPolygons.addAll(PolygonUtil.concaveToConvex(polygon1.flipped()));
157157
if (polygon1.getVertices().size() != polygon2.getVertices().size()) {
@@ -588,7 +588,7 @@ public static CSG sweep(Polygon p, Transform increment, Transform offset, int st
588588
}
589589
public static CSG sweep(Polygon p, Transform increment, Transform offset, int steps,ITransformProvider provider) {
590590
Polygon offsetP = p.transformed(offset);
591-
List<Polygon> newPolygons = new ArrayList<>();
591+
ArrayList<Polygon> newPolygons = new ArrayList<>();
592592
newPolygons.addAll(PolygonUtil.concaveToConvex(offsetP));
593593
Transform running = new Transform();
594594
Polygon prev = offsetP.transformed(provider.get(0, steps));

src/main/java/eu/mihosoft/vrl/v3d/Node.java

Lines changed: 172 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import java.util.stream.Collectors;
3939
import java.util.stream.Stream;
4040

41+
import eu.mihosoft.vrl.v3d.ext.org.poly2tri.PolygonUtil;
42+
4143
// Auto-generated Javadoc
4244
/**
4345
* Holds a node in a BSP tree. A BSP tree is built from a collection of polygons
@@ -47,11 +49,14 @@
4749
* no distinction between internal and leaf nodes.
4850
*/
4951
public final class Node {
50-
52+
private static final int COPLANAR = 0;
53+
private static final int FRONT = 1;
54+
private static final int BACK = 2;
55+
private static final int SPANNING = 3; // == some in the FRONT + some in the BACK
5156
/**
5257
* Polygons.
5358
*/
54-
private List<Polygon> polygons;
59+
private ArrayList<Polygon> polygons;
5560
/**
5661
* Plane used for BSP.
5762
*/
@@ -64,7 +69,7 @@ public final class Node {
6469
* Polygons in back of the plane.
6570
*/
6671
private Node back;
67-
72+
6873
private long maxDepth = -1;
6974

7075
/**
@@ -74,7 +79,7 @@ public final class Node {
7479
*
7580
* @param polygons polygons
7681
*/
77-
public Node(List<Polygon> polygons) {
82+
public Node(ArrayList<Polygon> polygons) {
7883
this.polygons = new ArrayList<>();
7984
if (polygons != null) {
8085
this.build(polygons);
@@ -106,12 +111,12 @@ public Node clone() {
106111

107112
Stream<Polygon> polygonStream;
108113

109-
if (polygons.size() > 200) {
110-
polygonStream = polygons.parallelStream();
111-
} else
112-
polygonStream = polygons.stream();
114+
if (polygons.size() > 200) {
115+
polygonStream = polygons.parallelStream();
116+
} else
117+
polygonStream = polygons.stream();
113118

114-
node.polygons = polygonStream.map(p -> p.clone()).collect(Collectors.toList());
119+
node.polygons = polygonStream.map(p -> p.clone()).collect(Collectors.toCollection(ArrayList::new));
115120

116121
return node;
117122
}
@@ -123,10 +128,10 @@ public void invert() {
123128

124129
Stream<Polygon> polygonStream;
125130

126-
if (polygons.size() > 200) {
127-
polygonStream = polygons.parallelStream();
128-
} else
129-
polygonStream = polygons.stream();
131+
if (polygons.size() > 200) {
132+
polygonStream = polygons.parallelStream();
133+
} else
134+
polygonStream = polygons.stream();
130135

131136
polygonStream.forEach((polygon) -> {
132137
polygon.flip();
@@ -136,8 +141,9 @@ public void invert() {
136141
this.setPlane(polygons.get(0).getPlane().clone());
137142
} else if (this.getPlane() == null && polygons.isEmpty()) {
138143

139-
//com.neuronrobotics.sdk.common.Log.error("Please fix me! I don't know what to do?");
140-
throw new RuntimeException("Please fix me! Plane = "+plane+" and polygons are empty");
144+
// com.neuronrobotics.sdk.common.Log.error("Please fix me! I don't know what to
145+
// do?");
146+
throw new RuntimeException("Please fix me! Plane = " + plane + " and polygons are empty");
141147
// return;
142148
}
143149

@@ -164,18 +170,17 @@ public void invert() {
164170
*
165171
* @return the cliped list of polygons
166172
*/
167-
private List<Polygon> clipPolygons(List<Polygon> polygons) {
173+
private ArrayList<Polygon> clipPolygons(ArrayList<Polygon> polygons) {
168174

169175
if (this.getPlane() == null) {
170176
return new ArrayList<>(polygons);
171177
}
172178

173-
List<Polygon> frontP = new ArrayList<>();
174-
List<Polygon> backP = new ArrayList<>();
179+
ArrayList<Polygon> frontP = new ArrayList<>();
180+
ArrayList<Polygon> backP = new ArrayList<>();
181+
182+
splitPolygon(polygons, frontP, backP, frontP, backP);
175183

176-
for (Polygon polygon : polygons) {
177-
this.getPlane().splitPolygon(polygon, frontP, backP, frontP, backP);
178-
}
179184
if (this.front != null) {
180185
frontP = this.front.clipPolygons(frontP);
181186
}
@@ -189,6 +194,138 @@ private List<Polygon> clipPolygons(List<Polygon> polygons) {
189194
return frontP;
190195
}
191196

197+
/**
198+
* Splits a {@link Polygon} by this plane if needed. After that it puts the
199+
* polygons or the polygon fragments in the appropriate lists ({@code front},
200+
* {@code back}). Coplanar polygons go into either {@code coplanarFront},
201+
* {@code coplanarBack} depending on their orientation with respect to this
202+
* plane. Polygons in front or back of this plane go into either {@code front}
203+
* or {@code back}.
204+
*
205+
* @param polygon polygon to split
206+
* @param coplanarFront "coplanar front" polygons
207+
* @param coplanarBack "coplanar back" polygons
208+
* @param front front polygons
209+
* @param back back polgons
210+
*/
211+
public void splitPolygon(ArrayList<Polygon> polygons, List<Polygon> coplanarFront, List<Polygon> coplanarBack,
212+
List<Polygon> front, List<Polygon> back) {
213+
for (int k = 0; k < polygons.size(); k++) {
214+
Polygon polygon = polygons.get(k);
215+
216+
217+
// search for the epsilon values of the incoming plane
218+
double negEpsilon = -Plane.getEPSILON();
219+
double posEpsilon = Plane.getEPSILON();
220+
for (int i = 0; i < polygon.getVertices().size(); i++) {
221+
double t = polygon.getPlane().getNormal().dot(polygon.getVertices().get(i).pos)
222+
- polygon.getPlane().getDist();
223+
if (t > posEpsilon) {
224+
// com.neuronrobotics.sdk.common.Log.error("Non flat polygon, increasing
225+
// positive epsilon "+t);
226+
posEpsilon = t + Plane.getEPSILON();
227+
}
228+
if (t < negEpsilon) {
229+
// com.neuronrobotics.sdk.common.Log.error("Non flat polygon, decreasing
230+
// negative epsilon "+t);
231+
negEpsilon = t - Plane.getEPSILON();
232+
}
233+
}
234+
int polygonType = 0;
235+
List<Integer> types = new ArrayList<>();
236+
boolean somePointsInfront = false;
237+
boolean somePointsInBack = false;
238+
for (int i = 0; i < polygon.getVertices().size(); i++) {
239+
double t = this.getPlane().getNormal().dot(polygon.getVertices().get(i).pos)
240+
- this.getPlane().getDist();
241+
int type = (t < negEpsilon) ? BACK : (t > posEpsilon) ? FRONT : COPLANAR;
242+
if (type == BACK)
243+
somePointsInBack = true;
244+
if (type == FRONT)
245+
somePointsInfront = true;
246+
types.add(type);
247+
}
248+
if (somePointsInBack && somePointsInfront)
249+
polygonType = SPANNING;
250+
else if (somePointsInBack) {
251+
polygonType = BACK;
252+
} else if (somePointsInfront)
253+
polygonType = FRONT;
254+
255+
// Put the polygon in the correct list, splitting it when necessary.
256+
switch (polygonType) {
257+
case COPLANAR:
258+
(this.getPlane().getNormal().dot(polygon.getPlane().getNormal()) > 0 ? coplanarFront : coplanarBack)
259+
.add(polygon);
260+
break;
261+
case FRONT:
262+
front.add(polygon);
263+
break;
264+
case BACK:
265+
back.add(polygon);
266+
break;
267+
case SPANNING:
268+
List<Vertex> f = new ArrayList<>();
269+
List<Vertex> b = new ArrayList<>();
270+
for (int i = 0; i < polygon.getVertices().size(); i++) {
271+
int j = (i + 1) % polygon.getVertices().size();
272+
int ti = types.get(i);
273+
int tj = types.get(j);
274+
Vertex vi = polygon.getVertices().get(i);
275+
Vertex vj = polygon.getVertices().get(j);
276+
if (ti != BACK) {
277+
f.add(vi);
278+
}
279+
if (ti != FRONT) {
280+
b.add(ti != BACK ? vi.clone() : vi);
281+
}
282+
if ((ti | tj) == SPANNING) {
283+
double t = (this.getPlane().getDist() - this.getPlane().getNormal().dot(vi.pos))
284+
/ this.getPlane().getNormal().dot(vj.pos.minus(vi.pos));
285+
Vertex v = vi.interpolate(vj, t);
286+
f.add(v);
287+
b.add(v.clone());
288+
}
289+
}
290+
if (f.size() >= 3) {
291+
try {
292+
Polygon fpoly = new Polygon(f, polygon.getStorage(), false, polygon.getPlane())
293+
.setColor(polygon.getColor());
294+
add(front, fpoly);
295+
} catch (Exception ex) {
296+
System.err.println("Pruning bad polygon Plane::splitPolygon");
297+
// skip adding broken polygon here
298+
}
299+
} else {
300+
// com.neuronrobotics.sdk.common.Log.error("Front Clip Fault!");
301+
}
302+
if (b.size() >= 3) {
303+
try {
304+
Polygon bpoly = new Polygon(b, polygon.getStorage(), false, polygon.getPlane())
305+
.setColor(polygon.getColor());
306+
add(back, bpoly);
307+
} catch (Exception ex) {
308+
// ex.printStackTrace();
309+
System.err.println("Pruning bad polygon Plane::splitPolygon");
310+
}
311+
} else {
312+
// com.neuronrobotics.sdk.common.Log.error("Back Clip Fault!");
313+
}
314+
break;
315+
}
316+
}
317+
}
318+
319+
private static void add(List<Polygon> l, Polygon p) {
320+
try {
321+
// test triangulation of new polygon before adding
322+
PolygonUtil.concaveToConvex(p);
323+
l.add(p);
324+
} catch (Exception ex) {
325+
ex.printStackTrace();
326+
}
327+
}
328+
192329
// Remove all polygons in this BSP tree that are inside the other BSP tree
193330
// `bsp`.
194331
/**
@@ -214,8 +351,8 @@ public void clipTo(Node bsp) {
214351
*
215352
* @return a list of all polygons in this BSP tree
216353
*/
217-
public List<Polygon> allPolygons() {
218-
List<Polygon> localPolygons = new ArrayList<>(this.polygons);
354+
public ArrayList<Polygon> allPolygons() {
355+
ArrayList<Polygon> localPolygons = new ArrayList<>(this.polygons);
219356
if (this.front != null) {
220357
localPolygons.addAll(this.front.allPolygons());
221358
// polygons = Utils.concat(polygons, this.front.allPolygons());
@@ -236,8 +373,8 @@ public List<Polygon> allPolygons() {
236373
*
237374
* @param polygons polygons used to build the BSP
238375
*/
239-
public final void build(List<Polygon> polygons) {
240-
build(polygons, 0,polygons.size() );
376+
public final void build(ArrayList<Polygon> polygons) {
377+
build(polygons, 0, polygons.size());
241378
}
242379

243380
/**
@@ -248,7 +385,7 @@ public final void build(List<Polygon> polygons) {
248385
*
249386
* @param polygons polygons used to build the BSP
250387
*/
251-
public final void build(List<Polygon> polygons, long depth, long maxDepth) {
388+
public final void build(ArrayList<Polygon> polygons, long depth, long maxDepth) {
252389
// if (depth > maxDepth) {
253390
// throw new RuntimeException("Impossible Node depth " + depth + " with " + polygons.size() + " remaining max = "+maxDepth );
254391
// }
@@ -267,26 +404,26 @@ public final void build(List<Polygon> polygons, long depth, long maxDepth) {
267404
if (this.getPlane() == null) {
268405
this.setPlane(polygons.get(0).getPlane().clone());
269406
}
270-
//this.polygons.add(polygons.get(0));
407+
// this.polygons.add(polygons.get(0));
271408

272-
List<Polygon> frontP = new ArrayList<>();
273-
List<Polygon> backP = new ArrayList<>();
409+
ArrayList<Polygon> frontP = new ArrayList<>();
410+
ArrayList<Polygon> backP = new ArrayList<>();
274411

275412
// parellel version does not work here
276-
for(int i=0;i<polygons.size();i++) {
277-
this.getPlane().splitPolygon(polygons.get(i), this.polygons, this.polygons, frontP, backP);
278-
}
413+
414+
splitPolygon(polygons, this.polygons, this.polygons, frontP, backP);
415+
279416
if (frontP.size() > 0) {
280417
if (this.front == null) {
281418
this.front = new Node();
282419
}
283-
this.front.build(frontP, depth + 1,maxDepth);
420+
this.front.build(frontP, depth + 1, maxDepth);
284421
}
285422
if (backP.size() > 0) {
286423
if (this.back == null) {
287424
this.back = new Node();
288425
}
289-
this.back.build(backP, depth + 1,maxDepth);
426+
this.back.build(backP, depth + 1, maxDepth);
290427
}
291428
}
292429

@@ -295,7 +432,7 @@ public Plane getPlane() {
295432
}
296433

297434
public void setPlane(Plane plane) {
298-
if(plane==null)
435+
if (plane == null)
299436
throw new RuntimeException("Plane can not be null!");
300437
this.plane = plane;
301438
}

0 commit comments

Comments
 (0)