From a148a0224703d01bd20c6d5ecf0817d1a8b73532 Mon Sep 17 00:00:00 2001 From: kijai <40791699+kijai@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:44:26 +0200 Subject: [PATCH] Add ODC https://github.com/KAIST-Visual-AI-Group/ODC/ --- hy3dgen/shapegen/models/vae.py | 59 +- nodes.py | 2 +- occupancy_dual_contouring.py | 1097 ++++++++++++++++++++++++++++++++ 3 files changed, 1133 insertions(+), 25 deletions(-) create mode 100644 occupancy_dual_contouring.py diff --git a/hy3dgen/shapegen/models/vae.py b/hy3dgen/shapegen/models/vae.py index c66f1dd..16f88ca 100755 --- a/hy3dgen/shapegen/models/vae.py +++ b/hy3dgen/shapegen/models/vae.py @@ -628,30 +628,48 @@ class ShapeVAE(nn.Module): ) xyz_samples = torch.FloatTensor(xyz_samples) + if mc_algo == 'odc': + from ....occupancy_dual_contouring import occupancy_dual_contouring + odc = occupancy_dual_contouring(device=device) + # 2. latents to 3d volume batch_logits = [] batch_size = latents.shape[0] comfy_pbar = ProgressBar(xyz_samples.shape[0]) for start in tqdm(range(0, xyz_samples.shape[0], num_chunks), desc=f"MC Level {mc_level} Implicit Function:"): - queries = xyz_samples[start: start + num_chunks, :].to(device) - queries = queries.half() - batch_queries = repeat(queries, "p c -> b p c", b=batch_size) + if mc_algo == 'odc': + imp_func = lambda xyz: torch.flatten(self.geo_decoder(repeat(xyz, "p c -> b p c", b=batch_size).to(latents.dtype), latents)) + vertices, faces = odc.extract_mesh(imp_func, num_grid = octree_resolution, isolevel=mc_level, batch_size=num_chunks, min_coord=bbox_min, max_coord=bbox_max) + comfy_pbar.update(num_chunks) + else: + queries = xyz_samples[start: start + num_chunks, :].to(device) + queries = queries.half() + batch_queries = repeat(queries, "p c -> b p c", b=batch_size) + logits = self.geo_decoder(batch_queries.to(latents.dtype), latents) + if mc_level == -1: + mc_level = 0 + logits = torch.sigmoid(logits) * 2 - 1 + print(f'Training with soft labels, inference with sigmoid and marching cubes level 0.') + batch_logits.append(logits) + comfy_pbar.update(num_chunks) - logits = self.geo_decoder(batch_queries.to(latents.dtype), latents) - if mc_level == -1: - mc_level = 0 - logits = torch.sigmoid(logits) * 2 - 1 - print(f'Training with soft labels, inference with sigmoid and marching cubes level 0.') - batch_logits.append(logits) - comfy_pbar.update(num_chunks) - grid_logits = torch.cat(batch_logits, dim=1) - grid_logits = grid_logits.view((batch_size, grid_size[0], grid_size[1], grid_size[2])).float() + if mc_algo == 'odc': + vertices = vertices.detach().cpu().numpy() + faces = faces.detach().cpu().numpy() + outputs = [ + Latent2MeshOutput( + mesh_v=vertices.astype(np.float32), + mesh_f=np.ascontiguousarray(faces) + )] + return outputs + else: + grid_logits = torch.cat(batch_logits, dim=1) + grid_logits = grid_logits.view((batch_size, grid_size[0], grid_size[1], grid_size[2])).float() - # 3. extract surface - outputs = [] - for i in range(batch_size): - try: + # 3. extract surface + outputs = [] + for i in range(batch_size): if mc_algo == 'mc': vertices, faces, normals, _ = measure.marching_cubes( grid_logits[i].cpu().numpy(), @@ -712,8 +730,6 @@ class ShapeVAE(nn.Module): else: vertices = np.array([]) faces = np.array([]) - else: - raise ValueError(f"mc_algo {mc_algo} not supported.") outputs.append( Latent2MeshOutput( @@ -722,12 +738,7 @@ class ShapeVAE(nn.Module): ) ) - except ValueError: - outputs.append(None) - except RuntimeError: - outputs.append(None) - - return outputs + return outputs def create_cube_mesh(pos, size=1.0): diff --git a/nodes.py b/nodes.py index 09aab02..e85acc2 100644 --- a/nodes.py +++ b/nodes.py @@ -1085,7 +1085,7 @@ class Hy3DVAEDecode: "octree_resolution": ("INT", {"default": 384, "min": 64, "max": 4096, "step": 16}), "num_chunks": ("INT", {"default": 8000, "min": 1, "max": 10000000, "step": 1, "tooltip": "Number of chunks to process at once, higher values use more memory, but make the process faster"}), "mc_level": ("FLOAT", {"default": 0, "min": -1.0, "max": 1.0, "step": 0.0001}), - "mc_algo": (["mc", "dmc", "none"], {"default": "mc"}), + "mc_algo": (["mc", "dmc", "odc", "none"], {"default": "mc"}), }, } diff --git a/occupancy_dual_contouring.py b/occupancy_dual_contouring.py new file mode 100644 index 0000000..a99e4b7 --- /dev/null +++ b/occupancy_dual_contouring.py @@ -0,0 +1,1097 @@ +# This code is written by Jisung Hwang (4011hjs@kaist.ac.kr) in 2024. +# This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. + +pfvtab = [ + [], + [[13, 2, 17, 3, 15, 1, 13]], + [[19, 4, 13, 1, 15, 5, 19]], + [[13, 2, 17, 3, 15, 5, 19, 4, 13]], + [[13, 6, 21, 7, 17, 2, 13]], + [[15, 1, 13, 6, 21, 7, 17, 3, 15]], + [[19, 4, 13, 1, 15, 5, 19], [14, 6, 21, 7, 17, 2, 14]], + [[21, 7, 17, 3, 15, 5, 19, 4, 13, 6, 21]], + [[21, 6, 13, 4, 19, 10, 21]], + [[13, 2, 17, 3, 15, 1, 13], [21, 6, 14, 4, 19, 10, 21]], + [[13, 1, 15, 5, 19, 10, 21, 6, 13]], + [[19, 10, 21, 6, 13, 2, 17, 3, 15, 5, 19]], + [[17, 2, 13, 4, 19, 10, 21, 7, 17]], + [[19, 10, 21, 7, 17, 3, 15, 1, 13, 4, 19]], + [[21, 7, 17, 2, 13, 1, 15, 5, 19, 10, 21]], + [[21, 7, 17, 3, 15, 5, 19, 10, 21]], + [[23, 8, 15, 3, 17, 9, 23]], + [[15, 1, 13, 2, 17, 9, 23, 8, 15]], + [[15, 5, 19, 4, 13, 1, 15], [23, 8, 16, 3, 17, 9, 23]], + [[17, 9, 23, 8, 15, 5, 19, 4, 13, 2, 17]], + [[21, 7, 17, 2, 13, 6, 21], [18, 9, 23, 8, 15, 3, 18]], + [[23, 8, 15, 1, 13, 6, 21, 7, 17, 9, 23]], + [[19, 4, 13, 1, 15, 5, 19], [14, 6, 21, 7, 17, 2, 14], [23, 8, 16, 3, 18, 9, 23]], + [[21, 7, 17, 9, 23, 8, 15, 5, 19, 4, 13, 6, 21]], + [[23, 8, 15, 3, 17, 9, 23], [21, 6, 13, 4, 19, 10, 21]], + [[15, 1, 13, 2, 17, 9, 23, 8, 15], [21, 6, 14, 4, 19, 10, 21]], + [[13, 1, 15, 5, 19, 10, 21, 6, 13], [23, 8, 16, 3, 17, 9, 23]], + [[17, 9, 23, 8, 15, 5, 19, 10, 21, 6, 13, 2, 17]], + [[17, 2, 13, 4, 19, 10, 21, 7, 17], [18, 9, 23, 8, 15, 3, 18]], + [[23, 8, 15, 1, 13, 4, 19, 10, 21, 7, 17, 9, 23]], + [[21, 7, 17, 2, 13, 1, 15, 5, 19, 10, 21], [23, 8, 16, 3, 18, 9, 23]], + [[21, 7, 17, 9, 23, 8, 15, 5, 19, 10, 21]], + [[15, 8, 23, 11, 19, 5, 15]], + [[17, 3, 15, 1, 13, 2, 17], [16, 8, 23, 11, 19, 5, 16]], + [[13, 1, 15, 8, 23, 11, 19, 4, 13]], + [[23, 11, 19, 4, 13, 2, 17, 3, 15, 8, 23]], + [[13, 6, 21, 7, 17, 2, 13], [15, 8, 23, 11, 19, 5, 15]], + [[15, 1, 13, 6, 21, 7, 17, 3, 15], [16, 8, 23, 11, 19, 5, 16]], + [[13, 1, 15, 8, 23, 11, 19, 4, 13], [14, 6, 21, 7, 17, 2, 14]], + [[21, 7, 17, 3, 15, 8, 23, 11, 19, 4, 13, 6, 21]], + [[19, 10, 21, 6, 13, 4, 19], [23, 11, 20, 5, 15, 8, 23]], + [[13, 2, 17, 3, 15, 1, 13], [21, 6, 14, 4, 19, 10, 21], [16, 8, 23, 11, 20, 5, 16]], + [[15, 8, 23, 11, 19, 10, 21, 6, 13, 1, 15]], + [[19, 10, 21, 6, 13, 2, 17, 3, 15, 8, 23, 11, 19]], + [[13, 4, 19, 10, 21, 7, 17, 2, 13], [23, 11, 20, 5, 15, 8, 23]], + [[19, 10, 21, 7, 17, 3, 15, 1, 13, 4, 19], [16, 8, 23, 11, 20, 5, 16]], + [[21, 7, 17, 2, 13, 1, 15, 8, 23, 11, 19, 10, 21]], + [[19, 10, 21, 7, 17, 3, 15, 8, 23, 11, 19]], + [[15, 3, 17, 9, 23, 11, 19, 5, 15]], + [[23, 11, 19, 5, 15, 1, 13, 2, 17, 9, 23]], + [[17, 9, 23, 11, 19, 4, 13, 1, 15, 3, 17]], + [[17, 9, 23, 11, 19, 4, 13, 2, 17]], + [[15, 3, 18, 9, 23, 11, 19, 5, 15], [21, 7, 17, 2, 13, 6, 21]], + [[13, 6, 21, 7, 17, 9, 23, 11, 19, 5, 15, 1, 13]], + [[18, 9, 23, 11, 19, 4, 13, 1, 15, 3, 18], [14, 6, 21, 7, 17, 2, 14]], + [[17, 9, 23, 11, 19, 4, 13, 6, 21, 7, 17]], + [[20, 5, 15, 3, 17, 9, 23, 11, 20], [19, 10, 21, 6, 13, 4, 19]], + [[23, 11, 20, 5, 15, 1, 13, 2, 17, 9, 23], [21, 6, 14, 4, 19, 10, 21]], + [[17, 9, 23, 11, 19, 10, 21, 6, 13, 1, 15, 3, 17]], + [[23, 11, 19, 10, 21, 6, 13, 2, 17, 9, 23]], + [[17, 2, 13, 4, 19, 10, 21, 7, 17], [15, 3, 18, 9, 23, 11, 20, 5, 15]], + [[19, 10, 21, 7, 17, 9, 23, 11, 20, 5, 15, 1, 13, 4, 19]], + [[21, 7, 17, 2, 13, 1, 15, 3, 18, 9, 23, 11, 19, 10, 21]], + [[17, 9, 23, 11, 19, 10, 21, 7, 17]], + [[23, 9, 17, 7, 21, 12, 23]], + [[17, 3, 15, 1, 13, 2, 17], [23, 9, 18, 7, 21, 12, 23]], + [[19, 4, 13, 1, 15, 5, 19], [23, 9, 17, 7, 21, 12, 23]], + [[13, 2, 17, 3, 15, 5, 19, 4, 13], [23, 9, 18, 7, 21, 12, 23]], + [[17, 2, 13, 6, 21, 12, 23, 9, 17]], + [[21, 12, 23, 9, 17, 3, 15, 1, 13, 6, 21]], + [[17, 2, 14, 6, 21, 12, 23, 9, 17], [19, 4, 13, 1, 15, 5, 19]], + [[15, 5, 19, 4, 13, 6, 21, 12, 23, 9, 17, 3, 15]], + [[19, 10, 21, 6, 13, 4, 19], [22, 12, 23, 9, 17, 7, 22]], + [[15, 1, 13, 2, 17, 3, 15], [14, 4, 19, 10, 21, 6, 14], [23, 9, 18, 7, 22, 12, 23]], + [[21, 6, 13, 1, 15, 5, 19, 10, 21], [22, 12, 23, 9, 17, 7, 22]], + [[19, 10, 21, 6, 13, 2, 17, 3, 15, 5, 19], [23, 9, 18, 7, 22, 12, 23]], + [[23, 9, 17, 2, 13, 4, 19, 10, 21, 12, 23]], + [[19, 10, 21, 12, 23, 9, 17, 3, 15, 1, 13, 4, 19]], + [[15, 5, 19, 10, 21, 12, 23, 9, 17, 2, 13, 1, 15]], + [[19, 10, 21, 12, 23, 9, 17, 3, 15, 5, 19]], + [[15, 3, 17, 7, 21, 12, 23, 8, 15]], + [[21, 12, 23, 8, 15, 1, 13, 2, 17, 7, 21]], + [[16, 3, 17, 7, 21, 12, 23, 8, 16], [15, 5, 19, 4, 13, 1, 15]], + [[19, 4, 13, 2, 17, 7, 21, 12, 23, 8, 15, 5, 19]], + [[23, 8, 15, 3, 17, 2, 13, 6, 21, 12, 23]], + [[23, 8, 15, 1, 13, 6, 21, 12, 23]], + [[23, 8, 16, 3, 17, 2, 14, 6, 21, 12, 23], [19, 4, 13, 1, 15, 5, 19]], + [[23, 8, 15, 5, 19, 4, 13, 6, 21, 12, 23]], + [[17, 7, 22, 12, 23, 8, 15, 3, 17], [19, 10, 21, 6, 13, 4, 19]], + [[22, 12, 23, 8, 15, 1, 13, 2, 17, 7, 22], [14, 4, 19, 10, 21, 6, 14]], + [[13, 1, 15, 5, 19, 10, 21, 6, 13], [16, 3, 17, 7, 22, 12, 23, 8, 16]], + [[19, 10, 21, 6, 13, 2, 17, 7, 22, 12, 23, 8, 15, 5, 19]], + [[23, 8, 15, 3, 17, 2, 13, 4, 19, 10, 21, 12, 23]], + [[21, 12, 23, 8, 15, 1, 13, 4, 19, 10, 21]], + [[15, 5, 19, 10, 21, 12, 23, 8, 16, 3, 17, 2, 13, 1, 15]], + [[23, 8, 15, 5, 19, 10, 21, 12, 23]], + [[23, 11, 19, 5, 15, 8, 23], [21, 12, 24, 9, 17, 7, 21]], + [[15, 1, 13, 2, 17, 3, 15], [19, 5, 16, 8, 23, 11, 19], [18, 7, 21, 12, 24, 9, 18]], + [[15, 8, 23, 11, 19, 4, 13, 1, 15], [21, 12, 24, 9, 17, 7, 21]], + [[23, 11, 19, 4, 13, 2, 17, 3, 15, 8, 23], [18, 7, 21, 12, 24, 9, 18]], + [[24, 9, 17, 2, 13, 6, 21, 12, 24], [23, 11, 19, 5, 15, 8, 23]], + [[21, 12, 24, 9, 17, 3, 15, 1, 13, 6, 21], [19, 5, 16, 8, 23, 11, 19]], + [[13, 1, 15, 8, 23, 11, 19, 4, 13], [17, 2, 14, 6, 21, 12, 24, 9, 17]], + [[23, 11, 19, 4, 13, 6, 21, 12, 24, 9, 17, 3, 15, 8, 23]], + [[13, 4, 19, 10, 21, 6, 13], [20, 5, 15, 8, 23, 11, 20], [17, 7, 22, 12, 24, 9, 17]], + [[13, 2, 17, 3, 15, 1, 13], [21, 6, 14, 4, 19, 10, 21], [16, 8, 23, 11, 20, 5, 16], [24, 9, 18, 7, 22, 12, 24]], + [[15, 8, 23, 11, 19, 10, 21, 6, 13, 1, 15], [17, 7, 22, 12, 24, 9, 17]], + [[19, 10, 21, 6, 13, 2, 17, 3, 15, 8, 23, 11, 19], [24, 9, 18, 7, 22, 12, 24]], + [[24, 9, 17, 2, 13, 4, 19, 10, 21, 12, 24], [20, 5, 15, 8, 23, 11, 20]], + [[19, 10, 21, 12, 24, 9, 17, 3, 15, 1, 13, 4, 19], [16, 8, 23, 11, 20, 5, 16]], + [[15, 8, 23, 11, 19, 10, 21, 12, 24, 9, 17, 2, 13, 1, 15]], + [[19, 10, 21, 12, 24, 9, 17, 3, 15, 8, 23, 11, 19]], + [[17, 7, 21, 12, 23, 11, 19, 5, 15, 3, 17]], + [[23, 11, 19, 5, 15, 1, 13, 2, 17, 7, 21, 12, 23]], + [[19, 4, 13, 1, 15, 3, 17, 7, 21, 12, 23, 11, 19]], + [[23, 11, 19, 4, 13, 2, 17, 7, 21, 12, 23]], + [[13, 6, 21, 12, 23, 11, 19, 5, 15, 3, 17, 2, 13]], + [[21, 12, 23, 11, 19, 5, 15, 1, 13, 6, 21]], + [[19, 4, 13, 1, 15, 3, 17, 2, 14, 6, 21, 12, 23, 11, 19]], + [[13, 6, 21, 12, 23, 11, 19, 4, 13]], + [[17, 7, 22, 12, 23, 11, 20, 5, 15, 3, 17], [13, 4, 19, 10, 21, 6, 13]], + [[23, 11, 20, 5, 15, 1, 13, 2, 17, 7, 22, 12, 23], [21, 6, 14, 4, 19, 10, 21]], + [[21, 6, 13, 1, 15, 3, 17, 7, 22, 12, 23, 11, 19, 10, 21]], + [[23, 11, 19, 10, 21, 6, 13, 2, 17, 7, 22, 12, 23]], + [[13, 4, 19, 10, 21, 12, 23, 11, 20, 5, 15, 3, 17, 2, 13]], + [[21, 12, 23, 11, 20, 5, 15, 1, 13, 4, 19, 10, 21]], + [[17, 2, 13, 1, 15, 3, 17], [23, 11, 19, 10, 21, 12, 23]], + [[23, 11, 19, 10, 21, 12, 23]], + [[19, 11, 23, 12, 21, 10, 19]], + [[13, 2, 17, 3, 15, 1, 13], [19, 11, 23, 12, 21, 10, 19]], + [[15, 5, 19, 4, 13, 1, 15], [20, 11, 23, 12, 21, 10, 20]], + [[19, 4, 13, 2, 17, 3, 15, 5, 19], [20, 11, 23, 12, 21, 10, 20]], + [[21, 7, 17, 2, 13, 6, 21], [23, 12, 22, 10, 19, 11, 23]], + [[13, 6, 21, 7, 17, 3, 15, 1, 13], [23, 12, 22, 10, 19, 11, 23]], + [[13, 1, 15, 5, 19, 4, 13], [17, 2, 14, 6, 21, 7, 17], [20, 11, 23, 12, 22, 10, 20]], + [[21, 7, 17, 3, 15, 5, 19, 4, 13, 6, 21], [20, 11, 23, 12, 22, 10, 20]], + [[13, 4, 19, 11, 23, 12, 21, 6, 13]], + [[14, 4, 19, 11, 23, 12, 21, 6, 14], [13, 2, 17, 3, 15, 1, 13]], + [[23, 12, 21, 6, 13, 1, 15, 5, 19, 11, 23]], + [[17, 3, 15, 5, 19, 11, 23, 12, 21, 6, 13, 2, 17]], + [[19, 11, 23, 12, 21, 7, 17, 2, 13, 4, 19]], + [[17, 3, 15, 1, 13, 4, 19, 11, 23, 12, 21, 7, 17]], + [[21, 7, 17, 2, 13, 1, 15, 5, 19, 11, 23, 12, 21]], + [[21, 7, 17, 3, 15, 5, 19, 11, 23, 12, 21]], + [[17, 9, 23, 8, 15, 3, 17], [24, 12, 21, 10, 19, 11, 24]], + [[23, 8, 15, 1, 13, 2, 17, 9, 23], [24, 12, 21, 10, 19, 11, 24]], + [[13, 1, 15, 5, 19, 4, 13], [16, 3, 17, 9, 23, 8, 16], [21, 10, 20, 11, 24, 12, 21]], + [[17, 9, 23, 8, 15, 5, 19, 4, 13, 2, 17], [21, 10, 20, 11, 24, 12, 21]], + [[17, 2, 13, 6, 21, 7, 17], [15, 3, 18, 9, 23, 8, 15], [22, 10, 19, 11, 24, 12, 22]], + [[23, 8, 15, 1, 13, 6, 21, 7, 17, 9, 23], [22, 10, 19, 11, 24, 12, 22]], + [[19, 4, 13, 1, 15, 5, 19], [14, 6, 21, 7, 17, 2, 14], [23, 8, 16, 3, 18, 9, 23], [20, 11, 24, 12, 22, 10, 20]], + [[21, 7, 17, 9, 23, 8, 15, 5, 19, 4, 13, 6, 21], [20, 11, 24, 12, 22, 10, 20]], + [[19, 11, 24, 12, 21, 6, 13, 4, 19], [17, 9, 23, 8, 15, 3, 17]], + [[15, 1, 13, 2, 17, 9, 23, 8, 15], [14, 4, 19, 11, 24, 12, 21, 6, 14]], + [[24, 12, 21, 6, 13, 1, 15, 5, 19, 11, 24], [16, 3, 17, 9, 23, 8, 16]], + [[17, 9, 23, 8, 15, 5, 19, 11, 24, 12, 21, 6, 13, 2, 17]], + [[19, 11, 24, 12, 21, 7, 17, 2, 13, 4, 19], [15, 3, 18, 9, 23, 8, 15]], + [[23, 8, 15, 1, 13, 4, 19, 11, 24, 12, 21, 7, 17, 9, 23]], + [[21, 7, 17, 2, 13, 1, 15, 5, 19, 11, 24, 12, 21], [23, 8, 16, 3, 18, 9, 23]], + [[21, 7, 17, 9, 23, 8, 15, 5, 19, 11, 24, 12, 21]], + [[19, 5, 15, 8, 23, 12, 21, 10, 19]], + [[19, 5, 16, 8, 23, 12, 21, 10, 19], [17, 3, 15, 1, 13, 2, 17]], + [[23, 12, 21, 10, 19, 4, 13, 1, 15, 8, 23]], + [[13, 2, 17, 3, 15, 8, 23, 12, 21, 10, 19, 4, 13]], + [[22, 10, 19, 5, 15, 8, 23, 12, 22], [21, 7, 17, 2, 13, 6, 21]], + [[15, 1, 13, 6, 21, 7, 17, 3, 15], [19, 5, 16, 8, 23, 12, 22, 10, 19]], + [[23, 12, 22, 10, 19, 4, 13, 1, 15, 8, 23], [17, 2, 14, 6, 21, 7, 17]], + [[21, 7, 17, 3, 15, 8, 23, 12, 22, 10, 19, 4, 13, 6, 21]], + [[15, 8, 23, 12, 21, 6, 13, 4, 19, 5, 15]], + [[16, 8, 23, 12, 21, 6, 14, 4, 19, 5, 16], [13, 2, 17, 3, 15, 1, 13]], + [[15, 8, 23, 12, 21, 6, 13, 1, 15]], + [[15, 8, 23, 12, 21, 6, 13, 2, 17, 3, 15]], + [[17, 2, 13, 4, 19, 5, 15, 8, 23, 12, 21, 7, 17]], + [[17, 3, 15, 1, 13, 4, 19, 5, 16, 8, 23, 12, 21, 7, 17]], + [[23, 12, 21, 7, 17, 2, 13, 1, 15, 8, 23]], + [[15, 8, 23, 12, 21, 7, 17, 3, 15]], + [[21, 10, 19, 5, 15, 3, 17, 9, 23, 12, 21]], + [[13, 2, 17, 9, 23, 12, 21, 10, 19, 5, 15, 1, 13]], + [[17, 9, 23, 12, 21, 10, 19, 4, 13, 1, 15, 3, 17]], + [[17, 9, 23, 12, 21, 10, 19, 4, 13, 2, 17]], + [[22, 10, 19, 5, 15, 3, 18, 9, 23, 12, 22], [17, 2, 13, 6, 21, 7, 17]], + [[13, 6, 21, 7, 17, 9, 23, 12, 22, 10, 19, 5, 15, 1, 13]], + [[18, 9, 23, 12, 22, 10, 19, 4, 13, 1, 15, 3, 18], [14, 6, 21, 7, 17, 2, 14]], + [[17, 9, 23, 12, 22, 10, 19, 4, 13, 6, 21, 7, 17]], + [[15, 3, 17, 9, 23, 12, 21, 6, 13, 4, 19, 5, 15]], + [[13, 2, 17, 9, 23, 12, 21, 6, 14, 4, 19, 5, 15, 1, 13]], + [[23, 12, 21, 6, 13, 1, 15, 3, 17, 9, 23]], + [[21, 6, 13, 2, 17, 9, 23, 12, 21]], + [[17, 2, 13, 4, 19, 5, 15, 3, 18, 9, 23, 12, 21, 7, 17]], + [[17, 9, 23, 12, 21, 7, 17], [13, 4, 19, 5, 15, 1, 13]], + [[23, 12, 21, 7, 17, 2, 13, 1, 15, 3, 18, 9, 23]], + [[17, 9, 23, 12, 21, 7, 17]], + [[17, 7, 21, 10, 19, 11, 23, 9, 17]], + [[18, 7, 21, 10, 19, 11, 23, 9, 18], [17, 3, 15, 1, 13, 2, 17]], + [[21, 10, 20, 11, 23, 9, 17, 7, 21], [15, 5, 19, 4, 13, 1, 15]], + [[13, 2, 17, 3, 15, 5, 19, 4, 13], [18, 7, 21, 10, 20, 11, 23, 9, 18]], + [[19, 11, 23, 9, 17, 2, 13, 6, 21, 10, 19]], + [[15, 1, 13, 6, 21, 10, 19, 11, 23, 9, 17, 3, 15]], + [[20, 11, 23, 9, 17, 2, 14, 6, 21, 10, 20], [13, 1, 15, 5, 19, 4, 13]], + [[15, 5, 19, 4, 13, 6, 21, 10, 20, 11, 23, 9, 17, 3, 15]], + [[23, 9, 17, 7, 21, 6, 13, 4, 19, 11, 23]], + [[23, 9, 18, 7, 21, 6, 14, 4, 19, 11, 23], [15, 1, 13, 2, 17, 3, 15]], + [[13, 1, 15, 5, 19, 11, 23, 9, 17, 7, 21, 6, 13]], + [[17, 3, 15, 5, 19, 11, 23, 9, 18, 7, 21, 6, 13, 2, 17]], + [[23, 9, 17, 2, 13, 4, 19, 11, 23]], + [[23, 9, 17, 3, 15, 1, 13, 4, 19, 11, 23]], + [[19, 11, 23, 9, 17, 2, 13, 1, 15, 5, 19]], + [[23, 9, 17, 3, 15, 5, 19, 11, 23]], + [[21, 10, 19, 11, 23, 8, 15, 3, 17, 7, 21]], + [[15, 1, 13, 2, 17, 7, 21, 10, 19, 11, 23, 8, 15]], + [[21, 10, 20, 11, 23, 8, 16, 3, 17, 7, 21], [13, 1, 15, 5, 19, 4, 13]], + [[19, 4, 13, 2, 17, 7, 21, 10, 20, 11, 23, 8, 15, 5, 19]], + [[23, 8, 15, 3, 17, 2, 13, 6, 21, 10, 19, 11, 23]], + [[23, 8, 15, 1, 13, 6, 21, 10, 19, 11, 23]], + [[23, 8, 16, 3, 17, 2, 14, 6, 21, 10, 20, 11, 23], [19, 4, 13, 1, 15, 5, 19]], + [[23, 8, 15, 5, 19, 4, 13, 6, 21, 10, 20, 11, 23]], + [[15, 3, 17, 7, 21, 6, 13, 4, 19, 11, 23, 8, 15]], + [[15, 1, 13, 2, 17, 7, 21, 6, 14, 4, 19, 11, 23, 8, 15]], + [[13, 1, 15, 5, 19, 11, 23, 8, 16, 3, 17, 7, 21, 6, 13]], + [[23, 8, 15, 5, 19, 11, 23], [21, 6, 13, 2, 17, 7, 21]], + [[19, 11, 23, 8, 15, 3, 17, 2, 13, 4, 19]], + [[13, 4, 19, 11, 23, 8, 15, 1, 13]], + [[19, 11, 23, 8, 16, 3, 17, 2, 13, 1, 15, 5, 19]], + [[23, 8, 15, 5, 19, 11, 23]], + [[17, 7, 21, 10, 19, 5, 15, 8, 23, 9, 17]], + [[18, 7, 21, 10, 19, 5, 16, 8, 23, 9, 18], [15, 1, 13, 2, 17, 3, 15]], + [[13, 1, 15, 8, 23, 9, 17, 7, 21, 10, 19, 4, 13]], + [[13, 2, 17, 3, 15, 8, 23, 9, 18, 7, 21, 10, 19, 4, 13]], + [[17, 2, 13, 6, 21, 10, 19, 5, 15, 8, 23, 9, 17]], + [[15, 1, 13, 6, 21, 10, 19, 5, 16, 8, 23, 9, 17, 3, 15]], + [[13, 1, 15, 8, 23, 9, 17, 2, 14, 6, 21, 10, 19, 4, 13]], + [[13, 6, 21, 10, 19, 4, 13], [15, 8, 23, 9, 17, 3, 15]], + [[15, 8, 23, 9, 17, 7, 21, 6, 13, 4, 19, 5, 15]], + [[16, 8, 23, 9, 18, 7, 21, 6, 14, 4, 19, 5, 16], [13, 2, 17, 3, 15, 1, 13]], + [[15, 8, 23, 9, 17, 7, 21, 6, 13, 1, 15]], + [[15, 8, 23, 9, 18, 7, 21, 6, 13, 2, 17, 3, 15]], + [[23, 9, 17, 2, 13, 4, 19, 5, 15, 8, 23]], + [[23, 9, 17, 3, 15, 1, 13, 4, 19, 5, 16, 8, 23]], + [[17, 2, 13, 1, 15, 8, 23, 9, 17]], + [[15, 8, 23, 9, 17, 3, 15]], + [[17, 7, 21, 10, 19, 5, 15, 3, 17]], + [[17, 7, 21, 10, 19, 5, 15, 1, 13, 2, 17]], + [[21, 10, 19, 4, 13, 1, 15, 3, 17, 7, 21]], + [[17, 7, 21, 10, 19, 4, 13, 2, 17]], + [[21, 10, 19, 5, 15, 3, 17, 2, 13, 6, 21]], + [[19, 5, 15, 1, 13, 6, 21, 10, 19]], + [[21, 10, 19, 4, 13, 1, 15, 3, 17, 2, 14, 6, 21]], + [[13, 6, 21, 10, 19, 4, 13]], + [[17, 7, 21, 6, 13, 4, 19, 5, 15, 3, 17]], + [[17, 7, 21, 6, 14, 4, 19, 5, 15, 1, 13, 2, 17]], + [[15, 3, 17, 7, 21, 6, 13, 1, 15]], + [[21, 6, 13, 2, 17, 7, 21]], + [[15, 3, 17, 2, 13, 4, 19, 5, 15]], + [[13, 4, 19, 5, 15, 1, 13]], + [[17, 2, 13, 1, 15, 3, 17]], + [] +]; + +vitogi = [ + [0, 0, 0, -1], + + [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 2], [0, 0, 1, 1], [0, 0, 1, 2], [0, 1, 0, 0], + [0, 1, 0, 2], [1, 0, 0, 0], [1, 0, 0, 1], [0, 1, 1, 2], [1, 0, 1, 1], [1, 1, 0, 0], + + [0, 0, 0, 3], [0, 0, 0, 4], [0, 0, 0, 5], [0, 0, 0, 6], [0, 0, 0, 7], [0, 0, 0, 8], + [0, 0, 1, 7], [0, 0, 1, 8], [0, 1, 0, 5], [0, 1, 0, 6], [1, 0, 0, 3], [1, 0, 0, 4], + + [0, 0, 0, 9], [0, 0, 0, 10], [0, 0, 0, 11], [0, 0, 0, 12] +]; + +lctab_2d = [ + [], + [[0, 0, 1], [0, 0, 0]], + [[0, 0, 1], [0, 1, 0]], + [[0, 0, 0], [0, 1, 0]], + [[0, 0, 0], [1, 0, 1]], + [[0, 0, 1], [1, 0, 1]], + [[0, 0, 1], [0, 1, 0], [0, 0, 0], [1, 0, 1]], + [[0, 1, 0], [1, 0, 1]], + [[0, 1, 0], [1, 0, 1]], + [[0, 0, 1], [0, 0, 0], [0, 1, 0], [1, 0, 1]], + [[0, 0, 1], [1, 0, 1]], + [[0, 0, 0], [1, 0, 1]], + [[0, 0, 0], [0, 1, 0]], + [[0, 0, 1], [0, 1, 0]], + [[0, 0, 1], [0, 0, 0]], + [] +]; + +cstrtab = [ + [6, [[0, 5, 3]], [[0, 3, 6]]], + [9, [[1, 2, 4]], [[1, 7, 2]]], + [18, [[0, 5, 3]], [[0, 6, 5]]], + [20, [[0, 3, 6]], [[0, 6, 5]]], + [22, [[0, 5, 3]], [[0, 3, 6]], [[0, 6, 5]]], + [24, [[0, 6, 5]], [[1, 7, 2]]], + [25, [[4, 1, 2], [5, 0, 6]], [[1, 7, 2]]], + [26, [[3, 0, 5], [2, 1, 7]], [[0, 6, 5]]], + [28, [[3, 6, 0], [1, 7, 2]], [[0, 6, 5]]], + [30, [[0, 5, 3]], [[0, 6, 5]]], + [33, [[1, 2, 4]], [[1, 4, 7]]], + [36, [[0, 3, 6]], [[1, 4, 7]]], + [37, [[2, 4, 1], [3, 6, 0]], [[1, 4, 7]]], + [38, [[5, 3, 0], [4, 7, 1]], [[0, 3, 6]]], + [40, [[1, 7, 2]], [[1, 4, 7]]], + [41, [[1, 2, 4]], [[1, 7, 2]], [[1, 4, 7]]], + [44, [[2, 1, 7], [0, 3, 6]], [[1, 4, 7]]], + [45, [[1, 2, 4]], [[1, 4, 7]]], + [52, [[5, 0, 6], [1, 4, 7]], [[0, 3, 6]]], + [54, [[0, 5, 3]], [[0, 3, 6]]], + [56, [[4, 7, 1], [0, 6, 5]], [[1, 7, 2]]], + [57, [[1, 2, 4]], [[1, 7, 2]]], + [60, [[3, 6, 0], [1, 7, 2]], [[0, 6, 5], [1, 4, 7]]], + [61, [[1, 2, 7], [4, 1, 7]]], + [62, [[0, 6, 3], [5, 6, 0]]], + [65, [[2, 4, 1]], [[2, 7, 4]]], + [66, [[0, 5, 3]], [[2, 7, 4]]], + [67, [[1, 2, 4], [3, 0, 5]], [[2, 7, 4]]], + [70, [[6, 0, 3], [4, 2, 7]], [[0, 5, 3]]], + [72, [[2, 1, 7]], [[2, 7, 4]]], + [73, [[2, 4, 1]], [[2, 1, 7]], [[2, 7, 4]]], + [74, [[1, 7, 2], [0, 5, 3]], [[2, 7, 4]]], + [75, [[2, 4, 1]], [[2, 7, 4]]], + [82, [[6, 5, 0], [2, 7, 4]], [[0, 5, 3]]], + [86, [[0, 3, 6]], [[0, 5, 3]]], + [88, [[4, 2, 7], [0, 6, 5]], [[2, 1, 7]]], + [89, [[2, 4, 1]], [[2, 1, 7]]], + [90, [[3, 0, 5], [2, 1, 7]], [[0, 6, 5], [2, 7, 4]]], + [91, [[2, 7, 1], [4, 7, 2]]], + [94, [[0, 3, 5], [6, 0, 5]]], + [96, [[4, 7, 1]], [[4, 2, 7]]], + [97, [[4, 1, 2]], [[4, 7, 1]], [[4, 2, 7]]], + [98, [[1, 4, 7], [0, 5, 3]], [[4, 2, 7]]], + [99, [[4, 1, 2]], [[4, 2, 7]]], + [100, [[2, 7, 4], [0, 3, 6]], [[4, 7, 1]]], + [101, [[4, 1, 2]], [[4, 7, 1]]], + [102, [[5, 3, 0], [4, 7, 1]], [[0, 3, 6], [4, 2, 7]]], + [103, [[4, 1, 7], [2, 4, 7]]], + [104, [[7, 2, 1]], [[7, 1, 4]], [[7, 4, 2]]], + [105, [[1, 2, 4]], [[1, 7, 2]], [[1, 4, 7]], [[2, 7, 4]]], + [106, [[7, 2, 1]], [[7, 4, 2]]], + [107, [[2, 4, 1]], [[2, 7, 4]]], + [108, [[7, 2, 1]], [[7, 1, 4]]], + [109, [[1, 2, 4]], [[1, 4, 7]]], + [110, [[7, 4, 1], [2, 4, 7]]], + [111, [[1, 7, 4], [2, 4, 7]]], + [118, [[0, 3, 5], [6, 3, 0]]], + [120, [[7, 1, 4]], [[7, 2, 1]]], + [121, [[1, 2, 4]], [[1, 7, 2]]], + [122, [[7, 1, 2], [4, 7, 2]]], + [123, [[1, 2, 7], [4, 7, 2]]], + [124, [[7, 1, 2], [4, 1, 7]]], + [125, [[2, 7, 1], [4, 1, 7]]], + [126, [[1, 2, 4]], [[3, 5, 6]]], + [129, [[1, 2, 4]], [[3, 5, 6]]], + [130, [[3, 0, 5]], [[3, 5, 6]]], + [131, [[0, 5, 3], [2, 4, 1]], [[3, 5, 6]]], + [132, [[3, 6, 0]], [[3, 5, 6]]], + [133, [[0, 3, 6], [1, 2, 4]], [[3, 5, 6]]], + [134, [[3, 0, 5]], [[3, 6, 0]], [[3, 5, 6]]], + [135, [[3, 0, 5]], [[3, 5, 6]]], + [137, [[7, 2, 1], [5, 6, 3]], [[1, 2, 4]]], + [144, [[5, 0, 6]], [[5, 6, 3]]], + [145, [[0, 6, 5], [1, 2, 4]], [[5, 6, 3]]], + [146, [[5, 3, 0]], [[5, 0, 6]], [[5, 6, 3]]], + [147, [[5, 3, 0]], [[5, 6, 3]]], + [148, [[6, 0, 3]], [[6, 5, 0]], [[6, 3, 5]]], + [149, [[6, 0, 3]], [[6, 3, 5]]], + [150, [[0, 5, 3]], [[0, 3, 6]], [[0, 6, 5]], [[3, 5, 6]]], + [151, [[3, 0, 5]], [[3, 5, 6]]], + [152, [[3, 5, 6], [1, 7, 2]], [[5, 0, 6]]], + [153, [[4, 1, 2], [5, 0, 6]], [[1, 7, 2], [5, 6, 3]]], + [154, [[5, 3, 0]], [[5, 0, 6]]], + [155, [[5, 6, 0], [3, 6, 5]]], + [156, [[6, 0, 3]], [[6, 5, 0]]], + [157, [[6, 0, 5], [3, 6, 5]]], + [158, [[0, 5, 3]], [[0, 6, 5]]], + [159, [[0, 5, 6], [3, 6, 5]]], + [161, [[7, 1, 4], [3, 5, 6]], [[1, 2, 4]]], + [164, [[5, 6, 3], [1, 4, 7]], [[3, 6, 0]]], + [165, [[2, 4, 1], [3, 6, 0]], [[1, 4, 7], [3, 5, 6]]], + [166, [[3, 0, 5]], [[3, 6, 0]]], + [167, [[3, 0, 6], [5, 3, 6]]], + [169, [[1, 7, 2]], [[1, 2, 4]]], + [173, [[1, 4, 2], [7, 4, 1]]], + [180, [[6, 5, 0]], [[6, 0, 3]]], + [181, [[6, 3, 0], [5, 3, 6]]], + [182, [[0, 5, 3]], [[0, 3, 6]]], + [183, [[0, 6, 3], [5, 3, 6]]], + [185, [[1, 4, 2], [7, 1, 2]]], + [188, [[6, 3, 0], [5, 6, 0]]], + [189, [[2, 7, 4]], [[0, 5, 3]]], + [190, [[3, 0, 6], [5, 6, 0]]], + [193, [[7, 4, 2], [3, 5, 6]], [[2, 4, 1]]], + [194, [[6, 3, 5], [2, 7, 4]], [[3, 0, 5]]], + [195, [[1, 2, 4], [3, 0, 5]], [[2, 7, 4], [3, 5, 6]]], + [198, [[3, 6, 0]], [[3, 0, 5]]], + [199, [[3, 5, 0], [6, 5, 3]]], + [201, [[2, 1, 7]], [[2, 4, 1]]], + [203, [[2, 1, 4], [7, 2, 4]]], + [210, [[5, 0, 6]], [[5, 3, 0]]], + [211, [[5, 0, 3], [6, 5, 3]]], + [214, [[0, 3, 6]], [[0, 5, 3]]], + [215, [[0, 3, 5], [6, 5, 3]]], + [217, [[2, 1, 4], [7, 1, 2]]], + [218, [[5, 0, 3], [6, 0, 5]]], + [219, [[1, 4, 7]], [[0, 3, 6]]], + [222, [[3, 5, 0], [6, 0, 5]]], + [225, [[4, 7, 1]], [[4, 1, 2]]], + [227, [[4, 2, 1], [7, 2, 4]]], + [229, [[4, 2, 1], [7, 4, 1]]], + [230, [[3, 5, 0], [6, 3, 0]]], + [231, [[1, 7, 2]], [[0, 6, 5]]], + [233, [[1, 7, 2]], [[1, 2, 4]]], + [235, [[1, 4, 2], [7, 2, 4]]], + [237, [[2, 1, 4], [7, 4, 1]]], + [246, [[5, 0, 3], [6, 3, 0]]], + [249, [[4, 2, 1], [7, 1, 2]]] +]; + +cpnttab = [ + [6, [4, 4, 20], [4, 20, 4]], + [9, [4, 4, 4], [4, 20, 20]], + [18, [4, 4, 20], [20, 4, 4]], + [20, [4, 20, 4], [20, 4, 4]], + [22, [4, 4, 20], [4, 20, 4], [20, 4, 4]], + [24, [20, 4, 4], [4, 20, 20]], + [25, [12, 3, 3], [4, 20, 20]], + [26, [3, 12, 21], [20, 4, 4]], + [28, [3, 21, 12], [20, 4, 4]], + [30, [4, 4, 20], [20, 4, 4]], + [33, [4, 4, 4], [20, 4, 20]], + [36, [4, 20, 4], [20, 4, 20]], + [37, [3, 12, 3], [20, 4, 20]], + [38, [12, 3, 21], [4, 20, 4]], + [40, [4, 20, 20], [20, 4, 20]], + [41, [4, 4, 4], [4, 20, 20], [20, 4, 20]], + [44, [3, 21, 12], [20, 4, 20]], + [45, [4, 4, 4], [20, 4, 20]], + [52, [21, 3, 12], [4, 20, 4]], + [54, [4, 4, 20], [4, 20, 4]], + [56, [21, 3, 12], [4, 20, 20]], + [57, [4, 4, 4], [4, 20, 20]], + [60, [3, 21, 12], [21, 3, 12]], + [65, [4, 4, 4], [20, 20, 4]], + [66, [4, 4, 20], [20, 20, 4]], + [67, [3, 3, 12], [20, 20, 4]], + [70, [12, 21, 3], [4, 4, 20]], + [72, [4, 20, 20], [20, 20, 4]], + [73, [4, 4, 4], [4, 20, 20], [20, 20, 4]], + [74, [3, 12, 21], [20, 20, 4]], + [75, [4, 4, 4], [20, 20, 4]], + [82, [21, 12, 3], [4, 4, 20]], + [86, [4, 20, 4], [4, 4, 20]], + [88, [21, 12, 3], [4, 20, 20]], + [89, [4, 4, 4], [4, 20, 20]], + [90, [3, 12, 21], [21, 12, 3]], + [96, [20, 4, 20], [20, 20, 4]], + [97, [4, 4, 4], [20, 4, 20], [20, 20, 4]], + [98, [12, 3, 21], [20, 20, 4]], + [99, [4, 4, 4], [20, 20, 4]], + [100, [12, 21, 3], [20, 4, 20]], + [101, [4, 4, 4], [20, 4, 20]], + [102, [12, 3, 21], [12, 21, 3]], + [104, [4, 20, 20], [20, 4, 20], [20, 20, 4]], + [105, [4, 4, 4], [4, 20, 20], [20, 4, 20], [20, 20, 4]], + [106, [4, 20, 20], [20, 20, 4]], + [107, [4, 4, 4], [20, 20, 4]], + [108, [4, 20, 20], [20, 4, 20]], + [109, [4, 4, 4], [20, 4, 20]], + [120, [20, 4, 20], [4, 20, 20]], + [121, [4, 4, 4], [4, 20, 20]], + [126, [4, 4, 4], [20, 20, 20]], + [129, [4, 4, 4], [20, 20, 20]], + [130, [4, 4, 20], [20, 20, 20]], + [131, [3, 3, 12], [20, 20, 20]], + [132, [4, 20, 4], [20, 20, 20]], + [133, [3, 12, 3], [20, 20, 20]], + [134, [4, 4, 20], [4, 20, 4], [20, 20, 20]], + [135, [4, 4, 20], [20, 20, 20]], + [137, [12, 21, 21], [4, 4, 4]], + [144, [20, 4, 4], [20, 20, 20]], + [145, [12, 3, 3], [20, 20, 20]], + [146, [4, 4, 20], [20, 4, 4], [20, 20, 20]], + [147, [4, 4, 20], [20, 20, 20]], + [148, [4, 20, 4], [20, 4, 4], [20, 20, 20]], + [149, [4, 20, 4], [20, 20, 20]], + [150, [4, 4, 20], [4, 20, 4], [20, 4, 4], [20, 20, 20]], + [151, [4, 4, 20], [20, 20, 20]], + [152, [12, 21, 21], [20, 4, 4]], + [153, [12, 3, 3], [12, 21, 21]], + [154, [4, 4, 20], [20, 4, 4]], + [156, [4, 20, 4], [20, 4, 4]], + [158, [4, 4, 20], [20, 4, 4]], + [161, [21, 12, 21], [4, 4, 4]], + [164, [21, 12, 21], [4, 20, 4]], + [165, [3, 12, 3], [21, 12, 21]], + [166, [4, 4, 20], [4, 20, 4]], + [169, [4, 20, 20], [4, 4, 4]], + [180, [20, 4, 4], [4, 20, 4]], + [182, [4, 4, 20], [4, 20, 4]], + [189, [20, 20, 4], [4, 4, 20]], + [193, [21, 21, 12], [4, 4, 4]], + [194, [21, 21, 12], [4, 4, 20]], + [195, [3, 3, 12], [21, 21, 12]], + [198, [4, 20, 4], [4, 4, 20]], + [201, [4, 20, 20], [4, 4, 4]], + [210, [20, 4, 4], [4, 4, 20]], + [214, [4, 20, 4], [4, 4, 20]], + [219, [20, 4, 20], [4, 20, 4]], + [225, [20, 4, 20], [4, 4, 4]], + [231, [4, 20, 20], [20, 4, 4]], + [233, [4, 20, 20], [4, 4, 4]] +]; + +ambtab = [ + [61, 0, 0, 1], + [62, 0, 0, -1], + [91, 0, 1, 0], + [94, 0, -1, 0], + [103, 1, 0, 0], + [110, 1, 0, 0], + [111, 1, 0, 0], + [118, -1, 0, 0], + [122, 0, 1, 0], + [123, 0, 1, 0], + [124, 0, 0, 1], + [125, 0, 0, 1], + [155, 1, 0, 0], + [157, 1, 0, 0], + [159, 1, 0, 0], + [167, 0, 1, 0], + [173, 0, -1, 0], + [181, 0, 1, 0], + [183, 0, 1, 0], + [185, -1, 0, 0], + [188, 0, 0, -1], + [190, 0, 0, -1], + [199, 0, 0, 1], + [203, 0, 0, -1], + [211, 0, 0, 1], + [215, 0, 0, 1], + [217, -1, 0, 0], + [218, 0, -1, 0], + [222, 0, -1, 0], + [227, 0, 0, -1], + [229, 0, -1, 0], + [230, -1, 0, 0], + [235, 0, 0, -1], + [237, 0, -1, 0], + [246, -1, 0, 0], + [249, -1, 0, 0] +]; + +axd_tab = [[1, 2], [1, 2], [0, 2], [0, 2], [0, 1], [0, 1]]; +ax0_tab = [[0, 1, 0], [0, 1, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]]; +ax1_tab = [[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 1, 0], [0, 1, 0]]; +cas_tab = [0, 2, 0, 2, 0, 2]; + +import torch as th + +class occupancy_dual_contouring: + def __init__(self, device): + self.device = device; + self.vitogi = th.tensor(vitogi, dtype = th.int64, device = device); + + for a in lctab_2d: a += [[-1, -1, -1]] * (4 - len(a)); + self.lctab_2d = th.tensor(lctab_2d, dtype = th.int64, device = device); + + for a in pfvtab: + for b in a: b += [0] * (15 - len(b)); + a += [[0] * 15] * (4 - len(a)); + self.pfvtab = th.tensor(pfvtab, dtype = th.int64, device = device); + + self.qvntab = th.div(th.sum(self.pfvtab > 0, dim = 2), 2, rounding_mode = 'floor'); + + self.vtstab = th.sort(self.pfvtab.reshape(256, -1), dim = 1)[0][:, -32:]; + for i in range(4): self.vtstab[self.qvntab[:, i] > 0, i] = 25 + i; + + self.vbitab = th.zeros((256, 12), dtype = th.int64, device = device); + for i in range(12): + tmp = th.sum(self.pfvtab == i + 1, dim = 2); + for j in range(4): self.vbitab[tmp[:, j] > 0, i] = j + 9; + + self.cstrtab = [[] for i in range(256)]; + for a in cstrtab: self.cstrtab[a[0]] = a[1:]; + for a in self.cstrtab: a += [[[0, 2, 1]]] * (4 - len(a)); + for a in self.cstrtab: + for b in a: b += [[0, 2, 1]] * (2 - len(b)); + tmp = th.tensor(self.cstrtab, dtype = th.int64, device = device); + self.cstrtab = th.stack((th.div(tmp, 4, rounding_mode = 'floor'), + th.div(tmp, 2, rounding_mode = 'floor') % 2, + tmp % 2), dim = 4).to(th.float64); + + self.cpnttab = [[] for i in range(256)]; + for a in cpnttab: self.cpnttab[a[0]] = a[1:]; + for a in self.cpnttab: a += [[12, 12, 12]] * (4 - len(a)); + self.cpnttab = th.tensor(self.cpnttab, dtype = th.float64, device = device) / 24.0; + + self.ambtab = [[0, 0, 0, 0] for _ in range(256)]; + for a in ambtab: self.ambtab[a[0]] = [1, a[1], a[2], a[3]]; + self.ambtab = th.tensor(self.ambtab, dtype = th.int64, device = device); + + self.axd_tab = th.tensor(axd_tab, dtype = th.int64, device = device); + self.ax0_tab = th.tensor(ax0_tab, dtype = th.int64, device = device); + self.ax1_tab = th.tensor(ax1_tab, dtype = th.int64, device = device); + self.cas_tab = th.tensor(cas_tab, dtype = th.int64, device = device); + + @th.no_grad() + def extract_mesh( + self, + imp_func, + min_coord: list[float] = [-0.5, -0.5, -0.5], + max_coord: list[float] = [ 0.5, 0.5, 0.5], + num_grid: int = 128, + isolevel: float = 0.0, + batch_size: int = 100000000, + imp_func_cplx: int = 3, + outside: bool = True, + BINARY_SEARCH: int = 15, + VERTICAL_RANGE: float = 0.8, + VERTICAL_LINEAR_SEARCH: int = 4, + VERTICAL_BINARY_SEARCH: int = 11, + HORIZNTL_RANGE: float = 0.71, + HORIZNTL_LINEAR_SEARCH: int = 3, + HORIZNTL_BINARY_SEARCH: int = 12, + ERR: float = 3e-7, + SEP: float = 1e-3, + QEF_REG: float = 0.05 + ): + GRID = num_grid; + GRID_ = GRID + 1; + GRID2_, GRID3_ = GRID_ ** 2, GRID_ ** 3; + device = self.device; + + min_coordc = th.tensor(min_coord, dtype = th.float64, device = device).reshape(1, 3); + siz_coordc = th.tensor(max_coord, dtype = th.float64, device = device).reshape(1, 3) - min_coordc; + + B = max(1, batch_size // imp_func_cplx); + if outside: get_occv_ = lambda x: imp_func(x) < isolevel; + else: get_occv_ = lambda x: imp_func(x) > isolevel; + + pidx1_to_pidx3 = lambda pi1: th.remainder(th.stack((th.div(pi1, GRID2_, rounding_mode = 'floor'), + th.div(pi1, GRID_, rounding_mode = 'floor'), pi1), dim = -1), GRID_); + pidx3_to_pnts = lambda pi3: (pi3 / GRID).to(th.float64); + + def get_occv(cor_): + if cor_.shape[0] == 0: + return th.zeros((0,), dtype = th.bool, device = device); + + cor = min_coordc + cor_ * siz_coordc; + if cor.shape[0] <= B: return get_occv_(cor); + + out = []; + for cal_i in range(0, cor.shape[0], B): + out.append(get_occv_(cor[cal_i: cal_i + B])); + return th.cat(out, dim = 0); + + pidx1 = th.arange(GRID3_, dtype = th.int64, device = device); + occs = get_occv(pidx3_to_pnts(pidx1_to_pidx3(pidx1))).reshape(GRID_, GRID_, GRID_); + + vals = th.zeros((GRID, GRID, GRID), dtype = th.uint8, device = device); + vals += occs[ 1:, 1:, 1:]; vals <<= 1; + vals += occs[ 1:, 1:, :-1]; vals <<= 1; + vals += occs[ 1:, :-1, 1:]; vals <<= 1; + vals += occs[ 1:, :-1, :-1]; vals <<= 1; + vals += occs[:-1, 1:, 1:]; vals <<= 1; + vals += occs[:-1, 1:, :-1]; vals <<= 1; + vals += occs[:-1, :-1, 1:]; vals <<= 1; + vals += occs[:-1, :-1, :-1]; + + cel_pidx3 = th.argwhere((vals > 0) & (vals < 255)); + + val_ = vals[cel_pidx3[:, 0], cel_pidx3[:, 1], cel_pidx3[:, 2]].to(th.int64); + ambres = self.ambtab[val_]; + amb_idcs = th.argwhere(ambres[:, 0] == 1).reshape(-1); + amb_pidx3 = cel_pidx3[amb_idcs] + ambres[amb_idcs, 1:]; + tpi = th.argwhere(th.sum((0 <= amb_pidx3) & (amb_pidx3 < GRID), dim = 1) == 3).reshape(-1); + tmp = vals[amb_pidx3[tpi, 0], amb_pidx3[tpi, 1], amb_pidx3[tpi, 2]].to(th.int64); + amb_pidx3 = amb_pidx3[tpi[self.ambtab[tmp, 0] == 1]]; + tmp = vals[amb_pidx3[:, 0], amb_pidx3[:, 1], amb_pidx3[:, 2]]; + vals[amb_pidx3[:, 0], amb_pidx3[:, 1], amb_pidx3[:, 2]] = 255 - tmp; + amb_chks = th.zeros((GRID_, GRID_, GRID_), dtype = th.bool, device = device); + amb_chks[amb_pidx3[:, 0], amb_pidx3[:, 1], amb_pidx3[:, 2]] = True; + + val_ = vals[cel_pidx3[:, 0], cel_pidx3[:, 1], cel_pidx3[:, 2]].to(th.int64); + + if cel_pidx3.shape[0] == 0: + print("Occupancy Dual Contouring: No Surface Detected."); + return th.zeros((0, 3), dtype = th.float64, device = device), \ + th.zeros((0, 3), dtype = th.int64, device = device); + + vtcs = self.vtstab[val_]; + sel_pidx1 = self.vitogi[vtcs, 3] * GRID3_ + \ + (cel_pidx3[:, 0:1] + self.vitogi[vtcs, 0]) * GRID2_ + \ + (cel_pidx3[:, 1:2] + self.vitogi[vtcs, 1]) * GRID_ + \ + (cel_pidx3[:, 2:3] + self.vitogi[vtcs, 2]); + sel_pidx1 = sel_pidx1.reshape(-1); + sel_pidx1 = th.unique(sel_pidx1[sel_pidx1 >= 0]); + + val_pnts = th.zeros((sel_pidx1.shape[0], 3), dtype = th.float64, device = device); + + def get_idx(q): + S = q.shape; q_ = q.reshape(-1); + std, idx = th.unique(th.cat((q_, sel_pidx1), dim = 0), return_inverse = True); + return idx[:q_.shape[0]].reshape(S); + + def _1d_search(pidx3, axis, val_): + P = pidx3.shape[0]; + pnts = pidx3_to_pnts(pidx3); + left = th.zeros((P, 1), dtype = th.float64, device = device); + rght = th.ones((P, 1), dtype = th.float64, device = device); + + for _ in range(BINARY_SEARCH): + mid = (left + rght) * 0.5; + occv = get_occv(pnts + axis * (mid / GRID)); + + jdge = (occv == val_).unsqueeze(1); + left = th.where(jdge, mid, left); + rght = th.where(jdge, rght, mid); + + return left * axis; + + start_idx = 0; + pidx1 = sel_pidx1[(0 * GRID3_ <= sel_pidx1) & (sel_pidx1 < 3 * GRID3_)]; + count_idx = pidx1.shape[0]; + + axis = th.zeros((count_idx, 3), dtype = th.float64, device = device); + axis[(0 * GRID3_ <= pidx1) & (pidx1 < 1 * GRID3_), 2] = 1.0; + axis[(1 * GRID3_ <= pidx1) & (pidx1 < 2 * GRID3_), 1] = 1.0; + axis[(2 * GRID3_ <= pidx1) & (pidx1 < 3 * GRID3_), 0] = 1.0; + + pidx3 = pidx1_to_pidx3(pidx1); + val_ = occs[pidx3[:, 0], pidx3[:, 1], pidx3[:, 2]]; + + val_pnts[start_idx: start_idx + count_idx] = _1d_search(pidx3, axis, val_); + start_idx += count_idx; + + + def _2d_linbin(pnts, spnt, locc, axis, srch_range, lin_step, bin_step): + P = pnts.shape[0]; + tstep = srch_range / lin_step; + lidx = th.full((P,), lin_step, dtype = th.int64, device = device); + locc = locc.reshape(-1); + + for lin_i in range(lin_step): + cor_ = pnts + (spnt + axis * ((lin_step - lin_i) * tstep)) / GRID; + occv = get_occv(cor_); + lidx = th.where(occv != locc, lin_step - lin_i - 1, lidx); + + left = spnt + axis * (lidx.unsqueeze(1) * tstep); + rght = left + axis * tstep; + locc = locc.unsqueeze(1); + + for bin_i in range(bin_step): + mid = (left + rght) * 0.5; + occv = get_occv(pnts + mid / GRID).unsqueeze(1); + + jdge = locc == occv; + left = th.where(jdge, mid, left); + rght = th.where(jdge, rght, mid); + + return left; + + def _2d_search(pidx3, axdir, axis0, axis1, val_, cases): + P = pidx3.shape[0]; + arng = th.arange(P, dtype = th.int64, device = device); + pnts = pidx3_to_pnts(pidx3); + axis0, axis1 = axis0.to(th.float64), axis1.to(th.float64); + + cidx3 = pidx3.clone(); + lcres = self.lctab_2d[val_, cases]; + cidx3[arng, axdir[:, 0]] += lcres[:, 0]; + cidx3[arng, axdir[:, 1]] += lcres[:, 1]; + edir = axdir[arng, lcres[:, 2]]; + cidx1 = (2 - edir) * GRID3_ + cidx3[:, 0] * GRID2_ + cidx3[:, 1] * GRID_ + cidx3[:, 2]; + linv = val_pnts[get_idx(cidx1)]; + lcord = th.where(lcres[:, 2:] == 0, linv, axis0 * lcres[:, 0: 1]) + \ + th.where(lcres[:, 2:] == 1, linv, axis1 * lcres[:, 1: 2]); + + chk0 = cidx3.clone(); + occ0 = occs[chk0[:, 0], chk0[:, 1], chk0[:, 2]].unsqueeze(1); + chk0 = (chk0 - pidx3).to(th.float64); + + chk1 = cidx3.clone(); chk1[arng, edir] += 1; + occ1 = occs[chk1[:, 0], chk1[:, 1], chk1[:, 2]].unsqueeze(1); + chk1 = (chk1 - pidx3).to(th.float64); + + cidx3 = pidx3.clone(); + lcres = self.lctab_2d[val_, cases + 1]; + cidx3[arng, axdir[:, 0]] += lcres[:, 0]; + cidx3[arng, axdir[:, 1]] += lcres[:, 1]; + edir = axdir[arng, lcres[:, 2]]; + cidx1 = (2 - edir) * GRID3_ + cidx3[:, 0] * GRID2_ + cidx3[:, 1] * GRID_ + cidx3[:, 2]; + linv = val_pnts[get_idx(cidx1)]; + rcord = th.where(lcres[:, 2:] == 0, linv, axis0 * lcres[:, 0: 1]) + \ + th.where(lcres[:, 2:] == 1, linv, axis1 * lcres[:, 1: 2]); + + chk2 = cidx3.clone(); + occ2 = occs[chk2[:, 0], chk2[:, 1], chk2[:, 2]].unsqueeze(1); + chk2 = (chk2 - pidx3).to(th.float64); + + chk3 = cidx3.clone(); chk3[arng, edir] += 1; + occ3 = occs[chk3[:, 0], chk3[:, 1], chk3[:, 2]].unsqueeze(1); + chk3 = (chk3 - pidx3).to(th.float64); + + spnt = (lcord + rcord) * 0.5; + locc = get_occv(pnts + spnt / GRID).unsqueeze(1); + + hnorm = lcord - rcord; + leng = th.norm(hnorm, dim = 1, keepdim = True); + cndt = leng < ERR; + leng[cndt] = 1.0; + hnorm = hnorm / leng; + vnorm = th.cross(th.cross(axis0, axis1, dim = 1), hnorm, dim = 1); + + tmp = th.sum((chk0 - spnt) * vnorm, dim = 1, keepdim = True); + vnorm = th.where((th.abs(tmp) > ERR) & th.bitwise_xor(tmp > 0, locc != occ0), -vnorm, vnorm); + + tmp = th.sum((chk1 - spnt) * vnorm, dim = 1, keepdim = True); + vnorm = th.where((th.abs(tmp) > ERR) & th.bitwise_xor(tmp > 0, locc != occ1), -vnorm, vnorm); + + tmp = th.sum((chk2 - spnt) * vnorm, dim = 1, keepdim = True); + vnorm = th.where((th.abs(tmp) > ERR) & th.bitwise_xor(tmp > 0, locc != occ2), -vnorm, vnorm); + + tmp = th.sum((chk3 - spnt) * vnorm, dim = 1, keepdim = True); + vnorm = th.where((th.abs(tmp) > ERR) & th.bitwise_xor(tmp > 0, locc != occ3), -vnorm, vnorm); + + left = _2d_linbin(pnts, spnt, locc, vnorm, + VERTICAL_RANGE, VERTICAL_LINEAR_SEARCH, VERTICAL_BINARY_SEARCH); + vl = th.sum((left - spnt) * vnorm, dim = 1, keepdim = True); + cndt |= th.abs(vl) < ERR; + + lleft = _2d_linbin(pnts, left, locc, hnorm, + HORIZNTL_RANGE, HORIZNTL_LINEAR_SEARCH, HORIZNTL_BINARY_SEARCH) - lcord; + lh = th.sum(lleft * hnorm, dim = 1, keepdim = True); + rleft = _2d_linbin(pnts, left, locc, -hnorm, + HORIZNTL_RANGE, HORIZNTL_LINEAR_SEARCH, HORIZNTL_BINARY_SEARCH) - rcord; + rh = th.sum(rleft * hnorm, dim = 1, keepdim = True); + + denm = rh - lh; + cndt |= th.abs(denm) < ERR; + rslt = ((0.5 * (rh + lh)) * hnorm + vl * vnorm) * (leng / denm); + + cndt |= th.sum(rslt * vnorm, dim = 1, keepdim = True) < 0; + rslt = th.where(cndt, th.zeros_like(rslt), rslt); + return spnt + rslt; + + + pidx1 = sel_pidx1[(3 * GRID3_ <= sel_pidx1) & (sel_pidx1 < 9 * GRID3_)]; + count_idx = pidx1.shape[0]; + + csidx = th.div(pidx1, GRID3_, rounding_mode = 'floor') - 3; + axdir = self.axd_tab[csidx]; + axis0 = self.ax0_tab[csidx]; + axis1 = self.ax1_tab[csidx]; + cases = self.cas_tab[csidx]; + axis2 = axis0 + axis1; + + pidx3 = pidx1_to_pidx3(pidx1); + val_ = occs[pidx3[:, 0] , pidx3[:, 1] , pidx3[:, 2] ] + \ + 2 * occs[pidx3[:, 0] + axis1[:, 0], pidx3[:, 1] + axis1[:, 1], pidx3[:, 2] + axis1[:, 2]] + \ + 4 * occs[pidx3[:, 0] + axis0[:, 0], pidx3[:, 1] + axis0[:, 1], pidx3[:, 2] + axis0[:, 2]] + \ + 8 * occs[pidx3[:, 0] + axis2[:, 0], pidx3[:, 1] + axis2[:, 1], pidx3[:, 2] + axis2[:, 2]]; + tmp = amb_chks[pidx3[:, 0], pidx3[:, 1], pidx3[:, 2]]; + val_[tmp] = 15 - val_[tmp]; + + val_pnts[start_idx: start_idx + count_idx] = _2d_search(pidx3, axdir, axis0, axis1, val_, cases); + start_idx += count_idx; + + + def _solve_QEF(pidx3, val_, cases): + out = th.zeros((pidx3.shape[0], 3), dtype = th.float64, device = device); + + for i in range(3, 8): + idx = th.argwhere(self.qvntab[val_, cases] == i).reshape(-1); + pid3_ = pidx3[idx]; val0_ = val_[idx]; cas = cases[idx]; + vtcs = self.pfvtab[val0_, cas, :2 * i + 1]; + vitg = self.vitogi[vtcs]; + + pnti = vitg[:, :, 3] * GRID3_ + \ + (pid3_[:, 0:1] + vitg[:, :, 0]) * GRID2_ + \ + (pid3_[:, 1:2] + vitg[:, :, 1]) * GRID_ + \ + (pid3_[:, 2:3] + vitg[:, :, 2]); + + pidx = get_idx(pnti); + vert = val_pnts[pidx]; + vert += vitg[:, :, :3]; + + _1vt = vert[:, 1::2]; + qnrm = th.cross(_1vt - vert[:, :-1:2], _1vt - vert[:, 2::2], dim = 2); + cntr = th.mean(_1vt, dim = 1); + + anrm = th.norm(qnrm, dim = 2, keepdim = True); + cndt = anrm < ERR; + qnrm /= th.where(cndt, th.ones_like(anrm), anrm); + cndt = th.sum(cndt, dim = 1) == cndt.shape[1]; + + eye_ = th.eye(3, dtype = th.float64, device = device).view(1, 3, 3).repeat(cntr.shape[0], 1, 1); + qefr = th.linalg.lstsq(th.cat((qnrm, QEF_REG * eye_), dim = 1), + th.cat((th.sum(_1vt * qnrm, dim = 2, keepdim = True), + QEF_REG * cntr.unsqueeze(2)), dim = 1)).solution.reshape(-1, 3); + qefr = th.where(cndt, cntr, qefr); + + cpnt = self.cpnttab[val0_, cas]; + ctoq = qefr - cpnt; + leng = th.norm(ctoq, dim = 1, keepdim = True); + + cscd = self.cstrtab[val0_, cas]; + cvtx = cscd[:, :, 0]; + cnml = th.cross(cscd[:, :, 2] - cvtx, cscd[:, :, 1] - cvtx, dim = 2); + t = th.sum(cnml * (cvtx - cpnt.unsqueeze(1)), dim = 2) / th.sum(cnml * ctoq.unsqueeze(1), dim = 2); + + t = th.cat(((1.0 - cpnt) / ctoq, -cpnt / ctoq, t), dim = 1) * leng; + cndt = (t < 0) | th.isnan(t); + t[cndt] = leng.repeat(1, 8)[cndt] + SEP; + t = (t - SEP) / th.where(leng < ERR, th.ones_like(leng), leng); + t = th.min(th.clamp(t, 0.0, 1.0), dim = 1, keepdim = True)[0]; + + out[idx] = cpnt + t * ctoq; + + return out; + + pidx1 = sel_pidx1[9 * GRID3_ <= sel_pidx1]; + count_idx = pidx1.shape[0]; + pidx3 = pidx1_to_pidx3(pidx1); + val_ = vals[pidx3[:, 0], pidx3[:, 1], pidx3[:, 2]].to(th.int64); + cases = th.div(pidx1, GRID3_, rounding_mode = 'floor') - 9; + + val_pnts[start_idx: start_idx + count_idx] = _solve_QEF(pidx3, val_, cases); + start_idx += count_idx; + + def _get_face(): + faces = []; + + cndts, vidcs, vpnts = [], [], []; + evofs = []; + idcs = th.tensor([[[12], [8], [6], [1]], + [[11], [4], [9], [2]], + [[10], [7], [5], [3]]], dtype = th.int64, device = device); + ofst = th.tensor([[[0], [GRID_ ], [GRID2_], [GRID2_ + GRID_]], + [[0], [GRID2_], [1 ], [GRID2_ + 1 ]], + [[0], [1 ], [GRID_ ], [1 + GRID_]]], + dtype = th.int64, device = device); + vofs = th.tensor([[[0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 0], [1, 1, 0]], + [[0, 0, 0], [1, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 1]], + [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [0, 1, 1]]], + dtype = th.int64, device = device).unsqueeze(2); + + z, y, x = th.argwhere(th.bitwise_xor(occs[1:-1, 1:-1, :-1], occs[1:-1, 1:-1, 1:])).T; + val_ = vals[z.unsqueeze(0) + vofs[0, :4, 0, 0:1], + y.unsqueeze(0) + vofs[0, :4, 0, 1:2], + x.unsqueeze(0) + vofs[0, :4, 0, 2:3]].to(th.int64); + fac_idx = (z * GRID2_ + y * GRID_ + x).unsqueeze(0); + vidx3 = self.vbitab[val_, idcs[0] - 1] * GRID3_ + fac_idx + ofst[0]; + gvidx = get_idx(th.cat((vidx3, fac_idx + (GRID2_ + GRID_)), dim = 0)); + gvpnt = val_pnts[gvidx] + vofs[0].to(th.float64); + + cndts.append(occs[z + 1, y + 1, x + 1]); + vidcs.append(gvidx.clone()); + vpnts.append(gvpnt.clone()); + evofs.append(vofs[0, 4].repeat(x.shape[0], 1)); + + z, y, x = th.argwhere(th.bitwise_xor(occs[1:-1, :-1, 1:-1], occs[1:-1, 1:, 1:-1])).T; + val_ = vals[z.unsqueeze(0) + vofs[1, :4, 0, 0:1], + y.unsqueeze(0) + vofs[1, :4, 0, 1:2], + x.unsqueeze(0) + vofs[1, :4, 0, 2:3]].to(th.int64); + fac_idx = (z * GRID2_ + y * GRID_ + x).unsqueeze(0); + vidx3 = self.vbitab[val_, idcs[1] - 1] * GRID3_ + fac_idx + ofst[1]; + gvidx = get_idx(th.cat((vidx3, fac_idx + (GRID3_ + GRID2_ + 1)), dim = 0)); + gvpnt = val_pnts[gvidx] + vofs[1].to(th.float64); + + cndts.append(occs[z + 1, y + 1, x + 1]); + vidcs.append(gvidx.clone()); + vpnts.append(gvpnt.clone()); + evofs.append(vofs[1, 4].repeat(x.shape[0], 1)); + + z, y, x = th.argwhere(th.bitwise_xor(occs[:-1, 1:-1, 1:-1], occs[1:, 1:-1, 1:-1])).T; + val_ = vals[z.unsqueeze(0) + vofs[2, :4, 0, 0:1], + y.unsqueeze(0) + vofs[2, :4, 0, 1:2], + x.unsqueeze(0) + vofs[2, :4, 0, 2:3]].to(th.int64); + fac_idx = (z * GRID2_ + y * GRID_ + x).unsqueeze(0); + vidx3 = self.vbitab[val_, idcs[2] - 1] * GRID3_ + fac_idx + ofst[2]; + gvidx = get_idx(th.cat((vidx3, fac_idx + (2 * GRID3_ + 1 + GRID_)), dim = 0)); + gvpnt = val_pnts[gvidx] + vofs[2].to(th.float64); + + cndts.append(occs[z + 1, y + 1, x + 1]); + vidcs.append(gvidx.clone()); + vpnts.append(gvpnt.clone()); + evofs.append(vofs[2, 4].repeat(x.shape[0], 1)); + + + cndts, vidcs, vpnts = th.cat(cndts, dim = 0), th.cat(vidcs, dim = 1), th.cat(vpnts, dim = 1); + evofs = th.cat(evofs, dim = 0).to(th.float64).unsqueeze(0); + + idcs = th.tensor([0, 1, 3, 2, 0, 1], dtype = th.int64, device = device); + pnts1 = vpnts[idcs[0: 4]]; pnts2 = vpnts[idcs[1: 5]]; pnts3 = vpnts[idcs[2: 6]]; + sitst = th.sum(th.cross(pnts1 - evofs, pnts3 - evofs, dim = 2) * (pnts2 - evofs), dim = 2) < 0; + sitst |= th.sum(th.cross(pnts1 - 1.0, pnts3 - 1.0, dim = 2) * (pnts2 - 1.0), dim = 2) > 0; + + sit12 = sitst[0] | sitst[2]; + sit03 = sitst[1] | sitst[3]; + sitsp = sit12 & sit03; + sitnp = ~sitsp; + + idcs0, idcs1, idcs2, idcs3, eidcs = vidcs[:, sitsp]; + faces = th.cat((th.stack((idcs1, idcs0, eidcs), dim = 1), + th.stack((idcs3, idcs1, eidcs), dim = 1), + th.stack((idcs2, idcs3, eidcs), dim = 1), + th.stack((idcs0, idcs2, eidcs), dim = 1)), dim = 0); + + idcs0, idcs1, idcs2, idcs3, _ = vidcs[:, sitnp]; + pnts0, pnts1, pnts2, pnts3, epnts = vpnts[:, sitnp]; + sit12, sit03 = sit12[sitnp], sit03[sitnp]; + + v03 = pnts3 - pnts0; + leng = th.norm(v03, dim = 1); + cnn03 = leng < ERR; + v03 /= th.where(cnn03, th.ones_like(leng), leng).unsqueeze(1); + + ev0s = epnts - pnts0; + t_ = th.sum(ev0s * v03, dim = 1); + vp03 = pnts0 + t_.unsqueeze(1) * v03; + + n1 = th.cross(v03, pnts1 - pnts0, dim = 1); + ln1 = th.norm(n1, dim = 1); + bnn03 = ln1 < ERR; + t_ = th.sum(ev0s * n1, dim = 1) / th.clamp(ln1, 1e-5) ** 2; + vp1 = epnts - t_.unsqueeze(1) * n1; + cndt = th.sum(th.cross(v03, vp1 - pnts0, dim = 1) * n1, dim = 1) > 0; + vp1 = th.where(cndt.unsqueeze(1), vp1, vp03); + + n2 = th.cross(v03, pnts2 - pnts0, dim = 1); + ln2 = th.norm(n2, dim = 1); + bnn03 |= ln2 < ERR; + t_ = th.sum(ev0s * n2, dim = 1) / th.clamp(ln2, 1e-5) ** 2; + vp2 = epnts - t_.unsqueeze(1) * n2; + cndt = th.sum(th.cross(v03, vp2 - pnts0, dim = 1) * n2, dim = 1) > 0; + vp2 = th.where(cndt.unsqueeze(1), vp2, vp03); + + lf03 = th.minimum(th.norm(vp1 - epnts, dim = 1), th.norm(vp2 - epnts, dim = 1)); + + v12 = pnts2 - pnts1; + leng = th.norm(v12, dim = 1); + cnn12 = leng < ERR; + v12 /= th.where(cnn12, th.ones_like(leng), leng).unsqueeze(1); + + ev1s = epnts - pnts1; + t_ = th.sum(ev1s * v12, dim = 1); + vp12 = pnts1 + t_.unsqueeze(1) * v12; + + n0 = th.cross(v12, pnts0 - pnts1, dim = 1); + ln0 = th.norm(n0, dim = 1); + t_ = th.sum(ev1s * n0, dim = 1) / th.clamp(ln0, 1e-5) ** 2; + vp0 = epnts - t_.unsqueeze(1) * n0; + cndt = th.sum(th.cross(v12, vp0 - pnts1, dim = 1) * n0, dim = 1) > 0; + vp0 = th.where(cndt.unsqueeze(1), vp0, vp12); + + n3 = th.cross(v12, pnts3 - pnts1, dim = 1); + ln3 = th.norm(n3, dim = 1); + t_ = th.sum(ev1s * n3, dim = 1) / th.clamp(ln3, 1e-5) ** 2; + vp3 = epnts - t_.unsqueeze(1) * n3; + cndt = th.sum(th.cross(v12, vp3 - pnts1, dim = 1) * n3, dim = 1) > 0; + vp3 = th.where(cndt.unsqueeze(1), vp3, vp12); + + lf12 = th.minimum(th.norm(vp0 - epnts, dim = 1), th.norm(vp3 - epnts, dim = 1)); + + cnn12 |= (lf12 < lf03) | bnn03 | sit12; + cnn12 &= ~(cnn03 | sit03); + + faces = th.cat((th.where(cnn12.unsqueeze(1), + th.stack((idcs0, idcs2, idcs1), dim = 1), + th.stack((idcs0, idcs3, idcs1), dim = 1)), + th.where(cnn12.unsqueeze(1), + th.stack((idcs1, idcs2, idcs3), dim = 1), + th.stack((idcs0, idcs2, idcs3), dim = 1)), + faces), dim = 0); + + cndtn, cndts = cndts[sitnp], cndts[sitsp]; + cndts = th.cat((cndtn, cndtn, cndts, cndts, cndts, cndts)); + ftmp = faces[cndts, 1:3].clone(); + faces[cndts, 1] = ftmp[:, 1]; faces[cndts, 2] = ftmp[:, 0]; + + return th.unique(faces, return_inverse = True); + + vidcs, faces = _get_face(); + + pidx1 = sel_pidx1[vidcs]; + pnts = pidx3_to_pnts(pidx1_to_pidx3(pidx1)) + val_pnts[vidcs] / GRID; + verts = min_coordc + pnts * siz_coordc; + + return verts, faces; +