Layout geometry
Label placement runs after everything else is drawn and uses display
coordinates: pixels, with \(y\) increasing upward. The package
mosaickit.layout contains this geometry and imports nothing from the
renderers, so it can be tested and reused independently. The functions in
this chapter are in mosaickit.layout.geometry; Rect and polylabel are
also exported from mosaickit.layout. A point is a pair (x, y), a segment
is a pair of points, and a polygon is a sequence of points whose last point
joins the first.
Rectangles
Rect(
x0,
y0,
x1,
y1
)
The closed axis-aligned rectangle \([x_0, x_1] \times [y_0, y_1]\).
Rect.centered(center, width, height) builds one around a point. It has
width, height, center, inflate(pad), corners() (counter-clockwise
from \((x_0, y_0)\)), edges(), contains(point) (edges included),
within(other), intersects(other) (touching counts) and
nearest_point(point).
Lemma · Nearest point of a rectangle
For a rectangle \(R\) and a point \(p\), nearest_point returns
This is the unique point of \(R\) closest to \(p\).
Callout leaders end at this point, so a leader is as short as the label's position allows.
Orientation and segments
Definition · Orientation
For points \(a, b, c\) in the plane,
the cross product of \(b - a\) and \(c - a\).
Lemma · Orientation test
\(\text{orient}(a, b, c)\) is positive when \(c\) lies to the left of the directed line from \(a\) to \(b\) (the turn \(a \to b \to c\) is counter-clockwise), negative when it lies to the right, and zero when \(a\), \(b\), \(c\) are collinear. It is invariant under cyclic permutation and changes sign when two arguments are swapped.
segments_intersect(
a,
b,
c,
d
) -> bool
Whether the closed segments \([a, b]\) and \([c, d]\) share a point. With \(d_1 = \text{orient}(c, d, a)\), \(d_2 = \text{orient}(c, d, b)\), \(d_3 = \text{orient}(a, b, c)\), \(d_4 = \text{orient}(a, b, d)\), it answers yes when \(d_1 d_2 < 0\) and \(d_3 d_4 < 0\), or when some \(d_i\) is zero and its point lies in the bounding box of the other segment Cormen (2009).
Theorem · Segment intersection
In exact arithmetic, segments_intersect(a, b, c, d) is true if and only
if \([a, b] \cap [c, d] \ne \emptyset\).
rect_hits_segment(
rect,
a,
b
) -> bool
Whether the segment touches the rectangle: an endpoint lies in it, or the segment meets one of its four edges.
Lemma · Segments and rectangles
rect_hits_segment(R, a, b) is true if and only if \([a, b] \cap R \ne \emptyset\).
Polygons
Definition · Simple polygon
A polygon \(P = (p_0, \ldots, p_{m-1})\), \(m \ge 3\), is simple when its edges \([p_i, p_{i+1}]\) (indices mod \(m\)) meet only at shared endpoints of consecutive edges and its vertices are not all collinear. Its boundary \(\partial P\) is the union of the edges; by the Jordan curve theorem the complement of \(\partial P\) has one bounded component, the interior \(\text{int} P\), and one unbounded one, the exterior. We write \(\overline{P} = \text{int} P \cup \partial P\).
polygon_edges(polygon) -> tuple[Segment, ...]
distance_to_boundary(point, polygon) -> float
The edges, last vertex to first included, and the Euclidean distance from a point to the nearest edge. Each edge distance projects the point onto the edge's line and clamps the parameter to \([0, 1]\).
Lemma · Distance to a segment
For \(a \ne b\), let
Then \(a + t^* (b - a)\) is the point of \([a, b]\) nearest to \(p\).
point_in_polygon(point, polygon) -> bool
The even–odd rule: follow the horizontal ray from the point to the right and count the edges it crosses. An edge counts when exactly one of its endpoints lies strictly above the ray's line and the crossing is right of the point Haines (1994).
Proposition · Even-odd rule
Let \(P\) be simple and \(p \notin \partial P\). Then
point_in_polygon(p, P) is true if and only if \(p \in \text{int} P\).
rect_inside_polygon(rect, polygon) -> bool
rect_overlaps_polygon(rect, polygon) -> bool
Inside: all four corners pass the even-odd test and no edge of the polygon touches the rectangle. Overlaps: some corner passes, or some edge touches the rectangle (which also catches a polygon lying wholly inside it).
Theorem · Rectangles in polygons
Let \(P\) be simple and \(R\) a rectangle. Then rect_inside_polygon(R, P) is
true if and only if \(R \subset \text{int} P\).
Corollary · Rectangles overlapping polygons
Let \(P\) be simple and \(R\) a rectangle. Then rect_overlaps_polygon(R, P)
is true if and only if \(R \cap \overline{P} \ne \emptyset\).
ray_exit(
origin,
direction,
polygon
) -> float
The largest \(t \ge 0\) at which the ray \(o + t r\) meets an edge that is not parallel to it, or \(0\) if there is none. Callouts use it to start their search where the ray leaves the region.
Proposition · Leaving a polygon for good
Let \(P\) be simple, \(r \ne 0\) and \(T =\) ray_exit(o, r, P). Then
\(o + t r \notin \overline{P}\) for every \(t > T\).
The visual centre of a region
The centroid of a region can lie outside it (Pole, largest disc and centroid.). A label belongs at the deepest interior point, which is farthest from the boundary.
Definition · Signed distance and pole
For a simple polygon \(P\) the signed distance is \(f(p) = d(p, \partial P)\) for \(p \in \overline{P}\) and \(f(p) = -d(p, \partial P)\) otherwise. A pole of inaccessibility of \(P\) is a point where \(f\) attains its maximum \(f^*\), the radius of the largest disc inside \(P\).
Lemma · Signed distance is 1-Lipschitz
For all points \(p, q\), \(|f(p) - f(q)| \le |p - q|\).
polylabel(polygon, precision=1.0) -> Point
A best-first search over square cells Agafonkin (2016). The
bounding box is covered with squares as wide as its shorter side. Each
cell with centre \(c\) and half-size \(h\) is given the bound
\(f(c) + h \sqrt{2}\) and kept in a priority queue, largest bound first. The
best centre seen so far is remembered, starting with the centre of the
bounding box; a popped cell whose bound exceeds the best value by more
than precision is split into four, the others are dropped. A polygon
with a zero-width or zero-height bounding box returns its first vertex.
Theorem · Pole of inaccessibility
Let \(P\) be simple, with a bounding box of positive width and height, and
let \(\epsilon > 0\) be the precision. Then polylabel terminates and
returns a point \(q\) with \(f(q) \ge f^* - \epsilon\). In particular \(q\) lies
in \(\text{int} P\) whenever \(f^* > \epsilon\).
For a triangle the pole is the incentre. Placement calls polylabel with
the default precision of one pixel, finer than any text-placement increment.
Brace outlines
brace_outline(
start,
end,
*,
base,
depth,
direction,
axis="y",
samples=12
)
A curly brace over start..end, as a Brace(points, tip). Its ends
lie on the line base (a constant across coordinate), and the tip sits at
the middle of the span, depth past base on the side direction
(\(\pm 1\)). On axis "y" points are (across, along); on "x" they
are (along, across). Each quarter arc is sampled with samples segments.
An empty span, a non-positive depth or another direction or axis raise
ValueError.
The outline is built in a local frame, \(u\) across and \(v\) along, from four quarter circles of radius \(r = \min(\text{depth}/2, (\text{end} - \text{start})/4)\) joined by two straight pieces, and then stretched across by \(\text{depth} / 2r\), so a short span gets small arcs but the tip still reaches the full depth (Brace outlines; the right span is under \(2 \delta\).).
Proposition · Shape of a brace
Let \(\ell < h\) be the ends of the span, \(m = (\ell + h) / 2\) its middle and \(\delta\) the depth. In the local frame, before sampling, the outline is a curve from \((0, \ell)\) to \((0, h)\) through the tip \((\delta, m)\) that (i) stays in the strip \(0 \le u \le \delta\), (ii) is symmetric under \(v |\to \ell + h - v\), and (iii) has a continuous tangent everywhere except at the tip, where it turns back on itself (a cusp).