14
14
import java .util .Map ;
15
15
import java .util .Set ;
16
16
import java .util .stream .Collectors ;
17
+ import java .util .stream .Stream ;
17
18
18
19
import javax .vecmath .Point3d ;
19
20
@@ -234,15 +235,26 @@ public static PShape chordalAxis(PShape shape) {
234
235
* consisting of straight-line segments only. Roughly, it is the geometric graph
235
236
* whose edges are the traces of vertices of shrinking mitered offset curves of
236
237
* the polygon.
237
- *
238
+ * <p>
239
+ * For a single polygon, this method returns a GROUP PShape containing three
240
+ * children:
241
+ * <ul>
242
+ * <li>Child 0: GROUP PShape consisting of skeleton faces.</li>
243
+ * <li>Child 1: LINES PShape representing branches, which are lines connecting
244
+ * the skeleton to the polygon's edge.</li>
245
+ * <li>Child 2: LINES PShape composed of bones, depicting the pure straight
246
+ * skeleton of the polygon.</li>
247
+ * </ul>
248
+ * <p>
249
+ * For multi-polygons, the method returns a master GROUP PShape. This master
250
+ * shape includes multiple skeleton GROUP shapes, each corresponding to a single
251
+ * polygon and structured as described above.
252
+ *
238
253
* @param shape a single polygon (that can contain holes), or a multi polygon
239
254
* (whose polygons can contain holes)
240
- * @return when the input is a single polygon, returns a GROUP PShape containing
241
- * 3 children: child 1 = GROUP PShape of skeleton faces; child 2 = LINES
242
- * PShape of branches (lines that connect skeleton to edge); child 3 =
243
- * LINES PShape of bones (the pure straight skeleton). For
244
- * multi-polygons, a master GROUP shape of skeleton GROUP shapes
245
- * (described above) is returned.
255
+ *
256
+ * @return PShape based on the input polygon structure, either as a single or
257
+ * multi-polygon skeleton representation.
246
258
*/
247
259
public static PShape straightSkeleton (PShape shape ) {
248
260
final Geometry g = fromPShape (shape );
@@ -257,25 +269,7 @@ public static PShape straightSkeleton(PShape shape) {
257
269
return shape ;
258
270
}
259
271
260
- /**
261
- *
262
- * @param polygon a single polygon that can contain holes
263
- * @return
264
- */
265
272
private static PShape straightSkeleton (Polygon polygon ) {
266
- /*
267
- * Kenzi implementation (since PGS 1.3.0) is much faster (~50x!) but can fail on
268
- * more complicated inputs. Therefore try Kenzi implementation first, but fall
269
- * back to Twak implementation if it fails.
270
- */
271
- try {
272
- return straightSkeletonKendzi (polygon );
273
- } catch (Exception e ) {
274
- return straightSkeletonTwak (polygon );
275
- }
276
- }
277
-
278
- private static PShape straightSkeletonTwak (Polygon polygon ) {
279
273
if (polygon .getCoordinates ().length > 1000 ) {
280
274
polygon = (Polygon ) DouglasPeuckerSimplifier .simplify (polygon , 2 );
281
275
}
@@ -303,7 +297,7 @@ private static PShape straightSkeletonTwak(Polygon polygon) {
303
297
skeleton .skeleton (); // compute skeleton
304
298
305
299
skeleton .output .faces .values ().forEach (f -> {
306
- final List <Point3d > vertices = f .getLoopL ().iterator ().next ().asList ();
300
+ List <Point3d > vertices = f .getLoopL ().iterator ().next ().stream (). toList ();
307
301
List <PVector > faceVertices = new ArrayList <>();
308
302
309
303
for (int i = 0 ; i < vertices .size (); i ++) {
@@ -352,95 +346,20 @@ private static PShape straightSkeletonTwak(Polygon polygon) {
352
346
return lines ;
353
347
}
354
348
355
- private static PShape straightSkeletonKendzi (Polygon polygon ) {
356
- final LinearRing [] rings = new LinearRingIterator (polygon ).getLinearRings ();
357
- Set <Vector2dc > edgeCoordsSet = new HashSet <>();
358
- final List <Vector2dc > points = ringToVec (rings [0 ], edgeCoordsSet );
359
- final List <List <Vector2dc >> holes = new ArrayList <>();
360
- for (int i = 1 ; i < rings .length ; i ++) {
361
- holes .add (ringToVec (rings [i ], edgeCoordsSet ));
362
- }
363
-
364
- final SkeletonOutput so = kendzi .math .geometry .skeleton .Skeleton .skeleton (points , holes , new SkeletonConfiguration ());
365
- final PShape skeleton = new PShape (PConstants .GROUP );
366
- final PShape faces = new PShape (PConstants .GROUP );
367
- /*
368
- * Create PEdges first to prevent lines being duplicated in output shapes since
369
- * faces share branches and bones.
370
- */
371
- final Set <PEdge > branchEdges = new HashSet <>();
372
- final Set <PEdge > boneEdges = new HashSet <>();
373
- so .getFaces ().forEach (f -> {
374
- /*
375
- * q stores the index of second vertex of the face that is a shape vertex. This
376
- * is used to rotate f.getPoints() so that the vertices of every face PShape
377
- * begin at the shape edge.
378
- */
379
- int q = 0 ;
380
- for (int i = 0 ; i < f .getPoints ().size (); i ++) {
381
- final Vector2dc p1 = f .getPoints ().get (i );
382
- final Vector2dc p2 = f .getPoints ().get ((i + 1 ) % f .getPoints ().size ());
383
- final boolean a = edgeCoordsSet .contains (p1 );
384
- final boolean b = edgeCoordsSet .contains (p2 );
385
- if (a ^ b ) { // branch (xor)
386
- branchEdges .add (new PEdge (p1 .x (), p1 .y (), p2 .x (), p2 .y ()));
387
- q = i ;
388
- } else {
389
- if (!a ) { // bone
390
- boneEdges .add (new PEdge (p1 .x (), p1 .y (), p2 .x (), p2 .y ()));
391
- } else {
392
- q = i ;
393
- }
394
- }
395
- }
396
-
397
- List <PVector > faceVertices = new ArrayList <>(f .getPoints ().size ());
398
- Collections .rotate (f .getPoints (), -q + 1 );
399
- f .getPoints ().forEach (p -> faceVertices .add (new PVector ((float ) p .x (), (float ) p .y ())));
400
-
401
- PShape face = PGS_Conversion .fromPVector (faceVertices );
402
- face .setStroke (true );
403
- face .setStrokeWeight (2 );
404
- face .setStroke (ColorUtils .composeColor (147 , 112 , 219 ));
405
- faces .addChild (face );
406
- });
407
-
408
- final PShape bones = prepareLinesPShape (null , null , 4 );
409
- boneEdges .forEach (e -> {
410
- bones .vertex (e .a .x , e .a .y );
411
- bones .vertex (e .b .x , e .b .y );
412
- });
413
- bones .endShape ();
414
-
415
- final PShape branches = prepareLinesPShape (ColorUtils .composeColor (40 , 235 , 180 ), null , null );
416
- branchEdges .forEach (e -> {
417
- branches .vertex (e .a .x , e .a .y );
418
- branches .vertex (e .b .x , e .b .y );
419
- });
420
- branches .endShape ();
421
-
422
- skeleton .addChild (faces );
423
- skeleton .addChild (branches );
424
- skeleton .addChild (bones );
425
-
426
- return skeleton ;
427
- }
428
-
429
349
/**
430
- * Generates a topographic-like isoline contour map from the shape's vertices.
431
- * The "elevation" (or z value) of points is the euclidean distance between a
432
- * point in the shape and the given "high" point.
350
+ * Generates a topographic-like isoline contour map from the shape's vertices
351
+ * and a given "high point". Isolines represent the "elevation", or euclidean
352
+ * distance, between a location in the shape and the "high point" .
433
353
* <p>
434
354
* Assigns each point feature a number equal to the distance between geometry's
435
355
* centroid and the point.
436
356
*
437
- * @param shape
357
+ * @param shape the bounds in which to draw isolines
438
358
* @param highPoint position of "high" point within the shape
439
359
* @param intervalSpacing distance between successive isolines
440
- * @return PShape containing isolines linework
360
+ * @return PShape containing isolines linework
441
361
*/
442
362
public static PShape isolines (PShape shape , PVector highPoint , double intervalSpacing ) {
443
-
444
363
/*
445
364
* Also See:
446
365
* https://github.com/hageldave/JPlotter/blob/master/jplotter/src/main/java/
0 commit comments