Search code examples
pythonshapely

How do I create drawing elements by levels?


Input data format:

obj_1 = [(x1, y1, z1, x2, y2, z2), (...),...]
obj_2 = [(x1, y1, z1, x2, y2, z2), (...),...]
etc.

https://pastebin.com/EiZZEeQR

The task is to find a way to create new objects under old ones according to the order in which the creation function is called. In this case, we start by creating a obj_1, then a obj_2, which will hide under the obj_1. Then the obj_3 is created, and consequently this object is hidden under the first and second objects.

Desired output data - coordinates of all lines for drawing the drawing

pic

Maybe i need to use the shapely library, but I'm not sure.

The shape of an object can be absolutely any, so it is not quite correct to write code based only on circles, triangles and lines.


Solution

  • from itertools import chain
    
    from matplotlib import pyplot as plt
    from more_itertools import batched
    from shapely import Point, LineString, Polygon, MultiPoint, MultiLineString
    from shapely.plotting import plot_polygon, plot_line, plot_points
    

    Declare objects like in file given by OP:

    obj_1 = [(0.3931383333340818, 1.1771275460626884, 0.0, 
    

    Parsing data into shapely objects:

    shapely_objects = []
    for obj in chain(obj_3, obj_1, obj_2):
        n = len(obj) / 3
        if n == 0:
            continue
        elif n == 1:
            geom = Point(obj[0])
        else:
            data = list(batched(obj, 3, strict=True))
            geom = LineString(data)
            if geom.is_ring:
                geom = Polygon(data)
        shapely_objects.append(geom)
    

    Actual processing:

    def overlay(geometries: Iterable[BaseGeometry]):
        """
        Overlay shapely geometries: cut away the part of shapes that are underneath others.
    
        First features in input will be underneath, last feature will not be cut.
        """
        result = []
        s = set()
        tree = STRtree(geometries)
        for idx, current_feature in enumerate(geometries):
            s.add(idx)
            overlapping_features = tree.geometries.take(list(set(tree.query(current_feature)) - s))
            cut = current_feature.difference(unary_union(overlapping_features))
            result.append(cut)
        return result
    
    shapes_overlain = overlay(shapely_objects)
    

    Visualization:

    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True)
    for shape in shapely_objects:
        if isinstance(shape, Polygon):
            plot_polygon(shape, color="w", add_points=False, ax=ax1)
        elif isinstance(shape, LineString):
            plot_line(shape, color="w", add_points=False, ax=ax1)
        else:
            plot_points(shape, color="w", add_points=False, ax=ax1)
    
    for shape in shapes_overlain:
        if isinstance(shape, Polygon):
            plot_polygon(shape, color="w", add_points=False, ax=ax2)
        elif isinstance(shape, (MultiLineString, LineString)):
            plot_line(shape, color="w", add_points=False, ax=ax2)
        elif isinstance(shape, (Point, MultiPoint)):
            plot_points(shape, color="w", add_points=False, ax=ax2)
        else:
            raise ValueError(f"shape: {shape}")
    
    
    ax1.set(facecolor="#242c34", xticks=[], yticks=[])
    ax2.set(facecolor="#242c34", xticks=[], yticks=[])
    ax1.set_title("Normal")
    ax2.set_title("Overlain")
    plt.show()
    

    enter image description here