bundle: update (2026-01-18)
This commit is contained in:
196
extensions/km-hatch/deps/inkex/elements/_groups.py
Normal file
196
extensions/km-hatch/deps/inkex/elements/_groups.py
Normal file
@@ -0,0 +1,196 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2020 Martin Owens <doctormo@gmail.com>
|
||||
# Sergei Izmailov <sergei.a.izmailov@gmail.com>
|
||||
# Ryan Jarvis <ryan@shopboxretail.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.
|
||||
#
|
||||
# pylint: disable=arguments-differ
|
||||
"""
|
||||
Interface for all group based elements such as Groups, Use, Markers etc.
|
||||
"""
|
||||
|
||||
from lxml import etree # pylint: disable=unused-import
|
||||
|
||||
from ..paths import Path
|
||||
from ..transforms import BoundingBox, Transform, Vector2d
|
||||
|
||||
from ._utils import addNS
|
||||
from ._base import ShapeElement, ViewboxMixin
|
||||
from ._polygons import PathElement
|
||||
|
||||
try:
|
||||
from typing import Optional, List # pylint: disable=unused-import
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class GroupBase(ShapeElement):
|
||||
"""Base Group element"""
|
||||
|
||||
def get_path(self):
|
||||
ret = Path()
|
||||
for child in self:
|
||||
if isinstance(child, ShapeElement):
|
||||
child_path = child.path.transform(child.transform)
|
||||
if child_path and child_path[0].is_relative:
|
||||
child_path[0] = child_path[0].to_absolute(Vector2d(0, 0))
|
||||
ret += child_path
|
||||
return ret
|
||||
|
||||
def bounding_box(self, transform=None):
|
||||
# type: (Optional[Transform]) -> Optional[BoundingBox]
|
||||
"""BoundingBox of the shape
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
Exclude invisible child objects from bounding box computation
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
result adjusted for element's clip path if applicable.
|
||||
"""
|
||||
bbox = None
|
||||
effective_transform = Transform(transform) @ self.transform
|
||||
for child in self:
|
||||
if isinstance(child, ShapeElement) and child.is_visible():
|
||||
child_bbox = child.bounding_box(transform=effective_transform)
|
||||
if child_bbox is not None:
|
||||
bbox += child_bbox
|
||||
clip = self.clip
|
||||
if clip is None or bbox is None:
|
||||
return bbox
|
||||
return bbox & clip.bounding_box(Transform(transform) @ self.transform)
|
||||
|
||||
def shape_box(self, transform=None):
|
||||
# type: (Optional[Transform]) -> Optional[BoundingBox]
|
||||
"""BoundingBox of the unclipped shape
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
returns the bounding box without possible clip effects of child objects
|
||||
|
||||
.. versionadded:: 1.1
|
||||
Previous :func:`bounding_box` function, returning the bounding box
|
||||
without computing the effect of a possible clip."""
|
||||
bbox = None
|
||||
effective_transform = Transform(transform) @ self.transform
|
||||
for child in self:
|
||||
if isinstance(child, ShapeElement):
|
||||
child_bbox = child.shape_box(transform=effective_transform)
|
||||
if child_bbox is not None:
|
||||
bbox += child_bbox
|
||||
return bbox
|
||||
|
||||
def bake_transforms_recursively(self, apply_to_paths=True):
|
||||
"""Bake transforms, i.e. each leaf node has the effective transform (starting
|
||||
from this group) set, and parent transforms are removed.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
Args:
|
||||
apply_to_paths (bool, optional): For path elements, the
|
||||
path data is transformed with its effective transform. Nodes and handles
|
||||
will have the same position as before, but visual appearance of the
|
||||
stroke may change (stroke-width is not touched). Defaults to True.
|
||||
"""
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.transform: Transform
|
||||
for element in self:
|
||||
if isinstance(element, PathElement) and apply_to_paths:
|
||||
element.path = element.path.transform(self.transform)
|
||||
else:
|
||||
element.transform = self.transform @ element.transform
|
||||
if isinstance(element, GroupBase):
|
||||
element.bake_transforms_recursively(apply_to_paths)
|
||||
self.transform = None
|
||||
|
||||
|
||||
class Group(GroupBase):
|
||||
"""Any group element (layer or regular group)"""
|
||||
|
||||
tag_name = "g"
|
||||
|
||||
@classmethod
|
||||
def new(cls, label, *children, **attrs):
|
||||
attrs["inkscape:label"] = label
|
||||
return super().new(*children, **attrs)
|
||||
|
||||
def effective_style(self):
|
||||
"""A blend of each child's style mixed together (last child wins)"""
|
||||
style = self.style
|
||||
for child in self:
|
||||
style.update(child.effective_style())
|
||||
return style
|
||||
|
||||
@property
|
||||
def groupmode(self):
|
||||
"""Return the type of group this is"""
|
||||
return self.get("inkscape:groupmode", "group")
|
||||
|
||||
|
||||
class Layer(Group):
|
||||
"""Inkscape extension of svg:g"""
|
||||
|
||||
def _init(self):
|
||||
self.set("inkscape:groupmode", "layer")
|
||||
|
||||
@classmethod
|
||||
def is_class_element(cls, elem):
|
||||
# type: (etree.Element) -> bool
|
||||
return (
|
||||
elem.get("{http://www.inkscape.org/namespaces/inkscape}groupmode", None)
|
||||
== "layer"
|
||||
)
|
||||
|
||||
|
||||
class Anchor(GroupBase):
|
||||
"""An anchor or link tag"""
|
||||
|
||||
tag_name = "a"
|
||||
|
||||
@classmethod
|
||||
def new(cls, href, *children, **attrs):
|
||||
attrs["xlink:href"] = href
|
||||
return super().new(*children, **attrs)
|
||||
|
||||
|
||||
class ClipPath(GroupBase):
|
||||
"""A path used to clip objects"""
|
||||
|
||||
tag_name = "clipPath"
|
||||
|
||||
|
||||
class Marker(GroupBase, ViewboxMixin):
|
||||
"""The <marker> element defines the graphic that is to be used for drawing
|
||||
arrowheads or polymarkers on a given <path>, <line>, <polyline> or <polygon>
|
||||
element."""
|
||||
|
||||
tag_name = "marker"
|
||||
|
||||
def get_viewbox(self) -> List[float]:
|
||||
"""Returns the viewbox of the Marker, falling back to
|
||||
[0 0 markerWidth markerHeight]
|
||||
|
||||
.. versionadded:: 1.3"""
|
||||
vbox = self.get("viewBox", None)
|
||||
result = self.parse_viewbox(vbox)
|
||||
if result is None:
|
||||
# use viewport, https://www.w3.org/TR/SVG11/painting.html#MarkerElement
|
||||
return [
|
||||
0,
|
||||
0,
|
||||
self.to_dimensionless(self.get("markerWidth")),
|
||||
self.to_dimensionless(self.get("markerHeight")),
|
||||
]
|
||||
return result
|
||||
Reference in New Issue
Block a user