Drawing a logo
Can planex2d draw its own logo? The example is planex2d’s mark, read from its SVG file: a Reuleaux triangle of width s = 74, the curve of constant width, split into the three curved faces of a tetrahedron seen from one corner and meshed into 27 curved triangles. Each triangle is three quadratic Bézier curves, and the outline is three arcs. planex2d draws every Bézier curve exactly as a B-spline of degree 2 and every arc as an arc, and the drawing is checked against what it should satisfy:
the outline is equally wide in every direction, and the triangles cover it without gaps or overlaps. Lengths are in the units of the SVG.

[1]:
import math
import re
import matplotlib.pyplot as plt
import planex as px
The path data
The SVG holds 30 paths. The d attribute of each is a list of drawing commands:
M x ymoves the pen to (x, y);Q cx cy x ydraws a quadratic Bézier curve to (x, y), with the control point (cx, cy);A r r 0 0 0 x ydraws an arc of radius r to (x, y);Zcloses the path.
The SVG’s y axis points down and planex2d’s up, so a point (x, y) of the SVG becomes (x, 100 − y). commands splits the d of a path into its commands, and curves gives its Bézier curves as (start, control point, end):
[3]:
svg = open("../_static/brand/planex2d-mark.svg").read()
paths = [dict(re.findall(r'([a-z-]+)="([^"]*)"', tag)) for tag in re.findall(r"<path [^>]*>", svg)]
def point(x, y):
return (x, 100 - y)
def commands(d):
return [(letter, [float(v) for v in args.split()]) for letter, args in re.findall(r"([MQA])([^MQAZ]*)", d)]
def curves(d):
pen, out = None, []
for letter, values in commands(d):
if letter == "M":
pen = point(*values)
else:
out.append((pen, point(*values[:2]), point(*values[2:])))
pen = out[-1][2]
return out
triangle_paths = [p for p in paths if p["fill"] != "none"]
mesh_lines = next(p for p in paths if p["stroke"] == "#ffffff")
dividers = next(p for p in paths if p["fill"] == "none" and "Q" in p["d"] and p["stroke"] != "#ffffff")
rim = next(p for p in paths if "A" in p["d"])
print(len(triangle_paths), "triangles, the mesh lines, the dividers and the outline")
print("outline:", rim["d"])
27 triangles, the mesh lines, the dividers and the outline
outline: M50 12.3A74 74 0 0 0 13 76.4A74 74 0 0 0 87 76.4A74 74 0 0 0 50 12.3Z
The outline
The outline is a Reuleaux triangle: three arcs of radius 74, each about the corner opposite to it. Arc(end, r) draws an arc of radius r from the pen to the end; without reverse, it takes the short way between two corners (Figure 1).
[4]:
start, *arcs = commands(rim["d"])
sketch = px.QuickSketch(point(*start[1]))
for _, values in arcs:
sketch.Arc(point(*values[-2:]), values[0])
outline = sketch.Face()
s = arcs[0][1][0]
corners = [point(*values[-2:]) for _, values in arcs]
Check: a Reuleaux triangle is as wide in every direction as its arcs’ radius, like a circle, and its area is (π − √3)/2 s². Turned in steps of 5° and measured across, the outline is 74 wide to within the 0.1 to which the SVG rounds its points. The same rounding makes two of its sides 74.01 long, and its area a little larger than the exact one:
[6]:
widths = []
for angle in range(0, 360, 5):
(x0, _), (x1, _) = outline.Rotated((50, 50), angle).bounding_box
widths.append(x1 - x0)
print("width from", min(widths), "to", max(widths))
print("area", outline.mass, "exact", (math.pi - math.sqrt(3)) / 2 * s**2)
width from 74.00000269399769 to 74.01412011995154
area 3860.3705784051845 exact 3859.3255744052676
The curved triangles
A quadratic Bézier curve is the B-spline of degree 2 with the curve’s three points as poles and the knots 0, 0, 0, 1, 1, 1, so px.BSpline draws it exactly. Each triangle is the face bounded by its three curves, in its color from the SVG (Figure 2).
[7]:
def bezier(start, control, end):
return px.BSpline([start, control, end], [0, 0, 0, 1, 1, 1], degree=2)
triangles = [px.Face([bezier(*c) for c in curves(p["d"])]) for p in triangle_paths]
colors = [p["fill"] for p in triangle_paths]

Check:
The triangles neither overlap nor leave gaps: their areas add up to the area of their union, which is one face.
Glued, they share their edges exactly: 27 faces, 45 edges and 19 vertices. A curved triangle of order 2 carries a node in the middle of each edge, so the mesh has 19 + 45 = 64 nodes, as the brand panel says.
The rim of the triangles is made of Bézier curves, not arcs, and it follows the outline closely: the area between the two is less than a thousandth of the mark’s.
[9]:
union = px.Unify(triangles)
print("sum of the areas", sum(t.mass for t in triangles), "union", union.mass, "in", len(union.faces), "face")
glued = px.Glue(triangles)
print(len(glued.faces), "faces,", len(glued.edges), "edges,", len(glued.vertices), "vertices,",
len(glued.vertices) + len(glued.edges), "nodes of order 2")
print("between the rim and the outline", (outline - union).mass + (union - outline).mass)
sum of the areas 3860.826418203769 union 3860.8264182040425 in 1 face
27 faces, 45 edges, 19 vertices, 64 nodes of order 2
between the rim and the outline 2.9542237009093966
The mark
The mark is drawn in the SVG’s own colors and line widths: each triangle filled and edged in its color, the mesh lines white at 60 % opacity, and the dividers and the outline in ink, over the SVG’s view box of 78 × 78.
[10]:
(np.float64(11.0), np.float64(89.0), np.float64(11.7), np.float64(89.7))
See Splines for B-splines given by their poles and knots, and Booleans for unions and glued faces.