Skip to content

Commit 13f24ed

Browse files
GITBOOK materialization bottom mesh
1 parent 8a5c0fb commit 13f24ed

17 files changed

+1180
-13
lines changed
74.9 KB
Binary file not shown.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#! python3
2+
# venv: brg-csd
3+
# r: compas_rv
4+
5+
import pathlib
6+
import compas
7+
from compas.scene import Scene
8+
9+
# =============================================================================
10+
# Load data
11+
# =============================================================================
12+
IFILE = pathlib.Path(__file__).parent / "rhinovault_session.json"
13+
rv_session = compas.json_load(IFILE)
14+
rv_scene: Scene = rv_session["scene"]
15+
pattern = rv_scene.find_by_name("Pattern").mesh
16+
17+
# =============================================================================
18+
# Visualisation
19+
# =============================================================================
20+
scene = Scene()
21+
scene.clear_context()
22+
scene.add(rv_scene.find_by_name("Pattern").mesh)
23+
scene.draw()
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#! python3
2+
# venv: brg-csd
3+
# r: compas_rv
4+
5+
import pathlib
6+
7+
import compas
8+
from compas.datastructures import Mesh
9+
from compas.scene import Scene
10+
from compas_tna.diagrams import FormDiagram
11+
from compas import json_dump
12+
from compas.itertools import pairwise
13+
14+
15+
def break_boundary(mesh: Mesh, breakpoints: list[int]) -> tuple[list[list[int]], list[int]]:
16+
17+
# Get the list of vertices on the boundary (first boundary from the mesh)
18+
# If the first and last vertices in the boundary are the same, remove the last vertex (close loop)
19+
boundary: list[int] = mesh.vertices_on_boundaries()[0]
20+
21+
if boundary[0] == boundary[-1]:
22+
del boundary[-1]
23+
24+
# Sort the breakpoints based on their index in the boundary
25+
# Find the starting point in the boundary for the first breakpoint
26+
# Rearrange the boundary to start from the first breakpoint
27+
breakpoints = sorted(breakpoints, key=lambda s: boundary.index(s))
28+
start = boundary.index(breakpoints[0])
29+
boundary = boundary[start:] + boundary[:start]
30+
31+
# Iterate over pairs of breakpoints and create sub-boundaries from the main boundary
32+
borders = []
33+
34+
for a, b in pairwise(breakpoints):
35+
start = boundary.index(a) # Find index of the first breakpoint in the boundary
36+
end = boundary.index(b) # Find index of the second breakpoint in the boundary
37+
borders.append(boundary[start : end + 1]) # Add the sub-boundary from start to end
38+
39+
# Add the last segment from the last breakpoint to the first one to close the loop
40+
borders.append(boundary[end:] + boundary[:1])
41+
42+
# Return the sub-boundaries and the breakpoints
43+
return borders, breakpoints
44+
45+
46+
# =============================================================================
47+
# Load data
48+
# =============================================================================
49+
50+
IFILE = pathlib.Path(__file__).parent / "rhinovault_session.json"
51+
52+
rv_session = compas.json_load(IFILE)
53+
rv_scene: Scene = rv_session["scene"]
54+
55+
thrustobject = rv_scene.find_by_name("ThrustDiagram")
56+
thrustdiagram: FormDiagram = thrustobject.mesh
57+
58+
# =============================================================================
59+
# Mesh
60+
#
61+
# - make a copy of the thrustdiagram
62+
# - remove the "TNA" faces cooresponding to boundary openings
63+
# - compute the average edge length for remeshing
64+
# =============================================================================
65+
66+
mesh: Mesh = thrustdiagram.copy(cls=Mesh)
67+
68+
for face in list(mesh.faces_where(_is_loaded=False)):
69+
mesh.delete_face(face)
70+
71+
# =============================================================================
72+
# Mesh: Borders
73+
# =============================================================================
74+
75+
supports = list(mesh.vertices_where(is_support=True))
76+
borders, supports = break_boundary(mesh, supports)
77+
print(borders)
78+
print(supports)
79+
80+
mesh.attributes["supports"] = supports
81+
mesh.attributes["borders"] = borders
82+
83+
# =============================================================================
84+
# Serialize
85+
# =============================================================================
86+
87+
json_dump(mesh, pathlib.Path(__file__).parent / "001_mesh.json")
88+
89+
# =============================================================================
90+
# Visualisation
91+
# =============================================================================
92+
93+
scene = Scene()
94+
# scene.clear_context()
95+
scene.add(mesh)
96+
scene.draw()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#! python3
2+
# venv: brg-csd
3+
# r: compas_rv
4+
5+
import pathlib
6+
7+
import compas
8+
from compas.datastructures import Mesh
9+
from compas.scene import Scene
10+
from compas import json_dump
11+
from compas.datastructures.mesh.remesh import trimesh_remesh
12+
from compas.geometry import Line
13+
from compas.geometry import normal_triangle
14+
from compas.geometry import KDTree
15+
from compas_model.geometry import intersection_ray_triangle
16+
17+
# =============================================================================
18+
# Load data
19+
# =============================================================================
20+
21+
IFILE = pathlib.Path(__file__).parent / "001_mesh.json"
22+
mesh = compas.json_load(IFILE)
23+
24+
# =============================================================================
25+
# Trimesh
26+
#
27+
# - convert a copy of the mesh to a trimesh using "quads to triangles"
28+
# - note that this doesn't work for other patterns
29+
# =============================================================================
30+
31+
trimesh: Mesh = mesh.copy()
32+
trimesh.quads_to_triangles()
33+
34+
# =============================================================================
35+
# Trimesh: Remeshing
36+
#
37+
# - remesh using CGAL
38+
# - use a percentage of the average edge length of the original mesh as target length
39+
# =============================================================================
40+
41+
42+
length = sum(mesh.edge_length(edge) for edge in mesh.edges()) / mesh.number_of_edges()
43+
44+
def project(mesh: Mesh, k, args):
45+
for vertex in mesh.vertices():
46+
if mesh.is_vertex_on_boundary(vertex):
47+
continue
48+
point = mesh.vertex_point(vertex)
49+
_, nbr, _ = tree.nearest_neighbor(point)
50+
triangles = vertex_triangles[nbr]
51+
for triangle in triangles:
52+
normal = normal_triangle(triangle)
53+
ray = Line.from_point_direction_length(point, normal, 10)
54+
result = intersection_ray_triangle(ray, triangle)
55+
if result:
56+
mesh.vertex_attributes(vertex, "xyz", result)
57+
break
58+
59+
vertex_index = {vertex: index for index, vertex in enumerate(trimesh.vertices())}
60+
vertex_triangles = {vertex_index[vertex]: [trimesh.face_points(face) for face in trimesh.vertex_faces(vertex)] for vertex in trimesh.vertices()}
61+
vertices = trimesh.vertices_attributes("xyz")
62+
tree = KDTree(vertices)
63+
64+
fixed = list(trimesh.vertices_on_boundary())
65+
free = set(list(trimesh.vertices())) - set(fixed)
66+
67+
trimesh_remesh(trimesh, length, kmax=300, tol=0.1, allow_boundary_split=False, callback=project)
68+
69+
70+
# =============================================================================
71+
# Serialize
72+
# =============================================================================
73+
74+
json_dump(trimesh, pathlib.Path(__file__).parent / "002_mesh.json")
75+
76+
# =============================================================================
77+
# Visualisation
78+
# =============================================================================
79+
80+
scene = Scene()
81+
scene.clear_context()
82+
scene.add(trimesh)
83+
scene.draw()
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#! python3
2+
# venv: brg-csd
3+
# r: compas_rv
4+
5+
import pathlib
6+
7+
import compas
8+
from compas.datastructures import Mesh
9+
from compas.scene import Scene
10+
from compas import json_dump
11+
12+
# =============================================================================
13+
# Load data
14+
# =============================================================================
15+
16+
IFILE = pathlib.Path(__file__).parent / "002_mesh.json"
17+
trimesh = compas.json_load(IFILE)
18+
19+
# =============================================================================
20+
# Dual
21+
#
22+
# - construct a dual
23+
# - update default attributes
24+
# - flip the cycles because a dual has opposite cycles compared to the original
25+
# =============================================================================
26+
27+
dual: Mesh = trimesh.dual(include_boundary=True)
28+
29+
dual.update_default_edge_attributes(is_support=False)
30+
dual.update_default_face_attributes(number=None, batch=None)
31+
dual.update_default_vertex_attributes(thickness=0, is_corner=False, is_support=False)
32+
33+
dual.flip_cycles()
34+
35+
36+
# =============================================================================
37+
# Serialize
38+
# =============================================================================
39+
40+
json_dump(dual, pathlib.Path(__file__).parent / "003_mesh.json")
41+
42+
# =============================================================================
43+
# Visualisation
44+
# =============================================================================
45+
46+
scene = Scene()
47+
scene.clear_context()
48+
scene.add(dual, show_vertices=True)
49+
scene.draw()
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#! python3
2+
# venv: brg-csd
3+
# r: compas_rv
4+
5+
import pathlib
6+
7+
import compas
8+
from compas.scene import Scene
9+
from compas import json_dump
10+
from compas.geometry import KDTree
11+
from compas.geometry import Sphere
12+
13+
# =============================================================================
14+
# Load data
15+
# =============================================================================
16+
17+
IFILE = pathlib.Path(__file__).parent / "003_mesh.json"
18+
dual = compas.json_load(IFILE)
19+
20+
IFILE = pathlib.Path(__file__).parent / "001_mesh.json"
21+
mesh = compas.json_load(IFILE)
22+
23+
# =============================================================================
24+
# Dual: Reconnect corners
25+
#
26+
# - construct a KD tree for nearest neighbour search
27+
# - find the nearest neighbours in the dual to the supports of the original
28+
# - snap the dual vertices to the location of the supports
29+
# - mark the corresponding vertices as "corners"
30+
# =============================================================================
31+
32+
vertices = dual.vertices_attributes("xyz")
33+
vertex_index = {vertex: index for index, vertex in enumerate(dual.vertices())}
34+
index_vertex = {index: vertex for index, vertex in enumerate(dual.vertices())}
35+
tree = KDTree(vertices)
36+
37+
spheres = []
38+
for vertex in mesh.vertices_where(is_support=True):
39+
point = mesh.vertex_point(vertex)
40+
closest, nnbr, distance = tree.nearest_neighbor(point)
41+
dual_vertex = index_vertex[nnbr]
42+
if distance > 5:
43+
dual.vertex_attributes(dual_vertex, names="xyz", values=point)
44+
dual.vertex_attribute(dual_vertex, name="is_corner", value=True)
45+
spheres.append(Sphere(10, point=point))
46+
47+
48+
# =============================================================================
49+
# Serialize
50+
# =============================================================================
51+
52+
json_dump(dual, pathlib.Path(__file__).parent / "004_mesh.json")
53+
54+
# =============================================================================
55+
# Visualisation
56+
# =============================================================================
57+
58+
scene = Scene()
59+
scene.clear_context()
60+
scene.add(dual, show_vertices=True)
61+
for sphere in spheres:
62+
scene.add(sphere)
63+
scene.draw()
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
#! python3
3+
# venv: brg-csd
4+
# r: compas_rv
5+
6+
import pathlib
7+
8+
import compas
9+
from compas.scene import Scene
10+
from compas import json_dump
11+
12+
# =============================================================================
13+
# Load data
14+
# =============================================================================
15+
16+
IFILE = pathlib.Path(__file__).parent / "004_mesh.json"
17+
dual = compas.json_load(IFILE)
18+
19+
# =============================================================================
20+
# Dual: Collapse 2-valent boundary edges
21+
# =============================================================================
22+
23+
tofix = []
24+
25+
for vertex in dual.vertices_on_boundary():
26+
if dual.vertex_degree(vertex) > 2:
27+
continue
28+
tofix.append(vertex)
29+
30+
for vertex in tofix:
31+
nbrs = dual.vertex_neighbors(vertex)
32+
v0 = dual.edge_vector((vertex, nbrs[0]))
33+
v1 = dual.edge_vector((vertex, nbrs[1]))
34+
angle = v0.angle(v1, degrees=True)
35+
36+
if abs(angle - 180) > 30:
37+
continue
38+
39+
if dual.has_edge((vertex, nbrs[0])):
40+
is_corner = dual.vertex_attribute(nbrs[0], name="is_corner")
41+
dual.collapse_edge((vertex, nbrs[0]), t=1, allow_boundary=True)
42+
else:
43+
is_corner = dual.vertex_attribute(nbrs[1], name="is_corner")
44+
dual.collapse_edge((vertex, nbrs[1]), t=1, allow_boundary=True)
45+
46+
if is_corner:
47+
dual.vertex_attribute(vertex, name="is_corner", value=True)
48+
49+
# =============================================================================
50+
# Serialize
51+
# =============================================================================
52+
53+
json_dump(dual, pathlib.Path(__file__).parent / "005_mesh.json")
54+
55+
# =============================================================================
56+
# Visualisation
57+
# =============================================================================
58+
59+
scene = Scene()
60+
scene.clear_context()
61+
scene.add(dual, show_vertices=True)
62+
scene.draw()

0 commit comments

Comments
 (0)