inkstitch/lib/elements/utils.py

140 wiersze
4.9 KiB
Python

2021-03-12 04:17:19 +00:00
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
2025-03-08 20:33:38 +00:00
from typing import List, Optional, Iterable
from inkex import BaseElement
from lxml.etree import Comment
from ..commands import is_command, layer_commands
from ..debug.utils import safe_get
from ..marker import has_marker
from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE,
NOT_EMBROIDERABLE_TAGS, SVG_CLIPPATH_TAG, SVG_DEFS_TAG,
SVG_GROUP_TAG, SVG_IMAGE_TAG, SVG_MASK_TAG,
SVG_TEXT_TAG)
from ..utils.paths import get_ini
2020-05-16 21:01:00 +00:00
from .clone import Clone, is_clone
from .element import EmbroideryElement
2020-05-27 16:39:04 +00:00
from .empty_d_object import EmptyDObject
from .fill_stitch import FillStitch
2020-05-16 21:01:00 +00:00
from .image import ImageObject
from .marker import MarkerObject
from .satin_column import SatinColumn
from .stroke import Stroke
2020-05-16 21:01:00 +00:00
from .text import TextObject
def node_to_elements(node, clone_to_element=False) -> List[EmbroideryElement]: # noqa: C901
if node.style('display') == 'none':
return []
if is_clone(node) and not clone_to_element:
# clone_to_element: get an actual embroiderable element once a clone has been defined as a clone
2020-05-16 21:01:00 +00:00
return [Clone(node)]
elif node.tag in EMBROIDERABLE_TAGS and not node.get_path():
2020-05-27 16:39:04 +00:00
return [EmptyDObject(node)]
elif has_marker(node):
return [MarkerObject(node)]
elif node.tag in EMBROIDERABLE_TAGS or is_clone(node):
2024-09-17 00:28:06 +00:00
elements: List[EmbroideryElement] = []
Sew Stack first steps (#3133) * handle more recursive cases * scaffolding for stitch layers * scaffolding for SewStack * always use DotDict when parsing json params * add DefaultDotDict + DotDict fixes * first working SewStack (no UI yet) * ignore inkstitch_debug.log and .svg * refactor * early WIP: property grid display temporarily in stitch plan preview * start of sew stack editor extension * add layer properties panel and splitter * spacing and better icon * handle checkbox * add layer action buttons * show selected property help text in an HtmlWindow * rename * rephrase help text for tolerance * refactor into separate file * simplify structure * better property type handling * add randomization button * add random seed re-roll button * simulator preview * update preview in a few more cases * always DotDict * avoid ridiculously slow simulations * preview selected layer or all layers * edit multiple objects and save only modified properties into the SVG * better preview handling * add reverse and jitter * add stitch path jitter * fix types * fix random shuffle button * fixes * fix repeats * type hinting to please pycharm * show layer description * avoid exception in properties with multiple values * fix typing * fix new layer * draw a box around property grid and help box * confirm before closing * rename properties and fix seed * fix close/cancel logic * add buttons to undo changes and reset to default value * set not modified if default is original setting * fix invisible icon * more space for properties * fix random properties * better regulation of simulator rendering speed * Fixed timer being passed a float * fix get_json_param() default handling * fix tests * add checkbox for sew stack only * fix property help * adjustable stitch layer editor help box size, with persistence * repeat exact stitches * "fix" style * adjust for new next_element stuff --------- Co-authored-by: CapellanCitizen <thecapellancitizen@gmail.com>
2025-01-29 17:04:07 +00:00
from ..sew_stack import SewStack
sew_stack = SewStack(node)
if not sew_stack.sew_stack_only:
element = EmbroideryElement(node)
if element.get_style("fill", "black") and not element.get_style('fill-opacity', 1) == "0":
elements.append(FillStitch(node))
if element.get_style("stroke"):
if element.get_boolean_param("satin_column") and len(element.path) > 1:
elements.append(SatinColumn(node))
elif not is_command(element.node):
elements.append(Stroke(node))
if element.get_boolean_param("stroke_first", False):
elements.reverse()
if safe_get(get_ini(), "DEBUG", "sew_stack_enable", default=False):
elements.append(sew_stack)
Sew Stack first steps (#3133) * handle more recursive cases * scaffolding for stitch layers * scaffolding for SewStack * always use DotDict when parsing json params * add DefaultDotDict + DotDict fixes * first working SewStack (no UI yet) * ignore inkstitch_debug.log and .svg * refactor * early WIP: property grid display temporarily in stitch plan preview * start of sew stack editor extension * add layer properties panel and splitter * spacing and better icon * handle checkbox * add layer action buttons * show selected property help text in an HtmlWindow * rename * rephrase help text for tolerance * refactor into separate file * simplify structure * better property type handling * add randomization button * add random seed re-roll button * simulator preview * update preview in a few more cases * always DotDict * avoid ridiculously slow simulations * preview selected layer or all layers * edit multiple objects and save only modified properties into the SVG * better preview handling * add reverse and jitter * add stitch path jitter * fix types * fix random shuffle button * fixes * fix repeats * type hinting to please pycharm * show layer description * avoid exception in properties with multiple values * fix typing * fix new layer * draw a box around property grid and help box * confirm before closing * rename properties and fix seed * fix close/cancel logic * add buttons to undo changes and reset to default value * set not modified if default is original setting * fix invisible icon * more space for properties * fix random properties * better regulation of simulator rendering speed * Fixed timer being passed a float * fix get_json_param() default handling * fix tests * add checkbox for sew stack only * fix property help * adjustable stitch layer editor help box size, with persistence * repeat exact stitches * "fix" style * adjust for new next_element stuff --------- Co-authored-by: CapellanCitizen <thecapellancitizen@gmail.com>
2025-01-29 17:04:07 +00:00
return elements
2020-05-16 21:01:00 +00:00
elif node.tag == SVG_IMAGE_TAG:
return [ImageObject(node)]
elif node.tag == SVG_TEXT_TAG:
return [TextObject(node)]
else:
return []
2025-03-08 20:33:38 +00:00
def nodes_to_elements(nodes: Iterable[BaseElement]) -> List[EmbroideryElement]:
elements = []
for node in nodes:
elements.extend(node_to_elements(node))
return elements
def iterate_nodes(node: BaseElement, # noqa: C901
selection: Optional[List[BaseElement]] = None,
troubleshoot=False) -> List[BaseElement]:
# Postorder traversal of selected nodes and their descendants.
# Returns all nodes if there is no selection.
def walk(node: BaseElement, selected: bool) -> List[BaseElement]:
nodes = []
2025-03-08 20:33:38 +00:00
# lxml-stubs types are wrong, node.tag can be Comment.
if node.tag is Comment: # type:ignore[comparison-overlap]
return []
element = EmbroideryElement(node)
if element.has_command('ignore_object'):
return []
if node.tag == SVG_GROUP_TAG and node.get(INKSCAPE_GROUPMODE) == "layer":
if len(list(layer_commands(node, "ignore_layer"))):
return []
if (node.tag in EMBROIDERABLE_TAGS or node.tag == SVG_GROUP_TAG) and element.get_style('display', 'inline') is None:
return []
# defs, masks and clippaths can contain embroiderable elements
# but should never be rendered directly.
if node.tag in [SVG_DEFS_TAG, SVG_MASK_TAG, SVG_CLIPPATH_TAG]:
return []
# command connectors with a fill color set, will glitch into the elements list
if is_command(node) or node.get(CONNECTOR_TYPE):
return []
if not selected:
if selection:
if node in selection:
selected = True
else:
# if the user didn't select anything that means we process everything
selected = True
for child in node:
nodes.extend(walk(child, selected))
if selected:
if node.tag == SVG_GROUP_TAG:
pass
elif (node.tag in EMBROIDERABLE_TAGS or is_clone(node)) and not has_marker(node):
nodes.append(node)
# add images, text and elements with a marker for the troubleshoot extension
elif troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or has_marker(node)):
nodes.append(node)
return nodes
return walk(node, False)