mpri-graphics-project/tools/gen_marching_cubes_conf.py

579 lines
13 KiB
Python

#!/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
import sys
PREAMBLE = """
#include "MarchingCubes.hpp"
typedef MarchingCubes::CubeTri Tri;
typedef std::vector<Tri> TriVect;
typedef MarchingCubes::CubeEdge Edge;
"""
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 "1" if val else "0"
return "Edge({}, {}, {}, {}, {}, {})".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:
ALL_ELTS = {
Vert.frnt_bot_l,
Vert.frnt_bot_r,
Vert.frnt_top_l,
Vert.frnt_top_r,
Vert.back_bot_l,
Vert.back_bot_r,
Vert.back_top_l,
Vert.back_top_r,
}
def __init__(self, activated, triangles=None):
self.triangles = [] if triangles is None else triangles
self.activated = activated
self.non_activated = TriangulatedCube.ALL_ELTS - 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 reverse_activated(self):
self.activated, self.non_activated = \
self.non_activated, self.activated
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 ("Tri({}, {}, {})".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 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):
cur = compose(
funcpow(rot_x, num_rx),
funcpow(rot_y, num_ry),
funcpow(rot_z, num_rz))
output.append(cur)
return output
def gen_index(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 rev_activated in [False, True]:
for case in base_cases:
tr_case = case.transform(transf)
if rev_activated:
tr_case.reverse_activated()
code = tr_case.activated_code()
if index[code] is None:
index[code] = tr_case
has_unbound = False
for (val, tri) in enumerate(index):
if tri is None:
print(">> UNBOUND {}".format(val), file=sys.stderr)
has_unbound = True
if has_unbound:
raise RuntimeError("Some cases were not generated.")
return index
def pretty_print(index):
output = ""
output += PREAMBLE
output += "static const TriVect edges_of_intersection[256] = {\n"
for (case_id, case) in enumerate(index):
output += "\tTriVect({\n"
for (tri_id, tri) in enumerate(case.triangles):
output += '\t\t{}{}'.format(
dump_tri(tri),
",\n" if tri_id != len(case.triangles) - 1 else '\n')
output += '\t\t})' + (',\n' if case_id != len(index) - 1 else '\n')
output += "};\n"
return output
def do_main(base_cases):
print(pretty_print(gen_index(base_cases)))
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,
],
]),
]
def main():
do_main(BASE_CASES)
if __name__ == "__main__":
main()