cleaned and fixed chain_paths

This commit is contained in:
Mario Voigt 2020-08-07 20:18:20 +02:00
parent ea71e26473
commit 008230e9f7
5 changed files with 2101 additions and 25 deletions

View File

@ -28,31 +28,13 @@ __version__ = '0.7' # Keep in sync with chain_paths.inx ca line 22
__author__ = 'Juergen Weigert <juergen@fabmail.org>' __author__ = 'Juergen Weigert <juergen@fabmail.org>'
__credits__ = ['Juergen Weigert', 'Veronika Irvine'] __credits__ = ['Juergen Weigert', 'Veronika Irvine']
import sys, os, shutil, time, logging, tempfile, math import sys
import math
import re import re
#debug = True
debug = False
# search path, so that inkscape libraries are found when we are standalone.
sys_platform = sys.platform.lower()
if sys_platform.startswith('win'): # windows
sys.path.append('C:\Program Files\Inkscape\share\extensions')
elif sys_platform.startswith('darwin'): # mac
sys.path.append('/Applications/Inkscape.app/Contents/Resources/extensions')
else: # linux
# if sys_platform.startswith('linux'):
sys.path.append('/usr/share/inkscape/extensions')
# inkscape libraries
import inkex import inkex
inkex.localization.localize() inkex.localization.localize()
from optparse import SUPPRESS_HELP from optparse import SUPPRESS_HELP
debug = False
def uutounit(self, nn, uu):
return self.svg.unittouu(str(nn)+uu)
class ChainPaths(inkex.Effect): class ChainPaths(inkex.Effect):
@ -86,7 +68,7 @@ class ChainPaths(inkex.Effect):
- The document units are always irrelevant as - The document units are always irrelevant as
everything in inkscape is expected to be in 90dpi pixel units everything in inkscape is expected to be in 90dpi pixel units
""" """
dialog_units = uutounit(self, 1.0, units) dialog_units = self.svg.unittouu(str(1.0)+units)
self.unit_factor = 1.0 / dialog_units self.unit_factor = 1.0 / dialog_units
return self.unit_factor return self.unit_factor
@ -237,15 +219,15 @@ class ChainPaths(inkex.Effect):
if self.near_ends(end1, seg['end2']): if self.near_ends(end1, seg['end2']):
# prepend seg to chain # prepend seg to chain
self.set_segment_done(seg['id'], seg['n'], 'prepended to ' + id + ' ' + str(cur_idx)) self.set_segment_done(seg['id'], seg['n'], 'prepended to ' + str(id) + ' ' + str(cur_idx))
chain = self.link_segments(seg['seg'], chain) chain = self.link_segments(seg['seg'], chain)
end1 = [chain[0][1][0], chain[0][1][1]] end1 = [chain[0][1][0], chain[0][1][1]]
segments_idx = 0 # this chain changed. re-visit all candidate segments_idx = 0 # this chain changed. re-visit all candidate
continue continue
if self.near_ends(end2, seg['end1']): if self.near_ends(end2, seg['end1']):
# append seg to chain # append seg to chain
self.set_segment_done(seg['id'], seg['n'], 'appended to ' + id + ' ' + str(cur_idx)) self.set_segment_done(seg['id'], seg['n'], 'appended to ' + str(id) + ' ' + str(cur_idx))
chain = self.link_segments(chain, seg['seg']) chain = self.link_segments(chain, seg['seg'])
end2 = [chain[-1][1][0], chain[-1][1][1]] end2 = [chain[-1][1][0], chain[-1][1][1]]
segments_idx = 0 # this chain changed. re-visit all candidate segments_idx = 0 # this chain changed. re-visit all candidate

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Pixel2SVG</name>
<id>fablabchemnitz.de.pixel2svg</id>
<param name="tab" type="notebook">
<page name="pixel2svg_tab" _gui-text="Options">
<param name="squaresize" type="int" min="1" max="100" _gui-text="Width and height of vector squares in pixels">5</param>
<param name="offset_image" type="boolean" _gui-text="Offset traced image">true</param>
<param name="delete_image" type="boolean" _gui-text="Delete bitmap image">false</param>
</page>
<page name="advanced_tab" _gui-text="Advanced">
<param name="transparency" type="boolean" _gui-text="Convert transparency to 'fill-opacity'">true</param>
<param name="overlap" type="boolean" _gui-text="Overlap vector squares by 1px">false</param>
<param name="verbose" type="boolean" _gui-text="Report image info (size, format, mode)">false</param>
<param name="maxsize" type="int" min="1" max="10000" _gui-text="Max. image size (width or height)">256</param>
</page>
<page name="advanced_color_tab" _gui-text="Colors">
<param name="color_mode" type="enum" _gui-text="">
<item value="all">Trace all colors.</item>
<item value="other">Don't trace this color:</item>
<item value="this">Only trace this color:</item>
</param>
<param name="color" type="string" max_length="6" _gui-text="Color (hex):">FFFFFF</param>
</page>
<page name="about_tab" _gui-text="About">
<_param name="instructions" type="description" xml:space="preserve">This extension is based on:
pixel2svg - converts pixel art to SVG - pixel by pixel.
Copyright 2011 Florian Berger
http://florian-berger.de/en/software/pixel2svg
</_param>
</page>
</param>
<effect needs-document="true" needs-live-preview="true">
<object-type>image</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Tracing/Edge Detection" />
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">fablabchemnitz_pixel2svg.py</command>
</script>
<options silent="false" />
</inkscape-extension>

View File

@ -0,0 +1,314 @@
#!/usr/bin/env python3
"""
Pixel2SVG - Convert the pixels of bitmap images to SVG rects
Idea and original implementation as standalone script:
Copyright (C) 2011 Florian Berger <fberger@florian-berger.de>
Homepage: <http://florian-berger.de/en/software/pixel2svg>
Rewritten as Inkscape extension:
Copyright (C) 2012 ~suv <suv-sf@users.sourceforge.net>
'getFilePath()' is based on code from 'extractimages.py':
Copyright (C) 2005 Aaron Spike, aaron@ekips.org
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
import os
import sys
import base64
from io import StringIO, BytesIO
import urllib.parse
import urllib.request
import inkex
from PIL import Image
from lxml import etree
inkex.localization.localize
DEBUG = False
# int r = ( hexcolor >> 16 ) & 0xFF;
# int g = ( hexcolor >> 8 ) & 0xFF;
# int b = hexcolor & 0xFF;
# int hexcolor = (r << 16) + (g << 8) + b;
def hex_to_int_color(v):
if (v[0] == '#'):
v = v[1:]
assert(len(v) == 6)
return int(v[:2], 16), int(v[2:4], 16), int(v[4:6], 16)
class Pixel2SVG(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
# pixel2svg options
self.arg_parser.add_argument("-s", "--squaresize", type=int, default="5", help="Width and height of vector squares in pixels")
self.arg_parser.add_argument("--transparency", type=inkex.Boolean, default=True, help="Convert transparency to 'fill-opacity'")
self.arg_parser.add_argument("--overlap", type=inkex.Boolean, default=False, help="Overlap vector squares by 1px")
self.arg_parser.add_argument("--offset_image", type=inkex.Boolean, default=True, help="Offset traced image")
self.arg_parser.add_argument("--delete_image", type=inkex.Boolean, default=False, help="Delete bitmap image")
self.arg_parser.add_argument("--maxsize", type=int, default="256", help="Max. image size (width or height)")
self.arg_parser.add_argument("--verbose", type=inkex.Boolean, default=False)
self.arg_parser.add_argument("--color_mode", default="all", help="Which colors to trace.")
self.arg_parser.add_argument("--color", default="FFFFFF", help="Special color")
self.arg_parser.add_argument("--tab")
def getImagePath(self, node, xlink):
"""
Find image file, return path
"""
absref = node.get(inkex.addNS('absref', 'sodipodi'))
url = urlparse(xlink)
href = urllib.request.url2pathname
path = ''
#path selection strategy:
# 1. href if absolute
# 2. realpath-ified href
# 3. absref, only if the above does not point to a file
if href is not None:
path = os.path.realpath(href)
if (not os.path.isfile(path)):
if absref is not None:
path = absref
try:
path = unicode(path, "utf-8")
except TypeError:
path = path
if (not os.path.isfile(path)):
inkex.errormsg(_(
"No xlink:href or sodipodi:absref attributes found, " +
"or they do not point to an existing file! Unable to find image file."))
if path:
inkex.errormsg(_("Sorry we could not locate %s") % str(path))
return False
if (os.path.isfile(path)):
return path
def getImageData(self, xlink):
"""
Read, decode and return data of embedded image
"""
comma = xlink.find(',')
data = ''
if comma > 0:
data = base64.decodebytes(xlink[comma:].encode('UTF-8'))
else:
inkex.errormsg(_("Failed to read embedded image data."))
return data
def getImage(self, node):
image_element=self.svg.find('.//{http://www.w3.org/2000/svg}image')
image_string=image_element.get('{http://www.w3.org/1999/xlink}href')
#find comma position
i=0
while i<40:
if image_string[i]==',':
break
i=i+1
return Image.open(BytesIO(base64.b64decode(image_string[i+1:len(image_string)])))
def drawFilledRect(self, parent, svgpx):
"""
Draw rect based on ((x, y), (width,height), ((r,g,b),a)), add to parent
"""
style = {}
pos = svgpx[0]
dim = svgpx[1]
rgb = svgpx[2][0]
alpha = svgpx[2][1]
style['stroke'] = 'none'
if len(rgb) == 3:
# fill: rgb tuple
style['fill'] = '#%02x%02x%02x' % (rgb[0], rgb[1], rgb[2])
elif len(rgb) == 1:
# fill: color name, or 'none'
style['fill'] = rgb[0]
else:
# fill: 'Unset' (no fill defined)
pass
if alpha < 255:
# only write 'fill-opacity' for non-default value
style['fill-opacity'] = '%s' % round(alpha/255.0, 8)
rect_attribs = {'x': str(pos[0]),
'y': str(pos[1]),
'width': str(dim[0]),
'height': str(dim[1]),
'style': str(inkex.Style(style)), }
rect = etree.SubElement(parent, inkex.addNS('rect', 'svg'), rect_attribs)
return rect
def vectorizeImage(self, node):
"""
Parse RGBA values of linked bitmap image, create a group and
draw the rectangles (SVG pixels) inside the new group
"""
image = self.getImage(node)
if image:
# init, set limit (default: 256)
pixel2svg_max = self.options.maxsize
if self.options.verbose:
inkex.debug("ID: %s" % node.get('id'))
inkex.debug("Image size:\t%dx%d" % image.size)
inkex.debug("Image format:\t%s" % image.format)
inkex.debug("Image mode:\t%s" % image.mode)
inkex.debug("Image info:\t%s" % image.info)
if (image.mode == 'P' and 'transparency' in image.info):
inkex.debug(
"Note: paletted image with an alpha channel is handled badly with " +
"current PIL:\n" +
"<http://stackoverflow.com/questions/12462548/pil-image-mode-p-rgba>")
elif not image.mode in ('RGBA', 'LA'):
inkex.debug("No alpha channel or transparency found")
image = image.convert("RGBA")
(width, height) = image.size
if width <= pixel2svg_max and height <= pixel2svg_max:
# color trace modes
trace_color = []
if self.options.color:
trace_color = hex_to_int_color(self.options.color)
# get RGBA data
rgba_values = list(image.getdata())
# create group
nodeParent = node.getparent()
nodeIndex = nodeParent.index(node)
pixel2svg_group = etree.Element(inkex.addNS('g', 'svg'))
pixel2svg_group.set('id', "%s_pixel2svg" % node.get('id'))
nodeParent.insert(nodeIndex+1, pixel2svg_group)
# move group beside original image
if self.options.offset_image:
pixel2svg_offset = width
else:
pixel2svg_offset = 0.0
pixel2svg_translate = ('translate(%s, %s)' %
(float(node.get('x') or 0.0) + pixel2svg_offset,
node.get('y') or 0.0))
pixel2svg_group.set('transform', pixel2svg_translate)
# draw bbox rectangle at the bottom of group
pixel2svg_bbox_fill = ('none', )
pixel2svg_bbox_alpha = 255
pixel2svg_bbox = ((0, 0),
(width * self.options.squaresize,
height * self.options.squaresize),
(pixel2svg_bbox_fill, pixel2svg_bbox_alpha))
self.drawFilledRect(pixel2svg_group, pixel2svg_bbox)
# reverse list (performance), pop last one instead of first
rgba_values.reverse()
# loop through pixels (per row)
rowcount = 0
while rowcount < height:
colcount = 0
while colcount < width:
rgba_tuple = rgba_values.pop()
# Omit transparent pixels
if rgba_tuple[3] > 0:
# color options
do_trace = True
if (self.options.color_mode != "all"):
if (trace_color == rgba_tuple[:3]):
# colors match
if (self.options.color_mode == "other"):
do_trace = False
else:
# colors don't match
if (self.options.color_mode == "this"):
do_trace = False
if do_trace:
# position
svgpx_x = colcount * self.options.squaresize
svgpx_y = rowcount * self.options.squaresize
# dimension + overlap
svgpx_size = self.options.squaresize + self.options.overlap
# get color, ignore alpha
svgpx_rgb = rgba_tuple[:3]
svgpx_a = 255
# transparency
if self.options.transparency:
svgpx_a = rgba_tuple[3]
svgpx = ((svgpx_x, svgpx_y),
(svgpx_size, svgpx_size),
(svgpx_rgb, svgpx_a)
)
# draw square in group
self.drawFilledRect(pixel2svg_group, svgpx)
colcount = colcount + 1
rowcount = rowcount + 1
# all done
if DEBUG:
inkex.debug("All rects drawn.")
if self.options.delete_image:
nodeParent.remove(node)
else:
# bail out with larger images
inkex.errormsg(_(
"Bailing out: this extension is not intended for large images.\n" +
"The current limit is %spx for either dimension of the bitmap image."
% pixel2svg_max))
sys.exit(0)
# clean-up?
if DEBUG:
inkex.debug("Done.")
else:
inkex.errormsg(_("Bailing out: No supported image file or data found"))
sys.exit(1)
def effect(self):
"""
Pixel2SVG - Convert the pixels of bitmap images to SVG rects
"""
found_image = False
if (self.options.ids):
for node in self.svg.selected.values():
if node.tag == inkex.addNS('image', 'svg'):
found_image = True
self.vectorizeImage(node)
if not found_image:
inkex.errormsg(_("Please select one or more bitmap image(s) for Pixel2SVG"))
sys.exit(0)
if __name__ == '__main__':
Pixel2SVG().run()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,495 @@
#!/usr/bin/env python3
#
# (c) 2020 Yoichi Tanibayashi
#
import inkex
from lxml import etree
import math
inkex.localization.localize
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, c):
return math.sqrt((c.x - self.x) ** 2 + (c.y - self.y) ** 2)
def rotate(self, rad):
new_x = math.cos(rad) * self.x - math.sin(rad) * self.y
new_y = math.sin(rad) * self.x + math.cos(rad) * self.y
self.x = new_x
self.y = new_y
return self
def mirror(self):
self.x = -self.x
return self
class Vpoint(Point):
'''
(x, y)座標と方向(rad)を持つ点
rad: 方向(真上: 0, : math.pi / 2, ..)
'''
def __init__(self, x, y, rad=0):
super(Vpoint, self).__init__(x, y)
self.rad = rad
def rotate(self, rad):
super(Vpoint, self).rotate(rad)
self.rad += rad
return self
def mirror(self):
super(Vpoint, self).mirror()
self.rad = -self.rad
return self
class SvgObj(object):
DEF_COLOR = '#00FF00'
DEF_STROKE_WIDTH = 0.2
DEF_STROKE_DASHARRAY = 'none'
def __init__(self, parent):
self.parent = parent
self.type = None
self.attr = {}
def draw(self, color=DEF_COLOR,
stroke_width=DEF_STROKE_WIDTH,
stroke_dasharray=DEF_STROKE_DASHARRAY):
self.attr['style'] = str(inkex.Style({
'stroke': str(color),
'stroke-width': str(stroke_width),
'stroke-dasharray': str(stroke_dasharray),
'fill': 'none'}))
return etree.SubElement(self.parent,
inkex.addNS(self.type, 'svg'),
self.attr)
class SvgCircle(SvgObj):
DEF_COLOR = '#FF0000'
DEF_STROKE_WIDTH = 0.2
DEF_STROKE_DASHARRAY = 'none'
def __init__(self, parent, r):
super(SvgCircle, self).__init__(parent)
self.r = r
self.type = 'circle'
def draw(self, point,
color=DEF_COLOR,
stroke_width=DEF_STROKE_WIDTH,
stroke_dasharray=DEF_STROKE_DASHARRAY):
self.attr['cx'] = str(point.x)
self.attr['cy'] = str(point.y)
self.attr['r'] = str(self.r)
return super(SvgCircle, self).draw(color,
stroke_width, stroke_dasharray)
class SvgPath(SvgObj):
DEF_COLOR = '#0000FF'
DEF_STROKE_WIDTH = 0.2
DEF_STROKE_DASHARRAY = 'none'
def __init__(self, parent, points):
super(SvgPath, self).__init__(parent)
self.points = points
self.type = 'path'
def create_svg_d(self, origin_vpoint, points):
'''
to be override
This is sample code.
'''
svg_d = ''
for i, p in enumerate(points):
(x1, y1) = (p.x + origin_vpoint.x, p.y + origin_vpoint.y)
if i == 0:
svg_d = 'M %f,%f' % (x1, y1)
else:
svg_d += ' L %f,%f' % (x1, y1)
return svg_d
def rotate(self, rad):
for p in self.points:
p.rotate(rad)
return self
def mirror(self):
for p in self.points:
p.mirror()
return self
def draw(self, origin,
color=DEF_COLOR, stroke_width=DEF_STROKE_WIDTH,
stroke_dasharray=DEF_STROKE_DASHARRAY):
self.rotate(origin.rad)
svg_d = self.create_svg_d(origin, self.points)
# inkex.errormsg('svg_d=%s' % svg_d)
# inkex.errormsg('svg_d=%s' % str(Path( svg_d )))
self.attr['d'] = svg_d
return super(SvgPath, self).draw(color, stroke_width, stroke_dasharray)
class SvgLine(SvgPath):
# exactly same as SvgPath
pass
class SvgPolygon(SvgPath):
def create_svg_d(self, origin, points):
svg_d = super(SvgPolygon, self).create_svg_d(origin, points)
svg_d += ' Z'
return svg_d
class SvgPart1Outline(SvgPolygon):
def __init__(self, parent, points, bw_bf):
super(SvgPart1Outline, self).__init__(parent, points)
self.bw_bf = bw_bf
def create_svg_d(self, origin, points, bw_bf=1):
for i, p in enumerate(points):
(x1, y1) = (p.x + origin.x, p.y + origin.y)
if i == 0:
d = 'M %f,%f' % (x1, y1)
elif i == 7:
d += ' L %f,%f' % (x1, y1)
x2 = x1
y2 = y1 + self.bw_bf
elif i == 8:
d += ' C %f,%f %f,%f %f,%f' % (x2, y2, x1, y2, x1, y1)
else:
d += ' L %f,%f' % (x1, y1)
d += ' Z'
return d
class SvgNeedleHole(SvgPolygon):
def __init__(self, parent, w, h, tf):
'''
w: width
h: height
tf: tilt factor
'''
self.w = w
self.h = h
self.tf = tf
self.gen_points(self.w, self.h, self.tf)
super(SvgNeedleHole, self).__init__(parent, self.points)
def gen_points(self, w, h, tf):
self.points = []
self.points.append(Point(-w / 2, h * tf))
self.points.append(Point( w / 2, h * (1 - tf)))
self.points.append(Point( w / 2, -h * tf))
self.points.append(Point(-w / 2, -h * (1 - tf)))
class Part1(object):
def __init__(self, parent,
w1, w2, h1, h2, bw, bl, bf, dia1, d1, d2,
needle_w, needle_h, needle_tf, needle_corner_rotation):
self.parent = parent
self.w1 = w1
self.w2 = w2
self.h1 = h1
self.h2 = h2
self.bw = bw
self.bl = bl
self.bf = bf
self.dia1 = dia1
self.d1 = d1
self.d2 = d2
self.needle_w = needle_w
self.needle_h = needle_h
self.needle_tf = needle_tf
self.needle_corner_rotation = needle_corner_rotation
# グループ作成
attr = {inkex.addNS('label', 'inkscape'): 'Part1'}
self.g = etree.SubElement(self.parent, 'g', attr)
# 図形作成
self.points_outline = self.create_points_outline()
self.svg_outline = SvgPart1Outline(self.g, self.points_outline,
(self.bw * self.bf))
self.svg_hole = SvgCircle(self.g, self.dia1 / 2)
self.vpoints_needle = self.create_needle_vpoints()
self.svgs_needle_hole = []
for v in self.vpoints_needle:
svg_nh = SvgNeedleHole(self.g,
self.needle_w,
self.needle_h,
self.needle_tf)
self.svgs_needle_hole.append((svg_nh, v))
def create_points_outline(self):
'''
外枠の座標を生成
'''
points = []
(x0, y0) = (-(self.w2 / 2), 0)
(x, y) = (x0, y0 + self.h1 + self.h2)
points.append(Point(x, y))
y = y0 + self.h1
points.append(Point(x, y))
x = -(self.w1 / 2)
y = y0
points.append(Point(x, y))
x = self.w1 / 2
points.append(Point(x, y))
x = self.w2 / 2
y += self.h1
points.append(Point(x, y))
y += self.h2
points.append(Point(x, y))
x = self.bw / 2
points.append(Point(x, y))
y += self.bl - self.bw / 2
points.append(Point(x, y))
x = -(self.bw / 2)
points.append(Point(x, y))
y = y0 + self.h1 + self.h2
points.append(Point(x, y))
return points
def create_needle_vpoints(self):
'''
針穴の点と方向を生成
'''
rad1 = math.atan((self.w2 - self.w1) / (2 * self.h1))
rad1a = (math.pi - rad1) / 2
a1 = self.d1 / math.tan(rad1a)
rad2 = (math.pi / 2) - rad1
rad2a = (math.pi - rad2) / 2
a2 = self.d1 / math.tan(rad2a)
#
# 頂点
#
vpoints1 = []
for i, p in enumerate(self.points_outline):
(nx, ny) = (p.x, p.y)
if i == 0:
nx += self.d1
ny -= self.d1 * 1.5
vpoints1.append(Vpoint(nx, ny, 0))
if i == 1:
nx += self.d1
ny += a1
vpoints1.append(Vpoint(nx, ny, rad1))
if i == 2:
nx += a2
ny += self.d1
vpoints1.append(Vpoint(nx, ny, math.pi / 2))
if i == 3:
nx -= a2
ny += self.d1
vpoints1.append(Vpoint(nx, ny, (math.pi / 2) + rad2))
if i == 4:
nx -= self.d1
ny += a1
vpoints1.append(Vpoint(nx, ny, math.pi))
if i == 5:
nx -= self.d1
ny -= self.d1 * 1.5
vpoints1.append(Vpoint(nx, ny, math.pi))
if i > 5:
break
# 頂点を補完する点を生成
vpoints2 = []
for i in range(len(vpoints1)-1):
d = vpoints1[i].distance(vpoints1[i+1])
n = int(abs(round(d / self.d2)))
for p in self.split_vpoints(vpoints1[i], vpoints1[i+1], n):
vpoints2.append(p)
vpoints2.insert(0, vpoints1[0])
return vpoints2
def split_vpoints(self, v1, v2, n):
'''
v1, v2間をn個に分割してリストを生成
'''
if n == 0:
return [v1]
(dx, dy) = ((v2.x - v1.x) / n, (v2.y - v1.y) / n)
v = []
for i in range(n):
v.append(Vpoint(v1.x + dx * (i + 1),
v1.y + dy * (i + 1),
v1.rad))
if self.needle_corner_rotation:
v[-1].rad = (v1.rad + v2.rad) / 2
return v
def draw(self, origin):
origin_base = Vpoint(origin.x + self.w2 / 2,
origin.y,
origin.rad)
self.svg_outline.draw(origin_base, color='#0000FF')
x = origin.x + self.w2 / 2
y = origin.y + self.h1 + self.h2 + self.bl - self.bw / 2
origin_hole = Point(x, y)
self.svg_hole.draw(origin_hole, color='#FF0000')
for (svg_nh, p) in self.svgs_needle_hole:
origin_nh = Vpoint(origin.x + p.x + self.w2 / 2,
origin.y + p.y,
p.rad)
svg_nh.draw(origin_nh, color='#FF0000')
class Part2(object):
def __init__(self, parent, part1, dia2):
self.parent = parent
self.part1 = part1
self.dia2 = dia2
# グループ作成
attr = {inkex.addNS('label', 'inkscape'): 'Part2'}
self.g = etree.SubElement(self.parent, 'g', attr)
# 外枠
# ``Part1``の``points_outline``をミラーして、
# 最初の6つのポイントを利用
self.points_outline = []
for i in range(6):
self.points_outline.append(self.part1.points_outline[i].mirror())
self.svg_outline = SvgPolygon(self.g, self.points_outline)
# 留め具
self.svg_hole = SvgCircle(self.g, self.dia2 / 2)
# 針穴
# ``Part1``の``vpoints_needle``をミラーして利用
self.svgs_needle_hole = []
for v in self.part1.vpoints_needle:
v.mirror()
# ``SvgNeedleHole``もミラーする
svg_nh = SvgNeedleHole(self.g,
self.part1.needle_w,
self.part1.needle_h,
self.part1.needle_tf)
svg_nh.mirror()
self.svgs_needle_hole.append((svg_nh, v))
def draw(self, origin):
origin_base = Vpoint(origin.x + self.part1.w2 / 2,
origin.y, origin.rad)
self.svg_outline.draw(origin_base, color='#0000FF')
x = origin.x + self.part1.w2 / 2
y = origin.y + self.part1.h1 + self.part1.h2
y -= (self.svg_hole.r + self.part1.d1)
origin_hole = Vpoint(x, y, origin.rad)
self.svg_hole.draw(origin_hole, color='#FF0000')
for (svg_nh, p) in self.svgs_needle_hole:
origin_nh = Vpoint(origin.x + p.x + self.part1.w2 / 2,
origin.y + p.y,
p.rad)
svg_nh.draw(origin_nh, color='#FF0000')
class PliersCover(inkex.Effect):
DEF_OFFSET_X = 20
DEF_OFFSET_Y = 20
def __init__(self):
inkex.Effect.__init__(self)
self.arg_parser.add_argument("--tabs")
self.arg_parser.add_argument("--w1", type=float)
self.arg_parser.add_argument("--w2", type=float)
self.arg_parser.add_argument("--h1", type=float)
self.arg_parser.add_argument("--h2", type=float)
self.arg_parser.add_argument("--bw", type=float)
self.arg_parser.add_argument("--bl", type=float)
self.arg_parser.add_argument("--bf", type=float)
self.arg_parser.add_argument("--dia1", type=float)
self.arg_parser.add_argument("--dia2", type=float)
self.arg_parser.add_argument("--d1", type=float)
self.arg_parser.add_argument("--d2", type=float)
self.arg_parser.add_argument("--needle_w", type=float)
self.arg_parser.add_argument("--needle_h", type=float)
self.arg_parser.add_argument("--needle_tf", type=float)
self.arg_parser.add_argument("--needle_corner_rotation", type=inkex.Boolean, default=True)
def effect(self):
# inkex.errormsg('view_center=%s' % str(self.view_center))
# inkex.errormsg('selected=%s' % str(self.selected))
# parameters
opt = self.options
#
# error check
#
if opt.w1 >= opt.w2:
msg = "Error: w1(%d) > w2(%d) !" % (opt.w1, opt.w2)
inkex.errormsg(msg)
return
if opt.dia1 >= opt.bw:
msg = "Error: dia1(%d) >= bw(%d) !" % (opt.dia1, opt.bw)
inkex.errormsg(msg)
return
#
# draw
#
origin_vpoint = Vpoint(self.DEF_OFFSET_X, self.DEF_OFFSET_Y)
# グループ作成
attr = {inkex.addNS('label', 'inkscape'): 'PliersCover'}
self.g = etree.SubElement(self.svg.get_current_layer(), 'g', attr)
part1 = Part1(self.g,
opt.w1, opt.w2, opt.h1, opt.h2,
opt.bw, opt.bl, opt.bf, opt.dia1,
opt.d1, opt.d2,
opt.needle_w, opt.needle_h, opt.needle_tf,
opt.needle_corner_rotation)
part1.draw(origin_vpoint)
origin_vpoint.x += opt.w2 + 10
part2 = Part2(self.g, part1, opt.dia2)
part2.draw(origin_vpoint)
if __name__ == '__main__':
PliersCover().run()