1 #!/usr/bin/env python \r
2 '''\r
3 Copyright (C) 2006 Johan Engelen, johan@shouraizou.nl\r
4 Copyright (C) 2005 Aaron Spike, aaron@ekips.org\r
5 \r
6 This program is free software; you can redistribute it and/or modify\r
7 it under the terms of the GNU General Public License as published by\r
8 the Free Software Foundation; either version 2 of the License, or\r
9 (at your option) any later version.\r
10 \r
11 This program is distributed in the hope that it will be useful,\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
14 GNU General Public License for more details.\r
15 \r
16 You should have received a copy of the GNU General Public License\r
17 along with this program; if not, write to the Free Software\r
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
19 \r
20 This program is a modified version of wavy.py by Aaron Spike.\r
21 \r
22 '''\r
23 import inkex, simplepath, simplestyle\r
24 from math import *\r
25 from random import *\r
26 \r
27 def drawfunction(xstart, xend, ybottom, ytop, samples, width, height, left, top, \r
28 fx = "sin(x)", fpx = "cos(x)", fponum = True):\r
29 \r
30 # step is the distance between nodes on x\r
31 step = (xend - xstart) / (samples-1)\r
32 third = step / 3.0\r
33 \r
34 # coords and scales based on the source rect\r
35 scalex = width / (xend - xstart)\r
36 xoff = left\r
37 coordx = lambda x: (x - xstart) * scalex + xoff #convert x-value to coordinate\r
38 scaley = height / (ytop - ybottom)\r
39 yoff = top\r
40 coordy = lambda y: (ytop-y) * scaley + yoff #convert y-value to coordinate\r
41 \r
42 # functions specified by the user\r
43 if fx != "":\r
44 f = eval('lambda x: ' + fx)\r
45 if fpx != "":\r
46 fp = eval('lambda x: ' + fpx)\r
47 \r
48 # initialize function and derivative for 0;\r
49 # they are carried over from one iteration to the next, to avoid extra function calculations \r
50 y0 = f(xstart) \r
51 if fponum == True: # numerical derivative, using 0.001*step as the small differential\r
52 d0 = (f(xstart + 0.001*step) - y0)/(0.001*step)\r
53 else: # derivative given by the user\r
54 d0 = fp(xstart)\r
55 \r
56 a = [] # path array \r
57 a.append(['M',[coordx(xstart), coordy(y0)]]) # initial moveto\r
58 \r
59 for i in range(int(samples-1)):\r
60 x = (i+1) * step + xstart\r
61 y1 = f(x)\r
62 if fponum == True: # numerical derivative\r
63 d1 = (y1 - f(x - 0.001*step))/(0.001*step)\r
64 else: # derivative given by the user\r
65 d1 = fp(x)\r
66 # create curve\r
67 a.append(['C',[coordx(x - step + third), coordy(y0 + (d0 * third)), \r
68 coordx(x - third), coordy(y1 - (d1 * third)),\r
69 coordx(x), coordy(y1)]])\r
70 y0 = y1 # next segment's y0 is this segment's y1\r
71 d0 = d1 # we assume the function is smooth everywhere, so carry over the derivative too\r
72 \r
73 return a\r
74 \r
75 class FuncPlot(inkex.Effect):\r
76 def __init__(self):\r
77 inkex.Effect.__init__(self)\r
78 self.OptionParser.add_option("--xstart",\r
79 action="store", type="float", \r
80 dest="xstart", default=0.0,\r
81 help="Start x-value")\r
82 self.OptionParser.add_option("--xend",\r
83 action="store", type="float", \r
84 dest="xend", default=1.0,\r
85 help="End x-value")\r
86 self.OptionParser.add_option("--ybottom",\r
87 action="store", type="float", \r
88 dest="ybottom", default=-1.0,\r
89 help="y-value of rectangle's bottom")\r
90 self.OptionParser.add_option("--ytop",\r
91 action="store", type="float", \r
92 dest="ytop", default=1.0,\r
93 help="y-value of rectangle's top")\r
94 self.OptionParser.add_option("-s", "--samples",\r
95 action="store", type="int", \r
96 dest="samples", default=8,\r
97 help="Samples") \r
98 self.OptionParser.add_option("--fofx",\r
99 action="store", type="string", \r
100 dest="fofx", default="sin(x)",\r
101 help="f(x) for plotting") \r
102 self.OptionParser.add_option("--fponum",\r
103 action="store", type="inkbool", \r
104 dest="fponum", default=True,\r
105 help="Calculate the first derivative numerically") \r
106 self.OptionParser.add_option("--fpofx",\r
107 action="store", type="string", \r
108 dest="fpofx", default="cos(x)",\r
109 help="f'(x) for plotting") \r
110 self.OptionParser.add_option("--tab",\r
111 action="store", type="string", \r
112 dest="tab", default="sampling",\r
113 help="The selected UI-tab when OK was pressed") \r
114 self.OptionParser.add_option("--pythonfunctions",\r
115 action="store", type="string", \r
116 dest="pythonfunctions", default="",\r
117 help="dummy") \r
118 \r
119 def effect(self):\r
120 for id, node in self.selected.iteritems():\r
121 if node.tagName == 'rect':\r
122 # create new path with basic dimensions of selected rectangle\r
123 newpath = self.document.createElement('svg:path')\r
124 x = float(node.attributes.getNamedItem('x').value)\r
125 y = float(node.attributes.getNamedItem('y').value)\r
126 w = float(node.attributes.getNamedItem('width').value)\r
127 h = float(node.attributes.getNamedItem('height').value)\r
128 \r
129 #copy attributes of rect\r
130 s = node.attributes.getNamedItem('style').value\r
131 newpath.setAttribute('style', s)\r
132 try:\r
133 t = node.attributes.getNamedItem('transform').value\r
134 newpath.setAttribute('transform', t)\r
135 except AttributeError:\r
136 pass\r
137 \r
138 newpath.setAttribute('d', simplepath.formatPath(\r
139 drawfunction(self.options.xstart,\r
140 self.options.xend,\r
141 self.options.ybottom,\r
142 self.options.ytop,\r
143 self.options.samples, \r
144 w,h,x,y,\r
145 self.options.fofx, \r
146 self.options.fpofx,\r
147 self.options.fponum)))\r
148 newpath.setAttribute('title', self.options.fofx)\r
149 \r
150 #newpath.setAttribute('desc', '!func;' + self.options.fofx + ';' \r
151 # + self.options.fpofx + ';'\r
152 # + `self.options.fponum` + ';'\r
153 # + `self.options.xstart` + ';'\r
154 # + `self.options.xend` + ';'\r
155 # + `self.options.samples`)\r
156 \r
157 # add path into SVG structure\r
158 node.parentNode.appendChild(newpath)\r
159 # TODO: make an option wether to remove the rectangle or not.\r
160 node.parentNode.removeChild(node)\r
161 \r
162 e = FuncPlot()\r
163 e.affect()