## [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot

 [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot From: Eric Emsellem - 2005-08-17 14:54:58 ```Hi, I needed to see visualize a set of points in 3D (scatter plot) and to be able to rotate the axes easily in a way similar to gnuplot. I have thus written a short python routine using matplotlib for the visu part (attached below). It works simply by doing something like: ########################################### x = array([0.,1.,2.,3.]) y = array([1.,0.,3.,2.]) z = array([4.,3.,0.,1.]) try = pxyz(x, y, z, labx="x", laby=y", labz="z") ########################################### It does show the 4 points in projection, including the projection of the x,y,z axis. And each time you move the mouse within the figure, it will move the projection accordingly, showing the 3D scatter plots and the 3 unit axes again. (the projection is defined via 2 rotation angles, theta - around x - and phi - around z). Now I realize this is really a naive way of doing such an exercise. The updating (projecting, refreshing, and reploting all points everytime, positions of the axis labels, etc) is very far from optimal (slow and somewhat ugly) mostly because I am a beginner here. So I was wondering if anyone on-line had produced a similar but somewhat refined version of this, or if you had any suggestion of how to improve the speed/efficiency, esthetic and functionality of such a routine? At the moment I am plotting 48 points... But I can imagine such a routine could just be unusable with 1000 points... Any input is welcome!!! Thanks, Eric ==================================================================== def rotaxe(x,y,rotangle=45.) : rotangle = rotangle * pi / 180. xp = x * cos(rotangle) + y * sin(rotangle) yp = - x * sin(rotangle) + y * cos(rotangle) return xp, yp def projpoint(x,y,z,theta=45.,phi=0.) : xp, y1 = rotaxe(x,y,rotangle=phi) yp, zp = rotaxe(y1,z,rotangle=theta) return xp, yp, zp class pxyz : def __init__(self, x, y, z, theta=45., phi=140., labx="x", laby="y", labz="z"): self.x = x self.y = y self.z = z self.labx = labx self.laby = laby self.labz = labz self.theta = theta self.phi = phi self.startx = min(self.x) self.starty = min(self.y) self.startz = min(self.z) self.endx = max(self.x) self.endy = max(self.y) self.endz = max(self.z) self.rangex = self.endx - self.startx self.rangey = self.endy - self.starty self.rangez = self.endz - self.startz self.axex = array([1,0,0]) self.axey = array([0,1,0]) self.axez = array([0,0,1]) if self.rangex != 0. : self.xn = (self.x - self.startx) / self.rangex else : self.xn = zeros(shape(self.x), Float) if self.rangey != 0. : self.yn = (self.y - self.starty) / self.rangey else : self.yn = zeros(shape(self.y), Float) if self.rangez != 0. : self.zn = (self.z - self.startz) / self.rangez else : self.zn = zeros(shape(self.z), Float) fig = gcf() self.w = fig.get_figwidth() self.h = fig.get_figheight() self.plotxyz() connect('motion_notify_event', self.on_move) def plotxyz(self) : clf() ioff() xp, yp, zp = projpoint(self.axex, self.axey, self.axez, theta=self.theta, phi=self.phi) plot([0.,xp[0]],[0.,yp[0]]) plot([0.,xp[1]],[0.,yp[1]]) plot([0.,xp[2]],[0.,yp[2]]) eps = 0.02 text(xp[0]+eps,yp[0]+eps, self.labx) text(xp[1]+eps,yp[1]+eps, self.laby) text(xp[2]+eps,yp[2]+eps, self.labz) text(xp[0],yp[0], str("%5.3f"%self.endx)) text(xp[1],yp[1], str("%5.3f"%self.endy)) text(xp[2],yp[2], str("%5.3f"%self.endz)) text(xp[0]/10.,yp[0]/10., str("%5.3f"%self.startx)) text(xp[1]/10.,yp[1]/10., str("%5.3f"%self.starty)) text(xp[2]/10.,yp[2]/10., str("%5.3f"%self.startz)) xpn, ypn, zpn = projpoint(self.xn, self.yn, self.zn, theta=self.theta, phi=self.phi) scatter(xpn, ypn) ion() draw() axis('off') return xpn, ypn, zpn def on_move(self, event): # get the x and y pixel coords x, y = event.x, event.y self.theta = x * 3.6 / self.w self.phi = y * 3.6 / self.h if event.inaxes: self.plotxyz() -- =============================================================== Observatoire de Lyon emsellem@... 9 av. Charles-Andre tel: +33 4 78 86 83 84 69561 Saint-Genis Laval Cedex fax: +33 4 78 86 83 86 France http://www-obs.univ-lyon1.fr/eric.emsellem =============================================================== ```

 [Matplotlib-users] hst_demo not available anymore? From: Eric Emsellem - 2005-08-11 13:38:33 ```Hi, is there a way to have the hst_demo.py back on line (in the screenshots)? I would need it desesperately! thanks!!! Eric ```
 Re: [Matplotlib-users] hst_demo not available anymore? From: John Hunter - 2005-08-11 14:02:45 ```>>>>> "Eric" == Eric Emsellem writes: Eric> Hi, is there a way to have the hst_demo.py back on line (in Eric> the screenshots)? I would need it desesperately! thanks!!! Eric> Eric Well, I don't want anyone getting desperate around here :-) It's back up -- thanks for letting me know. JDH ```
 [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot From: Eric Emsellem - 2005-08-17 14:54:58 ```Hi, I needed to see visualize a set of points in 3D (scatter plot) and to be able to rotate the axes easily in a way similar to gnuplot. I have thus written a short python routine using matplotlib for the visu part (attached below). It works simply by doing something like: ########################################### x = array([0.,1.,2.,3.]) y = array([1.,0.,3.,2.]) z = array([4.,3.,0.,1.]) try = pxyz(x, y, z, labx="x", laby=y", labz="z") ########################################### It does show the 4 points in projection, including the projection of the x,y,z axis. And each time you move the mouse within the figure, it will move the projection accordingly, showing the 3D scatter plots and the 3 unit axes again. (the projection is defined via 2 rotation angles, theta - around x - and phi - around z). Now I realize this is really a naive way of doing such an exercise. The updating (projecting, refreshing, and reploting all points everytime, positions of the axis labels, etc) is very far from optimal (slow and somewhat ugly) mostly because I am a beginner here. So I was wondering if anyone on-line had produced a similar but somewhat refined version of this, or if you had any suggestion of how to improve the speed/efficiency, esthetic and functionality of such a routine? At the moment I am plotting 48 points... But I can imagine such a routine could just be unusable with 1000 points... Any input is welcome!!! Thanks, Eric ==================================================================== def rotaxe(x,y,rotangle=45.) : rotangle = rotangle * pi / 180. xp = x * cos(rotangle) + y * sin(rotangle) yp = - x * sin(rotangle) + y * cos(rotangle) return xp, yp def projpoint(x,y,z,theta=45.,phi=0.) : xp, y1 = rotaxe(x,y,rotangle=phi) yp, zp = rotaxe(y1,z,rotangle=theta) return xp, yp, zp class pxyz : def __init__(self, x, y, z, theta=45., phi=140., labx="x", laby="y", labz="z"): self.x = x self.y = y self.z = z self.labx = labx self.laby = laby self.labz = labz self.theta = theta self.phi = phi self.startx = min(self.x) self.starty = min(self.y) self.startz = min(self.z) self.endx = max(self.x) self.endy = max(self.y) self.endz = max(self.z) self.rangex = self.endx - self.startx self.rangey = self.endy - self.starty self.rangez = self.endz - self.startz self.axex = array([1,0,0]) self.axey = array([0,1,0]) self.axez = array([0,0,1]) if self.rangex != 0. : self.xn = (self.x - self.startx) / self.rangex else : self.xn = zeros(shape(self.x), Float) if self.rangey != 0. : self.yn = (self.y - self.starty) / self.rangey else : self.yn = zeros(shape(self.y), Float) if self.rangez != 0. : self.zn = (self.z - self.startz) / self.rangez else : self.zn = zeros(shape(self.z), Float) fig = gcf() self.w = fig.get_figwidth() self.h = fig.get_figheight() self.plotxyz() connect('motion_notify_event', self.on_move) def plotxyz(self) : clf() ioff() xp, yp, zp = projpoint(self.axex, self.axey, self.axez, theta=self.theta, phi=self.phi) plot([0.,xp[0]],[0.,yp[0]]) plot([0.,xp[1]],[0.,yp[1]]) plot([0.,xp[2]],[0.,yp[2]]) eps = 0.02 text(xp[0]+eps,yp[0]+eps, self.labx) text(xp[1]+eps,yp[1]+eps, self.laby) text(xp[2]+eps,yp[2]+eps, self.labz) text(xp[0],yp[0], str("%5.3f"%self.endx)) text(xp[1],yp[1], str("%5.3f"%self.endy)) text(xp[2],yp[2], str("%5.3f"%self.endz)) text(xp[0]/10.,yp[0]/10., str("%5.3f"%self.startx)) text(xp[1]/10.,yp[1]/10., str("%5.3f"%self.starty)) text(xp[2]/10.,yp[2]/10., str("%5.3f"%self.startz)) xpn, ypn, zpn = projpoint(self.xn, self.yn, self.zn, theta=self.theta, phi=self.phi) scatter(xpn, ypn) ion() draw() axis('off') return xpn, ypn, zpn def on_move(self, event): # get the x and y pixel coords x, y = event.x, event.y self.theta = x * 3.6 / self.w self.phi = y * 3.6 / self.h if event.inaxes: self.plotxyz() -- =============================================================== Observatoire de Lyon emsellem@... 9 av. Charles-Andre tel: +33 4 78 86 83 84 69561 Saint-Genis Laval Cedex fax: +33 4 78 86 83 86 France http://www-obs.univ-lyon1.fr/eric.emsellem =============================================================== ```
 Re: [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot From: Darren Dale - 2005-08-17 15:12:33 ```On Wednesday 17 August 2005 10:53 am, Eric Emsellem wrote: > Hi, > > I needed to see visualize a set of points in 3D (scatter plot) and to be > able to rotate the axes easily in a way similar to gnuplot. > I have thus written a short python routine using matplotlib for the visu > part (attached below). Hi Eric, Could you post a complete example? I tried to construct a script from what you posted, and got the following: (test.py:10899): Gdk-CRITICAL **: gdk_pixmap_new: assertion `(drawable != NULL) || (depth != -1)' failed Traceback (most recent call last): File "test.py", line 98, in ? s = pxyz(x, y, z, labx="x", laby="y", labz="z") File "test.py", line 55, in __init__ self.plotxyz() File "test.py", line 81, in plotxyz draw() File "/usr/lib/python2.4/site-packages/matplotlib/pylab.py", line 727, in draw get_current_fig_manager().canvas.draw() File "/usr/lib/python2.4/site-packages/matplotlib/backends/backend_gtk.py", line 234, in draw self._pixmap_prepare (w, h) File "/usr/lib/python2.4/site-packages/matplotlib/backends/backend_gtk.py", line 291, in _pixmap_prepare self._pixmap_height) RuntimeError: could not create GdkPixmap object ```
 Re: [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot From: John Hunter - 2005-08-17 15:16:52 ```>>>>> "Darren" == Darren Dale writes: Darren> line 727, in draw get_current_fig_manager().canvas.draw() Darren> File Darren> "/usr/lib/python2.4/site-packages/matplotlib/backends/backend_gtk.py", Darren> line 234, in draw self._pixmap_prepare (w, h) File Darren> "/usr/lib/python2.4/site-packages/matplotlib/backends/backend_gtk.py", Darren> line 291, in _pixmap_prepare self._pixmap_height) Darren> RuntimeError: could not create GdkPixmap object Try updating from CVS -- this is unlikely to be due to his script, but to some changes I made in the gtk backend and then fixed. JDH ```
 Re: [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot From: Darren Dale - 2005-08-17 15:49:01 ```On Wednesday 17 August 2005 11:15 am, John Hunter wrote: > >>>>> "Darren" == Darren Dale writes: > > Darren> line 727, in draw get_current_fig_manager().canvas.draw() > Darren> File > Darren> > "/usr/lib/python2.4/site-packages/matplotlib/backends/backend_gtk.py", > Darren> line 234, in draw self._pixmap_prepare (w, h) File > Darren> > "/usr/lib/python2.4/site-packages/matplotlib/backends/backend_gtk.py", > Darren> line 291, in _pixmap_prepare self._pixmap_height) > Darren> RuntimeError: could not create GdkPixmap object > > Try updating from CVS -- this is unlikely to be due to his script, but > to some changes I made in the gtk backend and then fixed. That did it. That's a nice first stab at 3D scatter plots, Eric. > any suggestion of how to improve the > speed/efficiency, esthetic and functionality of such a routine? I'll let the guru's speak to efficiency, but I have a suggestion for aesthetic and functionality. I have done quite a lot of 3D design in AutoCAD, and they have a very nice "orbit" tool. They superimposed this compass thing over the drawing: ---O--- / \ / \ | | O O | | \ / \ / ---O--- When you press the mouse button down in the N or S pole and then drag it, it rotates around the E-W axis. If you press the mouse button down outside of the compass, it rotates around the axis pointing out of the screen. I forget what it does when you press the button down inside the compass but not on a pole. I guess this is way out of scope at this point, but I thought I'd mention it. Darren ```
 Re: [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot From: Eric Emsellem - 2005-08-17 15:25:09 ```just for testing, note that there is a syntax problem in the line I sent (in laby=... one " is missing) . So you should use: x = array([0.,1.,2.,3.]) y = array([1.,0.,3.,2.]) z = array([4.,3.,0.,1.]) tmp = pxyz(x, y, z, labx="x", laby="y", labz="z") sorry for this. Eric -- =============================================================== Observatoire de Lyon emsellem@... 9 av. Charles-Andre tel: +33 4 78 86 83 84 69561 Saint-Genis Laval Cedex fax: +33 4 78 86 83 86 France http://www-obs.univ-lyon1.fr/eric.emsellem =============================================================== ```
 Re: [Matplotlib-users] A naive trial at reproducing interactive 3D scatter plots as in gnuplot From: John Hunter - 2005-08-17 15:50:09 ```>>>>> "Eric" == Eric Emsellem writes: Eric> just for testing, note that there is a syntax problem in the Eric> line I sent (in laby=... one " is missing) . Neat example. I cleaned it up to make more efficient and to solve the flicker problems. In this version, all of the artists are created once in the init function and then their data is updated on move. This is much more efficient. I also refactored the code to be independent of pylab and to make Scatter3D inherit from matplotlib Axes, since since you do your own tick lines etc. With a little work, this approach has potential for simple 3D plots. Thanks! I'll look over this more later. import pylab as p import matplotlib.numerix as nx from matplotlib.axes import Axes class Scatter3D(Axes): def __init__(self, fig, rect, x, y, z, theta=45., phi=140., labx="x", laby="y", labz="z", **kwargs): Axes.__init__(self, fig, rect, frameon=False, xticks=[], yticks=[], **kwargs) self.x = x self.y = y self.z = z self.labx = labx self.laby = laby self.labz = labz self.theta = theta self.phi = phi self.startx = min(self.x) self.starty = min(self.y) self.startz = min(self.z) self.endx = max(self.x) self.endy = max(self.y) self.endz = max(self.z) self.rangex = self.endx - self.startx self.rangey = self.endy - self.starty self.rangez = self.endz - self.startz self.axex = nx.array([1,0,0]) self.axey = nx.array([0,1,0]) self.axez = nx.array([0,0,1]) if self.rangex != 0. : self.xn = (self.x - self.startx) / self.rangex else : self.xn = nx.zeros(shape(self.x), nx.Float) if self.rangey != 0. : self.yn = (self.y - self.starty) / self.rangey else : self.yn = nx.zeros(shape(self.y), nx.Float) if self.rangez != 0. : self.zn = (self.z - self.startz) / self.rangez else : self.zn = nx.zeros(shape(self.z), nx.Float) self.w = self.figure.get_figwidth() self.h = self.figure.get_figheight() xp, yp, zp = self.projpoint(self.axex, self.axey, self.axez, theta=self.theta, phi=self.phi) self.ax0line, = self.plot([0.,xp[0]],[0.,yp[0]]) self.ax1line, = self.plot([0.,xp[1]],[0.,yp[1]]) self.ax2line, = self.plot([0.,xp[2]],[0.,yp[2]]) self.eps = 0.02 eps = self.eps self.labxt = self.text(xp[0]+eps,yp[0]+eps, self.labx) self.labyt = self.text(xp[1]+eps,yp[1]+eps, self.laby) self.labzt = self.text(xp[2]+eps,yp[2]+eps, self.labz) self.endxt = self.text(xp[0],yp[0], str("%5.3f"%self.endx)) self.endyt = self.text(xp[1],yp[1], str("%5.3f"%self.endy)) self.endzt = self.text(xp[2],yp[2], str("%5.3f"%self.endz)) self.startxt = self.text(xp[0]/10.,yp[0]/10., str("%5.3f"%self.startx)) self.startyt = self.text(xp[1]/10.,yp[1]/10., str("%5.3f"%self.starty)) self.startzt = self.text(xp[2]/10.,yp[2]/10., str("%5.3f"%self.startz)) xpn, ypn, zpn = self.projpoint(self.xn, self.yn, self.zn, theta=self.theta, phi=self.phi) self.collection = self.scatter(xpn, ypn) self.figure.canvas.mpl_connect('motion_notify_event', self.on_move) def plotxyz(self) : xp, yp, zp = self.projpoint(self.axex, self.axey, self.axez, theta=self.theta, phi=self.phi) self.ax0line.set_data([0.,xp[0]],[0.,yp[0]]) self.ax1line.set_data([0.,xp[1]],[0.,yp[1]]) self.ax2line.set_data([0.,xp[2]],[0.,yp[2]]) eps = self.eps self.labxt.set_position((xp[0]+eps,yp[0]+eps)) self.labyt.set_position((xp[1]+eps,yp[1]+eps)) self.labzt.set_position((xp[2]+eps,yp[2]+eps)) self.endxt.set_position((xp[0],yp[0])) self.endyt.set_position((xp[1],yp[1])) self.endzt.set_position((xp[2],yp[2])) self.startxt.set_position((xp[0]/10.,yp[0]/10.)) self.startyt.set_position((xp[1]/10.,yp[1]/10.)) self.startzt.set_position((xp[2]/10.,yp[2]/10.)) xpn, ypn, zpn = self.projpoint(self.xn, self.yn, self.zn, theta=self.theta, phi=self.phi) # todo, expose the _offsets property "publicly" self.collection._offsets = zip(xpn, ypn) self.figure.canvas.draw() return xpn, ypn, zpn def on_move(self, event): # get the x and y pixel coords x, y = event.x, event.y self.theta = x * 3.6 / self.w self.phi = y * 3.6 / self.h if event.inaxes == self: self.plotxyz() def rotaxe(self, x,y,rotangle=45.) : rotangle = rotangle * nx.pi / 180. xp = x * nx.cos(rotangle) + y * nx.sin(rotangle) yp = - x * nx.sin(rotangle) + y * nx.cos(rotangle) return xp, yp def projpoint(self, x,y,z,theta=45.,phi=0.) : xp, y1 = self.rotaxe(x,y,rotangle=phi) yp, zp = self.rotaxe(y1,z,rotangle=theta) return xp, yp, zp from pylab import figure, show fig = figure() x,y,z = nx.mlab.rand(3,100) ax = Scatter3D(fig, [0.1, 0.1, 0.8, 0.8], x, y, z, labx="x", laby="y", labz="z") fig.add_axes(ax) show() ```