kopia lustrzana https://github.com/inkstitch/inkstitch
Undo stitch plan preview and density map (#1687)
rodzic
edacc3ec77
commit
eef2ec232e
|
@ -15,6 +15,7 @@ from .convert_to_satin import ConvertToSatin
|
|||
from .convert_to_stroke import ConvertToStroke
|
||||
from .cut_satin import CutSatin
|
||||
from .cutwork_segmentation import CutworkSegmentation
|
||||
from .density_map import DensityMap
|
||||
from .duplicate_params import DuplicateParams
|
||||
from .embroider_settings import EmbroiderSettings
|
||||
from .flip import Flip
|
||||
|
@ -39,13 +40,16 @@ from .params import Params
|
|||
from .print_pdf import Print
|
||||
from .remove_embroidery_settings import RemoveEmbroiderySettings
|
||||
from .reorder import Reorder
|
||||
from .selection_to_pattern import SelectionToPattern
|
||||
from .selection_to_guide_line import SelectionToGuideLine
|
||||
from .selection_to_pattern import SelectionToPattern
|
||||
from .simulator import Simulator
|
||||
from .stitch_plan_preview import StitchPlanPreview
|
||||
from .stitch_plan_preview_undo import StitchPlanPreviewUndo
|
||||
from .zip import Zip
|
||||
|
||||
__all__ = extensions = [StitchPlanPreview,
|
||||
StitchPlanPreviewUndo,
|
||||
DensityMap,
|
||||
Install,
|
||||
Params,
|
||||
Print,
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
# Authors: see git history
|
||||
#
|
||||
# Copyright (c) 2010 Authors
|
||||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||
|
||||
import numpy as np
|
||||
from scipy.spatial import KDTree
|
||||
|
||||
import inkex
|
||||
|
||||
from ..i18n import _
|
||||
from ..stitch_plan import stitch_groups_to_stitch_plan
|
||||
from ..svg import PIXELS_PER_MM
|
||||
from ..svg.tags import INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SVG_GROUP_TAG
|
||||
from ..svg.units import get_viewbox_transform
|
||||
from ..utils import cache
|
||||
from .base import InkstitchExtension
|
||||
|
||||
|
||||
class DensityMap(InkstitchExtension):
|
||||
def __init__(self, *args, **kwargs):
|
||||
InkstitchExtension.__init__(self, *args, **kwargs)
|
||||
self.arg_parser.add_argument("-v", "--layer-visibility", type=int, default=0, dest="layer_visibility")
|
||||
self.arg_parser.add_argument("-l", "--num-neighbors-red", type=int, default=6, dest="num_neighbors_red")
|
||||
self.arg_parser.add_argument("-r", "--density-radius-red", type=float, default=0.5, dest="radius_red")
|
||||
self.arg_parser.add_argument("-m", "--num-neighbors-yellow", type=int, default=3, dest="num_neighbors_yellow")
|
||||
self.arg_parser.add_argument("-s", "--density-radius-yellow", type=float, default=0.5, dest="radius_yellow")
|
||||
|
||||
def effect(self):
|
||||
# delete old stitch plan
|
||||
svg = self.document.getroot()
|
||||
reset_density_plan(svg)
|
||||
|
||||
# create new stitch plan
|
||||
if not self.get_elements():
|
||||
return
|
||||
|
||||
self.metadata = self.get_inkstitch_metadata()
|
||||
collapse_len = self.metadata['collapse_len_mm']
|
||||
patches = self.elements_to_stitch_groups(self.elements)
|
||||
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len)
|
||||
|
||||
layer = svg.find(".//*[@id='__inkstitch_density_plan__']")
|
||||
color_groups = create_color_groups(layer)
|
||||
density_options = [{'max_neighbors': self.options.num_neighbors_red, 'radius': self.options.radius_red},
|
||||
{'max_neighbors': self.options.num_neighbors_yellow, 'radius': self.options.radius_yellow}]
|
||||
color_block_to_density_markers(svg, color_groups, stitch_plan, density_options)
|
||||
|
||||
# update layer visibility 0 = unchanged, 1 = hidden, 2 = lower opacity
|
||||
groups = self.document.getroot().findall(SVG_GROUP_TAG)
|
||||
if self.options.layer_visibility == 1:
|
||||
self.hide_all_layers()
|
||||
layer.style['display'] = "inline"
|
||||
elif self.options.layer_visibility == 2:
|
||||
for g in groups:
|
||||
style = g.specified_style()
|
||||
# check groupmode and exclude density layer
|
||||
# exclude objects which are not displayed at all or already have opacity < 0.4
|
||||
if (g.get(INKSCAPE_GROUPMODE) == "layer" and not g == layer and
|
||||
float(style.get('opacity', 1)) > 0.4 and not style.get('display', 'inline') == 'none'):
|
||||
g.style['opacity'] = 0.4
|
||||
|
||||
|
||||
def reset_density_plan(svg):
|
||||
layer = svg.find(".//*[@id='__inkstitch_density_plan__']")
|
||||
if layer is None:
|
||||
layer = inkex.Group(attrib={
|
||||
'id': '__inkstitch_density_plan__',
|
||||
INKSCAPE_LABEL: _('Density Plan'),
|
||||
INKSCAPE_GROUPMODE: 'layer'
|
||||
})
|
||||
svg.append(layer)
|
||||
else:
|
||||
# delete old density plan
|
||||
del layer[:]
|
||||
|
||||
# make sure the layer is visible
|
||||
layer.set('style', 'display:inline')
|
||||
|
||||
|
||||
def create_color_groups(layer):
|
||||
color_groups = []
|
||||
colors = [_("Red"), _("Yellow"), _("Green")]
|
||||
for color in colors:
|
||||
color_group = inkex.Group(attrib={
|
||||
'id': '__%s_density_layer__' % color.lower(),
|
||||
INKSCAPE_LABEL: _('%s density') % color,
|
||||
})
|
||||
layer.append(color_group)
|
||||
color_groups.append(color_group)
|
||||
return color_groups
|
||||
|
||||
|
||||
def color_block_to_density_markers(svg, groups, stitch_plan, density_options):
|
||||
num_neighbors = []
|
||||
for option in density_options:
|
||||
radius = option['radius'] * PIXELS_PER_MM
|
||||
num_neighbors.append(get_stitch_density(stitch_plan, radius))
|
||||
|
||||
red_group, yellow_group, green_group = groups
|
||||
for red_neighbors, yellow_neighbors, coord in zip(num_neighbors[0][0], num_neighbors[1][0], num_neighbors[0][1]):
|
||||
color = "green" # green
|
||||
group = green_group
|
||||
if density_options[0]['max_neighbors'] < red_neighbors:
|
||||
color = "yellow"
|
||||
group = yellow_group
|
||||
elif density_options[1]['max_neighbors'] < yellow_neighbors:
|
||||
color = "red"
|
||||
group = red_group
|
||||
density_marker = inkex.Circle(attrib={
|
||||
'id': svg.get_unique_id("density_marker"),
|
||||
'style': "fill: %s; stroke: #7e7e7e; stroke-width: 0.02%%;" % color,
|
||||
'cx': "%s" % coord[0],
|
||||
'cy': "%s" % coord[1],
|
||||
'r': str(0.5),
|
||||
'transform': get_correction_transform(svg)
|
||||
})
|
||||
group.append(density_marker)
|
||||
|
||||
|
||||
def get_stitch_density(stitch_plan, radius):
|
||||
stitches = []
|
||||
for color_block in stitch_plan:
|
||||
for stitch in color_block:
|
||||
stitches.append((stitch.x, stitch.y))
|
||||
|
||||
# get density per stitch
|
||||
tree = KDTree(np.array(stitches))
|
||||
neighbors = tree.query_ball_tree(tree, radius)
|
||||
density = [len(i) for i in neighbors], stitches
|
||||
|
||||
return density
|
||||
|
||||
|
||||
@cache
|
||||
def get_correction_transform(svg):
|
||||
transform = get_viewbox_transform(svg)
|
||||
|
||||
# we need to correct for the viewbox
|
||||
transform = -inkex.transforms.Transform(transform)
|
||||
|
||||
return str(transform)
|
|
@ -3,14 +3,16 @@
|
|||
# Copyright (c) 2010 Authors
|
||||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||
|
||||
from inkex import Boolean, Style
|
||||
from lxml import etree
|
||||
|
||||
from inkex import Boolean, Style
|
||||
|
||||
from ..stitch_plan import stitch_groups_to_stitch_plan
|
||||
from ..svg import render_stitch_plan
|
||||
from ..svg.tags import (INKSCAPE_GROUPMODE, SVG_DEFS_TAG, SVG_GROUP_TAG,
|
||||
SVG_PATH_TAG)
|
||||
from ..svg.tags import (INKSCAPE_GROUPMODE, INKSTITCH_ATTRIBS, SVG_DEFS_TAG,
|
||||
SVG_GROUP_TAG, SVG_PATH_TAG)
|
||||
from .base import InkstitchExtension
|
||||
from .stitch_plan_preview_undo import reset_stitch_plan
|
||||
|
||||
|
||||
class StitchPlanPreview(InkstitchExtension):
|
||||
|
@ -23,37 +25,37 @@ class StitchPlanPreview(InkstitchExtension):
|
|||
def effect(self):
|
||||
# delete old stitch plan
|
||||
svg = self.document.getroot()
|
||||
layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']")
|
||||
if layer is not None:
|
||||
del layer[:]
|
||||
reset_stitch_plan(svg)
|
||||
|
||||
# create new stitch plan
|
||||
if not self.get_elements():
|
||||
return
|
||||
|
||||
realistic = False
|
||||
visual_commands = True
|
||||
self.metadata = self.get_inkstitch_metadata()
|
||||
collapse_len = self.metadata['collapse_len_mm']
|
||||
patches = self.elements_to_stitch_groups(self.elements)
|
||||
stitch_plan = stitch_groups_to_stitch_plan(patches, collapse_len=collapse_len)
|
||||
render_stitch_plan(svg, stitch_plan, realistic)
|
||||
render_stitch_plan(svg, stitch_plan, realistic, visual_commands)
|
||||
|
||||
# apply options
|
||||
layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']")
|
||||
|
||||
# update layer visibility 0 = unchanged, 1 = hidden, 2 = lower opacity
|
||||
groups = self.document.getroot().findall(SVG_GROUP_TAG)
|
||||
if self.options.layer_visibility == 1:
|
||||
self.set_invisible_layers_attribute(groups, layer)
|
||||
self.hide_all_layers()
|
||||
layer.set('style', None)
|
||||
layer.style['display'] = "inline"
|
||||
elif self.options.layer_visibility == 2:
|
||||
for g in self.document.getroot().findall(SVG_GROUP_TAG):
|
||||
for g in groups:
|
||||
style = g.specified_style()
|
||||
# check groupmode and exclude stitch_plan layer
|
||||
# exclude objects which are not displayed at all or already have opacity < 0.4
|
||||
if (g.get(INKSCAPE_GROUPMODE) == "layer" and not g == layer and
|
||||
float(style.get('opacity', 1)) > 0.4 and not style.get('display', 'inline') == 'none'):
|
||||
style += Style('opacity:0.4')
|
||||
g.set("style", style)
|
||||
g.style['opacity'] = 0.4
|
||||
|
||||
# translate stitch plan to the right side of the canvas
|
||||
if self.options.move_to_side:
|
||||
|
@ -65,10 +67,17 @@ class StitchPlanPreview(InkstitchExtension):
|
|||
if self.options.needle_points:
|
||||
markers = 'marker-mid:url(#inkstitch-needle-point);marker-start:url(#inkstitch-needle-point);marker-end:url(#inkstitch-needle-point)'
|
||||
for element in layer.iterdescendants(SVG_PATH_TAG):
|
||||
style = ';'.join([element.get('style'), markers])
|
||||
style = element.style + Style(markers)
|
||||
element.set('style', style)
|
||||
self.ensure_marker()
|
||||
|
||||
def set_invisible_layers_attribute(self, groups, layer):
|
||||
invisible_layers = []
|
||||
for g in groups:
|
||||
if g.get(INKSCAPE_GROUPMODE) == "layer" and 'display' in g.style and g.style['display'] == 'none':
|
||||
invisible_layers.append(g.get_id())
|
||||
layer.set(INKSTITCH_ATTRIBS['invisible_layers'], ",".join(invisible_layers))
|
||||
|
||||
def ensure_marker(self):
|
||||
xpath = ".//svg:marker[@id='inkstitch-needle-point']"
|
||||
point_marker = self.document.getroot().xpath(xpath)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Authors: see git history
|
||||
#
|
||||
# Copyright (c) 2022 Authors
|
||||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||
|
||||
from ..svg.tags import INKSCAPE_GROUPMODE, INKSTITCH_ATTRIBS, SVG_GROUP_TAG
|
||||
from .base import InkstitchExtension
|
||||
|
||||
|
||||
class StitchPlanPreviewUndo(InkstitchExtension):
|
||||
def effect(self):
|
||||
reset_stitch_plan(self.document.getroot())
|
||||
|
||||
|
||||
def reset_stitch_plan(svg):
|
||||
# delete old stitch plan
|
||||
layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']")
|
||||
# get previously invisible layers (they still should be hidden afterwards)
|
||||
if layer is not None:
|
||||
invisible_layers = layer.get(INKSTITCH_ATTRIBS['invisible_layers'], '').split(",")
|
||||
layer.getparent().remove(layer)
|
||||
|
||||
# if there are layers with reduced opacity, remove opacity attribute or unhide hidden layers
|
||||
for g in svg.findall(SVG_GROUP_TAG):
|
||||
style = g.specified_style()
|
||||
if (g.get(INKSCAPE_GROUPMODE) == "layer" and float(style.get('opacity', 1)) == 0.4 or style.get('display', 'inline') == 'none'):
|
||||
if g.get_id() in invisible_layers:
|
||||
continue
|
||||
|
||||
g.style['opacity'] = 1
|
||||
g.style['display'] = 'inline'
|
|
@ -8,10 +8,10 @@ from math import pi
|
|||
|
||||
import inkex
|
||||
|
||||
from .tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, INKSTITCH_ATTRIBS)
|
||||
from .units import PIXELS_PER_MM, get_viewbox_transform
|
||||
from ..i18n import _
|
||||
from ..utils import Point, cache
|
||||
from .tags import INKSCAPE_GROUPMODE, INKSCAPE_LABEL, INKSTITCH_ATTRIBS
|
||||
from .units import PIXELS_PER_MM, get_viewbox_transform
|
||||
|
||||
# The stitch vector path looks like this:
|
||||
# _______
|
||||
|
|
|
@ -112,6 +112,8 @@ inkstitch_attribs = [
|
|||
'e_stitch',
|
||||
'pull_compensation_mm',
|
||||
'stroke_first',
|
||||
# stitch_plan
|
||||
'invisible_layers',
|
||||
# Legacy
|
||||
'trim_after',
|
||||
'stop_after',
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Density Map</name>
|
||||
<id>org.inkstitch.density_map</id>
|
||||
<param name="extension" type="string" gui-hidden="true">density_map</param>
|
||||
<effect>
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="Ink/Stitch" translatable="no">
|
||||
<submenu name="Visualise and Export" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<label appearance="header">Red markers</label>
|
||||
<param name="num-neighbors-red" gui-text="Number of stitches" gui-description="0 = no density info" type="int" min="0" max="99" indents="1">6</param>
|
||||
<param name="density-radius-red" gui-text="within a radius (mm) of" type="float" min="0" max="50" indents="1">0.5</param>
|
||||
<spacer />
|
||||
<label appearance="header">Yellow markers</label>
|
||||
<param name="num-neighbors-yellow" gui-text="Number of stitches" gui-description="0 = no density info" type="int" min="0" max="99" indents="1">3</param>
|
||||
<param name="density-radius-yellow" gui-text="within a radius (mm) of" type="float" min="0" max="50">0.5</param>
|
||||
<spacer />
|
||||
<param name="layer-visibility" type="optiongroup" appearance="combo" gui-text="Design layer visibility" indents="1">
|
||||
<option value="0">Unchanged</option>
|
||||
<option value="1">Hidden</option>
|
||||
<option value="2">Lower opacity</option>
|
||||
</param>
|
||||
<script>
|
||||
{{ command_tag | safe }}
|
||||
</script>
|
||||
</inkscape-extension>
|
|
@ -18,8 +18,7 @@
|
|||
<option value="2">Lower opacity</option>
|
||||
</param>
|
||||
<param name="needle-points" type="boolean" gui-text="Needle points">false</param>
|
||||
<separator />
|
||||
<label>Hit Ctrl+Z to undo this action after inspection.</label>
|
||||
<spacer />
|
||||
<script>
|
||||
{{ command_tag | safe }}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Undo Stitch Plan Preview</name>
|
||||
<id>org.inkstitch.stitch_plan_preview_undo</id>
|
||||
<param name="extension" type="string" gui-hidden="true">stitch_plan_preview_undo</param>
|
||||
<effect>
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="Ink/Stitch" translatable="no">
|
||||
<submenu name="Visualise and Export" />
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
{{ command_tag | safe }}
|
||||
</script>
|
||||
</inkscape-extension>
|
Ładowanie…
Reference in New Issue