micycle1 / PGS

Processing Geometry Suite
https://micycle1.github.io/PGS/
165 stars 13 forks source link

Conversion: handle multi-contour/shape PShapes #67

Closed micycle1 closed 1 year ago

micycle1 commented 1 year ago

In converting from PShapes to Geometries, it was assumed until now that in shapes having multiple contours, every contour beyond the first must represent a hole.

Here's one counter-example to this assumption, where each band of the letter is not a child shape, but a contour:

image

To convert such shapes properly the code must check the winding of each contour: if it winds in the opposing (usually counter-clockwise) direction to the first contour, it represents a hole; if it winds in the same direction as the first contour, it represents another shape feature and should be preserved.

micycle1 commented 1 year ago

This has been quite a tricky issue to solve. I have developed a good solution that covers tricky cases (see below), but it's hard to say whether it can perfectly convert every such shape, owing to ambiguities in how PShapes are represented.

The solution iterates over the shape's contours. The orientation of each contour is measured to determine whether the contour represents a hole (orientated opposite to previous polygon exterior) or an additional polygon exterior (orientated in same direction to previous contour).

Now ideally, the process described above would be sufficient, as a hole that succeeds any given polygon exterior would belong to that very exterior. However, contours in valid PShapes are not necessarily ordered in this fashion: it is possible to have four contours: a,b,1,2; where the exteriors (a & b) are followed by two holes (1 & 2), where hole 1 belongs to a and hole 2 belongs to b. Hence (to be really thorough), we must check that a hole is actually contained by the last contour, otherwise we associate it with the first exterior (working backwards) that contains it.

Yet even a solution combining the approaches above does not always work...

In the 'A' shape below for example, the first contour (index=0) is the one in red, which forms a hole in the dark blue contour. That is followed by the green contour which in turn forms a hole of the pink contour. In other words, contours representing holes may precede the exterior boundary they belong to!

image

To get around this, one technique is to first order the contours by their orientation (all clockwise contours first, assuming that is default polygon exterior ring orientation, followed by all counter-clockwise contours). Doing this allows us to successfully convert 'A':

image

At this stage we're left with only one problematic character of this particularly tricky font: 'P', which fails to convert properly.

image

Like 'A', P's contours had a strange ordering (no discernible order at all). So in desperation I tried a simple approach: reverse the order of the contours and re-run the approaches above. It fixed it, which completes the solution!

Before

image

After

image