From 1a104c754226f16a8baaf3606e658a2185ade6b7 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 7 Dec 2012 09:31:21 -0500 Subject: [PATCH 1/2] doc update: added some missing functions setup.py updates --- documentation/source/functions.rst | 31 +++++++++++++++++++++++++++++- setup.py | 18 ++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/documentation/source/functions.rst b/documentation/source/functions.rst index ad43ca06..65f2c202 100644 --- a/documentation/source/functions.rst +++ b/documentation/source/functions.rst @@ -8,7 +8,7 @@ Simple Data Display Functions .. autofunction:: pyqtgraph.image - +.. autofunction:: pyqtgraph.dbg Color, Pen, and Brush Functions ------------------------------- @@ -34,6 +34,8 @@ Qt uses the classes QColor, QPen, and QBrush to determine how to draw lines and .. autofunction:: pyqtgraph.colorStr +.. autofunction:: pyqtgraph.glColor + Data Slicing ------------ @@ -41,6 +43,18 @@ Data Slicing .. autofunction:: pyqtgraph.affineSlice +Coordinate Transformation +------------------------- + +.. autofunction:: pyqtgraph.transformToArray + +.. autofunction:: pyqtgraph.transformCoordinates + +.. autofunction:: pyqtgraph.solve3DTransform + +.. autofunction:: pyqtgraph.solveBilinearTransform + + SI Unit Conversion Functions ---------------------------- @@ -59,6 +73,12 @@ Image Preparation Functions .. autofunction:: pyqtgraph.makeQImage +.. autofunction:: pyqtgraph.applyLookupTable + +.. autofunction:: pyqtgraph.rescaleData + +.. autofunction:: pyqtgraph.imageToArray + Mesh Generation Functions ------------------------- @@ -68,4 +88,13 @@ Mesh Generation Functions .. autofunction:: pyqtgraph.isosurface +Miscellaneous Functions +----------------------- + +.. autofunction:: pyqtgraph.pseudoScatter + +.. autofunction:: pyqtgraph.systemInfo + + + diff --git a/setup.py b/setup.py index 9d56a0cf..31376ce5 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,11 @@ all_packages = ['.'.join(p) for p in subdirs] setup(name='pyqtgraph', version='', description='Scientific Graphics and GUI Library for Python', - long_description="PyQtGraph is a pure-python graphics and GUI library built on PyQt4 and numpy. It is intended for use in mathematics / scientific / engineering applications. Despite being written entirely in python, the library is very fast due to its heavy leverage of numpy for number crunching and Qt's GraphicsView framework for fast display.", + long_description="""\ +PyQtGraph is a pure-python graphics and GUI library built on PyQt4/PySide and numpy. + +It is intended for use in mathematics / scientific / engineering applications. Despite being written entirely in python, the library is very fast due to its heavy leverage of numpy for number crunching and Qt's GraphicsView framework for fast display. +""", license='MIT', url='http://www.pyqtgraph.org', author='Luke Campagnola', @@ -17,5 +21,17 @@ setup(name='pyqtgraph', packages=all_packages, package_dir = {'pyqtgraph': '.'}, package_data={'pyqtgraph': ['graphicsItems/PlotItem/*.png']}, + classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Development Status :: 4 - Beta", + "Environment :: Other Environment", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Scientific/Engineering :: Visualization", + "Topic :: Software Development :: User Interfaces", + ], ) From ecca8855df49e9db646cdb9b67191f29caa7a90e Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Sat, 22 Dec 2012 15:16:38 -0500 Subject: [PATCH 2/2] - ScatterPlotItem disables render cache during export - Fixes for SVG exporter - functions.isosurface() is a bazillion times faster (API change: return value format has changed) --- examples/GLIsosurface.py | 4 +- exporters/Exporter.py | 3 +- exporters/SVGExporter.py | 7 +- functions.py | 783 +++++++++++++++++-------------- graphicsItems/ScatterPlotItem.py | 10 +- 5 files changed, 448 insertions(+), 359 deletions(-) diff --git a/examples/GLIsosurface.py b/examples/GLIsosurface.py index 32478605..4324d9cf 100644 --- a/examples/GLIsosurface.py +++ b/examples/GLIsosurface.py @@ -45,9 +45,9 @@ data = np.abs(np.fromfunction(psi, (50,50,100))) print("Generating isosurface..") -verts = pg.isosurface(data, data.max()/4.) +verts, faces = pg.isosurface(data, data.max()/4.) -md = gl.MeshData(vertexes=verts) +md = gl.MeshData(vertexes=verts, faces=faces) colors = np.ones((md.faceCount(), 4), dtype=float) colors[:,3] = 0.2 diff --git a/exporters/Exporter.py b/exporters/Exporter.py index c3917fca..2540b327 100644 --- a/exporters/Exporter.py +++ b/exporters/Exporter.py @@ -73,7 +73,8 @@ class Exporter(object): def getSourceRect(self): if isinstance(self.item, pg.GraphicsScene): - return self.item.getViewWidget().viewRect() + w = self.item.getViewWidget() + return w.viewportTransform().inverted()[0].mapRect(w.rect()) else: return self.item.sceneBoundingRect() diff --git a/exporters/SVGExporter.py b/exporters/SVGExporter.py index 0538aae7..2d040282 100644 --- a/exporters/SVGExporter.py +++ b/exporters/SVGExporter.py @@ -36,11 +36,14 @@ class SVGExporter(Exporter): return self.svg = QtSvg.QSvgGenerator() self.svg.setFileName(fileName) - self.svg.setSize(QtCore.QSize(100,100)) - #self.svg.setResolution(600) + dpi = QtGui.QDesktopWidget().physicalDpiX() + ## not really sure why this works, but it seems to be important: + self.svg.setSize(QtCore.QSize(self.params['width']*dpi/90., self.params['height']*dpi/90.)) + self.svg.setResolution(dpi) #self.svg.setViewBox() targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height']) sourceRect = self.getSourceRect() + painter = QtGui.QPainter(self.svg) try: self.setExportMode(True) diff --git a/functions.py b/functions.py index b83a2186..472a8596 100644 --- a/functions.py +++ b/functions.py @@ -1145,342 +1145,365 @@ def isocurve(data, level): return lines ## a list of pairs of points - +IsosurfaceDataCache = None def isosurface(data, level): """ Generate isosurface from volumetric data using marching cubes algorithm. See Paul Bourke, "Polygonising a Scalar Field" - (http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/) + (http://paulbourke.net/geometry/polygonise/) *data* 3D numpy array of scalar values *level* The level at which to generate an isosurface - Returns an array of vertex coordinates (N, 3, 3); - - This function is SLOW; plenty of room for optimization here. + Returns an array of vertex coordinates (Nv, 3) and an array of + per-face vertex indexes (Nf, 3) """ + ## For improvement, see: + ## + ## Efficient implementation of Marching Cubes' cases with topological guarantees. + ## Thomas Lewiner, Helio Lopes, Antonio Wilson Vieira and Geovan Tavares. + ## Journal of Graphics Tools 8(2): pp. 1-15 (december 2003) + + ## Precompute lookup tables on the first run + global IsosurfaceDataCache + if IsosurfaceDataCache is None: + ## map from grid cell index to edge index. + ## grid cell index tells us which corners are below the isosurface, + ## edge index tells us which edges are cut by the isosurface. + ## (Data stolen from Bourk; see above.) + edgeTable = np.array([ + 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ], dtype=np.uint16) + + ## Table of triangles to use for filling each grid cell. + ## Each set of three integers tells us which three edges to + ## draw a triangle between. + ## (Data stolen from Bourk; see above.) + triTable = [ + [], + [0, 8, 3], + [0, 1, 9], + [1, 8, 3, 9, 8, 1], + [1, 2, 10], + [0, 8, 3, 1, 2, 10], + [9, 2, 10, 0, 2, 9], + [2, 8, 3, 2, 10, 8, 10, 9, 8], + [3, 11, 2], + [0, 11, 2, 8, 11, 0], + [1, 9, 0, 2, 3, 11], + [1, 11, 2, 1, 9, 11, 9, 8, 11], + [3, 10, 1, 11, 10, 3], + [0, 10, 1, 0, 8, 10, 8, 11, 10], + [3, 9, 0, 3, 11, 9, 11, 10, 9], + [9, 8, 10, 10, 8, 11], + [4, 7, 8], + [4, 3, 0, 7, 3, 4], + [0, 1, 9, 8, 4, 7], + [4, 1, 9, 4, 7, 1, 7, 3, 1], + [1, 2, 10, 8, 4, 7], + [3, 4, 7, 3, 0, 4, 1, 2, 10], + [9, 2, 10, 9, 0, 2, 8, 4, 7], + [2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4], + [8, 4, 7, 3, 11, 2], + [11, 4, 7, 11, 2, 4, 2, 0, 4], + [9, 0, 1, 8, 4, 7, 2, 3, 11], + [4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1], + [3, 10, 1, 3, 11, 10, 7, 8, 4], + [1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4], + [4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3], + [4, 7, 11, 4, 11, 9, 9, 11, 10], + [9, 5, 4], + [9, 5, 4, 0, 8, 3], + [0, 5, 4, 1, 5, 0], + [8, 5, 4, 8, 3, 5, 3, 1, 5], + [1, 2, 10, 9, 5, 4], + [3, 0, 8, 1, 2, 10, 4, 9, 5], + [5, 2, 10, 5, 4, 2, 4, 0, 2], + [2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8], + [9, 5, 4, 2, 3, 11], + [0, 11, 2, 0, 8, 11, 4, 9, 5], + [0, 5, 4, 0, 1, 5, 2, 3, 11], + [2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5], + [10, 3, 11, 10, 1, 3, 9, 5, 4], + [4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10], + [5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3], + [5, 4, 8, 5, 8, 10, 10, 8, 11], + [9, 7, 8, 5, 7, 9], + [9, 3, 0, 9, 5, 3, 5, 7, 3], + [0, 7, 8, 0, 1, 7, 1, 5, 7], + [1, 5, 3, 3, 5, 7], + [9, 7, 8, 9, 5, 7, 10, 1, 2], + [10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3], + [8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2], + [2, 10, 5, 2, 5, 3, 3, 5, 7], + [7, 9, 5, 7, 8, 9, 3, 11, 2], + [9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11], + [2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7], + [11, 2, 1, 11, 1, 7, 7, 1, 5], + [9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11], + [5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0], + [11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0], + [11, 10, 5, 7, 11, 5], + [10, 6, 5], + [0, 8, 3, 5, 10, 6], + [9, 0, 1, 5, 10, 6], + [1, 8, 3, 1, 9, 8, 5, 10, 6], + [1, 6, 5, 2, 6, 1], + [1, 6, 5, 1, 2, 6, 3, 0, 8], + [9, 6, 5, 9, 0, 6, 0, 2, 6], + [5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8], + [2, 3, 11, 10, 6, 5], + [11, 0, 8, 11, 2, 0, 10, 6, 5], + [0, 1, 9, 2, 3, 11, 5, 10, 6], + [5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11], + [6, 3, 11, 6, 5, 3, 5, 1, 3], + [0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6], + [3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9], + [6, 5, 9, 6, 9, 11, 11, 9, 8], + [5, 10, 6, 4, 7, 8], + [4, 3, 0, 4, 7, 3, 6, 5, 10], + [1, 9, 0, 5, 10, 6, 8, 4, 7], + [10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4], + [6, 1, 2, 6, 5, 1, 4, 7, 8], + [1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7], + [8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6], + [7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9], + [3, 11, 2, 7, 8, 4, 10, 6, 5], + [5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11], + [0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6], + [9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6], + [8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6], + [5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11], + [0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7], + [6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9], + [10, 4, 9, 6, 4, 10], + [4, 10, 6, 4, 9, 10, 0, 8, 3], + [10, 0, 1, 10, 6, 0, 6, 4, 0], + [8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10], + [1, 4, 9, 1, 2, 4, 2, 6, 4], + [3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4], + [0, 2, 4, 4, 2, 6], + [8, 3, 2, 8, 2, 4, 4, 2, 6], + [10, 4, 9, 10, 6, 4, 11, 2, 3], + [0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6], + [3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10], + [6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1], + [9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3], + [8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1], + [3, 11, 6, 3, 6, 0, 0, 6, 4], + [6, 4, 8, 11, 6, 8], + [7, 10, 6, 7, 8, 10, 8, 9, 10], + [0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10], + [10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0], + [10, 6, 7, 10, 7, 1, 1, 7, 3], + [1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7], + [2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9], + [7, 8, 0, 7, 0, 6, 6, 0, 2], + [7, 3, 2, 6, 7, 2], + [2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7], + [2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7], + [1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11], + [11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1], + [8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6], + [0, 9, 1, 11, 6, 7], + [7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0], + [7, 11, 6], + [7, 6, 11], + [3, 0, 8, 11, 7, 6], + [0, 1, 9, 11, 7, 6], + [8, 1, 9, 8, 3, 1, 11, 7, 6], + [10, 1, 2, 6, 11, 7], + [1, 2, 10, 3, 0, 8, 6, 11, 7], + [2, 9, 0, 2, 10, 9, 6, 11, 7], + [6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8], + [7, 2, 3, 6, 2, 7], + [7, 0, 8, 7, 6, 0, 6, 2, 0], + [2, 7, 6, 2, 3, 7, 0, 1, 9], + [1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6], + [10, 7, 6, 10, 1, 7, 1, 3, 7], + [10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8], + [0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7], + [7, 6, 10, 7, 10, 8, 8, 10, 9], + [6, 8, 4, 11, 8, 6], + [3, 6, 11, 3, 0, 6, 0, 4, 6], + [8, 6, 11, 8, 4, 6, 9, 0, 1], + [9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6], + [6, 8, 4, 6, 11, 8, 2, 10, 1], + [1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6], + [4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9], + [10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3], + [8, 2, 3, 8, 4, 2, 4, 6, 2], + [0, 4, 2, 4, 6, 2], + [1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8], + [1, 9, 4, 1, 4, 2, 2, 4, 6], + [8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1], + [10, 1, 0, 10, 0, 6, 6, 0, 4], + [4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3], + [10, 9, 4, 6, 10, 4], + [4, 9, 5, 7, 6, 11], + [0, 8, 3, 4, 9, 5, 11, 7, 6], + [5, 0, 1, 5, 4, 0, 7, 6, 11], + [11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5], + [9, 5, 4, 10, 1, 2, 7, 6, 11], + [6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5], + [7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2], + [3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6], + [7, 2, 3, 7, 6, 2, 5, 4, 9], + [9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7], + [3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0], + [6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8], + [9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7], + [1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4], + [4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10], + [7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10], + [6, 9, 5, 6, 11, 9, 11, 8, 9], + [3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5], + [0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11], + [6, 11, 3, 6, 3, 5, 5, 3, 1], + [1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6], + [0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10], + [11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5], + [6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3], + [5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2], + [9, 5, 6, 9, 6, 0, 0, 6, 2], + [1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8], + [1, 5, 6, 2, 1, 6], + [1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6], + [10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0], + [0, 3, 8, 5, 6, 10], + [10, 5, 6], + [11, 5, 10, 7, 5, 11], + [11, 5, 10, 11, 7, 5, 8, 3, 0], + [5, 11, 7, 5, 10, 11, 1, 9, 0], + [10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1], + [11, 1, 2, 11, 7, 1, 7, 5, 1], + [0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11], + [9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7], + [7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2], + [2, 5, 10, 2, 3, 5, 3, 7, 5], + [8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5], + [9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2], + [9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2], + [1, 3, 5, 3, 7, 5], + [0, 8, 7, 0, 7, 1, 1, 7, 5], + [9, 0, 3, 9, 3, 5, 5, 3, 7], + [9, 8, 7, 5, 9, 7], + [5, 8, 4, 5, 10, 8, 10, 11, 8], + [5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0], + [0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5], + [10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4], + [2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8], + [0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11], + [0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5], + [9, 4, 5, 2, 11, 3], + [2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4], + [5, 10, 2, 5, 2, 4, 4, 2, 0], + [3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9], + [5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2], + [8, 4, 5, 8, 5, 3, 3, 5, 1], + [0, 4, 5, 1, 0, 5], + [8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5], + [9, 4, 5], + [4, 11, 7, 4, 9, 11, 9, 10, 11], + [0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11], + [1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11], + [3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4], + [4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2], + [9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3], + [11, 7, 4, 11, 4, 2, 2, 4, 0], + [11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4], + [2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9], + [9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7], + [3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10], + [1, 10, 2, 8, 7, 4], + [4, 9, 1, 4, 1, 7, 7, 1, 3], + [4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1], + [4, 0, 3, 7, 4, 3], + [4, 8, 7], + [9, 10, 8, 10, 11, 8], + [3, 0, 9, 3, 9, 11, 11, 9, 10], + [0, 1, 10, 0, 10, 8, 8, 10, 11], + [3, 1, 10, 11, 3, 10], + [1, 2, 11, 1, 11, 9, 9, 11, 8], + [3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9], + [0, 2, 11, 8, 0, 11], + [3, 2, 11], + [2, 3, 8, 2, 8, 10, 10, 8, 9], + [9, 10, 2, 0, 9, 2], + [2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8], + [1, 10, 2], + [1, 3, 8, 9, 1, 8], + [0, 9, 1], + [0, 3, 8], + [] + ] + edgeShifts = np.array([ ## maps edge ID (0-11) to (x,y,z) cell offset and edge ID (0-2) + [0, 0, 0, 0], + [1, 0, 0, 1], + [0, 1, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], + [1, 0, 1, 1], + [0, 1, 1, 0], + [0, 0, 1, 1], + [0, 0, 0, 2], + [1, 0, 0, 2], + [1, 1, 0, 2], + [0, 1, 0, 2], + #[9, 9, 9, 9] ## fake + ], dtype=np.ubyte) + nTableFaces = np.array([len(f)/3 for f in triTable], dtype=np.ubyte) + faceShiftTables = [None] + for i in range(1,6): + ## compute lookup table of index: vertexes mapping + faceTableI = np.zeros((len(triTable), i*3), dtype=np.ubyte) + faceTableInds = np.argwhere(nTableFaces == i) + faceTableI[faceTableInds[:,0]] = np.array([triTable[j] for j in faceTableInds]) + faceTableI = faceTableI.reshape((len(triTable), i, 3)) + faceShiftTables.append(edgeShifts[faceTableI]) + + ## Let's try something different: + #faceTable = np.empty((256, 5, 3, 4), dtype=np.ubyte) # (grid cell index, faces, vertexes, edge lookup) + #for i,f in enumerate(triTable): + #f = np.array(f + [12] * (15-len(f))).reshape(5,3) + #faceTable[i] = edgeShifts[f] + + + IsosurfaceDataCache = (faceShiftTables, edgeShifts, edgeTable, nTableFaces) + else: + faceShiftTables, edgeShifts, edgeTable, nTableFaces = IsosurfaceDataCache - ## map from grid cell index to edge index. - ## grid cell index tells us which corners are below the isosurface, - ## edge index tells us which edges are cut by the isosurface. - ## (Data stolen from Bourk; see above.) - edgeTable = [ - 0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, - 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, - 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, - 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, - 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, - 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, - 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, - 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, - 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, - 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, - 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, - 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, - 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, - 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, - 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , - 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, - 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, - 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, - 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, - 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, - 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, - 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, - 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, - 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, - 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, - 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, - 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, - 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, - 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, - 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, - 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, - 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] - ## Table of triangles to use for filling each grid cell. - ## Each set of three integers tells us which three edges to - ## draw a triangle between. - ## (Data stolen from Bourk; see above.) - triTable = [ - [], - [0, 8, 3], - [0, 1, 9], - [1, 8, 3, 9, 8, 1], - [1, 2, 10], - [0, 8, 3, 1, 2, 10], - [9, 2, 10, 0, 2, 9], - [2, 8, 3, 2, 10, 8, 10, 9, 8], - [3, 11, 2], - [0, 11, 2, 8, 11, 0], - [1, 9, 0, 2, 3, 11], - [1, 11, 2, 1, 9, 11, 9, 8, 11], - [3, 10, 1, 11, 10, 3], - [0, 10, 1, 0, 8, 10, 8, 11, 10], - [3, 9, 0, 3, 11, 9, 11, 10, 9], - [9, 8, 10, 10, 8, 11], - [4, 7, 8], - [4, 3, 0, 7, 3, 4], - [0, 1, 9, 8, 4, 7], - [4, 1, 9, 4, 7, 1, 7, 3, 1], - [1, 2, 10, 8, 4, 7], - [3, 4, 7, 3, 0, 4, 1, 2, 10], - [9, 2, 10, 9, 0, 2, 8, 4, 7], - [2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4], - [8, 4, 7, 3, 11, 2], - [11, 4, 7, 11, 2, 4, 2, 0, 4], - [9, 0, 1, 8, 4, 7, 2, 3, 11], - [4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1], - [3, 10, 1, 3, 11, 10, 7, 8, 4], - [1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4], - [4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3], - [4, 7, 11, 4, 11, 9, 9, 11, 10], - [9, 5, 4], - [9, 5, 4, 0, 8, 3], - [0, 5, 4, 1, 5, 0], - [8, 5, 4, 8, 3, 5, 3, 1, 5], - [1, 2, 10, 9, 5, 4], - [3, 0, 8, 1, 2, 10, 4, 9, 5], - [5, 2, 10, 5, 4, 2, 4, 0, 2], - [2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8], - [9, 5, 4, 2, 3, 11], - [0, 11, 2, 0, 8, 11, 4, 9, 5], - [0, 5, 4, 0, 1, 5, 2, 3, 11], - [2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5], - [10, 3, 11, 10, 1, 3, 9, 5, 4], - [4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10], - [5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3], - [5, 4, 8, 5, 8, 10, 10, 8, 11], - [9, 7, 8, 5, 7, 9], - [9, 3, 0, 9, 5, 3, 5, 7, 3], - [0, 7, 8, 0, 1, 7, 1, 5, 7], - [1, 5, 3, 3, 5, 7], - [9, 7, 8, 9, 5, 7, 10, 1, 2], - [10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3], - [8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2], - [2, 10, 5, 2, 5, 3, 3, 5, 7], - [7, 9, 5, 7, 8, 9, 3, 11, 2], - [9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11], - [2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7], - [11, 2, 1, 11, 1, 7, 7, 1, 5], - [9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11], - [5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0], - [11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0], - [11, 10, 5, 7, 11, 5], - [10, 6, 5], - [0, 8, 3, 5, 10, 6], - [9, 0, 1, 5, 10, 6], - [1, 8, 3, 1, 9, 8, 5, 10, 6], - [1, 6, 5, 2, 6, 1], - [1, 6, 5, 1, 2, 6, 3, 0, 8], - [9, 6, 5, 9, 0, 6, 0, 2, 6], - [5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8], - [2, 3, 11, 10, 6, 5], - [11, 0, 8, 11, 2, 0, 10, 6, 5], - [0, 1, 9, 2, 3, 11, 5, 10, 6], - [5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11], - [6, 3, 11, 6, 5, 3, 5, 1, 3], - [0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6], - [3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9], - [6, 5, 9, 6, 9, 11, 11, 9, 8], - [5, 10, 6, 4, 7, 8], - [4, 3, 0, 4, 7, 3, 6, 5, 10], - [1, 9, 0, 5, 10, 6, 8, 4, 7], - [10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4], - [6, 1, 2, 6, 5, 1, 4, 7, 8], - [1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7], - [8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6], - [7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9], - [3, 11, 2, 7, 8, 4, 10, 6, 5], - [5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11], - [0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6], - [9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6], - [8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6], - [5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11], - [0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7], - [6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9], - [10, 4, 9, 6, 4, 10], - [4, 10, 6, 4, 9, 10, 0, 8, 3], - [10, 0, 1, 10, 6, 0, 6, 4, 0], - [8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10], - [1, 4, 9, 1, 2, 4, 2, 6, 4], - [3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4], - [0, 2, 4, 4, 2, 6], - [8, 3, 2, 8, 2, 4, 4, 2, 6], - [10, 4, 9, 10, 6, 4, 11, 2, 3], - [0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6], - [3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10], - [6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1], - [9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3], - [8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1], - [3, 11, 6, 3, 6, 0, 0, 6, 4], - [6, 4, 8, 11, 6, 8], - [7, 10, 6, 7, 8, 10, 8, 9, 10], - [0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10], - [10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0], - [10, 6, 7, 10, 7, 1, 1, 7, 3], - [1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7], - [2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9], - [7, 8, 0, 7, 0, 6, 6, 0, 2], - [7, 3, 2, 6, 7, 2], - [2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7], - [2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7], - [1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11], - [11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1], - [8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6], - [0, 9, 1, 11, 6, 7], - [7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0], - [7, 11, 6], - [7, 6, 11], - [3, 0, 8, 11, 7, 6], - [0, 1, 9, 11, 7, 6], - [8, 1, 9, 8, 3, 1, 11, 7, 6], - [10, 1, 2, 6, 11, 7], - [1, 2, 10, 3, 0, 8, 6, 11, 7], - [2, 9, 0, 2, 10, 9, 6, 11, 7], - [6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8], - [7, 2, 3, 6, 2, 7], - [7, 0, 8, 7, 6, 0, 6, 2, 0], - [2, 7, 6, 2, 3, 7, 0, 1, 9], - [1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6], - [10, 7, 6, 10, 1, 7, 1, 3, 7], - [10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8], - [0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7], - [7, 6, 10, 7, 10, 8, 8, 10, 9], - [6, 8, 4, 11, 8, 6], - [3, 6, 11, 3, 0, 6, 0, 4, 6], - [8, 6, 11, 8, 4, 6, 9, 0, 1], - [9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6], - [6, 8, 4, 6, 11, 8, 2, 10, 1], - [1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6], - [4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9], - [10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3], - [8, 2, 3, 8, 4, 2, 4, 6, 2], - [0, 4, 2, 4, 6, 2], - [1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8], - [1, 9, 4, 1, 4, 2, 2, 4, 6], - [8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1], - [10, 1, 0, 10, 0, 6, 6, 0, 4], - [4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3], - [10, 9, 4, 6, 10, 4], - [4, 9, 5, 7, 6, 11], - [0, 8, 3, 4, 9, 5, 11, 7, 6], - [5, 0, 1, 5, 4, 0, 7, 6, 11], - [11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5], - [9, 5, 4, 10, 1, 2, 7, 6, 11], - [6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5], - [7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2], - [3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6], - [7, 2, 3, 7, 6, 2, 5, 4, 9], - [9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7], - [3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0], - [6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8], - [9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7], - [1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4], - [4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10], - [7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10], - [6, 9, 5, 6, 11, 9, 11, 8, 9], - [3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5], - [0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11], - [6, 11, 3, 6, 3, 5, 5, 3, 1], - [1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6], - [0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10], - [11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5], - [6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3], - [5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2], - [9, 5, 6, 9, 6, 0, 0, 6, 2], - [1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8], - [1, 5, 6, 2, 1, 6], - [1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6], - [10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0], - [0, 3, 8, 5, 6, 10], - [10, 5, 6], - [11, 5, 10, 7, 5, 11], - [11, 5, 10, 11, 7, 5, 8, 3, 0], - [5, 11, 7, 5, 10, 11, 1, 9, 0], - [10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1], - [11, 1, 2, 11, 7, 1, 7, 5, 1], - [0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11], - [9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7], - [7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2], - [2, 5, 10, 2, 3, 5, 3, 7, 5], - [8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5], - [9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2], - [9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2], - [1, 3, 5, 3, 7, 5], - [0, 8, 7, 0, 7, 1, 1, 7, 5], - [9, 0, 3, 9, 3, 5, 5, 3, 7], - [9, 8, 7, 5, 9, 7], - [5, 8, 4, 5, 10, 8, 10, 11, 8], - [5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0], - [0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5], - [10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4], - [2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8], - [0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11], - [0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5], - [9, 4, 5, 2, 11, 3], - [2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4], - [5, 10, 2, 5, 2, 4, 4, 2, 0], - [3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9], - [5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2], - [8, 4, 5, 8, 5, 3, 3, 5, 1], - [0, 4, 5, 1, 0, 5], - [8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5], - [9, 4, 5], - [4, 11, 7, 4, 9, 11, 9, 10, 11], - [0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11], - [1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11], - [3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4], - [4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2], - [9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3], - [11, 7, 4, 11, 4, 2, 2, 4, 0], - [11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4], - [2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9], - [9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7], - [3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10], - [1, 10, 2, 8, 7, 4], - [4, 9, 1, 4, 1, 7, 7, 1, 3], - [4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1], - [4, 0, 3, 7, 4, 3], - [4, 8, 7], - [9, 10, 8, 10, 11, 8], - [3, 0, 9, 3, 9, 11, 11, 9, 10], - [0, 1, 10, 0, 10, 8, 8, 10, 11], - [3, 1, 10, 11, 3, 10], - [1, 2, 11, 1, 11, 9, 9, 11, 8], - [3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9], - [0, 2, 11, 8, 0, 11], - [3, 2, 11], - [2, 3, 8, 2, 8, 10, 10, 8, 9], - [9, 10, 2, 0, 9, 2], - [2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8], - [1, 10, 2], - [1, 3, 8, 9, 1, 8], - [0, 9, 1], - [0, 3, 8], - [] - ] - - ## translation between edge index and - ## the vertex indexes that bound the edge - edgeKey = [ - [(0,0,0), (1,0,0)], - [(1,0,0), (1,1,0)], - [(1,1,0), (0,1,0)], - [(0,1,0), (0,0,0)], - [(0,0,1), (1,0,1)], - [(1,0,1), (1,1,1)], - [(1,1,1), (0,1,1)], - [(0,1,1), (0,0,1)], - [(0,0,0), (0,0,1)], - [(1,0,0), (1,0,1)], - [(1,1,0), (1,1,1)], - [(0,1,0), (0,1,1)], - ] - - - - facets = [] ## mark everything below the isosurface level mask = data < level @@ -1494,35 +1517,93 @@ def isosurface(data, level): for k in [0,1]: fields[i,j,k] = mask[slices[i], slices[j], slices[k]] vertIndex = i - 2*j*i + 3*j + 4*k ## this is just to match Bourk's vertex numbering scheme - #print i,j,k," : ", fields[i,j,k], 2**vertIndex index += fields[i,j,k] * 2**vertIndex - #print index - #print index - ## add facets - for i in range(index.shape[0]): # data x-axis - for j in range(index.shape[1]): # data y-axis - for k in range(index.shape[2]): # data z-axis - tris = triTable[index[i,j,k]] - for l in range(0, len(tris), 3): ## faces for this grid cell - edges = tris[l:l+3] - pts = [] - for m in [0,1,2]: # points in this face - p1 = edgeKey[edges[m]][0] - p2 = edgeKey[edges[m]][1] - v1 = data[i+p1[0], j+p1[1], k+p1[2]] - v2 = data[i+p2[0], j+p2[1], k+p2[2]] - f = (level-v1) / (v2-v1) - fi = 1.0 - f - p = ( ## interpolate between corners - p1[0]*fi + p2[0]*f + i + 0.5, - p1[1]*fi + p2[1]*f + j + 0.5, - p1[2]*fi + p2[2]*f + k + 0.5 - ) - pts.append(p) - facets.append(pts) + ### Generate table of edges that have been cut + cutEdges = np.zeros([x+1 for x in index.shape]+[3], dtype=np.uint32) + edges = edgeTable[index] + for i, shift in enumerate(edgeShifts[:12]): + slices = [slice(shift[j],cutEdges.shape[j]+(shift[j]-1)) for j in range(3)] + cutEdges[slices[0], slices[1], slices[2], shift[3]] += edges & 2**i + + ## for each cut edge, interpolate to see where exactly the edge is cut and generate vertex positions + m = cutEdges > 0 + vertexInds = np.argwhere(m) ## argwhere is slow! + vertexes = vertexInds[:,:3].astype(np.float32) + dataFlat = data.reshape(data.shape[0]*data.shape[1]*data.shape[2]) + + ## re-use the cutEdges array as a lookup table for vertex IDs + cutEdges[vertexInds[:,0], vertexInds[:,1], vertexInds[:,2], vertexInds[:,3]] = np.arange(vertexInds.shape[0]) + + for i in [0,1,2]: + vim = vertexInds[:,3] == i + vi = vertexInds[vim, :3] + viFlat = (vi * (np.array(data.strides[:3]) / data.itemsize)[np.newaxis,:]).sum(axis=1) + v1 = dataFlat[viFlat] + v2 = dataFlat[viFlat + data.strides[i]/data.itemsize] + vertexes[vim,i] += (level-v1) / (v2-v1) + + ### compute the set of vertex indexes for each face. + + ## This works, but runs a bit slower. + #cells = np.argwhere((index != 0) & (index != 255)) ## all cells with at least one face + #cellInds = index[cells[:,0], cells[:,1], cells[:,2]] + #verts = faceTable[cellInds] + #mask = verts[...,0,0] != 9 + #verts[...,:3] += cells[:,np.newaxis,np.newaxis,:] ## we now have indexes into cutEdges + #verts = verts[mask] + #faces = cutEdges[verts[...,0], verts[...,1], verts[...,2], verts[...,3]] ## and these are the vertex indexes we want. + + + ## To allow this to be vectorized efficiently, we count the number of faces in each + ## grid cell and handle each group of cells with the same number together. + ## determine how many faces to assign to each grid cell + nFaces = nTableFaces[index] + totFaces = nFaces.sum() + faces = np.empty((totFaces, 3), dtype=np.uint32) + ptr = 0 + #import debug + #p = debug.Profiler('isosurface', disabled=False) + + ## this helps speed up an indexing operation later on + cs = np.array(cutEdges.strides)/cutEdges.itemsize + cutEdges = cutEdges.flatten() - return np.array(facets) + ## this, strangely, does not seem to help. + #ins = np.array(index.strides)/index.itemsize + #index = index.flatten() + + for i in range(1,6): + ### expensive: + #p.mark('1') + cells = np.argwhere(nFaces == i) ## all cells which require i faces (argwhere is expensive) + #p.mark('2') + if cells.shape[0] == 0: + continue + #cellInds = index[(cells*ins[np.newaxis,:]).sum(axis=1)] + cellInds = index[cells[:,0], cells[:,1], cells[:,2]] ## index values of cells to process for this round + #p.mark('3') + + ### expensive: + verts = faceShiftTables[i][cellInds] + #p.mark('4') + verts[...,:3] += cells[:,np.newaxis,np.newaxis,:] ## we now have indexes into cutEdges + verts = verts.reshape((verts.shape[0]*i,)+verts.shape[2:]) + #p.mark('5') + + ### expensive: + #print verts.shape + verts = (verts * cs[np.newaxis, np.newaxis, :]).sum(axis=2) + #vertInds = cutEdges[verts[...,0], verts[...,1], verts[...,2], verts[...,3]] ## and these are the vertex indexes we want. + vertInds = cutEdges[verts] + #p.mark('6') + nv = vertInds.shape[0] + #p.mark('7') + faces[ptr:ptr+nv] = vertInds #.reshape((nv, 3)) + #p.mark('8') + ptr += nv + + return vertexes, faces diff --git a/graphicsItems/ScatterPlotItem.py b/graphicsItems/ScatterPlotItem.py index f0dcfb60..155330f1 100644 --- a/graphicsItems/ScatterPlotItem.py +++ b/graphicsItems/ScatterPlotItem.py @@ -233,7 +233,7 @@ class ScatterPlotItem(GraphicsObject): self.bounds = [None, None] ## caches data bounds self._maxSpotWidth = 0 ## maximum size of the scale-variant portion of all spots self._maxSpotPxWidth = 0 ## maximum size of the scale-invariant portion of all spots - self.opts = {'pxMode': True, 'useCache': True} ## If useCache is False, symbols are re-drawn on every paint. + self.opts = {'pxMode': True, 'useCache': True, 'exportMode': False} ## If useCache is False, symbols are re-drawn on every paint. self.setPen(200,200,200, update=False) self.setBrush(100,100,150, update=False) @@ -664,10 +664,14 @@ class ScatterPlotItem(GraphicsObject): rect = QtCore.QRectF(y, x, h, w) self.fragments.append(QtGui.QPainter.PixmapFragment.create(pos, rect)) + def setExportMode(self, enabled, opts): + self.opts['exportMode'] = enabled + + def paint(self, p, *args): #p.setPen(fn.mkPen('r')) #p.drawRect(self.boundingRect()) - if self.opts['pxMode']: + if self.opts['pxMode'] is True: atlas = self.fragmentAtlas.getAtlas() #arr = fn.imageToArray(atlas.toImage(), copy=True) #if hasattr(self, 'lastAtlas'): @@ -681,7 +685,7 @@ class ScatterPlotItem(GraphicsObject): p.resetTransform() - if not USE_PYSIDE and self.opts['useCache']: + if not USE_PYSIDE and self.opts['useCache'] and self.opts['exportMode'] is False: p.drawPixmapFragments(self.fragments, atlas) else: for i in range(len(self.data)):