contour
This commit is contained in:
parent
c2e1bc50a5
commit
c862e8976b
3 changed files with 45 additions and 32 deletions
|
|
@ -220,18 +220,13 @@ def extract_lineart_edges(mesh_data: Any, max_angle_deg: float = 30.0) -> NDArra
|
|||
|
||||
num_unique = len(unique_keys)
|
||||
|
||||
max_cat = np.full(num_unique, -1, dtype=int)
|
||||
min_cat = np.full(num_unique, 999, dtype=int)
|
||||
np.maximum.at(max_cat, inverse_indices, edge_category)
|
||||
np.minimum.at(min_cat, inverse_indices, edge_category)
|
||||
|
||||
# Rule 1: Silhouette edge - adjacent faces have different categories
|
||||
# (i.e., not all faces sharing this edge are in the same category)
|
||||
categories_all = edge_category[inverse_indices]
|
||||
rule1_unique = np.zeros(num_unique, dtype=bool)
|
||||
|
||||
for edge_id in range(num_unique):
|
||||
edge_cats = categories_all[inverse_indices == edge_id]
|
||||
if len(edge_cats) < 2:
|
||||
rule1_unique[edge_id] = False
|
||||
else:
|
||||
all_same = np.all(edge_cats == edge_cats[0])
|
||||
rule1_unique[edge_id] = not all_same
|
||||
rule1_unique = (counts >= 2) & (max_cat != min_cat)
|
||||
|
||||
# Rule 2: Boundary edge (owned by 1 face)
|
||||
rule2_unique = counts == 1
|
||||
|
|
@ -477,27 +472,19 @@ def extract_silhouette_contour_edges(mesh_data: Any) -> NDArray[np.float64]:
|
|||
|
||||
num_unique = len(unique_keys)
|
||||
|
||||
# For each unique edge, collect the categories of all adjacent faces
|
||||
# An edge is a silhouette if it has faces from different categories
|
||||
# (i.e., not all faces are in the same category)
|
||||
|
||||
# Get categories for all edge instances
|
||||
categories_all = edge_category[inverse_indices]
|
||||
|
||||
# For each unique edge, check if all its face categories are the same
|
||||
is_silhouette = np.zeros(num_unique, dtype=bool)
|
||||
|
||||
for edge_id in range(num_unique):
|
||||
edge_cats = categories_all[inverse_indices == edge_id]
|
||||
# If this edge has only one face, it's a boundary, not silhouette
|
||||
if len(edge_cats) < 2:
|
||||
is_silhouette[edge_id] = False
|
||||
else:
|
||||
# Check if all categories are the same
|
||||
all_same = np.all(edge_cats == edge_cats[0])
|
||||
is_silhouette[edge_id] = not all_same
|
||||
max_cat = np.full(num_unique, -1, dtype=int)
|
||||
min_cat = np.full(num_unique, 999, dtype=int)
|
||||
np.maximum.at(max_cat, inverse_indices, edge_category)
|
||||
np.minimum.at(min_cat, inverse_indices, edge_category)
|
||||
|
||||
return canonical_2d[first_indices[is_silhouette]]
|
||||
# An edge is a silhouette/contour edge if:
|
||||
# 1. It is owned by 1 face (boundary edge of open surface)
|
||||
# OR
|
||||
# 2. Adjacent faces belong to different normal.z orientation categories (e.g. front vs back, front vs vertical)
|
||||
is_silhouette = (counts == 1) | (max_cat != min_cat)
|
||||
|
||||
contour_edges = canonical_2d[first_indices[is_silhouette]]
|
||||
return deduplicate_2d_edges(contour_edges)
|
||||
|
||||
|
||||
def extract_outer_contour_edges(mesh_data: Any) -> NDArray[np.float64]:
|
||||
|
|
|
|||
|
|
@ -126,8 +126,10 @@ def export_mesh_to_pdf(
|
|||
if edge_list:
|
||||
combined = np.concatenate(edge_list, axis=0)
|
||||
edges_to_draw = deduplicate_2d_edges(combined)
|
||||
else:
|
||||
elif draw_wireframe:
|
||||
edges_to_draw = edges_2d
|
||||
else:
|
||||
edges_to_draw = np.empty((0, 2, 2), dtype=np.float64)
|
||||
|
||||
if len(edges_to_draw) > 0:
|
||||
min_x = float(edges_to_draw[:, :, 0].min())
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ from NodeSlicer.infra.blender.blender_mesh_capture import SlicingGroup, SlicingC
|
|||
from NodeSlicer.core.pdf_exporter import (
|
||||
extract_unique_top_view_edges,
|
||||
extract_lineart_edges,
|
||||
extract_outer_contour_edges,
|
||||
export_wireframe_to_pdf,
|
||||
export_mesh_to_pdf,
|
||||
MM_TO_PT
|
||||
|
|
@ -129,6 +130,29 @@ class TestPdfExporter(unittest.TestCase):
|
|||
lineart_100 = extract_lineart_edges(so, max_angle_deg=100.0)
|
||||
self.assertTrue(len(lineart_100) > 0)
|
||||
|
||||
def test_extract_outer_contour_edges(self):
|
||||
so = SlicingObject()
|
||||
# Top face made of 2 triangles (0,0)-(10,0)-(10,10) and (0,0)-(10,10)-(0,10)
|
||||
# Shared interior diagonal edge: (0,0)-(10,10)
|
||||
# Side wall triangles around the box (vertical wall, normal_z = 0)
|
||||
tri_top1 = np.array([[0.0, 0.0, 10.0], [10.0, 0.0, 10.0], [10.0, 10.0, 10.0], [0.0, 0.0, 1.0]], dtype=np.float64)
|
||||
tri_top2 = np.array([[0.0, 0.0, 10.0], [10.0, 10.0, 10.0], [0.0, 10.0, 10.0], [0.0, 0.0, 1.0]], dtype=np.float64)
|
||||
# Side walls
|
||||
tri_side1 = np.array([[0.0, 0.0, 10.0], [10.0, 0.0, 10.0], [10.0, 0.0, 0.0], [0.0, -1.0, 0.0]], dtype=np.float64)
|
||||
tri_side2 = np.array([[10.0, 0.0, 10.0], [10.0, 10.0, 10.0], [10.0, 10.0, 0.0], [1.0, 0.0, 0.0]], dtype=np.float64)
|
||||
tri_side3 = np.array([[10.0, 10.0, 10.0], [0.0, 10.0, 10.0], [0.0, 10.0, 0.0], [0.0, 1.0, 0.0]], dtype=np.float64)
|
||||
tri_side4 = np.array([[0.0, 10.0, 10.0], [0.0, 0.0, 10.0], [0.0, 0.0, 0.0], [-1.0, 0.0, 0.0]], dtype=np.float64)
|
||||
|
||||
so.mesh = np.array([tri_top1, tri_top2, tri_side1, tri_side2, tri_side3, tri_side4])
|
||||
|
||||
wireframe = extract_unique_top_view_edges(so)
|
||||
# Wireframe contains 4 outer edges + 1 interior diagonal = 5
|
||||
self.assertEqual(len(wireframe), 5)
|
||||
|
||||
contour = extract_outer_contour_edges(so)
|
||||
# Contour should exclude the internal diagonal edge and return only 4 outer boundary edges
|
||||
self.assertEqual(len(contour), 4)
|
||||
|
||||
def test_export_pdf_true_1_to_1_scale(self):
|
||||
edges_2d = np.array([
|
||||
[[0.0, 0.0], [100.0, 0.0]],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue