bundle: update (2026-01-16)
This commit is contained in:
601
extensions/botbox3000/deps/inkex/paths/lines.py
Normal file
601
extensions/botbox3000/deps/inkex/paths/lines.py
Normal file
@@ -0,0 +1,601 @@
|
||||
# coding=utf-8
|
||||
#
|
||||
# Copyright (C) 2018 Martin Owens <doctormo@gmail.com>
|
||||
# Copyright (C) 2023 Jonathan Neuhauser <jonathan.neuhauser@outlook.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
"""Line-like path commands (Line, Horz, Vert, ZoneClose and their relative siblings)"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import overload, Tuple, Optional, TYPE_CHECKING, Callable, Union
|
||||
|
||||
from inkex.paths.interfaces import ILengthSettings, LengthSettings
|
||||
|
||||
from ..transforms import Transform, BoundingBox, ComplexLike
|
||||
|
||||
from .interfaces import AbsolutePathCommand, RelativePathCommand, ILengthSettings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .curves import Curve
|
||||
|
||||
|
||||
class LineMixin:
|
||||
"""Common Line functions"""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
arg1: complex
|
||||
|
||||
cend_point: Callable[[complex, complex], complex]
|
||||
|
||||
def ccurve_points(self, first: complex, prev: complex, prev_prev: complex):
|
||||
"""Common implementation of ccurve_points for Lines"""
|
||||
arg1 = self.cend_point(first, prev)
|
||||
return prev, arg1, arg1
|
||||
|
||||
def ccontrol_points(
|
||||
self,
|
||||
first: complex,
|
||||
prev: complex,
|
||||
prev_prev: complex, # pylint: disable=unused-argument
|
||||
) -> Tuple[complex, ...]:
|
||||
"""Common implementation of ccontrol_points for Lines"""
|
||||
return (self.cend_point(first, prev),)
|
||||
|
||||
def _cderivative(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float, n: int = 1
|
||||
) -> complex:
|
||||
start = self.cend_point(first, prev)
|
||||
if prev == start:
|
||||
raise ValueError("Derivative is not defined for zero-length segments")
|
||||
if n == 1:
|
||||
return start - prev
|
||||
return 0j
|
||||
|
||||
def _curvature(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> float:
|
||||
return 0
|
||||
|
||||
def _cpoint(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> complex:
|
||||
return self.cend_point(first, prev) * t + (1 - t) * prev
|
||||
|
||||
def _length(
|
||||
self,
|
||||
first: complex,
|
||||
prev: complex,
|
||||
prev_control: complex,
|
||||
t0: float = 0,
|
||||
t1: float = 1,
|
||||
settings=LengthSettings(),
|
||||
) -> float:
|
||||
return abs(self.cend_point(first, prev) - prev) * (t1 - t0)
|
||||
|
||||
def _ilength(
|
||||
self,
|
||||
first: complex,
|
||||
prev: complex,
|
||||
prev_control: complex,
|
||||
length: float,
|
||||
settings: ILengthSettings = ILengthSettings(),
|
||||
):
|
||||
return length / self._length(first, prev, prev_control)
|
||||
|
||||
|
||||
class Line(LineMixin, AbsolutePathCommand):
|
||||
"""Line segment"""
|
||||
|
||||
letter = "L"
|
||||
nargs = 2
|
||||
|
||||
arg1: complex
|
||||
"""The (absolute) end points of the line."""
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"""x coordinate of the line's (absolute) end point."""
|
||||
return self.arg1.real
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"""y coordinate of the line's (absolute) end point."""
|
||||
return self.arg1.imag
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self.x, self.y
|
||||
|
||||
@overload
|
||||
def __init__(self, x: ComplexLike): ...
|
||||
|
||||
@overload
|
||||
def __init__(self, x: float, y: float): ...
|
||||
|
||||
def __init__(self, x, y=None):
|
||||
if y is not None:
|
||||
self.arg1 = x + y * 1j
|
||||
else:
|
||||
self.arg1 = complex(x)
|
||||
|
||||
def update_bounding_box(self, first, last_two_points, bbox):
|
||||
bbox += BoundingBox(
|
||||
(last_two_points[-1].real, self.x), (last_two_points[-1].imag, self.y)
|
||||
)
|
||||
|
||||
def to_relative(self, prev: ComplexLike) -> line:
|
||||
return line(self.arg1 - prev)
|
||||
|
||||
def transform(self, transform) -> Line:
|
||||
return Line(transform.capply_to_point(self.arg1))
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return self.arg1
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> Line:
|
||||
return Line(prev)
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[Line, Line]:
|
||||
return Line(self._cpoint(first, prev, prev_control, t)), Line(self.arg1)
|
||||
|
||||
|
||||
class line(LineMixin, RelativePathCommand): # pylint: disable=invalid-name
|
||||
"""Relative line segment"""
|
||||
|
||||
letter = "l"
|
||||
nargs = 2
|
||||
|
||||
arg1: complex
|
||||
"""The (relative) end points of the line."""
|
||||
|
||||
@property
|
||||
def dx(self):
|
||||
"""x coordinate of the line's (relative) end point."""
|
||||
return self.arg1.real
|
||||
|
||||
@property
|
||||
def dy(self):
|
||||
"""y coordinate of the line's (relative) end point."""
|
||||
return self.arg1.imag
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self.dx, self.dy
|
||||
|
||||
@overload
|
||||
def __init__(self, dx: ComplexLike): ...
|
||||
|
||||
@overload
|
||||
def __init__(self, dx: float, dy: float): ...
|
||||
|
||||
def __init__(self, dx, dy=None):
|
||||
if dy is not None:
|
||||
self.arg1 = dx + dy * 1j
|
||||
else:
|
||||
self.arg1 = complex(dx)
|
||||
|
||||
def to_absolute(self, prev: ComplexLike) -> Line:
|
||||
return Line(prev + self.arg1)
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return self.arg1 + prev
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> line:
|
||||
return line(-self.arg1)
|
||||
|
||||
def to_curve(
|
||||
self, prev: ComplexLike, prev_prev: Optional[ComplexLike] = 0j
|
||||
) -> Curve:
|
||||
raise ValueError("Move segments can not be changed into curves.")
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[line, line]:
|
||||
dx1 = self.cpoint(first, prev, prev_control, t) - prev
|
||||
return line(dx1), line(self.arg1 - dx1)
|
||||
|
||||
|
||||
class MoveMixin:
|
||||
"""Disable derivative / length method for Move command."""
|
||||
|
||||
def _cderivative(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float, n: int = 1
|
||||
) -> complex:
|
||||
raise ValueError("Derivative is not supported for move/Move")
|
||||
|
||||
def _cunit_tangent(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> complex:
|
||||
raise ValueError("Unit Tangent is not supported for move/Move")
|
||||
|
||||
def _curvature(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> float:
|
||||
raise ValueError("Curvature is not supported for move/Move")
|
||||
|
||||
def _cpoint(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> complex:
|
||||
raise ValueError("Point is not supported for move/Move")
|
||||
|
||||
def _length(
|
||||
self,
|
||||
first: complex,
|
||||
prev: complex,
|
||||
prev_control: complex,
|
||||
t0: float = 0,
|
||||
t1: float = 1,
|
||||
settings=LengthSettings(),
|
||||
) -> float:
|
||||
raise ValueError("Length is not supported for move/Move")
|
||||
|
||||
def _ilength(
|
||||
self,
|
||||
first: complex,
|
||||
prev: complex,
|
||||
prev_control: complex,
|
||||
length: float,
|
||||
settings: ILengthSettings = ILengthSettings(),
|
||||
):
|
||||
raise ValueError("ILength is not supported for move/Move")
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[Move, Move]:
|
||||
raise ValueError("Split is not supported for move/Move")
|
||||
|
||||
|
||||
class Move(MoveMixin, AbsolutePathCommand):
|
||||
"""Move pen segment without a line"""
|
||||
|
||||
letter = "M"
|
||||
nargs = 2
|
||||
next_command = Line
|
||||
|
||||
arg1: complex
|
||||
"""The (absolute) end points of the Move command"""
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"""x coordinate of the Moves's (absolute) end point."""
|
||||
return self.arg1.real
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"""y coordinate of the Move's (absolute) end point."""
|
||||
return self.arg1.imag
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self.x, self.y
|
||||
|
||||
@overload
|
||||
def __init__(self, x: ComplexLike): ...
|
||||
|
||||
@overload
|
||||
def __init__(self, x: float, y: float): ...
|
||||
|
||||
def __init__(self, x, y=None):
|
||||
if y is not None:
|
||||
self.arg1 = x + y * 1j
|
||||
else:
|
||||
self.arg1 = complex(x)
|
||||
|
||||
def update_bounding_box(self, first, last_two_points, bbox):
|
||||
bbox += BoundingBox(self.x, self.y)
|
||||
|
||||
def ccurve_points(self, first: complex, prev: complex, prev_prev: complex):
|
||||
return prev, self.arg1, self.arg1
|
||||
|
||||
def ccontrol_points(
|
||||
self, first: complex, prev: complex, prev_prev: complex
|
||||
) -> Tuple[complex, ...]:
|
||||
return (self.arg1,)
|
||||
|
||||
def to_relative(self, prev: ComplexLike) -> move:
|
||||
return move(self.arg1 - prev)
|
||||
|
||||
def transform(self, transform: Transform) -> Move:
|
||||
return Move(transform.capply_to_point(self.arg1))
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
return self.arg1
|
||||
|
||||
def to_curve(
|
||||
self, prev: ComplexLike, prev_prev: Optional[ComplexLike] = 0j
|
||||
) -> Curve:
|
||||
raise ValueError("Move segments can not be changed into curves.")
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> Move:
|
||||
return Move(prev)
|
||||
|
||||
|
||||
class move(MoveMixin, RelativePathCommand): # pylint: disable=invalid-name
|
||||
"""Relative move segment"""
|
||||
|
||||
letter = "m"
|
||||
nargs = 2
|
||||
next_command = line
|
||||
|
||||
@property
|
||||
def dx(self):
|
||||
"""x coordinate of the moves's (relative) end point."""
|
||||
return self.arg1.real
|
||||
|
||||
@property
|
||||
def dy(self):
|
||||
"""y coordinate of the move's (relative) end point."""
|
||||
return self.arg1.imag
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self.dx, self.dy
|
||||
|
||||
@overload
|
||||
def __init__(self, dx: ComplexLike): ...
|
||||
|
||||
@overload
|
||||
def __init__(self, dx: float, dy: float): ...
|
||||
|
||||
def __init__(self, dx, dy=None):
|
||||
if dy is not None:
|
||||
self.arg1 = dx + dy * 1j
|
||||
else:
|
||||
self.arg1 = complex(dx)
|
||||
|
||||
def ccurve_points(self, first: complex, prev: complex, prev_prev: complex):
|
||||
return prev, self.arg1 + prev, self.arg1 + prev
|
||||
|
||||
def ccontrol_points(
|
||||
self, first: complex, prev: complex, prev_prev: complex
|
||||
) -> Tuple[complex, ...]:
|
||||
return (self.arg1 + prev,)
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
return self.arg1 + prev
|
||||
|
||||
def to_absolute(self, prev: ComplexLike) -> Move:
|
||||
return Move(prev + self.arg1)
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> move:
|
||||
return move(prev - first)
|
||||
|
||||
def to_curve(
|
||||
self, prev: ComplexLike, prev_prev: Optional[ComplexLike] = 0j
|
||||
) -> Curve:
|
||||
raise ValueError("Move segments can not be changed into curves.")
|
||||
|
||||
|
||||
class ZoneClose(LineMixin, AbsolutePathCommand):
|
||||
"""Close segment to finish a path"""
|
||||
|
||||
letter = "Z"
|
||||
nargs = 0
|
||||
next_command = Move
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return ()
|
||||
|
||||
def update_bounding_box(self, first, last_two_points, bbox):
|
||||
pass
|
||||
|
||||
def transform(self, transform: Transform) -> ZoneClose:
|
||||
return ZoneClose()
|
||||
|
||||
def to_relative(self, prev: ComplexLike) -> zoneClose:
|
||||
return zoneClose()
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return first
|
||||
|
||||
def to_curve(
|
||||
self, prev: ComplexLike, prev_prev: Optional[ComplexLike] = 0j
|
||||
) -> Curve:
|
||||
raise ValueError("ZoneClose segments can not be changed into curves.")
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> Line:
|
||||
return Line(prev)
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[Line, ZoneClose]:
|
||||
return Line(self._cpoint(first, prev, prev_control, t)), ZoneClose()
|
||||
|
||||
|
||||
class zoneClose(LineMixin, RelativePathCommand): # pylint: disable=invalid-name
|
||||
"""Same as above (svg says no difference)"""
|
||||
|
||||
letter = "z"
|
||||
nargs = 0
|
||||
next_command = Move
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return ()
|
||||
|
||||
def to_absolute(self, prev: ComplexLike):
|
||||
return ZoneClose()
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> line:
|
||||
return line(prev - first)
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return first
|
||||
|
||||
def to_curve(
|
||||
self, prev: ComplexLike, prev_prev: Optional[ComplexLike] = 0j
|
||||
) -> Curve:
|
||||
raise ValueError("ZoneClose segments can not be changed into curves.")
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[line, zoneClose]:
|
||||
return line(self.cpoint(first, prev, prev_control, t) - prev), zoneClose()
|
||||
|
||||
|
||||
class Horz(LineMixin, AbsolutePathCommand):
|
||||
"""Horizontal Line segment"""
|
||||
|
||||
letter = "H"
|
||||
nargs = 1
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return (self.x,)
|
||||
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
def update_bounding_box(self, first, last_two_points, bbox):
|
||||
bbox += BoundingBox(
|
||||
(last_two_points[-1].real, self.x), last_two_points[-1].imag
|
||||
)
|
||||
|
||||
def to_relative(self, prev: ComplexLike) -> horz:
|
||||
return horz(self.x - complex(prev).real)
|
||||
|
||||
def to_non_shorthand(self, prev: ComplexLike, prev_control: ComplexLike) -> Line:
|
||||
return self.to_line(prev)
|
||||
|
||||
def transform(self, transform: Transform) -> AbsolutePathCommand:
|
||||
raise ValueError("Horizontal lines can't be transformed directly.")
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return self.x + prev.imag * 1j
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> Horz:
|
||||
return Horz(complex(prev).real)
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[Horz, Horz]:
|
||||
return Horz(self.cpoint(first, prev, prev_control, t).real), Horz(self.x)
|
||||
|
||||
|
||||
class horz(LineMixin, RelativePathCommand): # pylint: disable=invalid-name
|
||||
"""Relative horz line segment"""
|
||||
|
||||
letter = "h"
|
||||
nargs = 1
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return (self.dx,)
|
||||
|
||||
def __init__(self, dx):
|
||||
self.dx = dx
|
||||
|
||||
def to_absolute(self, prev: ComplexLike) -> Horz:
|
||||
return Horz(complex(prev).real + self.dx)
|
||||
|
||||
def to_non_shorthand(self, prev: ComplexLike, prev_control: ComplexLike) -> Line:
|
||||
return self.to_line(prev)
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return (self.dx + prev.real) + prev.imag * 1j
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> horz:
|
||||
return horz(-self.dx)
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[horz, horz]:
|
||||
dx1 = (self.cpoint(first, prev, prev_control, t) - prev).real
|
||||
return horz(dx1), horz(self.dx - dx1)
|
||||
|
||||
|
||||
class Vert(LineMixin, AbsolutePathCommand):
|
||||
"""Vertical Line segment"""
|
||||
|
||||
letter = "V"
|
||||
nargs = 1
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return (self.y,)
|
||||
|
||||
def __init__(self, y):
|
||||
self.y = y
|
||||
|
||||
def update_bounding_box(self, first, last_two_points, bbox):
|
||||
bbox += BoundingBox(
|
||||
last_two_points[-1].real, (last_two_points[-1].imag, self.y)
|
||||
)
|
||||
|
||||
def transform(self, transform: Transform) -> AbsolutePathCommand:
|
||||
raise ValueError("Vertical lines can't be transformed directly.")
|
||||
|
||||
def to_non_shorthand(self, prev: ComplexLike, prev_control: ComplexLike) -> Line:
|
||||
return self.to_line(prev)
|
||||
|
||||
def to_relative(self, prev: ComplexLike) -> vert:
|
||||
return vert(self.y - complex(prev).imag)
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return prev.real + self.y * 1j
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> Vert:
|
||||
return Vert(complex(prev).imag)
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[Vert, Vert]:
|
||||
return Vert(self.cpoint(first, prev, prev_control, t).imag), Vert(self.y)
|
||||
|
||||
|
||||
class vert(LineMixin, RelativePathCommand): # pylint: disable=invalid-name
|
||||
"""Relative vertical line segment"""
|
||||
|
||||
letter = "v"
|
||||
nargs = 1
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return (self.dy,)
|
||||
|
||||
def __init__(self, dy):
|
||||
self.dy = dy
|
||||
|
||||
def to_absolute(self, prev: ComplexLike) -> Vert:
|
||||
return Vert(complex(prev).imag + self.dy)
|
||||
|
||||
def to_non_shorthand(self, prev: ComplexLike, prev_control: ComplexLike) -> Line:
|
||||
return self.to_line(prev)
|
||||
|
||||
def cend_point(self, first: complex, prev: complex) -> complex:
|
||||
# pylint: disable=unused-argument
|
||||
return prev.real + (prev.imag + self.dy) * 1j
|
||||
|
||||
def reverse(self, first: ComplexLike, prev: ComplexLike) -> vert:
|
||||
return vert(-self.dy)
|
||||
|
||||
def _split(
|
||||
self, first: complex, prev: complex, prev_control: complex, t: float
|
||||
) -> Tuple[vert, vert]:
|
||||
dy1 = (self.cpoint(first, prev, prev_control, t) - prev).imag
|
||||
return vert(dy1), vert(self.dy - dy1)
|
||||
Reference in New Issue
Block a user