From: Michael J Gruber <michaeljgruber+<gmane@fa...>  20080606 13:32:06

Hi there: The upcoming patch introduces functions as data providers for 3D plots, analogous to graph.data.function() and the like for 2D plots. I also suggest enaming "functionxy", which does not correspond to graph.graphxy at all. In fact, current "functionxy" provides data for graph.graph only. It expects a function reference as parameter, which is why I suggest the name "functionlambda". (function parameters are most often specified as lambda expressions.) So, the implemented naming scheme for function type data providers is: functions specified as textual expressions: "function" 2D from expressions like "y(x)=..." "functionxy" 3d from expressions like "z(x,y)=..." functions specified as function references: "functionlambda" 2D from a function ref. with signature f(x) "functionxylambda" 3d from a function ref. with sign. f(x,y) parametric functions specified as textual expressions: "paramfunction" data depending on 1 parameter "paramtsfunction" data depending on 2 parameters parametric functions specified as function references: "paramfunctionlambda" data depending on 1 parameter "paramtsfunctionlambda" data depending on 2 parameters ["ts" for two parameters t, s] Cheers, Michael Notes: Parametric functions can provide 2D or 3D data (or anything else); PyX infers this from the textual expressions "x,y=..." or "x,y,z=..." resp. from the signature of the result of the function reference. Notably, the existing "paramfunction" can be used to draw 3D curves. Parametric 3D functions of 2 parameters, together with graph.style.surface() show some problems with the hidden surface logic, depending on the function. Maybe it assumes some ordering? The patch is against PyX 0.10 (r2907). If there's interest I'll rebase against trunk and provide docs/examples. Also, the patch is formatted by gitformatpatch and friends off my gitsvn clone of the sf repo, which prefers inline patches. I hope that's okay. The commit message would be subject + body of the upcoming mail, as for gitam. I know you guys prefer oneliners here, feel free to replace ;) Michael J Gruber (1): 3d function plots pyx/graph/data.py  134 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 132 insertions(+), 2 deletions() 
From: Michael J Gruber <michaeljgruber+<gmane@fa...>  20080606 13:34:59

We introduce analogues of graph.data.function etc. to be used as data sources for graph.graphxy. Naming is changed in order to make things more consistent with grah/graphxy. * rename functionxy to functionlambda (2D function given as lambda expression) * rename paramfunctionxy to paramfunctionlambda (2D parametric function given as lambda expression) * new functionxy (3D function given as textual expression) * new functionxylambda (3D function given as lambda expression) * new paramtsfunction (function of 2 parameters given as textual expression) * new paramtsfunctionlambda (function of 2 parameters given as lambda expression) Note that all parametric functions can provide 2D as well 3D data. Signedoffby: Michael J Gruber <michaeljgruber@...>  pyx/graph/data.py  134 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 132 insertions(+), 2 deletions() diff git a/pyx/graph/data.py b/pyx/graph/data.py index 9e525ba..8ce2607 100644  a/pyx/graph/data.py +++ b/pyx/graph/data.py @@ 573,7 +573,7 @@ class function(_data): return dynamiccolumns class functionxy(function): +class functionlambda(function): def __init__(self, f, min=None, max=None, **kwargs): function.__init__(self, "y(x)=f(x)", context={"f": f}, min=min, max=max, **kwargs) @@ 606,7 +606,137 @@ class paramfunction(_data): self.columnnames = self.columns.keys() class paramfunctionxy(paramfunction): +class paramfunctionlambda(paramfunction): def __init__(self, f, min, max, **kwargs): paramfunction.__init__(self, "t", min, max, "x, y = f(t)", context={"f": f}, **kwargs) + + +class functionxy(_data): + + defaultstyles = defaultlines + + assignmentpattern = re.compile(r"\s*([az_][az09_]*)\s*\(\s*([az_][az09_]*)\s*,\s*([az_][az09_]*)\s*\)\s*=", re.IGNORECASE) + + def __init__(self, expression, title=_notitle, xmin=None, xmax=None, ymin=None, ymax=None, + points=10, context={}): + + if title is _notitle: + self.title = expression + else: + self.title = title + self.xmin = xmin + self.xmax = xmax + self.ymin = ymin + self.ymax = ymax + self.numberofpoints = points + self.context = context.copy() # be safe on late evaluations + m = self.assignmentpattern.match(expression) + if m: + self.zname, self.xname, self.yname = m.groups() + expression = expression[m.end():] + else: + raise ValueError("z(x,y)=... or similar expected") + if context.has_key(self.xname): + raise ValueError("xname in context") + if context.has_key(self.yname): + raise ValueError("yname in context") + self.expression = compile(expression.strip(), __file__, "eval") + self.columns = {} + self.columnnames = [self.xname, self.yname, self.zname] + + def dynamiccolumns(self, graph): + dynamiccolumns = {self.xname: [], self.yname: [], self.zname: []} + + xaxis = graph.axes[self.xname] + yaxis = graph.axes[self.yname] + from pyx.graph.axis import logarithmic + logxaxis = isinstance(xaxis.axis, logarithmic) + logyaxis = isinstance(yaxis.axis, logarithmic) + if self.xmin is not None: + xmin = self.xmin + else: + xmin = xaxis.data.min + if self.xmax is not None: + xmax = self.xmax + else: + xmax = xaxis.data.max + if logxaxis: + xmin = math.log(xmin) + xmax = math.log(xmax) + if self.ymin is not None: + ymin = self.ymin + else: + ymin = yaxis.data.min + if self.ymax is not None: + ymax = self.ymax + else: + ymax = yaxis.data.max + if logyaxis: + ymin = math.log(ymin) + ymax = math.log(ymax) + for i in range(self.numberofpoints): + x = xmin + (xmaxxmin)*i / (self.numberofpoints1.0) + if logxaxis: + x = math.exp(x) + self.context[self.xname] = x + for j in range(self.numberofpoints): + y = ymin + (ymaxymin)*j / (self.numberofpoints1.0) + if logyaxis: + y = math.exp(y) + dynamiccolumns[self.xname].append(x) + dynamiccolumns[self.yname].append(y) + self.context[self.yname] = y + try: + z = eval(self.expression, _mathglobals, self.context) + except (ArithmeticError, ValueError): + z = None + dynamiccolumns[self.zname].append(z) + return dynamiccolumns + + +class functionxylambda(functionxy): + + def __init__(self, f, xmin=None, xmax=None, ymin=None, ymax=None, **kwargs): + functionxy.__init__(self, "z(x,y)=f(x,y)", context={"f": f}, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, **kwargs) + + +class paramtsfunction(_data): + + defaultstyles = defaultlines + + def __init__(self, tname, tmin, tmax, sname, smin, smax, expression, title=_notitle, points=10, context={}): + if context.has_key(tname): + raise ValueError("tname in context") + if context.has_key(sname): + raise ValueError("sname in context") + if title is _notitle: + self.title = expression + else: + self.title = title + varlist, expression = expression.split("=") + expression = compile(expression.strip(), __file__, "eval") + keys = [key.strip() for key in varlist.split(",")] + self.columns = dict([(key, []) for key in keys]) + context = context.copy() + for i in range(points): + tparam = tmin + (tmaxtmin)*i / (points1.0) + context[tname] = tparam + for j in range(points): + sparam = smin + (smaxsmin)*j / (points1.0) + context[sname] = sparam + values = eval(expression, _mathglobals, context) + for key, value in zip(keys, values): + self.columns[key].append(value) + if len(keys) != len(values): + raise ValueError("unpack tuple of wrong size") + self.columnnames = self.columns.keys() + + +class paramtsfunctionlambda(paramtsfunction): + + def __init__(self, f, tmin, tmax, smin, smax, **kwargs): + paramtsfunction.__init__(self, "t", tmin, tmax, "s", smin, smax, "x, y, z = f(t,s)", context={"f": f}, **kwargs) + + + 
From: Alan G Isaac <aisaac@am...>  20080606 14:51:28

On Fri, 06 Jun 2008, Michael J Gruber apparently wrote: > We introduce analogues of graph.data.function etc. to be used as data > sources for graph.graphxy. > Naming is changed in order to make things more consistent with grah/graphxy. > * rename functionxy to functionlambda (2D function given as lambda > expression) > * rename paramfunctionxy to paramfunctionlambda (2D parametric > function given as lambda expression) > * new functionxy (3D function given as textual expression) > * new functionxylambda (3D function given as lambda expression) > * new paramtsfunction (function of 2 parameters given as textual > expression) > * new paramtsfunctionlambda (function of 2 parameters given as > lambda expression) I hope a user reaction is welcome on this list:  never say lambda.  use xy to match graphxy (2D), and xyz for 3D Here is a different approach. Consider graph.data. I propose that from a user interface perspective, there was a mistake. The ``function`` class should be a facade which should include the following keyword arguments: parametric = False expression = '' function = None A user would then set one of the three. Perhaps a new ``functionxy`` or even ``x2y`` (ok, I can hear the groans) class could implement this facade. However it would be backward compatible to just generalize ``function``. The 3D code need never go the 2D class proliferation route: it can start right away with a nice unified user interface. The corresponding 3D class could be ``functionxyz`` or ``surface`` or even ``xy2z``. Talk is cheap! Alan Isaac 
From: Michael J Gruber <michaeljgruber+<gmane@fa...>  20080606 15:12:58

Alan G Isaac venit, vidit, dixit 06.06.2008 16:55: > On Fri, 06 Jun 2008, Michael J Gruber apparently wrote: >> We introduce analogues of graph.data.function etc. to be used as data >> sources for graph.graphxy. >> Naming is changed in order to make things more consistent with grah/graphxy. >> * rename functionxy to functionlambda (2D function given as lambda >> expression) >> * rename paramfunctionxy to paramfunctionlambda (2D parametric >> function given as lambda expression) >> * new functionxy (3D function given as textual expression) >> * new functionxylambda (3D function given as lambda expression) >> * new paramtsfunction (function of 2 parameters given as textual >> expression) >> * new paramtsfunctionlambda (function of 2 parameters given as >> lambda expression) > > > I hope a user reaction is welcome on this list: > >  never say lambda. >  use xy to match graphxy (2D), and xyz for 3D Gosh, I knew I got confused myself. As a matter of fact: There is no "graphxy" right now, only "graph" and "graphxyz". Do you suggest renaming graph as well? s/xy/xyz/g in (most of) what I submitted. About the onedoesitall function: The interface as it is saves people from having to use lambda expressions. I could do without that, it only creates trouble with "context". I don't know about a more complex interface, though. After all, all I wanted to do was providing a 3D function plot class. The search for an appropriate name got me into that renaming trap. Michael 
From: Alan G Isaac <aisaac@am...>  20080606 15:31:05

On Fri, 06 Jun 2008, Michael J Gruber apparently wrote: > Gosh, I knew I got confused myself. As a matter of fact: There is no > "graphxy" right now, only "graph" and "graphxyz". Do you suggest > renaming graph as well? s/xy/xyz/g in (most of) what I submitted. I was referring to the graphxy *class*, which I find appropriately named. > About the onedoesitall function: The interface as it is > saves people from having to use lambda expressions. > I could do without that, it only creates trouble with > "context". I really think the emphasis on lambda expressions is a sideshow: they are just functions. When the argument should be a function, the user is free to (but not required to) use a lambda expression. But I agree it would have been most natural to use real functions and forego string parsing. > I don't know about a more complex interface, though. It is not meant to be complex. I was just suggesting a unified interface that branches to existing code. > After all, all I wanted to do was providing a 3D function plot class. > The search for an appropriate name got me into that renaming trap. So starting with that observation, can I ask, why not start by only accepting functions, and leaving string parsing as a response to later user demands? (Which I do not expect you will see!) Also, even the whole parametric thing is redundant. Just accept one object. If it is a function, use it directly. If it is a tuple of three functions, use them for a parametric specification. (Being explicit about order is important of course.) Just a thought. Cheers, Alan 
From: André Wobst <wobsta@us...>  20080606 16:01:30

Hi, I haven't jet managed to take a look at your patch, but for the moment I do not fully understand what you want to do. You want to create some data which you can use in a surface plot, right? I do not consider function to be a good data source for that. It's just a (preliminary) accident, that function has a fixed "points" parameter and it just creates an equal spaced number of points within the keyvalue range (for a one dimensional range). Regarding paramfunction I think you can already do a x(t), y(t), z(t) where t is a parameter. paramfunction is not limited to 2d, but it has a single parameter (like t here) only. We could generalize that like creating a paramsfunction with a list of parameters. However, I do not yet think that the implicit resolution (due to points) is a good way to create a grid. A function does not create grid data. Beside that you are right, function indeed is a 2d only data source. However, if we want to change that the discussion quickly gets identical to what I wrote about the multi parameter paramfunctions and its implicit grid likebehaviour (which I don't like). Still there is plenty of room for creating a grid data source. Beside that: there is graphxy and graphxyz. graph is an abstract base class. Note that I'm not sure whether a grid data source could in the end be a function ... I just don't know yet ... I need to think about it. Best, André Am 06.06.2008 um 17:12 schrieb Michael J Gruber: > Alan G Isaac venit, vidit, dixit 06.06.2008 16:55: >> On Fri, 06 Jun 2008, Michael J Gruber apparently wrote: >>> We introduce analogues of graph.data.function etc. to be used as >>> data >>> sources for graph.graphxy. >>> Naming is changed in order to make things more consistent with >>> grah/graphxy. >>> * rename functionxy to functionlambda (2D function given as lambda >>> expression) >>> * rename paramfunctionxy to paramfunctionlambda (2D parametric >>> function given as lambda expression) >>> * new functionxy (3D function given as textual expression) >>> * new functionxylambda (3D function given as lambda expression) >>> * new paramtsfunction (function of 2 parameters given as textual >>> expression) >>> * new paramtsfunctionlambda (function of 2 parameters given as >>> lambda expression) >> >> >> I hope a user reaction is welcome on this list: >> >>  never say lambda. >>  use xy to match graphxy (2D), and xyz for 3D > > Gosh, I knew I got confused myself. As a matter of fact: There is no > "graphxy" right now, only "graph" and "graphxyz". Do you suggest > renaming graph as well? s/xy/xyz/g in (most of) what I submitted. > > About the onedoesitall function: The interface as it is saves > people > from having to use lambda expressions. I could do without that, it > only > creates trouble with "context". I don't know about a more complex > interface, though. > > After all, all I wanted to do was providing a 3D function plot class. > The search for an appropriate name got me into that renaming trap. > > Michael > > >  > Check out the new SourceForge.net Marketplace. > It's the best place to buy or sell services for > just about anything Open Source. > http://sourceforge.net/services/buy/index.php > _______________________________________________ > PyXdevel mailing list > PyXdevel@... > https://lists.sourceforge.net/lists/listinfo/pyxdevel >  by _ _ _ Dr. André Wobst, Amselweg 22, 85716 Unterschleißheim / \ \ / ) wobsta@..., http://www.wobsta.de/ / _ \ \/\/ / PyX  High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ 
From: Michael J Gruber <michaeljgruber+<gmane@fa...>  20080609 08:07:02

Hi André André Wobst venit, vidit, dixit 06.06.2008 17:51: > Hi, > > I haven't jet managed to take a look at your patch, but for the moment > I do not fully understand what you want to do. You want to create some > data which you can use in a surface plot, right? I do not consider All I wanted (originally) was making something like the following work: g = graph.graphxyz(size=1.5) g.plot(graph.data.functionxy("z(x,y)=x**2+y**2", xmin=1, xmax=1, ymin=1, ymax=1, points=25), [graph.style.surface()] ) > Regarding paramfunction I think you can already do a x(t), y(t), z(t) > where t is a parameter. paramfunction is not limited to 2d, but it has > a single parameter (like t here) only. We could generalize that like > creating a paramsfunction with a list of parameters. However, I do not > yet think that the implicit resolution (due to points) is a good way > to create a grid. A function does not create grid data. I don't know the underlying grid and hidden surface code. But I noticed that it's difficult to produce e.g. a sphere by providing the points in an obvious way (using a regular grid "in spherical coordinates"). > Beside that you are right, function indeed is a 2d only data source. > However, if we want to change that the discussion quickly gets > identical to what I wrote about the multi parameter paramfunctions and > its implicit grid likebehaviour (which I don't like). > > Still there is plenty of room for creating a grid data source. > > Beside that: there is graphxy and graphxyz. graph is an abstract base > class. Yes, I'm sorry, that's where my detour began... > Note that I'm not sure whether a grid data source could in the end be > a function ... I just don't know yet ... I need to think about it. I really appreciate the possibility to use my own grid, you're right. In fact I thought about submitting a simple example which shows how powerful PyX is in this regard. I just thought that having a simple method for "standard" 3D surface plots would be nice, at least for the people who use PyX for illustrations of text books/homework sheets and the like, where your input consists of "advanced functions" like the one above, not numbers crunched by your programmes. Even for 2D plots a nonstandard grid can help, and one could argue for the removal of the standard graph.data.function(). Maybe one should just use numpy or something similar rather than the cumbersome xs = [ xmin+i*(xmaxxmin)/(N1) for i in range(N) ] ps = [ [x,f(x)] for x in xs ] and similar for 3D. Cheers, Michael 
From: André Wobst <wobsta@us...>  20080610 06:34:16

Hi Michael, Am 09.06.2008 um 10:06 schrieb Michael J Gruber: > g = graph.graphxyz(size=1.5) > g.plot(graph.data.functionxy("z(x,y)=x**2+y**2", xmin=1, xmax=1, > ymin=1, ymax=1, points=25), [graph.style.surface()] ) In our current naming scheme it should either read: g.plot(graph.data.function("z(x,y)=x**2+y**2", xmin=1, xmax=1, ymin=1, ymax=1, points=25), [graph.style.surface()] ) or g.plot(graph.data.functionxyz("x**2+y**2", xmin=1, xmax=1, ymin=1, ymax=1, points=25), [graph.style.surface()] ) (Sure, one could argue why we don't spell it functionzxy, but we did use function xyz as this sounds more natural  to me.) But back to the problem. Somehow you use xmin ymin xmax ymax making x and y some special names. This is against all rules. PyX would use min1, min2, max1, max2. And here you already see that we're back at the multidimensionality of function which is something new. Somehow it's a twodfunction ... which is something different than function. I'm fine with adding such a code to the PyX core. We basically just need a good name. Somehow it's a grid ... or a gridfunction or what??? Ideas??? > I don't know the underlying grid and hidden surface code. But I > noticed > that it's difficult to produce e.g. a sphere by providing the points > in > an obvious way (using a regular grid "in spherical coordinates"). I thought about this at your first posting too. It's not that easy. > I really appreciate the possibility to use my own grid, you're > right. In > fact I thought about submitting a simple example which shows how > powerful PyX is in this regard. We definitely want that! :) > I just thought that having a simple method for "standard" 3D surface > plots would be nice, at least for the people who use PyX for > illustrations of text books/homework sheets and the like, where your > input consists of "advanced functions" like the one above, not numbers > crunched by your programmes. > > Even for 2D plots a nonstandard grid can help, and one could argue > for > the removal of the standard graph.data.function(). Maybe one should > just > use numpy or something similar rather than the cumbersome > > xs = [ xmin+i*(xmaxxmin)/(N1) for i in range(N) ] > ps = [ [x,f(x)] for x in xs ] > > and similar for 3D. I think there is plenty of room to make some fancy data providers for PyX (and I want function to become much more advanced in that respect), but at the same time it's absolutely appreciated to make clever uses of PyX + numpy or whatever else. It would not even render your example "exotic" in the respect that I would reject it for the examples page. Indeed it's the other way around: I would be happy to have one (basic) and some advanced examples in that respect. No problem, really! André  by _ _ _ Dr. André Wobst, Amselweg 22, 85716 Unterschleißheim / \ \ / ) wobsta@..., http://www.wobsta.de/ / _ \ \/\/ / PyX  High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ 