1 #!/usr/bin/env python\r
2 '''\r
3 Copyright (C) 2009 Michel Chatelain.\r
4 Copyright (C) 2007 Tavmjong Bah, tavmjong@free.fr\r
5 Copyright (C) 2006 Georg Wiora, xorx@quarkbox.de\r
6 Copyright (C) 2006 Johan Engelen, johan@shouraizou.nl\r
7 Copyright (C) 2005 Aaron Spike, aaron@ekips.org\r
8 \r
9 This program is free software; you can redistribute it and/or modify\r
10 it under the terms of the GNU General Public License as published by\r
11 the Free Software Foundation; either version 2 of the License, or\r
12 (at your option) any later version.\r
13 \r
14 This program is distributed in the hope that it will be useful,\r
15 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
17 GNU General Public License for more details.\r
18 \r
19 You should have received a copy of the GNU General Public License\r
20 along with this program; if not, write to the Free Software\r
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
22 \r
23 Changes:\r
24 * This program is derived by Michel Chatelain from funcplot.py. His changes are in the Public Domain.\r
25 * Michel Chatelain, 17-18 janvier 2009, a partir de funcplot.py\r
26 * 20 janvier 2009 : adaptation a la version 0.46 a partir de la nouvelle version de funcplot.py\r
27 \r
28 '''\r
29 \r
30 import inkex, simplepath, simplestyle\r
31 from math import *\r
32 from random import *\r
33 \r
34 def drawfunction(t_start, t_end, xleft, xright, ybottom, ytop, samples, width, height, left, bottom,\r
35 fx = "cos(3*t)", fy = "sin(5*t)", times2pi = False, isoscale = True, drawaxis = True):\r
36 \r
37 if times2pi == True:\r
38 t_start = 2 * pi * t_start\r
39 t_end = 2 * pi * t_end\r
40 \r
41 # coords and scales based on the source rect\r
42 scalex = width / (xright - xleft)\r
43 xoff = left\r
44 coordx = lambda x: (x - xleft) * scalex + xoff #convert x-value to coordinate\r
45 scaley = height / (ytop - ybottom)\r
46 yoff = bottom\r
47 coordy = lambda y: (ybottom - y) * scaley + yoff #convert y-value to coordinate\r
48 \r
49 # Check for isotropic scaling and use smaller of the two scales, correct ranges\r
50 if isoscale:\r
51 if scaley<scalex:\r
52 # compute zero location\r
53 xzero = coordx(0)\r
54 # set scale\r
55 scalex = scaley\r
56 # correct x-offset\r
57 xleft = (left-xzero)/scalex\r
58 xright = (left+width-xzero)/scalex\r
59 else :\r
60 # compute zero location\r
61 yzero = coordy(0)\r
62 # set scale\r
63 scaley = scalex\r
64 # correct x-offset\r
65 ybottom = (yzero-bottom)/scaley\r
66 ytop = (bottom+height-yzero)/scaley\r
67 \r
68 # functions specified by the user\r
69 if fx != "":\r
70 f1 = eval('lambda t: ' + fx.strip('"'))\r
71 if fy != "":\r
72 f2 = eval('lambda t: ' + fy.strip('"'))\r
73 \r
74 # step is increment of t\r
75 step = (t_end - t_start) / (samples-1)\r
76 third = step / 3.0\r
77 ds = step * 0.001 # Step used in calculating derivatives\r
78 \r
79 a = [] # path array\r
80 # add axis\r
81 if drawaxis :\r
82 # check for visibility of x-axis\r
83 if ybottom<=0 and ytop>=0:\r
84 # xaxis\r
85 a.append(['M ',[left, coordy(0)]])\r
86 a.append([' l ',[width, 0]])\r
87 # check for visibility of y-axis\r
88 if xleft<=0 and xright>=0:\r
89 # xaxis\r
90 a.append([' M ',[coordx(0),bottom]])\r
91 a.append([' l ',[0, -height]])\r
92 \r
93 # initialize functions and derivatives for 0;\r
94 # they are carried over from one iteration to the next, to avoid extra function calculations.\r
95 x0 = f1(t_start)\r
96 y0 = f2(t_start)\r
97 \r
98 # numerical derivatives, using 0.001*step as the small differential\r
99 t1 = t_start + ds # Second point AFTER first point (Good for first point)\r
100 x1 = f1(t1)\r
101 y1 = f2(t1)\r
102 dx0 = (x1 - x0)/ds\r
103 dy0 = (y1 - y0)/ds\r
104 \r
105 # Start curve\r
106 a.append([' M ',[coordx(x0), coordy(y0)]]) # initial moveto\r
107 for i in range(int(samples-1)):\r
108 t1 = (i+1) * step + t_start\r
109 t2 = t1 - ds # Second point BEFORE first point (Good for last point)\r
110 x1 = f1(t1)\r
111 x2 = f1(t2)\r
112 y1 = f2(t1)\r
113 y2 = f2(t2)\r
114 \r
115 # numerical derivatives\r
116 dx1 = (x1 - x2)/ds\r
117 dy1 = (y1 - y2)/ds\r
118 \r
119 # create curve\r
120 a.append([' C ',\r
121 [coordx(x0 + (dx0 * third)), coordy(y0 + (dy0 * third)),\r
122 coordx(x1 - (dx1 * third)), coordy(y1 - (dy1 * third)),\r
123 coordx(x1), coordy(y1)]\r
124 ])\r
125 t0 = t1 # Next segment's start is this segments end\r
126 x0 = x1\r
127 y0 = y1\r
128 dx0 = dx1 # Assume the functions are smooth everywhere, so carry over the derivatives too\r
129 dy0 = dy1\r
130 return a\r
131 \r
132 class ParamCurves(inkex.Effect):\r
133 def __init__(self):\r
134 inkex.Effect.__init__(self)\r
135 self.OptionParser.add_option("--t_start",\r
136 action="store", type="float",\r
137 dest="t_start", default=0.0,\r
138 help="Start t-value")\r
139 self.OptionParser.add_option("--t_end",\r
140 action="store", type="float",\r
141 dest="t_end", default=1.0,\r
142 help="End t-value")\r
143 self.OptionParser.add_option("--times2pi",\r
144 action="store", type="inkbool",\r
145 dest="times2pi", default=True,\r
146 help="Multiply t-range by 2*pi")\r
147 self.OptionParser.add_option("--xleft",\r
148 action="store", type="float",\r
149 dest="xleft", default=-1.0,\r
150 help="x-value of rectangle's left")\r
151 self.OptionParser.add_option("--xright",\r
152 action="store", type="float",\r
153 dest="xright", default=1.0,\r
154 help="x-value of rectangle's right")\r
155 self.OptionParser.add_option("--ybottom",\r
156 action="store", type="float",\r
157 dest="ybottom", default=-1.0,\r
158 help="y-value of rectangle's bottom")\r
159 self.OptionParser.add_option("--ytop",\r
160 action="store", type="float",\r
161 dest="ytop", default=1.0,\r
162 help="y-value of rectangle's top")\r
163 self.OptionParser.add_option("-s", "--samples",\r
164 action="store", type="int",\r
165 dest="samples", default=8,\r
166 help="Samples")\r
167 self.OptionParser.add_option("--fofx",\r
168 action="store", type="string",\r
169 dest="fofx", default="cos(3*t)",\r
170 help="fx(t) for plotting")\r
171 self.OptionParser.add_option("--fofy",\r
172 action="store", type="string",\r
173 dest="fofy", default="sin(5*t)",\r
174 help="fy(t) for plotting")\r
175 self.OptionParser.add_option("--remove",\r
176 action="store", type="inkbool",\r
177 dest="remove", default=True,\r
178 help="If True, source rectangle is removed")\r
179 self.OptionParser.add_option("--isoscale",\r
180 action="store", type="inkbool",\r
181 dest="isoscale", default=True,\r
182 help="If True, isotropic scaling is used")\r
183 self.OptionParser.add_option("--drawaxis",\r
184 action="store", type="inkbool",\r
185 dest="drawaxis", default=True,\r
186 help="If True, axis are drawn")\r
187 self.OptionParser.add_option("--tab",\r
188 action="store", type="string",\r
189 dest="tab", default="sampling",\r
190 help="The selected UI-tab when OK was pressed")\r
191 self.OptionParser.add_option("--paramcurvesuse",\r
192 action="store", type="string",\r
193 dest="paramcurvesuse", default="",\r
194 help="dummy")\r
195 self.OptionParser.add_option("--pythonfunctions",\r
196 action="store", type="string",\r
197 dest="pythonfunctions", default="",\r
198 help="dummy")\r
199 \r
200 def effect(self):\r
201 for id, node in self.selected.iteritems():\r
202 if node.tag == inkex.addNS('rect','svg'):\r
203 # create new path with basic dimensions of selected rectangle\r
204 newpath = inkex.etree.Element(inkex.addNS('path','svg'))\r
205 x = float(node.get('x'))\r
206 y = float(node.get('y'))\r
207 w = float(node.get('width'))\r
208 h = float(node.get('height'))\r
209 \r
210 #copy attributes of rect\r
211 s = node.get('style')\r
212 if s:\r
213 newpath.set('style', s)\r
214 \r
215 t = node.get('transform')\r
216 if t:\r
217 newpath.set('transform', t)\r
218 \r
219 # top and bottom were exchanged\r
220 newpath.set('d', simplepath.formatPath(\r
221 drawfunction(self.options.t_start,\r
222 self.options.t_end,\r
223 self.options.xleft,\r
224 self.options.xright,\r
225 self.options.ybottom,\r
226 self.options.ytop,\r
227 self.options.samples,\r
228 w,h,x,y+h,\r
229 self.options.fofx,\r
230 self.options.fofy,\r
231 self.options.times2pi,\r
232 self.options.isoscale,\r
233 self.options.drawaxis)))\r
234 newpath.set('title', self.options.fofx + " " + self.options.fofy)\r
235 \r
236 #newpath.set('desc', '!func;' + self.options.fofx + ';' + self.options.fofy + ';'\r
237 # + `self.options.t_start` + ';'\r
238 # + `self.options.t_end` + ';'\r
239 # + `self.options.samples`)\r
240 \r
241 # add path into SVG structure\r
242 node.getparent().append(newpath)\r
243 # option wether to remove the rectangle or not.\r
244 if self.options.remove:\r
245 node.getparent().remove(node)\r
246 \r
247 if __name__ == '__main__':\r
248 e = ParamCurves()\r
249 e.affect()\r
250 \r
251 \r
252 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99\r