Removed Extensions "elliptical box", "encoder disk generator", "fill row", "netting", "scatter", "shelves", "sheriff_star" and "x-agram" due to unresolved license status

This commit is contained in:
Mario Voigt 2019-12-23 01:51:36 +01:00
parent 4db21a039b
commit 3f8da06b73
17 changed files with 0 additions and 2608 deletions

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Box Maker - Elliptical Box</_name>
<id>fablabchemnitz.de.elliptical_box</id>
<dependency type="executable" location="extensions">inkex.py</dependency>
<dependency type="executable" location="extensions">fablabchemnitz_elliptical_box.py</dependency>
<param name="unit" type="enum" _gui-text="Unit">
<_item value="mm">mm</_item>
<_item value="cm">cm</_item>
<_item value="m">m</_item>
<_item value="km">km</_item>
<_item value="in">in</_item>
<_item value="ft">ft</_item>
<_item value="yd">yd</_item>
<_item value="pt">pt</_item>
<_item value="px">px</_item>
<_item value="pc">pc</_item>
</param>
<param name="thickness" type="float" min="1.0" max="100.0" _gui-text="Material thickness">3.0</param>
<param name="height" type="float" min="0.0" max="10000.0" _gui-text="Height">10.0</param>
<param name="width" type="float" min="0.0" max="10000.0" _gui-text="Width">10.0</param>
<param name="depth" type="float" min="0.0" max="10000.0" _gui-text="Depth">10.0</param>
<param name="cut_dist" type="float" min="0.1" max="100.0" _gui-text="Cut distance">1.5</param>
<param name="auto_cut_dist" type="boolean" _gui-text="Automatically vary cut distance according to curvature">false</param>
<param name="cut_nr" type="int" min="1" max="100" _gui-text="Number of cuts">3</param>
<param name="lid_angle" type="float" min="0.0" max="360.0" _gui-text="Lid angle">120</param>
<param name="invert_lid_notches" type="boolean" _gui-text="Invert lid notch pattern (this will create a lid without sideways support)">false</param>
<param name="central_rib_lid" type="boolean" _gui-text="Create central rib in the lid(requires an even number of cuts)">false</param>
<param name="central_rib_body" type="boolean" _gui-text="Create central rib in the body(requires an even number of cuts)">false</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Finger-jointed/Tabbed Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">fablabchemnitz_elliptical_box.py</command>
</script>
</inkscape-extension>

View File

@ -1,500 +0,0 @@
#!/usr/bin/env python
# We will use the inkex module with the predefined Effect base class.
import inkex
# The simplestyle module provides functions for style parsing.
import simplestyle
from math import *
from collections import namedtuple
#Note: keep in mind that SVG coordinates start in the top-left corner i.e. with an inverted y-axis
# first define some SVG primitives (we do not use them all so a cleanup may be in order)
objStyle = simplestyle.formatStyle(
{'stroke': '#000000',
'stroke-width': 0.28,
'fill': 'none'
})
greenStyle = simplestyle.formatStyle(
{'stroke': '#00ff00',
'stroke-width': 0.28,
'fill': 'none'
})
def draw_SVG_square((w,h), (x,y), parent):
attribs = {
'style': objStyle,
'height': str(h),
'width': str(w),
'x': str(x),
'y': str(y)
}
inkex.etree.SubElement(parent, inkex.addNS('rect', 'svg'), attribs)
def draw_SVG_ellipse((rx, ry), center, parent, start_end=(0, 2*pi), transform=''):
ell_attribs = {'style': objStyle,
inkex.addNS('cx', 'sodipodi'): str(center.x),
inkex.addNS('cy', 'sodipodi'): str(center.y),
inkex.addNS('rx', 'sodipodi'): str(rx),
inkex.addNS('ry', 'sodipodi'): str(ry),
inkex.addNS('start', 'sodipodi'): str(start_end[0]),
inkex.addNS('end', 'sodipodi'): str(start_end[1]),
inkex.addNS('open', 'sodipodi'): 'true', #all ellipse sectors we will draw are open
inkex.addNS('type', 'sodipodi'): 'arc',
'transform': transform
}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), ell_attribs)
def draw_SVG_arc((rx, ry), x_axis_rot):
arc_attribs = {'style': objStyle,
'rx': str(rx),
'ry': str(ry),
'x-axis-rotation': str(x_axis_rot),
'large-arc': '',
'sweep': '',
'x': '',
'y': ''
}
#name='part'
style = {'stroke': '#000000', 'fill': 'none'}
drw = {'style':simplestyle.formatStyle(style),inkex.addNS('label','inkscape'):name,'d':XYstring}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), drw)
inkex.addNS('', 'svg')
def draw_SVG_text(coordinate, txt, parent):
text = inkex.etree.Element(inkex.addNS('text', 'svg'))
text.text = txt
text.set('x', str(coordinate.x))
text.set('y', str(coordinate.y))
style = {'text-align': 'center', 'text-anchor': 'middle'}
text.set('style', simplestyle.formatStyle(style))
parent.append(text)
def SVG_move_to(x, y):
return "M %d %d" % (x, y)
def SVG_line_to(x, y):
return "L %d %d" % (x, y)
def SVG_arc_to(rx, ry, x, y):
la = sw = 0
return "A %d %d 0 %d %d" % (rx, ry, la, sw, x, y)
def SVG_path(components):
return '<path d="' + ' '.join(components) + '">'
def SVG_curve(parent, segments, style, closed=True):
#pathStr = 'M '+ segments[0]
pathStr = ' '.join(segments)
if closed:
pathStr += ' z'
attributes = {
'style': style,
'd': pathStr}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), attributes)
#draw an SVG line segment between the given (raw) points
def draw_SVG_line(start, end, parent, style = objStyle):
line_attribs = {'style': style,
'd': 'M '+str(start.x)+','+str(start.y)+' L '+str(end.x)+','+str(end.y)}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), line_attribs)
def _makeCurvedSurface(topLeft, w, h, cutSpacing, hCutCount, thickness, parent, invertNotches = False, centralRib = False):
group = inkex.etree.SubElement(parent, 'g')
width = Coordinate(w, 0)
height = Coordinate(0, h)
wCutCount = int(floor(w / cutSpacing))
if wCutCount % 2 == 0:
wCutCount += 1 # make sure we have an odd number of cuts
xCutDist = w / wCutCount
xSpacing = Coordinate(xCutDist, 0)
ySpacing = Coordinate(0, cutSpacing)
cut = height / hCutCount - ySpacing
plateThickness = Coordinate(0, thickness)
notchEdges = [0]
topHCuts = []
bottomHCuts = []
for cutIndex in range(wCutCount):
if (cutIndex % 2 == 1) != invertNotches: # make a notch here
inset = plateThickness
else:
inset = Coordinate(0, 0)
# A-column of cuts
aColStart = topLeft + xSpacing * cutIndex
notchEdges.append((aColStart - topLeft).x)
if cutIndex > 0: # no cuts at x == 0
draw_SVG_line(aColStart, aColStart + cut / 2, group)
for j in range(hCutCount - 1):
pos = aColStart + cut / 2 + ySpacing + (cut + ySpacing) * j
draw_SVG_line(pos, pos + cut, group)
draw_SVG_line(aColStart + height - cut / 2, aColStart + height, group)
# B-column of cuts, offset by half the cut length; these cuts run in the opposite direction
bColStart = topLeft + xSpacing * cutIndex + xSpacing / 2
for j in reversed(range(hCutCount)):
end = bColStart + ySpacing / 2 + (cut + ySpacing) * j
start = end + cut
if centralRib and hCutCount % 2 == 0 and cutIndex % 2 == 1:
holeTopLeft = start + (ySpacing - plateThickness - xSpacing) / 2
if j == hCutCount // 2 - 1:
start -= plateThickness / 2
draw_SVG_line(holeTopLeft + plateThickness + xSpacing, holeTopLeft + plateThickness, group)
draw_SVG_line(holeTopLeft, holeTopLeft + xSpacing, group)
elif j == hCutCount // 2:
end += plateThickness / 2
if j == 0: # first row
end += inset
elif j == hCutCount - 1: # last row
start -= inset
draw_SVG_line(start, end, group)
#horizontal cuts (should be done last)
topHCuts.append((aColStart + inset, aColStart + inset + xSpacing))
bottomHCuts.append((aColStart + height - inset, aColStart + height - inset + xSpacing))
# draw the outline
for c in reversed(bottomHCuts):
draw_SVG_line(c[1], c[0], group)
draw_SVG_line(topLeft + height, topLeft, group)
for c in topHCuts:
draw_SVG_line(c[0], c[1], group)
draw_SVG_line(topLeft + width, topLeft + width + height, group)
notchEdges.append(w)
return notchEdges
def _makeNotchedEllipse(center, ellipse, startAngle, thickness, notches, parent, invertNotches):
startAngle += pi # rotate 180 degrees to put the lid on the topside
c2 = ellipse.notchCoordinate(ellipse.rAngle(startAngle), thickness)
a1 = atan2((ellipse.w/2 + thickness) * c2.y, (ellipse.h/2 + thickness) * c2.x)
for n in range(1, len(notches) - 1):
startA = ellipse.angleFromDist(startAngle, notches[n])
endA = ellipse.angleFromDist(startAngle, notches[n + 1])
c1 = center + ellipse.coordinateFromAngle(endA)
c2 = ellipse.notchCoordinate(endA, thickness)
a2 = atan2((ellipse.w/2 + thickness) * c2.y, (ellipse.h/2 + thickness) * c2.x)
c2 += center
if (n % 2 == 1) != invertNotches:
draw_SVG_ellipse((ellipse.w / 2, ellipse.h / 2), center, parent, (startA, endA))
draw_SVG_line(c1, c2, parent)
else:
draw_SVG_ellipse((ellipse.w / 2 + thickness, ellipse.h / 2 + thickness), center, parent, (a1, a2))
draw_SVG_line(c2, c1, parent)
a1 = a2
class Ellipse():
nrPoints = 1000 #used for piecewise linear circumference calculation (ellipse circumference is tricky to calculate)
# approximate circumfere: c = pi * (3 * (a + b) - sqrt(10 * a * b + 3 * (a ** 2 + b ** 2)))
def __init__(self, w, h):
self.h = h
self.w = w
EllipsePoint = namedtuple('EllipsePoint', 'angle coord cDist')
self.ellData = [EllipsePoint(0, Coordinate(w/2, 0), 0)] # (angle, x, y, cumulative distance from angle = 0)
angle = 0
self.angleStep = 2 * pi / self.nrPoints
#note: the render angle (ra) corresponds to the angle from the ellipse center (ca) according to:
# ca = atan(w/h * tan(ra))
for i in range(self.nrPoints):
angle += self.angleStep
prev = self.ellData[-1]
x, y = w / 2 * cos(angle), h / 2 * sin(angle)
self.ellData.append(EllipsePoint(angle, Coordinate(x, y), prev.cDist + hypot(prev.coord.x - x, prev.coord.y - y)))
self.circumference = self.ellData[-1].cDist
#inkex.debug("circ: %d" % self.circumference)
def rAngle(self, a):
"""Convert an angle measured from ellipse center to the angle used to generate ellData (used for lookups)"""
cf = 0
if a > pi / 2:
cf = pi
if a > 3 * pi / 2:
cf = 2 * pi
return atan(self.w / self.h * tan(a)) + cf
def coordinateFromAngle(self, angle):
"""Coordinate of the point at angle."""
return Coordinate(self.w / 2 * cos(angle), self.h / 2 * sin(angle))
def notchCoordinate(self, angle, notchHeight):
"""Coordinate for a notch at the given angle. The notch is perpendicular to the ellipse."""
angle %= (2 * pi)
#some special cases to avoid divide by zero:
if angle == 0:
return (0, Coordinate(self.w / 2 + notchHeight, 0))
elif angle == pi:
return (pi, Coordinate(-self.w / 2 - notchHeight, 0))
elif angle == pi / 2:
return(pi / 2, Coordinate(0, self.h / 2 + notchHeight))
elif angle == 3 * pi / 2:
return(3 * pi / 2, Coordinate(0, -self.h / 2 - notchHeight))
x = self.w / 2 * cos(angle)
derivative = self.h / self.w * -x / sqrt((self.w / 2) ** 2 - x ** 2)
if angle > pi:
derivative = -derivative
normal = -1 / derivative
nAngle = atan(normal)
if angle > pi / 2 and angle < 3 * pi / 2:
nAngle += pi
nCoordinate = self.coordinateFromAngle(angle) + Coordinate(cos(nAngle), sin(nAngle)) * notchHeight
return nCoordinate
def distFromAngles(self, a1, a2):
"""Distance accross the surface from point at angle a2 to point at angle a2. Measured in CCW sense."""
i1 = int(self.rAngle(a1) / self.angleStep)
p1 = self.rAngle(a1) % self.angleStep
l1 = self.ellData[i1 + 1].cDist - self.ellData[i1].cDist
i2 = int(self.rAngle(a2) / self.angleStep)
p2 = self.rAngle(a2) % self.angleStep
l2 = self.ellData[i2 + 1].cDist - self.ellData[i2].cDist
if a1 <= a2:
len = self.ellData[i2].cDist - self.ellData[i1].cDist + l2 * p2 - l1 * p1
else:
len = self.circumference + self.ellData[i2].cDist - self.ellData[i1].cDist
return len
def angleFromDist(self, startAngle, relDist):
"""Returns the angle that you get when starting at startAngle and moving a distance (dist) in CCW direction"""
si = int(self.rAngle(startAngle) / self.angleStep)
p = self.rAngle(startAngle) % self.angleStep
l = self.ellData[si + 1].cDist - self.ellData[si].cDist
startDist = self.ellData[si].cDist + p * l
absDist = relDist + startDist
if absDist > self.ellData[-1].cDist: # wrap around zero angle
absDist -= self.ellData[-1].cDist
iMin = 0
iMax = self.nrPoints
count = 0
while iMax - iMin > 1: # binary search
count += 1
iHalf = iMin + (iMax - iMin) // 2
if self.ellData[iHalf].cDist < absDist:
iMin = iHalf
else:
iMax = iHalf
stepDist = self.ellData[iMax].cDist - self.ellData[iMin].cDist
return self.ellData[iMin].angle + self.angleStep * (absDist - self.ellData[iMin].cDist)/stepDist
class Coordinate:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Coordinate(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Coordinate(self.x - other.x, self.y - other.y)
def __mul__(self, factor):
return Coordinate(self.x * factor, self.y * factor)
def __div__(self, quotient):
return Coordinate(self.x / quotient, self.y / quotient)
class EllipticalBox(inkex.Effect):
"""
Creates a new layer with the drawings for a parametrically generaded box.
"""
def __init__(self):
inkex.Effect.__init__(self)
self.knownUnits = ['in', 'pt', 'px', 'mm', 'cm', 'm', 'km', 'pc', 'yd', 'ft']
self.OptionParser.add_option('-u', '--unit', action = 'store',
type = 'string', dest = 'unit', default = 'mm',
help = 'Unit, should be one of ')
self.OptionParser.add_option('-t', '--thickness', action = 'store',
type = 'float', dest = 'thickness', default = '3.0',
help = 'Material thickness')
self.OptionParser.add_option('-x', '--width', action = 'store',
type = 'float', dest = 'width', default = '3.0',
help = 'Box width')
self.OptionParser.add_option('-z', '--height', action = 'store',
type = 'float', dest = 'height', default = '10.0',
help = 'Box height')
self.OptionParser.add_option('-y', '--depth', action = 'store',
type = 'float', dest = 'depth', default = '3.0',
help = 'Box depth')
self.OptionParser.add_option('-d', '--cut_dist', action = 'store',
type = 'float', dest = 'cut_dist', default = '1.5',
help = 'Distance between cuts on the wrap around. Note that this value will change slightly to evenly fill up the available space.')
self.OptionParser.add_option('--auto_cut_dist', action = 'store',
type = 'inkbool', dest = 'auto_cut_dist', default = 'false',
help = 'Automatically set the cut distance based on the curvature.')
self.OptionParser.add_option('-c', '--cut_nr', action = 'store',
type = 'int', dest = 'cut_nr', default = '3',
help = 'Number of cuts across the depth of the box.')
self.OptionParser.add_option('-a', '--lid_angle', action = 'store',
type = 'float', dest = 'lid_angle', default = '120',
help = 'Angle that forms the lid (in degrees, measured from centerpoint of the ellipse)')
self.OptionParser.add_option('-b', '--body_ribcount', action = 'store',
type = 'int', dest = 'body_ribcount', default = '0',
help = 'Number of ribs in the body')
self.OptionParser.add_option('-l', '--lid_ribcount', action = 'store',
type = 'int', dest = 'lid_ribcount', default = '0',
help = 'Number of ribs in the lid')
self.OptionParser.add_option('-n', '--invert_lid_notches', action = 'store',
type = 'inkbool', dest = 'invert_lid_notches', default = 'false',
help = 'Invert the notch pattern on the lid (to prevent sideways motion)')
self.OptionParser.add_option('-r', '--central_rib_lid', action = 'store',
type = 'inkbool', dest = 'centralRibLid', default = 'false',
help = 'Create a central rib in the lid')
self.OptionParser.add_option('-R', '--central_rib_body', action = 'store',
type = 'inkbool', dest = 'centralRibBody', default = 'false',
help = 'Create a central rib in the body')
try:
inkex.Effect.unittouu # unitouu has moved since Inkscape 0.91
except AttributeError:
try:
def unittouu(self, unit):
return inkex.unittouu(unit)
except AttributeError:
pass
def effect(self):
"""
Draws as basic elliptical box, based on provided parameters
"""
# input sanity check
error = False
if min(self.options.height, self.options.width, self.options.depth) == 0:
inkex.errormsg('Error: Dimensions must be non zero')
error = True
if self.options.cut_nr < 1:
inkex.errormsg('Error: Number of cuts should be at least 1')
error = True
if (self.options.centralRibLid or self.options.centralRibBody) and self.options.cut_nr % 2 == 1:
inkex.errormsg('Error: Central rib is only valid with an even number of cuts')
error = True
if self.options.unit not in self.knownUnits:
inkex.errormsg('Error: unknown unit. '+ self.options.unit)
error = True
if error:
exit()
# convert units
unit = self.options.unit
H = self.unittouu(str(self.options.height) + unit)
W = self.unittouu(str(self.options.width) + unit)
D = self.unittouu(str(self.options.depth) + unit)
thickness = self.unittouu(str(self.options.thickness) + unit)
cutSpacing = self.unittouu(str(self.options.cut_dist) + unit)
cutNr = self.options.cut_nr
svg = self.document.getroot()
docWidth = self.unittouu(svg.get('width'))
docHeigh = self.unittouu(svg.attrib['height'])
layer = inkex.etree.SubElement(svg, 'g')
layer.set(inkex.addNS('label', 'inkscape'), 'Elliptical Box')
layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
ell = Ellipse(W, H)
#body and lid
lidAngleRad = self.options.lid_angle * 2 * pi / 360
lidStartAngle = pi / 2 - lidAngleRad / 2
lidEndAngle = pi / 2 + lidAngleRad / 2
lidLength = ell.distFromAngles(lidStartAngle, lidEndAngle)
bodyLength = ell.distFromAngles(lidEndAngle, lidStartAngle)
# do not put elements right at the edge of the page
xMargin = 3
yMargin = 3
bodyNotches = _makeCurvedSurface(Coordinate(xMargin, yMargin), bodyLength, D, cutSpacing, cutNr, thickness, layer, False, self.options.centralRibBody)
lidNotches = _makeCurvedSurface(Coordinate(xMargin, D + 2 * yMargin), lidLength, D, cutSpacing, cutNr, thickness, layer, not self.options.invert_lid_notches, self.options.centralRibLid)
a1 = lidEndAngle
# create elliptical sides
sidesGrp = inkex.etree.SubElement(layer, 'g')
elCenter = Coordinate(xMargin + thickness + W / 2, 2 * D + H / 2 + thickness + 3 * yMargin)
# indicate the division between body and lid
if self.options.invert_lid_notches:
draw_SVG_line(elCenter, elCenter + ell.coordinateFromAngle(ell.rAngle(lidStartAngle + pi)), sidesGrp, greenStyle)
draw_SVG_line(elCenter, elCenter + ell.coordinateFromAngle(ell.rAngle(lidEndAngle + pi)), sidesGrp, greenStyle)
else:
angleA = ell.angleFromDist(lidStartAngle, lidNotches[2])
angleB = ell.angleFromDist(lidStartAngle, lidNotches[-2])
draw_SVG_line(elCenter, elCenter + ell.coordinateFromAngle(angleA + pi), sidesGrp, greenStyle)
draw_SVG_line(elCenter, elCenter + ell.coordinateFromAngle(angleB + pi), sidesGrp, greenStyle)
_makeNotchedEllipse(elCenter, ell, lidEndAngle, thickness, bodyNotches, sidesGrp, False)
_makeNotchedEllipse(elCenter, ell, lidStartAngle, thickness, lidNotches, sidesGrp, not self.options.invert_lid_notches)
# ribs
spacer = Coordinate(0, 10)
innerRibCenter = Coordinate(xMargin + thickness + W / 2, 2 * D + 1.5 * (H + 2 *thickness) + 4 * yMargin)
innerRibGrp = inkex.etree.SubElement(layer, 'g')
outerRibCenter = Coordinate(2 * xMargin + 1.5 * (W + 2 * thickness) , 2 * D + 1.5 * (H + 2 * thickness) + 4 * yMargin)
outerRibGrp = inkex.etree.SubElement(layer, 'g')
if self.options.centralRibLid:
_makeNotchedEllipse(innerRibCenter, ell, lidStartAngle, thickness, lidNotches, innerRibGrp, False)
_makeNotchedEllipse(outerRibCenter, ell, lidStartAngle, thickness, lidNotches, outerRibGrp, True)
if self.options.centralRibBody:
_makeNotchedEllipse(innerRibCenter + spacer, ell, lidEndAngle, thickness, bodyNotches, innerRibGrp, False)
_makeNotchedEllipse(outerRibCenter + spacer, ell, lidEndAngle, thickness, bodyNotches, outerRibGrp, True)
if self.options.centralRibLid or self.options.centralRibBody:
draw_SVG_text(elCenter, 'side (duplicate this)', sidesGrp)
draw_SVG_text(innerRibCenter, 'inside rib', innerRibGrp)
draw_SVG_text(outerRibCenter, 'outside rib', outerRibGrp)
# Create effect instance and apply it.
effect = EllipticalBox()
effect.affect()

View File

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension>
<_name>Encoder Disk Generator</_name>
<id>fablabchemnitz.de.encoder_disk_generator</id>
<dependency type="executable" location="extensions">inkex.py</dependency>
<dependency
type="executable" location="extensions">fablabchemnitz_encoder_disk_generator.py</dependency>
<param name="tab" type="notebook">
<page name="rotary_enc" _gui-text="Rotary encoder">
<param name="diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the encoder disk">0.0</param>
<param name="hole_diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the center hole">0.0</param>
<param name="segments" type="int" min="1" max="10000"
_gui-text="Number of segments">1</param>
<param name="outer_encoder_diameter" type="float" min="0.0"
max="1000.0"
_gui-text="Diameter of the outer encoder disk">0.0</param>
<param name="outer_encoder_width" type="float" min="1.0" max="1000.0"
_gui-text="Width of the outer encoder disk">0.0</param>
<param name="inner_encoder_diameter" type="float" min="0.0"
max="1000.0"
_gui-text="Diameter of the inner encoder disk">0.0</param>
<param name="inner_encoder_width" type="float" min="1.0" max="1000.0"
_gui-text="Width of the inner encoder disk">0.0</param>
</page>
<page name="brgc" _gui-text="Binary reflected gray code (BRGC)">
<param name="brgc_diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the encoder disk">0.0</param>
<param name="brgc_hole_diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the center hole">0.0</param>
<param name="bits" type="int" min="1" max="32"
_gui-text="Number of bits/tracks">1</param>
<param name="encoder_diameter" type="float" min="1.0" max="1000.0"
_gui-text="Outer diameter of the last track">0.0</param>
<param name="track_width" type="float" min="1.0" max="1000.0"
_gui-text="Width of one track">0.0</param>
<param name="track_distance" type="float" min="0.0" max="1000.0"
_gui-text="Distance between tracks">0.0</param>
</page>
<page name="stgc" _gui-text="Single-track gray code (STGC)">
<param name="stgc_diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the encoder disk">0.0</param>
<param name="stgc_hole_diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the center hole">0.0</param>
<param name="cutouts" type="int" min="1" max="5"
_gui-text="Number of cutouts">1</param>
<param name="sensors" type="int" min="1" max="36"
_gui-text="Number of sensors">1</param>
<param name="stgc_encoder_diameter" type="float" min="1.0" max="1000.0"
_gui-text="Outer diameter of track">0.0</param>
<param name="stgc_track_width" type="float" min="1.0" max="1000.0"
_gui-text="Width of one track">0.0</param>
</page>
<page name="bitmap_enc" _gui-text="Bitmap encoder">
<param name="bm_diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the encoder disk">30.0</param>
<param name="bm_hole_diameter" type="float" min="0.0" max="1000.0"
_gui-text="Diameter of the center hole">5.0</param>
<param name="bm_bits" type="string"
_gui-text="Bits for segments">010011110111000010001101</param>
<param name="bm_outer_encoder_diameter" type="float" min="0.0"
max="1000.0"
_gui-text="Diameter of the outer encoder disk">25.0</param>
<param name="bm_outer_encoder_width" type="float" min="1.0" max="1000.0"
_gui-text="Width of the outer encoder disk">10.0</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Shape/Pattern from Generator"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions"
interpreter="python">fablabchemnitz_encoder_disk_generator.py</command>
</script>
</inkscape-extension>

View File

@ -1,487 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import inkex
import simplestyle
import math
import string
import simpletransform
# Function for calculating a point from the origin when you know the distance
# and the angle
def calculatePoint(angle, distance):
if (angle < 0 or angle > 360):
return None
else:
return [
distance * math.cos(math.radians(angle)),
distance * math.sin(math.radians(angle))]
class EncoderDiskGenerator(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
self.OptionParser.add_option("--tab",
action="store", type="string",
dest="tab", default="rotary_enc",
help="Selected tab")
self.OptionParser.add_option("--diameter",
action="store", type="float",
dest="diameter", default=0.0,
help="Diameter of the encoder disk")
self.OptionParser.add_option("--hole_diameter",
action="store", type="float",
dest="hole_diameter", default=0.0,
help="Diameter of the center hole")
self.OptionParser.add_option("--segments",
action="store", type="int",
dest="segments", default=0,
help="Number of segments")
self.OptionParser.add_option("--outer_encoder_diameter",
action="store", type="float",
dest="outer_encoder_diameter", default=0.0,
help="Diameter of the outer encoder disk")
self.OptionParser.add_option("--outer_encoder_width",
action="store", type="float",
dest="outer_encoder_width", default=0.0,
help="Width of the outer encoder disk")
self.OptionParser.add_option("--inner_encoder_diameter",
action="store", type="float",
dest="inner_encoder_diameter", default=0.0,
help="Diameter of the inner encoder disk")
self.OptionParser.add_option("--inner_encoder_width",
action="store", type="float",
dest="inner_encoder_width", default=0.0,
help="Width of the inner encoder disk")
self.OptionParser.add_option("--bits",
action="store", type="int",
dest="bits", default=1,
help="Number of bits/tracks")
self.OptionParser.add_option("--encoder_diameter",
action="store", type="float",
dest="encoder_diameter", default=0.0,
help="Outer diameter of the last track")
self.OptionParser.add_option("--track_width",
action="store", type="float",
dest="track_width", default=0.0,
help="Width of one track")
self.OptionParser.add_option("--track_distance",
action="store", type="float",
dest="track_distance", default=0.0,
help="Distance between tracks")
self.OptionParser.add_option("--bm_diameter",
action="store", type="float",
dest="bm_diameter", default=0.0,
help="Diameter of the encoder disk")
self.OptionParser.add_option("--bm_hole_diameter",
action="store", type="float",
dest="bm_hole_diameter", default=0.0,
help="Diameter of the center hole")
self.OptionParser.add_option("--bm_bits",
action="store", type="string",
dest="bm_bits", default="",
help="Bits of segments")
self.OptionParser.add_option("--bm_outer_encoder_diameter",
action="store", type="float",
dest="bm_outer_encoder_diameter", default=0.0,
help="Diameter of the outer encoder disk")
self.OptionParser.add_option("--bm_outer_encoder_width",
action="store", type="float",
dest="bm_outer_encoder_width", default=0.0,
help="Width of the outer encoder disk")
self.OptionParser.add_option("--brgc_diameter",
action="store", type="float",
dest="brgc_diameter", default=0.0,
help="Diameter of the encoder disk")
self.OptionParser.add_option("--stgc_diameter",
action="store", type="float",
dest="stgc_diameter", default=0.0,
help="Diameter of the encoder disk")
self.OptionParser.add_option("--brgc_hole_diameter",
action="store", type="float",
dest="brgc_hole_diameter", default=0.0,
help="Diameter of the center hole")
self.OptionParser.add_option("--cutouts",
action="store", type="int",
dest="cutouts", default=1,
help="Number of cutouts")
self.OptionParser.add_option("--sensors",
action="store", type="int",
dest="sensors", default=1,
help="Number of sensors")
self.OptionParser.add_option("--stgc_hole_diameter",
action="store", type="float",
dest="stgc_hole_diameter", default=0.0,
help="Diameter of the center hole")
self.OptionParser.add_option("--stgc_encoder_diameter",
action="store", type="float",
dest="stgc_encoder_diameter", default=0.0,
help="Outer diameter of the last track")
self.OptionParser.add_option("--stgc_track_width",
action="store", type="float",
dest="stgc_track_width", default=0.0,
help="Width of track")
# This function just concatenates the point and the command and returns
# the data string
def parsePathData(self, command, point):
path_data = command + ' %f ' % point[0] + ' %f ' % point[1]
return path_data
# Creates a gray code of size bits (n >= 1) in the format of a list
def createGrayCode(self, bits):
gray_code = [[False], [True]]
if bits == 1:
return gray_code
for i in range(bits - 1):
temp = []
# Reflect values
for j in range(len(gray_code[0]), 0, -1):
for k in range(0, len(gray_code)):
if j == len(gray_code[0]):
temp.append([gray_code[k][-j]])
else:
temp[k].append(gray_code[k][-j])
while temp:
gray_code.append(temp.pop())
# Add False to the "old" values and true to the new ones
for j in range(0, len(gray_code)):
if j < len(gray_code) / 2:
gray_code[j].insert(0, False)
else:
gray_code[j].insert(0, True)
temp = []
return gray_code
# This function returns the segments for a gray encoder
def drawGrayEncoder(self, line_style, bits, encoder_diameter, track_width,
track_distance):
gray_code = self.createGrayCode(bits)
segments = []
segment_size = 0
start_angle_position = 0
index = 0
current_encoder_diameter = encoder_diameter
previous_item = False
position_size = 360.0 / (2 ** bits)
for i in range(len(gray_code[0]) - 1, -1, -1):
for j in gray_code:
if j[i]:
segment_size += 1
if segment_size == 1:
start_angle_position = index
previous_item = True
elif not j[i] and previous_item:
segments.append(
self.drawSegment(line_style,
start_angle_position * position_size,
segment_size * position_size,
current_encoder_diameter,
track_width))
segment_size = 0
previous_item = False
start_angle_position = 0
index += 1
if previous_item:
segments.append(self.drawSegment(line_style,
start_angle_position * position_size,
segment_size * position_size,
current_encoder_diameter, track_width))
segment_size = 0
previous_item = False
start_angle_position = 0
current_encoder_diameter -= (2 * track_distance + 2 * track_width)
index = 0
return segments
# Check if there is too many cutouts compared to number of sensors
def validSTGrayEncoder(self, cutouts, sensors):
if sensors < 6 and cutouts > 1:
pass
elif sensors <= 10 and cutouts > 2:
pass
elif sensors <= 16 and cutouts > 3:
pass
elif sensors <= 23 and cutouts > 4:
pass
elif sensors <= 36 and cutouts > 5:
pass
else:
return True
return False
# This function returns the segments for a single-track gray encoder
def drawSTGrayEncoder(
self, line_style, cutouts, sensors, encoder_diameter, track_width):
segments = []
resolution = 360.0 / (cutouts * 2 * sensors)
current_angle = 0.0
added_angle = ((2 * cutouts + 1) * resolution)
for n in range(cutouts):
current_segment_size = ((n * 2 + 2) * cutouts + 1) * resolution
segments.append(
self.drawSegment(
line_style, current_angle,
current_segment_size,
encoder_diameter, track_width))
current_angle += added_angle + current_segment_size
return segments
def drawLabel(self, group, angle, segment_angle, outer_diameter, labelNum):
outer_radius = outer_diameter / 2
label_angle = angle + (segment_angle / 2)
point = calculatePoint(label_angle, outer_radius)
matrix = simpletransform.parseTransform(
'rotate(' + str(label_angle + 90) + ')')
matrix_str = str(matrix[0][0]) + "," + str(matrix[0][1])
matrix_str += "," + str(matrix[1][0]) + "," + str(matrix[1][1]) + ",0,0"
text = {
'id': 'text' + str(labelNum),
#'sodipodi:linespacing': '0%',
'style': 'font-size: 6px;font-style: normal;font-family: Sans',
#'transform': 'matrix(' + matrix_str + ')',
'x': str(point[0]),
'y': str(point[1]),
#'xml:space': 'preserve'
}
textElement = inkex.etree.SubElement(group, inkex.addNS('text', 'svg'), text)
#tspanElement = inkex.etree.Element(
# textElement, '{%s}%s' % (svg_uri, 'tspan'), tspan)
textElement.text = string.printable[labelNum % len(string.printable)]
self.current_layer.append(textElement)
# This function creates the path for one single segment
def drawSegment(self, line_style, angle, segment_angle, outer_diameter, width):
path = {'style': simplestyle.formatStyle(line_style)}
path['d'] = ''
outer_radius = outer_diameter / 2
# Go to the first point in the segment
path['d'] += self.parsePathData(
'M', calculatePoint(angle, outer_radius - width))
# Go to the second point in the segment
path['d'] += self.parsePathData('L', calculatePoint(angle, outer_radius))
# Go to the third point in the segment, draw an arc
point = calculatePoint(angle + segment_angle, outer_radius)
path['d'] += self.parsePathData('A', [outer_radius, outer_radius]) + \
'0 0 1' + self.parsePathData(' ', point)
# Go to the fourth point in the segment
point = calculatePoint(angle + segment_angle, outer_radius - width)
path['d'] += self.parsePathData('L', point)
# Go to the beginning in the segment, draw an arc
point = calculatePoint(angle, outer_radius - width)
# 'Z' closes the path
path['d'] += (self.parsePathData(
'A',
[outer_radius - width, outer_radius - width]) +
'0 0 0' + self.parsePathData(' ', point) + ' Z')
# Return the path
return path
# This function adds an element to the document
def addElement(self, element_type, group, element_attributes):
inkex.etree.SubElement(
group, inkex.addNS(element_type, 'svg'),
element_attributes)
def drawCircles(self, hole_diameter, diameter):
# Attributes for the center hole, then create it, if diameter is 0, dont
# create it
circle_elements = []
attributes = {
'style': simplestyle.formatStyle({'stroke': 'none', 'fill': 'black'}),
'r': str(hole_diameter / 2)
}
if self.options.hole_diameter > 0:
circle_elements.append(attributes)
# Attributes for the guide hole in the center hole, then create it
attributes = {
'style': simplestyle.formatStyle(
{'stroke': 'white', 'fill': 'white', 'stroke-width': '0.1'}),
'r': '1'
}
circle_elements.append(attributes)
# Attributes for the outer rim, then create it
attributes = {
'style': simplestyle.formatStyle(
{'stroke': 'black', 'stroke-width': '1', 'fill': 'none'}),
'r': str(diameter / 2)
}
if self.options.diameter > 0:
circle_elements.append(attributes)
return circle_elements
def drawCommonCircles(self, group, diameter, hole_diameter):
circle_elements = self.drawCircles(hole_diameter, diameter)
for circle in circle_elements:
self.addElement('circle', group, circle)
def effectBrgc(self, group, line_style, diameter, hole_diameter):
if (((self.options.encoder_diameter / 2) -
(self.options.bits * self.options.track_width +
(self.options.bits - 1) * self.options.track_distance)) <
self.options.brgc_hole_diameter / 2):
inkex.errormsg("Innermost encoder smaller than the center hole!")
else:
segments = self.drawGrayEncoder(
line_style, self.options.bits,
self.options.encoder_diameter,
self.options.track_width,
self.options.track_distance)
for item in segments:
self.addElement('path', group, item)
self.drawCommonCircles(group, diameter, hole_diameter)
def effectStgc(self, group, line_style, diameter, hole_diameter):
if ((self.options.stgc_encoder_diameter / 2) -
self.options.stgc_track_width < self.options.stgc_hole_diameter / 2):
inkex.errormsg("Encoder smaller than the center hole!")
elif not self.validSTGrayEncoder(self.options.cutouts, self.options.sensors):
inkex.errormsg("Too many cutouts compared to number of sensors!")
else:
segments = self.drawSTGrayEncoder(line_style, self.options.cutouts,
self.options.sensors, self.options.stgc_encoder_diameter,
self.options.stgc_track_width)
for item in segments:
self.addElement('path', group, item)
self.drawCommonCircles(group, diameter, hole_diameter)
def effectRotaryEnc(self, group, line_style, diameter, hole_diameter):
# Angle of one single segment
segment_angle = 360.0 / (self.options.segments * 2)
for segment_number in range(0, self.options.segments):
angle = segment_number * (segment_angle * 2)
if (self.options.outer_encoder_width > 0 and
self.options.outer_encoder_diameter > 0 and
self.options.outer_encoder_diameter / 2 >
self.options.outer_encoder_width):
segment = self.drawSegment(line_style, angle,
segment_angle,
self.options.outer_encoder_diameter,
self.options.outer_encoder_width)
self.addElement('path', group, segment)
# If the inner encoder diameter is something else than 0; create it
if (self.options.outer_encoder_width > 0 and
self.options.inner_encoder_diameter > 0 and
self.options.inner_encoder_diameter / 2 >
self.options.inner_encoder_width):
# The inner encoder must be half an encoder segment ahead of the outer one
segment = self.drawSegment(
line_style, angle + (segment_angle / 2), segment_angle,
self.options.inner_encoder_diameter,
self.options.inner_encoder_width)
self.addElement('path', group, segment)
self.drawCommonCircles(group, diameter, hole_diameter)
def effectBitmapEnc(self, group, line_style, diameter, hole_diameter):
bits = self.options.bm_bits
bm_segments = len(bits)
# Angle of one single segment
segment_angle = 360.0 / bm_segments
for segment_number in range(0, bm_segments):
angle = segment_number * segment_angle
if (self.options.bm_outer_encoder_width > 0 and
self.options.bm_outer_encoder_diameter > 0 and
self.options.bm_outer_encoder_diameter >
self.options.bm_outer_encoder_width):
self.drawLabel(group,
angle, segment_angle,
self.options.bm_diameter,
segment_number)
# Drawing only the black segments
if (bits[segment_number] == '1'):
segment = self.drawSegment(
line_style, angle,
segment_angle,
self.options.bm_outer_encoder_diameter,
self.options.bm_outer_encoder_width)
self.addElement('path', group, segment)
self.drawCommonCircles(group, diameter, hole_diameter)
def effect(self):
# Group to put all the elements in, center set in the middle of the view
group = inkex.etree.SubElement(self.current_layer, 'g', {
inkex.addNS('label', 'inkscape'): 'Encoder disk',
'transform': 'translate' + str(self.view_center)
})
# Line style for the encoder segments
line_style = {
'stroke': 'white',
'stroke-width': '0',
'fill': 'black'
}
if self.options.tab == "\"brgc\"":
self.effectBrgc(group, line_style,
self.options.brgc_diameter,
self.options.brgc_hole_diameter)
if self.options.tab == "\"stgc\"":
self.effectStgc(group, line_style,
self.options.stgc_diameter,
self.options.stgc_hole_diameter)
if self.options.tab == "\"rotary_enc\"":
self.effectRotaryEnc(group, line_style,
self.options.diameter,
self.options.hole_diameter)
if self.options.tab == "\"bitmap_enc\"":
self.effectBitmapEnc(group, line_style,
self.options.bm_diameter,
self.options.bm_hole_diameter)
if __name__ == '__main__':
# Run the effect
effect = EncoderDiskGenerator()
effect.affect()

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Fill With Copies</_name>
<id>fablabchemnitz.de.fill_row</id>
<dependency type="executable" location="extensions">inkex.py</dependency>
<dependency type="executable" location="extensions">fablabchemnitz_fill_row.py</dependency>
<param name="help_text" type="description">Fills a row with seleted object. Author: Pawel Mosakowski</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Nesting/Cut Optimization"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">fablabchemnitz_fill_row.py</command>
</script>
</inkscape-extension>

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python2
import sys
sys.path.append('/usr/share/inkscape/extensions')
from inkex import Effect as InkscapeEffect
from inkex import etree, addNS
from copy import deepcopy
from simpletransform import computeBBox, applyTransformToNode
from simplepath import parsePath, translatePath, formatPath
class FillRow(InkscapeEffect):
def __init__(self):
InkscapeEffect.__init__(self)
self.filename = sys.argv[-1]
def effect(self):
if len(self.selected) > 0:
# bboxes = self.calculate_bboxes(self.selected)
self.total_height = 0
for id, node in self.selected.items():
self.fill_row(node)
def fill_row(self, node):
max_line_width = self.unittouu('450mm')
x_gap = y_gap = self.unittouu('10mm')
x_start = self.unittouu('3mm')
y_start = self.unittouu('1600mm') - self.unittouu('3mm')
total_width = 0
total_height = self.total_height
group = etree.SubElement(self.current_layer, addNS('g','svg'))
bbox = computeBBox([node])
x, _, y, _ = bbox
node_width = x_gap + self.width(bbox)
while total_width + node_width < max_line_width:
node_copy = deepcopy(node)
group.append(node_copy)
x_dest = x_start + total_width
y_dest = y_start - (total_height + self.height(bbox))
# translation logic
if node_copy.tag == addNS('path','svg'):
x_delta = x_dest - x
y_delta = y_dest - y
path = parsePath(node_copy.attrib['d'])
translatePath(path, x_delta, y_delta)
node_copy.attrib['d'] = formatPath(path)
elif node_copy.tag == addNS('g','svg'):
x_delta = x_dest - x
y_delta = y_dest - y
translation_matrix = [[1.0, 0.0, x_delta], [0.0, 1.0, y_delta]]
applyTransformToNode(translation_matrix, node_copy)
else:
node_copy.attrib['x'] = str(x_dest)
node_copy.attrib['y'] = str(y_dest)
total_width += node_width
self.total_height += self.height(computeBBox(group)) + y_gap
def width(self,bbox):
(x1, x2, y1, y2) = bbox
width = x2 - x1
return width
def height(self,bbox):
(x1, x2, y1, y2) = bbox
height = y2 - y1
return height
effect = FillRow()
effect.affect()

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Netting</_name>
<id>fablabchemnitz.de.netting</id>
<dependency type="executable" location="extensions">fablabchemnitz_netting.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
<_param name="title" type="description">This effect net in the a path alternately.</_param>
<param name="s_width" type="float" _gui-text="Stroke Width, px">1.0</param>
<effect>
<object-type>path</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_netting.py</command>
</script>
</inkscape-extension>

View File

@ -1,63 +0,0 @@
#!/usr/bin/env python
'''
netting.py
Sunabe kazumichi 2010/3/4
http://dp48069596.lolipop.jp/
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
this program nets in the line.
'''
import random, math, inkex, simplestyle, cubicsuperpath
class RadiusRandomize(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
self.OptionParser.add_option("-w", "--s_width",
action="store", type="float",
dest="s_width", default=1.0,
help="atrke width")
self.OptionParser.add_option("--title")
def effect(self):
path_strings = []
net_strings= ["M"]
my_path = inkex.etree.Element(inkex.addNS('path','svg'))
s = {'stroke-width': self.options.s_width, 'stroke': '#000000', 'fill': 'none' }
my_path.set('style', simplestyle.formatStyle(s))
for id, node in self.selected.iteritems():
if node.tag == inkex.addNS('path','svg'):
d = node.get('d')
p = cubicsuperpath.parsePath(d)
for subpath in p:
for i, csp in enumerate(subpath):
path_strings.append("%f,%f" % ( csp[1][0], csp[1][1]))
node.set('d',cubicsuperpath.formatPath(p))
while len(path_strings)>0 :
net_strings.append(path_strings.pop(0))
if len(path_strings)>0 :
net_strings.append(path_strings.pop())
my_path.set('d', " ".join(net_strings))
self.current_layer.append( my_path )
e = RadiusRandomize()
e.affect()

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Scatter 2</_name>
<id>fablabchemnitz.de.scatter2</id>
<dependency type="executable" location="extensions">pathmodifier.py</dependency>
<dependency type="executable" location="extensions">fablabchemnitz_scatter.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
<param name="title" type="description">This effect scatters pattern objects randomly around segment points of arbitrary "skeleton" paths. The pattern is the top most object in the selection. Random_off:density=1,offset=0</param>
<param name="random" type="boolean" _gui-text="Random : off : density=1 offset=0">true</param>
<param name="density" type="float" _gui-text="Density : random only" min="1" max="100">6</param>
<param name="offset" type="float" _gui-text="Offset : random: offset area px" min="0" max="1000">3</param>
<param name="scale" type="float" _gui-text="Scale : random:from value to 100%" min="1" max="100">20.0</param>
<param name="rotation" type="float" _gui-text="Rotation : random: from -30 to 30 dgree" min="-30" max="30">20.0</param>
<effect>
<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_scatter.py</command>
</script>
</inkscape-extension>

View File

@ -1,140 +0,0 @@
#!/usr/bin/env python
'''
scatter.py
Sunabe kazumichi 2009/9/29
http://dp48069596.lolipop.jp/
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
attention:when rotation degree become large, patterns are deformed largely.
therefore,restricted the rotation in range -30<deg<30.
'''
import inkex, cubicsuperpath, bezmisc
import pathmodifier,simpletransform
import copy, math, re, random
def translate(pathcomp,dx,dy):
for ctl in pathcomp:
for pt in ctl:
pt[0]+=dx
pt[1]+=dy
def scale(pathcomp,scale,org):
for ctl in pathcomp:
for pt in ctl:
pt[0]=org[0]+(pt[0]-org[0])*scale
pt[1]=org[1]+(pt[1]-org[1])*scale
def rotate(pathcomp,angle):
for ctl in pathcomp:
for pt in ctl:
pt[0]=pt[0]*math.cos(angle)-pt[1]*math.sin(angle)
pt[1]=pt[0]*math.sin(angle)+pt[1]*math.cos(angle)
class ScatterAlongPath(pathmodifier.Diffeo):
def __init__(self):
pathmodifier.Diffeo.__init__(self)
self.OptionParser.add_option("--title")
self.OptionParser.add_option("-r", "--random",
action="store", type="inkbool",
dest="random", default=True,
help="random pattern on the skeleton")
self.OptionParser.add_option("-d", "--density",
action="store", type="int",
dest="density", default=3)
self.OptionParser.add_option("-o", "--offset",
action="store", type="float",
dest="offset", default=0.0, help="offset")
self.OptionParser.add_option("-s", "--scale",
action="store", type="float",
dest="scale", default=100, help="scale")
self.OptionParser.add_option("-p", "--rotation",
action="store", type="float",
dest="rotation", default=0,
help="rotation pattern on the skeleton")
def prepareSelectionList(self):
idList=self.options.ids
idList=pathmodifier.zSort(self.document.getroot(),idList)
id = idList[-1]
self.patterns={id:self.selected[id]}
self.patterns=self.duplicateNodes(self.patterns)
self.expandGroupsUnlinkClones(self.patterns, True, True)
self.objectsToPaths(self.patterns)
del self.selected[id]
self.skeletons=self.selected
self.expandGroupsUnlinkClones(self.skeletons, True, False)
self.objectsToPaths(self.skeletons)
def effect(self):
if len(self.options.ids)<2:
inkex.debug("This extension requires that you select two paths.")
return
self.prepareSelectionList()
bbox=simpletransform.computeBBox(self.patterns.values())
for id, node in self.patterns.iteritems():
if node.tag == inkex.addNS('path','svg') or node.tag=='path':
d = node.get('d')
p0 = cubicsuperpath.parsePath(d)
newp=[]
for skelnode in self.skeletons.itervalues():
self.curSekeleton=cubicsuperpath.parsePath(skelnode.get('d'))
den=1
while den <= self.options.density:
for comp in self.curSekeleton:
p=copy.deepcopy(p0)
if self.options.random:
xoffset=-bbox[0]-(bbox[1]-bbox[0])/2+random.uniform(-self.options.offset, self.options.offset)
yoffset=-(bbox[2]+bbox[3])/2-random.uniform(-self.options.offset, self.options.offset)
else:
self.options.density=1
self.options.offset=0
xoffset=-bbox[0]-(bbox[1]-bbox[0])/2+self.options.offset
yoffset=-(bbox[2]+bbox[3])/2-self.options.offset
NbCopies=len(comp)
new=[]
for sub in p:
for i in range(0,NbCopies,1):
new.append(copy.deepcopy(sub))
p=new
for sub in p:
translate(sub,xoffset,yoffset)
for sub in p:
if self.options.random:
scale(sub,random.uniform(self.options.scale, 100)/100,(0,0))
else:
scale(sub,self.options.scale/100,(0,0))
for sub in p:
if self.options.random:
rotate(sub,random.uniform(-self.options.rotation*math.pi/180, self.options.rotation*math.pi/180))
else:
rotate(sub,self.options.rotation*math.pi/180)
for i,sub in enumerate(p):
try:
translate(sub,comp[i][1][0],comp[i][1][1])
except:
pass
newp+=p
den+=1
node.set('d', cubicsuperpath.formatPath(newp))
e = ScatterAlongPath()
e.affect()

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Parametric Shelves</_name>
<id>fablabchemnitz.de.shelves</id>
<dependency type="executable" location="extensions">inkex.py</dependency>
<dependency type="executable" location="extensions">fablabchemnitz_shelves.py</dependency>
<param name="unit" type="enum" _gui-text="Unit">
<_item value="mm">mm</_item>
<_item value="cm">cm</_item>
<_item value="m">m</_item>
<_item value="km">km</_item>
<_item value="in">in</_item>
<_item value="ft">ft</_item>
<_item value="yd">yd</_item>
<_item value="pt">pt</_item>
<_item value="px">px</_item>
<_item value="pc">pc</_item>
</param>
<param name="thickness" type="float" min="1.0" max="100.0" _gui-text="Material thickness">1.2</param>
<param name="tool_diameter" type="float" min="0.0" _gui-text="Tool diameter (mind the units!)">0.3</param>
<param name="tolerance" type="float" min="-10000.0" max="10000.0" _gui-text="Tolerance">0.05</param>
<param name="height" type="float" min="0.0" max="10000.0" _gui-text="Height">100</param>
<param name="width" type="float" min="0.0" max="10000.0" _gui-text="Width">100</param>
<param name="depth" type="float" min="0.0" max="10000.0" _gui-text="Depth">40</param>
<param name="shelves" type="string" min="0.0" max="10000.0" _gui-text="Shelve heigths (semicolon separated list)">10; 20; 35</param>
<param name="groove_depth" type="float" min="0.0" max="10000.0" _gui-text="Groove depth">0.6</param>
<param name="tab_size" type="float" min="0.0" max="10000.0" _gui-text="Tab size (approximate; will be resized to evenly fit the side)">10</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Finger-jointed/Tabbed Boxes" />
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">fablabchemnitz_shelves.py</command>
</script>
</inkscape-extension>

View File

@ -1,226 +0,0 @@
#!/usr/bin/env python
#import Inkscape_helper.inkscape_helper as helper
import fablabchemnitz_shelves_helper as doc
#import simplestyle
#Note: keep in mind that SVG coordinates start in the top-left corner i.e. with an inverted y-axis
class Shelves(doc.Effect):
"""
Creates a new layer with the drawings for a parametrically generaded box.
"""
def __init__(self):
doc.Effect.__init__(self)
self.OptionParser.add_option('--unit', action = 'store',
type = 'string', dest = 'unit', default = 'cm',
help = 'Unit, should be one of ')
self.OptionParser.add_option('--tool_diameter', action = 'store',
type = 'float', dest = 'tool_diameter', default = '0.3',
help = 'Tool diameter')
self.OptionParser.add_option('--tolerance', action = 'store',
type = 'float', dest = 'tolerance', default = '0.05',
help = '')
self.OptionParser.add_option('--thickness', action = 'store',
type = 'float', dest = 'thickness', default = '1.2',
help = 'Material thickness')
self.OptionParser.add_option('--width', action = 'store',
type = 'float', dest = 'width', default = '3.0',
help = 'Box width')
self.OptionParser.add_option('--height', action = 'store',
type = 'float', dest = 'height', default = '10.0',
help = 'Box height')
self.OptionParser.add_option('--depth', action = 'store',
type = 'float', dest = 'depth', default = '3.0',
help = 'Box depth')
self.OptionParser.add_option('--shelves', action = 'store',
type = 'string', dest = 'shelve_list', default = '',
help = 'semicolon separated list of shelve heigths')
self.OptionParser.add_option('--groove_depth', action = 'store',
type = 'float', dest = 'groove_depth', default = '0.5',
help = 'Groove depth')
self.OptionParser.add_option('--tab_size', action = 'store',
type = 'float', dest = 'tab_size', default = '10',
help = 'Approximate tab width (tabs will be evenly spaced along the length of the edge)')
def effect(self):
"""
Draws a shelved cupboard, based on provided parameters
"""
# input sanity check and unit conversion
error = False
if self.options.unit not in self.knownUnits:
doc.errormsg('Error: unknown unit. '+ self.options.unit)
error = True
unit = self.options.unit
if min(self.options.height, self.options.width, self.options.depth) == 0:
doc.errormsg('Error: Dimensions must be non zero')
error = True
shelves = []
for s in self.options.shelve_list.split(';'):
try:
shelves.append(self.unittouu(str(s).strip() + unit))
except ValueError:
doc.errormsg('Error: nonnumeric value in shelves (' + s + ')')
error = True
if error:
exit()
height = self.unittouu(str(self.options.height) + unit)
width = self.unittouu(str(self.options.width) + unit)
depth = self.unittouu(str(self.options.depth) + unit)
thickness = self.unittouu(str(self.options.thickness) + unit)
groove_depth = self.unittouu(str(self.options.groove_depth) + unit)
tab_size = self.unittouu(str(self.options.tab_size) + unit)
tolerance = self.unittouu(str(self.options.tolerance) + unit)
tool_diameter = self.unittouu(str(self.options.tool_diameter) + unit)
doc_root = self.document.getroot()
docWidth = self.unittouu(doc_root.get('width'))
docHeigh = self.unittouu(doc_root.attrib['height'])
layer = doc.layer(doc_root, 'Shelves')
def H(x):
return doc.Coordinate(x, 0)
def V(x):
return doc.Coordinate(0, x)
def tab_count(dist, desired_tab_size):
F = int(dist // desired_tab_size)
if F / 2 % 2 == 0: # make sure we have an odd number of tabs
n = F // 2
else:
n = (F - 1) // 2
return 2 * n + 1
# create groups for the different parts
g_l_side = doc.group(layer)
g_r_side = doc.group(layer)
g_top = doc.group(layer)
g_bottom = doc.group(layer)
g_back = doc.group(layer)
g_divider = doc.group(layer)
h_spacing = H(10 + thickness)
v_spacing = V(10 + thickness)
v_tab_count = tab_count(height, tab_size)
v_tab = V(height / v_tab_count)
h_tab_count = tab_count(width, tab_size)
h_tab = H(width / h_tab_count)
d_tab_count = tab_count(depth, tab_size)
d_tab_size = depth / d_tab_count
h_tab_height = V(thickness)
top_origin = h_spacing * 2 + v_spacing + H(depth)
left_side_origin = h_spacing + v_spacing * 2 + V(depth)
back_origin = left_side_origin + h_spacing + H(depth)
right_side_origin = back_origin + h_spacing + H(width)
bottom_origin = back_origin + v_spacing + V(height)
def draw_tabbed_edge(parent, edge_start, tab, inset, count, invert = False):
start = edge_start + (inset if invert else doc.Coordinate(0, 0))
for i in range(count):
if (i % 2 == 0) != invert:
t_offset = inset
else:
t_offset = doc.Coordinate(0, 0)
end = start + tab
#inkex.debug(str((i, start, end, t_offset)))
doc.draw_line(parent, start, end)
if i < count - 1: # skip last one
start = edge_start + t_offset + tab * (i + 1)
doc.draw_line(parent, end, start)
# top
doc.draw_line(g_top, top_origin, top_origin + H(width))
draw_tabbed_edge(g_top, top_origin, V(d_tab_size), H(thickness), d_tab_count, False)
draw_tabbed_edge(g_top, top_origin + H(width) , V(d_tab_size), H(-thickness), d_tab_count, False)
draw_tabbed_edge(g_top, top_origin + V(depth), h_tab, V(-thickness), h_tab_count, True)
# groove
groove_style = doc.groove_style
center_v_groove_l = (width - thickness) / 2 - tolerance
groove_l_side = top_origin + H(center_v_groove_l)
groove_r_side = groove_l_side + H(thickness + tolerance * 2)
doc.draw_line(g_top, groove_l_side, groove_l_side + V(depth), groove_style)
doc.draw_line(g_top, groove_r_side, groove_r_side + V(depth), groove_style)
# left
doc.draw_line(g_l_side, left_side_origin, left_side_origin + V(height))
draw_tabbed_edge(g_l_side, left_side_origin + H(depth), v_tab, H(-thickness), v_tab_count, True)
draw_tabbed_edge(g_l_side, left_side_origin, H(d_tab_size), V(thickness), d_tab_count, True)
draw_tabbed_edge(g_l_side, left_side_origin + V(height), H(d_tab_size), V(-thickness), d_tab_count, True)
# back
draw_tabbed_edge(g_back, back_origin, v_tab, H(thickness), v_tab_count, False)
draw_tabbed_edge(g_back, back_origin + H(width), v_tab, H(-thickness), v_tab_count, False)
draw_tabbed_edge(g_back, back_origin, h_tab, V(thickness), h_tab_count, False)
draw_tabbed_edge(g_back, back_origin + V(height), h_tab, V(-thickness), h_tab_count, False)
# groove
groove_l_side = back_origin + H(center_v_groove_l)
groove_r_side = groove_l_side + H(thickness + tolerance * 2)
doc.draw_line(g_back, groove_l_side, groove_l_side + V(height), groove_style)
doc.draw_line(g_back, groove_r_side, groove_r_side + V(height), groove_style)
# right
doc.draw_line(g_r_side, right_side_origin + H(depth), right_side_origin + H(depth) + V(height))
draw_tabbed_edge(g_r_side, right_side_origin, v_tab, H(thickness), v_tab_count, True)
draw_tabbed_edge(g_r_side, right_side_origin, H(d_tab_size), V(thickness), d_tab_count, True)
draw_tabbed_edge(g_r_side, right_side_origin + V(height), H(d_tab_size), V(-thickness), d_tab_count, True)
# bottom
doc.draw_line(g_bottom, bottom_origin + V(depth), bottom_origin + V(depth) + H(width))
draw_tabbed_edge(g_bottom, bottom_origin, V(d_tab_size), H(thickness), d_tab_count, False)
draw_tabbed_edge(g_bottom, bottom_origin + H(width) , V(d_tab_size), H(-thickness), d_tab_count, False)
draw_tabbed_edge(g_bottom, bottom_origin, h_tab, V(thickness), h_tab_count, True)
# groove
groove_l_side = bottom_origin + H(center_v_groove_l)
groove_r_side = groove_l_side + H(thickness + tolerance * 2)
doc.draw_line(g_bottom, groove_l_side, groove_l_side + V(depth), groove_style)
doc.draw_line(g_bottom, groove_r_side, groove_r_side + V(depth), groove_style)
#shelves
prev_top = 0
gr_short = thickness - groove_depth + tool_diameter / 2 # avoid that the grooves are visible from the outside
for s in shelves:
s_top = prev_top + thickness + s - tolerance
s_bottom = s_top + thickness + tolerance * 2
doc.draw_line(g_l_side, left_side_origin + V(s_top), left_side_origin + V(s_top) + H(depth - gr_short), groove_style)
doc.draw_line(g_l_side, left_side_origin + V(s_bottom), left_side_origin + V(s_bottom) + H(depth - gr_short), groove_style)
doc.draw_line(g_r_side, right_side_origin + V(s_top) + H(gr_short), right_side_origin + V(s_top) + H(depth), groove_style)
doc.draw_line(g_r_side, right_side_origin + V(s_bottom) + H(gr_short), right_side_origin + V(s_bottom) + H(depth), groove_style)
doc.draw_line(g_back, back_origin + V(s_top) + H(gr_short), back_origin + V(s_top) + H(width - gr_short), groove_style)
doc.draw_line(g_back, back_origin + V(s_bottom) + H(gr_short), back_origin + V(s_bottom) + H(width - gr_short), groove_style)
prev_top = s_top
# Create effect instance and apply it.
effect = Shelves()
effect.affect()

View File

@ -1,479 +0,0 @@
#!/usr/bin/env python
from __future__ import division
import inkex
import simplestyle
from math import *
from collections import namedtuple
#Note: keep in mind that SVG coordinates start in the top-left corner i.e. with an inverted y-axis
errormsg = inkex.errormsg
debug = inkex.debug
default_style = simplestyle.formatStyle(
{'stroke': '#000000',
'stroke-width': '1',
'fill': 'none'
})
groove_style = simplestyle.formatStyle(
{'stroke': '#0000FF',
'stroke-width': '1',
'fill': 'none'
})
mark_style = simplestyle.formatStyle(
{'stroke': '#00FF00',
'stroke-width': '1',
'fill': 'none'
})
def draw_rectangle(parent, w, h, x, y, rx=0, ry=0, style=default_style):
attribs = {
'style': style,
'height': str(h),
'width': str(w),
'x': str(x),
'y': str(y)
}
if rx != 0 and ry != 0:
attribs['rx'] = str(rx)
attribs['ry'] = str(ry)
inkex.etree.SubElement(parent, inkex.addNS('rect', 'svg'), attribs)
def draw_ellipse(parent, rx, ry, center, start_end=(0, 2*pi), style=default_style, transform=''):
ell_attribs = {'style': style,
inkex.addNS('cx', 'sodipodi'): str(center.x),
inkex.addNS('cy', 'sodipodi'): str(center.y),
inkex.addNS('rx', 'sodipodi'): str(rx),
inkex.addNS('ry', 'sodipodi'): str(ry),
inkex.addNS('start', 'sodipodi'): str(start_end[0]),
inkex.addNS('end', 'sodipodi'): str(start_end[1]),
inkex.addNS('open', 'sodipodi'): 'true', #all ellipse sectors we will draw are open
inkex.addNS('type', 'sodipodi'): 'arc',
'transform': transform
}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), ell_attribs)
def draw_arc(parent, rx, ry, x_axis_rot, style=default_style):
arc_attribs = {'style': style,
'rx': str(rx),
'ry': str(ry),
'x-axis-rotation': str(x_axis_rot),
'large-arc': '',
'sweep': '',
'x': '',
'y': ''
}
#name='part'
style = {'stroke': '#000000', 'fill': 'none'}
drw = {'style':simplestyle.formatStyle(style),inkex.addNS('label','inkscape'):name,'d':XYstring}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), drw)
inkex.addNS('', 'svg')
def draw_text(parent, coordinate, txt, style=default_style):
text = inkex.etree.Element(inkex.addNS('text', 'svg'))
text.text = txt
text.set('x', str(coordinate.x))
text.set('y', str(coordinate.y))
style = {'text-align': 'center', 'text-anchor': 'middle'}
text.set('style', simplestyle.formatStyle(style))
parent.append(text)
#draw an SVG line segment between the given (raw) points
def draw_line(parent, start, end, style = default_style):
line_attribs = {'style': style,
'd': 'M '+str(start.x)+','+str(start.y)+' L '+str(end.x)+','+str(end.y)}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), line_attribs)
def layer(parent, layer_name):
layer = inkex.etree.SubElement(parent, 'g')
layer.set(inkex.addNS('label', 'inkscape'), layer_name)
layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
return layer
def group(parent):
return inkex.etree.SubElement(parent, 'g')
class IntersectionError(ValueError):
"""Raised when two lines do not intersect."""
def on_segment(pt, start, end):
"""Check if pt is between start and end. The three points are presumed to be collinear."""
pt -= start
end -= start
ex, ey = end.x, end.y
px, py = pt.x, pt.y
px *= cmp(ex, 0)
py *= cmp(ey, 0)
return px >= 0 and px <= abs(ex) and py >= 0 and py <= abs(ey)
def intersection (s1, e1, s2, e2, on_segments = True):
D = (s1.x - e1.x) * (s2.y - e2.y) - (s1.y - e1.y) * (s2.x - e2.x)
if D == 0:
raise IntersectionError("Lines from {s1} to {e1} and {s2} to {e2} are parallel")
N1 = s1.x * e1.y - s1.y * e1.x
N2 = s2.x * e2.y - s2.y * e2.x
I = ((s2 - e2) * N1 - (s1 - e1) * N2) / D
if on_segments and not (on_segment(I, s1, e1) and on_segment(I, s2, e2)):
raise IntersectionError("Intersection {0} is not on line segments [{1} -> {2}] [{3} -> {4}]".format(I, s1, e1, s2, e2))
return I
def inner_product(a, b):
return a.x * b.x + a.y * b.y
class Coordinate:
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
@property
def t(self):
return atan2(self.y, self.x)
#@t.setter
#def t(self, value):
@property
def r(self):
return hypot(self.x, self.y)
#@r.setter
#def r(self, value):
def __repr__(self):
return self.__str__()
def __str__(self):
return "(%f, %f)" % (self.x, self.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return Coordinate(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Coordinate(self.x - other.x, self.y - other.y)
def __mul__(self, factor):
return Coordinate(self.x * factor, self.y * factor)
def __rmul__(self, other):
return self * other
def __div__(self, quotient):
return Coordinate(self.x / quotient, self.y / quotient)
def __truediv__(self, quotient):
return self.__div__(quotient)
class Effect(inkex.Effect):
"""
"""
def __init__(self, options=None):
inkex.Effect.__init__(self)
self.knownUnits = ['in', 'pt', 'px', 'mm', 'cm', 'm', 'km', 'pc', 'yd', 'ft']
if options != None:
for opt in options:
if len(opt) == 2:
self.OptionParser.add_option('--' + opt[0], type = opt[1], dest = opt[0])
else:
self.OptionParser.add_option('--' + opt[0], type = opt[1], dest = opt[0],default = opt[2], help = opt[3])
try:
inkex.Effect.unittouu # unitouu has moved since Inkscape 0.91
except AttributeError:
try:
def unittouu(self, unit):
return inkex.unittouu(unit)
except AttributeError:
pass
def effect(self):
"""
"""
pass
def _format_1st(command, is_absolute):
return command.upper() if is_absolute else command.lower()
class Path:
def __init__(self):
self.nodes = []
def move_to(self, coord, absolute=False):
self.nodes.append("{0} {1} {2}".format(_format_1st('m', absolute), coord.x, coord.y))
def line_to(self, coord, absolute=False):
self.nodes.append("{0} {1} {2}".format(_format_1st('l', absolute), coord.x, coord.y))
def h_line_to(self, dist, absolute=False):
self.nodes.append("{0} {1}".format(_format_1st('h', absolute), dist))
def v_line_to(self, dist, absolute=False):
self.nodes.append("{0} {1}".format(_format_1st('v', absolute), dist))
def arc_to(self, rx, ry, x, y, rotation=0, pos_sweep=True, large_arc=False, absolute=False):
self.nodes.append("{0} {1} {2} {3} {4} {5} {6} {7}".format(_format_1st('a', absolute), rx, ry, rotation, 1 if large_arc else 0, 1 if pos_sweep else 0, x, y))
def close(self):
self.nodes.append('z')
def path(self, parent, style):
attribs = {'style': style,
'd': ' '.join(self.nodes)}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), attribs)
def curve(parent, segments, style, closed=True):
#pathStr = 'M '+ segments[0]
pathStr = ' '.join(segments)
if closed:
pathStr += ' z'
attributes = {
'style': style,
'd': pathStr}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), attributes)
def remove_last(self):
self.nodes.pop()
PathPoint = namedtuple('PathPoint', 't coord tangent curvature c_dist')
class PathSegment():
def __init__(self):
raise NotImplementedError
@property
def lenth(self):
raise NotImplementedError
def subdivide(self, part_length):
raise NotImplementedError
# also need:
# find a way do do curvature dependent spacing
# - based on deviation from a standard radius?
# - or ratio between thickness and curvature?
#def point_at_distance(d):
# pass
class Line(PathSegment):
def __init__(self, start, end):
self.start = start
self.end = end
@property
def length(self):
return (self.end - self.start).r
def subdivide(self, part_length, start_offset=0): # note: start_offset should be smaller than part_length
nr_parts = int((self.length - start_offset) // part_length)
k_o = start_offset / self.length
k2t = lambda k : k_o + k * part_length / self.length
pp = lambda t : PathPoint(t, self.start + t * (self.end - self.start), self.end - self.start, 0, t * self.length)
points = [pp(k2t(k)) for k in range(nr_parts + 1)]
return(points, self.length - points[-1].c_dist)
class BezierCurve(PathSegment):
nr_points = 10
def __init__(self, P): # number of points is limited to 3 or 4
if len(P) == 3: # quadratic
self.B = lambda t : (1 - t)**2 * P[0] + 2 * (1 - t) * t * P[1] + t**2 * P[2]
Bd = lambda t : 2 * (1 - t) * (P[1] - P[0]) + 2 * t * (P[2] - P[1])
Bdd = lambda t : 2 * (P[2] - 2 * P[1] + P[0])
elif len(P) == 4: #cubic
self.B = lambda t : (1 - t)**3 * P[0] + 3 * (1 - t)**2 * t * P[1] + 3 * (1 - t) * t**2 * P[2] + t**3 * P[3]
Bd = lambda t : 3 * (1 - t)**2 * (P[1] - P[0]) + 6 * (1 - t) * t * (P[2] - P[1]) + 3 * t**2 * (P[3] - P[2])
Bdd = lambda t : 6 * (1 - t) * (P[2] - 2 * P[1] + P[0]) + 6 * t * (P[3] - 2 * P[2] + P[1])
self.tangent = lambda t : Bd(t)
self.curvature = lambda t : (Bd(t).x * Bdd(t).y - Bd(t).y * Bdd(t).x) / hypot(Bd(t).x, Bd(t).y)**3
self.distances = [0] # cumulative distances for each 't'
prev_pt = self.B(0)
for i in range(self.nr_points):
t = (i + 1) / self.nr_points
pt = self.B(t)
self.distances.append(self.distances[-1] + hypot(prev_pt.x - pt.x, prev_pt.y - pt.y))
prev_pt = pt
self.length = self.distances[-1]
@classmethod
def quadratic(cls, start, c, end):
bezier = cls()
@classmethod
def cubic(cls, start, c1, c2, end):
bezier = cls()
def __make_eq__(self):
pass
@property
def length(self):
return self.length
def subdivide(self, part_length, start_offset=0):
nr_parts = int((self.length - start_offset) / part_length + 10E-10)
print "NR PARTS:", nr_parts, self.length, start_offset, part_length, int(self.length / part_length), self.length - 2 * part_length
k_o = start_offset / self.length
k2t = lambda k : k_o + k * part_length / self.length
points = [self.pathpoint_at_t(k2t(k)) for k in range(nr_parts + 1)]
return(points, self.length - points[-1].c_dist)
def pathpoint_at_t(self, t):
"""pathpoint on the curve from t=0 to point at t."""
step = 1 / self.nr_points
pt_idx = int(t / step)
#print "index", pt_idx, self.distances[pt_idx]
length = self.distances[pt_idx]
ip_fact = (t - pt_idx * step) / step
if ip_fact > 0 and t < 1: # not a perfect match, need to interpolate
length += ip_fact * (self.distances[pt_idx + 1] - self.distances[pt_idx])
return PathPoint(t, self.B(t), self.tangent(t), self.curvature(t), length)
def t_at_length(self, length):
"""interpolated t where the curve is at the given length"""
if length == self.length:
return 1
i_small = 0
i_big = self.nr_points + 1
while i_big - i_small > 1: # binary search
i_half = i_small + (i_big - i_small) // 2
if self.distances[i_half] <= length:
i_small = i_half
else:
i_big = i_half
small_dist = self.distances[i_small]
return i_small / self.nr_points + (length - small_dist) * (self.distances[i_big] - small_dist) # interpolated length
class Ellipse():
nrPoints = 1000 #used for piecewise linear circumference calculation (ellipse circumference is tricky to calculate)
# approximate circumfere: c = pi * (3 * (a + b) - sqrt(10 * a * b + 3 * (a ** 2 + b ** 2)))
def __init__(self, w, h):
self.h = h
self.w = w
EllipsePoint = namedtuple('EllipsePoint', 'angle coord cDist')
self.ellData = [EllipsePoint(0, Coordinate(w/2, 0), 0)] # (angle, x, y, cumulative distance from angle = 0)
angle = 0
self.angleStep = 2 * pi / self.nrPoints
#note: the render angle (ra) corresponds to the angle from the ellipse center (ca) according to:
# ca = atan(w/h * tan(ra))
for i in range(self.nrPoints):
angle += self.angleStep
prev = self.ellData[-1]
x, y = w / 2 * cos(angle), h / 2 * sin(angle)
self.ellData.append(EllipsePoint(angle, Coordinate(x, y), prev.cDist + hypot(prev.coord.x - x, prev.coord.y - y)))
self.circumference = self.ellData[-1].cDist
#inkex.debug("circ: %d" % self.circumference)
def rAngle(self, a):
"""Convert an angle measured from ellipse center to the angle used to generate ellData (used for lookups)"""
cf = 0
if a > pi / 2:
cf = pi
if a > 3 * pi / 2:
cf = 2 * pi
return atan(self.w / self.h * tan(a)) + cf
def coordinateFromAngle(self, angle):
"""Coordinate of the point at angle."""
return Coordinate(self.w / 2 * cos(angle), self.h / 2 * sin(angle))
def notchCoordinate(self, angle, notchHeight):
"""Coordinate for a notch at the given angle. The notch is perpendicular to the ellipse."""
angle %= (2 * pi)
#some special cases to avoid divide by zero:
if angle == 0:
return (0, Coordinate(self.w / 2 + notchHeight, 0))
elif angle == pi:
return (pi, Coordinate(-self.w / 2 - notchHeight, 0))
elif angle == pi / 2:
return(pi / 2, doc.Coordinate(0, self.h / 2 + notchHeight))
elif angle == 3 * pi / 2:
return(3 * pi / 2, Coordinate(0, -self.h / 2 - notchHeight))
x = self.w / 2 * cos(angle)
derivative = self.h / self.w * -x / sqrt((self.w / 2) ** 2 - x ** 2)
if angle > pi:
derivative = -derivative
normal = -1 / derivative
nAngle = atan(normal)
if angle > pi / 2 and angle < 3 * pi / 2:
nAngle += pi
nCoordinate = self.coordinateFromAngle(angle) + Coordinate(cos(nAngle), sin(nAngle)) * notchHeight
return nCoordinate
def distFromAngles(self, a1, a2):
"""Distance accross the surface from point at angle a2 to point at angle a2. Measured in CCW sense."""
i1 = int(self.rAngle(a1) / self.angleStep)
p1 = self.rAngle(a1) % self.angleStep
l1 = self.ellData[i1 + 1].cDist - self.ellData[i1].cDist
i2 = int(self.rAngle(a2) / self.angleStep)
p2 = self.rAngle(a2) % self.angleStep
l2 = self.ellData[i2 + 1].cDist - self.ellData[i2].cDist
if a1 <= a2:
len = self.ellData[i2].cDist - self.ellData[i1].cDist + l2 * p2 - l1 * p1
else:
len = self.circumference + self.ellData[i2].cDist - self.ellData[i1].cDist
return len
def angleFromDist(self, startAngle, relDist):
"""Returns the angle that you get when starting at startAngle and moving a distance (dist) in CCW direction"""
si = int(self.rAngle(startAngle) / self.angleStep)
p = self.rAngle(startAngle) % self.angleStep
l = self.ellData[si + 1].cDist - self.ellData[si].cDist
startDist = self.ellData[si].cDist + p * l
absDist = relDist + startDist
if absDist > self.ellData[-1].cDist: # wrap around zero angle
absDist -= self.ellData[-1].cDist
iMin = 0
iMax = self.nrPoints
count = 0
while iMax - iMin > 1: # binary search
count += 1
iHalf = iMin + (iMax - iMin) // 2
if self.ellData[iHalf].cDist < absDist:
iMin = iHalf
else:
iMax = iHalf
stepDist = self.ellData[iMax].cDist - self.ellData[iMin].cDist
return self.ellData[iMin].angle + self.angleStep * (absDist - self.ellData[iMin].cDist)/stepDist

View File

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Sheriff Star</_name>
<id>fablabchemnitz.de.sheriff-star</id>
<dependency type="executable" location="extensions">fablabchemnitz_sheriff_star.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
<param name="tab" type="notebook">
<page name="settings" _gui-text="Settings">
<param name="points" type="int" min="5" max="10" _gui-text="Num Points">6</param>
<param name="star-tip-ratio" type="int" min="1" max="100" _gui-text="Star Tip Circle Percentage">10</param>
<param name="inner-ratio" type="int" min="1" max="100" _gui-text="Inner Circle Percentage">58</param>
<param name="show-inner-circle" type="boolean" _gui-text="Show Inner Circle">false</param>
</page>
<page name="help" _gui-text="Help">
<_param name="help_info" type="description">
This extension creates a sheriff star in the currently selected circle or ellipse.
The outer vertices (points of the star) are connected through a point on the (usually hidden) inner circle. This inner connection point is located at an angle halfway between the angles of the two outer vertices. The radius of this inner circle is expressed as a percentage of the radius of the selected circle or ellipse.
Small circles are placed at the tip of each point of the star. The radius of these small circles is expressed as a percentage of the selected circle's radius (for ellipses, the x-radius is used).
The components that make up the star are left as separate objects so that additional processing can be performed.
</_param>
</page>
</param>
<effect>
<object-type>path</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Shape/Pattern from existing Object(s)"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">fablabchemnitz_sheriff_star.py</command>
</script>
</inkscape-extension>

View File

@ -1,158 +0,0 @@
#!/usr/bin/env python
"""
Sheriff Star
Create n-pointed sheriff star.
"""
import inkex
from math import *
def addPathCommand(a, cmd):
for x in cmd:
a.append(str(x))
class SheriffStarEffect(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
self.OptionParser.add_option('--tab',
action = 'store', type = 'string', dest = 'tab')
self.OptionParser.add_option('--points',
action='store', type='int', dest='points', default=5,
help='Number of points (or sides)')
self.OptionParser.add_option('--star-tip-ratio',
action='store', type='float', dest='star_tip_ratio', default=10,
help='Star tip circle % (star tip circle radius as a percentage of the outer radius)')
self.OptionParser.add_option('--inner-ratio',
action='store', type='float', dest='inner_ratio', default=58,
help='Inner circle % (inner radius as a percentage of the outer radius)')
self.OptionParser.add_option('--show-inner-circle',
action='store', type='inkbool', dest='show_inner_circle', default=False,
help='Show inner circle')
def effect(self):
layer = self.current_layer;
if len(self.selected) == 0:
inkex.errormsg('Please select a circle or ellipse.')
exit()
numValid = 0
for id, obj in self.selected.iteritems():
cx,cy, rx,ry = 0,0, 0,0
style = ''
isValid = False
if obj.tag == inkex.addNS('circle','svg'):
isValid = True
cx = float(obj.get('cx'))
cy = float(obj.get('cy'))
rx = float(obj.get('r'))
ry = rx
elif obj.tag == inkex.addNS('ellipse', 'svg'):
isValid = True
cx = float(obj.get('cx'))
cy = float(obj.get('cy'))
rx = float(obj.get('rx'))
ry = float(obj.get('ry'))
elif obj.tag == inkex.addNS('path', 'svg'):
if obj.get(inkex.addNS('type', 'sodipodi')) == 'arc':
isValid = True
cx = float(obj.get(inkex.addNS('cx', 'sodipodi')))
cy = float(obj.get(inkex.addNS('cy', 'sodipodi')))
rx = float(obj.get(inkex.addNS('rx', 'sodipodi')))
ry = float(obj.get(inkex.addNS('ry', 'sodipodi')))
if not isValid:
continue;
numValid += 1
style = obj.get('style')
transform = obj.get('transform')
isEllipse = False
if rx != ry:
isEllipse = True
skip = 1
sides = self.options.points
innerRatio = float(self.options.inner_ratio) / 100.0
starTipRatio = float(self.options.star_tip_ratio) / 100.0
showInnerCircle = self.options.show_inner_circle
if showInnerCircle:
if not isEllipse:
cin = inkex.etree.SubElement(layer, inkex.addNS('circle','svg'))
cin.set('r', str(rx * innerRatio))
else:
cin = inkex.etree.SubElement(layer, inkex.addNS('ellipse','svg'))
cin.set('rx', str(rx * innerRatio))
cin.set('ry', str(ry * innerRatio))
cin.set('cx', str(cx))
cin.set('cy', str(cy))
cin.set('style', style)
if transform:
cin.set('transform', transform)
tau = 2*pi
origin = -(tau / 4)
out_pts = []
in_pts = []
for i in range(sides):
# Outer points (on outer circle)
theta = (i * (tau / sides))
px = cx + rx * cos(origin + theta)
py = cy + ry * sin(origin + theta)
out_pts.append([px, py])
# Inner points (on inner circle)
theta = ((i + (skip / 2.0)) * (tau / sides))
px = cx + rx * innerRatio * cos(origin + theta)
py = cy + ry * innerRatio * sin(origin + theta)
in_pts.append([px, py])
# Add circles at each star tip.
for pt in out_pts:
cin = inkex.etree.SubElement(layer, inkex.addNS('circle','svg'))
cin.set('r', str(rx * starTipRatio))
cin.set('cx', str(pt[0]))
cin.set('cy', str(pt[1]))
cin.set('style', style)
if transform:
cin.set('transform', transform)
pts = []
pt_done = {}
for i in range(sides):
if i in pt_done:
continue;
p1 = out_pts[i]
addPathCommand(pts, ['M', p1[0], p1[1]])
pt_done[i] = True
start_index = i
curr = start_index
next = (curr + skip) % sides
while next != start_index:
p = out_pts[next]
pt_done[next] = True
addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]])
addPathCommand(pts, ['L', p[0], p[1]])
curr = next
next = (curr + skip) % sides
addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]])
addPathCommand(pts, ['z'])
# Create star polygon as a single path.
l1 = inkex.etree.SubElement(layer, inkex.addNS('path','svg'))
l1.set('style', style)
if transform:
l1.set('transform', transform)
l1.set('d', ' '.join(pts))
if numValid == 0:
inkex.errormsg('Selection must contain a circle or ellipse.')
if __name__ == '__main__':
effect = SheriffStarEffect()
effect.affect()

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>X-Agram</_name>
<id>fablabchemnitz.de.x-agram</id>
<dependency type="executable" location="extensions">fablabchemnitz_x_agram.py</dependency>
<dependency type="executable" location="extensions">inkex.py</dependency>
<param name="tab" type="notebook">
<page name="settings" _gui-text="Settings">
<param name="points" type="int" min="3" max="15" _gui-text="Num Points (Sides)">5</param>
<param name="skip" type="int" min="1" max="14" _gui-text="Vertex Skip Count">2</param>
<param name="rotate" type="float" min="0" max="360" _gui-text="Rotate (Clockwise)">0</param>
<param name="inner-circle" type="boolean" _gui-text="Connect via Inner Circle">false</param>
<param name="show-inner-circle" type="boolean" _gui-text="Show Inner Circle">true</param>
<param name="inner-ratio" type="int" min="1" max="100" _gui-text="Inner Circle Radius %">50</param>
</page>
<page name="help" _gui-text="Help">
<_param name="help_info" type="description">
This extension inscribes a star polygon (pentagram, hexagram, et al.) into the currently selected circle or ellipse.
If an inner-circle is enabled, then the outer vertices are connected through a point on the inner circle. This inner connection point is located at an angle halfway between the angles of the two outer vertices.
The components that make up the x-agram are left as separate objects so that additional processing can be performed.
</_param>
</page>
</param>
<effect>
<object-type>path</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Shape/Pattern from existing Object(s)" />
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">fablabchemnitz_x_agram.py</command>
</script>
</inkscape-extension>

View File

@ -1,158 +0,0 @@
#!/usr/bin/env python
"""
X-agram
Create n-pointed star polygons (pentagram, hexagram, etc)
"""
import inkex
from math import *
def addPathCommand(a, cmd):
for x in cmd:
a.append(str(x))
class XGramEffect(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
self.OptionParser.add_option('--tab',
action = 'store', type = 'string', dest = 'tab')
self.OptionParser.add_option('--points',
action='store', type='int', dest='points', default=5,
help='Number of points (or sides)')
self.OptionParser.add_option('--skip',
action='store', type='int', dest='skip', default=2,
help='Vertex increment when connecting points')
self.OptionParser.add_option('--rotate',
action='store', type='float', dest='rotate', default=0,
help='Rotation angle (clockwise, in degrees)')
self.OptionParser.add_option('--inner-circle',
action='store', type='inkbool', dest='inner_circle', default=False,
help='Connect points via inner circle')
self.OptionParser.add_option('--show-inner-circle',
action='store', type='inkbool', dest='show_inner_circle', default=True,
help='Show inner circle')
self.OptionParser.add_option('--inner-ratio',
action='store', type='int', dest='inner_ratio', default=50,
help='Inner radius percentage (inner radius as a percentage of the outer radius)')
def effect(self):
layer = self.current_layer;
if len(self.selected) == 0:
inkex.errormsg('Please select a circle or ellipse.')
exit()
numValid = 0
for id, obj in self.selected.iteritems():
cx,cy, rx,ry = 0,0, 0,0
style = ''
isValid = False
if obj.tag == inkex.addNS('circle','svg'):
isValid = True
cx = float(obj.get('cx'))
cy = float(obj.get('cy'))
rx = float(obj.get('r'))
ry = rx
elif obj.tag == inkex.addNS('ellipse', 'svg'):
isValid = True
cx = float(obj.get('cx'))
cy = float(obj.get('cy'))
rx = float(obj.get('rx'))
ry = float(obj.get('ry'))
elif obj.tag == inkex.addNS('path', 'svg'):
if obj.get(inkex.addNS('type', 'sodipodi')) == 'arc':
isValid = True
cx = float(obj.get(inkex.addNS('cx', 'sodipodi')))
cy = float(obj.get(inkex.addNS('cy', 'sodipodi')))
rx = float(obj.get(inkex.addNS('rx', 'sodipodi')))
ry = float(obj.get(inkex.addNS('ry', 'sodipodi')))
if not isValid:
continue;
numValid += 1
style = obj.get('style')
transform = obj.get('transform')
isEllipse = False
if rx != ry:
isEllipse = True
sides = self.options.points
skip = self.options.skip
rotate = self.options.rotate
useInnerCircle = self.options.inner_circle
showInnerCircle = self.options.show_inner_circle
innerRatio = float(self.options.inner_ratio) / 100.0
if useInnerCircle and showInnerCircle:
if not isEllipse:
cin = inkex.etree.SubElement(layer, inkex.addNS('circle','svg'))
cin.set('r', str(rx * innerRatio))
else:
cin = inkex.etree.SubElement(layer, inkex.addNS('ellipse','svg'))
cin.set('rx', str(rx * innerRatio))
cin.set('ry', str(ry * innerRatio))
cin.set('cx', str(cx))
cin.set('cy', str(cy))
cin.set('style', style)
if transform:
cin.set('transform', transform)
tau = 2*pi
origin = -(tau / 4) + (rotate * pi / 180)
out_pts = []
in_pts = []
for i in range(sides):
# Outer points (on outer circle)
theta = (i * (tau / sides))
px = cx + rx * cos(origin + theta)
py = cy + ry * sin(origin + theta)
out_pts.append([px, py])
# Inner points (on inner circle)
theta = ((i + (skip / 2.0)) * (tau / sides))
px = cx + rx * innerRatio * cos(origin + theta)
py = cy + ry * innerRatio * sin(origin + theta)
in_pts.append([px, py])
pts = []
pt_done = {}
for i in range(sides):
if i in pt_done:
continue;
p1 = out_pts[i]
addPathCommand(pts, ['M', p1[0], p1[1]])
pt_done[i] = True
start_index = i
curr = start_index
next = (curr + skip) % sides
while next != start_index:
p = out_pts[next]
pt_done[next] = True
if useInnerCircle:
addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]])
addPathCommand(pts, ['L', p[0], p[1]])
curr = next
next = (curr + skip) % sides
if useInnerCircle:
addPathCommand(pts, ['L', in_pts[curr][0], in_pts[curr][1]])
addPathCommand(pts, ['z'])
# Create star polygon as a single path.
l1 = inkex.etree.SubElement(layer, inkex.addNS('path','svg'))
l1.set('style', style)
if transform:
l1.set('transform', transform)
l1.set('d', ' '.join(pts))
if numValid == 0:
inkex.errormsg('Selection must contain a circle or ellipse.')
if __name__ == '__main__':
effect = XGramEffect()
effect.affect()