feat: OOOOOOOOOOOOOOOOOOO
This commit is contained in:
parent
1b40190198
commit
7318207dbd
377
main.py
377
main.py
|
@ -7,32 +7,63 @@ import argparse
|
||||||
from rich.progress import Progress
|
from rich.progress import Progress
|
||||||
|
|
||||||
|
|
||||||
def cot(x: float):
|
def cot(x: float) -> float:
|
||||||
|
"""Cotangeante of x
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x (float): angle
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: cotangeante of x
|
||||||
|
"""
|
||||||
sin_x = np.sin(x)
|
sin_x = np.sin(x)
|
||||||
if sin_x == 0:
|
if sin_x == 0:
|
||||||
return 1e16
|
return 1e16
|
||||||
return np.cos(x) / sin_x
|
return np.cos(x) / sin_x
|
||||||
|
|
||||||
|
|
||||||
def sliding_window(l: list, n: int = 2):
|
def sliding_window(l: list, n: int = 2) -> list[tuple]:
|
||||||
|
"""Create a sliding window of size n
|
||||||
|
|
||||||
|
Args:
|
||||||
|
l (list): list to create the sliding window from
|
||||||
|
n (int, optional): number of value. Defaults to 2.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[tuple]: sliding window
|
||||||
|
"""
|
||||||
k = n - 1
|
k = n - 1
|
||||||
l2 = l + [l[i] for i in range(k)]
|
l2 = l + [l[i] for i in range(k)]
|
||||||
res = [(x for x in l2[i:i+n]) for i in range(len(l2)-k)]
|
res = [(x for x in l2[i:i+n]) for i in range(len(l2)-k)]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class Edge:
|
class Edge:
|
||||||
def __init__(self, a, b):
|
"""Edge representation"""
|
||||||
|
def __init__(self, a: int, b: int) -> None:
|
||||||
|
"""Create an edge
|
||||||
|
|
||||||
|
Args:
|
||||||
|
a (int): first vertex
|
||||||
|
b (int): second vertex
|
||||||
|
"""
|
||||||
self.a = min(a, b)
|
self.a = min(a, b)
|
||||||
self.b = max(a, b)
|
self.b = max(a, b)
|
||||||
|
|
||||||
self.face1 = None
|
self.face1: Face | None = None
|
||||||
self.face2 = None
|
self.face2: Face | None = None
|
||||||
|
|
||||||
self.fold = 0.0
|
self.fold: float = 0.0
|
||||||
self.curvature = 0.0
|
self.curvature: float = 0.0
|
||||||
|
|
||||||
def __eq__(self, __o: object) -> bool:
|
def __eq__(self, __o: object) -> bool:
|
||||||
|
"""Check if two edges are equal
|
||||||
|
|
||||||
|
Args:
|
||||||
|
__o (object): other edge
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if equal, False otherwise
|
||||||
|
"""
|
||||||
if isinstance(__o, Edge):
|
if isinstance(__o, Edge):
|
||||||
return self.a == __o.a and self.b == __o.b
|
return self.a == __o.a and self.b == __o.b
|
||||||
|
|
||||||
|
@ -40,17 +71,43 @@ class Edge:
|
||||||
|
|
||||||
|
|
||||||
class Face:
|
class Face:
|
||||||
def __init__(self, a, b, c):
|
"""Face representation"""
|
||||||
|
def __init__(self, a: int, b: int, c: int) -> None:
|
||||||
|
"""Face constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
a (int): first vertex index
|
||||||
|
b (int): second vertex index
|
||||||
|
c (int): third vertex index
|
||||||
|
"""
|
||||||
|
self.a = a
|
||||||
|
self.b = b
|
||||||
|
self.c = c
|
||||||
|
|
||||||
|
self.edges: list[Edge] = []
|
||||||
self.a = a
|
self.a = a
|
||||||
self.b = b
|
self.b = b
|
||||||
self.c = c
|
self.c = c
|
||||||
|
|
||||||
self.normal = np.zeros(3)
|
self.normal = np.zeros(3)
|
||||||
|
|
||||||
def to_obja(self):
|
def to_obja(self) -> obja.Face:
|
||||||
|
"""Convert face to obja format
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
obja.Face: face in obja format
|
||||||
|
"""
|
||||||
return obja.Face(self.a, self.b, self.c)
|
return obja.Face(self.a, self.b, self.c)
|
||||||
|
|
||||||
def __eq__(self, __o: object) -> bool:
|
def __eq__(self, __o: object) -> bool:
|
||||||
|
"""Check if two faces are equal
|
||||||
|
|
||||||
|
Args:
|
||||||
|
__o (object): other face
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if equal, False otherwise
|
||||||
|
"""
|
||||||
if isinstance(__o, Face):
|
if isinstance(__o, Face):
|
||||||
return set((__o.a, __o.b, __o.c)) == set((self.a, self.b, self.c))
|
return set((__o.a, __o.b, __o.c)) == set((self.a, self.b, self.c))
|
||||||
|
|
||||||
|
@ -58,138 +115,161 @@ class Face:
|
||||||
|
|
||||||
|
|
||||||
class Vertex:
|
class Vertex:
|
||||||
def __init__(self, pos):
|
"""Vertex representation"""
|
||||||
|
def __init__(self, pos: np.ndarray[int, float]) -> None:
|
||||||
|
"""Vertex constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pos (np.ndarray[int, float]): position of the vertex
|
||||||
|
"""
|
||||||
self.pos = pos
|
self.pos = pos
|
||||||
|
|
||||||
self.vertex_ring = []
|
self.vertex_ring: list[int] = []
|
||||||
self.face_ring = []
|
self.face_ring: list[int] = []
|
||||||
|
|
||||||
self.normal = np.zeros(3)
|
self.normal: np.ndarray = np.zeros(3)
|
||||||
|
|
||||||
self.area = 0.0
|
self.area: float = 0.0
|
||||||
self.curvature = 0.0
|
self.curvature: float = 0.0
|
||||||
|
|
||||||
def to_obja(self):
|
def to_obja(self) -> np.ndarray:
|
||||||
|
"""Convert vertex to obja format
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
np.ndarray: vertex in obja format
|
||||||
|
"""
|
||||||
return self.pos
|
return self.pos
|
||||||
|
|
||||||
|
|
||||||
class MAPS(obja.Model):
|
class MAPS(obja.Model):
|
||||||
"""_summary_
|
"""MAPS compression model"""
|
||||||
|
|
||||||
Args:
|
|
||||||
obja (_type_): _description_
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""MAPS constructor"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def parse_file(self, path):
|
def parse_file(self, path: str) -> None:
|
||||||
|
"""Parse a file
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (str): path to the file
|
||||||
|
"""
|
||||||
super().parse_file(path)
|
super().parse_file(path)
|
||||||
for i, vertex in enumerate(self.vertices):
|
for i, vertex in enumerate(self.vertices):
|
||||||
self.vertices[i] = Vertex(vertex)
|
self.vertices[i] = Vertex(vertex)
|
||||||
for i, face in enumerate(self.faces):
|
for i, face in enumerate(self.faces):
|
||||||
self.faces[i] = Face(face.a, face.b, face.c)
|
self.faces[i] = Face(face.a, face.b, face.c)
|
||||||
|
|
||||||
def update(self):
|
def update(self) -> None:
|
||||||
|
"""Precompute things"""
|
||||||
|
# Reset progress bars
|
||||||
self.progress.reset(self.select_task, total=len(self.vertices))
|
self.progress.reset(self.select_task, total=len(self.vertices))
|
||||||
self.progress.reset(self.compress_task)
|
self.progress.reset(self.compress_task)
|
||||||
|
|
||||||
|
# Compute usefull things
|
||||||
self.update_rings()
|
self.update_rings()
|
||||||
self.update_edges()
|
self.update_edges()
|
||||||
self.update_normals()
|
self.update_normals()
|
||||||
self.update_area_curvature()
|
self.update_area_curvature()
|
||||||
|
|
||||||
def fix(self):
|
def fix(self) -> None:
|
||||||
|
"""Fix the model"""
|
||||||
|
|
||||||
fixed = True
|
fixed = True
|
||||||
|
|
||||||
|
# Find a vertex with less than 3 faces
|
||||||
for i, vertex in enumerate(self.vertices):
|
for i, vertex in enumerate(self.vertices):
|
||||||
if vertex is None:
|
if vertex is None:
|
||||||
continue
|
continue
|
||||||
if len(vertex.face_ring) < 3:
|
|
||||||
for face in vertex.face_ring:
|
|
||||||
self.faces[face] = None
|
|
||||||
self.vertices[i] = None
|
|
||||||
fixed = False
|
|
||||||
return fixed
|
|
||||||
|
|
||||||
|
|
||||||
def update_edges(self):
|
if len(vertex.face_ring) < 3:
|
||||||
|
# Remove the vertex and its faces
|
||||||
|
for face in vertex.face_ring:
|
||||||
|
if not self.final_only:
|
||||||
|
self.operations.append(
|
||||||
|
('af', face, self.faces[face].to_obja()))
|
||||||
|
self.faces[face] = None
|
||||||
|
if not self.final_only:
|
||||||
|
self.operations.append(
|
||||||
|
('av', i, vertex.to_obja()))
|
||||||
|
self.vertices[i] = None
|
||||||
|
|
||||||
|
# Indicate that the model has to be fixed again
|
||||||
|
fixed = False
|
||||||
|
|
||||||
|
return fixed
|
||||||
|
|
||||||
|
def update_edges(self) -> None:
|
||||||
|
"""Update edges"""
|
||||||
|
|
||||||
self.edges = {}
|
self.edges = {}
|
||||||
|
|
||||||
for face in self.faces:
|
for face in self.faces:
|
||||||
if face is None:
|
if face is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Create all edges for the face
|
||||||
for a, b in sliding_window([face.a, face.b, face.c], n=2):
|
for a, b in sliding_window([face.a, face.b, face.c], n=2):
|
||||||
new_edge = Edge(a, b)
|
new_edge = Edge(a, b)
|
||||||
if f"{new_edge.a}:{new_edge.b}" not in self.edges.keys():
|
|
||||||
new_edge.face1 = face
|
|
||||||
|
|
||||||
for face2_i in self.vertices[new_edge.a].face_ring:
|
# Check if the edge already exists
|
||||||
face2 = self.faces[face2_i]
|
if f"{new_edge.a}:{new_edge.b}" in self.edges.keys():
|
||||||
if face2 == face:
|
continue
|
||||||
continue
|
|
||||||
|
|
||||||
face2_vertices = (face2.a, face2.b, face2.c)
|
new_edge.face1 = face
|
||||||
if not (a in face2_vertices and b in face2_vertices):
|
|
||||||
continue
|
|
||||||
|
|
||||||
new_edge.face2 = face2
|
# Find the opoosite face
|
||||||
break
|
for face2_i in self.vertices[new_edge.a].face_ring:
|
||||||
|
face2 = self.faces[face2_i]
|
||||||
self.edges[f"{new_edge.a}:{new_edge.b}"] = new_edge
|
if face2 == face:
|
||||||
|
|
||||||
def update_rings(self):
|
|
||||||
try:
|
|
||||||
fixed = False
|
|
||||||
while not fixed:
|
|
||||||
for vertex in self.vertices:
|
|
||||||
if vertex is None:
|
|
||||||
continue
|
continue
|
||||||
vertex.face_ring = []
|
|
||||||
|
|
||||||
for i, face in enumerate(self.faces):
|
face2_vertices = (face2.a, face2.b, face2.c)
|
||||||
if face is None:
|
if not (a in face2_vertices and b in face2_vertices):
|
||||||
continue
|
continue
|
||||||
for vertex_i in (face.a, face.b, face.c):
|
|
||||||
self.vertices[vertex_i].face_ring.append(i)
|
|
||||||
|
|
||||||
fixed = self.fix()
|
|
||||||
|
|
||||||
for i, vertex in enumerate(self.vertices):
|
new_edge.face2 = face2
|
||||||
vertex = self.vertices[i]
|
break
|
||||||
|
|
||||||
|
# Add the new edge to the list
|
||||||
|
self.edges[f"{new_edge.a}:{new_edge.b}"] = new_edge
|
||||||
|
|
||||||
|
def update_rings(self) -> None:
|
||||||
|
"""Update vertex and face rings"""
|
||||||
|
|
||||||
|
fixed = False
|
||||||
|
|
||||||
|
# Wait till the model is fixed
|
||||||
|
while not fixed:
|
||||||
|
for vertex in self.vertices:
|
||||||
|
# Reset vertex ring
|
||||||
if vertex is None:
|
if vertex is None:
|
||||||
continue
|
continue
|
||||||
if len(vertex.face_ring) == 0:
|
vertex.face_ring = []
|
||||||
self.vertices[i] = None
|
|
||||||
continue
|
|
||||||
ring = self.one_ring(i)
|
|
||||||
vertex.vertex_ring = ring
|
|
||||||
except ValueError:
|
|
||||||
self.update_rings()
|
|
||||||
|
|
||||||
|
|
||||||
def fail(self, index):
|
# Add faces to vertex ring
|
||||||
print('fail')
|
for i, face in enumerate(self.faces):
|
||||||
output_file = open('obja/example/fail.obja', 'w')
|
if face is None:
|
||||||
output = obja.Output(output_file)
|
|
||||||
used = []
|
|
||||||
for i, x in enumerate(self.vertices[index].face_ring):
|
|
||||||
face = self.faces[x]
|
|
||||||
for y in (face.a, face.b, face.c):
|
|
||||||
if y in used:
|
|
||||||
continue
|
continue
|
||||||
output.add_vertex(y, self.vertices[y].to_obja())
|
for vertex_i in (face.a, face.b, face.c):
|
||||||
used.append(y)
|
self.vertices[vertex_i].face_ring.append(i)
|
||||||
output.add_face(x, face.to_obja())
|
|
||||||
print('fc {} {} {} {}'.format(
|
|
||||||
i + 1,
|
|
||||||
np.random.rand(),
|
|
||||||
np.random.rand(),
|
|
||||||
np.random.rand()),
|
|
||||||
file=output_file
|
|
||||||
)
|
|
||||||
print(x, (face.a, face.b, face.c))
|
|
||||||
|
|
||||||
def update_area_curvature(self):
|
# Fix the model
|
||||||
|
fixed = self.fix()
|
||||||
|
|
||||||
|
for i, vertex in enumerate(self.vertices):
|
||||||
|
vertex = self.vertices[i]
|
||||||
|
if vertex is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Compute rings
|
||||||
|
ring = self.one_ring(i)
|
||||||
|
vertex.vertex_ring = ring
|
||||||
|
|
||||||
|
def update_area_curvature(self) -> None:
|
||||||
|
"""Update area and curvature"""
|
||||||
|
|
||||||
|
# Get the area and curvature of each vertex
|
||||||
for i, vertex in enumerate(self.vertices):
|
for i, vertex in enumerate(self.vertices):
|
||||||
if vertex is None:
|
if vertex is None:
|
||||||
continue
|
continue
|
||||||
|
@ -198,34 +278,39 @@ class MAPS(obja.Model):
|
||||||
vertex.area = area
|
vertex.area = area
|
||||||
vertex.curvature = curvature
|
vertex.curvature = curvature
|
||||||
|
|
||||||
|
# Find feature edges
|
||||||
self.feature_edges = []
|
self.feature_edges = []
|
||||||
for edge in self.edges.values():
|
for edge in self.edges.values():
|
||||||
if edge.face2 is None:
|
|
||||||
self.fail(edge.b)
|
|
||||||
edge.fold = np.dot(edge.face1.normal, edge.face2.normal)
|
edge.fold = np.dot(edge.face1.normal, edge.face2.normal)
|
||||||
|
|
||||||
if edge.fold < 0.5:
|
if edge.fold < 0.5:
|
||||||
self.feature_edges.append(edge)
|
self.feature_edges.append(edge)
|
||||||
|
|
||||||
def update_normals(self):
|
def update_normals(self) -> None:
|
||||||
|
"""Update normals"""
|
||||||
for face in self.faces:
|
for face in self.faces:
|
||||||
if face is None:
|
if face is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Compute face normal
|
||||||
p1 = self.vertices[face.a].pos
|
p1 = self.vertices[face.a].pos
|
||||||
p2 = self.vertices[face.b].pos
|
p2 = self.vertices[face.b].pos
|
||||||
p3 = self.vertices[face.c].pos
|
p3 = self.vertices[face.c].pos
|
||||||
u = p2 - p1
|
u = p2 - p1
|
||||||
v = p3 - p1
|
v = p3 - p1
|
||||||
n = np.cross(u, v)
|
n = np.cross(u, v)
|
||||||
n /= np.linalg.norm(n)
|
norm = np.linalg.norm(n)
|
||||||
|
if norm != 0:
|
||||||
|
n /= np.linalg.norm(n)
|
||||||
|
|
||||||
face.normal = n
|
face.normal = n
|
||||||
|
|
||||||
|
# Sum vertex normal
|
||||||
self.vertices[face.a].normal += n
|
self.vertices[face.a].normal += n
|
||||||
self.vertices[face.b].normal += n
|
self.vertices[face.b].normal += n
|
||||||
self.vertices[face.c].normal += n
|
self.vertices[face.c].normal += n
|
||||||
|
|
||||||
|
# Normalize vertex normals
|
||||||
for vertex in self.vertices:
|
for vertex in self.vertices:
|
||||||
if vertex is None:
|
if vertex is None:
|
||||||
continue
|
continue
|
||||||
|
@ -235,7 +320,7 @@ class MAPS(obja.Model):
|
||||||
vertex.normal /= norm
|
vertex.normal /= norm
|
||||||
|
|
||||||
def one_ring(self, index: int) -> list[int]:
|
def one_ring(self, index: int) -> list[int]:
|
||||||
""" Return the corresponding 1-ring
|
"""Return the corresponding 1-ring
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
index (int): index of the 1-ring's main vertex
|
index (int): index of the 1-ring's main vertex
|
||||||
|
@ -271,40 +356,12 @@ class MAPS(obja.Model):
|
||||||
break
|
break
|
||||||
|
|
||||||
if not broke:
|
if not broke:
|
||||||
self.fail(index)
|
raise Exception('Ring not corrupted')
|
||||||
|
|
||||||
for i, face_i in enumerate(self.vertices[index].face_ring):
|
|
||||||
for face_j in self.vertices[index].face_ring[i+1:]:
|
|
||||||
face1 = self.faces[face_i]
|
|
||||||
face2 = self.faces[face_j]
|
|
||||||
if face1 == face2:
|
|
||||||
self.faces[face_i] = None
|
|
||||||
self.faces[face_j] = None
|
|
||||||
verts = (face1.a, face1.b, face1.c)
|
|
||||||
for vert in verts:
|
|
||||||
if vert == index:
|
|
||||||
continue
|
|
||||||
to_remove = True
|
|
||||||
for face_k in self.vertices[vert].face_ring:
|
|
||||||
face = self.faces[face_k]
|
|
||||||
if face is None:
|
|
||||||
continue
|
|
||||||
if vert in (face.a, face.b, face.c):
|
|
||||||
to_remove = False
|
|
||||||
break
|
|
||||||
if to_remove:
|
|
||||||
self.vertices[vert] = None
|
|
||||||
break
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
raise ValueError(
|
|
||||||
f"Vertex {prev_index} is not in the remaining faces {ring_faces}. Origin {ring} on {index}")
|
|
||||||
|
|
||||||
return ring
|
return ring
|
||||||
|
|
||||||
def compute_area_curvature(self, index: int) -> tuple[float, float]:
|
def compute_area_curvature(self, index: int) -> tuple[float, float]:
|
||||||
""" Compute area and curvature the corresponding 1-ring
|
"""Compute area and curvature the corresponding 1-ring
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
index (int): index of the 1-ring's main vertex
|
index (int): index of the 1-ring's main vertex
|
||||||
|
@ -426,7 +483,7 @@ class MAPS(obja.Model):
|
||||||
return selected_vertices
|
return selected_vertices
|
||||||
|
|
||||||
def project_polar(self, index: int) -> tuple[list[np.ndarray], list[int]]:
|
def project_polar(self, index: int) -> tuple[list[np.ndarray], list[int]]:
|
||||||
""" Flatten the 1-ring to retriangulate
|
"""Flatten the 1-ring to retriangulate
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
index (int): main vertex of the 1-ring
|
index (int): main vertex of the 1-ring
|
||||||
|
@ -450,7 +507,7 @@ class MAPS(obja.Model):
|
||||||
return coordinates, ring
|
return coordinates, ring
|
||||||
|
|
||||||
def compute_angle(self, i: int, j: int, k: int) -> float:
|
def compute_angle(self, i: int, j: int, k: int) -> float:
|
||||||
""" Calculate the angle defined by three points
|
"""Calculate the angle defined by three points
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
i (int): previous index
|
i (int): previous index
|
||||||
|
@ -472,7 +529,7 @@ class MAPS(obja.Model):
|
||||||
return np.arccos(np.clip(res, -1, 1))
|
return np.arccos(np.clip(res, -1, 1))
|
||||||
|
|
||||||
def clip_ear(self, index: int) -> list[obja.Face]:
|
def clip_ear(self, index: int) -> list[obja.Face]:
|
||||||
""" Retriangulate a polygon using the ear clipping algorithm
|
"""Retriangulate a polygon using the ear clipping algorithm
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
index (int): index of 1-ring
|
index (int): index of 1-ring
|
||||||
|
@ -567,11 +624,11 @@ class MAPS(obja.Model):
|
||||||
return faces
|
return faces
|
||||||
|
|
||||||
def is_convex(self,
|
def is_convex(self,
|
||||||
prev_vert: np.ndarray[int, np.dtype[np.float64]],
|
prev_vert: np.ndarray[int, float],
|
||||||
curr_vert: np.ndarray[int, np.dtype[np.float64]],
|
curr_vert: np.ndarray[int, float],
|
||||||
next_vert: np.ndarray[int, np.dtype[np.float64]]
|
next_vert: np.ndarray[int, float],
|
||||||
) -> bool:
|
) -> bool:
|
||||||
""" Check if the angle less than pi
|
"""Check if the angle less than pi
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prev_vert (np.ndarray): first point
|
prev_vert (np.ndarray): first point
|
||||||
|
@ -592,12 +649,12 @@ class MAPS(obja.Model):
|
||||||
return internal_angle >= np.pi
|
return internal_angle >= np.pi
|
||||||
|
|
||||||
def is_inside(self,
|
def is_inside(self,
|
||||||
a: np.ndarray[int, np.dtype[np.float64]],
|
a: np.ndarray[int, float],
|
||||||
b: np.ndarray[int, np.dtype[np.float64]],
|
b: np.ndarray[int, float],
|
||||||
c: np.ndarray[int, np.dtype[np.float64]],
|
c: np.ndarray[int, float],
|
||||||
p: np.ndarray[int, np.dtype[np.float64]]
|
p: np.ndarray[int, float],
|
||||||
) -> bool:
|
) -> bool:
|
||||||
""" Check if p is in the triangle a b c
|
"""Check if p is in the triangle a b c
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
a (np.ndarray): point one
|
a (np.ndarray): point one
|
||||||
|
@ -642,7 +699,7 @@ class MAPS(obja.Model):
|
||||||
colors = [c - min_c for c in colors]
|
colors = [c - min_c for c in colors]
|
||||||
max_c = max(colors)
|
max_c = max(colors)
|
||||||
|
|
||||||
operations = []
|
self.operations = []
|
||||||
for i, face in enumerate(self.faces):
|
for i, face in enumerate(self.faces):
|
||||||
if face is None:
|
if face is None:
|
||||||
continue
|
continue
|
||||||
|
@ -654,19 +711,19 @@ class MAPS(obja.Model):
|
||||||
r, g, b = 1.0, 0.0, 0.0
|
r, g, b = 1.0, 0.0, 0.0
|
||||||
break
|
break
|
||||||
|
|
||||||
operations.append(('fc', i, (r, g, b)))
|
self.operations.append(('fc', i, (r, g, b)))
|
||||||
operations.append(('af', i, face.to_obja()))
|
self.operations.append(('af', i, face.to_obja()))
|
||||||
|
|
||||||
for i, vertex in enumerate(self.vertices):
|
for i, vertex in enumerate(self.vertices):
|
||||||
if vertex is None:
|
if vertex is None:
|
||||||
continue
|
continue
|
||||||
operations.append(('av', i, vertex.to_obja()))
|
self.operations.append(('av', i, vertex.to_obja()))
|
||||||
operations.reverse()
|
self.operations.reverse()
|
||||||
|
|
||||||
# Write the result in output file
|
# Write the result in output file
|
||||||
output_model = obja.Output(output)
|
output_model = obja.Output(output)
|
||||||
|
|
||||||
for (op, index, value) in operations:
|
for (op, index, value) in self.operations:
|
||||||
if op == 'av':
|
if op == 'av':
|
||||||
output_model.add_vertex(index, value)
|
output_model.add_vertex(index, value)
|
||||||
elif op == 'af':
|
elif op == 'af':
|
||||||
|
@ -685,7 +742,7 @@ class MAPS(obja.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
def compress(self, output: io.TextIOWrapper, level: int, final_only: bool, debug: bool) -> None:
|
def compress(self, output: io.TextIOWrapper, level: int, final_only: bool, debug: bool) -> None:
|
||||||
""" Compress the 3d model
|
"""Compress the 3d model
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
output (io.TextIOWrapper): Output file descriptor
|
output (io.TextIOWrapper): Output file descriptor
|
||||||
|
@ -697,7 +754,8 @@ class MAPS(obja.Model):
|
||||||
self.compress_task = progress.add_task('╙── Compression')
|
self.compress_task = progress.add_task('╙── Compression')
|
||||||
self.progress = progress
|
self.progress = progress
|
||||||
|
|
||||||
operations = []
|
self.operations = []
|
||||||
|
self.final_only = final_only
|
||||||
|
|
||||||
# while len(self.vertices) > 64:
|
# while len(self.vertices) > 64:
|
||||||
for _ in progress.track(range(level), task_id=self.global_task):
|
for _ in progress.track(range(level), task_id=self.global_task):
|
||||||
|
@ -714,43 +772,43 @@ class MAPS(obja.Model):
|
||||||
|
|
||||||
# Edit the first faces
|
# Edit the first faces
|
||||||
for i in range(len(faces)):
|
for i in range(len(faces)):
|
||||||
if not final_only:
|
if not self.final_only:
|
||||||
operations.append(
|
self.operations.append(
|
||||||
('ef', ring_faces[i], self.faces[ring_faces[i]].to_obja()))
|
('ef', ring_faces[i], self.faces[ring_faces[i]].to_obja()))
|
||||||
self.faces[ring_faces[i]] = faces[i]
|
self.faces[ring_faces[i]] = faces[i]
|
||||||
|
|
||||||
# Remove the last faces
|
# Remove the last faces
|
||||||
for i in range(len(faces), len(ring_faces)):
|
for i in range(len(faces), len(ring_faces)):
|
||||||
if not final_only:
|
if not self.final_only:
|
||||||
operations.append(
|
self.operations.append(
|
||||||
('af', ring_faces[i], self.faces[ring_faces[i]].to_obja()))
|
('af', ring_faces[i], self.faces[ring_faces[i]].to_obja()))
|
||||||
self.faces[ring_faces[i]] = None
|
self.faces[ring_faces[i]] = None
|
||||||
|
|
||||||
# Remove the vertex
|
# Remove the vertex
|
||||||
if not final_only:
|
if not self.final_only:
|
||||||
operations.append(
|
self.operations.append(
|
||||||
('av', v_index, self.vertices[v_index].to_obja()))
|
('av', v_index, self.vertices[v_index].to_obja()))
|
||||||
self.vertices[v_index] = None
|
self.vertices[v_index] = None
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
self.debug(output)
|
self.debug(output)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Register remaining vertices and faces
|
# Register remaining vertices and faces
|
||||||
for i, face in enumerate(self.faces):
|
for i, face in enumerate(self.faces):
|
||||||
if face is not None:
|
if face is not None:
|
||||||
operations.append(('af', i, face.to_obja()))
|
self.operations.append(('af', i, face.to_obja()))
|
||||||
for i, v_index in enumerate(self.vertices):
|
for i, v_index in enumerate(self.vertices):
|
||||||
if v_index is not None:
|
if v_index is not None:
|
||||||
operations.append(('av', i, v_index.to_obja()))
|
self.operations.append(('av', i, v_index.to_obja()))
|
||||||
|
|
||||||
# To rebuild the model, run operations in reverse order
|
# To rebuild the model, run operations in reverse order
|
||||||
operations.reverse()
|
self.operations.reverse()
|
||||||
|
|
||||||
# Write the result in output file
|
# Write the result in output file
|
||||||
output_model = obja.Output(output)
|
output_model = obja.Output(output)
|
||||||
|
|
||||||
for (op, index, value) in operations:
|
for (op, index, value) in self.operations:
|
||||||
if op == 'av':
|
if op == 'av':
|
||||||
output_model.add_vertex(index, value)
|
output_model.add_vertex(index, value)
|
||||||
elif op == 'af':
|
elif op == 'af':
|
||||||
|
@ -773,7 +831,7 @@ class MAPS(obja.Model):
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
""" Run MAPS model compression
|
"""Run MAPS model compression
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
args (Namespace): arguments (input and output path)
|
args (Namespace): arguments (input and output path)
|
||||||
|
@ -782,7 +840,8 @@ def main(args):
|
||||||
model.parse_file(args.input)
|
model.parse_file(args.input)
|
||||||
|
|
||||||
with open(args.output, 'w') as output:
|
with open(args.output, 'w') as output:
|
||||||
model.compress(output, args.level, args.final or args.debug, args.debug)
|
model.compress(output, args.level,
|
||||||
|
args.final or args.debug, args.debug)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Reference in a new issue