From 39d12ef8a48f227890efff3668809fdcad861622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Fri, 9 Feb 2018 00:29:16 +0100 Subject: [PATCH] Marching cubes: tentative configurations generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .gitignore | 1 + tools/__init__.py | 0 tools/gen_marching_cubes_conf.py | 556 +++++++++++++++++++++++++++++++ 3 files changed, 557 insertions(+) create mode 100644 tools/__init__.py create mode 100644 tools/gen_marching_cubes_conf.py diff --git a/.gitignore b/.gitignore index 6769bd9..2530b6d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ *.app *.bin +__pycache__ diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/gen_marching_cubes_conf.py b/tools/gen_marching_cubes_conf.py new file mode 100644 index 0000000..899a462 --- /dev/null +++ b/tools/gen_marching_cubes_conf.py @@ -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 " + "edges_of_intersection[256] = {")) + + for (case_id, case) in enumerate(index): + print("\tstd::vector({") + + 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: + # -- 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()