├── 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 |
--------------------------------------------------------------------------------