funcplot.py revision 00b0f109e4e04891be218ee0bfbddaabbe2b85ae
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings#!/usr/bin/env python
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster'''
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterCopyright (C) 2006 Johan Engelen, johan@shouraizou.nl
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterCopyright (C) 2005 Aaron Spike, aaron@ekips.org
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterThis program is free software; you can redistribute it and/or modify
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterit under the terms of the GNU General Public License as published by
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterthe Free Software Foundation; either version 2 of the License, or
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster(at your option) any later version.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterThis program is distributed in the hope that it will be useful,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterbut WITHOUT ANY WARRANTY; without even the implied warranty of
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterGNU General Public License for more details.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterYou should have received a copy of the GNU General Public License
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosteralong with this program; if not, write to the Free Software
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterThis program is a modified version of wavy.py by Aaron Spike.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster'''
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterimport inkex, simplepath, simplestyle
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfrom math import *
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfrom random import *
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbingsdef drawfunction(xstart, xend, ybottom, ytop, samples, width, height, left, top,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster fx = "sin(x)", fpx = "cos(x)", fponum = True):
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # step is the distance between nodes on x
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings step = (xend - xstart) / (samples-1)
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings third = step / 3.0
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # coords and scales based on the source rect
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster scalex = width / (xend - xstart)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster xoff = left
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster coordx = lambda x: (x - xstart) * scalex + xoff #convert x-value to coordinate
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster scaley = height / (ytop - ybottom)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster yoff = top
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster coordy = lambda y: (ytop-y) * scaley + yoff #convert y-value to coordinate
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # functions specified by the user
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if fx != "":
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster f = eval('lambda x: ' + fx)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if fpx != "":
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster fp = eval('lambda x: ' + fpx)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # initialize function and derivative for 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # they are carried over from one iteration to the next, to avoid extra function calculations
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster y0 = f(xstart)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if fponum == True: # numerical derivative, using 0.001*step as the small differential
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster d0 = (f(xstart + 0.001*step) - y0)/(0.001*step)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster else: # derivative given by the user
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster d0 = fp(xstart)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster a = [] # path array
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster a.append(['M',[coordx(xstart), coordy(y0)]]) # initial moveto
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster for i in range(int(samples-1)):
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster x = (i+1) * step + xstart
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster y1 = f(x)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if fponum == True: # numerical derivative
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster d1 = (y1 - f(x - 0.001*step))/(0.001*step)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster else: # derivative given by the user
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster d1 = fp(x)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # create curve
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster a.append(['C',[coordx(x - step + third), coordy(y0 + (d0 * third)),
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster coordx(x - third), coordy(y1 - (d1 * third)),
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster coordx(x), coordy(y1)]])
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster y0 = y1 # next segment's y0 is this segment's y1
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster d0 = d1 # we assume the function is smooth everywhere, so carry over the derivative too
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return a
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterclass FuncPlot(inkex.Effect):
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster def __init__(self):
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster inkex.Effect.__init__(self)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--xstart",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="float",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="xstart", default=0.0,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="Start x-value")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--xend",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="float",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="xend", default=1.0,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="End x-value")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--ybottom",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="float",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="ybottom", default=-1.0,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="y-value of rectangle's bottom")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--ytop",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="float",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="ytop", default=1.0,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="y-value of rectangle's top")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("-s", "--samples",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="int",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="samples", default=8,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="Samples")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--fofx",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="string",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="fofx", default="sin(x)",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="f(x) for plotting")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--fponum",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="inkbool",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="fponum", default=True,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="Calculate the first derivative numerically")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--fpofx",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="string",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="fpofx", default="cos(x)",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="f'(x) for plotting")
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.OptionParser.add_option("--tab",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster action="store", type="string",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dest="tab", default="sampling",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="The selected UI-tab when OK was pressed")
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings self.OptionParser.add_option("--pythonfunctions",
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings action="store", type="string",
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings dest="pythonfunctions", default="",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster help="dummy")
5bdd6bf9211505ff52afc7e32bdc49cdfacf4879Charles Sparey
5bdd6bf9211505ff52afc7e32bdc49cdfacf4879Charles Sparey def effect(self):
449854c2a07b50ea64d9d6a8b03d18d4afeeee43Ken Stubbings for id, node in self.selected.iteritems():
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if node.tagName == 'rect':
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # create new path with basic dimensions of selected rectangle
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster newpath = self.document.createElement('svg:path')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster x = float(node.attributes.getNamedItem('x').value)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster y = float(node.attributes.getNamedItem('y').value)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster w = float(node.attributes.getNamedItem('width').value)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster h = float(node.attributes.getNamedItem('height').value)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster #copy attributes of rect
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster s = node.attributes.getNamedItem('style').value
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster newpath.setAttribute('style', s)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster try:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster t = node.attributes.getNamedItem('transform').value
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster newpath.setAttribute('transform', t)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster except AttributeError:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster pass
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster newpath.setAttribute('d', simplepath.formatPath(
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster drawfunction(self.options.xstart,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.options.xend,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.options.ybottom,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.options.ytop,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.options.samples,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster w,h,x,y,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.options.fofx,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.options.fpofx,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster self.options.fponum)))
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster newpath.setAttribute('title', self.options.fofx)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster #newpath.setAttribute('desc', '!func;' + self.options.fofx + ';'
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster # + self.options.fpofx + ';'
# + `self.options.fponum` + ';'
# + `self.options.xstart` + ';'
# + `self.options.xend` + ';'
# + `self.options.samples`)
# add path into SVG structure
node.parentNode.appendChild(newpath)
# TODO: make an option wether to remove the rectangle or not.
node.parentNode.removeChild(node)
e = FuncPlot()
e.affect()