inkstitch/lib/extensions/density_map.py

146 wiersze
5.7 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
import numpy as np
from scipy.spatial import KDTree
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("-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, 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 = "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': "%s" % coord[0],
'cy': "%s" % coord[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((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)