├── .gitignore ├── LICENSE ├── README.md └── myvi ├── __init__.py ├── canvas3d.py ├── data ├── dem.jpg ├── vessel-0000.png ├── vessel-0001.png ├── vessel-0002.png ├── vessel-0003.png ├── vessel-0004.png ├── vessel-0005.png ├── vessel-0006.png ├── vessel-0007.png ├── vessel-0008.png ├── vessel-0009.png ├── vessel-0010.png ├── vessel-0011.png ├── vessel-0012.png ├── vessel-0013.png ├── vessel-0014.png ├── vessel-0015.png ├── vessel-0016.png ├── vessel-0017.png ├── vessel-0018.png ├── vessel-0019.png ├── vessel-0020.png ├── vessel-0021.png ├── vessel-0022.png ├── vessel-0023.png ├── vessel-0024.png ├── vessel-0025.png ├── vessel-0026.png ├── vessel-0027.png ├── vessel-0028.png ├── vessel-0029.png ├── vessel-0030.png ├── vessel-0031.png ├── vessel-0032.png ├── vessel-0033.png ├── vessel-0034.png ├── vessel-0035.png ├── vessel-0036.png ├── vessel-0037.png ├── vessel-0038.png ├── vessel-0039.png ├── vessel-0040.png ├── vessel-0041.png ├── vessel-0042.png ├── vessel-0043.png ├── vessel-0044.png ├── vessel-0045.png ├── vessel-0046.png ├── vessel-0047.png ├── vessel-0048.png ├── vessel-0049.png ├── vessel-0050.png ├── vessel-0051.png ├── vessel-0052.png ├── vessel-0053.png ├── vessel-0054.png ├── vessel-0055.png ├── vessel-0056.png ├── vessel-0057.png ├── vessel-0058.png ├── vessel-0059.png ├── vessel-0060.png ├── vessel-0061.png ├── vessel-0062.png └── vessel-0063.png ├── frame3d.py ├── imgs ├── configure.png ├── isometric.png ├── logo.ico ├── open.png ├── parallel.png ├── save.png ├── stl.png ├── x-axis.png ├── y-axis.png └── z-axis.png ├── manager.py ├── test.py ├── txtmark.py └── util.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Yan xiaolong 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Myvi 2 | ====================== 3 | Myvi is a 3D visualization tool, *the name comes from [mayavi](http://code.enthought.com/projects/mayavi/) (Myvi is a lighter one, it also means mine)*, Myvi is not as powerful as Mayavi, but to do some simple work it is enough, what is more, mayavi has a heavy dependence, *vtk, traits, chaco...*, it is difficult to install, has many historical burdens, and did not support wxpython-phoenix. However, myvi just needs ModernGL, supports wxpython-phoenix and you can use Myvi's Manager with any UI Framework (such as QT) easily. 4 | 5 | ## Tutorial 6 | ### A sigle ball 7 | ```python 8 | # give position, r, and color 9 | vts, fs, ns, cs = myvi.build_ball((100,100,100), 50, (1,0,0)) 10 | 11 | manager = myvi.Manager() 12 | manager.add_surf('balls', vts, fs, ns, cs) 13 | manager.show('Ball Demo') 14 | ``` 15 | ![](http://myvi.imagepy.org/imgs/ball.jpg "ball") 16 | 17 | ### Random balls with random r and color 18 | ```python 19 | os = np.random.rand(30).reshape((-1,3)) 20 | rs = np.random.rand(10)/5 21 | cs = (np.random.rand(10)*255).astype(np.uint8) 22 | cs = myvi.linear_color('jet')[cs]/255 23 | 24 | vts, fs, ns, cs = myvi.build_balls(os, rs, cs) 25 | manager = myvi.Manager() 26 | manager.add_surf('balls', vts, fs, ns, cs) 27 | manager.show('Random Balls Demo') 28 | ``` 29 | ![](http://myvi.imagepy.org/imgs/balls.jpg "balls") 30 | 31 | ### Random balls with text mark 32 | ```python 33 | os = np.random.rand(30).reshape((-1,3)) 34 | rs = np.random.rand(10)/7 35 | cs = (np.random.rand(10)*255).astype(np.uint8) 36 | cs = myvi.linear_color('jet')[cs]/255 37 | 38 | vts_b, fs_b, ns_b, cs_b = myvi.build_balls(os, rs, cs) 39 | cont = ['ID:%s'%i for i in range(10)] 40 | vtss, fss, pps, h, color = myvi.build_marks(cont, os, rs, 0.05, (1,1,1)) 41 | manager = myvi.Manager() 42 | manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) 43 | line = manager.add_mark('line', vtss, fss, pps, h, color) 44 | line.set_style(mode='grid') 45 | manager.show('Balls Mark Demo') 46 | ``` 47 | ![](http://myvi.imagepy.org/imgs/mark.jpg "balls") 48 | 49 | ### Lines 50 | ```python 51 | vts = np.array([(0,0,0),(1,1,0),(2,1,0),(1,0,0)], dtype=np.float32) 52 | fs = np.array([(0,1,2),(1,2,3)], dtype=np.uint32) 53 | ns = np.ones((4,3), dtype=np.float32) 54 | 55 | n_mer, n_long = 6, 11 56 | pi = np.pi 57 | dphi = pi / 1000.0 58 | phi = np.arange(0.0, 2 * pi + 0.5 * dphi, dphi) 59 | mu = phi * n_mer 60 | x = np.cos(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) 61 | y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) 62 | z = np.sin(n_long * mu / n_mer) * 0.5 63 | 64 | vts, fs, ns, cs = myvi.build_line(x, y, z, (1, 0, 0)) 65 | cs[:] = myvi.auto_lookup(vts[:,2], myvi.linear_color('jet'))/255 66 | 67 | manager = myvi.Manager() 68 | obj = manager.add_surf('line', vts, fs, ns, cs) 69 | obj.set_style(mode='grid') 70 | manager.show('Line Rings') 71 | ``` 72 | ![](http://myvi.imagepy.org/imgs/line.jpg "line") 73 | 74 | ### Balls and Lines 75 | ```python 76 | os = np.random.rand(30).reshape((-1,3)) 77 | rs = np.random.rand(10)/7 78 | cs = (np.random.rand(10)*255).astype(np.uint8) 79 | cs = myvi.linear_color('jet')[cs]/255 80 | 81 | vts_b, fs_b, ns_b, cs_b = myvi.build_balls(list(os), list(rs), list(cs)) 82 | vts_l, fs_l, ns_l, cs_l = myvi.build_line(os[:,0], os[:,1], os[:,2], list(cs)) 83 | 84 | manager = myvi.Manager() 85 | manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) 86 | line = manager.add_surf('line', vts_l, fs_l, ns_l, cs_l) 87 | line.set_style(mode='grid') 88 | manager.show('Balls Ring Demo') 89 | ``` 90 | ![](http://myvi.imagepy.org/imgs/ball_ring.jpg "line") 91 | 92 | ### grid and mesh 93 | ```python 94 | dphi, dtheta = np.pi/20.0, np.pi/20.0 95 | [phi,theta] = np.mgrid[0:np.pi+dphi*1.5:dphi,0:2*np.pi+dtheta*1.5:dtheta] 96 | m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4; 97 | r = np.sin(m0*phi)**m1 + np.cos(m2*phi)**m3 + np.sin(m4*theta)**m5 + np.cos(m6*theta)**m7 98 | x = r*np.sin(phi)*np.cos(theta) 99 | y = r*np.cos(phi) 100 | z = r*np.sin(phi)*np.sin(theta) 101 | vts, fs, ns, cs = myvi.build_mesh(x, y, z) 102 | cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 103 | 104 | manager = myvi.Manager() 105 | obj = manager.add_surf('mesh', vts, fs, ns, cs) 106 | obj.set_style(mode='grid') 107 | manager.show('Mesh Demo') 108 | ``` 109 | ![](http://myvi.imagepy.org/imgs/grid.jpg "grid") 110 | #### set the draw mode and color on the viewer interactivily. 111 | ![](http://myvi.imagepy.org/imgs/mesh.jpg "mesh") 112 | 113 | ### Digital Elevation Model 114 | ```python 115 | img = imread('data/dem.png') 116 | vts, fs, ns, cs = myvi.util.build_surf2d(img, ds=1, k=0.3, sigma=2) 117 | 118 | manager = myvi.Manager() 119 | manager.add_surf('dem', vts, fs, ns, cs) 120 | manager.show('DEM Demo') 121 | ``` 122 | ![](http://myvi.imagepy.org/imgs/dem.jpg "dem") 123 | 124 | ### Surface from image sequence 125 | ```python 126 | fs = glob('data/vessel*.png') 127 | imgs = np.array([imread(i, True) for i in fs]) 128 | imgs = ndimg.gaussian_filter(imgs, 1) 129 | vts, fs, ns, vs = myvi.util.build_surf3d(imgs, 1, 128) 130 | 131 | manager = myvi.Manager() 132 | manager.add_surf('vessel', vts, fs, ns, (1,0,0)) 133 | manager.show('Vessel Demo') 134 | ``` 135 | ![](http://myvi.imagepy.org/imgs/vessel.jpg "vessel") 136 | 137 | ### Embed in your ui program 138 | myvi.Viewer3D is a wxpanel, which contains a manager, implements rendering and is interactive. You can just put the viewer into your program, then you can add objects in. 139 | 140 | ```python 141 | class YourFrame(wx.Frame): 142 | def __init__(self, parent, title='Frame3D', manager=None): 143 | wx.Frame.__init__(...) 144 | ... 145 | self.viewer = Viewer3D(self) 146 | self.viewer.add_surf('name', vts, fs, ns, cs) 147 | ... 148 | ``` 149 | 150 | ## Documents 151 | There are 4 modules in myvi: 152 | * **util:** help to generate the geometry and colors 153 | * **manager:** manage the render object 154 | * **canvas3d:** a wx.GLCanvas panel, and a viewer3d panel 155 | * **frame3d:** a simple Frame to wrap the viewer. 156 | 157 | You can access the function by the module (**myvi.util.build_surf2d**), and you can also use myvi to access every function (**myvi.build_surf2d**). 158 | ### util: 159 | Utilities help to generate geometry and colors. Every build function returns `vts, fs, ns, cs` which can then be added in the manager. 160 | 161 | **def build_surf2d(img, ds=1, sigma=0, k=0.2):** 162 | > **img:** M x N ndarray of uint8 163 | > 164 | > **ds:** how many pixel one sample 165 | > 166 | > **sigma:** do a gaussian blur to smooth 167 | > 168 | > **k:** scale on z axis 169 | 170 | > **return:** vts, fs, ns, cs 171 | 172 | **build_surf3d(imgs, ds, level, step=1, c=(1,0,0)):** 173 | > **imgs:** M x N x K ndarray of uint8 174 | > 175 | > **ds:** down sample 176 | > 177 | > **level:** which value to march 178 | > 179 | > **step:** how many pixel one step when marching 180 | > 181 | > **c:** the color 182 | 183 | > **return:** vts, fs, ns, cs 184 | 185 | **build_ball(o, r, c=(1,0,0)):** 186 | > **o:** center of ball 187 | > 188 | > **r:** r of ball 189 | > 190 | > **color:** color of ball 191 | 192 | > **return:** vts, fs, ns, cs 193 | 194 | **build_balls(os, rs, cs=(1,0,0)):** 195 | > 196 | > **os:** centers of balls 197 | > 198 | > **rs:** rs of balls 199 | > 200 | > **cs:** color of balls, can be a rgb tuple or a sequence like vts 201 | 202 | > **return:** vts, fs, ns, cs 203 | 204 | **build_mark(cont, pos, dz, h, color):** 205 | > 206 | > **cont:** the text (only support '0-9' and 'ID:') 207 | > 208 | > **pos:** center of mark 209 | > 210 | > **dz:** offset forward eye 211 | > 212 | > **h:** height of text 213 | > 214 | > **color:** color of text 215 | 216 | > **return:** vts, fs, pos, h, color 217 | 218 | **build_marks(cont, pos, dz, h, color):** 219 | > 220 | > **cont:** the text s(only support '0-9' and 'ID:') 221 | > 222 | > **pos:** centers of mark 223 | > 224 | > **dz:** offsets forward eye 225 | > 226 | > **h:** heights of text 227 | > 228 | > **color:** colors of text 229 | 230 | > **return:** vts, fs, pos, hs, colors 231 | 232 | **build_line(xs, ys, zs, c):** 233 | > 234 | > **xs:** x coordinates of line 235 | > 236 | > **ys:** y coordinates of line 237 | > 238 | > **zs:** z coordinates of line 239 | > 240 | > **color:** color of line, can be a rgb tuple or a sequence like vts 241 | 242 | > **return:** vts, fs, ns, cs 243 | 244 | **build_lines(xs, ys, zs, cs):** 245 | > 246 | > **xs:** xs coordinates of lines 247 | > 248 | > **ys:** ys coordinates of lines 249 | > 250 | > **zs:** zs coordinates of lines 251 | > 252 | > **color:** color of lines, can be a rgb tuple or a sequence like vts 253 | 254 | > **return:** vts, fs, ns, cs 255 | 256 | **build_mesh(xs, ys, zs, c=(1,0,0)):** 257 | > 258 | > **xs:** x coordinates of mesh 259 | > 260 | > **ys:** y coordinates of mesh 261 | > 262 | > **zs:** z coordinates of mesh 263 | > 264 | > **color:** color of lines, can be a rgb tuple or a sequence like vts 265 | 266 | > **return:** vts, fs, ns, cs 267 | 268 | **linear_color(cs):** 269 | > 270 | > **cs:** list of colors 271 | 272 | > **return:** color 273 | 274 | **auto_lookup(vs, cmap):** 275 | > 276 | > **vs:** value of point 277 | > 278 | > **cmap:** color map 279 | 280 | > **return:** color of every point 281 | --- 282 | ### Surface: 283 | Surface is a geometry object. 284 | 285 | **__init__(self, vts, fs, ns, cs=(0,0,1)):** 286 | 287 | > **vts:** vertex, ndarray of N x 3 288 | > 289 | > **fs:** faces index of vertex, ndarray of N x 3 290 | > 291 | > **ns:** normal vector of every 292 | > 293 | > **cs:** colors, can be a rgb tuple or a sequence like vts 294 | > 295 | **set_style(self, mode=None, blend=None, color=None, visible=None):** 296 | 297 | > **mode:** set the render mode of object, *grid* or *mesh* 298 | > 299 | > **blend:** set the blend of object 300 | > 301 | > **color:** set the color of object, can be a rgb tuple or a sequence like xs 302 | > 303 | > **visible:** set the visible of object, bool 304 | 305 | --- 306 | ### Manager: 307 | Manage the objects, and their boundbox, background color, mvp matrix, etc. 308 | 309 | **add_surf(self, name, vts, fs, ns=None, cs=(0,0,1)):** 310 | > 311 | > **name:** object's name, you can use get_obj to find it later. 312 | > 313 | > **vts:** vertex, ndarray of N x 3 314 | > 315 | > **fs:** faces index of vertex, ndarray of N x 3 316 | > 317 | > **ns:** normal vector of every 318 | > 319 | > **cs:** colors, can be a rgb tuple or a sequence like vts 320 | 321 | > **return:** the Surface object 322 | 323 | **add_mark(self, name, vts, fs, o, h, cs=(0,0,1)):** 324 | > 325 | > **name:** object's name, you can use get_obj to find it later. 326 | > 327 | > **vts:** vertex, ndarray of N x 3 328 | > 329 | > **fs:** faces index of vertex, ndarray of N x 3 330 | > 331 | > **o:** positions of mark 332 | > 333 | > **h:** height of mark 334 | > 335 | > **cs:** color of mark 336 | 337 | > **return:** the MarkText object 338 | 339 | **get_obj(self, name):** 340 | > 341 | > **name:** find the object by name, return None if not found 342 | 343 | **show(self, title='Myvi'):** 344 | 345 | Show a window when use manager to wrote a demo, just like matplotlib's plt.show(), when you embed Viewer3D in your Frame, you do not need to call it. 346 | > **title:** the title of the frame 347 | 348 | *The functions below, you do not need to call directly when using myvi as a api, unless you want to control it yourself* 349 | 350 | **draw(self):** render the objects 351 | 352 | **count_box(self):** count the boundbox of all objects 353 | 354 | **count_mvp(self):** count the mvp matrix 355 | 356 | **set_viewport(self, x, y, width, height):** set viewport 357 | 358 | **set_background(self, rgb):** set background color 359 | 360 | **reset(self, fovy=45, angx=0, angy=0):** reset the view by given 361 | 362 | **set_pers(self, fovy=None, angx=None, angy=None, l=None, pers=None):** set the perspect matrix 363 | 364 | --- 365 | ### Canvas3D 366 | A wx.GLCanvas object, which to renders the object(s) and has a Manager object. You need not use it directly in general, because you can use Viewer3D, which you can control it easily. 367 | 368 | --- 369 | ### Viewer3D 370 | A wx.Panel, which has a Canvas3D, and has a navigation bar, you can embed it in your Frame where you want. 371 | 372 | **__init__( self, parent, manager=None):** 373 | > **parent:** parent frame 374 | > 375 | > **manager:** if manager is given, viewer's Canvas3D object will use it, else a new empty manager is created. 376 | 377 | **add_surf(self, name, vts, fs, ns, cs, obj=None, mode=None, blend=None, color=None, visible=None):** 378 | > you can create a manager, and add object to it, then use it to create a Viewer3D. But after the viewer is created, you should use viewer's add_surf to add object, this make sure the ui refresh. 379 | > 380 | **add_surf_asyn(self, name, vts, fs, ns, cs, mode=None, blend=None, color=None, visible=None):** 381 | > sometimes, we want to do some processing background (if the data is too large), then you should use add_surf_asyn instead. 382 | 383 | ## About ImagePy 384 | [https://github.com/Image-Py/imagepy](https://github.com/Image-Py/imagepy) 385 | 386 | ImagePy is my opensource image processihng framework. It is the ImageJ of Python, you can wrap any numpy based function esaily. And Myvi is a sub module of ImagePy. You can use Myvi without any code. 387 | 388 | ![](http://myvi.imagepy.org/imgs/imagepy.jpg "vessel") 389 | 390 | ## Bug but I can't currently solve 391 | On some computer it does not look well when the blend is set. -------------------------------------------------------------------------------- /myvi/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | As Mayavi is a little outdated, and not support wxphoenix 3 | So I wrote a simple one, and remove two a, M(a)y(a)vi.self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0)self.color = cs if isinstance(cs, tuple) else (0,0,0) 4 | ''' 5 | from .canvas3d import * 6 | from .frame3d import * 7 | from .manager import * 8 | from .util import * -------------------------------------------------------------------------------- /myvi/canvas3d.py: -------------------------------------------------------------------------------- 1 | import sys, platform 2 | import moderngl 3 | import numpy as np 4 | import wx, math 5 | import wx.glcanvas as glcanvas 6 | from .manager import * 7 | import os.path as osp 8 | from wx.lib.pubsub import pub 9 | from .util import * 10 | 11 | class Canvas3D(glcanvas.GLCanvas): 12 | def __init__(self, parent, manager=None): 13 | attribList = attribs = (glcanvas.WX_GL_CORE_PROFILE, glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24) 14 | glcanvas.GLCanvas.__init__(self, parent, -1, attribList=attribList) 15 | self.init = False 16 | self.context = glcanvas.GLContext(self) 17 | self.SetBackgroundStyle(wx.BG_STYLE_PAINT) 18 | self.manager = self.manager = Manager() if manager is None else manager 19 | self.size = None 20 | 21 | self.SetBackgroundStyle(wx.BG_STYLE_PAINT) 22 | 23 | self.Bind(wx.EVT_SIZE, self.OnSize) 24 | self.Bind(wx.EVT_PAINT, self.OnPaint) 25 | self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) 26 | self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) 27 | self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) 28 | self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) 29 | self.Bind(wx.EVT_MOTION, self.OnMouseMotion) 30 | self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) 31 | self.lastx, self.lasty = None, None 32 | #self.update() 33 | #print('init===========') 34 | 35 | def InitGL(self): 36 | self.manager.on_ctx() 37 | self.DoSetViewport() 38 | self.manager.reset() 39 | 40 | def OnDraw(self): 41 | self.manager.set_viewport(0, 0, self.Size.width, self.Size.height) 42 | #self.manager.count_mvp() 43 | self.manager.draw() 44 | self.SwapBuffers() 45 | 46 | def OnSize(self, event): 47 | wx.CallAfter(self.DoSetViewport) 48 | event.Skip() 49 | 50 | def DoSetViewport(self): 51 | size = self.size = self.GetClientSize() 52 | self.SetCurrent(self.context) 53 | if not self.manager is None and not self.manager.ctx is None: 54 | self.manager.set_viewport(0, 0, self.Size.width, self.Size.height) 55 | 56 | def OnPaint(self, event): 57 | dc = wx.PaintDC(self) 58 | self.SetCurrent(self.context) 59 | #print(self, '=====', self.init) 60 | if not self.init: 61 | self.InitGL() 62 | self.init = True 63 | self.OnDraw() 64 | 65 | def OnMouseDown(self, evt): 66 | self.CaptureMouse() 67 | self.lastx, self.lasty = evt.GetPosition() 68 | 69 | def OnMouseUp(self, evt): 70 | self.ReleaseMouse() 71 | 72 | def OnMouseMotion(self, evt): 73 | self.SetFocus() 74 | if evt.Dragging() and evt.LeftIsDown(): 75 | x, y = evt.GetPosition() 76 | dx, dy = x-self.lastx, y-self.lasty 77 | self.lastx, self.lasty = x, y 78 | angx = self.manager.angx - dx/200 79 | angy = self.manager.angy + dy/200 80 | self.manager.set_pers(angx=angx, angy=angy) 81 | self.Refresh(False) 82 | if evt.Dragging() and evt.RightIsDown(): 83 | light = self.manager.light 84 | x, y = evt.GetPosition() 85 | dx, dy = x-self.lastx, y-self.lasty 86 | self.lastx, self.lasty = x, y 87 | angx, angy = dx/200, dy/200 88 | vx, vy, vz = self.manager.light 89 | ay = math.asin(vz/math.sqrt(vx**2+vy**2+vz**2))-angy 90 | xx = math.cos(angx)*vx - math.sin(angx)*vy 91 | yy = math.sin(angx)*vx + math.cos(angx)*vy 92 | ay = max(min(math.pi/2-1e-4, ay), -math.pi/2+1e-4) 93 | zz, k = math.sin(ay), math.cos(ay)/math.sqrt(vx**2+vy**2) 94 | self.manager.set_light((xx*k, yy*k, zz)) 95 | self.Refresh(False) 96 | 97 | def save_bitmap(self, path): 98 | context = wx.ClientDC( self ) 99 | memory = wx.MemoryDC( ) 100 | x, y = self.ClientSize 101 | bitmap = wx.Bitmap( x, y, -1 ) 102 | memory.SelectObject( bitmap ) 103 | memory.Blit( 0, 0, x, y, context, 0, 0) 104 | memory.SelectObject( wx.NullBitmap) 105 | bitmap.SaveFile( path, wx.BITMAP_TYPE_PNG ) 106 | 107 | def save_stl(self, path): 108 | from stl import mesh 109 | objs = self.manager.objs.values() 110 | vers = [i.vts[i.ids] for i in objs if isinstance(i, Surface)] 111 | vers = np.vstack(vers) 112 | model = mesh.Mesh(np.zeros(vers.shape[0], dtype=mesh.Mesh.dtype)) 113 | model.vectors = vers 114 | model.save(path) 115 | 116 | def OnMouseWheel(self, evt): 117 | k = 0.9 if evt.GetWheelRotation()>0 else 1/0.9 118 | self.manager.set_pers(l=self.manager.l*k) 119 | self.Refresh(False) 120 | #self.update() 121 | 122 | def make_bitmap(bmp): 123 | img = bmp.ConvertToImage() 124 | img.Resize((20, 20), (2, 2)) 125 | return img.ConvertToBitmap() 126 | 127 | class Viewer3D(wx.Panel): 128 | def __init__( self, parent, manager=None): 129 | wx.Panel.__init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.TAB_TRAVERSAL ) 130 | #self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) 131 | sizer = wx.BoxSizer( wx.VERTICAL ) 132 | self.canvas = Canvas3D(self, manager) 133 | self.toolbar = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize) 134 | tsizer = wx.BoxSizer( wx.HORIZONTAL ) 135 | 136 | root = osp.abspath(osp.dirname(__file__)) 137 | 138 | #self.SetIcon(wx.Icon('data/logo.ico', wx.BITMAP_TYPE_ICO)) 139 | 140 | self.btn_x = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/x-axis.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) 141 | tsizer.Add( self.btn_x, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) 142 | self.btn_y = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/y-axis.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) 143 | tsizer.Add( self.btn_y, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) 144 | self.btn_z = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/z-axis.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) 145 | tsizer.Add( self.btn_z, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) 146 | tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) 147 | self.btn_pers = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/isometric.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) 148 | tsizer.Add( self.btn_pers, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) 149 | self.btn_orth = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap( osp.join(root, 'imgs/parallel.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) 150 | tsizer.Add( self.btn_orth, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) 151 | tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) 152 | self.btn_open = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap(osp.join(root, 'imgs/open.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) 153 | tsizer.Add( self.btn_open, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) 154 | self.btn_stl = wx.BitmapButton( self.toolbar, wx.ID_ANY, make_bitmap(wx.Bitmap(osp.join(root, 'imgs/stl.png'), wx.BITMAP_TYPE_ANY )), wx.DefaultPosition, wx.DefaultSize, wx.BU_AUTODRAW ) 155 | tsizer.Add( self.btn_stl, 0, wx.ALIGN_CENTER|wx.ALL, 0 ) 156 | #pan = wx.Panel(self.toolbar, size=(50, 50)) 157 | self.btn_color = wx.ColourPickerCtrl( self.toolbar, wx.ID_ANY, wx.Colour( 128, 128, 128 ), wx.DefaultPosition, [(33, 38), (-1, -1)][platform.system() in ['Windows', 'Linux']], wx.CLRP_DEFAULT_STYLE ) 158 | tsizer.Add( self.btn_color, 0, wx.ALIGN_CENTER|wx.ALL|(0, wx.EXPAND)[platform.system() in ['Windows', 'Linux']], 0 ) 159 | tsizer.Add(wx.StaticLine( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL), 0, wx.ALL|wx.EXPAND, 2 ) 160 | self.cho_light = wx.Choice( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['force light', 'normal light', 'weak light', 'off light'], 0 ) 161 | self.cho_light.SetSelection( 1 ) 162 | tsizer.Add( self.cho_light, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) 163 | self.cho_bg = wx.Choice( self.toolbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, ['force scatter', 'normal scatter', 'weak scatter', 'off scatter'], 0 ) 164 | self.cho_bg.SetSelection( 1 ) 165 | tsizer.Add( self.cho_bg, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) 166 | 167 | self.toolbar.SetSizer( tsizer ) 168 | tsizer.Layout() 169 | 170 | self.settingbar = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL ) 171 | ssizer = wx.BoxSizer( wx.HORIZONTAL ) 172 | 173 | self.m_staticText1 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Object:", wx.DefaultPosition, wx.DefaultSize, 0 ) 174 | self.m_staticText1.Wrap( -1 ) 175 | ssizer.Add( self.m_staticText1, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) 176 | 177 | cho_objChoices = ['None'] 178 | self.cho_obj = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, cho_objChoices, 0 ) 179 | self.cho_obj.SetSelection( 0 ) 180 | ssizer.Add( self.cho_obj, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) 181 | 182 | self.chk_visible = wx.CheckBox( self.settingbar, wx.ID_ANY, u"visible", wx.DefaultPosition, wx.DefaultSize, 0 ) 183 | ssizer.Add( self.chk_visible, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) 184 | 185 | self.col_color = wx.ColourPickerCtrl( self.settingbar, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE ) 186 | ssizer.Add( self.col_color, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) 187 | 188 | self.m_staticText2 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Blend:", wx.DefaultPosition, wx.DefaultSize, 0 ) 189 | self.m_staticText2.Wrap( -1 ) 190 | ssizer.Add( self.m_staticText2, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) 191 | 192 | self.sli_blend = wx.Slider( self.settingbar, wx.ID_ANY, 10, 0, 10, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL ) 193 | ssizer.Add( self.sli_blend, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) 194 | self.settingbar.SetSizer(ssizer) 195 | 196 | self.m_staticText2 = wx.StaticText( self.settingbar, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0 ) 197 | self.m_staticText2.Wrap( -1 ) 198 | ssizer.Add( self.m_staticText2, 0, wx.ALIGN_CENTER|wx.LEFT, 10 ) 199 | 200 | cho_objChoices = ['mesh', 'grid'] 201 | self.cho_mode = wx.Choice( self.settingbar, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, cho_objChoices, 0 ) 202 | self.cho_mode.SetSelection( 0 ) 203 | ssizer.Add( self.cho_mode, 0, wx.ALIGN_CENTER|wx.ALL, 1 ) 204 | 205 | sizer.Add( self.toolbar, 0, wx.EXPAND |wx.ALL, 0 ) 206 | sizer.Add( self.canvas, 1, wx.EXPAND |wx.ALL, 0) 207 | sizer.Add( self.settingbar, 0, wx.EXPAND |wx.ALL, 0 ) 208 | 209 | self.SetSizer( sizer ) 210 | self.Layout() 211 | self.Centre( wx.BOTH ) 212 | 213 | self.btn_x.Bind( wx.EVT_BUTTON, self.view_x) 214 | self.btn_y.Bind( wx.EVT_BUTTON, self.view_y) 215 | self.btn_z.Bind( wx.EVT_BUTTON, self.view_z) 216 | self.btn_open.Bind( wx.EVT_BUTTON, self.on_open) 217 | self.btn_stl.Bind( wx.EVT_BUTTON, self.on_stl) 218 | self.btn_pers.Bind( wx.EVT_BUTTON, lambda evt, f=self.on_pers:f(True)) 219 | self.btn_orth.Bind( wx.EVT_BUTTON, lambda evt, f=self.on_pers:f(False)) 220 | self.btn_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_bgcolor ) 221 | 222 | self.cho_obj.Bind( wx.EVT_CHOICE, self.on_select ) 223 | self.cho_mode.Bind( wx.EVT_CHOICE, self.on_mode ) 224 | self.cho_light.Bind( wx.EVT_CHOICE, self.on_light ) 225 | self.cho_bg.Bind( wx.EVT_CHOICE, self.on_bg ) 226 | self.chk_visible.Bind( wx.EVT_CHECKBOX, self.on_visible) 227 | self.sli_blend.Bind( wx.EVT_SCROLL, self.on_blend ) 228 | self.col_color.Bind( wx.EVT_COLOURPICKER_CHANGED, self.on_color ) 229 | 230 | if manager!=None: self.cho_obj.Set(list(manager.objs.keys())) 231 | pub.subscribe(self.add_surf, 'add_surf') 232 | pub.subscribe(self.add_mark, 'add_mark') 233 | 234 | def view_x(self, evt): 235 | self.canvas.manager.reset(angx=0) 236 | self.canvas.Refresh(False) 237 | 238 | 239 | def view_y(self, evt): 240 | self.canvas.manager.reset(angx=pi/2) 241 | self.canvas.Refresh(False) 242 | 243 | def view_z(self, evt): 244 | self.canvas.manager.reset(angy=pi/2-1e-4) 245 | self.canvas.Refresh(False) 246 | 247 | def on_pers(self, b): 248 | self.canvas.manager.set_pers(pers=b) 249 | self.canvas.Refresh(False) 250 | 251 | def on_bgcolor(self, event): 252 | c = tuple(np.array(event.GetColour()[:3])/255) 253 | self.canvas.manager.set_background(c) 254 | self.canvas.Refresh(False) 255 | 256 | def on_bg(self, event): 257 | scatter = 3 - self.cho_bg.GetSelection() 258 | self.canvas.manager.set_bright_scatter(scatter=scatter/3) 259 | self.canvas.Refresh(False) 260 | 261 | def on_light(self, event): 262 | bright = 3 - self.cho_light.GetSelection() 263 | self.canvas.manager.set_bright_scatter(bright=bright/3) 264 | self.canvas.Refresh(False) 265 | 266 | def on_save(self, evt): 267 | dic = {'open':wx.FD_OPEN, 'save':wx.FD_SAVE} 268 | filt = 'PNG files (*.png)|*.png' 269 | dialog = wx.FileDialog(self, 'Save Picture', '', '', filt, wx.FD_SAVE) 270 | if dialog.ShowModal() == wx.ID_OK: 271 | path = dialog.GetPath() 272 | self.canvas.save_bitmap(path) 273 | dialog.Destroy() 274 | 275 | def on_stl(self, evt): 276 | filt = 'STL files (*.stl)|*.stl' 277 | dialog = wx.FileDialog(self, 'Save STL', '', '', filt, wx.FD_SAVE) 278 | rst = dialog.ShowModal() 279 | if rst == wx.ID_OK: 280 | path = dialog.GetPath() 281 | self.canvas.save_stl(path) 282 | dialog.Destroy() 283 | 284 | def on_open(self, evt): 285 | from stl import mesh 286 | filt = 'STL files (*.stl)|*.stl' 287 | dialog = wx.FileDialog(self, 'Open STL', '', '', filt, wx.FD_OPEN) 288 | rst = dialog.ShowModal() 289 | if rst == wx.ID_OK: 290 | path = dialog.GetPath() 291 | cube = mesh.Mesh.from_file(path) 292 | verts = cube.vectors.reshape((-1,3)).astype(np.float32) 293 | ids = np.arange(len(verts), dtype=np.uint32).reshape((-1,3)) 294 | norms = count_ns(verts, ids) 295 | fp, fn = osp.split(path) 296 | fn, fe = osp.splitext(fn) 297 | self.add_surf_asyn(fn, verts, ids, norms, (1,1,1)) 298 | dialog.Destroy() 299 | 300 | def get_obj(self, name): 301 | return self.canvas.manager.get_obj(name) 302 | 303 | def on_visible(self, evt): 304 | self.curobj.set_style(visible=evt.IsChecked()) 305 | self.canvas.Refresh(False) 306 | 307 | def on_blend(self, evt): 308 | self.curobj.set_style(blend=evt.GetInt()/10.0) 309 | self.canvas.Refresh(False) 310 | 311 | def on_mode(self, evt): 312 | self.curobj.set_style(mode=evt.GetString()) 313 | self.canvas.Refresh(False) 314 | 315 | def on_color(self, evt): 316 | c = tuple(np.array(evt.GetColour()[:3])/255) 317 | self.curobj.set_style(color = c) 318 | self.canvas.Refresh(False) 319 | 320 | def on_select(self, evt): 321 | n = self.cho_obj.GetSelection() 322 | self.curobj = self.get_obj(self.cho_obj.GetString(n)) 323 | self.chk_visible.SetValue(self.curobj.visible) 324 | color = (np.array(self.curobj.color)*255).astype(np.uint8) 325 | self.col_color.SetColour((tuple(color))) 326 | self.sli_blend.SetValue(int(self.curobj.blend*10)) 327 | self.cho_mode.SetSelection(['mesh', 'grid'].index(self.curobj.mode)) 328 | 329 | def add_surf_asyn(self, name, vts, fs, ns, cs, mode=None, blend=None, color=None, visible=None): 330 | wx.CallAfter(pub.sendMessage, 'add_surf', name=name, vts=vts, fs=fs, ns=ns, cs=cs, obj=self, 331 | mode=mode, blend=blend, color=color, visible=visible) 332 | 333 | def add_surf(self, name, vts, fs, ns, cs, obj=None, mode=None, blend=None, color=None, visible=None): 334 | if obj!=None and not obj is self:return 335 | manager = self.canvas.manager 336 | surf = manager.add_surf(name, vts, fs, ns, cs) 337 | 338 | surf.set_style(mode=mode, blend=blend, color=color, visible=visible) 339 | if len(manager.objs)==1: 340 | manager.reset() 341 | self.cho_obj.Append(name) 342 | self.canvas.Refresh(False) 343 | 344 | def add_mark_asyn(self, name, vts, fs, ps, h, cs): 345 | wx.CallAfter(pub.sendMessage, 'add_mark', name=name, vts=vts, fs=fs, ps=ps, h=h, cs=cs) 346 | 347 | def add_mark(self, name, vts, fs, ps, h, cs): 348 | manager = self.canvas.manager 349 | surf = manager.add_mark(name, vts, fs, ps, h, cs) 350 | if len(manager.objs)==1: 351 | manager.reset() 352 | self.cho_obj.Append(name) 353 | self.canvas.Refresh(False) 354 | 355 | if __name__ == '__main__': 356 | app = wx.App(False) 357 | frm = wx.Frame(None, title='GLCanvas Sample') 358 | canvas = Canvas3D(frm) 359 | 360 | frm.Show() 361 | app.MainLoop() -------------------------------------------------------------------------------- /myvi/data/dem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/dem.jpg -------------------------------------------------------------------------------- /myvi/data/vessel-0000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0000.png -------------------------------------------------------------------------------- /myvi/data/vessel-0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0001.png -------------------------------------------------------------------------------- /myvi/data/vessel-0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0002.png -------------------------------------------------------------------------------- /myvi/data/vessel-0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0003.png -------------------------------------------------------------------------------- /myvi/data/vessel-0004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0004.png -------------------------------------------------------------------------------- /myvi/data/vessel-0005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0005.png -------------------------------------------------------------------------------- /myvi/data/vessel-0006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0006.png -------------------------------------------------------------------------------- /myvi/data/vessel-0007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0007.png -------------------------------------------------------------------------------- /myvi/data/vessel-0008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0008.png -------------------------------------------------------------------------------- /myvi/data/vessel-0009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0009.png -------------------------------------------------------------------------------- /myvi/data/vessel-0010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0010.png -------------------------------------------------------------------------------- /myvi/data/vessel-0011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0011.png -------------------------------------------------------------------------------- /myvi/data/vessel-0012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0012.png -------------------------------------------------------------------------------- /myvi/data/vessel-0013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0013.png -------------------------------------------------------------------------------- /myvi/data/vessel-0014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0014.png -------------------------------------------------------------------------------- /myvi/data/vessel-0015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0015.png -------------------------------------------------------------------------------- /myvi/data/vessel-0016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0016.png -------------------------------------------------------------------------------- /myvi/data/vessel-0017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0017.png -------------------------------------------------------------------------------- /myvi/data/vessel-0018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0018.png -------------------------------------------------------------------------------- /myvi/data/vessel-0019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0019.png -------------------------------------------------------------------------------- /myvi/data/vessel-0020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0020.png -------------------------------------------------------------------------------- /myvi/data/vessel-0021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0021.png -------------------------------------------------------------------------------- /myvi/data/vessel-0022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0022.png -------------------------------------------------------------------------------- /myvi/data/vessel-0023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0023.png -------------------------------------------------------------------------------- /myvi/data/vessel-0024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0024.png -------------------------------------------------------------------------------- /myvi/data/vessel-0025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0025.png -------------------------------------------------------------------------------- /myvi/data/vessel-0026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0026.png -------------------------------------------------------------------------------- /myvi/data/vessel-0027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0027.png -------------------------------------------------------------------------------- /myvi/data/vessel-0028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0028.png -------------------------------------------------------------------------------- /myvi/data/vessel-0029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0029.png -------------------------------------------------------------------------------- /myvi/data/vessel-0030.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0030.png -------------------------------------------------------------------------------- /myvi/data/vessel-0031.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0031.png -------------------------------------------------------------------------------- /myvi/data/vessel-0032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0032.png -------------------------------------------------------------------------------- /myvi/data/vessel-0033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0033.png -------------------------------------------------------------------------------- /myvi/data/vessel-0034.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0034.png -------------------------------------------------------------------------------- /myvi/data/vessel-0035.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0035.png -------------------------------------------------------------------------------- /myvi/data/vessel-0036.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0036.png -------------------------------------------------------------------------------- /myvi/data/vessel-0037.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0037.png -------------------------------------------------------------------------------- /myvi/data/vessel-0038.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0038.png -------------------------------------------------------------------------------- /myvi/data/vessel-0039.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0039.png -------------------------------------------------------------------------------- /myvi/data/vessel-0040.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0040.png -------------------------------------------------------------------------------- /myvi/data/vessel-0041.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0041.png -------------------------------------------------------------------------------- /myvi/data/vessel-0042.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0042.png -------------------------------------------------------------------------------- /myvi/data/vessel-0043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0043.png -------------------------------------------------------------------------------- /myvi/data/vessel-0044.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0044.png -------------------------------------------------------------------------------- /myvi/data/vessel-0045.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0045.png -------------------------------------------------------------------------------- /myvi/data/vessel-0046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0046.png -------------------------------------------------------------------------------- /myvi/data/vessel-0047.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0047.png -------------------------------------------------------------------------------- /myvi/data/vessel-0048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0048.png -------------------------------------------------------------------------------- /myvi/data/vessel-0049.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0049.png -------------------------------------------------------------------------------- /myvi/data/vessel-0050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0050.png -------------------------------------------------------------------------------- /myvi/data/vessel-0051.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0051.png -------------------------------------------------------------------------------- /myvi/data/vessel-0052.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0052.png -------------------------------------------------------------------------------- /myvi/data/vessel-0053.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0053.png -------------------------------------------------------------------------------- /myvi/data/vessel-0054.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0054.png -------------------------------------------------------------------------------- /myvi/data/vessel-0055.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0055.png -------------------------------------------------------------------------------- /myvi/data/vessel-0056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0056.png -------------------------------------------------------------------------------- /myvi/data/vessel-0057.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0057.png -------------------------------------------------------------------------------- /myvi/data/vessel-0058.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0058.png -------------------------------------------------------------------------------- /myvi/data/vessel-0059.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0059.png -------------------------------------------------------------------------------- /myvi/data/vessel-0060.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0060.png -------------------------------------------------------------------------------- /myvi/data/vessel-0061.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0061.png -------------------------------------------------------------------------------- /myvi/data/vessel-0062.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0062.png -------------------------------------------------------------------------------- /myvi/data/vessel-0063.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/data/vessel-0063.png -------------------------------------------------------------------------------- /myvi/frame3d.py: -------------------------------------------------------------------------------- 1 | import wx, os 2 | from .canvas3d import Canvas3D 3 | from . import util 4 | from . import canvas3d 5 | import numpy as np 6 | 7 | class Frame3D(wx.Frame): 8 | frms = {} 9 | 10 | def __init__(self, parent, title='Frame3D', manager=None): 11 | wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = title, pos = wx.DefaultPosition, size = wx.Size( 800,600 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) 12 | self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) 13 | sizer = wx.BoxSizer( wx.VERTICAL ) 14 | root = os.path.abspath(os.path.dirname(__file__)) 15 | 16 | self.SetIcon(wx.Icon(os.path.join(root, 'imgs/logo.ico'), wx.BITMAP_TYPE_ICO)) 17 | self.viewer = canvas3d.Viewer3D( self , manager) 18 | sizer.Add( self.viewer, 1, wx.EXPAND |wx.ALL, 0 ) 19 | self.Bind(wx.EVT_CLOSE, self.on_closing) 20 | 21 | self.SetSizer( sizer ) 22 | self.Layout() 23 | 24 | self.Centre( wx.BOTH ) 25 | 26 | @classmethod 27 | def figure(cls, parent, title): 28 | if not title in cls.frms: 29 | cls.frms[title] = Frame3D(parent, title) 30 | cls.frms[title].Show() 31 | # wx.Yield() 32 | return cls.frms[title] 33 | 34 | def on_closing(self, event): 35 | if self.GetTitle() in Frame3D.frms: 36 | Frame3D.frms.pop(self.GetTitle()) 37 | event.Skip() 38 | 39 | if __name__ == '__main__': 40 | app = wx.App(False) 41 | frm = Frame3D(None, title='GLCanvas Sample') 42 | frm.Show() 43 | app.MainLoop() 44 | -------------------------------------------------------------------------------- /myvi/imgs/configure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/configure.png -------------------------------------------------------------------------------- /myvi/imgs/isometric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/isometric.png -------------------------------------------------------------------------------- /myvi/imgs/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/logo.ico -------------------------------------------------------------------------------- /myvi/imgs/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/open.png -------------------------------------------------------------------------------- /myvi/imgs/parallel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/parallel.png -------------------------------------------------------------------------------- /myvi/imgs/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/save.png -------------------------------------------------------------------------------- /myvi/imgs/stl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/stl.png -------------------------------------------------------------------------------- /myvi/imgs/x-axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/x-axis.png -------------------------------------------------------------------------------- /myvi/imgs/y-axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/y-axis.png -------------------------------------------------------------------------------- /myvi/imgs/z-axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Image-Py/myvi/84a46a92e7b64021897fbcb25904e798c14f8c66/myvi/imgs/z-axis.png -------------------------------------------------------------------------------- /myvi/manager.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import numpy as np 3 | import moderngl 4 | from time import time 5 | 6 | from skimage.io import imread 7 | import numpy as np 8 | from math import sin, cos, tan, pi 9 | import scipy.ndimage as nimg 10 | 11 | def look_at(eye, target, up, dtype=None): 12 | forward = (target - eye)/np.linalg.norm(target - eye) 13 | side = (np.cross(forward, up))/np.linalg.norm(np.cross(forward, up)) 14 | up = (np.cross(side, forward)/np.linalg.norm(np.cross(side, forward))) 15 | 16 | return np.array(( 17 | (side[0], up[0], -forward[0], 0.), 18 | (side[1], up[1], -forward[1], 0.), 19 | (side[2], up[2], -forward[2], 0.), 20 | (-np.dot(side, eye), -np.dot(up, eye), np.dot(forward, eye), 1.0) 21 | ), dtype=np.float32) 22 | 23 | def perspective(xmax, ymax, near, far): 24 | left, right = -xmax, xmax 25 | bottom, top = -ymax, ymax 26 | 27 | A = (right + left) / (right - left) 28 | B = (top + bottom) / (top - bottom) 29 | C = -(far + near) / (far - near) 30 | D = -2. * far * near / (far - near) 31 | E = 2. * near / (right - left) 32 | F = 2. * near / (top - bottom) 33 | return np.array(( 34 | ( E, 0., 0., 0.), 35 | ( 0., F, 0., 0.), 36 | ( A, B, C,-1.), 37 | ( 0., 0., D, 0.), 38 | ), dtype=np.float32) 39 | 40 | def orthogonal(xmax, ymax, near, far): 41 | rml = xmax * 2 42 | tmb = ymax * 2 43 | fmn = far - near 44 | 45 | A = 2. / rml 46 | B = 2. / tmb 47 | C = -2. / fmn 48 | Tx = 0 49 | Ty = 0 50 | Tz = -(far + near) / fmn 51 | 52 | return np.array(( 53 | ( A, 0., 0., 0.), 54 | (0., B, 0., 0.), 55 | (0., 0., C, 0.), 56 | (Tx, Ty, Tz, 1.), 57 | ), dtype=np.float32) 58 | 59 | class Surface: 60 | def __init__(self, vts, ids, ns, cs=(0,0,1)): 61 | self.vts, self.ids, self.ns, self.cs = vts, ids, ns, cs 62 | self.box = np.vstack((vts.min(axis=0), vts.max(axis=0))) 63 | self.mode, self.blend, self.visible = 'mesh', 1.0, True 64 | self.color = cs if isinstance(cs, tuple) else (0,0,0) 65 | self.width = 1 66 | 67 | def on_ctx(self, ctx, prog): 68 | self.ctx = ctx 69 | vts, ids, ns, cs = self.vts, self.ids, self.ns, self.cs; 70 | buf = self.buf = np.zeros((len(vts), 9), dtype=np.float32) 71 | buf[:,0:3], buf[:,3:6], buf[:,6:9] = vts, ns, cs 72 | self.vbo = ctx.buffer(buf.tobytes()) 73 | ibo = ctx.buffer(ids.tobytes()) 74 | 75 | content = [(self.vbo, '3f 3f 3f', 'v_vert', 'v_norm', 'v_color')] 76 | self.vao = ctx.vertex_array(prog, content, ibo) 77 | self.prog = prog 78 | 79 | def set_style(self, mode=None, blend=None, color=None, visible=None): 80 | if not mode is None: self.mode = mode 81 | if not blend is None: self.blend=blend 82 | if not visible is None: self.visible=visible 83 | if not color is None: 84 | self.buf[:,6:9] = color 85 | self.vbo.write(self.buf.tobytes()) 86 | self.color = color if isinstance(color, tuple) else (0,0,0) 87 | 88 | def draw(self, mvp, light, bright, scatter): 89 | if not self.visible: return 90 | self.ctx.line_width = self.width 91 | mvp = np.dot(*mvp) 92 | self.prog['Mvp'].write(mvp.astype(np.float32).tobytes()) 93 | self.prog['blend'].value = self.blend 94 | self.prog['scatter'].value = scatter 95 | self.prog['light'].value = tuple(light) 96 | self.prog['bright'].value = bright 97 | self.vao.render({'mesh':moderngl.TRIANGLES, 'grid':moderngl.LINES}[self.mode]) 98 | 99 | class MarkText: 100 | def __init__(self, vts, ids, os, h, color): 101 | self.vts, self.ids, self.color, self.os, self.h = vts, ids, color, os, h 102 | self.blend, self.box, self.visible, self.mode = 1, None, True, 'grid' 103 | 104 | def on_ctx(self, ctx, prog): 105 | self.ctx = ctx 106 | vts, ids, os = self.vts, self.ids, self.os 107 | buf = self.buf = np.zeros((len(vts), 6), dtype=np.float32) 108 | buf[:,0:3], buf[:,3:6] = vts, os 109 | self.vbo = ctx.buffer(buf.tobytes()) 110 | ibo = ctx.buffer(ids.tobytes()) 111 | content = [(self.vbo, '3f 3f', 'v_vert', 'v_pos')] 112 | self.vao = ctx.vertex_array(prog, content, ibo) 113 | self.prog = prog 114 | 115 | def set_style(self, mode=None, blend=None, color=None, visible=None): 116 | if not visible is None: self.visible = visible 117 | if not color is None: self.color = color 118 | 119 | def draw(self, mvp, light, bright, scatter): 120 | if not self.visible: return 121 | self.ctx.line_width = 2 122 | self.prog['mv'].write(mvp[0].astype(np.float32).tobytes()) 123 | self.prog['proj'].write(mvp[1].astype(np.float32).tobytes()) 124 | self.prog['f_color'].write(np.array(self.color).astype(np.float32).tobytes()) 125 | self.prog['h'].value = self.h 126 | self.vao.render(moderngl.LINES) 127 | 128 | class Manager: 129 | def __init__(self): 130 | self.h, self.v, self.r = 1.5, 0, 300 131 | self.ratio, self.dial = 1.0, 1.0 132 | self.pers, self.center = True, (0,0,0) 133 | self.background = 0.4, 0.4, 0.4 134 | self.light = (1,0,0) 135 | self.bright, self.scatter = 0.66, 0.66 136 | self.objs = {} 137 | self.ctx = None 138 | 139 | def on_ctx(self): 140 | self.ctx = moderngl.create_context() 141 | self.prog_suf = self.ctx.program( 142 | vertex_shader=''' 143 | #version 330 144 | uniform mat4 Mvp; 145 | in vec3 v_vert; 146 | in vec3 v_norm; 147 | in vec3 v_color; 148 | out vec3 f_norm; 149 | out vec3 f_color; 150 | void main() { 151 | gl_Position = Mvp * vec4(v_vert, 1); 152 | f_norm = v_norm; 153 | f_color = v_color; 154 | } 155 | ''', 156 | fragment_shader=''' 157 | #version 330 158 | uniform vec3 light; 159 | uniform float blend; 160 | uniform float scatter; 161 | uniform float bright; 162 | in vec3 f_norm; 163 | in vec3 f_color; 164 | out vec4 color; 165 | void main() { 166 | float d = clamp(dot(light, f_norm)*bright+scatter, 0, 1); 167 | color = vec4(f_color*d, blend); 168 | } 169 | ''' 170 | ) 171 | 172 | self.prog_txt = self.ctx.program( 173 | vertex_shader=''' 174 | #version 330 175 | uniform mat4 mv; 176 | uniform mat4 proj; 177 | uniform float h; 178 | in vec3 v_vert; 179 | in vec3 v_pos; 180 | void main() { 181 | vec4 o = mv * vec4(v_pos, 1); 182 | gl_Position = proj *(o + vec4(v_vert.x*h, v_vert.y*h, v_vert.z, 0)); 183 | } 184 | ''', 185 | fragment_shader=''' 186 | #version 330 187 | uniform vec3 f_color; 188 | out vec4 color; 189 | void main() { 190 | color = vec4(f_color, 1); 191 | } 192 | ''') 193 | 194 | for i in self.objs.values(): 195 | if isinstance(i, Surface): i.on_ctx(self.ctx, self.prog_suf) 196 | if isinstance(i, MarkText): i.on_ctx(self.ctx, self.prog_txt) 197 | 198 | def add_surf(self, name, vts, ids, ns=None, cs=(0,0,1), real=True): 199 | surf = Surface(vts, ids, ns, cs) 200 | if not real: surf.box = None 201 | if not self.ctx is None: 202 | surf.on_ctx(self.ctx, self.prog_suf) 203 | self.objs[name] = surf 204 | self.count_box() 205 | return surf 206 | 207 | def add_mark(self, name, vts, ids, o, h, cs=(0,0,1)): 208 | mark = MarkText(vts, ids, o, h, cs) 209 | if not self.ctx is None: 210 | mark.on_ctx(self.ctx, self.prog_txt) 211 | self.objs[name] = mark 212 | return mark 213 | 214 | 215 | def get_obj(self, key): 216 | if not key in self.objs: return None 217 | return self.objs[key] 218 | 219 | def draw(self): 220 | self.ctx.clear(*self.background) 221 | self.ctx.enable(moderngl.DEPTH_TEST) 222 | #self.ctx.enable(ModernGL.CULL_FACE) 223 | self.ctx.enable(moderngl.BLEND) 224 | for i in self.objs.values(): i.draw(self.mvp, self.light, self.bright, self.scatter) 225 | 226 | def count_box(self): 227 | minb = np.array([i.box[0] for i in self.objs.values() if not i.box is None]).min(axis=0) 228 | maxb = np.array([i.box[1] for i in self.objs.values() if not i.box is None]).max(axis=0) 229 | self.box = np.vstack((minb, maxb)) 230 | #print(self.box) 231 | self.center = self.box.mean(axis=0) 232 | self.dial = np.linalg.norm(self.box[1]-self.box[0]) 233 | 234 | def count_mvp(self): 235 | #print('mvp') 236 | ymax = (1.0 if self.pers else self.l) * np.tan(self.fovy * np.pi / 360.0) 237 | xmax = ymax * self.ratio 238 | proj = (perspective if self.pers else orthogonal)(xmax, ymax, 1.0, 100000) 239 | lookat = look_at(self.eye, self.center, (0.0,0.0,1.0)) 240 | self.mvp = (lookat, proj) 241 | 242 | def set_viewport(self, x, y, width, height): 243 | self.ctx.viewport = (x, y, width, height) 244 | self.ratio = width*1.0/height 245 | 246 | def set_background(self, rgb): self.background = rgb 247 | 248 | def set_light(self, light): self.light = light 249 | 250 | def set_bright_scatter(self, bright=None, scatter=None): 251 | if not bright is None: self.bright = bright 252 | if not scatter is None: self.scatter = scatter 253 | 254 | def reset(self, fovy=45, angx=0, angy=0): 255 | self.fovy, self.angx, self.angy = fovy, angx, angy 256 | self.l = self.dial/2/(tan(fovy*pi/360)) 257 | v = np.array([cos(angy)*cos(angx), cos(angy)*sin(angx), sin(angy)]) 258 | self.eye = self.center + v*self.l*1 259 | self.count_mvp() 260 | #print('reset', self.eye, self.center) 261 | 262 | def set_pers(self, fovy=None, angx=None, angy=None, l=None, pers=None): 263 | if not pers is None: self.pers = pers 264 | if not fovy is None: self.fovy = fovy 265 | if not angx is None: self.angx = angx 266 | if not angy is None: self.angy = angy 267 | self.angx %= 2*pi 268 | self.angy = max(min(pi/2-1e-4, self.angy), -pi/2+1e-4) 269 | if not l is None: self.l = l 270 | v = np.array([cos(self.angy)*cos(self.angx), 271 | cos(self.angy)*sin(self.angx), sin(self.angy)]) 272 | 273 | self.eye = self.center + v*self.l*1 274 | self.count_mvp() 275 | 276 | def show(self, title='Myvi'): 277 | import wx 278 | from .frame3d import Frame3D 279 | app = wx.App(False) 280 | self.locale = wx.Locale(wx.LANGUAGE_ENGLISH) 281 | Frame3D(None, title, self).Show() 282 | app.MainLoop() 283 | 284 | if __name__ == '__main__': 285 | img = imread('gis.png') 286 | build_surf2d(img) -------------------------------------------------------------------------------- /myvi/test.py: -------------------------------------------------------------------------------- 1 | import sys, wx 2 | from scipy.misc import imread 3 | import scipy.ndimage as ndimg 4 | sys.path.append('..') 5 | import numpy as np 6 | from glob import glob 7 | import myvi 8 | 9 | def dem(): 10 | img = imread('data/dem.jpg') 11 | vts, fs, ns, cs = myvi.util.build_surf2d(img, ds=1, k=0.3, sigma=2) 12 | 13 | manager = myvi.Manager() 14 | manager.add_surf('dem', vts, fs, ns, cs) 15 | manager.show('DEM Demo') 16 | 17 | def volume(): 18 | fs = glob('data/vessel*.png') 19 | imgs = np.array([imread(i, True) for i in fs]) 20 | print() 21 | imgs = ndimg.gaussian_filter(imgs, 1) 22 | vts, fs, ns, vs = myvi.util.build_surf3d(imgs, 1, 80) 23 | 24 | manager = myvi.Manager() 25 | manager.add_surf('vessel', vts, fs, ns, (1,0,0)) 26 | manager.show('Vessel Demo') 27 | 28 | def ball(): 29 | vts, fs, ns, cs = myvi.build_ball((100,100,100),50, (1,0,0)) 30 | manager = myvi.Manager() 31 | manager.add_surf('balls', vts, fs, ns, cs) 32 | manager.show('Ball Demo') 33 | 34 | def random_balls(): 35 | os = np.random.rand(30).reshape((-1,3)) 36 | rs = np.random.rand(10)/5 37 | cs = (np.random.rand(10)*255).astype(np.uint8) 38 | cs = myvi.linear_color('jet')[cs]/255 39 | 40 | vts, fs, ns, cs = myvi.build_balls(os, rs, cs) 41 | manager = myvi.Manager() 42 | manager.add_surf('balls', vts, fs, ns, cs) 43 | manager.show('Random Balls Demo') 44 | 45 | def line(): 46 | vts = np.array([(0,0,0),(1,1,0),(2,1,0),(1,0,0)], dtype=np.float32) 47 | fs = np.array([(0,1,2),(1,2,3)], dtype=np.uint32) 48 | ns = np.ones((4,3), dtype=np.float32) 49 | 50 | n_mer, n_long = 6, 11 51 | pi = np.pi 52 | dphi = pi / 1000.0 53 | phi = np.arange(0.0, 2 * pi + 0.5 * dphi, dphi) 54 | mu = phi * n_mer 55 | x = np.cos(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) 56 | y = np.sin(mu) * (1 + np.cos(n_long * mu / n_mer) * 0.5) 57 | z = np.sin(n_long * mu / n_mer) * 0.5 58 | 59 | vts, fs, ns, cs = myvi.build_line(x, y, z, (1, 0, 0)) 60 | cs[:] = myvi.auto_lookup(vts[:,2], myvi.linear_color('jet'))/255 61 | 62 | manager = myvi.Manager() 63 | obj = manager.add_surf('line', vts, fs, ns, cs) 64 | obj.set_style(mode='grid') 65 | manager.show('Line Rings') 66 | 67 | def mesh(): 68 | dphi, dtheta = np.pi/80.0, np.pi/80.0 69 | [phi,theta] = np.mgrid[0:np.pi+dphi*1.5:dphi,0:2*np.pi+dtheta*1.5:dtheta] 70 | m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4; 71 | r = np.sin(m0*phi)**m1 + np.cos(m2*phi)**m3 + np.sin(m4*theta)**m5 + np.cos(m6*theta)**m7 72 | x = r*np.sin(phi)*np.cos(theta) 73 | y = r*np.cos(phi) 74 | z = r*np.sin(phi)*np.sin(theta) 75 | vts, fs, ns, cs = myvi.build_mesh(x, y, z) 76 | cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 77 | 78 | manager = myvi.Manager() 79 | obj = manager.add_surf('mesh', vts, fs, ns, cs) 80 | obj.set_style(mode='grid') 81 | manager.show('Mesh Demo') 82 | 83 | def ball_ring_box(): 84 | os = np.random.rand(30).reshape((-1,3)) 85 | rs = np.random.rand(10)/7 86 | cs = (np.random.rand(10)*255).astype(np.uint8) 87 | cs = myvi.linear_color('jet')[cs]/255 88 | 89 | vts_b, fs_b, ns_b, cs_b = myvi.build_balls(list(os), list(rs), list(cs)) 90 | vts_l, fs_l, ns_l, cs_l = myvi.build_line(os[:,0], os[:,1], os[:,2], list(cs)) 91 | vts_c, fs_c, ns_c, cs_c = myvi.build_cube((0,0,0), (1,1,1)) 92 | manager = myvi.Manager() 93 | manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) 94 | line = manager.add_surf('line', vts_l, fs_l, ns_l, cs_l) 95 | line.set_style(mode='grid') 96 | box = manager.add_surf('box', vts_c, fs_c, ns_c, cs_c) 97 | box.set_style(mode='grid') 98 | manager.show('Balls Ring Demo') 99 | 100 | def balls_with_mark(): 101 | os = np.random.rand(30).reshape((-1,3)) 102 | rs = np.random.rand(10)/7 103 | cs = (np.random.rand(10)*255).astype(np.uint8) 104 | cs = myvi.linear_color('jet')[cs]/255 105 | 106 | vts_b, fs_b, ns_b, cs_b = myvi.build_balls(os, rs, cs) 107 | cont = ['ID:%s'%i for i in range(10)] 108 | vtss, fss, pps, h, color = myvi.build_marks(cont, os, rs, 0.05, (1,1,1)) 109 | manager = myvi.Manager() 110 | manager.add_surf('balls', vts_b, fs_b, ns_b, cs_b) 111 | line = manager.add_mark('line', vtss, fss, pps, h, color) 112 | line.set_style(mode='grid') 113 | manager.show('Balls Mark Demo') 114 | 115 | def frame_demo(): 116 | app = wx.App(False) 117 | frm = myvi.Frame3D(None, 'Frame') 118 | img = imread('data/dem.jpg') 119 | vts, fs, ns, cs = myvi.util.build_surf2d(img, ds=1, k=0.3, sigma=2) 120 | frm.viewer.add_surf_ansy('dem', vts, fs, ns, cs) 121 | frm.Show() 122 | app.MainLoop() 123 | 124 | def surface2d(): 125 | x, y = np.ogrid[-2:2:20j, -2:2:20j] 126 | z = x * np.exp( - x**2 - y**2) 127 | vts, fs, ns, cs = myvi.util.build_surf2d(z, ds=1, k=20, sigma=2) 128 | cs[:] = myvi.util.auto_lookup(vts[:,2], myvi.util.linear_color('jet'))/255 129 | manager = myvi.Manager() 130 | manager.add_surf('dem', vts, fs, ns, cs) 131 | manager.show('DEM Demo') 132 | 133 | def arrow(): 134 | v1, v2 = np.array([[[0,0,0],[5,5,5]],[[0,15,5],[2,8,3]]], dtype=np.float32) 135 | vts, fs, ns, c = myvi.util.build_arrows(v1, v2, 1, 1, 1, 1, (1,0,0)) 136 | manager = myvi.Manager() 137 | manager.add_surf('dem', vts, fs, ns, c) 138 | manager.show('DEM Demo') 139 | 140 | def cube(): 141 | vts, fs, ns, cs = myvi.build_cube((0,0,0), (1,1,1)) 142 | manager = myvi.Manager() 143 | obj = manager.add_surf('cube', vts, fs, ns, cs) 144 | obj.set_style(mode='grid') 145 | manager.show('Cube Demo') 146 | 147 | def cube_surf(): 148 | from skimage.data import camera 149 | lut = np.zeros((256,3), dtype=np.uint8) 150 | lut[:,0] = np.arange(256) 151 | imgs = np.array([camera()[:300,::]]*256) 152 | ''' 153 | imgs = np.zeros((100,200,300), dtype=np.uint8) 154 | imgs[:30,10:20,50:60] = 255 155 | 156 | vts, fs, ns, vs = myvi.util.build_surf3d(imgs, 1, 128) 157 | manager = myvi.Manager() 158 | manager.add_surf('vessel', vts, fs, ns, (1,0,0)) 159 | ''' 160 | manager = myvi.Manager() 161 | vts, fs, ns, cs = myvi.build_img_cube(imgs) 162 | obj = manager.add_surf('cube', vts, fs, ns, cs) 163 | vts, fs, ns, cs = myvi.build_img_box(imgs) 164 | obj = manager.add_surf('box', vts, fs, ns, cs) 165 | obj.set_style(mode='grid') 166 | 167 | manager.show('Cube Demo') 168 | 169 | 170 | if __name__ == '__main__': 171 | 172 | cube_surf() 173 | ''' 174 | volume() 175 | surface2d() 176 | dem() 177 | volume() 178 | ball() 179 | random_balls() 180 | line() 181 | mesh() 182 | ball_ring_box() 183 | balls_with_mark() 184 | arrow() 185 | ''' -------------------------------------------------------------------------------- /myvi/txtmark.py: -------------------------------------------------------------------------------- 1 | lib = {'0':([(0,0.5,0.5,0,0)],[(1,1,0,0,1)],0.5), 2 | '1':([(0.25,0.25)], [(0,1)], 0.5), 3 | '2':([(0,0.5,0.5,0,0,0.5)], [(1,1,0.5,0.5,0,0)], 0.5), 4 | '3':([(0,0.5,0.5,0),(0,0.5)],[(1,1,0,0),(0.5,0.5)], 0.5), 5 | '4':([(0,0,0.5),(0.5,0.5)],[(1,0.5,0.5),(1,0)],0.5), 6 | '5':([(0.5,0,0,0.5,0.5,0)], [(1,1,0.5,0.5,0,0)], 0.5), 7 | '6':([(0.5,0,0,0.5,0.5,0,0)], [(1,1,0.5,0.5,0,0,0.5)], 0.5), 8 | '7':([(0,0.5,0.5)], [(1,1,0)], 0.5), 9 | '8':([(0.5,0.5,0,0,0.5,0.5,0,0)], [(0.5,1,1,0.5,0.5,0,0,0.5)], 0.5), 10 | '9':([(0.5,0.5,0,0,0.5,0.5,0)], [(0.5,1,1,0.5,0.5,0,0)], 0.5), 11 | 'I':([(0,0.5),(0.25,0.25),(0,0.5)],[(1,1),(1,0),(0,0)],0.5), 12 | 'D':([(0,0.25,0.4,0.5,0.5,0.4,0.25,0),(0.1,0.1)],[(1,1,0.9,0.75,0.25,0.1,0,0),(0,1)],0.5), 13 | ':':([(0.2,0.3),(0.2,0.3)],[(0.75,0.75),(0.25,0.25)],0.5)} -------------------------------------------------------------------------------- /myvi/util.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | import numpy as np 3 | from math import pi 4 | from .txtmark import lib 5 | 6 | def count_ns(vts, fs): 7 | dv1 = vts[fs[:,1]] - vts[fs[:,2]] 8 | dv2 = vts[fs[:,1]] - vts[fs[:,0]] 9 | ns = np.cross(dv1, dv2) 10 | ass = np.linalg.norm(ns, axis=1) 11 | ns /= np.linalg.norm(ns, axis=1).reshape((-1,1)) 12 | buf = np.zeros_like(vts) 13 | for i in (0,1,2): np.add.at(buf, fs[:,i], ns) 14 | buf /= np.linalg.norm(buf, axis=1).reshape((-1,1)) 15 | return buf 16 | 17 | def build_twringidx(n, offset=0): 18 | idx = np.array([[0,1,n+1],[n+1,n+2,1]]) 19 | idx = idx[np.arange(n*2)%2].T + np.arange(n*2)//2 20 | return (idx.T+offset).astype(np.uint32) 21 | 22 | def build_pringidx(p, n, offset=0): 23 | ridx = np.array([[0,0,1]]*n, dtype=np.uint32) 24 | ridx += np.arange(n, dtype=np.uint32).reshape((-1,1))+offset 25 | ridx[:,0] = p 26 | return ridx 27 | 28 | def build_grididx(r, c): 29 | idx = np.arange(r*c, dtype=np.uint32) 30 | rs, cs = idx//c, idx%c 31 | idx1 = idx[(rs0: fs[-rem:] = len(xs)-1 101 | ns = np.ones((len(vts), 3), dtype=np.float32) 102 | cs = (np.ones((len(vts), 3))*c).astype(np.float32) 103 | return vts, fs.reshape((-1,3)), ns, cs 104 | 105 | def build_lines(xs, ys, zs, cs): 106 | if not isinstance(cs, list): 107 | cs = [cs] * len(xs) 108 | vtss, fss, nss, css = [], [], [], [] 109 | s = 0 110 | for x, y, z, c in zip(xs, ys, zs, cs): 111 | vv, ff, nn, cc = build_line(x, y, z, c) 112 | fss.append(ff+s) 113 | s += len(vv) 114 | vtss.append(vv) 115 | nss.append(nn) 116 | css.append(cc) 117 | return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) 118 | 119 | def build_arrow(v1, v2, rs, re, ts, te, c): 120 | v = (v2-v1)/np.linalg.norm(v2-v1) 121 | ss, ee = v1 + v*rs*ts, v2 - v*re*te 122 | vx = np.cross(v, np.random.rand(3)) 123 | vx /= np.linalg.norm(vx) 124 | vy = np.cross(vx, v) 125 | angs = np.linspace(0, np.pi*2, 17) 126 | vas = np.array([np.cos(angs), np.sin(angs)]) 127 | vxy = np.dot(vas.T, np.array([vx, vy])) 128 | vts = np.vstack((v1, ss + rs * vxy, ee + re * vxy, v2)) 129 | fs1 = build_pringidx(0, 16, 1) 130 | fs = build_twringidx(16, 1) 131 | fs2 = build_pringidx(35, 16, 18) 132 | face = np.vstack((fs1, fs, fs2)) 133 | ns = np.vstack((-v, vxy, vxy, v)).astype(np.float32) 134 | cs = (np.ones((len(vts), 3))*c).astype(np.float32) 135 | return vts.astype(np.float32), face, ns, cs 136 | 137 | def build_arrows(v1s, v2s, rss, res, tss, tes, cs): 138 | if not isinstance(cs, list): cs = [cs] * len(v1s) 139 | if not isinstance(tss, list): tss = [tss] * len(v1s) 140 | if not isinstance(tes, list): tes = [tes] * len(v1s) 141 | if not isinstance(rss, list): rss = [rss] * len(v1s) 142 | if not isinstance(res, list): res = [res] * len(v1s) 143 | vtss, fss, nss, css = [], [], [], [] 144 | s = 0 145 | for v1, v2, rs, re, ts, te, c in zip(v1s, v2s, rss, res, tss, tes, cs): 146 | if np.linalg.norm(v1-v2) < 0.1: continue 147 | vv, ff, nn, cc = build_arrow(v1, v2, rs, re, ts, te, c) 148 | fss.append(ff+s) 149 | s += len(vv) 150 | vtss.append(vv) 151 | nss.append(nn) 152 | css.append(cc) 153 | print(np.vstack(vtss).shape, np.vstack(fss).shape, np.vstack(nss).shape, np.vstack(css).shape) 154 | return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) 155 | 156 | def build_mark(cont, pos, dz, h, color): 157 | vts, fss = [], [] 158 | s, sw = 0, 0 159 | for i in cont: 160 | xs, ys, w = lib[i] 161 | vv, ff, nn, cc = build_lines(xs, ys, ys, (0,0,0)) 162 | fss.append(ff+s) 163 | vts.append(vv+[sw,0,0]) 164 | vts[-1][:,2] = dz 165 | s += len(vv) 166 | sw += w+0.3 167 | sw -= 0.3 168 | vts = (np.vstack(vts)-[sw/2.0, 0.5, 0]) 169 | return vts, np.vstack(fss), pos, h, color 170 | 171 | def build_marks(conts, poss, dz, h, color): 172 | if not hasattr(dz, '__len__'): 173 | dz = [dz] * len(conts) 174 | vtss, fss, pps = [], [], [] 175 | s = 0 176 | for cont, pos, z in zip(conts, poss, dz): 177 | vv, ff, pp, hh, cc = build_mark(cont, pos, z, h, color) 178 | fss.append(ff+s) 179 | s += len(vv) 180 | vtss.append(vv) 181 | pps.append((np.ones((len(vv),3))*pp).astype(np.float32)) 182 | 183 | return np.vstack(vtss), np.vstack(fss), np.vstack(pps), h, color 184 | 185 | def build_cube(p1, p2, color=(1,1,1)): 186 | (x1,y1,z1),(x2,y2,z2) = p1, p2 187 | xs = (x1,x2,x2,x1,x1,x1,x1,x1,x1,x2,x2,x1,x2,x2,x2,x2) 188 | ys = (y1,y1,y1,y1,y1,y2,y2,y1,y2,y2,y2,y2,y2,y1,y1,y2) 189 | zs = (z1,z1,z2,z2,z1,z1,z2,z2,z2,z2,z1,z1,z1,z1,z2,z2) 190 | return build_line(xs, ys, zs, color) 191 | 192 | def build_img_cube(imgs, ds=1): 193 | imgs = imgs[::ds,::ds,::ds] 194 | (h, r, c), total = imgs.shape[:3], 0 195 | print(h, r, c) 196 | vtss, fss, nss, css = [], [], [], [] 197 | shp = [(h,r,c,h*r), (h,c,r,h*c), (r,c,h,r*c)] 198 | nn = [[(0,0,-1),(0,0,1)], [(0,1,0),(0,-1,0)], [(1,0,0),(-1,0,0)]] 199 | for i in (0,1,2): 200 | rs, cs, fs12 = build_grididx(*shp[i][:2]) 201 | idx1, idx2 = [rs*ds, cs*ds], [rs*ds, cs*ds] 202 | rcs1, rcs2 = [rs, cs], [rs, cs] 203 | rcs1.insert(2-i, 0); rcs2.insert(2-i, -1) 204 | vs1, vs2 = imgs[tuple(rcs1)]/255, imgs[tuple(rcs2)]/255 205 | idx1.insert(2-i, rs*0); idx2.insert(2-i, cs*0+shp[i][2]*ds-1) 206 | vtss.append(np.array(idx1, dtype=np.float32).T) 207 | vtss.append(np.array(idx2, dtype=np.float32).T) 208 | css.append((np.ones((1, 3))*vs1.reshape((len(vs1),-1))).astype(np.float32)) 209 | css.append((np.ones((1, 3))*vs2.reshape((len(vs1),-1))).astype(np.float32)) 210 | nss.append((np.ones((shp[i][3],1))*[nn[i][0]]).astype(np.float32)) 211 | nss.append((np.ones((shp[i][3],1))*[nn[i][1]]).astype(np.float32)) 212 | fss.extend([fs12+total, fs12+(total+shp[i][0]*shp[i][1])]) 213 | total += shp[i][3] * 2 214 | return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) 215 | 216 | def build_img_box(imgs, color=(1,1,1)): 217 | return build_cube((-1,-1,-1), imgs.shape[:3], color) 218 | 219 | def build_coord(m, cs=np.eye(3)): 220 | data = m.copy().T 221 | data[:3,:3] += data[3,:3] 222 | pts = data[:,:3][[3,0,3,3,1,3,3,2,3]].T 223 | cs = cs[[0,0,0,1,1,1,2,2,2]] 224 | return build_line(pts[0], pts[1], pts[2], cs) 225 | 226 | def build_coords(ms, color=np.eye(3)): 227 | vtss, fss, nss, css = [], [], [], [] 228 | for m in ms: 229 | vts, fs, ns, cs = build_coord(m, color) 230 | vtss.append(vts) 231 | fss.append(fs+len(fss)*9) 232 | nss.append(ns) 233 | css.append(cs) 234 | return np.vstack(vtss), np.vstack(fss), np.vstack(nss), np.vstack(css) 235 | 236 | 237 | cmp = {'rainbow':[(127, 0, 255), (43, 126, 246), (42, 220, 220), (128, 254, 179), (212, 220, 127), (255, 126, 65), (255, 0, 0)], 238 | 'jet':[(0, 0, 127), (0, 40, 255), (0, 212, 255), (124, 255, 121), (255, 229, 0), (255, 70, 0), (127, 0, 0)], 239 | 'ocean':[(0, 127, 0), (0, 64, 42), (0, 0, 85), (0, 64, 128), (0, 127, 170), (129, 192, 213), (255, 255, 255)], 240 | 'earth':[(0, 0, 0), (27, 77, 122), (54, 135, 111), (93, 160, 75), (169, 179, 91), (206, 171, 132), (253, 250, 250)]} 241 | 242 | def linear_color(cs): 243 | if isinstance(cs, str): cs=cmp[cs] 244 | cmap = np.zeros((256, 3), dtype=np.uint8) 245 | idx = np.linspace(0, 256, len(cs)).astype(np.uint16) 246 | for i in range(1, len(cs)): 247 | c1, c2 = cs[i-1], cs[i] 248 | rs, gs, bs = [np.linspace(c1[j], c2[j], idx[i]-idx[i-1]) for j in (0,1,2)] 249 | cmap[idx[i-1]:idx[i]] = np.array((rs, gs, bs)).T 250 | return cmap 251 | 252 | def auto_lookup(vs, cmap): 253 | vs = vs - vs.min() 254 | vs = vs/vs.max() 255 | vs = (vs*255).astype(np.uint8) 256 | return cmap[vs] 257 | 258 | if __name__ == '__main__': 259 | from matplotlib import cm 260 | cmap = linear_color('earth') 261 | import matplotlib.pyplot as plt 262 | img = np.zeros((30,256), dtype=np.uint8) 263 | img[:] = np.arange(256) 264 | img = cmap[img] 265 | plt.imshow(img) 266 | plt.show() --------------------------------------------------------------------------------