Code

extension printing-marks: the star align test is now useful, using full 4 CMYK colors
[inkscape.git] / share / extensions / param_curves.py
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