svg_path_editor.geometry ======================== .. py:module:: svg_path_editor.geometry Classes ------- .. autoapisummary:: svg_path_editor.geometry.Point svg_path_editor.geometry.Vec2 svg_path_editor.geometry.Mat2 svg_path_editor.geometry.Line svg_path_editor.geometry.ParametricEllipticalArc Functions --------- .. autoapisummary:: svg_path_editor.geometry.rotation_matrix svg_path_editor.geometry.dot svg_path_editor.geometry.polygon_signed_area Module Contents --------------- .. py:function:: rotation_matrix(phi) Rotation matrix for angle :math:`φ` in degrees. Uses .. math:: R(φ) = \begin{pmatrix} \cos φ & -\sin φ \\ \sin φ & \cos φ \end{pmatrix}, where :math:`\cos` and :math:`\sin` are evaluated after converting :math:`φ` from degrees to radians via :func:`sympy.rad`. .. py:class:: Point(x, y) 2D point with :class:`decimal.Decimal` coordinates. .. py:attribute:: x :type: decimal.Decimal .. py:attribute:: y :type: decimal.Decimal .. py:method:: __iter__() Iterate as ``(x, y)``. .. py:method:: __eq__(other) Compare coordinates for equality. .. py:method:: __ne__(value, /) Compare coordinates for inequality. .. py:property:: vec2 :type: Vec2 Exact conversion to :class:`Vec2`. Coordinates are converted to SymPy rationals via :func:`dec_to_rat`. .. py:method:: __str__() Human-readable representation ``(x, y)`` with decimal formatting. .. py:method:: __repr__() Debug representation ``Point(x, y)`` with decimal formatting. .. py:property:: length :type: decimal.Decimal Euclidean norm :math:`‖v‖_2 = \sqrt{x^2 + y^2}`. .. py:property:: normalized :type: Point Unit vector :math:`v / ‖v‖_2`. The zero vector is returned unchanged. .. py:method:: __neg__() Unary minus :math:`-v`. .. py:method:: __add__(other) Vector addition :math:`v + w`. .. py:method:: __sub__(other) Vector subtraction :math:`v - w`. .. py:method:: __mul__(other) Scalar multiplication :math:`v ⋅ λ`. .. py:method:: __truediv__(other) Scalar division :math:`v / λ`. .. py:class:: Vec2 2D vector with SymPy coordinates. Supports exact arithmetic and simple linear operations. .. py:attribute:: x :type: svg_path_editor.math.Expr .. py:attribute:: y :type: svg_path_editor.math.Expr .. py:method:: from_point(p) :staticmethod: Construct a :class:`Vec2` from a :class:`Point`. Coordinates are converted to SymPy rationals via :func:`dec_to_rat`. .. py:property:: point :type: Point Convert to numeric :class:`Point`. Uses :func:`rat_to_dec` to convert SymPy expressions to :class:`~decimal.Decimal`. .. py:method:: __iter__() Iterate as ``(x, y)``. .. py:method:: subs(sub, *, n = None) Substitute symbols in both coordinates. :param sub: Substitution dictionary mapping symbols to expressions. :param n: Optional precision passed through to :func:`subs`. .. py:property:: swapped :type: Vec2 Swap coordinates: :math:`(x, y) ↦ (y, x)`. .. py:method:: evalf(*, n = None) Evaluate coordinates numerically. :param n: Optional precision passed to :func:`evalf`. .. py:method:: __eq__(other) Equality comparison between :class:`Vec2` instances, ``False`` when comparing to non-:class:`Vec2`. .. py:method:: __ne__(value, /) Inequality comparison between :class:`Vec2` instances, ``False`` when comparing to non-:class:`Vec2`. .. py:property:: length :type: svg_path_editor.math.Expr Euclidean norm :math:`‖v‖_2 = \sqrt{x^2 + y^2}`. .. py:property:: normalized :type: Vec2 Unit vector :math:`v / ‖v‖_2`. The zero vector is returned unchanged. .. py:method:: __neg__() Unary minus :math:`-v`. .. py:method:: __add__(other) Vector addition :math:`v + w`. .. py:method:: __sub__(other) Vector subtraction :math:`v - w`. .. py:method:: __mul__(other) Scalar multiplication :math:`v ⋅ λ`. .. py:method:: __truediv__(other) Scalar division :math:`v / λ`. .. py:function:: dot(v1: Point, v2: Point) -> decimal.Decimal dot(v1: Vec2, v2: Vec2) -> svg_path_editor.math.Expr Compute the dot product of two 2D vectors. .. py:class:: Mat2 :math:`2×2` matrix .. math:: M = \begin{pmatrix} a & b \\ c & d \end{pmatrix} acting on :class:`Vec2` by standard matrix-vector multiplication. :ivar a: Entry :math:`a_{11}`. :ivar b: Entry :math:`a_{12}`. :ivar c: Entry :math:`a_{21}`. :ivar d: Entry :math:`a_{22}`. .. py:attribute:: a :type: svg_path_editor.math.Expr .. py:attribute:: b :type: svg_path_editor.math.Expr .. py:attribute:: c :type: svg_path_editor.math.Expr .. py:attribute:: d :type: svg_path_editor.math.Expr .. py:method:: __matmul__(v) Matrix-vector product ``M @ v``. .. math:: (x', y') = (a x + b y, \; c x + d y). .. py:function:: polygon_signed_area(poly) Signed area of a simple polygon. Uses the shoelace formula .. math:: A = \frac12 \sum_i (x_i y_{i+1} - x_{i+1} y_i), with positive area for counter-clockwise vertex order. :param poly: Vertex sequence, implicitly closed. .. py:class:: Line(p, q) Line segment from :attr:`p` to :attr:`q`. Parametric form .. math:: L(t) = p + (q - p)\,t, \quad t \in \mathbb{R}. .. py:attribute:: p :type: Vec2 .. py:attribute:: q :type: Vec2 .. py:property:: delta :type: Vec2 Direction vector :math:`q - p` of the segment. .. py:property:: length :type: svg_path_editor.math.Expr Euclidean segment length :math:`‖q - p‖_2`. .. py:method:: inward_normal(is_ccw) Unit inward normal. For a CCW-oriented boundary, the inward normal is obtained by rotating the edge direction :math:`(Δx, Δy)` 90° clockwise; for a CW boundary, by rotating 90° counter-clockwise: .. math:: n = \begin{cases} (Δy, -Δx) & \text{if CCW} \\ (-Δy, Δx) & \text{if CW} \end{cases} The resulting vector is then normalized. :param is_ccw: ``True`` if the enclosing polygon is CCW oriented. .. py:method:: offset(*, d, is_ccw, n = None) Offset the line along its inward normal by distance ``d``. Both endpoints are translated by :math:`d ⋅ n_{\mathit{in}}`. :param d: Signed offset distance. :param is_ccw: Orientation of the surrounding boundary. :param n: Optional precision used in :meth:`Vec2.evalf`. .. py:method:: __call__(t) Evaluate the parametric line :math:`L(t) = p + (q - p)\,t`. No restriction is imposed on :math:`t`; points with :math:`t \notin [0, 1]` lie on the infinite supporting line but outside the segment. .. py:method:: __str__() Human-readable representation ``(p, q)``. .. py:method:: __repr__() Debug representation ``Line(p, q)``. .. py:class:: ParametricEllipticalArc Elliptical arc in parametric form. The underlying full ellipse is .. math:: E(θ) &= R(φ) ⋅ \begin{pmatrix} r_x \cos θ \\ r_y \sin θ \end{pmatrix} + \begin{pmatrix} c_x \\ c_y \end{pmatrix} where :math:`θ` and :math:`φ` are in degrees and :math:`(c_x, c_y)` is the center. This arc covers the interval :math:`[θ_0, θ_0 + Δθ]` modulo :math:`360°`. :ivar c: Center :math:`(c_x, c_y)`. :ivar r: Radii :math:`(r_x, r_y)`. :ivar theta0: Start angle :math:`θ_0` in degrees. :ivar dtheta: Sweep :math:`Δθ` in degrees (signed). :ivar phi: Rotation angle :math:`φ` in degrees. .. py:attribute:: c :type: Vec2 .. py:attribute:: r :type: Vec2 .. py:attribute:: theta0 :type: svg_path_editor.math.Expr .. py:attribute:: dtheta :type: svg_path_editor.math.Expr .. py:attribute:: phi :type: svg_path_editor.math.Expr .. py:property:: theta1 :type: svg_path_editor.math.Expr End angle of the arc. :return: :math:`θ_1 = θ_0 + Δθ` in degrees. .. py:method:: locally_convex(*, is_ccw) Test if the arc is locally convex with respect to the boundary orientation. :return: ``True`` iff the interior lies on the convex side of the arc for a boundary with orientation ``is_ccw``. .. py:method:: offset(*, d, is_ccw, n = None) Offset the arc by changing its radii. * :math:`d > 0`: move inward * :math:`d < 0`: move outward “Inward” is defined with respect to the polygon orientation: for a CCW boundary, the interior is to the *left* of the path, for a CW boundary to the *right*. :param d: Signed offset distance applied to both radii. :param is_ccw: Orientation of the surrounding boundary. :param n: Unused; kept for API symmetry with :meth:`Line.offset`. .. py:method:: angle_condition(theta: svg_path_editor.math.Symbol, *, n: svg_path_editor.math.Precision | None = None) -> svg_path_editor.math.Boolean angle_condition(theta: svg_path_editor.math.Expr, *, n: svg_path_editor.math.Precision | None = None) -> svg_path_editor.math.Boolean Test whether ``theta`` (in degrees) lies on this arc, modulo 360°. Works for positive and negative :math:`Δθ` and wrap-around intervals. :param theta: Angle to test, interpreted in degrees. :param n: Optional precision passed to :func:`evalf`, :func:`ge`, and :func:`le`. .. py:method:: point_tangent(theta, n = None) Point and tangent at parameter ``theta`` (in degrees). Returns :math:`(p(θ), ±p'(θ))`, where the derivative is w.r.t. :math:`θ` in degrees and not normalized. The sign of the derivative is chosen so that the tangent at :math:`θ_0` points along the arc and that at :math:`θ_1` points away from the arc. :param n: Optional precision used in :func:`evalf` and sign checks. .. py:method:: transform(p, *, inverse = False) Affine map between unit circle and this ellipse. * ``inverse=False``: :math:`(u, v) \mapsto (x, y)` on the ellipse. * ``inverse=True``: :math:`(x, y) \mapsto (u, v)` on the unit circle. The forward mapping is .. math:: (x, y) = c + R(φ)\,\mathrm{diag}(r_x, r_y)\,(u, v), where all angles are in degrees. :param p: Point to transform, either on the unit circle (forward) or on the ellipse (inverse). :param inverse: Select direction of the mapping. .. py:method:: implicit(x, y) Implicit ellipse equation at ``(x, y)``. Returns .. math:: F(x, y) = u^2 + v^2 - 1, where :math:`(u, v)` is the image of :math:`(x, y)` under the inverse transform to the unit circle. Points on the ellipse satisfy :math:`F(x, y) = 0`.