Polygons split example

This example demonstrates how to create a dynamic tiling of polygons on a PowerPoint slide. It uses the Polygon and Group shapes, as well as FillStyle and StrokeStyle from the pptx_shapes library.

The script defines an iterative polygon-splitting algorithm. Starting with a single square, it repeatedly splits polygons using lines and adds the result as groups of colored polygons to a slide.

Overview

  • Start from a single square polygon

  • Iteratively split polygons using defined lines

  • Assign new colors to sub-polygons after each split

  • Map logical coordinates to slide coordinates

  • Add results as grouped shapes to the slide

Example Code

  1from typing import List, Tuple
  2
  3from pptx_shapes import Presentation
  4from pptx_shapes.shapes import Group, Polygon
  5from pptx_shapes.style import FillStyle, StrokeStyle
  6
  7
  8def split_polygon(polygon: List[Tuple[float, float]], line: Tuple[float, float, float]) -> Tuple[List[Tuple[float, float]], List[Tuple[float, float]]]:
  9    polygon1, polygon2 = [], []
 10    a, b, c = line
 11
 12    for i, (x1, y1) in enumerate(polygon):
 13        x2, y2 = polygon[(i + 1) % len(polygon)]
 14
 15        sign1 = a * x1 + b * y1 + c
 16        sign2 = a * x2 + b * y2 + c
 17
 18        if sign1 <= 0:
 19            polygon1.append((x1, y1))
 20
 21        if sign1 >= 0:
 22            polygon2.append((x1, y1))
 23
 24        if sign1 * sign2 >= 0:
 25            continue
 26
 27        t = sign1 / (sign1 - sign2)
 28        x = x1 + t * (x2 - x1)
 29        y = y1 + t * (y2 - y1)
 30
 31        polygon1.append((x, y))
 32        polygon2.append((x, y))
 33
 34    return polygon1, polygon2
 35
 36
 37def mix_colors(color1: str, color2: str) -> str:
 38    r1, g1, b1 = color1[1:3], color1[3:5], color1[5:]
 39    r2, g2, b2 = color2[1:3], color2[3:5], color2[5:]
 40
 41    r = (int(r1, 16) + int(r2, 16)) // 2
 42    g = (int(g1, 16) + int(g2, 16)) // 2
 43    b = (int(b1, 16) + int(b2, 16)) // 2
 44
 45    return f"#{r:02X}{g:02X}{b:02X}"
 46
 47
 48def split_polygons(polygons: List[dict], line: Tuple[float, float, float]) -> List[dict]:
 49    new_polygons = []
 50
 51    for polygon in polygons:
 52        polygon1, polygon2 = split_polygon(polygon=polygon["points"], line=line)
 53
 54        if len(polygon1) > 2:
 55            new_polygons.append({"points": polygon1, "color": mix_colors(polygon["color"], "#dd7373")})
 56
 57        if len(polygon2) > 2:
 58            new_polygons.append({"points": polygon2, "color": mix_colors(polygon["color"], "#7699d4")})
 59
 60    return new_polygons
 61
 62
 63def view_points(points: List[Tuple[float, float]], limits: dict, x0: float, y0: float, width: float, height: float) -> List[Tuple[float, float]]:
 64    mapped_points = []
 65
 66    for x, y in points:
 67        x = x0 + (x - limits["x_min"]) / (limits["x_max"] - limits["x_min"]) * width
 68        y = y0 + (limits["y_max"] - y) / (limits["y_max"] - limits["y_min"]) * height
 69        mapped_points.append((x, y))
 70
 71    return mapped_points
 72
 73
 74def main() -> None:
 75    lines = [
 76        (0.04, 0.3, -0.01),
 77        (-0.75, 0.1, -0.97),
 78        (-0.14, 0.9, 0.96),
 79        (1.14, 0.18, -1.05),
 80        (1.27, -0.07, 0.04),
 81        (-0.2, 0.24, -0.15),
 82        (0.35, 1.34, -0.96),
 83        (0.26, -0.9, -0.54)
 84    ]
 85
 86    limits = {"x_min": -1.7, "y_min": -1.7, "x_max": 1.7, "y_max": 1.7}
 87    polygons = [
 88        {"points": [(-1.7, -1.7), (-1.7, 1.7), (1.7, 1.7), (1.7, -1.7)], "color": "#ffffff"}
 89    ]
 90
 91    x0, y0 = 1, 1.5
 92    size = 7.5
 93    gap = 0.5
 94    columns = 4
 95
 96    with Presentation(presentation_path="empty.pptx") as presentation:
 97        for i, line in enumerate(lines):
 98            polygons = split_polygons(polygons=polygons, line=line)
 99            x = x0 + (size + gap) * (i % columns)
100            y = y0 + (size + gap) * (i // columns)
101
102            shapes = []
103            for polygon in polygons:
104                points = view_points(points=polygon["points"], limits=limits, x0=x, y0=y, width=size, height=size)
105                shapes.append(Polygon(points=points, fill=FillStyle(color=polygon["color"]), stroke=StrokeStyle(color="#222", thickness=0.5)))
106
107            presentation.add(shape=Group(shapes=shapes))
108
109        presentation.save("polygons.pptx")
110
111
112if __name__ == "__main__":
113    main()

Result

This example produces a grid of polygon patterns, each illustrating the effect of applying a new splitting line.

Basic example

Each shape is created using:

Note

Logical coordinates are automatically transformed to slide coordinates using the provided limits and size parameters.

Output file: polygons.pptx