├── COPYING ├── README ├── py ├── __pycache__ │ └── myvtk.cpython-39.pyc ├── ascii_stlin_test.py ├── binary_stlin_test.py ├── cone_test.py ├── draw_triangles.py ├── example_stl │ ├── libcutsim_ascii_import.stl │ └── libcutsim_binary_import.stl ├── loadmesh_test.py ├── minimal.py ├── myvtk.py ├── simple_move.py ├── stlout_test.py └── wireframe_test.py └── src ├── CMakeLists.txt ├── bbox.cpp ├── bbox.hpp ├── cube_wireframe.hpp ├── cutsim.cpp ├── cutsim.hpp ├── cutsim_py.cpp ├── facet.hpp ├── fileio.cpp ├── fileio.hpp ├── gldata.cpp ├── gldata.hpp ├── glvertex.hpp ├── isosurface.hpp ├── marching_cubes.cpp ├── marching_cubes.hpp ├── octnode.cpp ├── octnode.hpp ├── octree.cpp ├── octree.hpp ├── volume.cpp └── volume.hpp /README: -------------------------------------------------------------------------------- 1 | 2 | $ git clone git://github.com/aewallin/libcutsim.git 3 | $ cd libcutsim 4 | $ mkdir build 5 | $ cd build 6 | $ cmake ../src 7 | $ make 8 | $ sudo make install 9 | use ! 10 | 11 | check py/ for examples 12 | 13 | see also: http://openscam.com/ 14 | 15 | References 16 | 17 | Sarah F. Frisken, Ronald N. Perry, Alyn P. Rockwood, Thouis R. Jones 18 | Adaptively Sampled Distance Fields: A General Representation of Shape for 19 | Computer Graphics 20 | http://www.merl.com/papers/docs/TR2000-15.pdf 21 | http://www.merl.com/papers/docs/TR2006-054.pdf 22 | 23 | Dual Contouring of Hermite Data 24 | http://www.cs.rice.edu/~jwarren/papers/dualcontour.pdf 25 | 26 | Dual Contouring "The Secret Sauce" 27 | http://www.cs.rice.edu/~jwarren/papers/techreport02408.pdf 28 | 29 | Hong T. Yau, Lee S. Tsou and Yu C. Tong 30 | Adaptive NC Simulation for Multi-axis Solid Machining 31 | http://www.cadanda.com/V2Nos1to4_11.pdf 32 | 33 | Hong-Tzong Yau and Lee-Sen Tsou 34 | Efficient NC Simulation for Multi-Axis Solid Machining With 35 | a Universal APT Cutter 36 | http://computingengineering.asmedigitalcollection.asme.org/article.aspx?articleid=1401485 37 | 38 | -------------------------------------------------------------------------------- /py/__pycache__/myvtk.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aewallin/libcutsim/16fd8046cef7d04e27955a28aa96d7bd3db84db9/py/__pycache__/myvtk.cpython-39.pyc -------------------------------------------------------------------------------- /py/ascii_stlin_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import libcutsim 3 | import myvtk 4 | import math 5 | import time 6 | 7 | # Test importing / loading of STL file 8 | 9 | def main(): 10 | 11 | gl = libcutsim.GLData() # this class holds lines, triangles, or quads for OpenGL drawing 12 | iso = libcutsim.MarchingCubes() # this is the algorithm that produces GLData from the stock-model 13 | cs = libcutsim.Cutsim(200.0, 10, gl, iso) # this is the cutting simulation 14 | cs.init(3) # initialize by subdividing octree n-times 15 | 16 | # define path to the stl file 17 | dir = os.path.dirname(__file__) 18 | file_name = "libcutsim_ascii_import.stl" 19 | folder_name = "example_stl" 20 | file_path = dir + os.sep + folder_name + os.sep + file_name 21 | 22 | print("Importing: ", file_path) 23 | 24 | volume = libcutsim.MeshVolume() # create the MeshVolume to hold the STL data 25 | fileLoaded = volume.loadStl(file_path) # load the stl file into the mesh volume 26 | 27 | if not fileLoaded: 28 | print("Error loading stl file") 29 | return 30 | 31 | cs.sum_volume(volume) 32 | 33 | cutter = libcutsim.SphereVolume() 34 | cutter.setRadius(float(3)) 35 | # move around the cutter and subtract at each point 36 | t_before = time.time() 37 | Nmax = 1000 38 | x = 50 39 | y = -1 40 | for n in range(Nmax): 41 | y = 0.1*n 42 | # print("position:", x, y) 43 | cutter.setCenter(x,y,10.5) 44 | cs.diff_volume(cutter) # subtract the volume from the stock 45 | t_after = time.time() 46 | print(Nmax, " diff() calls took ", t_after-t_before," seconds") 47 | cs.updateGL() 48 | 49 | # create a VTK view for drawing 50 | w=1024 51 | h=1024 52 | myscreen = myvtk.VTKScreen(width=w, height=h) 53 | myvtk.drawTriangles(myscreen, gl.get_triangles()) 54 | myscreen.render() 55 | myscreen.iren.Start() 56 | 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /py/binary_stlin_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import libcutsim 3 | import myvtk 4 | import math 5 | import time 6 | 7 | # Test importing / loading of STL file 8 | 9 | def main(): 10 | 11 | gl = libcutsim.GLData() # this class holds lines, triangles, or quads for OpenGL drawing 12 | iso = libcutsim.MarchingCubes() # this is the algorithm that produces GLData from the stock-model 13 | cs = libcutsim.Cutsim(200.0, 10, gl, iso) # this is the cutting simulation 14 | cs.init(6) # initialize by subdividing octree n-times 15 | 16 | # define path to the stl file 17 | dir = os.path.dirname(__file__) 18 | file_name = "libcutsim_binary_import.stl" 19 | folder_name = "example_stl" 20 | file_path = dir + os.sep + folder_name + os.sep + file_name 21 | 22 | print("Importing: ", file_path) 23 | 24 | volume = libcutsim.MeshVolume() # create the MeshVolume to hold the STL data 25 | fileLoaded = volume.loadStl(file_path) # load the stl file into the mesh volume 26 | 27 | if not fileLoaded: 28 | print("Error loading stl file") 29 | return 30 | 31 | cs.sum_volume(volume) 32 | 33 | cutter = libcutsim.SphereVolume() 34 | cutter.setRadius(float(3)) 35 | # move around the cutter and subtract at each point 36 | t_before = time.time() 37 | Nmax = 1000 38 | x = 50 39 | y = -1 40 | for n in range(Nmax): 41 | y = 0.1*n 42 | # print("position:", x, y) 43 | cutter.setCenter(x,y,10.5) 44 | cs.diff_volume(cutter) # subtract the volume from the stock 45 | t_after = time.time() 46 | print(Nmax, " diff() calls took ", t_after-t_before," seconds") 47 | cs.updateGL() 48 | 49 | # create a VTK view for drawing 50 | w=1024 51 | h=1024 52 | myscreen = myvtk.VTKScreen(width=w, height=h) 53 | myvtk.drawTriangles(myscreen, gl.get_triangles()) 54 | myscreen.render() 55 | myscreen.iren.Start() 56 | 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /py/cone_test.py: -------------------------------------------------------------------------------- 1 | import libcutsim 2 | import myvtk 3 | import math 4 | import time 5 | 6 | def main(): 7 | gl = libcutsim.GLData() # this class holds lines, triangles, or quads for OpenGL drawing 8 | iso = libcutsim.MarchingCubes() # this is the algorithm that produces GLData from the stock-model 9 | cs = libcutsim.Cutsim(20.0, 10, gl, iso) # this is the cutting simulation 10 | print(cs) 11 | 12 | cs.init(3) # initialize by subdividing octree n-times 13 | print(cs) 14 | 15 | vol = libcutsim.CubeVolume() # a volume for creating stock 16 | vol.setSide(10) 17 | vol.setCenter(0,0,-5) 18 | cs.sum_volume(vol) # sum the volume to the stock, creating new material 19 | 20 | cutter = libcutsim.ConeVolume() 21 | #cutter.setRadius(float(0.7)) 22 | # move around the cutter and subtract at each point 23 | t_before = time.time() 24 | Nmax = 100 25 | for n in range(Nmax): 26 | x = 3*math.cos(0.1*n) 27 | y = -3 + 0.08*n 28 | #print x,y 29 | cutter.setCenter(x,y,-0.4) 30 | cs.diff_volume(cutter) # subtract the volume from the stock 31 | #cs.updateGL() 32 | t_after = time.time() 33 | print(Nmax, " diff() calls took ", t_after-t_before," seconds") 34 | 35 | cs.updateGL() # this updates the GLData so we can draw the stock 36 | 37 | print(cs) 38 | print(gl) 39 | 40 | # create a VTK view for drawing 41 | w=1024 42 | h=1024 43 | myscreen = myvtk.VTKScreen(width=w, height=h) 44 | myvtk.drawTriangles(myscreen, gl.get_triangles()) 45 | myscreen.render() 46 | myscreen.iren.Start() 47 | 48 | if __name__ == "__main__": 49 | main() 50 | -------------------------------------------------------------------------------- /py/draw_triangles.py: -------------------------------------------------------------------------------- 1 | import libcutsim 2 | import myvtk 3 | 4 | def main(): 5 | gl = libcutsim.GLData() # this class holds lines, triangles, or quads for OpenGL drawing 6 | iso = libcutsim.MarchingCubes() # this is the algorithm that produces GLData from the stock-model 7 | octree_size = 10.0 # size of 'world' 8 | octree_max_depth = 8 9 | cs = libcutsim.Cutsim(octree_size, octree_max_depth, gl, iso) # this is the cutting simulation 10 | print(cs) 11 | 12 | cs.init(3) # initialize by subdividing octree n-times 13 | print(cs) 14 | 15 | # create stock material 16 | vol = libcutsim.SphereVolume() # a volume with which we operate on the stock 17 | vol.setRadius(4) 18 | vol.setCenter(0,0,0) 19 | cs.sum_volume(vol) # sum the volume to the stock, creating new stock material 20 | 21 | # resize/position the same sphere for a cut 22 | vol.setRadius(1) 23 | vol.setCenter(0,4,0) 24 | cs.diff_volume(vol) # subtract the volume from the stock 25 | 26 | cs.updateGL() # this updates the GLData so we can draw the stock 27 | 28 | print(cs) 29 | print(gl) 30 | 31 | # create a VTK view for drawing 32 | w=1024 33 | h=1024 34 | myscreen = myvtk.VTKScreen(width=w, height=h) 35 | 36 | myvtk.drawTriangles(myscreen, gl.get_triangles()) 37 | 38 | myscreen.render() 39 | myscreen.iren.Start() 40 | 41 | if __name__ == "__main__": 42 | main() 43 | -------------------------------------------------------------------------------- /py/example_stl/libcutsim_ascii_import.stl: -------------------------------------------------------------------------------- 1 | solid libcutsim_import 2 | facet normal 0.000000 -1.000000 0.000000 3 | outer loop 4 | vertex 0.000000 0.000000 10.000000 5 | vertex 0.000000 0.000000 0.000000 6 | vertex 100.000000 0.000000 0.000000 7 | endloop 8 | endfacet 9 | facet normal -0.000000 -1.000000 -0.000000 10 | outer loop 11 | vertex 100.000000 0.000000 10.000000 12 | vertex 0.000000 0.000000 10.000000 13 | vertex 100.000000 0.000000 0.000000 14 | endloop 15 | endfacet 16 | facet normal 1.000000 0.000000 0.000000 17 | outer loop 18 | vertex 100.000000 0.000000 10.000000 19 | vertex 100.000000 0.000000 0.000000 20 | vertex 100.000000 100.000000 0.000000 21 | endloop 22 | endfacet 23 | facet normal 1.000000 0.000000 0.000000 24 | outer loop 25 | vertex 100.000000 100.000000 10.000000 26 | vertex 100.000000 0.000000 10.000000 27 | vertex 100.000000 100.000000 0.000000 28 | endloop 29 | endfacet 30 | facet normal 0.000000 1.000000 0.000000 31 | outer loop 32 | vertex 100.000000 100.000000 10.000000 33 | vertex 100.000000 100.000000 0.000000 34 | vertex 0.000000 100.000000 0.000000 35 | endloop 36 | endfacet 37 | facet normal -0.000000 1.000000 0.000000 38 | outer loop 39 | vertex 0.000000 100.000000 10.000000 40 | vertex 100.000000 100.000000 10.000000 41 | vertex 0.000000 100.000000 0.000000 42 | endloop 43 | endfacet 44 | facet normal -1.000000 0.000000 -0.000000 45 | outer loop 46 | vertex 0.000000 100.000000 10.000000 47 | vertex 0.000000 100.000000 0.000000 48 | vertex 0.000000 0.000000 0.000000 49 | endloop 50 | endfacet 51 | facet normal -1.000000 0.000000 0.000000 52 | outer loop 53 | vertex 0.000000 0.000000 10.000000 54 | vertex 0.000000 100.000000 10.000000 55 | vertex 0.000000 0.000000 0.000000 56 | endloop 57 | endfacet 58 | facet normal 0.000000 0.000000 -1.000000 59 | outer loop 60 | vertex 100.000000 0.000000 0.000000 61 | vertex 0.000000 0.000000 0.000000 62 | vertex 100.000000 100.000000 0.000000 63 | endloop 64 | endfacet 65 | facet normal 0.000000 0.000000 -1.000000 66 | outer loop 67 | vertex 0.000000 0.000000 0.000000 68 | vertex 0.000000 100.000000 0.000000 69 | vertex 100.000000 100.000000 0.000000 70 | endloop 71 | endfacet 72 | facet normal 0.000000 -0.000000 1.000000 73 | outer loop 74 | vertex 100.000000 0.000000 10.000000 75 | vertex 100.000000 100.000000 10.000000 76 | vertex 0.000000 0.000000 10.000000 77 | endloop 78 | endfacet 79 | facet normal 0.000000 0.000000 1.000000 80 | outer loop 81 | vertex 0.000000 0.000000 10.000000 82 | vertex 100.000000 100.000000 10.000000 83 | vertex 0.000000 100.000000 10.000000 84 | endloop 85 | endfacet 86 | endsolid Mesh 87 | -------------------------------------------------------------------------------- /py/example_stl/libcutsim_binary_import.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aewallin/libcutsim/16fd8046cef7d04e27955a28aa96d7bd3db84db9/py/example_stl/libcutsim_binary_import.stl -------------------------------------------------------------------------------- /py/loadmesh_test.py: -------------------------------------------------------------------------------- 1 | import libcutsim 2 | import myvtk 3 | import time 4 | import os 5 | import math 6 | import sys 7 | 8 | 9 | gl = libcutsim.GLData() # holds GL-data for drawing 10 | iso = libcutsim.MarchingCubes() # isosurface algorithm 11 | world_size = 100.0 12 | max_tree_depth = 10 13 | cs = libcutsim.Cutsim(world_size, max_tree_depth, gl, iso) # cutting simulation 14 | # print("Initial Data", cs) 15 | 16 | cs.init(3) # initial subdivision of octree 17 | # print("Init Data", cs) 18 | 19 | vol = libcutsim.MeshVolume() # a volume for adding/subtracting 20 | facets = [[(-1.0, 0.0, 0.0), (-66.24879455566406, -39.76404571533203, -1.0), (-66.24879455566406, -39.76404571533203, 11.0), (-66.24879455566406, 44.41893768310547, -1.0)], [(-1.0, 0.0, 0.0), (-66.24879455566406, 44.41893768310547, -1.0), (-66.24879455566406, -39.76404571533203, 11.0), (-66.24879455566406, 44.41893768310547, 11.0)], [(1.0, 0.0, 0.0), (54.04975509643555, -39.76404571533203, 11.0), (54.04975509643555, -39.76404571533203, -1.0), (54.04975509643555, 44.41893768310547, -1.0)], [(1.0, -0.0, 0.0), (54.04975509643555, -39.76404571533203, 11.0), (54.04975509643555, 44.41893768310547, -1.0), (54.04975509643555, 44.41893768310547, 11.0)], [(0.0, -1.0, 0.0), (54.04975509643555, -39.76404571533203, -1.0), (54.04975509643555, -39.76404571533203, 11.0), (-66.24879455566406, -39.76404571533203, -1.0)], [(0.0, -1.0, 0.0), (-66.24879455566406, -39.76404571533203, -1.0), (54.04975509643555, -39.76404571533203, 11.0), (-66.24879455566406, -39.76404571533203, 11.0)], [(0.0, 1.0, 0.0), (54.04975509643555, 44.41893768310547, 11.0), (54.04975509643555, 44.41893768310547, -1.0), (-66.24879455566406, 44.41893768310547, -1.0)], [(0.0, 1.0, 0.0), (54.04975509643555, 44.41893768310547, 11.0), (-66.24879455566406, 44.41893768310547, -1.0), (-66.24879455566406, 44.41893768310547, 11.0)], [(0.0, 0.0, -1.0), (-66.24879455566406, 44.41893768310547, -1.0), (54.04975509643555, 44.41893768310547, -1.0), (-66.24879455566406, -39.76404571533203, -1.0)], [(0.0, 0.0, -1.0), (-66.24879455566406, -39.76404571533203, -1.0), (54.04975509643555, 44.41893768310547, -1.0), (54.04975509643555, -39.76404571533203, -1.0)], [(0.0, 0.0, 1.0), (54.04975509643555, 44.41893768310547, 11.0), (-66.24879455566406, 44.41893768310547, 11.0), (-66.24879455566406, -39.76404571533203, 11.0)], [(0.0, 0.0, 1.0), (54.04975509643555, 44.41893768310547, 11.0), (-66.24879455566406, -39.76404571533203, 11.0), (54.04975509643555, -39.76404571533203, 11.0)]] 21 | meshLoaded = vol.loadMesh(facets) # load mesh data 22 | 23 | if not meshLoaded: 24 | print("Error loading mesh data") 25 | sys.exit() 26 | 27 | cs.sum_volume(vol) # add volume to octree 28 | 29 | if (True): 30 | cutter = libcutsim.SphereVolume() 31 | cutter.setRadius(float(5.0)) 32 | # move around the cutter and subtract at each point 33 | t_before = time.time() 34 | 35 | stepSize = 0.5 36 | x = 0 37 | y = 0 38 | ymax = 100 39 | 40 | while y < ymax: 41 | # print("y value:", y) 42 | y = y + stepSize 43 | cutter.setCenter(x,y,12.0) 44 | cs.diff_volume(cutter) # subtract the volume from the stock 45 | t_after = time.time() 46 | 47 | # print(ymax / stepSize, " diff() calls took ", t_after-t_before," seconds") 48 | cs.updateGL() 49 | 50 | ''' 51 | # create a VTK view for drawing 52 | w=1024 53 | h=1024 54 | myscreen = myvtk.VTKScreen(width=w, height=h) 55 | 56 | myvtk.drawTriangles(myscreen, gl.get_triangles()) 57 | 58 | myscreen.render() 59 | myscreen.iren.Start() 60 | ''' 61 | 62 | # generate an stl file 63 | folder_name = "stl_out" 64 | dir = os.path.dirname(__file__) + os.sep + folder_name 65 | 66 | if not os.path.exists(dir): 67 | os.mkdir(dir) 68 | 69 | file_name = "libcutsim.stl" 70 | file_path = dir + os.sep + file_name 71 | 72 | #delete existing file 73 | if os.path.exists(file_path): 74 | os.remove(file_path) 75 | 76 | stl_file_path = gl.get_stl(file_path, True) # second parameter True for binary stl, False for assci 77 | 78 | if os.path.exists(stl_file_path): 79 | print("stl file written:", stl_file_path) 80 | print("stl file size (bytes):", os.path.getsize(stl_file_path)) 81 | else: 82 | print("Error: stl file creation failed at requested path:", stl_file_path) -------------------------------------------------------------------------------- /py/minimal.py: -------------------------------------------------------------------------------- 1 | import libcutsim # https://github.com/aewallin/libcutsim 2 | 3 | 4 | gl = libcutsim.GLData() # holds GL-data for drawing 5 | iso = libcutsim.MarchingCubes() # isosurface algorithm 6 | world_size = 10.0 7 | max_tree_depth = 5 8 | cs = libcutsim.Cutsim(world_size, max_tree_depth, gl, iso) # cutting simulation 9 | print(cs) 10 | 11 | cs.init(3) # initial subdivision of octree 12 | print(cs) 13 | 14 | vol = libcutsim.SphereVolume() # a volume for adding/subtracting 15 | vol.setRadius(4) 16 | vol.setCenter(0,0,0) 17 | 18 | cs.sum_volume(vol) # add volume to octree 19 | cs.updateGL() 20 | 21 | print(cs) 22 | print(gl) 23 | -------------------------------------------------------------------------------- /py/myvtk.py: -------------------------------------------------------------------------------- 1 | """@camvtk docstring 2 | This module provides classes for visualizing CAD/CAM algorithms using VTK. 3 | This module is part of OpenCAMLib (ocl), a toolpath-generation library. 4 | 5 | Copyright 2010-2011 Anders Wallin (anders.e.e.wallin "at" gmail.com) 6 | Published under the GNU General Public License, see http://www.gnu.org/licenses/ 7 | """ 8 | 9 | import vtk 10 | import time 11 | import datetime 12 | import math 13 | 14 | # color definitions 15 | white = (1,1,1) 16 | black = (0,0,0) 17 | grey = ( float(127)/255,float(127)/255,float(127)/255) 18 | 19 | red= (1,0,0) 20 | pink = ( float(255)/255,float(192)/255,float(203)/255) 21 | orange = ( float(255)/255,float(165)/255,float(0)/255) 22 | yellow= (1,1,0) 23 | purple=( float(255)/255,float(0)/255,float(176)/255) 24 | 25 | green= (0,1,0) 26 | lgreen = ( float(150)/255,float(255)/255,float(150)/255) 27 | dgreen= ( float(21)/255,float(119)/255,float(41)/255) 28 | grass = ( float(182)/255,float(248)/255,float(71)/255) 29 | 30 | blue= (0,0,1) 31 | lblue= ( float(125)/255,float(191)/255,float(255)/255 ) 32 | blue2= ( float(0)/255,float(188)/255,float(255)/255 ) 33 | cyan= (0,1,1) 34 | mag2 =( float(123)/255 , float(35)/255 , float(251)/255 ) 35 | magenta = ( float(153)/255 , float(42)/255 , float(165)/255 ) 36 | 37 | def drawTriangles(myscreen, trianglelist): 38 | # list of triangles 39 | # [ [p1,p2,p3] , 40 | # [p4,p5,p6] , 41 | # ... 42 | # ] 43 | # this draws all triangles with the same color 44 | triactor = STLSurf(triangleList=trianglelist, color=cyan) 45 | #triactor.SetWireframe() 46 | myscreen.addActor(triactor) 47 | 48 | def drawLines(myscreen, seglist): 49 | n =0 50 | for seg in seglist: 51 | print(n) 52 | n=n+1 53 | drawLine(myscreen, seg[0], seg[1], cyan) 54 | 55 | 56 | 57 | def drawLine(myscreen, pt1, pt2, lineColor): 58 | myscreen.addActor( Line(p1=(pt1.x,pt1.y,pt1.z),p2=(pt2.x,pt2.y,pt2.z),color=lineColor) ) 59 | 60 | def drawArc(myscreen, pt1, pt2, r, cen,cw,arcColor): 61 | # draw arc as many line-segments 62 | start = pt1-cen 63 | end = pt2-cen 64 | theta1 = math.atan2(start.x,start.y) 65 | theta2 = math.atan2(end.x,end.y) 66 | alfa=[] # the list of angles 67 | da=0.1 68 | CIRCLE_FUZZ = 1e-9 69 | # idea from emc2 / cutsim g-code interp G2/G3 70 | if (cw == False ): 71 | while ( (theta2 - theta1) > -CIRCLE_FUZZ): 72 | theta2 -= 2*math.pi 73 | else: 74 | while( (theta2 - theta1) < CIRCLE_FUZZ): 75 | theta2 += 2*math.pi 76 | 77 | dtheta = theta2-theta1 78 | arclength = r*dtheta 79 | dlength = min(0.01, arclength/10) 80 | steps = int( float(arclength) / float(dlength)) 81 | rsteps = float(1)/float(steps) 82 | dc = math.cos(-dtheta*rsteps) # delta-cos 83 | ds = math.sin(-dtheta*rsteps) # delta-sin 84 | 85 | previous = pt1 86 | tr = [start.x, start.y] 87 | for i in range(steps): 88 | #f = (i+1) * rsteps #; // varies from 1/rsteps..1 (?) 89 | #theta = theta1 + i* dtheta 90 | tr = rotate(tr[0], tr[1], dc, ds) #; // rotate center-start vector by a small amount 91 | x = cen.x + tr[0] 92 | y = cen.y + tr[1] 93 | current = ovd.Point(x,y) 94 | myscreen.addActor( Line(p1=(previous.x,previous.y,0),p2=(current.x,current.y,0),color=arcColor) ) 95 | previous = current 96 | 97 | # rotate by cos/sin. from emc2 gcodemodule.cc 98 | def rotate(x, y, c, s): 99 | tx = x * c - y * s; 100 | y = x * s + y * c; 101 | x = tx; 102 | return [x,y] 103 | 104 | def drawOffsets(myscreen, ofs): 105 | # draw loops 106 | nloop = 0 107 | lineColor = lgreen 108 | arcColor = green #grass 109 | for lop in ofs: 110 | n = 0 111 | N = len(lop) 112 | first_point=[] 113 | previous=[] 114 | for p in lop: 115 | # p[0] is the Point 116 | # p[1] is -1 for lines, and r for arcs 117 | if n==0: # don't draw anything on the first iteration 118 | previous=p[0] 119 | #first_point = p[0] 120 | else: 121 | cw=p[3] 122 | cen=p[2] 123 | r=p[1] 124 | p=p[0] 125 | if r==-1: 126 | drawLine(myscreen, previous, p, lineColor) 127 | else: 128 | drawArc(myscreen, previous, p, r,cen,cw, arcColor) 129 | #myscreen.addActor( ovdvtk.Line(p1=(previous.x,previous.y,0),p2=(p.x,p.y,0),color=loopColor) ) 130 | previous=p 131 | n=n+1 132 | print("rendered loop ",nloop, " with ", len(lop), " points") 133 | nloop = nloop+1 134 | 135 | 136 | 137 | def drawOCLtext(myscreen, rev_text=" "): 138 | t = Text() 139 | t.SetPos( (myscreen.width-250, myscreen.height-70) ) 140 | date_text = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 141 | #rev_text = ovd.version() 142 | t.SetText( "OpenVoronoi\n" + rev_text + "\n" + date_text ) 143 | myscreen.addActor(t) 144 | 145 | def drawBB( myscreen, vol ): 146 | """ draw a bounding-box """ 147 | lines = [] 148 | lines.append( Line(p1=(vol.bb.minx, vol.bb.miny, vol.bb.minz) , p2=(vol.bb.maxx, vol.bb.miny, vol.bb.minz)) ) 149 | lines.append( Line(p1=(vol.bb.minx, vol.bb.maxy, vol.bb.minz) , p2=(vol.bb.maxx, vol.bb.maxy, vol.bb.minz)) ) 150 | lines.append( Line(p1=(vol.bb.minx, vol.bb.maxy, vol.bb.maxz) , p2=(vol.bb.maxx, vol.bb.maxy, vol.bb.maxz)) ) 151 | lines.append( Line(p1=(vol.bb.minx, vol.bb.miny, vol.bb.maxz) , p2=(vol.bb.maxx, vol.bb.miny, vol.bb.maxz)) ) 152 | 153 | lines.append( Line(p1=(vol.bb.minx, vol.bb.miny, vol.bb.minz) , p2=(vol.bb.minx, vol.bb.miny, vol.bb.maxz)) ) 154 | lines.append( Line(p1=(vol.bb.maxx, vol.bb.miny, vol.bb.minz) , p2=(vol.bb.maxx, vol.bb.miny, vol.bb.maxz)) ) 155 | 156 | lines.append( Line(p1=(vol.bb.minx, vol.bb.maxy, vol.bb.minz) , p2=(vol.bb.minx, vol.bb.maxy, vol.bb.maxz)) ) 157 | lines.append( Line(p1=(vol.bb.maxx, vol.bb.maxy, vol.bb.minz) , p2=(vol.bb.maxx, vol.bb.maxy, vol.bb.maxz)) ) 158 | 159 | lines.append( Line(p1=(vol.bb.minx, vol.bb.miny, vol.bb.minz) , p2=(vol.bb.minx, vol.bb.maxy, vol.bb.minz)) ) 160 | lines.append( Line(p1=(vol.bb.maxx, vol.bb.miny, vol.bb.minz) , p2=(vol.bb.maxx, vol.bb.maxy, vol.bb.minz)) ) 161 | 162 | lines.append( Line(p1=(vol.bb.minx, vol.bb.miny, vol.bb.maxz) , p2=(vol.bb.minx, vol.bb.maxy, vol.bb.maxz)) ) 163 | lines.append( Line(p1=(vol.bb.maxx, vol.bb.miny, vol.bb.maxz) , p2=(vol.bb.maxx, vol.bb.maxy, vol.bb.maxz)) ) 164 | 165 | for l in lines: 166 | myscreen.addActor(l) 167 | 168 | def drawTree(myscreen,t,color=red,opacity=0.2, offset=(0,0,0)): 169 | """ draw an octree """ 170 | nodes = t.get_nodes() 171 | #nmax=len(nodes) 172 | #i=0 173 | for n in nodes: 174 | cen = n.point() # center of cube 175 | scale = n.get_scale() # scale of cube 176 | cube = camvtk.Cube(center=(cen.x+offset[0], cen.y+offset[1], cen.z+offset[2]), length= scale, color=color) 177 | cube.SetOpacity(opacity) 178 | #cube.SetPhong() 179 | cube.SetGouraud() 180 | #cube.SetWireframe() 181 | myscreen.addActor( cube ) 182 | #if (nmax>100): 183 | # print "i=", i 184 | # print "div=", (float(nmax)/10) 185 | # if ( (i % (float(nmax)/10))==0): 186 | # print ".", 187 | #i=i+1 188 | #print "done." 189 | 190 | 191 | 192 | def drawTree2(myscreen,t,color=red,opacity=0.2): 193 | """ draw an octree as an STLSurface """ 194 | tlist = pyocl.octree2trilist(t) 195 | surf = STLSurf(triangleList=tlist) 196 | surf.SetColor(color) 197 | surf.SetOpacity(opacity) 198 | myscreen.addActor(surf) 199 | 200 | def drawArrows(myscreen,center=(0,0,0)): 201 | # X Y Z arrows 202 | arrowcenter=center 203 | xar = Arrow(color=red, center=arrowcenter, rotXYZ=(0,0,0)) 204 | yar = Arrow(color=green, center=arrowcenter, rotXYZ=(0,0,90)) 205 | zar = Arrow(color=blue, center=arrowcenter, rotXYZ=(0,-90,0)) 206 | myscreen.addActor(xar) 207 | myscreen.addActor(yar) 208 | myscreen.addActor(zar) 209 | 210 | def drawCylCutter(myscreen, c, p): 211 | cyl = Cylinder(center=(p.x,p.y,p.z), radius=c.radius, 212 | height=c.length, 213 | rotXYZ=(90,0,0), color=grey) 214 | cyl.SetWireframe() 215 | myscreen.addActor(cyl) 216 | 217 | def drawBallCutter(myscreen, c, p): 218 | cyl = Cylinder(center=(p.x,p.y,p.z+c.getRadius() ), radius=c.getRadius(), 219 | height=c.getLength(), 220 | rotXYZ=(90,0,0), color=red) 221 | #cyl.SetWireframe() 222 | sph = Sphere(center=(p.x,p.y,p.z+c.getRadius()), radius=c.getRadius(), color=red) 223 | myscreen.addActor(cyl) 224 | myscreen.addActor(sph) 225 | acts=[] 226 | acts.append(cyl) 227 | acts.append(sph) 228 | return acts 229 | 230 | 231 | class VTKScreen(): 232 | """ 233 | a vtk render window for displaying geometry 234 | """ 235 | def __init__(self, width=1280, height=720): # 1080p: 1920x1080 236 | """ create a screen """ 237 | self.width=width 238 | self.height=height 239 | 240 | self.ren = vtk.vtkRenderer() 241 | self.renWin = vtk.vtkRenderWindow() 242 | self.renWin.AddRenderer(self.ren) 243 | self.renWin.SetSize(self.width,self.height) 244 | 245 | self.iren = vtk.vtkRenderWindowInteractor() 246 | self.iren.SetRenderWindow(self.renWin) 247 | interactorstyle = self.iren.GetInteractorStyle() 248 | interactorstyle.SetCurrentStyleToTrackballCamera() 249 | 250 | self.camera = vtk.vtkCamera() 251 | self.camera.SetClippingRange(0.01, 1000) 252 | self.camera.SetFocalPoint(0, 0, 0) 253 | self.camera.SetPosition(0, 35, 5) 254 | self.camera.SetViewAngle(30) 255 | self.camera.SetViewUp(0, 0, 1) 256 | self.ren.SetActiveCamera(self.camera) 257 | self.iren.Initialize() 258 | 259 | 260 | def setAmbient(self, r, g, b): 261 | """ set ambient color """ 262 | self.ren.SetAmbient(r, g, b) 263 | 264 | def addActor(self, actor): 265 | """ add an actor """ 266 | self.ren.AddActor(actor) 267 | 268 | def removeActor(self, actor): 269 | """ remove an actor""" 270 | #actor.Delete() 271 | self.ren.RemoveActor(actor) 272 | 273 | 274 | def render(self): 275 | """ render scene""" 276 | self.renWin.Render() 277 | 278 | def GetLights(self): 279 | return self.ren.GetLights() 280 | def CreateLight(self): 281 | self.ren.CreateLight() 282 | def MakeLight(self): 283 | return self.ren.MakeLight() 284 | def AddLight(self,l): 285 | self.ren.AddLight(l) 286 | def RemoveAllLights(self): 287 | self.ren.RemoveAllLights() 288 | def SetLightCollection(self,lights): 289 | self.ren.SetLightCollection(lights) 290 | def Close(self): 291 | self.iren.TerminateApp() 292 | 293 | 294 | class CamvtkActor(vtk.vtkActor): 295 | """ base class for actors""" 296 | def __init__(self): 297 | """ do nothing""" 298 | pass 299 | 300 | def Delete(self): 301 | self.Delete() 302 | 303 | def SetColor(self, color): 304 | """ set color of actor""" 305 | self.GetProperty().SetColor(color) 306 | 307 | def SetOpacity(self, op=0.5): 308 | """ set opacity of actor, 0 is see-thru (invisible)""" 309 | self.GetProperty().SetOpacity(op) 310 | 311 | def SetWireframe(self): 312 | """ set surface to wireframe""" 313 | self.GetProperty().SetRepresentationToWireframe() 314 | 315 | def SetSurface(self): 316 | """ set surface rendering on""" 317 | self.GetProperty().SetRepresentationToSurface() 318 | 319 | def SetPoints(self): 320 | """ render only points""" 321 | self.GetProperty().SetRepresentationToPoints() 322 | 323 | def SetFlat(self): 324 | """ set flat shading""" 325 | self.GetProperty().SetInterpolationToFlat() 326 | 327 | def SetGouraud(self): 328 | """ set gouraud shading""" 329 | self.GetProperty().SetInterpolationToGouraud() 330 | 331 | def SetPhong(self): 332 | """ set phong shading""" 333 | self.GetProperty().SetInterpolationToPhong() 334 | 335 | # possible TODOs 336 | # specular 337 | # diffuse 338 | # ambient 339 | 340 | 341 | 342 | class FollowerText(vtk.vtkFollower): 343 | """ 3D text """ 344 | def __init__(self,text="test",color=cyan,center=(0,0,0),scale=1): 345 | self.textSource = vtk.vtkVectorText() 346 | self.textSource.SetText( text ) 347 | self.scale = scale 348 | 349 | self.transform = vtk.vtkTransform() 350 | 351 | self.transform.Translate(center[0], center[1], center[2] ) 352 | self.transform.Scale(self.scale, self.scale, self.scale) 353 | self.transformFilter=vtk.vtkTransformPolyDataFilter() 354 | self.transformFilter.SetTransform(self.transform) 355 | self.transformFilter.SetInputConnection(self.textSource.GetOutputPort()) 356 | self.transformFilter.Update() 357 | 358 | self.mapper = vtk.vtkPolyDataMapper() 359 | self.mapper.SetInputConnection( self.transformFilter.GetOutputPort() ) 360 | self.SetMapper(self.mapper) 361 | self.SetColor(color) 362 | 363 | def SetScale(self, scale): 364 | self.scale = scale 365 | self.transform.Scale(self.scale, self.scale, self.scale) 366 | self.transformFilter.Update() 367 | 368 | def SetText(self,text): 369 | self.textSource.SetText( text ) 370 | 371 | def SetColor(self, color): 372 | """ set color of actor""" 373 | self.GetProperty().SetColor(color) 374 | 375 | 376 | class Cone(CamvtkActor): 377 | """ a cone""" 378 | def __init__(self, center=(-2,0,0), radius = 1, angle=45, height=0.4, color=(1,1,0) , resolution=60): 379 | """ cone""" 380 | self.src = vtk.vtkConeSource() 381 | self.src.SetResolution(resolution) 382 | self.src.SetRadius( radius ) 383 | #self.src.SetAngle( angle ) 384 | self.src.SetHeight( height ) 385 | #self.src.SetCenter(center) 386 | 387 | transform = vtk.vtkTransform() 388 | transform.Translate(center[0], center[1], center[2] - self.src.GetHeight()/2) 389 | #transform.RotateX(rotXYZ[0]) 390 | transform.RotateY( -90 ) 391 | #transform.RotateZ(rotXYZ[2]) 392 | transformFilter=vtk.vtkTransformPolyDataFilter() 393 | transformFilter.SetTransform(transform) 394 | transformFilter.SetInputConnection(self.src.GetOutputPort()) 395 | transformFilter.Update() 396 | 397 | self.mapper = vtk.vtkPolyDataMapper() 398 | self.mapper.SetInputConnection(transformFilter.GetOutputPort()) 399 | 400 | 401 | #self.mapper = vtk.vtkPolyDataMapper() 402 | #self.mapper.SetInput(self.src.GetOutput()) 403 | self.SetMapper(self.mapper) 404 | self.SetColor(color) 405 | 406 | class Sphere(CamvtkActor): 407 | """ a sphere""" 408 | def __init__(self, radius=1, resolution=20, center=(0,2,0), 409 | color=(1,0,0)): 410 | """ create sphere""" 411 | self.src = vtk.vtkSphereSource() 412 | self.src.SetRadius(radius) 413 | self.src.SetCenter(center) 414 | self.src.SetThetaResolution(resolution) 415 | self.src.SetPhiResolution(resolution) 416 | 417 | self.mapper = vtk.vtkPolyDataMapper() 418 | self.mapper.SetInputConnection(self.src.GetOutputPort()) 419 | self.SetMapper(self.mapper) 420 | self.SetColor(color) 421 | 422 | class Cube(CamvtkActor): 423 | """ a cube""" 424 | def __init__(self,center=(2,2,0) , length=1, color=(0,1,0) ): 425 | """ create cube""" 426 | self.src = vtk.vtkCubeSource() 427 | self.src.SetCenter(center) 428 | self.src.SetXLength(length) 429 | self.src.SetYLength(length) 430 | self.src.SetZLength(length) 431 | 432 | self.mapper = vtk.vtkPolyDataMapper() 433 | self.mapper.SetInputConnection(self.src.GetOutputPort()) 434 | self.SetMapper(self.mapper) 435 | self.SetColor(color) 436 | 437 | class Cylinder(CamvtkActor): 438 | """ cylinder """ 439 | def __init__(self,center=(0,-2,0) , radius=0.5, height=2, color=(0,1,1), 440 | rotXYZ=(0,0,0), resolution=50 ): 441 | """ cylinder """ 442 | self.src = vtk.vtkCylinderSource() 443 | self.src.SetCenter(0,0,0) 444 | self.src.SetHeight(height) 445 | self.src.SetRadius(radius) 446 | self.src.SetResolution(resolution) 447 | # SetResolution 448 | # SetCapping(int) 449 | # CappingOn() CappingOff() 450 | 451 | # this transform rotates the cylinder so it is vertical 452 | # and then translates the lower tip to the center point 453 | transform = vtk.vtkTransform() 454 | transform.Translate(center[0], center[1], center[2]+height/2) 455 | transform.RotateX(rotXYZ[0]) 456 | transformFilter=vtk.vtkTransformPolyDataFilter() 457 | transformFilter.SetTransform(transform) 458 | transformFilter.SetInputConnection(self.src.GetOutputPort()) 459 | transformFilter.Update() 460 | 461 | 462 | self.mapper = vtk.vtkPolyDataMapper() 463 | #self.mapper.SetInput(self.src.GetOutput()) 464 | self.mapper.SetInputConnection( transformFilter.GetOutputPort() ) 465 | self.SetMapper(self.mapper) 466 | self.SetColor(color) 467 | 468 | 469 | class Line(CamvtkActor): 470 | """ line """ 471 | def __init__(self,p1=(0,0,0) , p2=(1,1,1), color=(0,1,1) ): 472 | """ line """ 473 | self.src = vtk.vtkLineSource() 474 | self.src.SetPoint1(p1) 475 | self.src.SetPoint2(p2) 476 | self.mapper = vtk.vtkPolyDataMapper() 477 | self.mapper.SetInputConnection(self.src.GetOutputPort()) 478 | self.SetMapper(self.mapper) 479 | self.SetColor(color) 480 | 481 | class PolyLine(CamvtkActor): 482 | def __init__(self, pointList=[], color=(1,1,1) ): 483 | self.src=[] 484 | points = vtk.vtkPoints() 485 | polyline = vtk.vtkCellArray() 486 | 487 | idx = 0 488 | first = 1 489 | last_idx = 0 490 | segs=[] 491 | for p in pointList: 492 | points.InsertNextPoint(p.x, p.y, 0) 493 | #print "p = ",p 494 | if first==0: 495 | seg = [last_idx,idx] 496 | segs.append(seg) 497 | first = 0 498 | last_idx = idx 499 | idx = idx + 1 500 | 501 | for seg in segs: 502 | line = vtk.vtkLine() 503 | line.GetPointIds().SetId(0, seg[0]) 504 | line.GetPointIds().SetId(1, seg[1]) 505 | #print " indexes: ", seg[0]," to ",seg[1] 506 | polyline.InsertNextCell(line) 507 | 508 | 509 | polydata = vtk.vtkPolyData() 510 | polydata.SetPoints(points) 511 | polydata.SetLines(polyline) 512 | polydata.Modified() 513 | polydata.Update() 514 | self.src=polydata 515 | 516 | self.mapper = vtk.vtkPolyDataMapper() 517 | self.mapper.SetInputData(self.src) 518 | self.SetMapper(self.mapper) 519 | self.SetColor(color) 520 | polydata.Modified() 521 | polydata.Update() 522 | 523 | # SetScaleFactor(double) 524 | # GetOrigin 525 | 526 | 527 | class Tube(CamvtkActor): 528 | """ line with tube filter""" 529 | def __init__(self,p1=(0,0,0) , p2=(1,1,1), radius=0.1, color=(0,1,1) ): 530 | self.src = vtk.vtkLineSource() 531 | self.src.SetPoint1(p1) 532 | self.src.SetPoint2(p2) 533 | 534 | self.tubefilter = vtk.vtkTubeFilter() 535 | self.tubefilter.SetInputConnection( self.src.GetOutputPort() ) 536 | self.tubefilter.SetRadius( radius ) 537 | self.tubefilter.SetNumberOfSides( 30 ) 538 | self.tubefilter.Update() 539 | 540 | self.mapper = vtk.vtkPolyDataMapper() 541 | self.mapper.SetInputConnection( self.tubefilter.GetOutputPort() ) 542 | self.SetMapper(self.mapper) 543 | self.SetColor(color) 544 | 545 | 546 | class Circle(CamvtkActor): 547 | """ circle""" 548 | def __init__(self,center=(0,0,0) , radius=1, color=(0,1,1), resolution=50 ): 549 | """ create circle """ 550 | lines =vtk.vtkCellArray() 551 | id = 0 552 | points = vtk.vtkPoints() 553 | for n in xrange(0,resolution): 554 | line = vtk.vtkLine() 555 | angle1 = (float(n)/(float(resolution)))*2*math.pi 556 | angle2 = (float(n+1)/(float(resolution)))*2*math.pi 557 | p1 = (center[0]+radius*math.cos(angle1), center[1]+radius*math.sin(angle1), center[2]) 558 | p2 = (center[0]+radius*math.cos(angle2), center[1]+radius*math.sin(angle2), center[2]) 559 | points.InsertNextPoint(p1) 560 | points.InsertNextPoint(p2) 561 | line.GetPointIds().SetId(0,id) 562 | id=id+1 563 | line.GetPointIds().SetId(1,id) 564 | id=id+1 565 | lines.InsertNextCell(line) 566 | 567 | 568 | self.pdata = vtk.vtkPolyData() 569 | self.pdata.SetPoints(points) 570 | self.pdata.SetLines(lines) 571 | 572 | self.mapper = vtk.vtkPolyDataMapper() 573 | self.mapper.SetInput(self.pdata) 574 | self.SetMapper(self.mapper) 575 | self.SetColor(color) 576 | 577 | class Tube(CamvtkActor): 578 | """ a Tube is a line with thickness""" 579 | def __init__(self, p1=(0,0,0) , p2=(1,1,1), radius=0.2, color=(0,1,1) ): 580 | """ tube""" 581 | points = vtk.vtkPoints() 582 | points.InsertNextPoint(p1) 583 | points.InsertNextPoint(p2) 584 | line = vtk.vtkLine() 585 | line.GetPointIds().SetId(0,0) 586 | line.GetPointIds().SetId(1,1) 587 | lines =vtk.vtkCellArray() 588 | lines.InsertNextCell(line) 589 | self.pdata = vtk.vtkPolyData() 590 | self.pdata.SetPoints(points) 591 | self.pdata.SetLines(lines) 592 | 593 | tubefilter=vtk.vtkTubeFilter() 594 | tubefilter.SetInputData(self.pdata) 595 | tubefilter.SetRadius(radius) 596 | tubefilter.SetNumberOfSides(50) 597 | tubefilter.Update() 598 | 599 | self.mapper = vtk.vtkPolyDataMapper() 600 | self.mapper.SetInputConnection(tubefilter.GetOutputPort()) 601 | self.SetMapper(self.mapper) 602 | self.SetColor(color) 603 | 604 | 605 | class Point(CamvtkActor): 606 | """ point""" 607 | def __init__(self, center=(0,0,0), color=(1,2,3) ): 608 | """ create point """ 609 | self.src = vtk.vtkPointSource() 610 | self.src.SetCenter(center) 611 | self.src.SetRadius(0) 612 | self.src.SetNumberOfPoints(1) 613 | 614 | self.mapper = vtk.vtkPolyDataMapper() 615 | self.mapper.SetInputConnection(self.src.GetOutputPort()) 616 | self.SetMapper(self.mapper) 617 | self.SetColor(color) 618 | 619 | class Arrow(CamvtkActor): 620 | """ arrow """ 621 | def __init__(self, center=(0,0,0), color=(0,0,1), rotXYZ=(0,0,0) ): 622 | """ arrow """ 623 | self.src = vtk.vtkArrowSource() 624 | #self.src.SetCenter(center) 625 | 626 | transform = vtk.vtkTransform() 627 | transform.Translate(center[0], center[1], center[2]) 628 | transform.RotateX(rotXYZ[0]) 629 | transform.RotateY(rotXYZ[1]) 630 | transform.RotateZ(rotXYZ[2]) 631 | transformFilter=vtk.vtkTransformPolyDataFilter() 632 | transformFilter.SetTransform(transform) 633 | transformFilter.SetInputConnection(self.src.GetOutputPort()) 634 | transformFilter.Update() 635 | 636 | 637 | self.mapper = vtk.vtkPolyDataMapper() 638 | self.mapper.SetInputConnection( transformFilter.GetOutputPort() ) 639 | self.SetMapper(self.mapper) 640 | self.SetColor(color) 641 | 642 | 643 | class Text(vtk.vtkTextActor): 644 | """ 2D text, HUD-type""" 645 | def __init__(self, text="text",size=18,color=(1,1,1),pos=(100,100)): 646 | """create text""" 647 | self.SetText(text) 648 | self.properties=self.GetTextProperty() 649 | self.properties.SetFontFamilyToArial() 650 | self.properties.SetFontSize(size) 651 | 652 | self.SetColor(color) 653 | self.SetPos(pos) 654 | 655 | def SetColor(self,color): 656 | """ set color of text """ 657 | self.properties.SetColor(color) 658 | 659 | def SetPos(self, pos): 660 | """ set position on screen """ 661 | self.SetDisplayPosition(pos[0], pos[1]) 662 | 663 | def SetText(self, text): 664 | """ set text to be displayed """ 665 | self.SetInputData(text) 666 | 667 | def SetSize(self, size): 668 | self.properties.SetFontSize(size) 669 | 670 | class Text3D(vtk.vtkFollower): 671 | """ 3D text rendered in the scene""" 672 | def __init__(self, color=(1,1,1), center=(0,0,0), text="hello", scale=1, camera=[]): 673 | """ create text """ 674 | self.src = vtk.vtkVectorText() 675 | self.SetText(text) 676 | #self.SetCamera(camera) 677 | transform = vtk.vtkTransform() 678 | 679 | transform.Translate(center[0], center[1], center[2]) 680 | transform.Scale(scale, scale, scale) 681 | #transform.RotateY(90) 682 | #transform2 = vtk.vtkTransform() 683 | #transform.Concatenate(transform2) 684 | #transformFilter=vtk.vtkTransformPolyDataFilter() 685 | #transformFilter.SetTransform(transform) 686 | #transformFilter.SetInputConnection(self.src.GetOutputPort()) 687 | #transformFilter.Update() 688 | 689 | #follower = vtk.vtkFollower() 690 | #follower.SetMapper 691 | 692 | self.SetUserTransform(transform) 693 | self.mapper = vtk.vtkPolyDataMapper() 694 | self.mapper.SetInputConnection(self.src.GetOutputPort()) 695 | self.SetMapper(self.mapper) 696 | self.SetColor(color) 697 | 698 | def SetText(self, text): 699 | """ set text to be displayed""" 700 | self.src.SetText(text) 701 | 702 | def SetColor(self,color): 703 | """ set color of text""" 704 | self.GetProperty().SetColor(color) 705 | 706 | class Axes(vtk.vtkActor): 707 | """ axes (x,y,z) """ 708 | def __init__(self, center=(0,0,0), color=(0,0,1) ): 709 | """ create axes """ 710 | self.src = vtk.vtkAxes() 711 | #self.src.SetCenter(center) 712 | 713 | self.mapper = vtk.vtkPolyDataMapper() 714 | self.mapper.SetInputConnection(self.src.GetOutputPort()) 715 | self.SetMapper(self.mapper) 716 | 717 | self.SetColor(color) 718 | self.SetOrigin(center) 719 | # SetScaleFactor(double) 720 | # GetOrigin 721 | 722 | 723 | def SetColor(self, color): 724 | self.GetProperty().SetColor(color) 725 | 726 | def SetOrigin(self, center=(0,0,0)): 727 | self.src.SetOrigin(center[0], center[1], center[2]) 728 | 729 | class Toroid(CamvtkActor): 730 | def __init__(self, r1=1, r2=0.25, center=(0,0,0), rotXYZ=(0,0,0), color=(1,0,0)): 731 | self.parfun = vtk.vtkParametricSuperToroid() 732 | self.parfun.SetRingRadius(r1) 733 | self.parfun.SetCrossSectionRadius(r2) 734 | self.parfun.SetN1(1) 735 | self.parfun.SetN2(1) 736 | 737 | self.src = vtk.vtkParametricFunctionSource() 738 | self.src.SetParametricFunction(self.parfun) 739 | 740 | transform = vtk.vtkTransform() 741 | transform.Translate(center[0], center[1], center[2]) 742 | transform.RotateX(rotXYZ[0]) 743 | transform.RotateY(rotXYZ[1]) 744 | transform.RotateZ(rotXYZ[2]) 745 | transformFilter=vtk.vtkTransformPolyDataFilter() 746 | transformFilter.SetTransform(transform) 747 | transformFilter.SetInputConnection(self.src.GetOutputPort()) 748 | transformFilter.Update() 749 | 750 | self.mapper = vtk.vtkPolyDataMapper() 751 | self.mapper.SetInputConnection(transformFilter.GetOutputPort()) 752 | self.SetMapper(self.mapper) 753 | self.SetColor(color) 754 | 755 | """ 756 | class TrilistReader(vtk.vtkPolyDataAlgorithm): 757 | def __init__(self, triangleList): 758 | vtk.vtkPolyDataAlgorithm.__init__(self) 759 | self.FileName = None 760 | self.SetNumberOfInputPorts(0) 761 | self.SetNumberOfOutputPorts(1) 762 | 763 | def FillOutputPortInfornmation(self, port, info): 764 | if port == 0: 765 | info.Set( vtk.vtkDataObject.DATA_TYPE_NAME(), "vtkPolyData") 766 | return 1 767 | return 0 768 | 769 | def RequestData(self, request, inputVector, outputVector): 770 | outInfo = outputVector.GetInformationObject(0) 771 | output = outInfo.Get( vtk.vtkDataObject.DATA_OBJECT() ) 772 | polydata = vtk.vtkPolyData() 773 | points = vtk.vtkPoints() 774 | points.InsertNextPoint(0,0,0) 775 | polydata.SetPoints(points) 776 | 777 | output.ShallowCopy(polydata) 778 | return 1 779 | """ 780 | 781 | class STLSurf(CamvtkActor): 782 | def __init__(self, filename=None, triangleList=[], color=(1,1,1) ): 783 | self.src=[] 784 | 785 | points = vtk.vtkPoints() 786 | triangles = vtk.vtkCellArray() 787 | n=0 788 | for t in triangleList: 789 | triangle = vtk.vtkTriangle() 790 | for p in t: 791 | points.InsertNextPoint(p.x, p.y, p.z) 792 | triangle.GetPointIds().SetId(0,n) 793 | n=n+1 794 | triangle.GetPointIds().SetId(1,n) 795 | n=n+1 796 | triangle.GetPointIds().SetId(2,n) 797 | n=n+1 798 | triangles.InsertNextCell(triangle) 799 | 800 | polydata= vtk.vtkPolyData() 801 | polydata.SetPoints(points) 802 | polydata.SetPolys(triangles) 803 | polydata.Modified() 804 | # polydata.Update() 805 | self.src=polydata 806 | self.mapper = vtk.vtkPolyDataMapper() 807 | self.mapper.SetInputData(self.src) 808 | self.SetMapper(self.mapper) 809 | 810 | 811 | 812 | self.SetColor(color) 813 | # SetScaleFactor(double) 814 | # GetOrigin 815 | 816 | class PointCloud(CamvtkActor): 817 | def __init__(self, pointlist=[]): 818 | points = vtk.vtkPoints() 819 | cellArr = vtk.vtkCellArray() 820 | #Colors = vtk.vtkUnsignedCharArray() 821 | #Colors.SetNumberOfComponents(3) 822 | #Colors.SetName("Colors") 823 | self.zheight = 0 824 | 825 | 826 | n=0 827 | for p in pointlist: 828 | vert = vtk.vtkVertex() 829 | points.InsertNextPoint(p.x, p.y, self.zheight) 830 | vert.GetPointIds().SetId(0,n) 831 | cellArr.InsertNextCell( vert ) 832 | #col = clColor(p.cc()) 833 | #Colors.InsertNextTuple3( float(255)*col[0], float(255)*col[1], float(255)*col[2] ) 834 | n=n+1 835 | 836 | 837 | polydata= vtk.vtkPolyData() 838 | polydata.SetPoints(points) 839 | polydata.SetVerts( cellArr ) 840 | #polydata.GetPointData().SetScalars(Colors) 841 | 842 | polydata.Modified() 843 | polydata.Update() 844 | self.src=polydata 845 | self.mapper = vtk.vtkPolyDataMapper() 846 | self.mapper.SetInputData(self.src) 847 | self.SetMapper(self.mapper) 848 | #self.SetColor(color) 849 | 850 | 851 | class CLPointCloud(CamvtkActor): 852 | def __init__(self, pointlist=[]): 853 | points = vtk.vtkPoints() 854 | cellArr = vtk.vtkCellArray() 855 | Colors = vtk.vtkUnsignedCharArray() 856 | Colors.SetNumberOfComponents(3) 857 | Colors.SetName("Colors") 858 | 859 | n=0 860 | for p in pointlist: 861 | vert = vtk.vtkVertex() 862 | points.InsertNextPoint(p.x, p.y, p.z) 863 | vert.GetPointIds().SetId(0,n) 864 | cellArr.InsertNextCell( vert ) 865 | col = clColor(p.cc()) 866 | Colors.InsertNextTuple3( float(255)*col[0], float(255)*col[1], float(255)*col[2] ) 867 | n=n+1 868 | 869 | polydata= vtk.vtkPolyData() 870 | polydata.SetPoints(points) 871 | polydata.SetVerts( cellArr ) 872 | polydata.GetPointData().SetScalars(Colors) 873 | 874 | polydata.Modified() 875 | polydata.Update() 876 | self.src=polydata 877 | self.mapper = vtk.vtkPolyDataMapper() 878 | self.mapper.SetInputData(self.src) 879 | self.SetMapper(self.mapper) 880 | #self.SetColor(color) 881 | 882 | 883 | 884 | class Plane(CamvtkActor): 885 | def __init__(self, center=(0,0,0), color=(0,0,1) ): 886 | self.src = vtk.vtkPlaneSource() 887 | #self.src.SetCenter(center) 888 | self.mapper = vtk.vtkPolyDataMapper() 889 | self.mapper.SetInputConnection(self.src.GetOutputPort()) 890 | self.SetMapper(self.mapper) 891 | 892 | self.SetColor(color) 893 | self.SetOrigin(center) 894 | # SetScaleFactor(double) 895 | # GetOrigin 896 | 897 | 898 | 899 | # TODO: 900 | # vtkArcSource 901 | # vtkDiskSource 902 | # vtkFrustumSource 903 | # vtkOutlineSource 904 | # vtkParametricFunctionSource 905 | # PlatonicSolid 906 | # ProgrammableSource (?) 907 | # PSphereSource 908 | # RegularPolygon 909 | 910 | #---------------------------------------------------------------- 911 | 912 | #---- misc helper functions 913 | def vtkPolyData2OCLSTL(vtkPolyData,oclSTL): 914 | """ read vtkPolyData and add each triangle to an ocl.STLSurf """ 915 | for cellId in range(0,vtkPolyData.GetNumberOfCells()): 916 | cell = vtkPolyData.GetCell(cellId) 917 | points = cell.GetPoints() 918 | plist = [] 919 | for pointId in range(0,points.GetNumberOfPoints()): 920 | vertex = points.GetPoint(pointId) 921 | p = ocl.Point(vertex[0],vertex[1],vertex[2]) 922 | plist.append(p) 923 | t = ocl.Triangle(plist[0],plist[1],plist[2]) 924 | oclSTL.addTriangle(t) 925 | -------------------------------------------------------------------------------- /py/simple_move.py: -------------------------------------------------------------------------------- 1 | import libcutsim 2 | import myvtk 3 | import math 4 | import time 5 | 6 | # draw triangles from GLData 7 | """ 8 | def drawTriangles(myscreen, gl): 9 | trianglelist = gl.get_triangles() 10 | # list of triangles 11 | # [ [p1,p2,p3] , 12 | # [p4,p5,p6] , 13 | # ... 14 | # ] 15 | print "drawing ",len(trianglelist)," triangles" 16 | 17 | # this draws all triangles with the same color 18 | triactor = myvtk.STLSurf(triangleList=trianglelist, color=myvtk.cyan) 19 | #triactor.SetWireframe() 20 | myscreen.addActor(triactor) 21 | """ 22 | 23 | def main(): 24 | gl = libcutsim.GLData() # this class holds lines, triangles, or quads for OpenGL drawing 25 | iso = libcutsim.MarchingCubes() # this is the algorithm that produces GLData from the stock-model 26 | cs = libcutsim.Cutsim(20.0, 9, gl, iso) # this is the cutting simulation 27 | print(cs) 28 | 29 | cs.init(3) # initialize by subdividing octree n-times 30 | print(cs) 31 | 32 | vol = libcutsim.CubeVolume() # a volume with which we operate on the stock 33 | vol.setSide(10) 34 | vol.setCenter(0,0,-5) 35 | 36 | cs.sum_volume(vol) # sum the volume to the stock, creating new material 37 | 38 | # the volume with which we cut 39 | 40 | cutter = libcutsim.SphereVolume() 41 | cutter.setRadius(float(0.7)) 42 | # move around the cutter and subtract at each point 43 | t_before = time.time() 44 | Nmax = 100 45 | for n in range(Nmax): 46 | x = 3*math.cos(0.1*n) 47 | y = -3 + 0.08*n 48 | #print x,y 49 | cutter.setCenter(x,y,0.1) 50 | cs.diff_volume(cutter) # subtract the volume from the stock 51 | #cs.updateGL() 52 | t_after = time.time() 53 | print(Nmax, " diff() calls took ", t_after-t_before," seconds") 54 | cs.updateGL() 55 | # this updates the GLData so we can draw the stock 56 | 57 | print(cs) 58 | print(gl) 59 | 60 | # create a VTK view for drawing 61 | w=1024 62 | h=1024 63 | myscreen = myvtk.VTKScreen(width=w, height=h) 64 | 65 | myvtk.drawTriangles(myscreen, gl.get_triangles()) 66 | 67 | myscreen.render() 68 | myscreen.iren.Start() 69 | 70 | if __name__ == "__main__": 71 | main() 72 | -------------------------------------------------------------------------------- /py/stlout_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import libcutsim 3 | 4 | # Test export / outputting of the GLData to an STL file 5 | 6 | def main(): 7 | gl = libcutsim.GLData() # this class holds lines, triangles, or quads for OpenGL drawing 8 | iso = libcutsim.MarchingCubes() # this is the algorithm that produces GLData from the stock-model 9 | octree_size = 10.0 # size of 'world' 10 | octree_max_depth = 8 11 | cs = libcutsim.Cutsim(octree_size, octree_max_depth, gl, iso) # this is the cutting simulation 12 | print(cs) 13 | 14 | cs.init(3) # initialize by subdividing octree n-times 15 | print(cs) 16 | 17 | # create stock material 18 | vol = libcutsim.SphereVolume() # a volume with which we operate on the stock 19 | vol.setRadius(4) 20 | vol.setCenter(0, 0, 0) 21 | cs.sum_volume(vol) # sum the volume to the stock, creating new stock material 22 | 23 | # resize/position the same sphere for a cut 24 | vol.setRadius(1) 25 | vol.setCenter(0, 4, 0) 26 | cs.diff_volume(vol) # subtract the volume from the stock 27 | 28 | cs.updateGL() # this updates the GLData so we can draw the stock 29 | 30 | # generate an stl file 31 | dir = os.path.dirname(__file__) 32 | file_name = "libcutsim.stl" 33 | file_path = dir + os.sep + file_name 34 | stl_file_path = gl.get_stl(file_path, True) # second parameter True for binary stl, False for assci 35 | print("stl file written:", stl_file_path) 36 | 37 | 38 | if __name__ == "__main__": 39 | main() 40 | -------------------------------------------------------------------------------- /py/wireframe_test.py: -------------------------------------------------------------------------------- 1 | import libcutsim 2 | import myvtk 3 | 4 | def main(): 5 | gl = libcutsim.GLData() # this class holds lines, triangles, or quads for OpenGL drawing 6 | iso = libcutsim.CubeWireFrame() 7 | octree_size = 10.0 # size of 'world' 8 | octree_max_depth = 5 9 | cs = libcutsim.Cutsim(octree_size, octree_max_depth, gl, iso) # this is the cutting simulation 10 | print(cs) 11 | 12 | cs.init(3) # initialize by subdividing octree n-times 13 | print(cs) 14 | 15 | # create stock material 16 | vol = libcutsim.SphereVolume() # a volume with which we operate on the stock 17 | vol.setRadius(4) 18 | vol.setCenter(0,0,0) 19 | cs.sum_volume(vol) # sum the volume to the stock, creating new stock material 20 | 21 | # resize/position the same sphere for a cut 22 | vol.setRadius(1) 23 | vol.setCenter(0,4,0) 24 | cs.diff_volume(vol) # subtract the volume from the stock 25 | 26 | cs.updateGL() # this updates the GLData so we can draw the stock 27 | 28 | print(cs) 29 | print(gl) 30 | 31 | # create a VTK view for drawing 32 | w=1024 33 | h=1024 34 | myscreen = myvtk.VTKScreen(width=w, height=h) 35 | 36 | myvtk.drawLines(myscreen, gl.get_lines()) 37 | 38 | myscreen.render() 39 | myscreen.iren.Start() 40 | 41 | if __name__ == "__main__": 42 | main() 43 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(libcutsim) 2 | 3 | cmake_minimum_required(VERSION 2.4) 4 | 5 | # 6 | # Turn compiler warnings up to 11, at least with gcc. I dont know how to 7 | # do this with other compilers we might support and I'm leaving it up to 8 | # the relevant platform maintainers... 9 | # #include'ing the boost graph-library creates deprecated warnings 10 | # thus we use now use -Wno-deprecated here. 11 | # 12 | if (CMAKE_BUILD_TOOL MATCHES "make") 13 | MESSAGE(STATUS "setting gcc options: -Wall -Werror -Wno-deprecated -pedantic-errors") 14 | add_definitions(-Wall -Wno-deprecated -Werror -pedantic-errors -Wno-error=multichar ) 15 | add_definitions(-fPIC) 16 | add_definitions(-std=c++17) 17 | endif (CMAKE_BUILD_TOOL MATCHES "make") 18 | 19 | option(BUILD_TYPE 20 | "Build type: Release=ON/Debug=OFF " ON) 21 | #"Build type: Release=ON/Debug=OFF " OFF) 22 | 23 | if (BUILD_TYPE) 24 | MESSAGE(STATUS " CMAKE_BUILD_TYPE = Release") 25 | set(CMAKE_BUILD_TYPE Release) 26 | endif(BUILD_TYPE) 27 | 28 | if (NOT BUILD_TYPE) 29 | MESSAGE(STATUS " CMAKE_BUILD_TYPE = Debug") 30 | set(CMAKE_BUILD_TYPE Debug) 31 | endif(NOT BUILD_TYPE) 32 | 33 | # find BOOST and boost-python 34 | find_package( Boost COMPONENTS python REQUIRED) 35 | if(Boost_FOUND) 36 | include_directories(${Boost_INCLUDE_DIRS}) 37 | MESSAGE(STATUS "found Boost: " ${Boost_LIB_VERSION}) 38 | MESSAGE(STATUS "boost-incude dirs are: " ${Boost_INCLUDE_DIRS}) 39 | MESSAGE(STATUS "boost_LIBRARY_DIRS is: " ${Boost_LIBRARY_DIRS}) 40 | MESSAGE(STATUS "Boost_LIBRARIES is: " ${Boost_LIBRARIES}) 41 | endif() 42 | 43 | # this figures out the Python include directories and adds them to the 44 | # header file search path 45 | execute_process( 46 | COMMAND python3-config --includes 47 | COMMAND sed -r "s/-I//g; s/ +/;/g" 48 | COMMAND tr -d '\n' 49 | OUTPUT_VARIABLE Python_Includes 50 | ) 51 | include_directories(${Python_Includes}) 52 | 53 | # this defines the source-files 54 | 55 | MESSAGE(STATUS "CMAKE_SOURCE_DIR = " ${CMAKE_SOURCE_DIR} ) 56 | 57 | set( CUTSIM_SRC 58 | ${CMAKE_CURRENT_SOURCE_DIR}/volume.cpp 59 | ${CMAKE_CURRENT_SOURCE_DIR}/fileio.cpp 60 | ${CMAKE_CURRENT_SOURCE_DIR}/octnode.cpp 61 | ${CMAKE_CURRENT_SOURCE_DIR}/octree.cpp 62 | ${CMAKE_CURRENT_SOURCE_DIR}/marching_cubes.cpp 63 | ${CMAKE_CURRENT_SOURCE_DIR}/gldata.cpp 64 | ${CMAKE_CURRENT_SOURCE_DIR}/bbox.cpp 65 | ${CMAKE_CURRENT_SOURCE_DIR}/cutsim.cpp 66 | ) 67 | 68 | set( CUTSIM_INCLUDE_FILES 69 | ${CMAKE_CURRENT_SOURCE_DIR}/facet.hpp 70 | ${CMAKE_CURRENT_SOURCE_DIR}/fileio.hpp 71 | ${CMAKE_CURRENT_SOURCE_DIR}/octnode.hpp 72 | ${CMAKE_CURRENT_SOURCE_DIR}/octree.hpp 73 | ${CMAKE_CURRENT_SOURCE_DIR}/volume.hpp 74 | ${CMAKE_CURRENT_SOURCE_DIR}/bbox.hpp 75 | ${CMAKE_CURRENT_SOURCE_DIR}/isosurface.hpp 76 | ${CMAKE_CURRENT_SOURCE_DIR}/marching_cubes.hpp 77 | ${CMAKE_CURRENT_SOURCE_DIR}/cube_wireframe.hpp 78 | ${CMAKE_CURRENT_SOURCE_DIR}/gldata.hpp 79 | ${CMAKE_CURRENT_SOURCE_DIR}/glvertex.hpp 80 | ${CMAKE_CURRENT_SOURCE_DIR}/cutsim.hpp 81 | ) 82 | 83 | 84 | # static library, linked with the python module below 85 | add_library( 86 | libcutsim_static 87 | STATIC 88 | ${CUTSIM_SRC} 89 | ) 90 | 91 | set_target_properties(libcutsim_static PROPERTIES PREFIX "") # avoid liblib 92 | 93 | # this makes the Python module 94 | add_library( 95 | libcutsim 96 | MODULE 97 | cutsim_py.cpp 98 | ) 99 | target_link_libraries(libcutsim libcutsim_static ${Boost_LIBRARIES} ) 100 | set_target_properties(libcutsim PROPERTIES PREFIX "") # avoid liblib 101 | 102 | 103 | 104 | # this figures out where to install the Python modules 105 | execute_process( 106 | COMMAND python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" 107 | OUTPUT_VARIABLE Python_site_packages 108 | OUTPUT_STRIP_TRAILING_WHITESPACE 109 | ) # on Ubuntu 11.10 this outputs: /usr/local/lib/python2.7/dist-packages 110 | 111 | # strip away /usr/local/ because that is what CMAKE_INSTALL_PREFIX is set to 112 | # also, since there is no leading "/", it makes ${Python_site_packages} a relative path. 113 | STRING(REGEX REPLACE "/usr/local/(.*)$" "\\1" Python_site_packages "${Python_site_packages}" ) 114 | 115 | MESSAGE(STATUS "CMAKE_INSTALL_PREFIX is : " ${CMAKE_INSTALL_PREFIX}) 116 | MESSAGE(STATUS "Python libraries will be installed to: " ${Python_site_packages}) 117 | 118 | # this installs the python library 119 | install( 120 | TARGETS libcutsim 121 | LIBRARY DESTINATION ${Python_site_packages} 122 | ) 123 | -------------------------------------------------------------------------------- /src/bbox.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "bbox.hpp" 25 | 26 | namespace cutsim 27 | { 28 | 29 | Bbox::Bbox() 30 | { 31 | minpt = GLVertex(0, 0, 0); 32 | maxpt = GLVertex(0, 0, 0); 33 | initialized = false; 34 | } 35 | // minx maxx miny maxy minz maxz 36 | Bbox::Bbox(double b1, double b2, double b3, double b4, double b5, double b6) 37 | { 38 | minpt = GLVertex(b1, b3, b5); 39 | maxpt = GLVertex(b2, b4, b6); 40 | initialized = true; 41 | } 42 | 43 | void Bbox::clear() 44 | { 45 | initialized = false; 46 | } 47 | 48 | void Bbox::addPoint(const GLVertex &p) 49 | { 50 | if (!initialized) 51 | { 52 | maxpt = p; 53 | minpt = p; 54 | initialized = true; 55 | } 56 | else 57 | { 58 | if (p.x > maxpt.x) 59 | maxpt.x = p.x; 60 | if (p.x < minpt.x) 61 | minpt.x = p.x; 62 | 63 | if (p.y > maxpt.y) 64 | maxpt.y = p.y; 65 | if (p.y < minpt.y) 66 | minpt.y = p.y; 67 | 68 | if (p.z > maxpt.z) 69 | maxpt.z = p.z; 70 | if (p.z < minpt.z) 71 | minpt.z = p.z; 72 | } 73 | } 74 | 75 | /// does this Bbox overlap with b? 76 | bool Bbox::overlaps(const Bbox &b) const 77 | { 78 | if ((this->maxpt.x < b.minpt.x) || (this->minpt.x > b.maxpt.x)) 79 | return false; 80 | else if ((this->maxpt.y < b.minpt.y) || (this->minpt.y > b.maxpt.y)) 81 | return false; 82 | else if ((this->maxpt.z < b.minpt.z) || (this->minpt.z > b.maxpt.z)) 83 | return false; 84 | else 85 | return true; 86 | } 87 | 88 | } // end namespace 89 | // end of file 90 | -------------------------------------------------------------------------------- /src/bbox.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "glvertex.hpp" 23 | 24 | namespace cutsim 25 | { 26 | 27 | /// axis-aligned bounding-box, stores max and min (x,y,z) coordinates 28 | /// as GLVertex minpt and GLVertex maxpt and provides methods for clearing and setting these 29 | /// 30 | /// used for rapid overlap-checking of Vola ume with an Octnode 31 | class Bbox 32 | { 33 | public: 34 | Bbox(); 35 | /// create box 36 | Bbox(double minx, double maxx, double miny, double maxy, double minz, double maxz); 37 | virtual ~Bbox(){}; 38 | 39 | /// return true if *this overlaps Bbox b 40 | bool overlaps(const Bbox &other) const; 41 | 42 | /// reset the Bbox (sets initialized=false) 43 | void clear(); 44 | /// Add a Point to the Bbox. 45 | /// This enlarges the Bbox so that p is contained within it. 46 | void addPoint(const GLVertex &p); 47 | 48 | //DATA 49 | /// the maximum point 50 | GLVertex maxpt; 51 | /// the minimum point 52 | GLVertex minpt; 53 | 54 | private: 55 | /// false until one Point or one Triangle has been added 56 | bool initialized; 57 | }; 58 | 59 | } // end namespace 60 | 61 | // end file bbox.h 62 | -------------------------------------------------------------------------------- /src/cube_wireframe.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | #include "isosurface.hpp" 26 | 27 | namespace cutsim 28 | { 29 | 30 | /// simple cube wireframe display of octree 31 | /// inside, outside, and undecided nodes/cubes can be colored with a different color 32 | /// we can choose to draw only inside, outside, or undecided nodes 33 | /// 34 | /// very simple algorithm, all GLData is updated every time we run updateGL (this is slow!) 35 | /// 36 | class CubeWireFrame : public IsoSurfaceAlgorithm 37 | { 38 | public: 39 | CubeWireFrame() : IsoSurfaceAlgorithm() 40 | { 41 | //g->setLines(); // two indexes per line-segment 42 | inside_color.set(1, 0, 0); 43 | undecided_color.set(0, 1, 0); 44 | outside_color.set(0, 0, 1); 45 | draw_inside = true; 46 | draw_outside = true; 47 | draw_undecided = true; 48 | } 49 | virtual void set_polyVerts(unsigned int) { g->setLines(); } 50 | 51 | protected: 52 | Color inside_color; ///< color for inside nodes 53 | Color outside_color; ///< color for outside nodes 54 | Color undecided_color; ///< color for undecided nodes 55 | bool draw_inside; ///< flag for drawing inside nodes 56 | bool draw_outside; ///< flag for drawing outside nodes 57 | bool draw_undecided; ///< flag for drawing undecided nodes 58 | 59 | // traverse tree and add/remove gl-elements to GLData 60 | void updateGL(Octnode *node) 61 | { 62 | if (node->valid()) 63 | { 64 | valid_count++; 65 | return; 66 | } 67 | else if (!node->valid()) 68 | { 69 | update_calls++; 70 | node->clearVertexSet(); // remove all previous GLData 71 | 72 | // add lines corresponding to the cube. 73 | // cube image: http://paulbourke.net/geometry/polygonise/ 74 | const int segTable[12][2] = { 75 | {0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}}; 76 | if ((node->is_inside() && draw_inside) || 77 | (node->is_outside() && draw_outside) || 78 | (node->is_undecided() && draw_undecided)) 79 | { 80 | for (unsigned int i = 0; i < 12; i++) 81 | { 82 | std::vector lineSeg; 83 | GLVertex p1 = *(node->vertex)[segTable[i][0]]; 84 | GLVertex p2 = *(node->vertex)[segTable[i][1]]; 85 | Color line_color = outside_color; 86 | if (node->is_outside()) 87 | { 88 | line_color = outside_color; 89 | } 90 | if (node->is_inside()) 91 | { 92 | line_color = inside_color; 93 | } 94 | if (node->is_undecided()) 95 | { 96 | line_color = undecided_color; 97 | } 98 | p1.setColor(line_color); 99 | p2.setColor(line_color); 100 | 101 | lineSeg.push_back(g->addVertex(p1, node)); 102 | lineSeg.push_back(g->addVertex(p2, node)); 103 | node->addIndex(lineSeg[0]); 104 | node->addIndex(lineSeg[1]); 105 | std::cout << "line " << lineSeg[0] << " - " << lineSeg[1] << "\n"; 106 | g->addPolygon(lineSeg); 107 | } 108 | } 109 | node->setValid(); 110 | 111 | // current node done, now recurse into tree. 112 | if (node->childcount == 8) 113 | { 114 | for (unsigned int m = 0; m < 8; m++) 115 | { 116 | //if ( !node->child[m]->valid() ) 117 | updateGL(node->child[m]); 118 | } 119 | } 120 | } 121 | } 122 | 123 | /* 124 | std::vector< std::vector< GLVertex > > polygonize_node(const Octnode* node) { 125 | assert( node->childcount == 0 ); // don't call this on non-leafs! 126 | std::vector< std::vector< GLVertex > > triangles; 127 | // unsigned int edgeTableIndex = mc_edgeTableIndex(node); 128 | // the index into this table now tells us which edges have the vertices 129 | // for the new triangles 130 | // the lookup returns a 12-bit number, where each bit indicates wether 131 | // the edge is cut by the isosurface 132 | //unsigned int edges = edgeTable[edgeTableIndex]; 133 | // calculate intersection points by linear interpolation 134 | // there are now 12 different cases: 135 | //std::vector< GLVertex > vertices = interpolated_vertices(node, edges); 136 | // assert( vertices.size()==12 ); 137 | // form triangles by lookup in triTable 138 | 139 | const int triTable[12][3] = { 140 | {0, 1, 2}, 141 | {0, 2, 3}, 142 | {0, 4, 7}, 143 | {0, 3, 7}, 144 | {0, 1, 5}, 145 | {0, 4, 5}, 146 | {4, 5, 6}, 147 | {4, 6, 7}, 148 | {1, 5, 6}, 149 | {1, 2, 6}, 150 | {2, 6, 7}, 151 | {2, 3, 7} 152 | }; 153 | if (node->inside) { 154 | for (unsigned int i=0; i <12 ; i++ ) { 155 | std::vector< GLVertex > triangle; 156 | triangle.push_back( *(node->vertex)[ triTable[i][0 ] ] ); 157 | triangle.push_back( *(node->vertex)[ triTable[i][1 ] ] ); 158 | triangle.push_back( *(node->vertex)[ triTable[i][2 ] ] ); 159 | // calculate normal 160 | GLVertex n = (triangle[0]-triangle[1]).cross( triangle[0]-triangle[2] ); 161 | n.normalize(); 162 | triangle[0].setNormal(n.x,n.y,n.z); 163 | triangle[1].setNormal(n.x,n.y,n.z); 164 | triangle[2].setNormal(n.x,n.y,n.z); 165 | // setColor 166 | triangle[0].setColor(red,green,blue); 167 | triangle[1].setColor(red,green,blue); 168 | triangle[2].setColor(red,green,blue); 169 | triangles.push_back( triangle ); 170 | } 171 | } 172 | return triangles; 173 | }*/ 174 | }; 175 | 176 | } // end namespace 177 | 178 | // end file 179 | -------------------------------------------------------------------------------- /src/cutsim.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include "cutsim.hpp" 21 | 22 | namespace cutsim { 23 | 24 | Cutsim::Cutsim (double octree_size, unsigned int octree_max_depth, GLData* gld, IsoSurfaceAlgorithm* iso) 25 | : iso_algo(iso), g(gld) { 26 | GLVertex octree_center(0,0,0); 27 | tree = new Octree(octree_size, octree_max_depth, octree_center, g ); 28 | tree->debug=false; 29 | iso_algo->set_gl(g); 30 | iso_algo->set_tree(tree); 31 | iso_algo->set_polyVerts(); 32 | } 33 | 34 | Cutsim::~Cutsim() { 35 | delete tree; 36 | } 37 | 38 | void Cutsim::init(unsigned int n) { 39 | tree->init(n); 40 | //std::cout << "Cutsim::init() tree after init: " << tree->str() << "\n"; 41 | } 42 | 43 | std::string Cutsim::str() const { 44 | std::string out = tree->str(); 45 | return out; 46 | } 47 | 48 | void Cutsim::updateGL() { 49 | //std::clock_t start, stop; 50 | //start = std::clock(); 51 | iso_algo->updateGL(); 52 | //stop = std::clock(); 53 | //std::cout << "cutsim.cpp updateGL() : " << ( ( stop - start ) / (double)CLOCKS_PER_SEC ) <<"\n"; 54 | } 55 | 56 | void Cutsim::sum_volume( const Volume* volume ) { 57 | //std::clock_t start, stop; 58 | //start = std::clock(); 59 | tree->sum( volume ); 60 | //stop = std::clock(); 61 | //std::cout << "Cutsim::sum_volume() :" << ( ( stop - start ) / (double)CLOCKS_PER_SEC ) <<" s\n"; 62 | } 63 | 64 | void Cutsim::diff_volume( const Volume* volume ) { 65 | //std::clock_t start, stop; 66 | //start = std::clock(); 67 | tree->diff( volume ); 68 | //stop = std::clock(); 69 | //std::cout << "cutsim.cpp diff_volume() :" << ( ( stop - start ) / (double)CLOCKS_PER_SEC ) <<"\n"; 70 | } 71 | 72 | void Cutsim::intersect_volume( const Volume* volume ) { 73 | //std::clock_t start, stop; 74 | //start = std::clock(); 75 | tree->intersect( volume ); 76 | //stop = std::clock(); 77 | //std::cout << "cutsim.cpp intersect_volume() :" << ( ( stop - start ) / (double)CLOCKS_PER_SEC ) <<'\n'; 78 | } 79 | 80 | } // end namespace 81 | -------------------------------------------------------------------------------- /src/cutsim.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include "octree.hpp" 32 | #include "octnode.hpp" 33 | #include "volume.hpp" 34 | #include "marching_cubes.hpp" 35 | #include "cube_wireframe.hpp" 36 | #include "gldata.hpp" 37 | 38 | namespace cutsim 39 | { 40 | 41 | /// a Cutsim stores/manipulates an Octree stock model, uses an IsoSurfaceAlgorithm 42 | /// algorithm to generate surface triangles, and communicates with 43 | /// the corresponding GLData surface which is used for rendering 44 | /// 45 | /// the stock is modified using boolean operations: 46 | /// - sum / union 47 | /// - subtract / difference 48 | /// - product / intersection 49 | /// 50 | /// after a boolean operation, calling updateGL() will update the graphics object 51 | /// 52 | class Cutsim 53 | { 54 | 55 | public: 56 | /// create a cutting simulation 57 | /// \param octree_size side length of the depth=0 octree cube 58 | /// \param octree_max_depth maximum sub-division depth of the octree 59 | /// each linear dimension (xyz) will potentially be divided into 60 | /// smaller cubes, with a minimum side-length of octree_size/pow(2,max_depth) 61 | /// in practice max_depth= 6 or 7 works for testing, and 9 or 10 looks very smooth (but is slower) 62 | /// \param gld the GLData used to draw the stock 63 | Cutsim(double octree_size, unsigned int octree_max_depth, GLData *gld, IsoSurfaceAlgorithm *iso); 64 | virtual ~Cutsim(); 65 | void diff_volume(const Volume *vol); ///< subtract/diff given Volume 66 | void sum_volume(const Volume *vol); ///< sum/union given Volume 67 | void intersect_volume(const Volume *vol); ///< intersect/"and" given Volume 68 | void updateGL(); ///< update the GL-data 69 | 70 | void init(unsigned int n); 71 | std::string str() const; 72 | 73 | private: 74 | IsoSurfaceAlgorithm *iso_algo; // the isosurface-extraction algorithm to use 75 | Octree *tree; // this is the stock model 76 | GLData *g; // this is the graphics object, for rendering 77 | }; 78 | 79 | } // end Cutsim namespace 80 | -------------------------------------------------------------------------------- /src/cutsim_py.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include 21 | namespace bp = boost::python; 22 | 23 | #include "cutsim.hpp" 24 | #include "gldata.hpp" 25 | #include "volume.hpp" 26 | #include "isosurface.hpp" 27 | 28 | // python wrapper for libcutsim classes & functions 29 | 30 | BOOST_PYTHON_MODULE(libcutsim) 31 | { 32 | using namespace cutsim; 33 | 34 | bp::class_("Cutsim", bp::no_init) 35 | .def(bp::init()) 36 | .def("init", &Cutsim::init) 37 | .def("diff_volume", &Cutsim::diff_volume) 38 | .def("sum_volume", &Cutsim::sum_volume) 39 | .def("updateGL", &Cutsim::updateGL) 40 | .def("__str__", &Cutsim::str); 41 | bp::class_("GLData") 42 | .def("get_triangles", &GLData::get_triangles) 43 | .def("get_lines", &GLData::get_lines) 44 | .def("get_stl", &GLData::get_stl) 45 | 46 | .def("__str__", &GLData::str); 47 | bp::class_("GLVertex") 48 | .add_property("x", &GLVertex::x) 49 | .add_property("y", &GLVertex::y) 50 | .add_property("z", &GLVertex::z) 51 | .add_property("r", &GLVertex::r) 52 | .add_property("g", &GLVertex::g) 53 | .add_property("b", &GLVertex::b); 54 | bp::class_("Volume") 55 | .def("setCenter", &Volume::setCenter); 56 | bp::class_>("SphereVolume") 57 | .def("setRadius", &SphereVolume::setRadius); 58 | bp::class_>("CubeVolume") 59 | .def("setSide", &CubeVolume::setSide); 60 | bp::class_>("ConeVolume") 61 | .def("setHeight", &ConeVolume::setHeight); 62 | bp::class_>("MeshVolume") 63 | .def("loadMesh", &MeshVolume::loadMesh) 64 | .def("loadStl", &MeshVolume::loadStl) 65 | .def("setMeshCenter", &MeshVolume::setMeshCenter); 66 | bp::class_("IsoSurfaceAlgorithm"); 67 | bp::class_>("MarchingCubes"); 68 | bp::class_>("CubeWireFrame"); 69 | } 70 | -------------------------------------------------------------------------------- /src/facet.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * Copyright 2015 Kazuyasu Hamada (k-hamada@gifu-u.ac.jp) 4 | * 5 | * This file is part of libcutsim. 6 | * 7 | * libcutsim is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * libcutsim is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with libcutsim. If not, see . 19 | */ 20 | 21 | #ifndef FACET_H 22 | #define FACET_H 23 | 24 | #include 25 | 26 | #include "glvertex.hpp" 27 | 28 | namespace cutsim 29 | { 30 | 31 | class Facet 32 | { 33 | 34 | public: 35 | Facet(GLVertex n, GLVertex p1, GLVertex p2, GLVertex p3) 36 | { 37 | normal = n; 38 | v1 = p1; 39 | v2 = p2; 40 | v3 = p3; 41 | } 42 | GLVertex normal; 43 | GLVertex v1, v2, v3; 44 | }; 45 | 46 | } 47 | 48 | #endif // FACET_H 49 | -------------------------------------------------------------------------------- /src/fileio.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "fileio.hpp" 30 | 31 | namespace cutsim 32 | { 33 | 34 | FileIO::FileIO() 35 | { 36 | //std::cout << "FileIO object created" << std::endl; 37 | } 38 | 39 | FileIO::~FileIO() 40 | { 41 | //std::cout << "FileIO object destroyed" << std::endl; 42 | facets.clear(); 43 | } 44 | 45 | std::vector FileIO::getFacets() 46 | { 47 | 48 | //TODO: Check the facets are valid before returning 49 | return facets; 50 | } 51 | 52 | bool FileIO::loadStl(boost::python::str fPath) 53 | { 54 | 55 | // Load mesh data from binary stl file 56 | std::cout << "Loading Data From STL File" << std::endl; 57 | std::string filePath = boost::python::extract(fPath); 58 | 59 | // clear the facets 60 | facets.clear(); 61 | 62 | /// Open file to check if its an ASCII file 63 | std::string line; 64 | std::ifstream stlFile(filePath); 65 | bool binaryFile = false; 66 | 67 | if (stlFile.is_open()) 68 | { 69 | getline(stlFile, line); 70 | // ASCII stl files should begin with the word 'solid' 71 | // Check if the first line of the file contains 'solid' 72 | if (!boost::algorithm::contains(line, "solid")) 73 | { 74 | binaryFile = true; 75 | stlFile.close(); 76 | } 77 | 78 | if (!binaryFile) 79 | { 80 | 81 | GLVertex v, n; // vertex, normal 82 | std::vector vertices; 83 | int vertexTracker = 0; 84 | int vertexTotal = 0; 85 | int facetTotal = 0; 86 | std::string stlString; 87 | 88 | while (getline(stlFile, line)) 89 | { 90 | // Typical facet definition 91 | 92 | // facet normal -0.000000 -1.000000 -0.000000 93 | // outer loop 94 | // vertex 100.000000 0.000000 10.000000 95 | // vertex 0.000000 0.000000 10.000000 96 | // vertex 100.000000 0.000000 0.000000 97 | // endloop 98 | // endfacet 99 | 100 | /// normal 101 | stlString = "facet normal"; 102 | if (boost::algorithm::contains(line, stlString)) 103 | { 104 | n = parseStlLine(line, stlString); 105 | } 106 | 107 | /// vertex 108 | stlString = "vertex"; 109 | if (boost::algorithm::contains(line, stlString)) 110 | { 111 | v = parseStlLine(line, stlString); 112 | vertices.push_back(v); 113 | vertexTotal++; 114 | vertexTracker++; 115 | } 116 | 117 | //endfacet 118 | stlString = "endfacet"; 119 | if (boost::algorithm::contains(line, stlString)) 120 | { 121 | if (vertices.size() == 3) 122 | { 123 | facets.push_back(new Facet(n, vertices[0], vertices[1], vertices[2])); 124 | } 125 | else 126 | { 127 | //TODO: handle processing errors 128 | std::cout << "Error processing vertex" << std::endl; 129 | } 130 | vertexTracker = 0; 131 | vertices.clear(); 132 | facetTotal++; 133 | } 134 | 135 | //endsolid 136 | stlString = "endsolid"; 137 | if (boost::algorithm::contains(line, stlString)) 138 | { 139 | std::cout << "stl import complete - processed " << facetTotal << " facets, with " << vertexTotal << " vertices" << std::endl; 140 | return true; 141 | } 142 | } 143 | 144 | //reached the end of the file, possibly not an stl of formatting is incorrect. 145 | return false; 146 | } 147 | } 148 | else 149 | { 150 | //TODO: handle processing errors 151 | std::cout << "Error opening stl file" << std::endl; 152 | return false; 153 | } 154 | 155 | /// Binary 156 | if (binaryFile) 157 | { 158 | 159 | std::ifstream stlFile(filePath.c_str(), std::ios::in | std::ios::binary); 160 | 161 | if (!stlFile) 162 | { 163 | std::cout << "Error reading file" << std::endl; 164 | assert(!stlFile); 165 | return false; 166 | } 167 | 168 | char headerData[80] = ""; 169 | char triangleData[4]; 170 | stlFile.read(headerData, 80); 171 | stlFile.read(triangleData, 4); 172 | 173 | std::string headerString(headerData); 174 | //TODO: The header may contain ASCII string for units 175 | // see: https://en.wikipedia.org/wiki/STL_(file_format)#ASCII_STL 176 | std::cout << "STL Header: " << headerString << std::endl; 177 | 178 | // cast triangleData to an unsigned int 179 | unsigned int triangleCount = static_cast(*triangleData); 180 | 181 | std::cout << "Importing " << triangleCount << " Triangles" << std::endl; 182 | 183 | if (!triangleCount) 184 | { 185 | std::cout << "Error reading data" << std::endl; 186 | assert(!triangleCount); 187 | return false; 188 | } 189 | 190 | for (unsigned int i = 0; i < triangleCount; i++) 191 | { 192 | 193 | GLVertex normal = parseStlData(stlFile); 194 | GLVertex v1 = parseStlData(stlFile); 195 | GLVertex v2 = parseStlData(stlFile); 196 | GLVertex v3 = parseStlData(stlFile); 197 | 198 | facets.push_back(new Facet(normal, v1, v2, v3)); 199 | 200 | char attribute[2]; 201 | stlFile.read(attribute, 2); 202 | } 203 | } 204 | 205 | stlFile.close(); 206 | 207 | // file loaded successfully, return true 208 | return true; 209 | } 210 | 211 | GLVertex FileIO::parseStlData(std::ifstream &stlFile) 212 | { 213 | // parse the binary stl data to a GLVertex and return 214 | // TODO: error checking required 215 | // valid numerical data? 216 | 217 | float x; 218 | stlFile.read(reinterpret_cast(&x), sizeof(float)); 219 | float y; 220 | stlFile.read(reinterpret_cast(&y), sizeof(float)); 221 | float z; 222 | stlFile.read(reinterpret_cast(&z), sizeof(float)); 223 | 224 | return GLVertex(x, y, z); 225 | } 226 | 227 | GLVertex FileIO::parseStlLine(std::string line, std::string stlString) 228 | { 229 | // parse the ascii stl line to a GLVertex and return 230 | // TODO: error checking required 231 | // valid numerical data? 232 | 233 | boost::algorithm::replace_all(line, stlString, ""); 234 | boost::algorithm::trim(line); 235 | std::vector strVec; 236 | strVec = boost::algorithm::split(strVec, line, boost::algorithm::is_space()); 237 | 238 | GLVertex vertex; 239 | if (strVec.size() == 3) 240 | { 241 | vertex.x = std::stof(strVec[0]); 242 | vertex.y = std::stof(strVec[1]); 243 | vertex.z = std::stof(strVec[2]); 244 | } 245 | 246 | return vertex; 247 | } 248 | 249 | bool FileIO::loadMesh(boost::python::list pyfacets) 250 | { 251 | // Load mesh data from python facets 252 | // expected input 253 | // [[(normal),(v1), (v2), (v3)],...] 254 | // TODO: check the face data structure is valid 255 | 256 | // clear the facets 257 | facets.clear(); 258 | 259 | boost::python::ssize_t len = boost::python::len(pyfacets); 260 | 261 | if (!len) 262 | { 263 | std::cout << "Mesh data invalid" << std::endl; 264 | return false; 265 | } 266 | 267 | std::cout << " Load Mesh Shape from " << len << " Facets" << std::endl; 268 | 269 | GLVertex vertexData[4] = {}; 270 | 271 | for (int i = 0; i < len; i++) 272 | { 273 | boost::python::ssize_t dataLen = boost::python::len(pyfacets[i]); 274 | if (dataLen == 4) 275 | { 276 | // std::cout << " Loading Data from facet:" << i << std::endl; 277 | for (int j = 0; j < dataLen; j++) 278 | { 279 | 280 | float f0 = boost::python::extract(pyfacets[i][j][0]); 281 | float f1 = boost::python::extract(pyfacets[i][j][1]); 282 | float f2 = boost::python::extract(pyfacets[i][j][2]); 283 | 284 | // std::cout << "Facet " << i << " vertex " << j << " x " << f0 << " y " << f1 << " z " << f2 << std::endl; 285 | 286 | vertexData[j].x = f0; 287 | vertexData[j].y = f1; 288 | vertexData[j].z = f2; 289 | } 290 | 291 | facets.push_back(new Facet(vertexData[0], vertexData[1], vertexData[2], vertexData[3])); 292 | } 293 | } 294 | 295 | // file loaded successfully, return true 296 | return true; 297 | } 298 | 299 | /// export stl file and return the path to python 300 | std::string FileIO::writeStl(std::vector indexArray, std::vector vertexArray, boost::python::str fPath, bool binary) 301 | { 302 | 303 | boost::python::str out; 304 | std::ofstream stlFile; 305 | 306 | std::string filePath = boost::python::extract(fPath); 307 | 308 | /// make sure the last charater isn't a seperator 309 | std::string lastchar = filePath.substr(filePath.length() - 1); 310 | if (boost::algorithm::contains(lastchar, "\\") || boost::algorithm::contains(lastchar, "/")) 311 | { 312 | filePath = filePath + "libcutsim.stl"; 313 | } 314 | 315 | /// check for a .stl file extension 316 | std::string ext = filePath.substr(filePath.length() - 4); 317 | boost::algorithm::to_lower(ext); 318 | 319 | if (ext != ".stl") 320 | { 321 | filePath = filePath + ".stl"; 322 | } 323 | 324 | /// split the filename from the path name and check the path exists 325 | std::size_t pos = filePath.find_last_of("/\\"); 326 | std::string path = filePath.substr(0, pos); 327 | 328 | if (!std::filesystem::exists(path)) 329 | { 330 | std::filesystem::create_directory(path); 331 | } 332 | 333 | if (binary) 334 | { 335 | 336 | stlFile.open(filePath.c_str(), std::ios::out | std::ios::binary); 337 | std::string header_info = "output"; 338 | char head[81]; 339 | std::strncpy(head, header_info.c_str(), 81); 340 | unsigned long len = indexArray.size() / 3; 341 | 342 | stlFile.write(head, 80); 343 | stlFile.write((char *)&len, 4); 344 | } 345 | else 346 | { 347 | stlFile.open(filePath.c_str()); 348 | stlFile << "solid libcutsim" << std::endl; 349 | } 350 | 351 | for (unsigned int n = 0; n < indexArray.size(); n += 3) 352 | { 353 | 354 | GLVertex p1 = vertexArray[n]; 355 | GLVertex p2 = vertexArray[n + 1]; 356 | GLVertex p3 = vertexArray[n + 2]; 357 | 358 | if (binary) 359 | { 360 | //normal coordinates 361 | stlFile.write((char *)&p1.nx, sizeof(p1.nx)); 362 | stlFile.write((char *)&p1.ny, sizeof(p1.ny)); 363 | stlFile.write((char *)&p1.nz, sizeof(p1.nz)); 364 | 365 | //p1 coordinates 366 | stlFile.write((char *)&p1.x, sizeof(p1.x)); 367 | stlFile.write((char *)&p1.y, sizeof(p1.y)); 368 | stlFile.write((char *)&p1.z, sizeof(p1.z)); 369 | 370 | //p2 coordinates 371 | stlFile.write((char *)&p2.x, sizeof(p2.x)); 372 | stlFile.write((char *)&p2.y, sizeof(p2.y)); 373 | stlFile.write((char *)&p2.z, sizeof(p2.z)); 374 | 375 | //p3 coordinates 376 | stlFile.write((char *)&p3.x, sizeof(p3.x)); 377 | stlFile.write((char *)&p3.y, sizeof(p3.y)); 378 | stlFile.write((char *)&p3.z, sizeof(p3.z)); 379 | 380 | char attribute[2] = "0"; 381 | stlFile.write(attribute, sizeof(attribute)); 382 | } 383 | else 384 | { 385 | 386 | stlFile << "facet normal " << p1.nx << " " << p1.ny << " " << p1.nz << std::endl; 387 | stlFile << " outer loop" << std::endl; 388 | stlFile << " vertex " << p1.x << " " << p1.y << " " << p1.z << std::endl; 389 | stlFile << " vertex " << p2.x << " " << p2.y << " " << p2.z << std::endl; 390 | stlFile << " vertex " << p3.x << " " << p3.y << " " << p3.z << std::endl; 391 | stlFile << " endloop" << std::endl; 392 | stlFile << "endfacet" << std::endl; 393 | } 394 | } 395 | 396 | if (!binary) 397 | { 398 | stlFile << "endsolid libcutsim" << std::endl; 399 | } 400 | 401 | stlFile.close(); 402 | //return boost::python::str(filePath); 403 | return filePath; 404 | } 405 | 406 | } // end namespace 407 | // end of file stl.cpp 408 | -------------------------------------------------------------------------------- /src/fileio.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #ifndef FILEIO_H 21 | #define FILEIO_H 22 | 23 | #include 24 | 25 | #include "facet.hpp" 26 | #include "glvertex.hpp" 27 | 28 | 29 | 30 | namespace cutsim { 31 | 32 | class FileIO { 33 | 34 | public: 35 | FileIO(); 36 | ~FileIO(); 37 | bool loadStl(boost::python::str); 38 | bool loadMesh(boost::python::list); 39 | std::vector getFacets(); 40 | std::string writeStl(std::vector indexArray, std::vector vertexArray, boost::python::str fPath, bool binary); 41 | 42 | private: 43 | GLVertex parseStlData(std::ifstream&); 44 | GLVertex parseStlLine(std::string, std::string); 45 | 46 | std::vector facets; 47 | 48 | }; 49 | 50 | }// end namespace 51 | // end file volume.hpp 52 | 53 | #endif // FILEIO_H -------------------------------------------------------------------------------- /src/gldata.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "gldata.hpp" 25 | #include "octnode.hpp" 26 | #include "fileio.hpp" 27 | 28 | namespace cutsim { 29 | 30 | GLData::GLData() { 31 | polyVerts = 3; 32 | } 33 | 34 | /// add a vertex with given position and color, return its index 35 | unsigned int GLData::addVertex(float x, float y, float z, float r, float g, float b) { 36 | return addVertex( GLVertex(x,y,z,r,g,b), NULL ); 37 | } 38 | 39 | /// add vertex, associate given Octnode with the vertex, and return index 40 | unsigned int GLData::addVertex(GLVertex v, Octnode* n) { 41 | // add vertex with empty polygon-list. 42 | unsigned int idx = vertexArray.size(); 43 | vertexArray.push_back(v); 44 | vertexDataArray.push_back( VertexData() ); 45 | vertexDataArray[idx].node = n; 46 | assert( vertexArray.size() == vertexDataArray.size() ); 47 | return idx; // return index of newly appended vertex 48 | } 49 | 50 | /// add vertex at given position 51 | unsigned int GLData::addVertex(float x, float y, float z, float r, float g, float b, Octnode* n) { 52 | unsigned int id = addVertex(x,y,z,r,g,b); 53 | vertexDataArray[id].node = n; 54 | return id; 55 | } 56 | 57 | /// set vertex normal 58 | void GLData::setNormal(unsigned int vertexIdx, float nx, float ny, float nz) { 59 | vertexArray[vertexIdx].setNormal(nx,ny,nz); 60 | } 61 | 62 | /// modify given vertex 63 | void GLData::modifyVertex( unsigned int id, float x, float y, float z, float r, float g, float b, float nx, float ny, float nz) { 64 | GLVertex p = GLVertex(x,y,z,r,g,b,nx,ny,nz); 65 | vertexArray[id] = p; 66 | } 67 | 68 | /// remove vertex with given index 69 | void GLData::removeVertex( unsigned int vertexIdx ) { 70 | // i) for each polygon of this vertex, call remove_polygon: 71 | typedef std::set< unsigned int, std::greater > PolygonSet; 72 | PolygonSet pset = vertexDataArray[vertexIdx].polygons; 73 | BOOST_FOREACH( unsigned int polygonIdx, pset ) { 74 | removePolygon( polygonIdx ); 75 | } 76 | // ii) overwrite with last vertex: 77 | unsigned int lastIdx = vertexArray.size()-1; 78 | if (vertexIdx != lastIdx) { 79 | vertexArray[vertexIdx] = vertexArray[lastIdx]; 80 | vertexDataArray[vertexIdx] = vertexDataArray[lastIdx]; 81 | // notify octree-node with new index here! 82 | // vertex that was at lastIdx is now at vertexIdx 83 | vertexDataArray[vertexIdx].node->swapIndex( lastIdx, vertexIdx ); 84 | 85 | // request each polygon to re-number this vertex. 86 | BOOST_FOREACH( unsigned int polygonIdx, vertexDataArray[vertexIdx].polygons ) { 87 | unsigned int idx = polygonIdx*polygonVertices(); 88 | for (int m=0;m& verts) { 102 | // append to indexArray, then request each vertex to update 103 | unsigned int polygonIdx = indexArray.size()/polygonVertices(); 104 | BOOST_FOREACH( unsigned int vertex, verts ) { 105 | indexArray.push_back(vertex); 106 | vertexDataArray[vertex].addPolygon(polygonIdx); // add index to vertex i1 107 | } 108 | return polygonIdx; 109 | } 110 | 111 | /// remove polygon at given index 112 | void GLData::removePolygon( unsigned int polygonIdx) { 113 | unsigned int idx = polygonVertices()*polygonIdx; // start-index for polygon 114 | // i) request remove for each vertex in polygon: 115 | for (int m=0; m. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "glvertex.hpp" 33 | 34 | namespace cutsim 35 | { 36 | 37 | class Octnode; 38 | 39 | /// This class holds additional vertex data not needed for OpenGL rendering 40 | /// but required for the isosurface or cutting-simulation algorithm. 41 | struct VertexData 42 | { 43 | /// string output 44 | void str() 45 | { 46 | BOOST_FOREACH (unsigned int pIdx, polygons) 47 | { 48 | std::cout << pIdx << " "; 49 | } 50 | } 51 | /// insert polygon-id to polygon set 52 | inline void addPolygon(unsigned int idx) { polygons.insert(idx); } 53 | /// remove polygon-id from polygon set 54 | inline void removePolygon(unsigned int idx) { polygons.erase(idx); } 55 | /// is the polygon set empty? 56 | inline bool empty() { return polygons.empty(); } 57 | // DATA 58 | /// The set of polygons. Each polygon has an uint index which is stored here. 59 | /// Note: we want to access polygons from highest index to lowest, thus compare with "greater" 60 | typedef std::set> PolygonSet; 61 | /// the polygons to which this vertex belongs. i.e. for each vertex we store in this set all the polygons to which it belongs. 62 | PolygonSet polygons; 63 | /// the Octnode that created this vertex. 64 | /// when an Octnode is cut the corresponding vertex/vertices are deleted. 65 | /// when a vertex is deleted, the Octnode that generated it is notified 66 | Octnode *node; 67 | // (an alternative callback-mechanism would be to store a function-pointer or similar) 68 | }; 69 | 70 | // See the "secret sauce" paper: 71 | // http://www.cs.berkeley.edu/~jrs/meshpapers/SchaeferWarren2.pdf 72 | // or 73 | // http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.13.2631 74 | // 75 | // The following four operations are perfomed by an IsoSurfaceAlgorithm 76 | // 77 | // - add vertex 78 | // add vertex with empty polygon list and pointer to octree-node 79 | // 80 | // - remove vertex (also removes associated polygons) 81 | // process list of polygons, from highest to lowest. call remove_polygon on each poly. 82 | // overwrite with last vertex. shorten list. request each poly to re-number. 83 | // 84 | // - add polygon 85 | // append new polygon to end of list, request each vertex to add new polygon to list. 86 | // 87 | // - remove_polygon( polygonIndex ) 88 | // i) for each vertex: request remove this polygons index from list 89 | // ii) then remove polygon from polygon-list: overwrite with last polygon, then shorten list. 90 | // iii) process each vertex in the moved polygon, request renumber on each vert for this poly 91 | // 92 | // data structure: 93 | // vertex-table: index, pos(x,y,z) , polygons(id1,id2,...), Node-pointer to octree 94 | // polygon-table: index, vertex-list 95 | // 96 | 97 | /// a GLData object holds data which is drawn/rendered by OpenGL 98 | /// 99 | /// Data is held in two arrays vertexArray and indexArray 100 | /// 101 | /// vertexArray holds GLVertex vertices for drawing 102 | /// a corresponding vertexDataArray holds extra data for each vertex. the extra data is: 103 | /// - which polygons make use of this vertex 104 | /// - which octree node created this vertex 105 | /// 106 | /// indexArray holds polygon data. If triangles(quads) are to be drawn 107 | /// we draw a triangle(quad) for each set of three(four) indices in this array 108 | /// 109 | class GLData 110 | { 111 | public: 112 | GLData(); 113 | unsigned int addVertex(float x, float y, float z, float r, float g, float b); 114 | unsigned int addVertex(GLVertex v, Octnode *n); 115 | unsigned int addVertex(float x, float y, float z, float r, float g, float b, Octnode *n); 116 | void setNormal(unsigned int vertexIdx, float nx, float ny, float nz); 117 | void modifyVertex(unsigned int id, float x, float y, float z, float r, float g, float b, float nx, float ny, float nz); 118 | void removeVertex(unsigned int vertexIdx); 119 | int addPolygon(std::vector &verts); 120 | void removePolygon(unsigned int polygonIdx); 121 | std::string str(); 122 | boost::python::str get_stl(boost::python::str, bool); 123 | 124 | /// export triangle-list to python 125 | boost::python::list get_triangles() 126 | { 127 | boost::python::list out; 128 | for (unsigned int n = 0; n < indexArray.size(); n += 3) 129 | { 130 | GLVertex p1 = vertexArray[n]; 131 | GLVertex p2 = vertexArray[n + 1]; 132 | GLVertex p3 = vertexArray[n + 2]; 133 | boost::python::list tri; 134 | tri.append(p1); 135 | tri.append(p2); 136 | tri.append(p3); 137 | out.append(tri); 138 | } 139 | return out; 140 | } 141 | 142 | /// export line-list to python 143 | boost::python::list get_lines() 144 | { 145 | boost::python::list out; 146 | for (unsigned int n = 0; n < indexArray.size(); n += 2) 147 | { 148 | GLVertex p1 = vertexArray[n]; 149 | GLVertex p2 = vertexArray[n + 1]; 150 | //GLVertex p3 = vertexArray[n+2]; 151 | boost::python::list tri; 152 | tri.append(p1); 153 | tri.append(p2); 154 | //tri.append(p3); 155 | out.append(tri); 156 | } 157 | return out; 158 | } 159 | 160 | // type of GLData 161 | void setTriangles() 162 | { 163 | triangles = true; 164 | lines = false; 165 | polyVerts = 3; 166 | } 167 | void setLines() 168 | { 169 | lines = true; 170 | triangles = false; 171 | polyVerts = 2; 172 | } 173 | 174 | // these are really properties of GLVertex, so why are they here?? 175 | /// byte-offset for coordinate data in the vertexArray 176 | static const unsigned int vertex_offset = 0; 177 | /// byte-offset for color data in the vertexArray 178 | static const unsigned int color_offset = 12; 179 | /// byte-offset for normal data in the vertexArray 180 | static const unsigned int normal_offset = 24; 181 | 182 | /// pointer to the vertex-array 183 | const GLVertex *getVertexArray() const { return vertexArray.data(); } 184 | /// pointer to the index-array 185 | const unsigned int *getIndexArray() const { return indexArray.data(); } 186 | /// number of vertices per polygon (usually 3 or 4) 187 | inline const int polygonVertices() const { return polyVerts; } 188 | /// length of indexArray 189 | inline const int indexCount() const { return indexArray.size(); } 190 | 191 | protected: 192 | std::vector vertexArray; ///< vertex coordinates 193 | std::vector vertexDataArray; ///< non-OpenGL data associated with vertices. 194 | std::vector indexArray; ///< polygon indices 195 | int polyVerts; ///< number of vertices per polygon. 3 for triangles, 4 for quads. 196 | bool triangles; ///< true GLData should be drawn as triangles 197 | bool lines; ///< true if GLData should be drawn as lines 198 | }; 199 | 200 | } // end namespace 201 | -------------------------------------------------------------------------------- /src/glvertex.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * Copyright 2015 Kazuyasu Hamada (k-hamada@gifu-u.ac.jp) 4 | * 5 | * This file is part of libcutsim. 6 | * 7 | * libcutsim is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * libcutsim is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with libcutsim. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | namespace cutsim 27 | { 28 | 29 | /// color of a GL-vertex 30 | struct Color 31 | { 32 | float r; ///< red 33 | float g; ///< green 34 | float b; ///< blue 35 | /// set color 36 | void set(float ri, float gi, float bi) 37 | { 38 | r = ri; 39 | g = gi; 40 | b = bi; 41 | } 42 | }; 43 | 44 | /// a vertex/point in 3D, with (x,y,z) coordinates of type float 45 | /// normal is (nx,ny,nz) 46 | /// color is (r,g,b) 47 | struct GLVertex 48 | { 49 | /// default (0,0,0) ctor 50 | GLVertex() : x(0), y(0), z(0), r(0), g(0), b(0) {} 51 | /// ctor with given (x,y,z) 52 | GLVertex(float x, float y, float z) 53 | : x(x), y(y), z(z), r(0), g(0), b(0) {} 54 | /// ctor with given (x,y,z) and (r,g,b) 55 | GLVertex(float x, float y, float z, float r, float g, float b) 56 | : x(x), y(y), z(z), r(r), g(g), b(b) {} 57 | /// ctor with given position (x,y,z) color (r,g,b) and normal (xn,yn,zn) 58 | GLVertex(float x, float y, float z, float red, float gre, float blu, float xn, float yn, float zn) 59 | : x(x), y(y), z(z), r(red), g(gre), b(blu), nx(xn), ny(yn), nz(zn) {} 60 | 61 | /// set normal 62 | void setNormal(float xn, float yn, float zn) 63 | { 64 | nx = xn; 65 | ny = yn; 66 | nz = zn; 67 | // normalize: 68 | float norm = sqrt(nx * nx + ny * ny + nz * nz); 69 | if (norm != 1.0) 70 | { 71 | nx /= norm; 72 | ny /= norm; 73 | nz /= norm; 74 | } 75 | } 76 | /// set the vertex color 77 | void setColor(Color c) 78 | { 79 | setColor(c.r, c.g, c.b); 80 | } 81 | /// set the vertex color 82 | void setColor(float red, float green, float blue) 83 | { 84 | r = red; 85 | g = green; 86 | b = blue; 87 | } 88 | 89 | /// assume p1-p2-p3 forms a triangle. set normals. set color. 90 | static void set_normal_and_color(GLVertex &p1, GLVertex &p2, GLVertex &p3, Color c) 91 | { 92 | GLVertex n = (p1 - p2).cross(p1 - p3); 93 | n.normalize(); 94 | p1.setNormal(n.x, n.y, n.z); 95 | p2.setNormal(n.x, n.y, n.z); 96 | p3.setNormal(n.x, n.y, n.z); 97 | p1.setColor(c); 98 | p2.setColor(c); 99 | p3.setColor(c); 100 | } 101 | /// string output 102 | std::string str() { return "glvertex"; } //(%1, %2, %3 )").arg(x).arg(y).arg(z); } 103 | 104 | // DATA 105 | float x; ///< x-coordinate 106 | float y; ///< y-coordinate 107 | float z; ///< z-coordinate 108 | // color, 3*4=12-bytes offset from position data. 109 | float r; ///< red 110 | float g; ///< green 111 | float b; ///< blue 112 | // normal, 24-bytes offset 113 | float nx; ///< normal x-coordinate 114 | float ny; ///< normal y-coordinate 115 | float nz; ///< normal z-coordinate 116 | 117 | // Operators 118 | /// return length 119 | float norm() const 120 | { 121 | return sqrt(x * x + y * y + z * z); 122 | } 123 | /// set length to 1 124 | void normalize() 125 | { 126 | if (this->norm() != 0.0) 127 | *this *= (1 / this->norm()); 128 | } 129 | /// multiplication by scalar 130 | GLVertex &operator*=(const double &a) 131 | { 132 | x *= a; 133 | y *= a; 134 | z *= a; 135 | return *this; 136 | } 137 | /// multiplication by scalar 138 | GLVertex operator*(const double &a) const 139 | { 140 | return GLVertex(*this) *= a; 141 | } 142 | /// vector addition 143 | GLVertex &operator+=(const GLVertex &p) 144 | { 145 | x += p.x; 146 | y += p.y; 147 | z += p.z; 148 | return *this; 149 | } 150 | /// vector addition 151 | const GLVertex operator+(const GLVertex &p) const 152 | { 153 | return GLVertex(*this) += p; 154 | } 155 | /// vector subtraction 156 | GLVertex &operator-=(const GLVertex &p) 157 | { 158 | x -= p.x; 159 | y -= p.y; 160 | z -= p.z; 161 | return *this; 162 | } 163 | /// vector subtraction 164 | const GLVertex operator-(const GLVertex &p) const 165 | { 166 | return GLVertex(*this) -= p; 167 | } 168 | /// cross product 169 | GLVertex cross(const GLVertex &p) const 170 | { 171 | float xc = y * p.z - z * p.y; 172 | float yc = z * p.x - x * p.z; 173 | float zc = x * p.y - y * p.x; 174 | return GLVertex(xc, yc, zc); 175 | } 176 | 177 | /// dot product 178 | float dot(const GLVertex &p) const 179 | { 180 | return x * p.x + y * p.y + z * p.z; 181 | } 182 | 183 | /// rotate vertex by amount alfa around o->v axis 184 | void rotate(const GLVertex &origin, const GLVertex &v, float alfa) 185 | { 186 | // rotate point p by alfa deg/rad around vector o->v 187 | // p = o + M*(p-o) 188 | float M[3][3]; 189 | float c = cos(alfa); 190 | float D = 1.0 - c; 191 | float s = sin(alfa); 192 | M[0][0] = v.x * v.x * D + c; 193 | M[0][1] = v.y * v.x * D + v.z * s; 194 | M[0][2] = v.z * v.x * D - v.y * s; 195 | M[1][0] = v.x * v.y * D - v.z * s; 196 | M[1][1] = v.y * v.y * D + c; 197 | M[1][2] = v.z * v.y * D + v.x * s; 198 | M[2][0] = v.x * v.z * D + v.y * s; 199 | M[2][1] = v.y * v.z * D - v.x * s; 200 | M[2][2] = v.z * v.z * D + c; 201 | // matrix multiply 202 | float vector[3]; 203 | vector[0] = x - origin.x; 204 | vector[1] = y - origin.y; 205 | vector[2] = z - origin.z; 206 | float result[3]; 207 | for (int i = 0; i < 3; i++) 208 | { 209 | result[i] = 0; 210 | for (int j = 0; j < 3; j++) 211 | { 212 | result[i] += vector[j] * M[i][j]; 213 | } 214 | } 215 | x = origin.x + result[0]; 216 | y = origin.y + result[1]; 217 | z = origin.z + result[2]; 218 | } 219 | /// rotate vertex around A and C axis 220 | GLVertex rotateAC(const float a, const float c) 221 | { 222 | float M[3][3]; 223 | float zC = cos(c); 224 | float zS = sin(c); 225 | float xC = cos(a); 226 | float xS = sin(a); 227 | M[0][0] = zC; 228 | M[0][1] = -zS; 229 | M[0][2] = 0.0; 230 | M[1][0] = zS * xC; 231 | M[1][1] = zC * xC; 232 | M[1][2] = -xS; 233 | M[2][0] = zS * xS; 234 | M[2][1] = zC * xS; 235 | M[2][2] = xC; 236 | // matrix multiply 237 | GLVertex result; 238 | result.x = x * M[0][0] + y * M[0][1]; 239 | result.y = x * M[1][0] + y * M[1][1] + z * M[1][2]; 240 | result.z = x * M[2][0] + y * M[2][1] + z * M[2][2]; 241 | return result; 242 | } 243 | /// rotate vertex by A and C rotation matrix 244 | GLVertex rotateAC(const float M[3][3]) 245 | { 246 | // matrix multiply 247 | GLVertex result; 248 | result.x = x * M[0][0] + y * M[0][1]; 249 | result.y = x * M[1][0] + y * M[1][1] + z * M[1][2]; 250 | result.z = x * M[2][0] + y * M[2][1] + z * M[2][2]; 251 | return result; 252 | } 253 | }; 254 | 255 | } // end namespace 256 | -------------------------------------------------------------------------------- /src/isosurface.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | #include "gldata.hpp" 26 | #include "octree.hpp" 27 | #include "octnode.hpp" 28 | 29 | namespace cutsim 30 | { 31 | 32 | /// abstract base class for isosurface extraction algorithms 33 | /// 34 | /// isosurface algorithms produce vertices and polygons based on an Octree. 35 | /// Vertices and polygons are added to a GLData using addVertex, addPolygon, etc. 36 | /// 37 | class IsoSurfaceAlgorithm 38 | { 39 | public: 40 | /// create algorithm wich writes to given GLData and reads from given Octree 41 | IsoSurfaceAlgorithm() {} 42 | virtual ~IsoSurfaceAlgorithm() {} 43 | void set_gl(GLData *gl) { g = gl; } 44 | void set_tree(Octree *tr) { tree = tr; } 45 | virtual void set_polyVerts() {} ///< set vertices per polygon (2, 3, or 4) 46 | /// update GLData 47 | virtual void updateGL() { updateGL(tree->root); } 48 | 49 | protected: 50 | /// update the GLData for the given Octnode. re-implement in sub-class 51 | virtual void updateGL(Octnode *node) {} 52 | 53 | // DATA 54 | int update_calls; ///< how many updateGL calls were made? for debug 55 | int valid_count; ///< how many valid nodes? for debug 56 | 57 | GLData *g; ///< the GLData to which we udpate vertices/polygons 58 | Octree *tree; ///< the Octree which we traverse to update GLData 59 | }; 60 | 61 | } // end namespace 62 | 63 | // end file isosurface.hpp 64 | -------------------------------------------------------------------------------- /src/marching_cubes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include "marching_cubes.hpp" 21 | 22 | namespace cutsim 23 | { 24 | 25 | // this could maybe moved to the base-class, if all sub-classes do it the same way? 26 | void MarchingCubes::updateGL(Octnode *node) 27 | { 28 | // traverse tree here and call polygonize_node 29 | if (node->valid()) 30 | return; // don't process valid nodes 31 | 32 | if (node->is_undecided() && node->isLeaf()) 33 | { 34 | assert(!node->valid()); 35 | node->clearVertexSet(); 36 | mc_node(node); // create triangles for undecided leaf-node 37 | node->setValid(); 38 | } 39 | 40 | // current node done, now recurse into tree. 41 | if (node->childcount == 8) 42 | { 43 | for (unsigned int m = 0; m < 8; m++) 44 | { 45 | if (!node->child[m]->valid()) 46 | { 47 | node->clearVertexSet(); // remove old vertices 48 | updateGL(node->child[m]); // create new ones 49 | } 50 | } 51 | } 52 | } 53 | 54 | /// run mc on one Octnode 55 | /// this generates one or more triangles which are pushed to the GLData 56 | void MarchingCubes::mc_node(Octnode *node) 57 | { 58 | assert(node->childcount == 0); // don't call this on non-leafs! 59 | assert(node->is_undecided()); // must be undecided, completelu inside/outside nodes don't contribute to the surface 60 | unsigned int edgeTableIndex = mc_edgeTableIndex(node); 61 | unsigned int edges = edgeTable[edgeTableIndex]; 62 | std::vector vertices = interpolated_vertices(node, edges); 63 | for (unsigned int i = 0; triTable[edgeTableIndex][i] != -1; i += 3) 64 | { 65 | std::vector triangle; 66 | GLVertex p1 = vertices[triTable[edgeTableIndex][i]]; 67 | GLVertex p2 = vertices[triTable[edgeTableIndex][i + 1]]; 68 | GLVertex p3 = vertices[triTable[edgeTableIndex][i + 2]]; 69 | GLVertex::set_normal_and_color(p1, p2, p3, node->color); 70 | triangle.push_back(g->addVertex(p1, node)); 71 | triangle.push_back(g->addVertex(p2, node)); 72 | triangle.push_back(g->addVertex(p3, node)); 73 | g->addPolygon(triangle); 74 | node->addIndex(triangle[0]); 75 | node->addIndex(triangle[1]); 76 | node->addIndex(triangle[2]); 77 | } 78 | } 79 | 80 | std::vector MarchingCubes::interpolated_vertices(const Octnode *node, unsigned int edges) 81 | { 82 | std::vector vertices(12); 83 | vertices[0] = *(node->vertex[0]); // intialize these to the node-vertex positions (?why?) 84 | vertices[1] = *(node->vertex[1]); 85 | vertices[2] = *(node->vertex[2]); 86 | vertices[3] = *(node->vertex[3]); 87 | vertices[4] = *(node->vertex[4]); 88 | vertices[5] = *(node->vertex[5]); 89 | vertices[6] = *(node->vertex[6]); 90 | vertices[7] = *(node->vertex[7]); 91 | if (edges & 1) 92 | vertices[0] = interpolate(node, 0, 1); 93 | if (edges & 2) 94 | vertices[1] = interpolate(node, 1, 2); 95 | if (edges & 4) 96 | vertices[2] = interpolate(node, 2, 3); 97 | if (edges & 8) 98 | vertices[3] = interpolate(node, 3, 0); 99 | if (edges & 16) 100 | vertices[4] = interpolate(node, 4, 5); 101 | if (edges & 32) 102 | vertices[5] = interpolate(node, 5, 6); 103 | if (edges & 64) 104 | vertices[6] = interpolate(node, 6, 7); 105 | if (edges & 128) 106 | vertices[7] = interpolate(node, 7, 4); 107 | if (edges & 256) 108 | vertices[8] = interpolate(node, 0, 4); 109 | if (edges & 512) 110 | vertices[9] = interpolate(node, 1, 5); 111 | if (edges & 1024) 112 | vertices[10] = interpolate(node, 2, 6); 113 | if (edges & 2048) 114 | vertices[11] = interpolate(node, 3, 7); 115 | return vertices; 116 | } 117 | 118 | /// use linear interpolation of the distance-field between vertices idx1 and idx2 119 | /// to generate a new iso-surface vertex on the idx1-idx2 edge 120 | GLVertex MarchingCubes::interpolate(const Octnode *node, int idx1, int idx2) 121 | { 122 | // p = p1 - f1 (p2-p1)/(f2-f1) 123 | if (!(fabs(node->f[idx2] - node->f[idx1]) > 1e-16)) 124 | std::cout << "mc::interpolate error " << node->f[idx2] << " and " << node->f[idx1] << " don't differ in sign!\n"; 125 | 126 | //assert( ( (node->f[idx2] * node->f[idx1] ) < 0 ) ); // should have unequal sign! 127 | assert(fabs(node->f[idx2] - node->f[idx1]) > 1e-16); 128 | return *(node->vertex[idx1]) - (*(node->vertex[idx2]) - (*(node->vertex[idx1]))) * 129 | (1.0 / (node->f[idx2] - node->f[idx1])) * node->f[idx1]; 130 | } 131 | 132 | // based on the funcion values (positive or negative) at the corners of the node, 133 | // calculate the edgeTableIndex 134 | unsigned int MarchingCubes::mc_edgeTableIndex(const Octnode *node) 135 | { 136 | unsigned int edgeTableIndex = 0; 137 | if (node->f[0] < 0.0) 138 | edgeTableIndex |= 1; 139 | if (node->f[1] < 0.0) 140 | edgeTableIndex |= 2; 141 | if (node->f[2] < 0.0) 142 | edgeTableIndex |= 4; 143 | if (node->f[3] < 0.0) 144 | edgeTableIndex |= 8; 145 | if (node->f[4] < 0.0) 146 | edgeTableIndex |= 16; 147 | if (node->f[5] < 0.0) 148 | edgeTableIndex |= 32; 149 | if (node->f[6] < 0.0) 150 | edgeTableIndex |= 64; 151 | if (node->f[7] < 0.0) 152 | edgeTableIndex |= 128; 153 | return edgeTableIndex; 154 | } 155 | 156 | // I think the tables are from http://paulbourke.net/geometry/polygonise/ 157 | 158 | // this table stores indices into the triTable below, i.e. it tells 159 | // us which of the 256 cases we are in. 160 | // the function mc_edgeTableIndex() looks at the distance-field signs 161 | // at each corner of the cube and computes an index for this table. 162 | const unsigned int MarchingCubes::edgeTable[256] = { 163 | 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 164 | 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 165 | 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 166 | 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 167 | 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 168 | 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 169 | 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 170 | 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 171 | 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 172 | 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 173 | 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 174 | 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 175 | 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, 176 | 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 177 | 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 178 | 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 179 | 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 180 | 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 181 | 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 182 | 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 183 | 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 184 | 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 185 | 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 186 | 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, 187 | 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 188 | 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, 189 | 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 190 | 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, 191 | 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 192 | 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 193 | 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 194 | 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0}; 195 | 196 | // these are the 256 different cases for how to generate triangles from a cube 197 | // each row has indices, in groups of three, of the triangles to be generated. 198 | // the sequence ends with a -1. 199 | // each row is of length 16, so a maximum of 15/3 = 5 triangles can be generated from one cube 200 | const int MarchingCubes::triTable[256][16] = { 201 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 202 | {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 203 | {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 204 | {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 205 | {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 206 | {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 207 | {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 208 | {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, 209 | {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 210 | {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 211 | {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 212 | {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, 213 | {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 214 | {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, 215 | {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, 216 | {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 217 | {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 218 | {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 219 | {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 220 | {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, 221 | {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 222 | {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, 223 | {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, 224 | {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, 225 | {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 226 | {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, 227 | {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, 228 | {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, 229 | {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, 230 | {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, 231 | {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, 232 | {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, 233 | {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 234 | {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 235 | {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 236 | {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, 237 | {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 238 | {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, 239 | {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, 240 | {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, 241 | {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 242 | {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, 243 | {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, 244 | {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, 245 | {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, 246 | {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, 247 | {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, 248 | {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, 249 | {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 250 | {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, 251 | {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, 252 | {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 253 | {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, 254 | {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, 255 | {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, 256 | {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, 257 | {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, 258 | {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, 259 | {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, 260 | {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, 261 | {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, 262 | {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, 263 | {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, 264 | {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 265 | {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 266 | {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 267 | {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 268 | {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, 269 | {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 270 | {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, 271 | {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, 272 | {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, 273 | {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 274 | {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, 275 | {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, 276 | {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, 277 | {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, 278 | {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, 279 | {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, 280 | {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, 281 | {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 282 | {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, 283 | {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, 284 | {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, 285 | {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, 286 | {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, 287 | {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, 288 | {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, 289 | {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, 290 | {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, 291 | {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, 292 | {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, 293 | {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, 294 | {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, 295 | {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, 296 | {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, 297 | {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 298 | {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, 299 | {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, 300 | {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, 301 | {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, 302 | {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, 303 | {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 304 | {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, 305 | {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, 306 | {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, 307 | {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, 308 | {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, 309 | {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, 310 | {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, 311 | {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, 312 | {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 313 | {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, 314 | {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, 315 | {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, 316 | {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, 317 | {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, 318 | {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, 319 | {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, 320 | {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 321 | {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, 322 | {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, 323 | {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, 324 | {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, 325 | {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, 326 | {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 327 | {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, 328 | {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 329 | {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 330 | {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 331 | {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 332 | {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, 333 | {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 334 | {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, 335 | {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, 336 | {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, 337 | {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 338 | {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, 339 | {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, 340 | {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, 341 | {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, 342 | {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, 343 | {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, 344 | {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, 345 | {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 346 | {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, 347 | {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, 348 | {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, 349 | {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, 350 | {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, 351 | {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, 352 | {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, 353 | {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, 354 | {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 355 | {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, 356 | {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, 357 | {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, 358 | {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, 359 | {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, 360 | {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 361 | {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 362 | {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, 363 | {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, 364 | {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, 365 | {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, 366 | {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, 367 | {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, 368 | {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, 369 | {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, 370 | {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, 371 | {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, 372 | {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, 373 | {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, 374 | {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, 375 | {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, 376 | {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, 377 | {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, 378 | {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, 379 | {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, 380 | {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, 381 | {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, 382 | {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, 383 | {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, 384 | {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, 385 | {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, 386 | {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, 387 | {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, 388 | {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 389 | {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, 390 | {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, 391 | {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 392 | {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 393 | {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 394 | {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, 395 | {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, 396 | {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, 397 | {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, 398 | {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, 399 | {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, 400 | {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, 401 | {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, 402 | {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, 403 | {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, 404 | {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, 405 | {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 406 | {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, 407 | {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, 408 | {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 409 | {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, 410 | {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, 411 | {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, 412 | {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, 413 | {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, 414 | {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, 415 | {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, 416 | {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 417 | {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, 418 | {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, 419 | {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, 420 | {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, 421 | {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, 422 | {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 423 | {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, 424 | {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 425 | {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, 426 | {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, 427 | {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, 428 | {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, 429 | {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, 430 | {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, 431 | {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, 432 | {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, 433 | {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, 434 | {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, 435 | {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, 436 | {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 437 | {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, 438 | {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, 439 | {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 440 | {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 441 | {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 442 | {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, 443 | {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, 444 | {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 445 | {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, 446 | {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, 447 | {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 448 | {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 449 | {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, 450 | {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 451 | {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, 452 | {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 453 | {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 454 | {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 455 | {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, 456 | {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; 457 | 458 | } // end namespace 459 | -------------------------------------------------------------------------------- /src/marching_cubes.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "isosurface.hpp" 29 | #include "bbox.hpp" 30 | #include "octnode.hpp" 31 | #include "gldata.hpp" 32 | 33 | namespace cutsim 34 | { 35 | 36 | /// Marching-cubes isosurface extraction from distance field stored in Octree 37 | /// see http://en.wikipedia.org/wiki/Marching_cubes 38 | /// 39 | class MarchingCubes : public IsoSurfaceAlgorithm 40 | { 41 | public: 42 | MarchingCubes() : IsoSurfaceAlgorithm() {} 43 | virtual void set_polyVerts(unsigned int) { g->setTriangles(); } 44 | virtual ~MarchingCubes() {} 45 | 46 | protected: 47 | void updateGL(Octnode *node); 48 | /// run MC algorithm and create triangles for given node 49 | void mc_node(Octnode *node); 50 | /// based on the f[] values, generate a list of interpolated vertices, 51 | /// all on the cube-edges of the node. 52 | /// These vertices are later used for defining triangles. 53 | std::vector interpolated_vertices(const Octnode *node, unsigned int edges); 54 | GLVertex interpolate(const Octnode *node, int idx1, int idx2); 55 | // DATA 56 | /// get table-index based on the funcion values (positive or negative) at the corners 57 | unsigned int mc_edgeTableIndex(const Octnode *node); 58 | /// Marching-Cubes edge table 59 | static const unsigned int edgeTable[256]; 60 | /// Marching-Cubes triangle table 61 | static const int triTable[256][16]; 62 | }; 63 | 64 | } // end namespace 65 | 66 | // end file marching_cubes.hpp 67 | -------------------------------------------------------------------------------- /src/octnode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "octnode.hpp" 28 | 29 | namespace cutsim 30 | { 31 | //**************** Octnode ********************/ 32 | 33 | // this defines the position of each octree-vertex with relation to the center of the node 34 | // this also determines in which direction the center of a child node is 35 | const GLVertex Octnode::direction[8] = { 36 | GLVertex(1, 1, -1), // 0 37 | GLVertex(-1, 1, -1), // 1 38 | GLVertex(-1, -1, -1), // 2 39 | GLVertex(1, -1, -1), // 3 40 | GLVertex(1, 1, 1), // 4 41 | GLVertex(-1, 1, 1), // 5 42 | GLVertex(-1, -1, 1), // 6 43 | GLVertex(1, -1, 1) // 7 44 | }; 45 | // surface enumeration 46 | // surf vertices vertices 47 | // 0: 2,3,7 2,6,7 48 | // 1: 0,4,7 0,3,7 49 | // 2: 0,1,4 1,4,5 50 | // 3: 1,5,6 1,2,6 51 | // 4: 0,2,3 0,1,2 52 | // 5: 4,6,7 4,5,6 53 | 54 | // bit-mask for setting valid() property of child-nodes 55 | const int Octnode::octant[8] = { 56 | 1, 57 | 2, 58 | 4, 59 | 8, 60 | 16, 61 | 32, 62 | 64, 63 | 128}; 64 | 65 | Octnode::Octnode(Octnode *nodeparent, unsigned int index, float nodescale, unsigned int nodedepth, GLData *gl) 66 | { 67 | g = gl; 68 | parent = nodeparent; 69 | idx = index; 70 | scale = nodescale; 71 | depth = nodedepth; 72 | 73 | //child.resize(8); 74 | //vertex.resize(8); 75 | //f.resize(8); 76 | if (parent) 77 | { 78 | center = parent->childcenter(idx); 79 | state = parent->prev_state; 80 | prev_state = state; 81 | color = parent->color; 82 | } 83 | else 84 | { // root node has no parent 85 | center = new GLVertex(0, 0, 0); // default center for root is (0,0,0) 86 | state = UNDECIDED; 87 | prev_state = OUTSIDE; 88 | } 89 | 90 | for (int n = 0; n < 8; ++n) 91 | { 92 | child[n] = NULL; 93 | vertex[n] = new GLVertex(*center + direction[n] * scale); 94 | if (parent) 95 | { 96 | assert(parent->state == UNDECIDED); 97 | assert(parent->prev_state != UNDECIDED); 98 | //std::cout << parent->prev_state << "\n"; 99 | if (parent->prev_state == INSIDE) 100 | { 101 | f[n] = 1; 102 | state = INSIDE; 103 | } 104 | else if (parent->prev_state == OUTSIDE) 105 | { 106 | f[n] = -1; 107 | state = OUTSIDE; 108 | } 109 | else 110 | assert(0); 111 | 112 | //f[n]= parent->f[n]; // why does this make a big diggerence in the speed of sum() and dif() ?? 113 | // sum() sum(): 0.15s + 0.27s compared to 1.18 + 0.47 114 | // sum() diff(): 0.15 + 0.2 compared to 1.2 + 0.46 115 | } 116 | else 117 | { 118 | f[n] = -1; 119 | } 120 | } 121 | bb.clear(); 122 | bb.addPoint(*vertex[2]); // vertex[2] has the minimum x,y,z coordinates 123 | bb.addPoint(*vertex[4]); // vertex[4] has the max x,y,z 124 | 125 | isosurface_valid = false; 126 | 127 | childcount = 0; 128 | childStatus = 0; 129 | } 130 | 131 | // call delete on children, vertices, and center 132 | Octnode::~Octnode() 133 | { 134 | if (childcount == 8) 135 | { 136 | for (int n = 0; n < 8; ++n) 137 | { 138 | delete child[n]; 139 | child[n] = 0; 140 | delete vertex[n]; 141 | vertex[n] = 0; 142 | } 143 | } 144 | delete center; 145 | center = 0; 146 | } 147 | 148 | // return centerpoint of child with index n 149 | GLVertex *Octnode::childcenter(int n) 150 | { 151 | return new GLVertex(*center + (direction[n] * 0.5 * scale)); 152 | } 153 | 154 | // create the 8 children of this node 155 | void Octnode::subdivide() 156 | { 157 | if (this->childcount == 0) 158 | { 159 | if (state != UNDECIDED) 160 | std::cout << " subdivide() error: state==" << state << "\n"; 161 | 162 | assert(state == UNDECIDED); 163 | 164 | for (int n = 0; n < 8; ++n) 165 | { 166 | Octnode *newnode = new Octnode(this, n, scale / 2.0, depth + 1, g); // parent, idx, scale, depth, GLdata 167 | this->child[n] = newnode; 168 | ++childcount; 169 | } 170 | } 171 | else 172 | { 173 | std::cout << " DON'T subdivide a non-leaf node \n"; 174 | assert(0); 175 | } 176 | } 177 | // A union B = max( d(A), d(B) ) 178 | void Octnode::sum(const Volume *vol) 179 | { 180 | for (int n = 0; n < 8; ++n) 181 | { 182 | if (vol->dist(*(vertex[n])) > f[n]) 183 | color = vol->color; 184 | f[n] = std::max(f[n], vol->dist(*(vertex[n]))); 185 | } 186 | set_state(); 187 | } 188 | // A \ B = min( d(A), -d(B) 189 | void Octnode::diff(const Volume *vol) 190 | { 191 | for (int n = 0; n < 8; ++n) 192 | { 193 | if (-1 * vol->dist(*(vertex[n])) < f[n]) 194 | color = vol->color; 195 | f[n] = std::min(f[n], -vol->dist(*(vertex[n]))); 196 | } 197 | set_state(); 198 | } 199 | // A intersect B = min( d(A), d(B) ) 200 | void Octnode::intersect(const Volume *vol) 201 | { 202 | for (int n = 0; n < 8; ++n) 203 | { 204 | if (vol->dist(*(vertex[n])) < f[n]) 205 | color = vol->color; 206 | f[n] = std::min(f[n], vol->dist(*(vertex[n]))); 207 | } 208 | set_state(); 209 | } 210 | 211 | // look at the f-values in the corner of the cube and set state 212 | // to inside, outside, or undecided 213 | void Octnode::set_state() 214 | { 215 | NodeState old_state = state; 216 | bool outside = true; 217 | bool inside = true; 218 | for (int n = 0; n < 8; n++) 219 | { 220 | if (f[n] >= 0.0) 221 | { // if one vertex is inside 222 | outside = false; // then it's not an outside-node 223 | } 224 | else 225 | { // if one vertex is outside 226 | assert(f[n] < 0.0); 227 | inside = false; // then it's not an inside node anymore 228 | } 229 | } 230 | assert(!(outside && inside)); // sanity check 231 | 232 | if ((inside) && (!outside)) 233 | setInside(); 234 | else if ((outside) && (!inside)) 235 | setOutside(); 236 | else if ((!inside) && (!outside)) 237 | setUndecided(); 238 | else 239 | assert(0); 240 | 241 | // sanity check.. 242 | assert((is_inside() && !is_outside() && !is_undecided()) || 243 | (!is_inside() && is_outside() && !is_undecided()) || 244 | (!is_inside() && !is_outside() && is_undecided())); 245 | 246 | if (((old_state == INSIDE) && (state == INSIDE)) || 247 | ((old_state == OUTSIDE) && (state == OUTSIDE))) 248 | { 249 | // do nothing if state did not change 250 | } 251 | else 252 | { 253 | setInvalid(); 254 | } 255 | } 256 | 257 | void Octnode::setInside() 258 | { 259 | if ((state != INSIDE) && (all_child_state(INSIDE))) 260 | { 261 | state = INSIDE; 262 | if (parent && (parent->state != INSIDE)) 263 | parent->setInside(); 264 | } 265 | } 266 | 267 | void Octnode::setOutside() 268 | { 269 | if ((state != OUTSIDE) && (all_child_state(OUTSIDE))) 270 | { 271 | state = OUTSIDE; 272 | if (parent && (parent->state != OUTSIDE)) 273 | parent->setOutside(); 274 | } 275 | } 276 | void Octnode::setUndecided() 277 | { 278 | if (state != UNDECIDED) 279 | { 280 | prev_state = state; 281 | state = UNDECIDED; 282 | setInvalid(); 283 | } 284 | } 285 | 286 | bool Octnode::all_child_state(NodeState s) const 287 | { 288 | if (childcount == 8) 289 | { 290 | return (child[0]->state == s) && 291 | (child[1]->state == s) && 292 | (child[2]->state == s) && 293 | (child[3]->state == s) && 294 | (child[4]->state == s) && 295 | (child[5]->state == s) && 296 | (child[6]->state == s) && 297 | (child[7]->state == s); 298 | } 299 | else 300 | { 301 | return true; 302 | } 303 | } 304 | 305 | void Octnode::delete_children() 306 | { 307 | if (childcount == 8) 308 | { 309 | NodeState s0 = child[0]->state; 310 | //std::cout << spaces() << depth << ":" << idx << " delete_children\n"; 311 | //std::cout << "before: s0= " << s0 << " \n"; 312 | //std::cout << " delete_children() states: "; 313 | //for ( int m=0;m<8;m++ ) { 314 | // std::cout << child[m]->state << " "; 315 | //} 316 | 317 | for (int n = 0; n < 8; n++) 318 | { 319 | //std::cout << depth << " deleting " << n << "\n"; 320 | if (s0 != child[n]->state) 321 | { 322 | std::cout << " delete_children() error: "; 323 | //for ( int m=0;m<8;m++ ) { 324 | // std::cout << child[m]->state << " "; 325 | //} 326 | std::cout << "\n"; 327 | std::cout << " s0= " << s0 << " \n"; 328 | } 329 | assert(s0 == child[n]->state); 330 | child[n]->clearVertexSet(); 331 | delete child[n]; 332 | child[n] = 0; 333 | childcount--; 334 | } 335 | assert(childcount == 0); 336 | } 337 | } 338 | 339 | void Octnode::setValid() 340 | { 341 | isosurface_valid = true; 342 | //std::cout << spaces() << depth << ":" << idx << " setValid()\n"; 343 | if (parent) 344 | parent->setChildValid(idx); // try to propagate valid up the tree: 345 | } 346 | void Octnode::setChildValid(unsigned int id) 347 | { 348 | childStatus |= octant[id]; // OR with mask 349 | if (childStatus == 255) 350 | { // all children valid... 351 | setValid(); // ...so this valid 352 | } 353 | } 354 | 355 | void Octnode::setChildInvalid(unsigned int id) 356 | { 357 | childStatus &= ~octant[id]; // AND with not-mask 358 | setInvalid(); 359 | } 360 | 361 | void Octnode::setInvalid() 362 | { 363 | isosurface_valid = false; 364 | if (parent && parent->valid()) 365 | { // update parent status also 366 | parent->setChildInvalid(idx); 367 | } 368 | } 369 | bool Octnode::valid() const 370 | { 371 | return isosurface_valid; 372 | } 373 | 374 | void Octnode::addIndex(unsigned int id) 375 | { 376 | #ifndef NDEBUG 377 | std::set::iterator found = vertexSet.find(id); 378 | assert(found == vertexSet.end()); // we should not have id 379 | #endif 380 | vertexSet.insert(id); 381 | } 382 | void Octnode::swapIndex(unsigned int oldId, unsigned int newId) 383 | { 384 | #ifndef NDEBUG 385 | std::set::iterator found = vertexSet.find(oldId); 386 | assert(found != vertexSet.end()); // we must have oldId 387 | #endif 388 | vertexSet.erase(oldId); 389 | vertexSet.insert(newId); 390 | } 391 | 392 | void Octnode::removeIndex(unsigned int id) 393 | { 394 | #ifndef NDEBUG 395 | std::set::iterator found = vertexSet.find(id); 396 | assert(found != vertexSet.end()); // we must have id 397 | #endif 398 | vertexSet.erase(id); 399 | } 400 | 401 | void Octnode::clearVertexSet() 402 | { 403 | while (!vertexSetEmpty()) 404 | { 405 | unsigned int delId = vertexSetTop(); 406 | removeIndex(delId); 407 | g->removeVertex(delId); 408 | } 409 | assert(vertexSetEmpty()); // when done, set should be empty 410 | } 411 | 412 | // string repr 413 | std::ostream &operator<<(std::ostream &stream, const Octnode &n) 414 | { 415 | stream << " node "; //c=" << *(n.center) << " depth=" << n.depth ; 416 | return stream; 417 | } 418 | 419 | std::string Octnode::printF() 420 | { 421 | std::ostringstream o; 422 | for (int n = 0; n < 8; n++) 423 | { 424 | o << "f[" << n << "] = " << f[n] << "\n"; 425 | } 426 | return o.str(); 427 | } 428 | 429 | std::string Octnode::spaces() const 430 | { 431 | std::ostringstream stream; 432 | for (unsigned int m = 0; m < this->depth; m++) 433 | stream << " "; 434 | return stream.str(); 435 | } 436 | 437 | std::string Octnode::type() const 438 | { 439 | std::ostringstream stream; 440 | if (state == INSIDE) 441 | stream << "inside"; 442 | else if (state == OUTSIDE) 443 | stream << "outside"; 444 | else if (state == UNDECIDED) 445 | stream << "undecided"; 446 | else 447 | assert(0); 448 | return stream.str(); 449 | } 450 | 451 | // string repr 452 | std::string Octnode::str() const 453 | { 454 | std::ostringstream o; 455 | o << *this; 456 | return o.str(); 457 | } 458 | 459 | } // end namespace 460 | // end of file octnode.cpp 461 | -------------------------------------------------------------------------------- /src/octnode.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "volume.hpp" 30 | #include "bbox.hpp" 31 | #include "glvertex.hpp" 32 | #include "gldata.hpp" 33 | 34 | namespace cutsim 35 | { 36 | 37 | /// \class Octnode 38 | /// Octnode represents a node in the octree. 39 | /// 40 | /// each node in the octree is a cube with side length scale 41 | /// the distance field at each corner vertex is stored. 42 | class Octnode 43 | { 44 | public: 45 | /// node state, one of inside, outside, or undecided 46 | enum NodeState 47 | { 48 | INSIDE, 49 | OUTSIDE, 50 | UNDECIDED 51 | }; 52 | NodeState state; ///< the current state of this node 53 | NodeState prev_state; ///< previous state of this node 54 | Color color; /// the color of this node 55 | 56 | /// create suboctant idx of parent with scale nodescale and depth nodedepth 57 | Octnode(Octnode *parent, unsigned int idx, float nodescale, unsigned int nodedepth, GLData *g); 58 | virtual ~Octnode(); 59 | /// create all eight children of this node 60 | void subdivide(); 61 | /// for subdivision even though state is not undecided. called/used from Octree::init() 62 | void force_subdivide() 63 | { // this is only called from octree-init.. 64 | setUndecided(); 65 | subdivide(); 66 | } 67 | // BOOLEAN OPS 68 | void sum(const Volume *vol); ///< sum Volume to this Octnode 69 | void diff(const Volume *vol); ///< diff Volume from this Octnode 70 | void intersect(const Volume *vol); ///< intersect this Octnode with given Volume 71 | 72 | bool is_inside() { return (state == INSIDE); } 73 | bool is_outside() { return (state == OUTSIDE); } 74 | bool is_undecided() { return (state == UNDECIDED); } 75 | 76 | /// return true if all children of this node in given state s 77 | bool all_child_state(NodeState s) const; 78 | /// delete all children of this node 79 | void delete_children(); 80 | 81 | // manipulate the valid-flag 82 | /// set valid-flag true 83 | void setValid(); 84 | /// set valid-flag false 85 | void setInvalid(); 86 | /// true if the GLData for this node is valid 87 | bool valid() const; 88 | 89 | /// true if this node has child n 90 | inline bool hasChild(int n) { return (this->child[n] != NULL); } 91 | /// true if this node has no children 92 | inline bool isLeaf() { return (childcount == 0); } 93 | // DATA 94 | /// pointers to child nodes 95 | Octnode *child[8]; 96 | /// pointer to parent node 97 | Octnode *parent; 98 | /// number of children 99 | unsigned int childcount; 100 | /// The eight corners of this node 101 | GLVertex *vertex[8]; 102 | /// value of distance-field at corner vertex 103 | float f[8]; 104 | /// the center point of this node 105 | GLVertex *center; // the centerpoint of this node 106 | /// the tree-dept of this node 107 | unsigned int depth; // depth of node 108 | /// the index of this node [0,7] 109 | unsigned int idx; // index of node 110 | /// the scale of this node, i.e. distance from center out to corner vertices 111 | float scale; // distance from center to vertices 112 | /// bounding-box corresponding to this node 113 | Bbox bb; 114 | 115 | // for manipulating vertexSet 116 | /// add id to the vertex set 117 | void addIndex(unsigned int id); 118 | /// swap the id for an existing oldId to the given newId. 119 | /// This is called from GLData when GLData needs to move around vertices 120 | void swapIndex(unsigned int oldId, unsigned int newId); 121 | /// remove given id from vertex set 122 | void removeIndex(unsigned int id); 123 | /// is the vertex set empty? 124 | bool vertexSetEmpty() { return vertexSet.empty(); } 125 | /// return begin() for vertex set 126 | unsigned int vertexSetTop() { return *(vertexSet.begin()); } 127 | /// remove all vertices associated with this node. calls GLData to also remove nodes 128 | void clearVertexSet(); 129 | 130 | // string output 131 | friend std::ostream &operator<<(std::ostream &stream, const Octnode &o); 132 | std::string str() const; 133 | std::string printF(); 134 | std::string spaces() const; // "tabs" for aligned printing 135 | std::string type() const; 136 | 137 | /// set node to undecided 138 | void setUndecided(); 139 | 140 | protected: 141 | /// based on the f[]-values at the corners of this node, set the state to one of inside, outside, or undecided. 142 | void set_state(); 143 | /// set node to inside 144 | void setInside(); 145 | /// set node to outside 146 | void setOutside(); 147 | 148 | /// set given child valid 149 | void setChildValid(unsigned int id); 150 | /// set the given child to invalid 151 | inline void setChildInvalid(unsigned int id); 152 | 153 | /// the vertex indices that this node has produced. 154 | /// These correspond to vertex id's in the GLData vertexArray. 155 | std::set vertexSet; 156 | /// return center of child with index n 157 | GLVertex *childcenter(int n); // return position of child center 158 | /// The GLData, i.e. vertices and polygons, associated with this node 159 | /// when this node is deleted we notify the GLData that vertices should be removed 160 | GLData *g; 161 | /// flag for telling isosurface extraction is valid for this node 162 | /// if false, the node needs updating. 163 | bool isosurface_valid; 164 | /// bit-field indicating if children have valid gldata 165 | char childStatus; 166 | 167 | // STATIC 168 | /// the direction to the vertices, from the center 169 | static const GLVertex direction[8]; 170 | /// bit masks for the status 171 | static const int octant[8]; 172 | 173 | private: 174 | Octnode() {} 175 | }; 176 | 177 | } // end namespace 178 | 179 | // end file octnode.hpp 180 | -------------------------------------------------------------------------------- /src/octree.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * 4 | * This file is part of libcutsim. 5 | * 6 | * libcutsim is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * libcutsim is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with libcutsim. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "octree.hpp" 28 | #include "octnode.hpp" 29 | #include "volume.hpp" 30 | 31 | namespace cutsim 32 | { 33 | 34 | //**************** Octree ********************/ 35 | 36 | Octree::Octree(double scale, unsigned int depth, GLVertex ¢erp, GLData *gl) 37 | { 38 | root_scale = scale; 39 | max_depth = depth; 40 | g = gl; 41 | // parent, idx, scale, depth 42 | root = new Octnode(NULL, 0, root_scale, 0, g); 43 | root->center = new GLVertex(centerp); 44 | for (int n = 0; n < 8; ++n) 45 | { 46 | root->child[n] = NULL; 47 | } 48 | debug = false; 49 | debug_mc = false; 50 | } 51 | 52 | Octree::~Octree() 53 | { 54 | delete root; 55 | root = 0; 56 | } 57 | 58 | unsigned int Octree::get_max_depth() const 59 | { 60 | return max_depth; 61 | } 62 | 63 | double Octree::get_root_scale() const 64 | { 65 | return root_scale; 66 | } 67 | 68 | double Octree::leaf_scale() const 69 | { 70 | return (2.0 * root_scale) / pow(2.0, (int)max_depth); 71 | } 72 | 73 | /// subdivide the Octree n times 74 | void Octree::init(const unsigned int n) 75 | { 76 | for (unsigned int m = 0; m < n; ++m) 77 | { 78 | std::vector nodelist; 79 | get_leaf_nodes(root, nodelist); 80 | BOOST_FOREACH (Octnode *node, nodelist) 81 | { 82 | node->force_subdivide(); 83 | } 84 | } 85 | } 86 | /* 87 | void Octree::get_invalid_leaf_nodes( std::vector& nodelist) const { 88 | get_invalid_leaf_nodes( root, nodelist ); 89 | }*/ 90 | 91 | /* 92 | void Octree::get_invalid_leaf_nodes(Octnode* current, std::vector& nodelist) const { 93 | if ( current->childcount == 0 ) { 94 | if ( !current->valid() ) { 95 | nodelist.push_back( current ); 96 | } 97 | } else { 98 | for ( int n=0;n<8;++n) { 99 | if ( current->hasChild(n) ) { 100 | if ( !current->valid() ) { 101 | get_leaf_nodes( current->child[n], nodelist ); 102 | } 103 | } 104 | } 105 | } 106 | } */ 107 | 108 | /// put leaf nodes into nodelist 109 | 110 | void Octree::get_leaf_nodes(Octnode *current, std::vector &nodelist) const 111 | { 112 | if (current->isLeaf()) 113 | { 114 | nodelist.push_back(current); 115 | } 116 | else 117 | { 118 | for (int n = 0; n < 8; ++n) 119 | { 120 | if (current->child[n] != 0) 121 | get_leaf_nodes(current->child[n], nodelist); 122 | } 123 | } 124 | } 125 | 126 | /// put all nodes into nodelist 127 | /* 128 | void Octree::get_all_nodes(Octnode* current, std::vector& nodelist) const { 129 | if ( current ) { 130 | nodelist.push_back( current ); 131 | for ( int n=0;n<8;++n) { 132 | if ( current->child[n] != 0 ) 133 | get_all_nodes( current->child[n], nodelist ); 134 | } 135 | } 136 | }*/ 137 | 138 | // sum (union) of tree and OCTVolume 139 | void Octree::sum(Octnode *current, const Volume *vol) 140 | { 141 | if (!vol->bb.overlaps(current->bb) || current->is_inside()) // if no overlap, or already INSIDE, then quit. 142 | return; // abort if no overlap. 143 | 144 | current->sum(vol); 145 | if ((current->childcount == 8) && current->is_undecided()) 146 | { // recurse into existing tree 147 | for (int m = 0; m < 8; ++m) 148 | { 149 | if (!current->child[m]->is_inside()) // nodes that are already INSIDE cannot change in a sum-operation 150 | sum(current->child[m], vol); // call sum on children 151 | } 152 | } 153 | else if (current->is_undecided()) 154 | { // no children, subdivide if undecided 155 | if ((current->depth < (this->max_depth - 1))) 156 | { 157 | current->subdivide(); // smash into 8 sub-pieces 158 | for (int m = 0; m < 8; ++m) 159 | sum(current->child[m], vol); // call sum on children 160 | } 161 | } 162 | // now all children of current have their status set, and we can prune. 163 | if ((current->childcount == 8) && (current->all_child_state(Octnode::INSIDE) || current->all_child_state(Octnode::OUTSIDE))) 164 | { 165 | current->delete_children(); 166 | } 167 | } 168 | 169 | void Octree::diff(Octnode *current, const Volume *vol) 170 | { 171 | if (!vol->bb.overlaps(current->bb) || current->is_outside()) 172 | { 173 | //std::cout << vol->center.x << "," << vol->center.y << "," << vol->center.z << " overlaps? " << vol->bb.overlaps( current->bb ) << "\n"; 174 | return; // if no overlap, or already OUTSIDE, then quit. 175 | } 176 | 177 | current->diff(vol); 178 | if (vol->bb.overlaps(current->bb) || current->bb.overlaps(vol->bb)) 179 | current->setUndecided(); 180 | 181 | if (((current->childcount) == 8) && (current->is_undecided())) 182 | { // recurse into existing tree 183 | for (int m = 0; m < 8; ++m) 184 | { 185 | //if ( !current->child[m]->is_outside() ) // nodes that are OUTSIDE don't change 186 | diff(current->child[m], vol); // call diff on children 187 | } 188 | } 189 | else if ((current->is_undecided())) 190 | { // no children, subdivide if undecided 191 | if ((current->depth < (this->max_depth - 1))) 192 | { 193 | current->subdivide(); // smash into 8 sub-pieces 194 | for (int m = 0; m < 8; ++m) 195 | { 196 | diff(current->child[m], vol); // call diff on children 197 | } 198 | } 199 | } 200 | 201 | // now all children have their status set, prune. 202 | if ((current->childcount == 8) && (current->all_child_state(Octnode::INSIDE) || current->all_child_state(Octnode::OUTSIDE))) 203 | { 204 | current->delete_children(); 205 | } 206 | } 207 | 208 | void Octree::intersect(Octnode *current, const Volume *vol) 209 | { 210 | if (current->is_outside()) // if already OUTSIDE, then quit. 211 | return; 212 | 213 | current->intersect(vol); 214 | if (((current->childcount) == 8) && current->is_undecided()) 215 | { // recurse into existing tree 216 | for (int m = 0; m < 8; ++m) 217 | { 218 | //if ( !current->child[m]->is_outside() ) // nodes that are OUTSIDE don't change 219 | intersect(current->child[m], vol); // call diff on children 220 | } 221 | } 222 | else if (current->is_undecided()) 223 | { // no children, subdivide if undecided 224 | if ((current->depth < (this->max_depth - 1))) 225 | { 226 | current->subdivide(); // smash into 8 sub-pieces 227 | for (int m = 0; m < 8; ++m) 228 | { 229 | intersect(current->child[m], vol); // call diff on children 230 | } 231 | } 232 | } 233 | // now all children have their status set, prune. 234 | if ((current->childcount == 8) && (current->all_child_state(Octnode::INSIDE) || current->all_child_state(Octnode::OUTSIDE))) 235 | { 236 | current->delete_children(); 237 | } 238 | } 239 | 240 | // string repr 241 | std::string Octree::str() const 242 | { 243 | std::ostringstream o; 244 | o << " Octree: "; 245 | /* 246 | std::vector nodelist; 247 | Octree::get_all_nodes(root, nodelist); 248 | std::vector nodelevel(this->max_depth); 249 | std::vector invalidsAtLevel(this->max_depth); 250 | std::vector surfaceAtLevel(this->max_depth); 251 | BOOST_FOREACH( Octnode* n, nodelist) { 252 | ++nodelevel[n->depth]; 253 | if ( !n->valid() ) 254 | ++invalidsAtLevel[n->depth]; 255 | if (n->is_undecided() ) 256 | ++surfaceAtLevel[n->depth]; 257 | } 258 | o << " " << nodelist.size() << " leaf-nodes:\n"; 259 | int m=0; 260 | BOOST_FOREACH( int count, nodelevel) { 261 | o << "depth="<. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "bbox.hpp" 27 | #include "gldata.hpp" 28 | 29 | namespace cutsim 30 | { 31 | 32 | class Octnode; 33 | class Volume; 34 | 35 | /// Octree class for cutting simulation 36 | /// see http://en.wikipedia.org/wiki/Octree 37 | /// 38 | /// The root node is divided into eight sub-octants, and each sub-octant 39 | /// is recursively further divided into octants. 40 | /// 41 | /// The side-length of the root node is root_scale 42 | /// The dept of the root node is zero. 43 | /// Subdivision is continued unti max_depth is reached. 44 | /// A node at tree-dept n is a cube with side-length root_scale/pow(2,n) 45 | /// 46 | /// This class stores the root Octnode and allows operations on the tree 47 | /// 48 | class Octree 49 | { 50 | public: 51 | /// create an octree with a root node with scale=root_scale, maximum 52 | /// tree-depth of max_depth and centered at centerp. 53 | Octree(double root_scale, unsigned int max_depth, GLVertex ¢erPoint, GLData *gl); 54 | virtual ~Octree(); 55 | 56 | // bolean operations on tree 57 | /// diff given Volume from tree 58 | void diff(const Volume *vol) { diff(this->root, vol); } 59 | /// sum given Volume to tree 60 | void sum(const Volume *vol) { sum(this->root, vol); } 61 | /// intersect tree with given Volume 62 | void intersect(const Volume *vol) { intersect(this->root, vol); } 63 | 64 | // debug, can be removed? 65 | // put all leaf-nodes in a list 66 | //void get_leaf_nodes( std::vector& nodelist) const { get_leaf_nodes( root, nodelist); } 67 | 68 | /// put all leaf-nodes in a list 69 | void get_leaf_nodes(Octnode *current, std::vector &nodelist) const; 70 | 71 | // put all invalid nodes in a list 72 | //void get_invalid_leaf_nodes(std::vector& nodelist) const; 73 | // put all invalid nodes in a list 74 | //void get_invalid_leaf_nodes( Octnode* current, std::vector& nodelist) const; 75 | // put all nodes in a list 76 | //void get_all_nodes(Octnode* current, std::vector& nodelist) const; 77 | 78 | /// initialize by recursively calling subdivide() on all nodes n times 79 | void init(const unsigned int n); 80 | /// return max depth 81 | unsigned int get_max_depth() const; 82 | /// return the maximum cube side-length, (i.e. at depth=0) 83 | double get_root_scale() const; 84 | /// return the minimum cube side-length (i.e. at maximum depth) 85 | double leaf_scale() const; 86 | /// string output 87 | std::string str() const; 88 | /// flag for debug mode 89 | bool debug; 90 | /// flag for debug-mode of marching-cubes 91 | bool debug_mc; 92 | /// the root scale, i.e. side-length of depth=0 cube 93 | double root_scale; 94 | /// the maximum tree-depth 95 | unsigned int max_depth; 96 | /// pointer to the root node 97 | Octnode *root; 98 | 99 | protected: 100 | /// recursively traverse the tree subtracting Volume 101 | void diff(Octnode *current, const Volume *vol); 102 | /// union Octnode with Volume 103 | void sum(Octnode *current, const Volume *vol); 104 | /// intersect Octnode with Volume 105 | void intersect(Octnode *current, const Volume *vol); 106 | 107 | // DATA 108 | /// the GLData used to draw this tree 109 | GLData *g; 110 | 111 | private: 112 | Octree() {} // disable constructor 113 | }; 114 | 115 | } // end namespace 116 | 117 | // end file octree.hpp 118 | -------------------------------------------------------------------------------- /src/volume.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * Copyright 2015 Kazuyasu Hamada (k-hamada@gifu-u.ac.jp) 4 | * 5 | * This file is part of libcutsim. 6 | * 7 | * libcutsim is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * libcutsim is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with libcutsim. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "volume.hpp" 25 | 26 | #define TOLERANCE (1e-2) 27 | 28 | namespace cutsim 29 | { 30 | 31 | //************* Sphere **************/ 32 | 33 | /// sphere at center 34 | SphereVolume::SphereVolume() 35 | { 36 | center = GLVertex(0, 0, 0); 37 | radius = 1.0; 38 | calcBB(); 39 | } 40 | 41 | float SphereVolume::dist(const GLVertex &p) const 42 | { 43 | float d = (center - p).norm(); 44 | return radius - d; // positive inside. negative outside. 45 | } 46 | 47 | /// set the bounding box values 48 | void SphereVolume::calcBB() 49 | { 50 | bb.clear(); 51 | GLVertex maxpt = GLVertex(center.x + radius, center.y + radius, center.z + radius); 52 | GLVertex minpt = GLVertex(center.x - radius, center.y - radius, center.z - radius); 53 | bb.addPoint(maxpt); 54 | bb.addPoint(minpt); 55 | } 56 | 57 | //************* Cube **************/ 58 | 59 | /// cube at center with side length side 60 | CubeVolume::CubeVolume() 61 | { 62 | center = GLVertex(0, 0, 0); 63 | side = 1.234; 64 | } 65 | 66 | /// set bbox values 67 | void CubeVolume::calcBB() 68 | { 69 | bb.clear(); 70 | GLVertex maxpt = GLVertex(center.x + side / 2, center.y + side / 2, center.z + side / 2); 71 | GLVertex minpt = GLVertex(center.x - side / 2, center.y - side / 2, center.z - side / 2); 72 | bb.addPoint(maxpt); 73 | bb.addPoint(minpt); 74 | } 75 | 76 | float CubeVolume::dist(const GLVertex &p) const 77 | { 78 | float m; 79 | m = p.x - center.x; 80 | 81 | if (fabs(m) < fabs(p.y - center.y)) 82 | m = p.y - center.y; 83 | if (fabs(m) < fabs(p.z - center.z)) 84 | m = p.z - center.z; 85 | // m is now the maximum coordinate 86 | //bool sign = (invert ? -1.0 : 1.0 ); 87 | return (side / 2.0 - fabs(m)); 88 | // positive inside. negative outside. 89 | } 90 | 91 | //************* Cone **************/ 92 | 93 | float ConeVolume::dist(const GLVertex &p) const 94 | { 95 | float h = p.z - center.z; 96 | 97 | if (h <= 0) // p is below cone 98 | return -1; 99 | else 100 | { 101 | float radius = h * tan(alfa); 102 | float dxy = sqrt((p.x - center.x) * (p.x - center.x) + (p.y - center.y) * (p.y - center.y)); 103 | return radius - dxy; 104 | } 105 | // return 0; 106 | } 107 | 108 | void ConeVolume::calcBB() 109 | { 110 | bb.clear(); 111 | GLVertex maxpt = GLVertex(center.x + height * tan(alfa), center.y + height * tan(alfa), center.z + height); 112 | GLVertex minpt = GLVertex(center.x - height * tan(alfa), center.y - height * tan(alfa), center.z); 113 | bb.addPoint(maxpt); 114 | bb.addPoint(minpt); 115 | } 116 | 117 | //************* STL **************/ 118 | 119 | MeshVolume::MeshVolume() 120 | { 121 | center = GLVertex(0, 0, 0); // center is treated as the origin's offset of STL 122 | rotationCenter = GLVertex(0, 0, 0); 123 | angle = GLVertex(0, 0, 0); 124 | } 125 | 126 | void MeshVolume::calcBB() 127 | { 128 | GLVertex maxpt; 129 | GLVertex minpt; 130 | GLVertex centreDiff = meshCenter - center; 131 | // std::cout << "MeshVolume::calcBB() Center x:" << center.x << " y: " << center.y << " z: " << center.z << std::endl; 132 | for (int i = 0; i < (int)facets.size(); i++) 133 | { 134 | facets[i]->v1 += centreDiff; 135 | facets[i]->v2 += centreDiff; 136 | facets[i]->v3 += centreDiff; 137 | facets[i]->normal = facets[i]->normal.rotateAC(angle.x, angle.z); 138 | GLVertex v1p = facets[i]->v1 - rotationCenter; 139 | facets[i]->v1 = v1p.rotateAC(angle.x, angle.z) + rotationCenter; 140 | GLVertex v2p = facets[i]->v2 - rotationCenter; 141 | facets[i]->v2 = v2p.rotateAC(angle.x, angle.z) + rotationCenter; 142 | GLVertex v3p = facets[i]->v3 - rotationCenter; 143 | facets[i]->v3 = v3p.rotateAC(angle.x, angle.z) + rotationCenter; 144 | } 145 | if (facets.size()) 146 | { 147 | maxpt.x = fmax(fmax(facets[0]->v1.x, facets[0]->v2.x), facets[0]->v3.x); 148 | maxpt.y = fmax(fmax(facets[0]->v1.y, facets[0]->v2.y), facets[0]->v3.y); 149 | maxpt.z = fmax(fmax(facets[0]->v1.z, facets[0]->v2.z), facets[0]->v3.z); 150 | minpt.x = fmin(fmin(facets[0]->v1.x, facets[0]->v2.x), facets[0]->v3.x); 151 | minpt.y = fmin(fmin(facets[0]->v1.y, facets[0]->v2.y), facets[0]->v3.y); 152 | minpt.z = fmin(fmin(facets[0]->v1.z, facets[0]->v2.z), facets[0]->v3.z); 153 | } 154 | for (int i = 0; i < (int)facets.size(); i++) 155 | { 156 | maxpt.x = fmax(fmax(fmax(facets[i]->v1.x, facets[i]->v2.x), facets[i]->v3.x), maxpt.x); 157 | maxpt.y = fmax(fmax(fmax(facets[i]->v1.y, facets[i]->v2.y), facets[i]->v3.y), maxpt.y); 158 | maxpt.z = fmax(fmax(fmax(facets[i]->v1.z, facets[i]->v2.z), facets[i]->v3.z), maxpt.z); 159 | minpt.x = fmin(fmin(fmin(facets[i]->v1.x, facets[i]->v2.x), facets[i]->v3.x), minpt.x); 160 | minpt.y = fmin(fmin(fmin(facets[i]->v1.y, facets[i]->v2.y), facets[i]->v3.y), minpt.y); 161 | minpt.z = fmin(fmin(fmin(facets[i]->v1.z, facets[i]->v2.z), facets[i]->v3.z), minpt.z); 162 | V21.push_back(facets[i]->v2 - facets[i]->v1); 163 | invV21dotV21.push_back(1.0 / (facets[i]->v2 - facets[i]->v1).dot(facets[i]->v2 - facets[i]->v1)); 164 | V32.push_back(facets[i]->v3 - facets[i]->v2); 165 | invV32dotV32.push_back(1.0 / (facets[i]->v3 - facets[i]->v2).dot(facets[i]->v3 - facets[i]->v2)); 166 | V13.push_back(facets[i]->v1 - facets[i]->v3); 167 | invV13dotV13.push_back(1.0 / (facets[i]->v1 - facets[i]->v3).dot(facets[i]->v1 - facets[i]->v3)); 168 | } 169 | bb.clear(); 170 | maxpt += GLVertex(TOLERANCE, TOLERANCE, TOLERANCE); 171 | minpt -= GLVertex(TOLERANCE, TOLERANCE, TOLERANCE); 172 | // std::cout << "STL maxpt x:" << maxpt.x << " y: " << maxpt.y << " z:" << maxpt.z << "\n"; 173 | // std::cout << "STL minpt x:" << minpt.x << " y: " << minpt.y << " z:" << minpt.z << "\n"; 174 | center = meshCenter; 175 | bb.addPoint(maxpt); 176 | bb.addPoint(minpt); 177 | } 178 | 179 | float MeshVolume::dist(const GLVertex &p) const 180 | { 181 | GLVertex q, r; 182 | GLVertex n1, n2, n3; 183 | double s12, s23, s31; 184 | float min = 1.0e+3, d, ret = -1.0, u, abs_d; 185 | for (int i = 0; i < (int)facets.size(); i++) 186 | { 187 | u = (p - facets[i]->v1).dot(V21[i]) * invV21dotV21[i]; 188 | q = facets[i]->v1 + V21[i] * u; 189 | d = (q - p).dot(facets[i]->normal); 190 | if ((abs_d = fabs(d)) > min) 191 | continue; 192 | r = p + facets[i]->normal * d; 193 | n1 = (r - facets[i]->v1).cross(V13[i]); 194 | n2 = (r - facets[i]->v2).cross(V21[i]); 195 | n3 = (r - facets[i]->v3).cross(V32[i]); 196 | s12 = n1.dot(n2); 197 | s23 = n2.dot(n3); 198 | s31 = n3.dot(n1); 199 | 200 | if ((s12 * s31 > 0.0) && (s12 * s23 > 0.0) && (s23 * s31 > 0.0)) 201 | if (abs_d < min) 202 | { 203 | min = abs_d; 204 | ret = d; 205 | continue; 206 | } 207 | 208 | if (s12 <= 0.0 && s31 >= 0.0) 209 | { 210 | if (u > 0.0 && u < 1.0) 211 | /*q21 = q*/; 212 | else if (u <= 0.0) 213 | q = facets[i]->v1; 214 | else 215 | q = facets[i]->v2; 216 | abs_d = (q - p).norm(); 217 | if (abs_d < min) 218 | { 219 | d = (q - p).dot(facets[i]->normal); 220 | if (d > 0.0 + TOLERANCE) 221 | { 222 | min = abs_d + TOLERANCE; 223 | ret = abs_d; 224 | } 225 | else 226 | { 227 | min = abs_d; 228 | ret = -abs_d; 229 | } 230 | } 231 | } 232 | else if (s31 <= 0.0 && s23 >= 0.0) 233 | { 234 | u = (p - facets[i]->v3).dot(V13[i]) * invV13dotV13[i]; 235 | if (u > 0.0 && u < 1.0) 236 | q = facets[i]->v3 + V13[i] * u; 237 | else if (u <= 0.0) 238 | q = facets[i]->v3; 239 | else 240 | q = facets[i]->v1; 241 | abs_d = (q - p).norm(); 242 | if (abs_d < min) 243 | { 244 | d = (q - p).dot(facets[i]->normal); 245 | if (d > 0.0 + TOLERANCE) 246 | { 247 | min = abs_d + TOLERANCE; 248 | ret = abs_d; 249 | } 250 | else 251 | { 252 | min = abs_d; 253 | ret = -abs_d; 254 | } 255 | } 256 | } 257 | else if (s23 <= 0.0 && s12 >= 0.0) 258 | { 259 | u = (p - facets[i]->v2).dot(V32[i]) * invV32dotV32[i]; 260 | if (u > 0.0 && u < 1.0) 261 | q = facets[i]->v2 + V32[i] * u; 262 | else if (u <= 0.0) 263 | q = facets[i]->v2; 264 | else 265 | q = facets[i]->v3; 266 | abs_d = (q - p).norm(); 267 | if (abs_d < min) 268 | { 269 | d = (q - p).dot(facets[i]->normal); 270 | if (d > 0.0 + TOLERANCE) 271 | { 272 | min = abs_d + TOLERANCE; 273 | ret = abs_d; 274 | } 275 | else 276 | { 277 | min = abs_d; 278 | ret = -abs_d; 279 | } 280 | } 281 | } 282 | } 283 | //std::cout << " STL dist " << ret << std::endl; 284 | return ret; // positive inside. negative outside. 285 | } 286 | 287 | bool MeshVolume::loadMesh(boost::python::list pyfacets) 288 | { 289 | 290 | FileIO mesh; 291 | facets.clear(); 292 | bool processed = false; 293 | processed = mesh.loadMesh(pyfacets); 294 | 295 | if (processed) 296 | { 297 | facets = mesh.getFacets(); 298 | // calculate the bounding box of the mesh volume 299 | calcBB(); 300 | } 301 | 302 | return processed; 303 | } 304 | 305 | bool MeshVolume::loadStl(boost::python::str fPath) 306 | { 307 | 308 | FileIO stl; 309 | facets.clear(); 310 | bool processed = false; 311 | processed = stl.loadStl(fPath); 312 | 313 | if (processed) 314 | { 315 | facets = stl.getFacets(); 316 | // calculate the bounding box of the mesh volume 317 | calcBB(); 318 | } 319 | 320 | return processed; 321 | } 322 | 323 | } // end namespace 324 | // end of file volume.cpp 325 | -------------------------------------------------------------------------------- /src/volume.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Anders Wallin (anders.e.e.wallin "at" gmail.com) 3 | * Copyright 2015 Kazuyasu Hamada (k-hamada@gifu-u.ac.jp) 4 | * 5 | * This file is part of libcutsim. 6 | * 7 | * libcutsim is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * libcutsim is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with libcutsim. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "bbox.hpp" 28 | #include "facet.hpp" 29 | #include "glvertex.hpp" 30 | #include "gldata.hpp" 31 | #include "fileio.hpp" 32 | 33 | namespace cutsim 34 | { 35 | 36 | /// Volume is a base-class for defining implicit volumes from which to build octrees 37 | /// an implicit volume is defined as a function dist(Point p) 38 | /// which returns a positive value inside the volume and a negative value outside. 39 | /// 40 | /// the "positive inside, negative outside" sign-convetion means that 41 | /// boolean operations between two Volumes A and B can be done with: 42 | /// 43 | /// A U B ('union' or 'sum') = max( d(A), d(B) ) 44 | /// A \ B ('diff' or 'minus') = min( d(A), -d(B) ) 45 | /// A int B ('intersection') = min( d(A), d(B) ) 46 | /// 47 | /// reference: Frisken et al. "Designing with Distance Fields" 48 | /// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.69.9025 49 | /// 50 | /// iso-surface extraction using standard marching-cubes requires just the distance 51 | /// field to be stored at each corner vertex of an octree leaf-node. 52 | /// 53 | /// advanced iso-surface extraction using extended-marching-cubes or dual-contouring may require 54 | /// more information such as normals of the distance field or exact 55 | /// intersection points and normals. This is similar to a tri-dexel representation. 56 | /// In multi-material simulation a material-index can be stored. 57 | /// Each cutter may also cut the material with a color of its own 58 | /// (new vertices have the color of the cutter). 59 | class Volume 60 | { 61 | public: 62 | Volume() {} 63 | /// return signed distance from volume surface to Point p 64 | /// Points p inside the volume should return positive values. 65 | /// Points p outside the volume should return negative values. 66 | virtual float dist(const GLVertex &p) const { return 0; } 67 | /// set the color 68 | void setColor(float r, float g, float b) 69 | { 70 | color.r = r; 71 | color.g = g; 72 | color.b = b; 73 | } 74 | /// set the centerpoint of the sphere 75 | void setCenter(float x, float y, float z) 76 | { 77 | center = GLVertex(x, y, z); 78 | calcBB(); 79 | } 80 | virtual void calcBB() {} ///< update bounding-box 81 | // DATA 82 | /// bounding-box. This holds the maximum and minimum points along the (x,y,z) coordinates 83 | /// of the volume 84 | /// dist(p) can return positive "inside" values only inside the bounding-box 85 | Bbox bb; 86 | /// the color of this Volume 87 | Color color; 88 | GLVertex center; 89 | }; 90 | 91 | // sub-classes of Volume below: 92 | 93 | /// sphere centered at center 94 | class SphereVolume : public Volume 95 | { 96 | public: 97 | /// default constructor 98 | SphereVolume(); 99 | /// set radius of sphere 100 | void setRadius(float r) 101 | { 102 | radius = r; 103 | calcBB(); 104 | } 105 | /// update the Bbox 106 | virtual void calcBB(); 107 | virtual float dist(const GLVertex &p) const; 108 | float radius; ///< radius of sphere 109 | }; 110 | 111 | /// cube at center with side-length side 112 | class CubeVolume : public Volume 113 | { 114 | public: 115 | CubeVolume(); 116 | virtual float dist(const GLVertex &p) const; 117 | void setSide(float s) 118 | { 119 | side = s; 120 | calcBB(); 121 | } 122 | void calcBB(); 123 | // DATA 124 | float side; ///< side length of cube 125 | }; 126 | 127 | /// cone, for v-carving sim 128 | class ConeVolume : public Volume 129 | { 130 | public: 131 | ConeVolume() 132 | { 133 | alfa = M_PI / 4; 134 | setHeight(10); 135 | } 136 | virtual float dist(const GLVertex &p) const; 137 | void setHeight(float h) 138 | { 139 | height = h; 140 | calcBB(); 141 | } 142 | void calcBB(); 143 | // DATA 144 | float height; ///< height of cone 145 | float alfa; ///< half-angle of cone 146 | }; 147 | 148 | /// STL volume 149 | class MeshVolume : public Volume 150 | { 151 | 152 | public: 153 | MeshVolume(); 154 | virtual ~MeshVolume() 155 | { 156 | V21.resize(0); 157 | invV21dotV21.resize(0); 158 | V32.resize(0); 159 | invV32dotV32.resize(0); 160 | V13.resize(0); 161 | invV13dotV13.resize(0); 162 | facets.resize(0); 163 | } 164 | 165 | void addFacet(Facet *f) { facets.push_back(f); } 166 | 167 | /// set the center of STL 168 | void setCenter(GLVertex v) 169 | { 170 | center = v; 171 | } 172 | 173 | /// set the target center of STL 174 | void setMeshCenter(float x, float y, float z) 175 | { 176 | meshCenter = GLVertex(x, y, z); 177 | calcBB(); 178 | } 179 | 180 | /// set the rotation center of STL 181 | void setRotationCenter(GLVertex c) 182 | { 183 | rotationCenter = c; 184 | } 185 | 186 | /// set the angle of STL 187 | void setAngle(GLVertex a) 188 | { 189 | angle = a; 190 | } 191 | 192 | /// update the bounding-box of STL 193 | void calcBB(); 194 | 195 | virtual float dist(const GLVertex &p) const; 196 | 197 | // load mesh from facet data 198 | bool loadMesh(boost::python::list); 199 | 200 | // load mesh from stl file 201 | bool loadStl(boost::python::str); 202 | 203 | private: 204 | // V21[i] = facets[i]->v2 - facets[i]->v1 205 | std::vector V21; 206 | // 1/ 207 | std::vector invV21dotV21; 208 | // V32[i] = facets[i]->v3 - facets[i]->v2 209 | std::vector V32; 210 | // 1/ 211 | std::vector invV32dotV32; 212 | // V13[i] = facets[i]->v1 - facets[i]->v3 213 | std::vector V13; 214 | // 1/ 215 | std::vector invV13dotV13; 216 | /// STL center 217 | GLVertex center; 218 | /// STL Target Center 219 | GLVertex meshCenter; 220 | /// center of rotation 221 | GLVertex rotationCenter; 222 | /// STL angle 223 | GLVertex angle; 224 | // facets 225 | std::vector facets; 226 | }; 227 | 228 | } // end namespace 229 | // end file volume.hpp 230 | --------------------------------------------------------------------------------