Added Affine Spirals
This commit is contained in:
parent
5d6a1367c7
commit
bdf43f912e
26
extensions/fablabchemnitz_affine_spirals.inx
Normal file
26
extensions/fablabchemnitz_affine_spirals.inx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
|
||||||
|
<_name>Affine Spirals</_name>
|
||||||
|
<id>fablabchemnitz.de.affine_torus</id>
|
||||||
|
<param name="active-tab" type="notebook">
|
||||||
|
<page name="title" _gui-text="Settings">
|
||||||
|
<param name="num_lines" type="int" min="1" max="6" _gui-text="Depth">3</param>
|
||||||
|
<param name="num_petals" type="int" min="2" max="8" _gui-text="petals">2</param>
|
||||||
|
<param name="shrink_ratio" type="float" min="0.1" max="0.9" precision="2" _gui-text="shrink factor">0.8</param>
|
||||||
|
<_param name="help" type="description" xml:space="preserve">------------------------------</_param>
|
||||||
|
<!--<param name="mk_full" type="boolean" _gui-text="Full Koch">true</param>
|
||||||
|
<param name="mk_filled" type="boolean" _gui-text="Filled">false</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_affine_spirals.py</command>
|
||||||
|
</script>
|
||||||
|
</inkscape-extension>
|
100
extensions/fablabchemnitz_affine_spirals.py
Normal file
100
extensions/fablabchemnitz_affine_spirals.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import inkex
|
||||||
|
from lxml import etree
|
||||||
|
from math import cos, sin, pi, exp
|
||||||
|
__version__ = '0.1'
|
||||||
|
inkex.localization.localize
|
||||||
|
|
||||||
|
def line(npts=40, x0=0, y0=0, delta=.5, sgn=1):
|
||||||
|
#returns a list of points on a line (y = +/- x + c) starting at x0,y0
|
||||||
|
return [ (x0 + delta*t, y0 + sgn*delta*t) for t in range(npts)]
|
||||||
|
|
||||||
|
def ff(v, ww=.25, ds=.4):
|
||||||
|
#covering map from R^2 ro punctured plane
|
||||||
|
x,y = v
|
||||||
|
r,u = exp(-ds*x), cos(pi*ww*y) + 1J*sin(pi*ww*y)
|
||||||
|
return r*u
|
||||||
|
|
||||||
|
def mk_plugs(pts):
|
||||||
|
#returns a list of complex representing a plug type segment
|
||||||
|
segs = [fit_plug(end_pts) for end_pts in zip(pts,pts[1:]) ]
|
||||||
|
tmp = []
|
||||||
|
for seg in segs:
|
||||||
|
tmp.extend(seg)
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
def fit_plug(ss):
|
||||||
|
a,b = ss
|
||||||
|
rot = complex(b-a)
|
||||||
|
pts = [0,.45,.4 + .15*1J, .6 + .15*1J, .55, 1]
|
||||||
|
return [rot*z + a for z in pts]
|
||||||
|
|
||||||
|
def pts2curve(cplxs):
|
||||||
|
'''makes a polyline path element from a list of complex
|
||||||
|
'''
|
||||||
|
def cplx2pt(z):
|
||||||
|
return (z.real,z.imag)
|
||||||
|
|
||||||
|
scale = 200
|
||||||
|
data = [cplx2pt( scale*z ) for z in cplxs ]
|
||||||
|
pth = [ '%.2f, %.2f '%z for z in data]
|
||||||
|
return 'M '+ ''.join(pth)
|
||||||
|
|
||||||
|
class AffineTorus(inkex.Effect): # choose a better name
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
inkex.Effect.__init__(self)
|
||||||
|
self.arg_parser.add_argument("--num_lines", type=int, default=3)
|
||||||
|
self.arg_parser.add_argument("--num_petals", type=int, default=3)
|
||||||
|
self.arg_parser.add_argument("--shrink_ratio", type=float, default=3)
|
||||||
|
#self.arg_parser.add_argument("--mk_filled", type=inkex.Boolean, default=False)
|
||||||
|
self.arg_parser.add_argument("--mk_full", type=inkex.Boolean, default=False)
|
||||||
|
self.arg_parser.add_argument("--active-tab", default='title')
|
||||||
|
|
||||||
|
def calc_unit_factor(self):
|
||||||
|
unit_factor = self.svg.unittouu(str(1.0) + self.options.units)
|
||||||
|
return unit_factor
|
||||||
|
|
||||||
|
def effect(self):
|
||||||
|
path_stroke = '#DD0000' # take color from tab3
|
||||||
|
path_fill = 'none' # no fill - just a line
|
||||||
|
path_stroke_width = 1. # can also be in form '0.6mm'
|
||||||
|
page_id = self.options.active_tab # sometimes wrong the very first time
|
||||||
|
|
||||||
|
styles = [ {'stroke': path_stroke , 'fill': 'none', 'stroke-width': path_stroke_width},
|
||||||
|
{'stroke': 'none', 'fill': '#FFFF00', 'stroke-width': 0}]
|
||||||
|
|
||||||
|
styles = [str(inkex.Style(x)) for x in styles]
|
||||||
|
|
||||||
|
# This finds center of current view in inkscape
|
||||||
|
t = 'translate(%s,%s)' % (self.svg.namedview.center[0], self.svg.namedview.center[1])
|
||||||
|
|
||||||
|
# Make a nice useful name
|
||||||
|
g_attribs = {inkex.addNS('label','inkscape'): 'koch',
|
||||||
|
inkex.addNS('transform-center-x','inkscape'): str(0),
|
||||||
|
inkex.addNS('transform-center-y','inkscape'): str(0),
|
||||||
|
'transform': t,
|
||||||
|
'style' : styles[1],
|
||||||
|
'info':'N: '+str(self.options.num_lines) }
|
||||||
|
topgroup = etree.SubElement(self.svg.get_current_layer(), 'g', g_attribs)
|
||||||
|
|
||||||
|
NN = 2*self.options.num_lines
|
||||||
|
NP = self.options.num_petals
|
||||||
|
SF = 2*self.options.shrink_ratio
|
||||||
|
|
||||||
|
payload = []
|
||||||
|
for y in range(-NP,NP):
|
||||||
|
mpts = [ff(z,ww=1./NP, ds=SF) for z in line(npts=NN, y0=y)]
|
||||||
|
payload.append(mk_plugs(mpts))
|
||||||
|
mpts = [ff(z,ww=1./NP, ds=SF) for z in line(npts=NN, y0=y,sgn=-1 )]
|
||||||
|
payload.append(mk_plugs(mpts))
|
||||||
|
|
||||||
|
payload = [pts2curve(cc) for cc in payload]
|
||||||
|
payload = ' '.join(payload)
|
||||||
|
|
||||||
|
curve_attribs = { 'style': styles[0], 'd': payload}
|
||||||
|
etree.SubElement(topgroup, inkex.addNS('path','svg'), curve_attribs)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
AffineTorus().run()
|
Reference in New Issue
Block a user