expanded join paths extension to create dimples/pressfits
This commit is contained in:
parent
6c3966cbd3
commit
9700a2ef34
@ -1,10 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||
<name>Join Paths</name>
|
||||
<name>Join Paths / Create Dimples</name>
|
||||
<id>fablabchemnitz.de.join_paths</id>
|
||||
<param name="tab" type="notebook">
|
||||
<page name="subdividePath" gui-text="Join Paths">
|
||||
<param name="optimized" type="bool" gui-text="Optimized">true</param>
|
||||
<param name="optimized" type="bool" gui-text="Optimized joining">true</param>
|
||||
<label appearance="header">Create Dimples</label>
|
||||
<label>Enable to insert dimples (pressfit noses) into the gaps (instead regular straight lines)</label>
|
||||
<param name="add_dimples" type="bool" gui-text="Create dimples">false</param>
|
||||
<param name="dimple_type" type="optiongroup" appearance="combo" gui-text="Dimple type">
|
||||
<option value="lines">lines</option>
|
||||
<option value="arcs">arcs</option>
|
||||
</param>
|
||||
<param name="draw_dimple_centers" type="bool" gui-text="Draw dimple centers">false</param>
|
||||
<param name="dimple_invert" type="bool" gui-text="Invert dimples">false</param>
|
||||
<param name="draw_both_sides" type="bool" gui-text="Draw both sides">false</param>
|
||||
<param name="dimple_height_mode" type="optiongroup" appearance="combo" gui-text="Height by">
|
||||
<option value="by_height">by height</option>
|
||||
<option value="by_angle">by angle</option>
|
||||
</param>
|
||||
<param name="dimple_angle" type="float" min="0.000" max="360.000" precision="3" gui-text="Angle">45.000</param>
|
||||
<param name="dimple_height" type="float" min="0.001" max="99999.000" precision="3" gui-text="Height">4.000</param>
|
||||
<param name="dimple_height_units" gui-text="Height units" 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="dimples_to_group" type="bool" gui-text="Unify into single group">false</param>
|
||||
</page>
|
||||
<page name="desc" gui-text="Help">
|
||||
<label xml:space="preserve">This effect joins the Bezier curves, with straight line segments. If the end nodes are close enough, they are merged into a single one. With the optimized option selected, the new curve starts from the top most curve from the selection. The curves are then joined based on the distance of their closest end point to the previous curve.
|
||||
|
@ -24,7 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
import inkex
|
||||
from inkex.paths import CubicSuperPath
|
||||
import sys
|
||||
import copy
|
||||
import math
|
||||
|
||||
def floatCmpWithMargin(float1, float2, margin):
|
||||
return abs(float1 - float2) < margin
|
||||
@ -104,21 +106,45 @@ def getArrangedIds(pathMap, startPathId):
|
||||
nextPathId = closestId
|
||||
return orderPathIds
|
||||
|
||||
def rotate(origin, point, angle):
|
||||
"""
|
||||
Rotate a point counterclockwise by a given angle around a given origin.
|
||||
|
||||
The angle should be given in radians.
|
||||
"""
|
||||
ox, oy = origin
|
||||
px, py = point
|
||||
|
||||
qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
|
||||
qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
|
||||
return qx, qy
|
||||
|
||||
class JoinPaths(inkex.EffectExtension):
|
||||
|
||||
def add_arguments(self, pars):
|
||||
pars.add_argument("--optimized", type=inkex.Boolean, default=True)
|
||||
pars.add_argument("--add_dimples", type=inkex.Boolean, default=False)
|
||||
pars.add_argument("--draw_dimple_centers", type=inkex.Boolean, default=False)
|
||||
pars.add_argument("--dimple_invert", type=inkex.Boolean, default=False)
|
||||
pars.add_argument("--dimple_type", default="lines")
|
||||
pars.add_argument("--dimples_to_group", type=inkex.Boolean, default=False)
|
||||
pars.add_argument("--draw_both_sides", type=inkex.Boolean, default=False)
|
||||
pars.add_argument("--dimple_height_mode", default="by_height")
|
||||
pars.add_argument("--dimple_height", type=float, default=4)
|
||||
pars.add_argument("--dimple_angle", type=float, default=45)
|
||||
pars.add_argument("--dimple_height_units", default="mm")
|
||||
pars.add_argument("--tab", default="sampling", help="Tab")
|
||||
|
||||
def effect(self):
|
||||
selections = self.svg.selected
|
||||
pathNodes = self.document.xpath('//svg:path',namespaces=inkex.NSS)
|
||||
|
||||
paths = {p.get('id'): getPartsFromCubicSuper(CubicSuperPath(p.get('d'))) for p in pathNodes }
|
||||
|
||||
paths = {p.get('id'): getPartsFromCubicSuper(CubicSuperPath(p.get('d'))) for p in pathNodes }
|
||||
#paths.keys() Order disturbed
|
||||
pathIds = [p.get('id') for p in pathNodes]
|
||||
|
||||
if self.options.dimples_to_group is True:
|
||||
dimpleUnifyGroup = self.svg.get_current_layer().add(inkex.Group(id=self.svg.get_unique_id("dimplesCollection")))
|
||||
|
||||
if(len(paths) > 1):
|
||||
if(self.options.optimized):
|
||||
startPathId = pathIds[0]
|
||||
@ -140,16 +166,130 @@ class JoinPaths(inkex.EffectExtension):
|
||||
if(vectCmpWithMargin(start, newParts[-1][-1][-1], margin = .01)):
|
||||
newParts[-1] += parts[0]
|
||||
else:
|
||||
newSeg = [newParts[-1][-1][-1], newParts[-1][-1][-1], start, start]
|
||||
newParts[-1].append(newSeg)
|
||||
newParts[-1] += parts[0]
|
||||
|
||||
if self.options.add_dimples is True:
|
||||
if self.options.dimples_to_group is True:
|
||||
dimpleGroup = dimpleUnifyGroup.add(inkex.Group(id="dimpleGroup-{}".format(elem.attrib["id"])))
|
||||
else:
|
||||
dimpleGroup = elem.getparent().add(inkex.Group(id="dimpleGroup-{}".format(elem.attrib["id"])))
|
||||
|
||||
p1 = newParts[-1][-1][-1]
|
||||
p2 = start
|
||||
midPoint = [(p1[0] + p2[0])/2, (p1[1] + p2[1])/2]
|
||||
newParts[-1].append([newParts[-1][-1][-1], newParts[-1][-1][-1], midPoint, midPoint])
|
||||
newParts[-1].append([newParts[-1][-1][-1], newParts[-1][-1][-1], p2, p2])
|
||||
newParts[-1] += parts[0]
|
||||
|
||||
#angle=self.options.dimple_angle
|
||||
#p3 = rotate(midPoint, p2, math.radians(angle))
|
||||
#p4 = rotate(midPoint, p2, math.radians(360-angle))
|
||||
|
||||
#add a new dimple
|
||||
#line = self.svg.get_current_layer().add(inkex.PathElement(id=self.svg.get_unique_id('dimple')))
|
||||
#line.set('d', "m{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], p3[0], p3[1]))
|
||||
#line.style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
|
||||
|
||||
#add a new dimple
|
||||
#line = self.svg.get_current_layer().add(inkex.PathElement(id=self.svg.get_unique_id('dimple')))
|
||||
#line.set('d', "m{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], p4[0], p4[1]))
|
||||
#line.style = {'stroke': '#000000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
|
||||
|
||||
dx = midPoint[0]-p1[0]
|
||||
dy = midPoint[1]-p1[1]
|
||||
dist = math.sqrt(dx*dx + dy*dy)
|
||||
dx /= dist
|
||||
dy /= dist
|
||||
|
||||
dx2 = p2[0]-p1[0]
|
||||
dy2 = p2[1]-p1[1]
|
||||
dist2 = math.sqrt(dx2*dx2 + dy2*dy2)
|
||||
|
||||
if dx2 == 0:
|
||||
slope=sys.float_info.max #vertical
|
||||
else:
|
||||
slope=(p2[1] - p1[1]) / dx2
|
||||
slope_angle = 90 + math.degrees(math.atan(slope))
|
||||
|
||||
if self.options.dimple_height_mode == "by_height":
|
||||
dimple_height = self.svg.unittouu(str(self.options.dimple_height) + self.options.dimple_height_units)
|
||||
else:
|
||||
dimple_height = dist * math.sin(math.radians(self.options.dimple_angle))
|
||||
|
||||
x3 = midPoint[0] + (dimple_height)*dy
|
||||
y3 = midPoint[1] - (dimple_height)*dx
|
||||
x4 = midPoint[0] - (dimple_height)*dy
|
||||
y4 = midPoint[1] + (dimple_height)*dx
|
||||
|
||||
dimple_style = {'stroke': '#0000FF', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px'))}
|
||||
|
||||
if self.options.draw_dimple_centers is True:
|
||||
#add a new dimple center cross (4 segments)
|
||||
line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_perp1')))
|
||||
line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], x3, y3))
|
||||
line.style = dimple_style
|
||||
line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_perp2')))
|
||||
line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], x4, y4))
|
||||
line.style = dimple_style
|
||||
line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_join1')))
|
||||
line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], midPoint[0], midPoint[1]))
|
||||
line.style = dimple_style
|
||||
line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple_center_join1')))
|
||||
line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(midPoint[0], midPoint[1], p2[0], p2[1]))
|
||||
line.style = dimple_style
|
||||
|
||||
if self.options.dimple_type == "lines":
|
||||
if self.options.dimple_invert is True:
|
||||
x5 = x3
|
||||
y5 = y3
|
||||
x3 = x4
|
||||
y3 = y4
|
||||
x4 = x5
|
||||
y4 = y5
|
||||
#add a new dimple center
|
||||
line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple')))
|
||||
line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], x3, y3, p2[0], p2[1]))
|
||||
line.style = dimple_style
|
||||
|
||||
if self.options.draw_both_sides is True:
|
||||
#add a new opposite dimple center
|
||||
line = dimpleGroup.add(inkex.PathElement(id=self.svg.get_unique_id('dimple')))
|
||||
line.set('d', "M{:0.6f},{:0.6f} L{:0.6f},{:0.6f} L{:0.6f},{:0.6f}".format(p1[0], p1[1], x4, y4, p2[0], p2[1]))
|
||||
line.style = dimple_style
|
||||
else:
|
||||
ellipse = dimpleGroup.add(inkex.Ellipse(id=self.svg.get_unique_id('dimple')))
|
||||
ellipse.set('transform', "rotate({:0.6f} {:0.6f} {:0.6f})".format(slope_angle, midPoint[0], midPoint[1]))
|
||||
ellipse.set('sodipodi:arc-type', "arc")
|
||||
ellipse.set('sodipodi:type', "arc")
|
||||
ellipse.set('sodipodi:cx', "{:0.6f}".format(midPoint[0]))
|
||||
ellipse.set('sodipodi:cy', "{:0.6f}".format(midPoint[1]))
|
||||
ellipse.set('sodipodi:rx', "{:0.6f}".format(dimple_height))
|
||||
ellipse.set('sodipodi:ry', "{:0.6f}".format(dist2 / 2))
|
||||
if self.options.draw_both_sides is False:
|
||||
ellipse.set('sodipodi:open', 'true')
|
||||
if self.options.dimple_invert is True:
|
||||
ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(90.0)))
|
||||
ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(270.0)))
|
||||
else:
|
||||
ellipse.set('sodipodi:start', "{:0.6f}".format(math.radians(270.0)))
|
||||
ellipse.set('sodipodi:end', "{:0.6f}".format(math.radians(90.0)))
|
||||
ellipse.style = dimple_style
|
||||
|
||||
#cleanup groups
|
||||
if len(dimpleGroup) == 1: ##move up child if group has only one child
|
||||
for child in dimpleGroup:
|
||||
dimpleGroup.getparent().insert(elem.getparent().index(elem), child)
|
||||
dimpleGroup.delete() #delete the empty group now
|
||||
|
||||
else:
|
||||
newParts[-1].append([newParts[-1][-1][-1], newParts[-1][-1][-1], start, start])
|
||||
newParts[-1] += parts[0]
|
||||
|
||||
if(len(parts) > 1):
|
||||
newParts += parts[1:]
|
||||
|
||||
parent = elem.getparent()
|
||||
idx = parent.index(elem)
|
||||
parent.remove(elem)
|
||||
if self.options.add_dimples is False:
|
||||
parent.remove(elem)
|
||||
except:
|
||||
pass #elem might come from group item - in this case we need to ignore it
|
||||
|
||||
@ -157,7 +297,8 @@ class JoinPaths(inkex.EffectExtension):
|
||||
oldId = firstElem.get('id')
|
||||
newElem.set('d', CubicSuperPath(getCubicSuperFromParts(newParts)))
|
||||
newElem.set('id', oldId + '_joined')
|
||||
parent.insert(idx, newElem)
|
||||
if self.options.add_dimples is False:
|
||||
parent.insert(idx, newElem)
|
||||
|
||||
if __name__ == '__main__':
|
||||
JoinPaths().run()
|
@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"name": "Join Paths",
|
||||
"name": "Join Paths / Create Dimples",
|
||||
"id": "fablabchemnitz.de.join_paths",
|
||||
"path": "join_paths",
|
||||
"original_name": "Join Paths Optimized",
|
||||
|
Reference in New Issue
Block a user