#!/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 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: # -- 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()