#!/usr/bin/env python3 import inkex from lxml import etree from math import cos, sin, pi, exp __version__ = '0.1' 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 AffineSpirals(inkex.EffectExtension): def add_arguments(self, pars): pars.add_argument("--num_lines", type=int, default=3) pars.add_argument("--num_petals", type=int, default=3) pars.add_argument("--shrink_ratio", type=float, default=3) pars.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__': AffineSpirals().run()