kopia lustrzana https://github.com/inkstitch/inkstitch
145 wiersze
5.8 KiB
Python
145 wiersze
5.8 KiB
Python
# 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 inkex
|
|
from shapely import STRtree
|
|
from shapely.geometry import Point
|
|
|
|
from ..commands import add_layer_commands
|
|
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("--notebook")
|
|
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")
|
|
self.arg_parser.add_argument("-i", "--indicator-size", type=float, default=0.5, dest="indicator_size")
|
|
|
|
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']
|
|
min_stitch_len = self.metadata['min_stitch_len_mm']
|
|
stitch_groups = self.elements_to_stitch_groups(self.elements)
|
|
stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_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, self.options.indicator_size)
|
|
|
|
# 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)
|
|
add_layer_commands(layer, ["ignore_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, indicator_size):
|
|
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, point 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 = "red"
|
|
group = red_group
|
|
elif density_options[1]['max_neighbors'] <= yellow_neighbors:
|
|
color = "yellow"
|
|
group = yellow_group
|
|
density_marker = inkex.Circle(attrib={
|
|
'id': svg.get_unique_id("density_marker"),
|
|
'style': "fill: %s; stroke: #7e7e7e; stroke-width: 0.02%%;" % color,
|
|
'cx': str(point.coords[0][0]),
|
|
'cy': str(point.coords[0][1]),
|
|
'r': str(indicator_size * 2),
|
|
'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(Point(stitch.x, stitch.y))
|
|
|
|
points = STRtree(stitches)
|
|
density = [len(points.query(stitch, 'dwithin', radius)) for stitch in stitches], 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)
|