Code

redesign the dialog into 3 tabs, rewrite most texts, increase preview size
[inkscape.git] / share / extensions / funcplot.py
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()