Planar Shape Construction¶
Basic representations of common two-dimensional shapes:
Rectangle
:- An axis-aligned rectangle.
Circle
:- A planar circle with a defined origin and radius.
Text
:- The shapes formed from a line of text.
Also contains convenience methods for creating complex polygons:
arc()
:- a semi-circular arc of points
bezier()
:- a bezier curve with specified control points
fillet()
:- a filleted corner with a specified radius from three points
-
class
petrify.shape.
Circle
(origin, radius, segments=10)[source]¶ Bases:
petrify.plane.Polygon2
Approximates a perfect circle with a finite number of line segments:
>>> circle = Circle(Point(0, 0), 1, 5)
-
PointsConstructor
¶ alias of
petrify.plane.Polygon2
-
centered
(point)¶ Center this polygon at a given point:
>>> from petrify.shape import Rectangle >>> Rectangle(Point(0, 0), Vector(2, 2)).centered(Point(3, 3)) Polygon([Point(2.0, 2.0), Point(2.0, 4.0), Point(4.0, 4.0), Point(4.0, 2.0)])
-
clockwise
()¶ Returns True if the points in this polygon are in clockwise order:
>>> Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]).clockwise() True >>> Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]).clockwise() False
-
contains
(p)¶ Tests whether a point lies within this polygon:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.contains(Point(1.0, 0.5)) True >>> tri.contains(Point(0.5, 1.5)) False
-
envelope
()¶ Returns the bounding
Rectangle
around this polygon:>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.envelope() Rectangle(Point(0, 0), Vector(2, 1))
-
index_of
(point)¶ Finds index of given point:
>>> Polygon([Point(0, 0), Point(1, 0), Point(1, 1)]).index_of(Point(1, 1)) 2
-
inverted
()¶ Reverse the points in this polygon:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.inverted() Polygon([Point(1, 1), Point(0, 0), Point(2, 0)])
-
inwards
(edge)¶ Finds the normalized
Ray2
facing inwards for a given edge:>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.inwards(tri.segments()[0]) Vector(0.0, 1.0)
-
is_convex
()¶ Return True if the polynomial defined by the sequence of 2D points is ‘strictly convex’:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.is_convex() True >>> indent = Polygon([ ... Point(0, 0), ... Point(10, 0), ... Point(5, 5), ... Point(10, 10), ... Point(0, 10) ... ]) >>> indent.is_convex() False
Note
“strictly convex” is defined as follows:
- points are valid
- side lengths are non-zero
- interior angles are strictly between zero and a straight angle
- the polygon does not intersect itself
-
offset
(amount)¶ Finds the dynamic offset of a polygon by moving all edges by a given amount perpendicular to their direction:
>>> square = Polygon([Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]) >>> square.offset(-0.1).exterior[0] Polygon([Point(0.1, 0.1), Point(0.1, 0.9), Point(0.9, 0.9), Point(0.9, 0.1)]) >>> square.offset(0.1).exterior[0] Polygon([Point(-0.1, -0.1), Point(-0.1, 1.1), Point(1.1, 1.1), Point(1.1, -0.1)]) >>> square.offset(-10) ComplexPolygon([])
Note
This always returns a
ComplexPolygon
. Collisions during the offset process can subdivide the polygon.
-
shift
(n)¶ Shift the points in this polygon by n:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.shift(1) Polygon([Point(0, 0), Point(1, 1), Point(2, 0)])
-
simplify
(tolerance=0.0001)¶ Remove any duplicate points, within a certain tolerance:
>>> Polygon([Point(1, 1), Point(2, 0), Point(0, 0), Point(1, 1)]).simplify() Polygon([Point(2, 0), Point(0, 0), Point(1, 1)])
Returns None if the resulting simplification would create a point:
>>> Polygon([ ... Point(1, 1), ... Point(2, 0), ... Point(0, 0), ... Point(1, 1) ... ]).simplify(100) is None True
-
to_clockwise
()¶ Converts this polygon to a clockwise one if necessary:
>>> Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]).to_clockwise() Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]).to_clockwise() Polygon([Point(2, 0), Point(0, 0), Point(1, 1)])
-
to_counterclockwise
()¶ Converts this polygon to a clockwise one if necessary:
>>> Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]).to_counterclockwise() Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]) >>> Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]).to_counterclockwise() Polygon([Point(1, 1), Point(0, 0), Point(2, 0)])
-
-
class
petrify.shape.
Rectangle
(origin, extent)[source]¶ Bases:
petrify.plane.Polygon2
An axis-aligned rectangle with a point of origin and a vector or point extent:
>>> Rectangle(Point(0, 0), Vector(1, 1)) Rectangle(Point(0, 0), Vector(1, 1)) >>> Rectangle(Point(1, 1), Point(2, 3)) Rectangle(Point(1, 1), Vector(1, 2))
-
PointsConstructor
¶ alias of
petrify.plane.Polygon2
-
centered
(point)¶ Center this polygon at a given point:
>>> from petrify.shape import Rectangle >>> Rectangle(Point(0, 0), Vector(2, 2)).centered(Point(3, 3)) Polygon([Point(2.0, 2.0), Point(2.0, 4.0), Point(4.0, 4.0), Point(4.0, 2.0)])
-
clockwise
()¶ Returns True if the points in this polygon are in clockwise order:
>>> Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]).clockwise() True >>> Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]).clockwise() False
-
contains
(p)¶ Tests whether a point lies within this polygon:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.contains(Point(1.0, 0.5)) True >>> tri.contains(Point(0.5, 1.5)) False
-
envelope
()¶ Returns the bounding
Rectangle
around this polygon:>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.envelope() Rectangle(Point(0, 0), Vector(2, 1))
-
index_of
(point)¶ Finds index of given point:
>>> Polygon([Point(0, 0), Point(1, 0), Point(1, 1)]).index_of(Point(1, 1)) 2
-
inverted
()¶ Reverse the points in this polygon:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.inverted() Polygon([Point(1, 1), Point(0, 0), Point(2, 0)])
-
inwards
(edge)¶ Finds the normalized
Ray2
facing inwards for a given edge:>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.inwards(tri.segments()[0]) Vector(0.0, 1.0)
-
is_convex
()¶ Return True if the polynomial defined by the sequence of 2D points is ‘strictly convex’:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.is_convex() True >>> indent = Polygon([ ... Point(0, 0), ... Point(10, 0), ... Point(5, 5), ... Point(10, 10), ... Point(0, 10) ... ]) >>> indent.is_convex() False
Note
“strictly convex” is defined as follows:
- points are valid
- side lengths are non-zero
- interior angles are strictly between zero and a straight angle
- the polygon does not intersect itself
-
offset
(amount)¶ Finds the dynamic offset of a polygon by moving all edges by a given amount perpendicular to their direction:
>>> square = Polygon([Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]) >>> square.offset(-0.1).exterior[0] Polygon([Point(0.1, 0.1), Point(0.1, 0.9), Point(0.9, 0.9), Point(0.9, 0.1)]) >>> square.offset(0.1).exterior[0] Polygon([Point(-0.1, -0.1), Point(-0.1, 1.1), Point(1.1, 1.1), Point(1.1, -0.1)]) >>> square.offset(-10) ComplexPolygon([])
Note
This always returns a
ComplexPolygon
. Collisions during the offset process can subdivide the polygon.
-
shift
(n)¶ Shift the points in this polygon by n:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> tri.shift(1) Polygon([Point(0, 0), Point(1, 1), Point(2, 0)])
-
simplify
(tolerance=0.0001)¶ Remove any duplicate points, within a certain tolerance:
>>> Polygon([Point(1, 1), Point(2, 0), Point(0, 0), Point(1, 1)]).simplify() Polygon([Point(2, 0), Point(0, 0), Point(1, 1)])
Returns None if the resulting simplification would create a point:
>>> Polygon([ ... Point(1, 1), ... Point(2, 0), ... Point(0, 0), ... Point(1, 1) ... ]).simplify(100) is None True
-
to_clockwise
()¶ Converts this polygon to a clockwise one if necessary:
>>> Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]).to_clockwise() Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]).to_clockwise() Polygon([Point(2, 0), Point(0, 0), Point(1, 1)])
-
to_counterclockwise
()¶ Converts this polygon to a clockwise one if necessary:
>>> Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]).to_counterclockwise() Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]) >>> Polygon([Point(1, 1), Point(0, 0), Point(2, 0)]).to_counterclockwise() Polygon([Point(1, 1), Point(0, 0), Point(2, 0)])
-
-
class
petrify.shape.
Text
(face, text)[source]¶ Bases:
petrify.plane.ComplexPolygon2
Using a
Face
provided by the freetype-py bindings, generate aComplexPolygon2
for the corresponding text:>>> from freetype import Face >>> face = Face('./tests/fixtures/RussoOne.ttf') >>> polygon = Text(face, 'petrify')
Uses the face’s pre-defined line height for the size of the text, scaled to 1.0 units on the y axis.
-
centered
(point)¶ Center this polygon at a given point:
>>> from petrify.shape import Rectangle >>> ComplexPolygon([ ... Rectangle(Point(0, 0), Vector(2, 2)), ... Rectangle(Point(1, 1), Vector(1, 1)), ... ]).centered(Point(3, 3)).envelope() Rectangle(Point(2.0, 2.0), Vector(2.0, 2.0))
-
envelope
()¶ Returns the bounding
Rectangle
around this polygon:>>> square = Polygon([Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]) >>> complex = ComplexPolygon([square + Vector(1, 1), square * 3]) >>> complex.envelope() Rectangle(Point(0, 0), Vector(3, 3))
-
offset
(amount)¶ Finds the dynamic offset of this complex polygon by moving all edges by a given amount perpendicular to their direction.
Any inner polygons are offset in the reverse direction to the outer polygons:
>>> square = Polygon([Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]) >>> complex = ComplexPolygon([square + Vector(1, 1), square * 3]) >>> complex.offset(-0.1).interior [Polygon([Point(0.9, 0.9), Point(0.9, 2.1), Point(2.1, 2.1), Point(2.1, 0.9)])] >>> complex.offset(-0.1).exterior [Polygon([Point(0.1, 0.1), Point(0.1, 2.9), Point(2.9, 2.9), Point(2.9, 0.1)])]
-
to_clockwise
()¶ Converts all sub-polygons to clockwise:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> ComplexPolygon([tri]).to_clockwise() ComplexPolygon([Polygon([Point(2, 0), Point(0, 0), Point(1, 1)])]) >>> ComplexPolygon([tri.inverted()]).to_clockwise() ComplexPolygon([Polygon([Point(2, 0), Point(0, 0), Point(1, 1)])])
-
to_counterclockwise
()¶ Converts all sub-polygons to counter-clockwise:
>>> tri = Polygon([Point(2, 0), Point(0, 0), Point(1, 1)]) >>> ComplexPolygon([tri]).to_counterclockwise() ComplexPolygon([Polygon([Point(1, 1), Point(0, 0), Point(2, 0)])]) >>> ComplexPolygon([tri.inverted()]).to_counterclockwise() ComplexPolygon([Polygon([Point(1, 1), Point(0, 0), Point(2, 0)])])
-
-
petrify.shape.
arc
(center, radius, start, end, segments=10)[source]¶ Creates an arc of points around the given center with a specified radius and start and end angles, approximated with a fixed number of segments:
>>> points = arc(Point(5, 0), 5, 0, tau / 2, segments = 3) >>> [p.snap(0.1) for p in points] [Point(10.0, 0.0), Point(5.0, 5.0), Point(0.0, 0.0)]
-
petrify.shape.
bezier
(a, b, c, d, segments=10)[source]¶ Creates a bezier curve between the given control points, approximated with a given number of segments:
>>> points = bezier(Point(0, 0), Point(5, 0), Point(5, 5), Point(10, 5), 4) >>> [p.snap(1.0) for p in points] [Point(0.0, 0.0), Point(4.0, 1.0), Point(6.0, 4.0), Point(10.0, 5.0)]
You can also use relative
Vector
controls instead of absolute points:>>> points = bezier(Point(0, 0), Vector(5, 0), Vector(-5, 0), Point(10, 5), 4) >>> [p.snap(1.0) for p in points] [Point(0.0, 0.0), Point(4.0, 1.0), Point(6.0, 4.0), Point(10.0, 5.0)]
-
petrify.shape.
fillet
(a, b, c, r, segments=10)[source]¶ Creates a smooth circular fillet from an ordered triplet of points that define a corner:
>>> points = fillet(Point(1, 0), Point(0, 0), Point(0, 1), 0.5, segments = 3) >>> [p.snap(0.1) for p in points] [Point(0.5, 0.0), Point(0.1, 0.1), Point(0.0, 0.5)]