create.geometry¶
Shapes and curves, the traits that make a type drawable, and the collision tests: overlaps, and each shape's contains and intersects.
Rectangle¶
struct Rectangle
A rectangle, upright unless turned by rotation: center, area, bounds, left/right/bottom/top, closest_point, contains, move_to, translate.
Locations are Point2D and displacements Vector2D, throughout this
module; extents are plain scalars, like Circle's radius. Width and
height are two independent lengths rather than a coordinate pair, so
they are not grouped: read them as w/h and pair them however the
caller needs.
position is the centre, not a corner -- consistent with every shape in
this module and with canvas.rectangle. w/h are full width and
height, so left()/right()/bottom()/top() are +-w/2/+-h/2
from the centre. Coordinates follow world space: y grows upward, so
top() is position.y + h/2 and bottom() is position.y - h/2.
contains and closest_point treat the boundary as inside -- a point
exactly on an edge is contained, and closest_point returns it
unchanged. A zero w/h collapses the rectangle to a segment or a
point; contains/overlaps remain exact for it rather than reporting
a false containment or overlap. w/h are assumed non-negative.
corner_radius rounds all four corners, as canvas.rectangle draws
them, and every test here sees the rounded shape: a point in a sharp
corner's cut-away is outside. It is clamped to half the shorter side.
left()/right()/bottom()/top() stay the bounding box's.
rotation turns the rectangle about position, in radians
counter-clockwise (y is up), as canvas.transform(rotate(...)) turns
what it draws; w and h stay its own width and height. Every test
sees the turned shape, exactly, and left()/right()/bottom()/
top() and bounds() are its upright bounding box's. Drawn, it is the
upright rectangle turned, so a gradient fill, the rounding and the
stroke turn with it, while a shadow's offset stays screen-fixed unless
shadow_follows_transform is set, as for any turned shape.
Implements Copyable, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Region, Shape, Writable, _Collider.
Fields¶
position: Point2Dw: Float64h: Float64corner_radius: Float64: How far each corner is rounded, in the units ofw/h; 0 is sharp.rotation: Float64: How far the rectangle is turned aboutposition, in radians counter-clockwise; 0 is upright.
Constructors¶
@staticmethod
def __init__(
out self,
position: Point2D,
w: Float64,
h: Float64,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
rotation: _OptionalFloat64 = SIMD(0),
)
@staticmethod
def __init__(
out self,
position: Point2D,
w: Int,
h: Int,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
rotation: _OptionalFloat64 = SIMD(0),
)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
center¶
def center(self) -> Point2D
area¶
def area(self) -> Float64
bounds¶
def bounds(self) -> Self
The tightest axis-aligned rectangle around this one: upright, itself, sharp, since rounding never pulls in an edge's middle; turned, as far as its rounded corners reach along each axis.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
left¶
def left(self) -> Float64
right¶
def right(self) -> Float64
bottom¶
def bottom(self) -> Float64
top¶
def top(self) -> Float64
contains¶
def contains(self, p: Point2D) -> Bool
def contains(self, other: Self) -> Bool
def contains(self, c: Circle) -> Bool
def contains(self, t: Triangle) -> Bool
def contains(self, l: Line) -> Bool
def contains(self, c: Capsule) -> Bool
def contains[C: Curve](self, curve: C) -> Bool
Whether the whole of curve (an Arc, Bezier or Spline) is inside: every segment of it flattened to within 1e-4 of its size, as intersects follows it, so a curve grazing the edge from inside is decided only that closely.
def contains(self, s: Sector) -> Bool
def contains(self, p: Polygon) -> Bool
def contains(self, e: Ellipse) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Circle¶
struct Circle
A circle: center, area, diameter, closest_point, contains, move_to, translate.
position is the centre and r the radius -- there is no orientation, so
unlike Rectangle/Triangle there is nothing y-up affects beyond the
centre's own coordinates.
contains treats the boundary as inside (distance <= r), and
closest_point returns the query point itself when it is already
inside or exactly at the centre, rather than an arbitrary point on the
circumference. A zero r collapses the circle to a point; contains
then holds only for that exact point, and overlaps/intersects
remain exact rather than always-false. r is assumed non-negative.
Implements Copyable, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Region, Shape, Writable, _Collider.
Fields¶
position: Point2Dr: Float64
Constructors¶
@staticmethod
def __init__(out self, position: Point2D, r: Float64)
@staticmethod
def __init__(out self, position: Point2D, r: Int)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
center¶
def center(self) -> Point2D
area¶
def area(self) -> Float64
diameter¶
def diameter(self) -> Float64
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the circle.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
contains¶
def contains(self, p: Point2D) -> Bool
def contains(self, other: Self) -> Bool
def contains(self, r: Rectangle) -> Bool
def contains(self, t: Triangle) -> Bool
def contains(self, l: Line) -> Bool
def contains(self, c: Capsule) -> Bool
def contains[C: Curve](self, curve: C) -> Bool
Whether the whole of curve (an Arc, Bezier or Spline) is inside: every segment of it flattened to within 1e-4 of its size, as intersects follows it, so a curve grazing the edge from inside is decided only that closely.
def contains(self, s: Sector) -> Bool
def contains(self, p: Polygon) -> Bool
def contains(self, e: Ellipse) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Ellipse¶
struct Ellipse
An axis-aligned ellipse: center, area, bounds, closest_point, contains, move_to, translate.
position is the centre and w/h the full width and height, as for
Rectangle, whose bounds() it fills; a turned ellipse is drawn
through canvas.transform. With w == h it is the circle of diameter
w, and every test agrees with that Circle's.
contains treats the boundary as inside, and closest_point returns
the query point itself when it is inside. A zero w or h collapses
the ellipse to the segment (or point) its bounds() is, and every test
treats it as that zero-size Rectangle, exactly. w/h are assumed
non-negative.
An ellipse is a circle stretched along its axes, so a test against a
shape that stretching keeps a shape of its kind -- a point, a Line,
a sharp Rectangle, Triangle or Polygon -- runs exactly on the
unit circle with the other shape mapped there. Against a Circle or
another Ellipse it is exact too, through the closest point on the
ellipse, accurate to rounding. Against a Sector and its arc, the
ellipse stands in as its outline polygon, within 1e-4 of its larger
side; a curve is followed as closely, and a rounded shape is tested as
its own outline, as everywhere.
Implements Copyable, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Region, Shape, Writable.
Fields¶
position: Point2Dw: Float64h: Float64
Constructors¶
@staticmethod
def __init__(out self, position: Point2D, w: Float64, h: Float64)
@staticmethod
def __init__(out self, position: Point2D, w: Int, h: Int)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
center¶
def center(self) -> Point2D
area¶
def area(self) -> Float64
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the ellipse.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
contains¶
def contains(self, p: Point2D) -> Bool
def contains(self, r: Rectangle) -> Bool
def contains(self, c: Circle) -> Bool
Whether c is inside: its centre is, and no nearer the curve than its radius.
def contains(self, t: Triangle) -> Bool
def contains(self, l: Line) -> Bool
def contains[C: Curve](self, curve: C) -> Bool
Whether the whole of curve (an Arc, Bezier or Spline) is inside: every segment of it flattened to within 1e-4 of its size, as intersects follows it, so a curve grazing the edge from inside is decided only that closely.
def contains(self, s: Sector) -> Bool
Whether s is inside: its tip and its arc are, since every point of a sector lies between the two and the ellipse is convex.
def contains(self, p: Polygon) -> Bool
def contains(self, other: Self) -> Bool
Whether other is inside: with other mapped onto the unit circle, this one, mapped alike, still an ellipse, holds that circle.
def contains(self, c: Capsule) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Capsule¶
struct Capsule
A capsule: every point within r of the segment from start to end -- a rectangle along the segment, capped by a half disc at each end. center, area, bounds, closest_point, contains, move_to, translate.
Like a Line, it lies along any direction and is placed by its ends,
so center() is the segment's midpoint and move_to moves that. A
stadium is also Rectangle(position, w, h, corner_radius=h / 2,
rotation=angle); a capsule is the one placed by its ends, the usual
round collider for a moving body. With start == end it is the Circle of
radius r there, and a zero r collapses it to the segment.
contains treats the boundary as inside, and closest_point returns
the query point itself when it is inside. Every test is a distance from
the segment, so they are exact: against a point, a line, a circle,
another capsule, an arc, an ellipse and every straight-edged shape,
rounded rectangles and triangles included. A shape inside it only needs
its corners (or its ends' discs) inside, since a capsule is convex.
Holding a Sector's arc or an Ellipse, it follows them as a curve is
followed, within 1e-4 of their size, and a rounded polygon or sector is
tested as its outline, as everywhere. r is assumed non-negative.
Implements Copyable, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Region, Shape, Writable, _Collider.
Fields¶
start: Point2Dend: Point2Dr: Float64
Constructors¶
@staticmethod
def __init__(out self, start: Point2D, end: Point2D, r: Float64)
@staticmethod
def __init__(out self, start: Point2D, end: Point2D, r: Int)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
center¶
def center(self) -> Point2D
area¶
def area(self) -> Float64
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the capsule: the segment's, grown by r all round.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
contains¶
def contains(self, p: Point2D) -> Bool
def contains(self, l: Line) -> Bool
def contains(self, r: Rectangle) -> Bool
def contains(self, c: Circle) -> Bool
def contains(self, t: Triangle) -> Bool
def contains(self, other: Self) -> Bool
def contains[C: Curve](self, curve: C) -> Bool
Whether the whole of curve (an Arc, Bezier or Spline) is inside: every segment of it flattened to within 1e-4 of its size, as intersects follows it, so a curve grazing the edge from inside is decided only that closely.
def contains(self, s: Sector) -> Bool
Whether s is inside: its tip and its arc are, since every point of a sector lies between the two and the capsule is convex.
def contains(self, p: Polygon) -> Bool
def contains(self, e: Ellipse) -> Bool
Whether e is inside, its curve followed by its outline polygon.
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Line¶
struct Line
A line segment from start to end: length, at, tangent, at_distance, split, midpoint, bounds, flatten, beziers, closest_point, intersects, move_to, translate.
A Curve, like Arc, Bezier and Spline: a straight one, as in
mathematics, with t proportional to distance. It has no interior, so
unlike the Regions it has no center(), area() or overlaps, and
is the subject of l.intersects(x) -- see the comment above
intersects for why that method exists only on curves.
intersects and _point_on_segment-based checks treat the endpoints
and any touching/collinear-overlapping point as inclusive. A
zero-length line (start == end) is a degenerate point segment:
intersects and closest_point remain exact for it (a point can
still "intersect" the collapsed line if it coincides with it).
Implements Copyable, Curve, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Shape, Writable, _Collider.
Fields¶
start: Point2Dend: Point2D
Constructors¶
@staticmethod
def __init__(out self, start: Point2D, end: Point2D)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
length¶
def length(self) -> Float64
at¶
def at(self, t: Float64) -> Point2D
The point at parameter t: start at 0, end at 1, and proportional to distance in between. Not clamped: outside 0..1 it carries on along the line, as lerp extrapolates.
tangent¶
def tangent(self, t: Float64) -> Vector2D
The derivative at t: end - start, the same everywhere — the direction of travel, with the line's length as its magnitude. Zero for a zero-length line.
at_distance¶
def at_distance(self, distance: Float64) -> Point2D
The point distance along the line from start, clamped to the line's ends. Exact, and as cheap as at.
split¶
def split(self, t: Float64) -> Tuple[Self, Self]
The line cut at t into two, together tracing exactly the original: the first runs start to at(t), the second at(t) to end.
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the line: the box its two ends span, zero-width or zero-height when it is axis-aligned.
flatten¶
def flatten(self, tolerance: Float64) -> List[Point2D]
start and end: a line is already straight, whatever the tolerance.
beziers¶
def beziers(self) -> List[Bezier]
The line as one cubic Bézier, its controls at the thirds, so it traces the line exactly and at the same speed.
intersects¶
def intersects(self, other: Self) -> Bool
def intersects(self, p: Point2D) -> Bool
def intersects(self, a: Arc) -> Bool
def intersects(self, b: Bezier) -> Bool
def intersects(self, s: Spline) -> Bool
def intersects(self, s: Sector) -> Bool
def intersects(self, p: Polygon) -> Bool
def intersects(self, c: Circle) -> Bool
def intersects(self, c: Capsule) -> Bool
def intersects(self, e: Ellipse) -> Bool
def intersects(self, r: Rectangle) -> Bool
def intersects(self, t: Triangle) -> Bool
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
midpoint¶
def midpoint(self) -> Point2D
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Triangle¶
struct Triangle
A triangle defined by its three vertices a, b, c: center, area, closest_point, contains, move_to, translate.
Unlike Rectangle/Circle, a Triangle is not centre-positioned in
its fields -- center() (the centroid) is derived, and move_to
translates all three vertices so the centroid lands on the given
point. Vertex winding (clockwise or counter-clockwise) does not matter
to any method here; contains and area both work from unsigned or
sign-normalized quantities.
contains treats every edge as inside (boundary-inclusive), matching
Rectangle/Circle. When the three vertices are collinear (including
all three coincident), the hull has zero area and collapses to the
longest edge as a segment; contains/overlaps/area all remain
exact for this degenerate case rather than reporting a false
containment, overlap, or a divide-by-zero.
corner_radius rounds all three corners by one radius, as
canvas.triangle draws them, and every test here sees the rounded
shape. It is clamped so no corner's rounding overruns half of either
edge beside it; one thin corner limits all three.
Implements Copyable, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Region, Shape, Writable, _Collider.
Fields¶
a: Point2Db: Point2Dc: Point2Dcorner_radius: Float64: How far each corner is rounded; 0 is sharp.
Constructors¶
@staticmethod
def __init__(
out self,
a: Point2D,
b: Point2D,
c: Point2D,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
center¶
def center(self) -> Point2D
area¶
def area(self) -> Float64
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the triangle. Rounded, it is the inner triangle's grown by the radius, so a sharp tip's rounding pulls it in.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
contains¶
def contains(self, p: Point2D) -> Bool
def contains(self, other: Self) -> Bool
def contains(self, r: Rectangle) -> Bool
def contains(self, c: Circle) -> Bool
def contains(self, l: Line) -> Bool
def contains(self, c: Capsule) -> Bool
def contains[C: Curve](self, curve: C) -> Bool
Whether the whole of curve (an Arc, Bezier or Spline) is inside: every segment of it flattened to within 1e-4 of its size, as intersects follows it, so a curve grazing the edge from inside is decided only that closely.
def contains(self, s: Sector) -> Bool
def contains(self, p: Polygon) -> Bool
def contains(self, e: Ellipse) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Arc¶
struct Arc
A circular arc: part of the circle around position with radius r, from start_angle round by sweep_angle. end_angle, at, tangent, length, at_distance, split, bounds, flatten, beziers, closest_point, intersects, move_to, translate.
Angles are radians, counted counter-clockwise from the +x axis: y is up,
so this is the direction rotate turns. The sweep is signed -- positive
runs counter-clockwise, negative clockwise -- so every arc has one
spelling and reversing one is negating its sweep. A sweep of a full turn
(TAU) or more is the whole circle. The fields keep what was given; the
methods read the sweep clamped to a turn.
Like Line and Bezier, an arc is a curve with no interior: there is
no area, contains or overlaps for it. position is the circle's
centre, which the arc passes through only when r is zero, and
move_to moves that centre. A zero r or sweep collapses the arc to a
point.
Implements Copyable, Curve, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Shape, Writable.
Fields¶
position: Point2Dr: Float64start_angle: Float64sweep_angle: Float64
Constructors¶
@staticmethod
def __init__(
out self,
position: Point2D,
r: Float64,
start_angle: Float64,
sweep_angle: Float64,
)
@staticmethod
def __init__(
out self,
position: Point2D,
r: Int,
start_angle: Float64,
sweep_angle: Float64,
)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
end_angle¶
def end_angle(self) -> Float64
start_angle + sweep_angle: where the arc ends, as an angle.
at¶
def at(self, t: Float64) -> Point2D
The point at parameter t: 0 at the start, 1 at the end, and proportional to distance along the arc in between. Not clamped: outside 0..1 it carries on round the circle.
tangent¶
def tangent(self, t: Float64) -> Vector2D
The derivative at t: the direction of travel, with a magnitude that is the arc's length (the speed per unit of t, the same everywhere). Zero for a collapsed arc.
length¶
def length(self) -> Float64
The distance along the arc from start to end. Exact.
at_distance¶
def at_distance(self, distance: Float64) -> Point2D
The point distance along the arc from its start, clamped to the arc's ends. An arc has constant speed, so this is at with t scaled: exact, and as cheap as at.
split¶
def split(self, t: Float64) -> Tuple[Self, Self]
The arc cut at t into two on the same circle, together tracing exactly the original: the first runs from the start to at(t), the second from at(t) to the end. A sweep past a full turn is clamped to one first, as everywhere else.
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the arc: its two ends, widened to each of the circle's four extremes the arc passes.
flatten¶
def flatten(self, tolerance: Float64) -> List[Point2D]
Points along the arc, from start to end inclusive, such that the straight segments between them stray from the arc by at most tolerance.
The points are evenly spaced, as few as the tolerance allows, and capped at 1024 segments. A collapsed arc still gives two points.
beziers¶
def beziers(self) -> List[Bezier]
The arc as cubic Béziers, each ending where the next starts, in pieces of at most 45°. An approximation -- no cubic is exactly a circle -- but within about 4e-6 of the radius. A full circle ends exactly where it starts. A collapsed arc gives no curves.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
The point on the arc nearest p: straight out from the centre when p lies within the sweep, otherwise the nearer end. From the centre itself every point is as near, and the start is returned.
intersects¶
def intersects(self, p: Point2D) -> Bool
def intersects(self, l: Line) -> Bool
def intersects(self, other: Self) -> Bool
def intersects(self, c: Circle) -> Bool
def intersects(self, c: Capsule) -> Bool
def intersects(self, e: Ellipse) -> Bool
def intersects(self, r: Rectangle) -> Bool
def intersects(self, t: Triangle) -> Bool
def intersects(self, s: Sector) -> Bool
def intersects(self, p: Polygon) -> Bool
def intersects(self, b: Bezier) -> Bool
def intersects(self, s: Spline) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Sector¶
struct Sector
A sector -- a slice of pie: the region between two radii of the circle around position with radius r, from start_angle round by sweep_angle. end_angle, center, area, arc, bounds, closest_point, contains, move_to, translate.
The angles are an Arc's: radians, counter-clockwise from the +x axis,
and a signed sweep, so a negative one runs clockwise and a full turn or
more is the whole circle. The fields keep what was given.
center() is position, the tip of the slice, as for Circle: not the
centroid. contains treats the boundary as inside, within the slack
Arc.intersects allows, so points found on the sector's own arc count;
closest_point returns the query point itself when it is inside. A sweep wider than a
half turn is not convex. A zero r collapses the sector to its tip, and
a zero sweep to the one radius at start_angle. r is assumed
non-negative.
corner_radius rounds the tip and both ends of the arc, as
canvas.sector draws them, and every test here sees the rounded shape.
An end rounds against the arc, tangent to it from inside. Each corner is
clamped to half of what lies beside it, as a polygon's is; past a half
turn the tip is concave and its rounding fills the notch. A whole disc
has no corners. The tests use the rounded outline with each curve
flattened, so a point within about 0.1% of the radii of the boundary
may fall either way.
Implements Copyable, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Region, Shape, Writable.
Fields¶
position: Point2Dr: Float64start_angle: Float64sweep_angle: Float64corner_radius: Float64: How far each corner is rounded; 0 is sharp.
Constructors¶
@staticmethod
def __init__(
out self,
position: Point2D,
r: Float64,
start_angle: Float64,
sweep_angle: Float64,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
)
@staticmethod
def __init__(
out self,
position: Point2D,
r: Int,
start_angle: Float64,
sweep_angle: Float64,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
end_angle¶
def end_angle(self) -> Float64
start_angle + sweep_angle: where the sector ends, as an angle.
center¶
def center(self) -> Point2D
area¶
def area(self) -> Float64
arc¶
def arc(self) -> Arc
The curved edge of the sector, running the same way round.
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the sector: its arc's, widened to take in the tip.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
contains¶
def contains(self, p: Point2D) -> Bool
def contains(self, r: Rectangle) -> Bool
def contains(self, t: Triangle) -> Bool
def contains(self, c: Circle) -> Bool
def contains(self, other: Self) -> Bool
def contains(self, l: Line) -> Bool
def contains[C: Curve](self, curve: C) -> Bool
Whether the whole of curve (an Arc, Bezier or Spline) is inside: every segment of it flattened to within 1e-4 of its size, as intersects follows it, so a curve grazing the edge from inside is decided only that closely.
def contains(self, p: Polygon) -> Bool
def contains(self, e: Ellipse) -> Bool
def contains(self, c: Capsule) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
Polygon¶
struct Polygon
A polygon through vertices, in order, closed back to the first: regular, star, center, area, bounds, closest_point, contains, move_to, translate.
Any vertices make a polygon, concave or crossing itself. What is inside follows the nonzero rule: a point is inside when the edges wind round it, either way, so a pentagram given as five crossing vertices is solid through its middle. Vertex winding does not matter, and the boundary counts as inside. With fewer than three vertices, or all of them in line, the polygon collapses to its edges.
It is a region: in every overlaps pair, contains both ways, and
met by Line.intersects and Arc.intersects.
Like a Triangle's, the fields are the vertices and center() (the
centroid) is derived; move_to moves the centroid. The centroid and
area are those of a simple polygon -- one whose edges do not cross.
corner_radius rounds every corner, as canvas.polygon draws it, and
every test here sees the rounded shape. Each corner is clamped on its
own so its rounding takes at most half of either edge beside it: a
sharp tip rounds less without holding back the rest. A concave corner
rounds too, filling its notch. The tests use the rounded outline with
each fillet flattened, so a point within about 0.1% of the radius of a
fillet may fall either way.
Implements Copyable, Deinitable, Drawable, Equatable, Movable, Region, Shape, Writable.
Fields¶
vertices: List[Point2D]corner_radius: Float64: How far each corner is rounded; 0 is sharp.
Constructors¶
@staticmethod
def __init__(out self, *vertices: Point2D, *, corner_radius: _OptionalFloat64 = SIMD(0))
@staticmethod
def __init__(
out self,
var vertices: List[Point2D],
*,
corner_radius: _OptionalFloat64 = SIMD(0),
)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
regular¶
@staticmethod
def regular(
position: Point2D,
r: Float64,
sides: Int,
start_angle: Float64 = 1.5707963267948966,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
) -> Self
A regular polygon of sides vertices on the circle around position with radius r, the first at start_angle (radians counter-clockwise from +x; straight up by default), the rest following counter-clockwise.
@staticmethod
def regular(
position: Point2D,
r: Int,
sides: Int,
start_angle: Float64 = 1.5707963267948966,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
) -> Self
star¶
@staticmethod
def star(
position: Point2D,
r: Float64,
inner_radius: Float64,
tips: Int,
start_angle: Float64 = 1.5707963267948966,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
) -> Self
A star of tips points about position: its tips on radius r, the first at start_angle (straight up by default), and between each two tips a vertex on radius inner_radius, halfway round. The outline does not cross itself, so it is a simple polygon.
@staticmethod
def star(
position: Point2D,
r: Int,
inner_radius: Int,
tips: Int,
start_angle: Float64 = 1.5707963267948966,
*,
corner_radius: _OptionalFloat64 = SIMD(0),
) -> Self
center¶
def center(self) -> Point2D
The centroid of the enclosed area; with no area, the mean of the vertices (the origin for none).
area¶
def area(self) -> Float64
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the polygon; a zero-size one at the origin for none.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
p itself when inside, else the nearest point on an edge. A polygon with no vertices has no points, and returns p.
contains¶
def contains(self, p: Point2D) -> Bool
def contains(self, l: Line) -> Bool
def contains[C: Curve](self, curve: C) -> Bool
Whether the whole of curve (an Arc, Bezier or Spline) is inside: every segment of it flattened to within 1e-4 of its size, as intersects follows it, so a curve grazing the edge from inside is decided only that closely.
def contains(self, r: Rectangle) -> Bool
def contains(self, t: Triangle) -> Bool
def contains(self, c: Circle) -> Bool
def contains(self, s: Sector) -> Bool
def contains(self, other: Self) -> Bool
def contains(self, e: Ellipse) -> Bool
def contains(self, c: Capsule) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
move_to¶
def move_to(self, position: Point2D) -> Self
translate¶
def translate(self, delta: Vector2D) -> Self
overlaps¶
def overlaps(a: Circle, b: Circle) -> Bool
def overlaps(a: Circle, b: Capsule) -> Bool
def overlaps(a: Circle, b: Rectangle) -> Bool
def overlaps(a: Circle, b: Triangle) -> Bool
def overlaps(a: Capsule, b: Circle) -> Bool
def overlaps(a: Capsule, b: Capsule) -> Bool
def overlaps(a: Capsule, b: Rectangle) -> Bool
def overlaps(a: Capsule, b: Triangle) -> Bool
def overlaps(a: Rectangle, b: Circle) -> Bool
def overlaps(a: Rectangle, b: Capsule) -> Bool
def overlaps(a: Rectangle, b: Rectangle) -> Bool
def overlaps(a: Rectangle, b: Triangle) -> Bool
def overlaps(a: Triangle, b: Circle) -> Bool
def overlaps(a: Triangle, b: Capsule) -> Bool
def overlaps(a: Triangle, b: Rectangle) -> Bool
def overlaps(a: Triangle, b: Triangle) -> Bool
def overlaps(a: Sector, b: Circle) -> Bool
def overlaps(a: Circle, b: Sector) -> Bool
def overlaps(a: Sector, b: Rectangle) -> Bool
def overlaps(a: Rectangle, b: Sector) -> Bool
def overlaps(a: Sector, b: Triangle) -> Bool
def overlaps(a: Triangle, b: Sector) -> Bool
def overlaps(a: Sector, b: Sector) -> Bool
def overlaps(a: Polygon, b: Circle) -> Bool
def overlaps(a: Circle, b: Polygon) -> Bool
def overlaps(a: Polygon, b: Rectangle) -> Bool
def overlaps(a: Rectangle, b: Polygon) -> Bool
def overlaps(a: Polygon, b: Triangle) -> Bool
def overlaps(a: Triangle, b: Polygon) -> Bool
def overlaps(a: Polygon, b: Sector) -> Bool
def overlaps(a: Sector, b: Polygon) -> Bool
def overlaps(a: Polygon, b: Polygon) -> Bool
def overlaps(a: Ellipse, b: Circle) -> Bool
def overlaps(a: Circle, b: Ellipse) -> Bool
def overlaps(a: Ellipse, b: Rectangle) -> Bool
def overlaps(a: Rectangle, b: Ellipse) -> Bool
def overlaps(a: Ellipse, b: Triangle) -> Bool
def overlaps(a: Triangle, b: Ellipse) -> Bool
def overlaps(a: Ellipse, b: Sector) -> Bool
def overlaps(a: Sector, b: Ellipse) -> Bool
def overlaps(a: Ellipse, b: Polygon) -> Bool
def overlaps(a: Polygon, b: Ellipse) -> Bool
def overlaps(a: Ellipse, b: Ellipse) -> Bool
def overlaps(a: Capsule, b: Ellipse) -> Bool
def overlaps(a: Ellipse, b: Capsule) -> Bool
def overlaps(a: Capsule, b: Sector) -> Bool
def overlaps(a: Sector, b: Capsule) -> Bool
def overlaps(a: Capsule, b: Polygon) -> Bool
def overlaps(a: Polygon, b: Capsule) -> Bool
Bezier¶
struct Bezier
A cubic Bézier curve from start to end, shaped by control1 and control2: at, tangent, split, bounds, flatten, beziers, closest_point, intersects, translate.
The curve passes through start and end only; the controls pull it
towards themselves and set the direction it leaves start
(control1 - start) and arrives at end (end - control2). It is the
curve SVG paths, fonts and vector editors use. For a smooth curve through
given points instead, see Spline.
Like Line, a curve has no interior and is not a region: there is no
area, contains or overlaps for it.
t runs from 0 at start to 1 at end, but it is not proportional to
distance along the curve — equal steps in t bunch up where the
controls do.
Implements Copyable, Curve, Deinitable, Drawable, Equatable, ImplicitlyCopyable, Movable, Shape, Writable.
Fields¶
start: Point2Dcontrol1: Point2Dcontrol2: Point2Dend: Point2D
Constructors¶
@staticmethod
def __init__(
out self,
start: Point2D,
control1: Point2D,
control2: Point2D,
end: Point2D,
)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
at¶
def at(self, t: Float64) -> Point2D
The point at parameter t. Not clamped: outside 0..1 the cubic continues past its endpoints, as lerp extrapolates.
tangent¶
def tangent(self, t: Float64) -> Vector2D
The derivative at t: the direction of travel, with a magnitude that is the curve's speed per unit of t. Not normalised — it is zero where the curve stops, e.g. at a cusp or where a control sits on its endpoint.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
The point on the curve nearest p.
Samples the curve at even steps in t, then refines every sample
nearer than its neighbours with Newton's method on the distance's
derivative, kept within the curve, and keeps the nearest result. Exact to
rounding for any curve that fits on a screen, though a search, not
a formula.
intersects¶
def intersects(self, p: Point2D) -> Bool
def intersects(self, l: Line) -> Bool
def intersects(self, a: Arc) -> Bool
def intersects(self, other: Self) -> Bool
def intersects(self, s: Spline) -> Bool
def intersects(self, c: Circle) -> Bool
def intersects(self, c: Capsule) -> Bool
def intersects(self, e: Ellipse) -> Bool
def intersects(self, r: Rectangle) -> Bool
def intersects(self, t: Triangle) -> Bool
def intersects(self, s: Sector) -> Bool
def intersects(self, p: Polygon) -> Bool
split¶
def split(self, t: Float64) -> Tuple[Self, Self]
The curve cut at t into two, together tracing exactly the original: the first runs start to at(t), the second at(t) to end (de Casteljau).
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the curve — around the curve itself, not its control points, which usually reach further.
flatten¶
def flatten(self, tolerance: Float64) -> List[Point2D]
Points along the curve, from start to end inclusive, such that the straight segments between them stray from the curve by at most tolerance.
The points are evenly spaced in t, with the count from Wang's
formula, and capped at 1024 segments. A curve collapsed to one point
still gives two points.
beziers¶
def beziers(self) -> List[Self]
Itself, alone: the form Arc.beziers, Line.beziers and Spline.beziers share, so every curve converts the same way.
length¶
def length(self) -> Float64
The distance along the curve from start to end.
Gauss–Legendre quadrature (8 points on each of 16 equal steps in
t) of the curve's speed — accurate to far below a pixel for any
curve that fits on a screen, but an approximation, not exact.
at_distance¶
def at_distance(self, distance: Float64) -> Point2D
The point distance along the curve from start, clamped to the curve's ends.
Unlike at, equal steps in distance are equal steps along the
curve, so this is what moves something along it at a steady speed.
Each call inverts length numerically; sample once per frame per
entity, not in a tight loop.
translate¶
def translate(self, delta: Vector2D) -> Self
Spline¶
struct Spline
A smooth curve through every one of points, in order: at, tangent, length, at_distance, bounds, flatten, beziers, closest_point, intersects, translate.
It is a Catmull-Rom spline: each stretch between two neighbouring points
is a cubic whose direction at each point is set by that point's
neighbours, so the curve turns smoothly through every point without the
off-curve controls a Bezier needs. The first and last points have one
neighbour each; the curve leaves and arrives there heading along the
missing neighbour's mirror image. With closed, the curve runs on from
the last point back to the first and the ends have neighbours like any
other point.
alpha sets how the spacing of the points shapes the curve: 0 is
uniform, which can loop or cusp where points bunch up; 0.5,
centripetal, the default, never does within a stretch; 1 is chordal,
which hugs the points more loosely still. Between 0 and 1 is useful.
t runs from 0 at the first point to 1 at the last (back at the first
when closed), each stretch taking an equal share whatever its length, so
equal steps in t are not equal distances; at_distance is.
Fewer than two distinct points make no curve: then at and
at_distance give the one point (the origin if there is none), length
is 0 and flatten gives no points.
Like Bezier, a spline has no interior and is not a region.
Implements Copyable, Curve, Deinitable, Drawable, Equatable, Movable, Shape, Writable.
Fields¶
points: List[Point2D]alpha: Float64closed: Bool
Constructors¶
@staticmethod
def __init__(
out self,
var points: List[Point2D],
alpha: Float64 = 0.5,
closed: Bool = False,
)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
beziers¶
def beziers(self) -> List[Bezier]
The spline as cubic Béziers, one per stretch between neighbouring points, each ending where the next starts. Exact, not an approximation: every stretch is a cubic, and any cubic is a Bézier.
An open spline of n points gives n - 1 curves; a closed one gives n, the last ending at the first point. A point repeating the one before it is skipped, as is a closed spline's last point repeating its first; fewer than two distinct points give no curves.
at¶
def at(self, t: Float64) -> Point2D
The point at parameter t, clamped to 0..1: a spline has no natural continuation past its ends.
tangent¶
def tangent(self, t: Float64) -> Vector2D
The derivative with respect to t at t, clamped to 0..1: the direction of travel, with a magnitude that is the speed t moves at. A joint belongs to the stretch that starts there. Zero when there is no curve.
length¶
def length(self) -> Float64
The distance along the curve from the first point to the last, as each stretch's Bezier.length summed.
at_distance¶
def at_distance(self, distance: Float64) -> Point2D
The point distance along the curve from the first point, clamped to the curve's ends. Equal steps in distance are equal steps along the curve. Measures every stretch per call; sample once per frame per entity, not in a tight loop.
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the curve. A spline with no curve gives a rectangle of no size at its one point.
flatten¶
def flatten(self, tolerance: Float64) -> List[Point2D]
Points along the curve, from the first point to the last inclusive, straying from it by at most tolerance between them: each stretch's Bezier.flatten, joined without repeating the point where one stretch meets the next.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
The point on the curve nearest p: the nearest of each stretch's Bezier.closest_point. A spline with no curve gives its one point.
intersects¶
def intersects(self, p: Point2D) -> Bool
def intersects(self, l: Line) -> Bool
def intersects(self, a: Arc) -> Bool
def intersects(self, b: Bezier) -> Bool
def intersects(self, c: Circle) -> Bool
def intersects(self, c: Capsule) -> Bool
def intersects(self, e: Ellipse) -> Bool
def intersects(self, r: Rectangle) -> Bool
def intersects(self, t: Triangle) -> Bool
def intersects(self, s: Sector) -> Bool
def intersects(self, p: Polygon) -> Bool
def intersects(self, other: Self) -> Bool
translate¶
def translate(self, delta: Vector2D) -> Self
Curve¶
trait Curve
A shape with no inside, traced from one end to the other: Line, Arc, Bezier and Spline. A straight line is a curve too, as in mathematics.
t runs from 0 at the start to 1 at the end; only on a Line and an
Arc is it proportional to distance, which at_distance always is.
Implements Copyable, Deinitable, Drawable, Movable, Shape.
at¶
def at(self, t: Float64) -> Point2D
The point at parameter t.
tangent¶
def tangent(self, t: Float64) -> Vector2D
The derivative at t: the direction of travel, with the speed per unit of t as its magnitude.
length¶
def length(self) -> Float64
The distance along the curve from start to end.
at_distance¶
def at_distance(self, distance: Float64) -> Point2D
The point distance along the curve from its start, clamped to its ends.
flatten¶
def flatten(self, tolerance: Float64) -> List[Point2D]
Points along the curve, start to end, whose straight segments stray from it by at most tolerance.
beziers¶
def beziers(self) -> List[Bezier]
The curve as cubic Béziers, each ending where the next starts.
Drawable¶
trait Drawable
Something a DrawTarget can draw: a shape, a curve, or a program's own type made of them.
draw_to¶
def draw_to[T: DrawTarget](self, mut target: T)
Draw this onto target, through its draw or, for a shape, the primitive for its kind.
DrawTarget¶
trait DrawTarget
Somewhere shapes are drawn: Canvas. Its primitives, one per kind of shape, are the library's own; a program draws through draw.
draw¶
def draw[D: Drawable](mut self, shape: D)
Draw shape in the current style and transform.
Region¶
trait Region
A shape with an inside: Rectangle, Circle, Ellipse, Capsule, Triangle, Sector and Polygon, the region types of overlaps and contains, and what canvas.clip takes. A curve has no inside, so it is not one.
Implements Copyable, Deinitable, Drawable, Movable, Shape.
center¶
def center(self) -> Point2D
area¶
def area(self) -> Float64
contains¶
def contains(self, p: Point2D) -> Bool
Whether p is inside, its boundary included.
move_to¶
def move_to(self, position: Point2D) -> Self
The region moved so its position (or, without one, its center()) is at position; this one is unchanged.
Shape¶
trait Shape
A geometric shape: a Region or a Curve. What every one of them answers, whichever it is, and a plain value generic code can keep, copy and drop. Tests between two shapes (contains, intersects, overlaps) are overloads per pair, not part of this.
Implements Copyable, Deinitable, Drawable, Movable.
bounds¶
def bounds(self) -> Rectangle
The tightest axis-aligned rectangle around the shape.
closest_point¶
def closest_point(self, p: Point2D) -> Point2D
The point of the shape nearest p: p itself inside a region.
translate¶
def translate(self, delta: Vector2D) -> Self
The shape moved by delta; this one is unchanged.