Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

10 edge collapse #55

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion .github/workflows/model-view-ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: model-ci
name: mesh_model-ci

on:
push:
Expand Down
164 changes: 125 additions & 39 deletions actions/triangular_actions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

from model.mesh_struct.mesh import Mesh
from model.mesh_struct.mesh_elements import Dart, Node
from model.mesh_analysis import degree, isFlipOk
from mesh_model.mesh_struct.mesh import Mesh
from mesh_model.mesh_struct.mesh_elements import Dart, Node
from mesh_model.mesh_analysis import isFlipOk, isCollapseOk, adjacent_darts, isSplitOk


def flip_edge_ids(mesh: Mesh, id1: int, id2: int) -> True:
Expand All @@ -15,10 +15,7 @@ def flip_edge(mesh: Mesh, n1: Node, n2: Node) -> True:
if not found or not isFlipOk(d):
return False

d2, d1, d11, d21, d211, n1, n2, n3, n4 = active_triangles(mesh, d)

test_degree(n3)
test_degree(n4)
d2, d1, d11, d21, d211, n1, n2, n3, n4 = mesh.active_triangles(d)

f1 = d.get_face()
f2 = d2.get_face()
Expand Down Expand Up @@ -54,12 +51,10 @@ def split_edge_ids(mesh: Mesh, id1: int, id2: int) -> True:

def split_edge(mesh: Mesh, n1: Node, n2: Node) -> True:
found, d = mesh.find_inner_edge(n1, n2)
if not found:
if not found or not isSplitOk(d):
return False

d2, d1, d11, d21, d211, n1, n2, n3, n4 = active_triangles(mesh, d)
test_degree(n3)
test_degree(n4)
d2, d1, _, d21, _, n1, n2, n3, n4 = mesh.active_triangles(d)

# create a new node in the middle of [n1, n2]
N5 = mesh.add_node((n1.x() + n2.x()) / 2, (n1.y() + n2.y()) / 2)
Expand All @@ -86,33 +81,124 @@ def split_edge(mesh: Mesh, n1: Node, n2: Node) -> True:
return True


def active_triangles(mesh: Mesh, d: Dart) -> tuple[Dart, Dart, Dart, Dart, Dart, Node, Node, Node, Node]:
"""
Return the darts and nodes around selected dart
:param mesh: the mesh
:param d: selected dart
:return: a tuple of darts and nodes
"""
d2 = d.get_beta(2)
d1 = d.get_beta(1)
d11 = d1.get_beta(1)
d21 = d2.get_beta(1)
d211 = d21.get_beta(1)
n1 = d.get_node()
n2 = d2.get_node()
n3 = d11.get_node()
n4 = d211.get_node()

return d2, d1, d11, d21, d211, n1, n2, n3, n4


def test_degree(n: Node) -> bool:
"""
Verify that the degree of a vertex is lower than 10
:param n: a Node
:return: True if the degree is lower than 10, False otherwise
"""
if degree(n) > 10:
def collapse_edge_ids(mesh: Mesh, id1: int, id2: int) -> True:
return collapse_edge(mesh, Node(mesh, id1), Node(mesh, id2))


def collapse_edge(mesh: Mesh, n1: Node, n2: Node) -> True:
found, d = mesh.find_inner_edge(n1, n2)

if not found or not isCollapseOk(d):
return False

_, d1, d11, d21, d211, n1, n2, _, _ = mesh.active_triangles(d)

d212 = d21.get_beta(2) #T1
d2112 = d211.get_beta(2) #T2
d12 = d1.get_beta(2) #T3
d112 = d11.get_beta(2) #T4

#Delete the darts around selected dart
mesh.del_adj_triangles(d)

#Move n1 node in the middle of [n1, n2]
n1.set_xy((n1.x() + n2.x()) / 2, (n1.y() + n2.y()) / 2)

#Update node relations
if d12 is not None:
d121 = d12.get_beta(1)
d121.set_node(n1)
ds = d121
while ds is not None and ds != d2112:
d2s = ds.get_beta(2)
if d2s is None:
ds = d2112
while ds is not None:
ds.set_node(n1)
ds1 = ds.get_beta(1)
ds11 = ds1.get_beta(1)
ds = ds11.get_beta(2)
else:
ds = d2s.get_beta(1)
ds.set_node(n1)
elif d12 is None and d2112 is not None:
d2112.set_node(n1)
ds = (d2112.get_beta(1)).get_beta(1)
ds2 = ds.get_beta(2)
while ds2 is not None:
ds2.set_node(n1)
ds = (ds2.get_beta(1)).get_beta(1)
ds2 = ds.get_beta(2)

#update beta2 relations
if d112 is not None:
d112.set_beta(2, d12)
if d12 is not None:
d12.set_beta(2, d112)

if d212 is not None:
d212.set_beta(2, d2112)
if d2112 is not None:
d2112.set_beta(2, d212)

#delete n2 node
mesh.del_node(n2)

return mesh_check(mesh)


def test_boundary(n1: Node, n2: Node) -> bool:
boundary_darts_n1 = []
boundary_darts_n2 = []
for d in adjacent_darts(n1):
if d.get_beta(2) is None:
boundary_darts_n1.append(d)
for d in adjacent_darts(n2):
if d.get_beta(2) is None:
boundary_darts_n2.append(d)
if (len(boundary_darts_n1) + len(boundary_darts_n2)) > 3:
return False
else:
return True
return True


def check_beta2_relation(mesh: Mesh) -> bool:
for dart_info in mesh.active_darts():
d = dart_info[0]
d2 = dart_info[2]
if d2 >= 0 and mesh.dart_info[d2, 0] < 0:
raise ValueError("error beta2")
elif d2 >= 0 and mesh.dart_info[d2, 2] != d:
raise ValueError("error beta2")
return True


def check_double(mesh: Mesh) -> bool:
for dart_info in mesh.active_darts():
d = Dart(mesh, dart_info[0])
d2 = Dart(mesh, dart_info[2]) if dart_info[2] >= 0 else None
n1 = dart_info[3]
if d2 is None:
d1 = d.get_beta(1)
n2 = d1.get_node().id
else:
n2 = d2.get_node().id
for dart_info2 in mesh.active_darts():
ds = Dart(mesh, dart_info2[0])
ds2 = Dart(mesh, dart_info2[2]) if dart_info2[2] >= 0 else None
if d != ds and d != ds2:
ns1 = dart_info2[3]
if ds2 is None:
ds1 = ds.get_beta(1)
ns2 = ds1.get_node().id
else:
ns2 = ds2.get_node().id

if n1 == ns1 and n2 == ns2:
raise ValueError("double error")
elif n2 == ns1 and n1 == ns2:
return False
return True

def mesh_check(mesh: Mesh) -> bool:
return check_double(mesh) and check_beta2_relation(mesh)
108 changes: 60 additions & 48 deletions environment/trimesh_env.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
from typing import Any
import math
import numpy as np
from model.mesh_analysis import global_score, isValidAction, find_template_opposite_node
from model.mesh_struct.mesh_elements import Dart
from model.mesh_struct.mesh import Mesh
from actions.triangular_actions import flip_edge
from model.random_trimesh import random_flip_mesh
from mesh_model.mesh_analysis import global_score, find_template_opposite_node
from mesh_model.mesh_struct.mesh_elements import Dart
from mesh_model.mesh_struct.mesh import Mesh
from actions.triangular_actions import flip_edge, split_edge, collapse_edge
from mesh_model.random_trimesh import random_flip_mesh, random_mesh

# possible actions
FLIP = 0
SPLIT = 1
COLLAPSE = 2
GLOBAL = 0


class TriMesh:
def __init__(self, mesh=None, mesh_size: int = None, max_steps: int = 50, feat: int = 0):
self.mesh = mesh if mesh is not None else random_flip_mesh(mesh_size)
self.mesh_size = len(self.mesh.nodes)
self.mesh_size = len(self.mesh.active_nodes())
self.size = len(self.mesh.dart_info)
self.actions = np.array([FLIP])
self.actions = np.array([FLIP, SPLIT, COLLAPSE])
self.reward = 0
self.steps = 0
self.max_steps = max_steps
self.nodes_scores = global_score(self.mesh)[0]
self.ideal_score = global_score(self.mesh)[2]
self.nodes_scores, self.mesh_score, self.ideal_score = global_score(self.mesh)
self.terminal = False
self.feat = feat
self.won = 0
Expand All @@ -30,30 +32,34 @@ def reset(self, mesh=None):
self.reward = 0
self.steps = 0
self.terminal = False
self.mesh = mesh if mesh is not None else random_flip_mesh(self.mesh_size)
self.mesh = mesh if mesh is not None else random_mesh(self.mesh_size)
self.size = len(self.mesh.dart_info)
self.nodes_scores = global_score(self.mesh)[0]
self.ideal_score = global_score(self.mesh)[2]
self.nodes_scores, self.mesh_score, self.ideal_score = global_score(self.mesh)
self.won = 0

def step(self, action):
dart_id = action[1]
_, mesh_score, mesh_ideal_score = global_score(self.mesh)
d = Dart(self.mesh, dart_id)
d1 = d.get_beta(1)
n1 = d.get_node()
n2 = d1.get_node()
flip_edge(self.mesh, n1, n2)
if action[2] == FLIP:
flip_edge(self.mesh, n1, n2)
elif action[2] == SPLIT:
split_edge(self.mesh, n1, n2)
elif action[2] == COLLAPSE:
collapse_edge(self.mesh, n1, n2)
self.steps += 1
next_nodes_score, next_mesh_score, _ = global_score(self.mesh)
self.nodes_scores = next_nodes_score
self.reward = (mesh_score - next_mesh_score)*10
if self.steps >= self.max_steps or next_mesh_score == mesh_ideal_score:
if next_mesh_score == mesh_ideal_score:
self.reward = (self.mesh_score - next_mesh_score) * 10
if self.steps >= self.max_steps or next_mesh_score == self.ideal_score:
if next_mesh_score == self.ideal_score:
self.won = True
self.terminal = True
self.nodes_scores, self.mesh_score = next_nodes_score, next_mesh_score

def get_x(self, s: Mesh, a: int) -> tuple[Any, list[int | list[int]]]:
def get_x(self, s: Mesh, a: int):
"""
Get the feature vector of the state-action pair
:param s: the state
Expand All @@ -66,19 +72,39 @@ def get_x(self, s: Mesh, a: int) -> tuple[Any, list[int | list[int]]]:
return get_x_global_4(self, s)


def get_x_global_4(env, state: Mesh) -> tuple[Any, list[int | list[int]]]:
def get_x_global_4(env, state: Mesh):
"""
Get the feature vector of the state.
:param state: the state
:param env: The environment
:return: the feature vector
"""
mesh = state
template = get_template_2(mesh)
darts_to_delete = []
darts_id = []

for i, d_info in enumerate(mesh.active_darts()):
d_id = d_info[0]
if d_info[2] == -1: #test the validity of all action type
darts_to_delete.append(i)
else:
darts_id.append(d_id)
valid_template = np.delete(template, darts_to_delete, axis=0)
score_sum = np.sum(np.abs(valid_template), axis=1)
indices_top_10 = np.argsort(score_sum)[-5:][::-1]
valid_dart_ids = [darts_id[i] for i in indices_top_10]
X = valid_template[indices_top_10, :]
X = X.flatten()
return X, valid_dart_ids


def get_template_2(mesh: Mesh):
nodes_scores = global_score(mesh)[0]
size = len(mesh.dart_info)
size = len(mesh.active_darts())
template = np.zeros((size, 6))

for d_info in mesh.dart_info:
for i, d_info in enumerate(mesh.active_darts()):

d = Dart(mesh, d_info[0])
A = d.get_node()
Expand All @@ -87,35 +113,21 @@ def get_x_global_4(env, state: Mesh) -> tuple[Any, list[int | list[int]]]:
d11 = d1.get_beta(1)
C = d11.get_node()

#Template niveau 1
template[d_info[0], 0] = nodes_scores[C.id]
template[d_info[0], 1] = nodes_scores[A.id]
template[d_info[0], 2] = nodes_scores[B.id]
# Template niveau 1
template[i, 0] = nodes_scores[C.id] if not math.isnan(nodes_scores[C.id]) else 0
template[i, 1] = nodes_scores[A.id] if not math.isnan(nodes_scores[A.id]) else 0
template[i, 2] = nodes_scores[B.id] if not math.isnan(nodes_scores[B.id]) else 0

#template niveau 2
# template niveau 2

n_id = find_template_opposite_node(d)
if n_id is not None:
template[d_info[0], 3] = nodes_scores[n_id]
if n_id is not None and not math.isnan(nodes_scores[n_id]):
template[i, 3] = nodes_scores[n_id]
n_id = find_template_opposite_node(d1)
if n_id is not None:
template[d_info[0], 4] = nodes_scores[n_id]
if n_id is not None and not math.isnan(nodes_scores[n_id]):
template[i, 4] = nodes_scores[n_id]
n_id = find_template_opposite_node(d11)
if n_id is not None:
template[d_info[0], 5] = nodes_scores[n_id]

dart_to_delete = []
dart_ids = []
for i in range(size):
d = Dart(mesh, i)
if not isValidAction(mesh, d.id):
dart_to_delete.append(i)
else :
dart_ids.append(i)
valid_template = np.delete(template, dart_to_delete, axis=0)
score_sum = np.sum(np.abs(valid_template), axis=1)
indices_top_10 = np.argsort(score_sum)[-5:][::-1]
valid_dart_ids = [dart_ids[i] for i in indices_top_10]
X = valid_template[indices_top_10, :]
X = X.flatten()
return X, valid_dart_ids
if n_id is not None and not math.isnan(nodes_scores[n_id]):
template[i, 5] = nodes_scores[n_id]

return template
Loading