Added Contour Scanner (written by myself)
This commit is contained in:
parent
bb3505ed9e
commit
bdb77f587c
@ -32,6 +32,7 @@ import sys
|
||||
import math
|
||||
import re
|
||||
import inkex
|
||||
from inkex.paths import CubicSuperPath, Path
|
||||
inkex.localization.localize()
|
||||
from optparse import SUPPRESS_HELP
|
||||
debug = False
|
||||
@ -149,7 +150,7 @@ class ChainPaths(inkex.Effect):
|
||||
inkex.errormsg(_("Object " + id + " is not a path. Try\n - Path->Object to Path\n - Object->Ungroup"))
|
||||
return
|
||||
if debug: inkex.utils.debug("id=" + str(id) + ", tag=" + str(node.tag))
|
||||
path_d = inkex.paths.CubicSuperPath(inkex.paths.Path(node.get('d')))
|
||||
path_d = CubicSuperPath(Path(node.get('d')))
|
||||
sub_idx = -1
|
||||
for sub in path_d:
|
||||
sub_idx += 1
|
||||
@ -180,7 +181,7 @@ class ChainPaths(inkex.Effect):
|
||||
remaining = 0
|
||||
for id in self.svg.selected:
|
||||
node = self.svg.selected[id]
|
||||
path_d = inkex.paths.CubicSuperPath(inkex.paths.Path(node.get('d')))
|
||||
path_d = CubicSuperPath(Path(node.get('d')))
|
||||
# ATTENTION: for parsePath() it is the same, if first and last point coincide, or if the path is really closed.
|
||||
path_closed = True if re.search("z\s*$", node.get('d')) else False
|
||||
new = []
|
||||
@ -269,7 +270,7 @@ class ChainPaths(inkex.Effect):
|
||||
else:
|
||||
remaining += 1
|
||||
# BUG: All previously closed loops, are open, after we convert them back with cubicsuperpath.formatPath()
|
||||
p_fmt = str(inkex.paths.Path(inkex.paths.CubicSuperPath(new).to_path().to_arrays()))
|
||||
p_fmt = str(Path(CubicSuperPath(new).to_path().to_arrays()))
|
||||
if path_closed: p_fmt += " z"
|
||||
if debug: inkex.utils.debug("new path: "+str(p_fmt))
|
||||
node.set('d', p_fmt)
|
||||
|
45
extensions/fablabchemnitz_contour_scanner.inx
Normal file
45
extensions/fablabchemnitz_contour_scanner.inx
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Contour Scanner</name>
|
||||
<id>fablabchemnitz.de.contour_scanner</id>
|
||||
<param name="main_tabs" type="notebook">
|
||||
<page name="tab_active" gui-text="Active">
|
||||
<param name="desc" type="description">This tool helps you to find nasty contours which might bug you and prevent your work from being ready for production. It will find open contours, closed contours and self-intersecting contours. Intersecting contours can be closed or open contours so you can select this option additionally! Last ones usually happen if two or more handles (points) are coincident but which you might don't see. Or you just have large overlaps where the contour crosses itself like an 'eight' character for example. Using the highlighting it's easy to find contours with unproper path handles. Note that if you did not select any paths it will scan the whole document instead.</param>
|
||||
<param name="help_general" type="description" appearance="header">General</param>
|
||||
<param name="breakapart" type="bool" gui-text="Break apart contours">false</param>
|
||||
<param name="removefillsetstroke" type="bool" gui-text="Remove fill and define stroke">false</param>
|
||||
<param name="strokewidth" min="0.0" max="10000.0" gui-text="Stroke width (px)" type="float">1.0</param>
|
||||
<param name="help_highlight" type="description" appearance="header">Highlight paths</param>
|
||||
<param name="highlight_opened" type="bool" gui-text="Highlight opened contours">true</param>
|
||||
<param name="color_opened" type="color" appearance="colorbutton" gui-text="Color opened contours">#FF0000FF</param>
|
||||
<param name="highlight_closed" type="bool" gui-text="Highlight closed contours">true</param>
|
||||
<param name="color_closed" type="color" appearance="colorbutton" gui-text="Color closed contours">#00FF00FF</param>
|
||||
<param name="highlight_selfintersecting" type="bool" gui-text="Highlight self-intersecting contours">true</param>
|
||||
<param name="color_selfintersecting" type="color" appearance="colorbutton" gui-text="Color self-intersecting contours">#0000FFFF</param>
|
||||
<param name="highlight_intersectionpoints" type="bool" gui-text="Highlight self-intersecting points">true</param>
|
||||
<param name="color_intersectionpoints" type="color" appearance="colorbutton" gui-text="Color self-intersecting points">#0066FFFF</param>
|
||||
<param name="dotsize" type="int" min="0" max="10000" gui-text="Dot size (px) for self-intersecting points">10</param>
|
||||
<param name="help_remove" type="description" appearance="header">Remove paths</param>
|
||||
<param name="remove_opened" type="bool" gui-text="Remove opened contours">false</param>
|
||||
<param name="remove_closed" type="bool" gui-text="Remove closed contours">false</param>
|
||||
<param name="remove_selfintersecting" type="bool" gui-text="Remove self-intersecting contours">false</param>
|
||||
</page>
|
||||
<page name="tab_info" gui-text="License/Version">
|
||||
<param name="desc1" type="description">Written by Mario Voigt (Stadtfabrikanten e.V. / FabLab Chemnitz) (https://gitea.fablabchemnitz.de)</param>
|
||||
<param name="desc2" type="description">Last update: 09.08.2020</param>
|
||||
<param name="desc3" type="description">This piece of software is part of the MightyScape for InkScape 1.0/1.1dev Extension Collection</param>
|
||||
<param name="desc4" type="description" appearance="header">you found a bug or got some fresh code? Just report to mario.voigt@stadtfabrikanten.org. Thanks!</param>
|
||||
</page>
|
||||
</param>
|
||||
<effect>
|
||||
<object-type>path</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Nesting/Cut Optimization"/>
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command reldir="extensions" interpreter="python">fablabchemnitz_contour_scanner.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
213
extensions/fablabchemnitz_contour_scanner.py
Normal file
213
extensions/fablabchemnitz_contour_scanner.py
Normal file
@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Extension for InkScape 1.0
|
||||
Features
|
||||
- helps to find contours which are closed or not. Good for repairing contours, closing contours,...
|
||||
- works for paths which are packed into groups or groups of groups. #
|
||||
- can break contours apart like in "Path -> Break Apart"
|
||||
- implements Bentley-Ottmann algorithm from https://github.com/ideasman42/isect_segments-bentley_ottmann to scan for self-intersecting paths. This only works correctly if your path is within the canvas correctly. Otherwise you might get "assert(event.in_sweep == False) AssertionError". This is commented out yet
|
||||
- colorized paths respective to their type
|
||||
- can add dots to intersection points you'd like to fix
|
||||
|
||||
Author: Mario Voigt / FabLab Chemnitz
|
||||
Mail: mario.voigt@stadtfabrikanten.org
|
||||
Date: 09.08.2020
|
||||
License: GNU GPL v3
|
||||
"""
|
||||
|
||||
import sys
|
||||
from math import *
|
||||
import inkex
|
||||
from inkex.paths import Path, CubicSuperPath
|
||||
from inkex import Style, Color, Circle
|
||||
from lxml import etree
|
||||
import fablabchemnitz_poly_point_isect
|
||||
import copy
|
||||
|
||||
def pout(t):
|
||||
sys.exit()
|
||||
|
||||
def adjustStyle(self, node):
|
||||
if node.attrib.has_key('style'):
|
||||
style = node.get('style')
|
||||
if style:
|
||||
declarations = style.split(';')
|
||||
for i,decl in enumerate(declarations):
|
||||
parts = decl.split(':', 2)
|
||||
if len(parts) == 2:
|
||||
(prop, val) = parts
|
||||
prop = prop.strip().lower()
|
||||
if prop == 'stroke-width':
|
||||
declarations[i] = prop + ':' + str(self.svg.unittouu(str(self.options.strokewidth) +"px"))
|
||||
if prop == 'fill':
|
||||
declarations[i] = prop + ':none'
|
||||
node.set('style', ';'.join(declarations) + ';stroke:#000000;stroke-opacity:1.0')
|
||||
|
||||
class ContourScanner(inkex.Effect):
|
||||
|
||||
def getColorString(self, pickerColor):
|
||||
longcolor = int(pickerColor)
|
||||
if longcolor < 0:
|
||||
longcolor = longcolor & 0xFFFFFFFF
|
||||
return '#' + format(longcolor >> 8, '06X')
|
||||
|
||||
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.arg_parser.add_argument("--breakapart", type=inkex.Boolean, default=False, help="Break apart contours")
|
||||
self.arg_parser.add_argument("--removefillsetstroke", type=inkex.Boolean, default=False, help="Remove fill and define stroke")
|
||||
self.arg_parser.add_argument("--strokewidth", type=float, default=1.0, help="Stroke width (px)")
|
||||
self.arg_parser.add_argument("--highlight_opened", type=inkex.Boolean, default=True, help="Highlight opened contours")
|
||||
self.arg_parser.add_argument("--color_opened", type=Color, default='#FF0000FF', help="Color opened contours")
|
||||
self.arg_parser.add_argument("--highlight_closed", type=inkex.Boolean, default=True, help="Highlight closed contours")
|
||||
self.arg_parser.add_argument("--color_closed", type=Color, default='#00FF00FF', help="Color closed contours")
|
||||
self.arg_parser.add_argument("--highlight_selfintersecting", type=inkex.Boolean, default=True, help="Highlight self-intersecting contours")
|
||||
self.arg_parser.add_argument("--highlight_intersectionpoints", type=inkex.Boolean, default=True, help="Highlight self-intersecting points")
|
||||
self.arg_parser.add_argument("--color_selfintersecting", type=Color, default='#0000FFFF', help="Color closed contours")
|
||||
self.arg_parser.add_argument("--color_intersectionpoints", type=Color, default='#0066FFFF', help="Color closed contours")
|
||||
self.arg_parser.add_argument("--dotsize", type=int, default=10, help="Dot size (px) for self-intersecting points")
|
||||
self.arg_parser.add_argument("--remove_opened", type=inkex.Boolean, default=False, help="Remove opened contours")
|
||||
self.arg_parser.add_argument("--remove_closed", type=inkex.Boolean, default=False, help="Remove closed contours")
|
||||
self.arg_parser.add_argument("--remove_selfintersecting", type=inkex.Boolean, default=False, help="Remove self-intersecting contours")
|
||||
self.arg_parser.add_argument("--main_tabs")
|
||||
|
||||
#split combined contours into single contours if enabled - this is exactly the same as "Path -> Break Apart"
|
||||
def breakContours(self, node):
|
||||
replacedNodes = []
|
||||
if node.tag == inkex.addNS('path','svg'):
|
||||
parent = node.getparent()
|
||||
idx = parent.index(node)
|
||||
idSuffix = 0
|
||||
raw = Path(node.get("d")).to_arrays()
|
||||
subpaths, prev = [], 0
|
||||
for i in range(len(raw)): # Breaks compound paths into simple paths
|
||||
if raw[i][0] == 'M' and i != 0:
|
||||
subpaths.append(raw[prev:i])
|
||||
prev = i
|
||||
subpaths.append(raw[prev:])
|
||||
for subpath in subpaths:
|
||||
replacedNode = copy.copy(node)
|
||||
oldId = replacedNode.get('id')
|
||||
|
||||
replacedNode.set('d', CubicSuperPath(subpath))
|
||||
replacedNode.set('id', oldId + str(idSuffix).zfill(5))
|
||||
parent.insert(idx, replacedNode)
|
||||
idSuffix += 1
|
||||
replacedNodes.append(replacedNode)
|
||||
parent.remove(node)
|
||||
for child in node:
|
||||
self.breakContours(child)
|
||||
return replacedNodes
|
||||
|
||||
def scanContours(self, node):
|
||||
if node.tag == inkex.addNS('path','svg'):
|
||||
if self.options.removefillsetstroke:
|
||||
adjustStyle(self, node)
|
||||
|
||||
dot_group = node.getparent().add(inkex.Group())
|
||||
|
||||
raw = (Path(node.get('d')).to_arrays())
|
||||
subpaths, prev = [], 0
|
||||
for i in range(len(raw)): # Breaks compound paths into simple paths
|
||||
if raw[i][0] == 'M' and i != 0:
|
||||
subpaths.append(raw[prev:i])
|
||||
prev = i
|
||||
subpaths.append(raw[prev:])
|
||||
|
||||
for simpath in subpaths:
|
||||
if len(simpath) > 0:
|
||||
closed = False
|
||||
if simpath[-1][0] == 'Z':
|
||||
closed = True
|
||||
|
||||
if not closed:
|
||||
if self.options.highlight_opened:
|
||||
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
|
||||
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
|
||||
'stroke': self.options.color_opened, 'stroke-linecap': 'butt', 'fill': 'none'}
|
||||
node.attrib['style'] = Style(style).to_str()
|
||||
if self.options.remove_opened:
|
||||
try:
|
||||
node.getparent().remove(node)
|
||||
except AttributeError:
|
||||
pass #we ignore that parent can be None
|
||||
if closed:
|
||||
if self.options.highlight_closed:
|
||||
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
|
||||
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
|
||||
'stroke': self.options.color_closed, 'stroke-linecap': 'butt', 'fill': 'none'}
|
||||
node.attrib['style'] = Style(style).to_str()
|
||||
if self.options.remove_closed:
|
||||
try:
|
||||
node.getparent().remove(node)
|
||||
except AttributeError:
|
||||
pass #we ignore that parent can be None
|
||||
|
||||
for simpath in subpaths:
|
||||
closed = False
|
||||
if simpath[-1][0] == 'Z':
|
||||
closed = True
|
||||
if simpath[-2][0] == 'L': simpath[-1][1] = simpath[0][1]
|
||||
else: simpath.pop()
|
||||
nodes = []
|
||||
for i in range(len(simpath)):
|
||||
if simpath[i][0] == 'V': # vertical and horizontal lines only have one point in args, but 2 are required
|
||||
simpath[i][0]='L' #overwrite V with regular L command
|
||||
add=simpath[i-1][1][0] #read the X value from previous segment
|
||||
simpath[i][1].append(simpath[i][1][0]) #add the second (missing) argument by taking argument from previous segment
|
||||
simpath[i][1][0]=add #replace with recent X after Y was appended
|
||||
if simpath[i][0] == 'H': # vertical and horizontal lines only have one point in args, but 2 are required
|
||||
simpath[i][0]='L' #overwrite H with regular L command
|
||||
simpath[i][1].append(simpath[i-1][1][1]) #add the second (missing) argument by taking argument from previous segment
|
||||
nodes.append(simpath[i][1][-2:])
|
||||
|
||||
#if one of the options is activated we also check for self-intersecting
|
||||
if self.options.highlight_selfintersecting or self.options.highlight_intersectionpoints:
|
||||
try:
|
||||
if len(nodes) > 0: #try to find self-intersecting /overlapping polygons
|
||||
isect = fablabchemnitz_poly_point_isect.isect_polygon(nodes) # TODO: CREATE MARKERS FOR THIS
|
||||
if len(isect) > 0:
|
||||
#make dot markings at the intersection points
|
||||
if self.options.highlight_intersectionpoints:
|
||||
for xy in isect:
|
||||
#Add a dot label for this path element
|
||||
style = inkex.Style({'stroke': 'none', 'fill': self.getColorString(self.options.color_intersectionpoints)})
|
||||
circle = dot_group.add(Circle(cx=str(xy[0]), cy=str(xy[1]), r=str(self.svg.unittouu(str(self.options.dotsize/2) + "px"))))
|
||||
circle.style = style
|
||||
|
||||
if self.options.highlight_selfintersecting:
|
||||
style = {'stroke-linejoin': 'miter', 'stroke-width': str(self.svg.unittouu(str(self.options.strokewidth) +"px")),
|
||||
'stroke-opacity': '1.0', 'fill-opacity': '1.0',
|
||||
'stroke': self.getColorString(self.options.color_selfintersecting), 'stroke-linecap': 'butt', 'fill': 'none'}
|
||||
node.attrib['style'] = Style(style).to_str()
|
||||
if self.options.remove_selfintersecting:
|
||||
if node.getparent() is not None: #might be already been deleted by previously checked settings so check again
|
||||
node.getparent().remove(node)
|
||||
except AssertionError as e: # we skip AssertionError
|
||||
pass
|
||||
#inkex.utils.debug("Found some path which cannot be tested for self-intersecting behaviour")
|
||||
for child in node:
|
||||
self.scanContours(child)
|
||||
|
||||
def effect(self):
|
||||
if self.options.breakapart:
|
||||
if len(self.svg.selected) == 0:
|
||||
self.breakContours(self.document.getroot())
|
||||
self.scanContours(self.document.getroot())
|
||||
else:
|
||||
newContourSet = []
|
||||
for id, item in self.svg.selected.items():
|
||||
newContourSet.append(self.breakContours(item))
|
||||
for newContours in newContourSet:
|
||||
for newContour in newContours:
|
||||
self.scanContours(newContour)
|
||||
else:
|
||||
if len(self.svg.selected) == 0:
|
||||
self.scanContours(self.document.getroot())
|
||||
else:
|
||||
for id, item in self.svg.selected.items():
|
||||
self.scanContours(item)
|
||||
|
||||
ContourScanner().run()
|
@ -16,7 +16,7 @@
|
||||
#
|
||||
import inkex
|
||||
import sys
|
||||
from inkex import paths
|
||||
from inkex.paths import CubicSuperPath
|
||||
from inkex import transforms
|
||||
|
||||
def warn(*args, **kwargs):
|
||||
@ -28,15 +28,15 @@ class ExportXY(inkex.Effect):
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
def effect(self):
|
||||
for node in self.selected.items():
|
||||
for node in self.svg.selected.items():
|
||||
output_all = output_nodes = ""
|
||||
for id, node in self.selected.items():
|
||||
for id, node in self.svg.selected.items():
|
||||
if node.tag == inkex.addNS('path','svg'):
|
||||
output_all += ""
|
||||
output_nodes += ""
|
||||
node.apply_transform()
|
||||
d = node.get('d')
|
||||
p = paths.CubicSuperPath(d)
|
||||
p = CubicSuperPath(d)
|
||||
for subpath in p:
|
||||
for csp in subpath:
|
||||
output_nodes += str(csp[1][0]) + "\t" + str(csp[1][1]) + "\n"
|
||||
|
1287
extensions/fablabchemnitz_poly_point_isect.py
Normal file
1287
extensions/fablabchemnitz_poly_point_isect.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,91 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Shapes</name>
|
||||
<id>fablabchemnitz.de.shapes</id>
|
||||
<param name="help-ren" type="description" xml:space="preserve">Create shapes using the bounding box
|
||||
of the selected objects</param>
|
||||
<param name="tab" type="notebook">
|
||||
<page name="chamfer" gui-text="From corners">
|
||||
<param name="chamfertype" type="optiongroup" appearance="combo" gui-text="Corners type:">
|
||||
<_item value="rombus">Rombus</_item>
|
||||
<_item value="chamfer">Chamfer</_item>
|
||||
<_item value="chamferinv">Chamfer inverse</_item>
|
||||
<_item value="rect">Rect inside</_item>
|
||||
<_item value="round">Round inside</_item>
|
||||
<_item value="roundinv">Round inside inverse</_item>
|
||||
<_item value="cross">Cross</_item>
|
||||
<_item value="starcenter">Star from center</_item>
|
||||
<_item value="starcorners">Star from corners</_item>
|
||||
<_item value="crosscornersquad">Crossed corners quads</_item>
|
||||
<_item value="crosscornerstri">Crossed corners tris</_item>
|
||||
<_item value="crosscornersround">Crossed corners round</_item>
|
||||
</param>
|
||||
<param name="size" type="float" min="0.0" max="1000.0" gui-text="Size:">20</param>
|
||||
</page>
|
||||
<page name="triangles" gui-text="Triangles">
|
||||
<param name="tritype" type="optiongroup" appearance="combo" gui-text="Triangle type:">
|
||||
<_item value="isosceles">Isosceles</_item>
|
||||
<_item value="equi">Equilateral</_item>
|
||||
<_item value="rect">Rectangle</_item>
|
||||
</param>
|
||||
<param name="trihside" type="bool" gui-text="Right side aligned">false</param>
|
||||
<param name="trivside" type="bool" gui-text="Top side aligned">false</param>
|
||||
</page>
|
||||
<page name="spikes" gui-text="Spikes">
|
||||
<param name="spikestype" type="optiongroup" appearance="combo" gui-text="Shape:">
|
||||
<_item value="tri">Triangle</_item>
|
||||
<_item value="trirect">Rectangle</_item>
|
||||
<_item value="squ">Square</_item>
|
||||
<_item value="rnd">Rounded</_item>
|
||||
<_item value="wav">Wave</_item>
|
||||
</param>
|
||||
<param name="spikesdir" type="optiongroup" appearance="combo" gui-text="Direction:">
|
||||
<_item value="out">Outside</_item>
|
||||
<_item value="ins">Inside</_item>
|
||||
<_item value="alt">Alternate</_item>
|
||||
</param>
|
||||
<param name="spikesize" type="float" min="0.1" max="1000.0" gui-text="Size:" appearance="full">2.0</param>
|
||||
<param name="spikesep" type="float" min="-1000.0" max="1000.0" gui-text="Separation:" appearance="full">0.0</param>
|
||||
</page>
|
||||
<page name="arrow" gui-text="Arrows">
|
||||
<param name="arrowtype" type="optiongroup" appearance="combo" gui-text="Arrow type:">
|
||||
<_item value="arrowfilled">Filled</_item>
|
||||
<_item value="arrowstick">Stick</_item>
|
||||
</param>
|
||||
<param name="headWidth" type="float" min="0.1" max="1000.0" gui-text="Head width:">20.0</param>
|
||||
<param name="headHeight" type="float" min="0.1" max="1000.0" gui-text="Head height:">40.0</param>
|
||||
<param name="arrowWidth" type="float" min="0.1" max="1000.0" gui-text="Tail width:">10.0</param>
|
||||
</page>
|
||||
<page name="extra" gui-text="Join circles">
|
||||
<param name="joincirctype" type="optiongroup" appearance="combo" gui-text="Function:">
|
||||
<_item value="trapecio">Rect</_item>
|
||||
<_item value="blob">Blob</_item>
|
||||
<_item value="oval">Oval</_item>
|
||||
</param>
|
||||
<param name="joinradius" type="float" min="0.0" max="1000.0" gui-text="Join radius:">0.0</param>
|
||||
<param name="help-circ" type="description" appearance="header" xml:space="preserve">Need to perform boolean operations.</param>
|
||||
</page>
|
||||
</param>
|
||||
<param name="incdec" type="float" min="-1000.0" max="1000.0" gui-text="Increase/decrease size:">0.0</param>
|
||||
<param name="unit" gui-text="Unit for values:" type="optiongroup" appearance="combo">
|
||||
<option value="px">px</option>
|
||||
<option value="pt">pt</option>
|
||||
<option value="in">in</option>
|
||||
<option value="cm">cm</option>
|
||||
<option value="mm">mm</option>
|
||||
</param>
|
||||
<param name="squareselection" type="bool" gui-text="Make the result object square">false</param>
|
||||
<param name="copyfill" type="bool" gui-text="Copy fill from selected">false</param>
|
||||
<param name="deleteorigin" type="bool" gui-text="Delete origin object">false</param>
|
||||
<effect needs-live-preview="true">
|
||||
<object-type>all</object-type>
|
||||
<effects-menu>
|
||||
<submenu name="FabLab Chemnitz">
|
||||
<submenu name="Shape/Pattern from existing Path(s)"/>
|
||||
</submenu>
|
||||
</effects-menu>
|
||||
</effect>
|
||||
<script>
|
||||
<command reldir="extensions" interpreter="python">fablabchemnitz_shapes.py</command>
|
||||
</script>
|
||||
</inkscape-extension>
|
@ -1,508 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
'''
|
||||
shapes.py
|
||||
|
||||
Copyright (C) 2015-2017 Paco Garcia, www.arakne.es
|
||||
|
||||
2017_07_30: added crossed corners
|
||||
copy class of original object if exists
|
||||
2017_08_09: rombus moved to From corners tab
|
||||
2017_08_17: join circles not need boolen operations now
|
||||
join circles added Oval
|
||||
2017_08_25: fixed error in objects without style
|
||||
in oval sets the minimal radius necessary
|
||||
|
||||
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 locale
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import webbrowser
|
||||
import math
|
||||
from subprocess import Popen, PIPE
|
||||
import inkex
|
||||
from fablabchemnitz_arakne_xy import *
|
||||
from lxml import etree
|
||||
|
||||
defStyle = [['stroke-width','0.5'],['fill','#f0ff00'],['stroke','#ff0000']]
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
class Shapes(inkex.Effect):
|
||||
|
||||
def __init__(self):
|
||||
inkex.Effect.__init__(self)
|
||||
self.arg_parser.add_argument("--tab")
|
||||
self.arg_parser.add_argument("--chamfertype")
|
||||
self.arg_parser.add_argument("--size", type=float, default="20")
|
||||
self.arg_parser.add_argument("--incdec", type=float, default="0")
|
||||
self.arg_parser.add_argument("--tritype", default="")
|
||||
self.arg_parser.add_argument("--spikestype")
|
||||
self.arg_parser.add_argument("--spikesdir")
|
||||
self.arg_parser.add_argument("--unit")
|
||||
self.arg_parser.add_argument("--spikesize", type=float, default="2.0")
|
||||
self.arg_parser.add_argument("--spikesep", type=float, default="0.0")
|
||||
self.arg_parser.add_argument("--arrowtype", default="")
|
||||
self.arg_parser.add_argument("--headWidth", type=float, default="20.0")
|
||||
self.arg_parser.add_argument("--headHeight", type=float, default="40.0")
|
||||
self.arg_parser.add_argument("--arrowWidth", type=float, default="10.0")
|
||||
self.arg_parser.add_argument("--squareselection", type=inkex.Boolean, default="false")
|
||||
self.arg_parser.add_argument("--trihside", type=inkex.Boolean, default="false")
|
||||
self.arg_parser.add_argument("--trivside", type=inkex.Boolean, default="false")
|
||||
self.arg_parser.add_argument("--copyfill", type=inkex.Boolean, default="false")
|
||||
self.arg_parser.add_argument("--deleteorigin", type=inkex.Boolean, default="false")
|
||||
self.arg_parser.add_argument("--joincirctype", default="")
|
||||
self.arg_parser.add_argument("--joinradius", type=float, default="0.0")
|
||||
|
||||
def addEle(self, ele, parent, props):
|
||||
elem = etree.SubElement(parent, ele)
|
||||
for n in props: elem.set(n,props[n])
|
||||
return elem
|
||||
|
||||
def chStyles(self,node,sty):
|
||||
style = inkex.Style.parse_str(node.get('style'))
|
||||
for n in sty:
|
||||
if str(style) in n[0]: style.pop(n[0], None)
|
||||
#if n[1]!="": style[n[0]]=n[1]
|
||||
node.set('style',str(inkex.Style(style)))
|
||||
|
||||
def unit2uu(self, val):
|
||||
if hasattr(self,"unittouu") is True:
|
||||
return self.unittouu(val)
|
||||
else:
|
||||
return inkex.unittouu(val)
|
||||
|
||||
def limits(self, node):
|
||||
s = node.bounding_box()
|
||||
l,r,t,b = (s.left,s.right,s.top,s.bottom)
|
||||
an,al = (r - l, b - t)
|
||||
incdec = self.svg.unittouu(self.options.incdec)
|
||||
l, t, r, b, an, al = (l - incdec, t - incdec, r + incdec, b + incdec, an + incdec*2, al + incdec*2)
|
||||
return (l,r,t,b,an,al)
|
||||
|
||||
def estilo(self, nn, orig, style=defStyle):
|
||||
if self.options.copyfill:
|
||||
if orig.get('style'):
|
||||
nn.set("style", orig.get('style'))
|
||||
if orig.get('class'):
|
||||
nn.set("class", orig.get('class'))
|
||||
else:
|
||||
self.chStyles(nn,style)
|
||||
|
||||
def circleABCD(self,p,r,abcd="ABCD",inverse=False,xtra=None):
|
||||
aa = r * 0.551915024494
|
||||
parts={
|
||||
'A':[XY(0,-r),XY(aa,-r), XY(r, -aa),XY(r,0)],
|
||||
'B':[XY(r,0), XY(r, aa), XY(aa, r),XY(0,r)],
|
||||
'C':[XY(0,r), XY(-aa,r), XY(-r, aa),XY(-r,0)],
|
||||
'D':[XY(-r,0),XY(-r,-aa),XY(-aa,-r),XY(0,-r)]}
|
||||
#pA = parts[abcd[0]]
|
||||
pA = [XY(p)+N for N in parts[abcd[0]]]
|
||||
for aa in abcd[1:]:
|
||||
pA = pA + [XY(p)+N for N in parts[aa][1:]]
|
||||
if inverse==True: pA.reverse()
|
||||
listA = XYList(pA)
|
||||
if xtra:
|
||||
for n in xtra:
|
||||
listA[n].extend(xtra[n])
|
||||
return listA
|
||||
|
||||
def draw(self, node, sh='"rombus"'):
|
||||
#inkex.errormsg(sh)
|
||||
sO = self.options
|
||||
l, r, t, b, an, al = self.limits(node)
|
||||
sqSel = sO.squareselection
|
||||
copyfill = sO.copyfill
|
||||
deleteorigin=sO.deleteorigin
|
||||
|
||||
side = min(al,an)
|
||||
if sqSel:
|
||||
incx=(an-side)/2.0
|
||||
l,r,an =(l+incx,r-incx,side)
|
||||
incy=(al-side)/2.0
|
||||
t +=incy
|
||||
b -=incy
|
||||
al = side
|
||||
cX, cY = (an/2.0,al/2.0)
|
||||
|
||||
pp = node.getparent()
|
||||
varBez = 0.551915024494
|
||||
a = self.svg.unittouu(sO.size)
|
||||
a_2, a2 = (a / 2.0,a * 2.0)
|
||||
dS = "m %sz"
|
||||
pnts = [[l+cX,t],[cX,cY],[-cX,cY],[-cX,-cY]]
|
||||
aa = a * varBez
|
||||
chtype=sO.chamfertype
|
||||
tritype=sO.tritype
|
||||
if sh=='"chamfer"':
|
||||
an2, al2 = ((an-a)/2.0,(al-a)/2.0)
|
||||
if chtype=="rombus" and a>0:
|
||||
pnts=[[l+cX - a_2,t],[a,0],[an2,al2],[0,a],[-an2,al2],[-a,0],[-an2,-al2],[0,-a]]
|
||||
if chtype=="chamfer":
|
||||
pnts=[[l+a,t],[an - a2,0],[a,a],[0,al-a2],[-a,a],[-(an - a2),0],[-a,-a],[0,-(al-a2)]]
|
||||
if chtype=="chamferinv":
|
||||
pnts=[[l,t],[a,0],[-a,a],[an-a,0," z m"],[a,0],[0,a],[a,al," z m"],[0,-a],[-a,a],[-an+a,0," z m"],[-a,-a],[0,a]]
|
||||
if chtype=="round":
|
||||
pnts = circQ(XY(l,t),a,"B",0,{1:"C"}) + circQ(XY(l,b),a,"A",0,{0:"L",1:"C"}) + circQ(XY(r,b),a,"D",0,{0:"L",1:"C"}) + circQ(XY(r,t),a,"C",0,{0:"L",1:"C"})
|
||||
if chtype=="roundinv":
|
||||
pnts=[[l,t],[a,0],[0,aa,"c "],[-aa,a],[-a,a],[an-a,0,"z m "],[a,0],[0,a],[-aa,0," c"],[-a,-aa],[-a,-a],
|
||||
[a,al-a,"z m "],[0,a],[-a,0],[0,-aa,"c "],[aa,-a],[a,-a],[-an,0,"z m "],[0,a],[a,0],[0,-aa,"c "],[-aa,-a],[-a,-a]]
|
||||
if chtype=="rect":
|
||||
pnts=[[l+a,t],[an - a2,0],[0,a],[a,0],[0,al-a2],[-a,0],[0,a],[-(an-a2),0],[0,-a],[-a,0],[0,-(al-a2)],[a,0]]
|
||||
if chtype=="cross":
|
||||
pnts=[[l+an2,t],[a,0],[0,al2],[an2,0],[0,a],[-an2,0],[0,al2],[-a,0],[0,-al2],[-an2,0],[0,-a],[an2,0]]
|
||||
if chtype=="starcorners":
|
||||
pnts=[[l,t],[cX,al2],[cX,-al2],[-an2,cY],[an2,cY],[-cX,-al2],[-cX,al2],[an2,-cY]]
|
||||
if chtype=="starcenter":
|
||||
pnts=[[l+cX,t],[a_2,al2], [an2,a_2], [-an2,a_2],[-a_2,al2],[-a_2,-al2],[-an2,-a_2],[an2,-a_2]]
|
||||
if chtype=="crosscornersquad":
|
||||
pnts=[[l-a,t],[0,-a],[a,0],[0,al+a*2],[-a,0],[0,-a],[an+a*2,0],[0,a],[-a,0],[0,-al-a*2],[a,0],[0,a]]
|
||||
if chtype=="crosscornerstri":
|
||||
pnts=[[l-a,t],[a,-a],[0,al+a*2],[-a,-a],[an+a*2,0],[-a,a], [0,-al-a*2],[a,a]]
|
||||
if chtype=="crosscornersround":
|
||||
dS = "M %sZ"
|
||||
aa2 = a_2 * varBez
|
||||
p1 = circQ(XY(r + a_2, t - a_2),a_2,"DAB",1)
|
||||
p2 = circQ(XY(r + a_2, b + a_2),a_2,"ABC",1)
|
||||
p3 = circQ(XY(l - a_2, b + a_2),a_2,"BCD",1)
|
||||
p4 = circQ(XY(l - a_2, t - a_2),a_2,"CDA",1)
|
||||
pnts = p1 + [[r,t],[r,b+a_2-aa2]] + p2 + [[r+a_2-aa2,b],[l-a_2+aa2,b]] + p3 + [[l,b+a_2-aa],[l,t-a_2+aa]] + p4
|
||||
pnts[1].append(" C")
|
||||
|
||||
if sh=='"triangles"':
|
||||
trihside, trivside=(sO.trihside, sO.trivside)
|
||||
if tritype=="isosceles": pnts=[[l+cX,t],[cX,al],[-an,0]]
|
||||
if tritype=="equi":
|
||||
sqrt3 = 1.7320508075
|
||||
height = sqrt3/2*side
|
||||
tcx, tcy = ((an - side)/2.0, (al - height)/2.0)
|
||||
pnts=[[cX+l,t+tcy],[an/2.0-tcx,height],[-side,0]]
|
||||
if tritype=="rect":
|
||||
x1 = l + tern(not trivside and trihside,an,0)
|
||||
x2 = tern(not trivside and trihside,0,an)
|
||||
x3 = tern(trivside and trihside,0,-an)
|
||||
pnts=[[x1,t],[x2,tern(not trivside,al,0)],[x3,tern(not trivside,0,al)]]
|
||||
if sh=='"spikes"':
|
||||
spikestype = sO.spikestype
|
||||
spikesdir = sO.spikesdir
|
||||
ssep = self.svg.unittouu(sO.spikesep)
|
||||
ss = self.svg.unittouu(sO.spikesize)
|
||||
anX, anY = (int( (an+ssep) / (ss * 2 + ssep)), int( (al+ssep) / (ss * 2 + ssep)))
|
||||
iniX, iniY = (((an+ssep) - (anX * (ss * 2 + ssep))) / 2.0, ((al+ssep) - (anY * (ss * 2 + ssep))) / 2.0)
|
||||
dir = 1
|
||||
pnts = [[l,t],[iniX,0]]
|
||||
if spikesdir=='ins': dir = -1.0
|
||||
if spikestype=="tri":
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[ss,-ss*dir],[ss,ss*dir]])
|
||||
if ssep>0 and n < (anX-1): pnts.append([ssep,0])
|
||||
pnts.extend([[iniX,0],[0,iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[ss * dir,ss],[-ss * dir,ss]])
|
||||
if ssep>0 and n < (anY-1): pnts.append([0, ssep])
|
||||
pnts.extend([[0,iniY],[-iniX,0]])
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[-ss,ss*dir],[-ss,-ss*dir]])
|
||||
if ssep>0 and n < (anX-1): pnts.append([-ssep,0])
|
||||
pnts.extend([[-iniX,0],[0,-iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[-ss*dir,-ss],[ss*dir,-ss]])
|
||||
if ssep>0 and n < (anY-1): pnts.append([0, -ssep])
|
||||
if spikestype=="trirect":
|
||||
anX, anY = ( int((an + ssep) / (ss + ssep)), int((al + ssep) / (ss + ssep)) )
|
||||
iniX, iniY = (((an + ssep) - (anX * (ss + ssep))) / 2.0, ((al + ssep) - (anY * (ss + ssep))) / 2.0)
|
||||
pnts = [[l,t],[iniX,0]]
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,-ss*dir],[ss,ss*dir]])
|
||||
if ssep>0 and n < (anX-1): pnts.append([ssep,0])
|
||||
pnts.extend([[iniX,0],[0,iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[ss * dir,0],[-ss * dir,ss]])
|
||||
if ssep>0 and n < (anY-1): pnts.append([0, ssep])
|
||||
pnts.extend([[0,iniY],[-iniX,0]])
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,ss*dir],[-ss,-ss*dir]])
|
||||
if ssep>0 and n < (anX-1): pnts.append([-ssep,0])
|
||||
pnts.extend([[-iniX,0],[0,-iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[-ss*dir,0],[ss*dir,-ss]])
|
||||
if ssep>0 and n < (anY-1): pnts.append([0, -ssep])
|
||||
if spikestype=="squ":
|
||||
anX, anY = ( int((an + ssep) / (ss + ssep)), int((al + ssep) / (ss + ssep)) )
|
||||
iniX, iniY = (((an + ssep) - (anX * (ss + ssep))) / 2.0, ((al + ssep) - (anY * (ss + ssep))) / 2.0)
|
||||
pnts = [[l,t],[iniX,0]]
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,-ss * dir], [ss,0], [0,ss * dir]])
|
||||
if ssep>0 and n < (anX-1): pnts.append([ssep,0])
|
||||
pnts.extend([[iniX,0],[0,iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[ss * dir,0],[0,ss],[-ss * dir,0]])
|
||||
if ssep>0 and n < (anY-1): pnts.append([0,ssep])
|
||||
pnts.extend([[0,iniY],[-iniX,0]])
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,ss * dir],[-ss,0],[0,-ss * dir]])
|
||||
if ssep>0 and n < (anX-1): pnts.append([-ssep,0])
|
||||
pnts.extend([[-iniX,0],[0,-iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[-ss * dir,0],[0,-ss],[ss * dir,0]])
|
||||
if ssep>0 and n < (anY-1): pnts.append([0,-ssep])
|
||||
|
||||
if spikestype=="rnd":
|
||||
dif = ss - (ss*varBez)
|
||||
dBez = ss*varBez
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,-dBez * dir," c"],[dif,-ss * dir],[ss,-ss * dir],#fijo
|
||||
[dBez,0],[ss,dif * dir],[ss,ss * dir]]) #fijo
|
||||
if ssep>0 and n < (anX-1): pnts.append([ssep,0,' l'])
|
||||
pnts.extend([[iniX,0," l"],[0,iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[dBez * dir,0," c"],[ss * dir,dif],[ss * dir,ss],#fijo
|
||||
[0,dBez],[-dif * dir,ss],[-ss * dir,ss]]) #fijo
|
||||
if ssep>0 and n < (anY-1): pnts.append([0,ssep,' l'])
|
||||
pnts.extend([[0,iniY,' l'],[-iniX,0]])
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,dBez * dir," c"],[-dif,ss * dir],[-ss,ss * dir],#fijo
|
||||
[-dBez,0],[-ss,-dif * dir],[-ss,-ss * dir]]) #fijo
|
||||
if ssep>0 and n < (anX-1): pnts.append([-ssep,0,' l'])
|
||||
pnts.extend([[-iniX,0,' l'],[0,-iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[-dBez * dir,0," c"],[-ss * dir,-dif],[-ss * dir,-ss],#fijo
|
||||
[0,-dBez],[dif * dir,-ss],[ss * dir,-ss]]) #fijo
|
||||
if ssep>0 and n < (anY-1): pnts.append([0,-ssep,' l'])
|
||||
|
||||
if spikestype=="wav":
|
||||
dif = ss - (ss*varBez)
|
||||
dBez = ss*varBez
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,-dBez * dir," c"],[dif,-ss * dir],[ss,-ss * dir],#fijo
|
||||
[0,dBez*dir],[dBez,ss*dir],[ss,ss * dir]]) #fijo
|
||||
if ssep>0 and n < (anX-1): pnts.append([ssep,0,' l'])
|
||||
pnts.extend([[iniX,0," l"],[0,iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[dBez * dir,0," c"],[ss * dir,dif],[ss * dir,ss],#fijo
|
||||
[-dBez*dir,0],[-ss*dir,dBez],[-ss * dir,ss]]) #fijo
|
||||
if ssep>0 and n < (anY-1): pnts.append([0,ssep,' l'])
|
||||
pnts.extend([[0,iniY,' l'],[-iniX,0]])
|
||||
for n in range(anX):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[0,dBez * dir," c"],[-dif,ss * dir],[-ss,ss * dir],#fijo
|
||||
[0,-dBez*dir], [-dif, -ss*dir],[-ss,-ss * dir]]) #fijo
|
||||
if ssep>0 and n < (anX-1): pnts.append([-ssep,0,' l'])
|
||||
|
||||
pnts.extend([[-iniX,0,' l'],[0,-iniY]])
|
||||
for n in range(anY):
|
||||
if spikesdir == 'alt' : dir = 1 if n % 2 == 1 else -1
|
||||
pnts.extend([[-dBez * dir,0," c"],[-ss * dir,-dif],[-ss * dir,-ss],#fijo
|
||||
[dBez*dir,0],[ss*dir,-dBez],[ss * dir,-ss]]) #fijo
|
||||
if ssep>0 and n < (anY-1): pnts.append([0,-ssep,' l'])
|
||||
if sh=='"arrow"':
|
||||
arrowType=sO.arrowtype
|
||||
headH, headW, arrowW = (self.svg.unittouu(sO.headHeight), self.svg.unittouu(sO.headWidth), self.svg.unittouu(sO.arrowWidth))
|
||||
hw2=headW/2.0
|
||||
if arrowType=="arrowfilled":
|
||||
pnts=[[l+cX,t],[hw2,headH],[-(headW-arrowW)/2.0,0],[0,al-headH],[-arrowW,0],[0,-(al-headH)],[-(headW-arrowW)/2.0,0]]
|
||||
else:
|
||||
dS = "m %s"
|
||||
pnts=[[l+cX,t],[0,al],[-hw2,-al+headH,"m "],[hw2,-headH],[hw2,headH]]
|
||||
d = ""
|
||||
for n in pnts:
|
||||
ss = "" if len(n)<3 else n[2]
|
||||
d += "%s%s,%s " % (ss, str(n[0]),str(n[1]))
|
||||
nn = self.addEle('path',pp, {'d':dS % (d)})
|
||||
self.estilo(nn,node)
|
||||
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
|
||||
def makeRel(self,arr):
|
||||
b = arr[:]
|
||||
for n in range(1,len(arr)):
|
||||
s = b[n]
|
||||
for i in range(0,n):
|
||||
s = s - arr[i]
|
||||
b[n] = s
|
||||
return b
|
||||
|
||||
def circle(self,p,r):
|
||||
varBez = 0.551915024494
|
||||
dS = "m %s"
|
||||
aa = r * varBez
|
||||
d=""
|
||||
pnts=[[p.x - r,p.y],[0,aa,"c "],[r - aa,r],[r,r],[aa,0,"c "],[r,-r+aa],[r,-r],[0,-aa,"c "],[-r+aa,-r],[-r,-r],[-aa,0,"c "],[-r,r-aa],[-r, r]]
|
||||
for n in pnts:
|
||||
ss = "" if len(n)<3 else n[2]
|
||||
d += "%s%s,%s " % (ss, str(n[0]),str(n[1]))
|
||||
return d
|
||||
|
||||
def addTxt(self, node, x, y, text, dy = 0):
|
||||
new2 = self.addEle(inkex.addNS('text','svg'), node,{'x':str(x),'y':str(y)})
|
||||
new = etree.SubElement(new2, inkex.addNS('tspan','svg'), {inkex.addNS('role','sodipodi'): 'line'})
|
||||
new.set('style','text-align:center; vertical-align:bottom; font-size:10; fill-opacity:1.0;stroke:none; font-weight:normal; font-style:normal; fill:#000000')
|
||||
new.set('dy', str(dy))
|
||||
new.text = str(text)
|
||||
|
||||
def circsCone(self, sels, sh='"rombus"'):
|
||||
sO = self.options
|
||||
copyfill = sO.copyfill
|
||||
deleteorigin = sO.deleteorigin
|
||||
joincirctype = sO.joincirctype
|
||||
r2 = sO.joinradius
|
||||
|
||||
for nodos in range(len(sels)-1):
|
||||
node = sels[nodos]
|
||||
node2 = sels[nodos+1]
|
||||
lA, rA, tA, bA, anA, alA = self.limits(node)
|
||||
lB, rB, tB, bB, anB, alB = self.limits(node2)
|
||||
rA, cY = (anA/2.0,alA/2.0)
|
||||
rB, cY2 = (anB/2.0,alB/2.0)
|
||||
|
||||
PtA = XY(lA + rA, tA + cY)
|
||||
PtB = XY(lB + rB, tB + cY2)
|
||||
if (circleInCircle(PtA,rA,PtB,rB) or circleInCircle(PtB,rB,PtA,rA)):
|
||||
pass
|
||||
else:
|
||||
pp = node.getparent()
|
||||
rotAB = XY(PtB).getAngle(PtA)
|
||||
dist = PtA.hipo(PtB)
|
||||
if joincirctype=='trapecio':
|
||||
# alineamos las esferas en Y
|
||||
rDif = rA - rB
|
||||
Axis = XY(-rDif,0)
|
||||
D2 = math.sqrt((dist*dist) - (rDif*rDif)) / dist
|
||||
P1 = XY(Axis).mul(rA / dist)
|
||||
P2 = XY(-dist,0) + XY(Axis).mul(rB / dist)
|
||||
r = P1.VDist(P2)
|
||||
Rot1 = XY(P2.x,rB * D2).getAngleD(XY(P2.x + r, rA * D2))
|
||||
curva1a = bezs2XYList(createArcBez(rA,-90 -Rot1, -270 + Rot1))
|
||||
d = XYListSt(curva1a, rotAB, PtA)
|
||||
pnts2 = bezs2XYList(createArcBez(rB, 90 + Rot1, 270 - Rot1),XY(-dist,0))
|
||||
d2 = XYListSt(pnts2, rotAB, PtA)
|
||||
nn = self.addEle('path',pp, {'d':"M%s L%sZ" % (d,d2)})
|
||||
self.estilo(nn,node)
|
||||
# ################## B L O B ##############
|
||||
if joincirctype=='blob':
|
||||
if ((r2==0) and (dist<(rA+rB))):
|
||||
r2 = dist - rB
|
||||
if (r2 > 0):
|
||||
rad1 = rA + r2
|
||||
rad2 = rB + r2
|
||||
a = (math.pow(dist,2) - math.pow(rB+r2,2) + math.pow(rA+r2,2))/(dist*2)
|
||||
else:
|
||||
r2 = dist - rA - rB
|
||||
rad1 = dist - rB
|
||||
rad2 = dist - rA
|
||||
a = (math.pow(dist-rB,2) - math.pow(dist-rA,2) + math.pow(dist,2))/(dist*2);
|
||||
# alineamos las esferas en Y
|
||||
|
||||
rt = math.atan2(PtB.y - PtA.y, PtB.x - PtA.x)
|
||||
# # distancia del centro 1 a la interseccion de los circulos
|
||||
x = (dist * dist - rad2 * rad2 + rad1 * rad1) / (dist*2)
|
||||
if (rad1 * rad1 - x * x) > 0 :
|
||||
catB = math.sqrt(rad1 * rad1 - x * x)
|
||||
|
||||
rt = math.degrees(XY(0,0).getAngle(XY(-x, -catB)))
|
||||
rt2 = math.degrees(XY(0,0).getAngle(XY(-(dist - x), -catB)))
|
||||
|
||||
curva1 = bezs2XYList(createArcBez(rA, rt, -rt))
|
||||
curva1.reverse()
|
||||
curva2 = bezs2XYList(createArcBez(r2, -180 + rt, -rt2),XY(-x, -catB))
|
||||
curva3 = bezs2XYList(createArcBez(rB, rt2+180,180-rt2),XY(-dist, 0))
|
||||
curva3.reverse()
|
||||
curva4 = bezs2XYList(createArcBez(r2, rt2, 180 - rt),XY(-x, catB))
|
||||
|
||||
curva1= curva1+curva2[1:]+curva3[1:]+curva4[1:]
|
||||
sCurva1 = XYListSt(curva1, rotAB, PtA)
|
||||
|
||||
nn = self.addEle('path',pp,{'d':"M %s" % (sCurva1)})
|
||||
self.estilo(nn,node)
|
||||
# ################################################
|
||||
# ################## O V A L #####################
|
||||
# ################################################
|
||||
if joincirctype=='oval':
|
||||
minR2 = dist + min(rA,rB)
|
||||
if r2 < minR2:
|
||||
r2 = minR2
|
||||
info('Changed radius to '+str(minR2))
|
||||
rad1 = r2 - rA
|
||||
rad2 = r2 - rB
|
||||
a = (math.pow(dist,2) - math.pow(rB+r2,2) + math.pow(rA+r2,2))/(dist*2)
|
||||
|
||||
rt = math.atan2(PtB.y - PtA.y, PtB.x - PtA.x)
|
||||
D = dist #XY(PtA).sub(PtB).vlength() # distancia entre los centros
|
||||
# distancia del centro 1 a la interseccion de los circulos
|
||||
x = (D*D - rad2 * rad2 + rad1 * rad1) / (D*2)
|
||||
catB = math.sqrt(rad1 * rad1 - x * x)
|
||||
|
||||
rotAB=XY(PtB).getAngle(PtA)
|
||||
rot1 = math.degrees(XY(0,0).getAngle(XY(-x,-catB))) + 180.0
|
||||
curva1 = bezs2XYList(createArcBez(rA, -rot1, rot1))
|
||||
curva1.reverse()
|
||||
rot2 = math.degrees(XY(-dist,0).getAngle(XY(-x,-catB))) +180.0
|
||||
curva2 = bezs2XYList(createArcBez(r2, -rot2,-rot1),XY(-x,catB))
|
||||
curva2.reverse()
|
||||
curva3 = bezs2XYList(createArcBez(rB, rot2,-rot2),XY(-dist,0))
|
||||
curva3.reverse()
|
||||
curva4 = bezs2XYList(createArcBez(r2, rot1,rot2),XY(-x,-catB))
|
||||
curva4.reverse()
|
||||
curva1= curva1+curva2[1:]+curva3[1:]+curva4[1:] #+curva3[1:]+curva4[1:]
|
||||
sCurva1 = XYListSt(curva1, rotAB, PtA)
|
||||
# curva1
|
||||
nn = self.addEle('path',pp,{'d':"M %sZ" % (sCurva1),'style':'stroke-width:0.02;fill:#cc0000;stroke:#000000;'})
|
||||
self.estilo(nn,node)
|
||||
if deleteorigin: node.getparent().remove(node)
|
||||
|
||||
def draw_shapes(self):
|
||||
tab = str(self.options.tab)
|
||||
sels = []
|
||||
for id, node in self.svg.selected.items():
|
||||
sels.append(node)
|
||||
if tab != '"extra"':
|
||||
for id, node in self.svg.selected.items():
|
||||
self.draw(node, tab)
|
||||
else:
|
||||
if len(sels)<2:
|
||||
inkex.errormsg('Select at least two objects')
|
||||
else:
|
||||
self.circsCone(sels, tab)
|
||||
|
||||
def loc_str(self, str):
|
||||
return locale.format("%.f", float(str), 0)
|
||||
|
||||
def effect(self):
|
||||
slices = self.draw_shapes()
|
||||
|
||||
if __name__ == "__main__":
|
||||
Shapes().run()
|
Reference in New Issue
Block a user