Polygon Simplification

Basic simplification:

[1]:
import skgeom as sg
poly = sg.random_polygon(30, seed=3)
poly
_images/simplify_2_0.svg

simplify tries to remove a given portion of the vertices without changing the topology:

[2]:
sg.draw.draw(poly, facecolor="white", alpha=0.4)
res = sg.simplify(poly, 0.5)  # try to remove 50% of the vertices
sg.draw.draw(res, alpha=0.85)
_images/simplify_4_0.svg
[3]:
sg.draw.draw(poly, facecolor="red")
sg.simplify(poly, 0.2)  # try to remove 80% of the vertices
_images/simplify_5_0.svg

simplify works on Polygon, PolygonWithHoles and PolygonSet. You can also apply simplify on lists of polygons.

[4]:
poly_set = sg.PolygonSet([sg.random_polygon(10, seed=4), sg.random_polygon(40, seed=3)])
sg.draw.draw(poly_set, facecolor="red")
sg.simplify(poly_set, 0.5)
_images/simplify_7_0.svg

Simplification Modes

simplify currently supports three modes: * “ratio”: try to reduce number of vertices to a given ratio of the original count * “count”: try to reduce number of vertices to an absolute number * “sqeuclidean”: try to reduce number of vertices until the error (squared euclidean distance) becomes too big

[5]:
sg.draw.draw(poly, facecolor="red")
sg.simplify(poly, 0.5, "ratio")  # try to remove 50% of vertices
_images/simplify_10_0.svg
[6]:
sg.draw.draw(poly, facecolor="red")
sg.simplify(poly, 0.2, "sqeuclidean")  # try to remove while err (sq. distance) < 0.2
_images/simplify_11_0.svg

In general, all modes are targets that might not be met:

[7]:
len(poly.coords)
[7]:
30
[8]:
len(sg.simplify(poly, 0.5, "ratio").coords)
[8]:
21
[9]:
len(sg.simplify(poly, 0.25, "ratio").coords)
[9]:
18
[10]:
len(sg.simplify(poly, 5, "count").coords)
[10]:
17

Simplification and Topology

By default, simplify tries to preserve topology.

[11]:
from skgeom import boolean_set
poly = boolean_set.difference(
    sg.random_polygon(20, size=4, seed=4234),
    sg.random_polygon(20, size=1, seed=8421))[0]
poly
_images/simplify_19_0.svg

By preserving topology, features like holes will not get lost:

[12]:
poly2 = sg. simplify(poly, 0.2, "ratio")  # try to remove 50% of vertices
poly2
_images/simplify_21_0.svg

Turning off preserve_topology is much faster, but might destroy structural features:

[13]:
poly2 = sg. simplify(poly, 0.2, "ratio", preserve_topology=False)
poly2
_images/simplify_23_0.svg

preserve_topology implies limits to how much simplifcation can happen:

[14]:
poly2 = sg. simplify(poly, 1, "count")
poly2
_images/simplify_25_0.svg
[15]:
poly2 = sg.simplify(poly, 1, "count", preserve_topology=False)
poly2
_images/simplify_26_0.svg

More Topology Examples

[16]:
nested = [sg.random_polygon(15, shape="circle", size=r ** 3, seed=r * 5) for r in range(3, 10)]
[17]:
def draw_nested(nested):
    for p in reversed(nested):
        sg.draw.draw(p)

draw_nested(nested)
_images/simplify_29_0.svg
[18]:
draw_nested(sg.simplify(nested, 0.3))
_images/simplify_30_0.svg

with defaults, forms have been simplified while preserving topology:

[19]:
def n_vertices(nested):
    return sum(len(p) for p in nested)

(n_vertices(nested), n_vertices([p.outer_boundary() for p in sg.simplify(nested, 0.3)]))
[19]:
(105, 59)

same call without preserving topology:

[20]:
draw_nested(sg.simplify(nested, 0.3, preserve_topology=False))
_images/simplify_34_0.svg