Skip to content

Commit

Permalink
Merge pull request #55 from LIHPC-Computational-Geometry/10-edge-coll…
Browse files Browse the repository at this point in the history
…apse

10 edge collapse
  • Loading branch information
ArzhelaR authored Dec 2, 2024
2 parents b546f6e + 7aed23e commit 620e9df
Show file tree
Hide file tree
Showing 29 changed files with 1,142 additions and 537 deletions.
4 changes: 2 additions & 2 deletions .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 All @@ -23,7 +23,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest numpy coverage pygame
pip install flake8 pytest numpy coverage pygame matplotlib
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
Expand Down
150 changes: 111 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,110 @@ 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:
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)
"""
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
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)
"""
if degree(n) > 10:
return False
else:
return True
#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 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

0 comments on commit 620e9df

Please sign in to comment.