Marching cubes: tentative configurations generator

First attempt to generate the configurations of the marching cubes
algorithm, following naively the definition — using the 15 base cases
and generating all rotations and symmetries. This implementation does
not try to be parcimonious, it bruteforces the configurations, since the
search space is quite small anyway.

The plan is to implement a python generator that outputs a valid .cpp
file containing the pre-computed (at compile time) array for
configurations.
This commit is contained in:
Théophile Bastian 2018-02-09 00:29:16 +01:00
parent 776303b18d
commit 39d12ef8a4
3 changed files with 557 additions and 0 deletions

1
.gitignore vendored
View File

@ -28,3 +28,4 @@
*.app
*.bin
__pycache__

0
tools/__init__.py Normal file
View File

View File

@ -0,0 +1,556 @@
#!/usr/bin/env python3
""" Generates Marching Cubes' algorithm array of triangles
Generates the array of triangles for a given intersection pattern of a unit
cube.
"""
from functools import reduce
class Vert:
frnt_bot_l = (0, 0, 1)
frnt_bot_r = (1, 0, 1)
frnt_top_l = (0, 1, 1)
frnt_top_r = (1, 1, 1)
back_bot_l = (0, 0, 0)
back_bot_r = (1, 0, 0)
back_top_l = (0, 1, 0)
back_top_r = (1, 1, 0)
class Edge:
def __init__(self, v1, v2):
self.vert = [
v1 if v1 < v2 else v2,
v2 if v1 < v2 else v1,
]
if v1 == v2:
raise ValueError((v1, v2))
def same_as(self, oth):
return self.vert == oth.vert
def transform(self, transf):
return Edge(
transf(self.vert[0]),
transf(self.vert[1]))
def dump(self):
def cbool(val):
return "true" if val else "false"
return "MarchingCubes::CubeEdge({}, {}, {}, {}, {}, {})".format(
cbool(self.vert[0][0]),
cbool(self.vert[0][1]),
cbool(self.vert[0][2]),
cbool(self.vert[1][0]),
cbool(self.vert[1][1]),
cbool(self.vert[1][2]))
class Edg:
frnt_l = Edge(Vert.frnt_bot_l, Vert.frnt_top_l)
frnt_r = Edge(Vert.frnt_bot_r, Vert.frnt_top_r)
frnt_top = Edge(Vert.frnt_top_l, Vert.frnt_top_r)
frnt_bot = Edge(Vert.frnt_bot_l, Vert.frnt_bot_r)
back_l = Edge(Vert.back_bot_l, Vert.back_top_l)
back_r = Edge(Vert.back_bot_r, Vert.back_top_r)
back_top = Edge(Vert.back_top_l, Vert.back_top_r)
back_bot = Edge(Vert.back_bot_l, Vert.back_bot_r)
top_l = Edge(Vert.frnt_top_l, Vert.back_top_l)
top_r = Edge(Vert.frnt_top_r, Vert.back_top_r)
bot_l = Edge(Vert.frnt_bot_l, Vert.back_bot_l)
bot_r = Edge(Vert.frnt_bot_r, Vert.back_bot_r)
class TriangulatedCube:
def __init__(self, activated, triangles=None):
self.triangles = [] if triangles is None else triangles
self.activated = activated
def transform(self, transf):
n_tri = []
for tri in self.triangles:
n_edge = []
for edge in tri:
n_edge.append(edge.transform(transf))
n_tri.append(n_edge)
n_act = set()
for act in self.activated:
n_act.add(transf(act))
return TriangulatedCube(n_act, n_tri)
def activated_code(self):
out = 0
for act in self.activated:
val = (act[0]
+ (act[1] << 1)
+ (act[2] << 2))
out |= (1 << val)
return out
def dump_tri(tri):
return ("MarchingCubes::CubeTri({{{}, {}, {}}})".format(
tri[0].dump(), tri[1].dump(), tri[2].dump))
def rot_general(vert, fixed, ax1, ax2):
cases = {
(0, 0): (0, 1),
(0, 1): (1, 1),
(1, 1): (1, 0),
(1, 0): (0, 0),
}
moved = cases[(vert[ax1], vert[ax2])]
out = [0] * 3
out[fixed] = vert[fixed]
out[ax1] = moved[0]
out[ax2] = moved[1]
return (out[0], out[1], out[2])
def rot_x(vert):
return rot_general(vert, 0, 1, 2)
def rot_y(vert):
return rot_general(vert, 1, 2, 0)
def rot_z(vert):
return rot_general(vert, 2, 0, 1)
def sym_general(vert, normal):
return (
vert[0] if normal != 0 else 1-vert[0],
vert[1] if normal != 1 else 1-vert[1],
vert[2] if normal != 2 else 1-vert[2]
)
def sym_x(vert):
return sym_general(vert, 0)
def sym_y(vert):
return sym_general(vert, 1)
def sym_z(vert):
return sym_general(vert, 2)
def all_transforms():
def compose2(fun1, fun2):
return lambda x: fun1(fun2(x))
def compose(*fs):
return reduce(compose2, fs, lambda x: x)
def funcpow(fun, exp):
return compose(*([fun] * exp))
output = []
for num_rx in range(4):
for num_ry in range(4):
for num_rz in range(4):
for syms in range(8):
cur = compose(
funcpow(rot_x, num_rx),
funcpow(rot_y, num_ry),
funcpow(rot_z, num_rz),
sym_x if (syms & 0x1) else lambda x: x,
sym_y if (syms & 0x2) else lambda x: x,
sym_z if (syms & 0x4) else lambda x: x)
output.append(cur)
return output
def do_main(base_cases):
transforms = all_transforms()
index = [None for x in range(256)]
for case in base_cases:
code = case.activated_code()
assert index[code] is None
index[code] = case
for transf in transforms:
for case in base_cases:
tr_case = case.transform(transf)
code = tr_case.activated_code()
if index[code] is None:
index[code] = tr_case
for (val, tri) in enumerate(index):
if tri is None:
print(">> UNBOUND {}".format(val))
print(("static const std::vector<MarchingCubes::CubeTri> "
"edges_of_intersection[256] = {"))
for (case_id, case) in enumerate(index):
print("\tstd::vector<MarchingCubes::CubeTri>({")
for (tri_id, tri) in enumerate(case.triangles):
print(dump_tri(tri),
end=",\n" if tri_id == len(case.triangles) - 1 else '\n')
print('})', end=',\n' if case_id == len(base_cases) - 1 else '\n')
print("};")
def main():
base_cases = [
# Source: <https://en.wikipedia.org/wiki/File:MarchingCubes.svg>
# -- 1 --
TriangulatedCube(set(), []),
# -- 2 --
TriangulatedCube(
{
Vert.frnt_bot_l,
},
[
[
Edg.frnt_bot,
Edg.bot_l,
Edg.frnt_l,
],
]),
# -- 3 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.frnt_bot_r,
},
[
[
Edg.frnt_l,
Edg.bot_l,
Edg.bot_r,
],
[
Edg.frnt_l,
Edg.frnt_r,
Edg.bot_r,
],
]),
# -- 4 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.frnt_top_r,
},
[
[
Edg.frnt_bot,
Edg.bot_l,
Edg.frnt_l,
],
[
Edg.frnt_top,
Edg.top_r,
Edg.frnt_r,
],
]),
# -- 5 --
TriangulatedCube(
{
Vert.frnt_bot_r,
Vert.back_bot_r,
Vert.back_bot_l,
},
[
[
Edg.frnt_r,
Edg.back_r,
Edg.back_l,
],
[
Edg.bot_l,
Edg.frnt_bot,
Edg.frnt_r,
],
[
Edg.frnt_r,
Edg.back_l,
Edg.bot_l,
],
]),
# -- 6 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.back_bot_l,
Vert.frnt_bot_r,
Vert.back_bot_r,
},
[
[
Edg.frnt_l,
Edg.frnt_r,
Edg.back_r,
],
[
Edg.back_r,
Edg.back_l,
Edg.frnt_l,
],
]),
# -- 7 --
TriangulatedCube(
{
Vert.frnt_top_l,
Vert.back_bot_l,
Vert.frnt_bot_r,
Vert.back_bot_r,
},
[
[
Edg.frnt_r,
Edg.back_r,
Edg.back_l,
],
[
Edg.bot_l,
Edg.frnt_bot,
Edg.frnt_r,
],
[
Edg.frnt_r,
Edg.back_l,
Edg.bot_l,
],
[
Edg.frnt_l,
Edg.frnt_top,
Edg.top_l,
],
]),
# -- 8 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.back_bot_r,
Vert.frnt_top_r,
Vert.back_top_l,
},
[
[
Edg.frnt_bot,
Edg.bot_l,
Edg.frnt_l,
],
[
Edg.frnt_top,
Edg.top_r,
Edg.frnt_r,
],
[
Edg.back_bot,
Edg.bot_r,
Edg.back_r,
],
[
Edg.back_top,
Edg.top_l,
Edg.back_l,
],
]),
# -- 9 --
TriangulatedCube(
{
Vert.back_bot_l,
Vert.back_bot_r,
Vert.back_top_l,
Vert.frnt_bot_l,
},
[
[
Edg.top_l,
Edg.frnt_l,
Edg.back_top,
],
[
Edg.frnt_l,
Edg.back_top,
Edg.frnt_bot,
],
[
Edg.back_top,
Edg.frnt_bot,
Edg.back_r,
],
[
Edg.frnt_bot,
Edg.back_r,
Edg.bot_r,
],
]),
# -- 10 --
TriangulatedCube(
{
Vert.back_top_l,
Vert.back_bot_l,
Vert.back_bot_r,
Vert.frnt_bot_r,
},
[
[
Edg.bot_l,
Edg.frnt_bot,
Edg.top_l,
],
[
Edg.frnt_bot,
Edg.top_l,
Edg.back_r,
],
[
Edg.top_l,
Edg.back_r,
Edg.back_top,
],
[
Edg.frnt_bot,
Edg.back_r,
Edg.frnt_r,
],
]),
# -- 11 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.back_top_r,
},
[
[
Edg.frnt_bot,
Edg.frnt_l,
Edg.bot_l,
],
[
Edg.back_top,
Edg.back_r,
Edg.top_r,
],
]),
# -- 12 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.frnt_bot_r,
Vert.back_top_r,
},
[
[
Edg.frnt_l,
Edg.bot_l,
Edg.bot_r,
],
[
Edg.frnt_l,
Edg.frnt_r,
Edg.bot_r,
],
[
Edg.back_top,
Edg.back_r,
Edg.top_r,
],
]),
# -- 13 --
TriangulatedCube(
{
Vert.frnt_top_l,
Vert.frnt_bot_r,
Vert.back_top_r,
},
[
[
Edg.frnt_l,
Edg.frnt_top,
Edg.top_l,
],
[
Edg.frnt_r,
Edg.frnt_bot,
Edg.bot_r,
],
[
Edg.back_r,
Edg.back_top,
Edg.top_r,
],
]),
# -- 14 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.frnt_top_l,
Vert.back_bot_r,
Vert.back_top_r,
},
[
[
Edg.frnt_top,
Edg.frnt_bot,
Edg.top_l,
],
[
Edg.frnt_bot,
Edg.top_l,
Edg.bot_l,
],
[
Edg.back_bot,
Edg.back_top,
Edg.bot_r,
],
[
Edg.back_top,
Edg.bot_r,
Edg.top_r,
],
]),
# -- 15 --
TriangulatedCube(
{
Vert.frnt_bot_l,
Vert.back_bot_l,
Vert.back_bot_r,
Vert.back_top_r,
},
[
[
Edg.frnt_l,
Edg.frnt_bot,
Edg.back_l,
],
[
Edg.frnt_bot,
Edg.back_l,
Edg.top_r,
],
[
Edg.back_l,
Edg.top_r,
Edg.back_top,
],
[
Edg.frnt_bot,
Edg.top_r,
Edg.bot_r,
],
]),
]
do_main(base_cases)
if __name__ == "__main__":
main()