1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2025 lacklustr@protonmail.com https://github.com/eadf
use crate::common::macros::integrity_assert_eq;
use crate::corner_table::CornerIndex;
use crate::isotropic_remesh::IsotropicRemeshAlgo;
use std::fmt::Debug;
use vector_traits::num_traits::AsPrimitive;
use vector_traits::prelude::SimdUpgradable;
impl<S, V, const ENABLE_UNSAFE: bool> IsotropicRemeshAlgo<S, V, ENABLE_UNSAFE>
where
S: crate::common::sealed::ScalarType,
f64: AsPrimitive<S>,
V: Debug + Copy + From<[S; 3]> + Into<[S; 3]> + Sync + 'static,
{
/// Split an edge in a manifold mesh (everything is CCW)
///
/// ```text
/// Notation: cₓ = corner, Vₓ = vertex, tₓ = triangle
/// cₓn = cₓ.next, cₓp = cₓ.prev, cₓo = cₓ.opposite
///
/// Input: corner 'c₀' with vertex 'V₀', where edge to split is V₀->V₂
/// c₀o
/// V₃ ─────────────── V₂ V₃ ─────────────── V₂
/// │c₀p c₀n / │ │ \ c₃p c₃n / │
/// │ / │ │c₀p\ t₃ /c₂p│
/// │ t₀ / c₁ │ │ \ / │
/// │ / │ => │ t₀ \c₃/ t₂ │
/// c₀no│ / │c₁no │ c₀n M c₂ │
/// │ / │ │ / \ │
/// │ / t₁ │ │ / c₁ \ │
/// │c₀/ │ │c₀ / t₁ \c₂n│
/// │/ c₁n c₁p │ │ / c₁n c₁p \ │
/// V₀ ────────────── V₁ V₀ ────────────── V₁
/// c₁o
///
/// A new vertex M is placed at the center of the V₀->V₂ edge.
/// Triangles:
/// - Original triangle t₀ (V₀,V₂,V₃) is split into t₀ (V₀,M,V₃) and t₃ (M,V₂,V₃)
/// - Original triangle t₁ (V₂,V₀,V₁) is split into t₁ (M,V₀,V₁) and t₂ (M,V₁,V₂)
/// ```
pub(crate) fn split_manifold_edge(&mut self, c0: CornerIndex) {
// 1. Extract the structure: c0 points to V0, edge to split is V0->V2
let v0 = self.corner_table.vertex(c0);
let (c0n, c0p) = self.corner_table.next_prev(c0);
let v2 = self.corner_table.vertex(c0n); // c0.next points to V2
let v3 = self.corner_table.vertex(c0p); // c0.prev points to V3
// Get opposite corner c1 (must exist in manifold)
let c1p = self.corner_table.opposite(c0p); // opposite of edge V0->V2
let (c1, c1n) = self.corner_table.next_prev(c1p);
let v1 = self.corner_table.vertex(c1p); // c1.prev points to V1
// Verify the quad structure (V0, V2, V3, V1)
integrity_assert_eq!(self.corner_table.vertex(c1), v2); // c1 points to V2
integrity_assert_eq!(self.corner_table.vertex(c1n), v0); // c1.next points to V0
// 2. Find the opposing corners for the outer edges
let c0no = self.corner_table.opposite(c0n);
let c0o = self.corner_table.opposite(c0);
let c1no = self.corner_table.opposite(c1n);
let c1o = self.corner_table.opposite(c1);
#[cfg(feature = "integrity_check")]
{
println!("c0:{c0:?} c0n: {c0n:?}, c0p: {c0p:?}");
self.corner_table.dbg_triangle_context("c0 (t0)", c0);
println!("c1:{c1:?} c1n: {c1n:?}, c1p: {c1p:?}");
self.corner_table.dbg_triangle_context("c1 (t1)", c1);
//println!("v0:{v0} v1:{v1}, v2:{v2} v3:{v3}");
//println!("c0no:{c0no} c1no:{c1no}");
//self.corner_table.dbg_triangle_context("c0", c0);
//self.corner_table.dbg_triangle_context("c1", c1);
}
// 3. Create new midpoint vertex M between V0 and V2
let pos_v0 = self.vertex(v0).to_simd();
let pos_v2 = self.vertex(v2).to_simd();
let midpoint = (pos_v0 + pos_v2) * (S::ONE / S::TWO);
let vm = self.add_vertex(midpoint);
// 4. Split into 4 triangles:
// - t0 remains: V0, M, V3 (original corners c0, c0n, c0p)
// - t1 remains: M, V0, V1 (original corners c1, c1n, c1p)
// - t3 new: M, V2, V3
// - t2 new: M, V1, V2
let (c3, c3n, c3p) = self.corner_table.allocate_triangle(vm, v2, v3);
let (c2, c2n, c2p) = self.corner_table.allocate_triangle(vm, v1, v2);
// Update vertices: c0n and c1 now point to M instead of V2
self.corner_table.link_corner_vertex(c0n, vm); // t0: V0, M, V3
self.corner_table.link_corner_vertex(c1, vm); // t1: M, V0, V1
// Set opposite relationships
self.corner_table.set_dual_opposite(c0n, c0no); // same
self.corner_table.set_dual_opposite(c1, c1o); // same
self.corner_table.set_dual_opposite(c2, c1no);
self.corner_table.set_dual_opposite(c3, c0o);
self.corner_table.set_dual_opposite(c0, c3n);
self.corner_table.set_dual_opposite(c3p, c2n);
self.corner_table.set_dual_opposite(c2p, c1n);
#[cfg(feature = "integrity_check")]
{
self.corner_table.assert_valid_corner(c0);
self.corner_table.assert_valid_corner(c0n);
self.corner_table.assert_valid_corner(c0p);
self.corner_table.assert_valid_corner(c1);
self.corner_table.assert_valid_corner(c1n);
self.corner_table.assert_valid_corner(c1p);
self.corner_table.assert_valid_corner(c3);
self.corner_table.assert_valid_corner(c3n);
self.corner_table.assert_valid_corner(c3p);
self.corner_table.assert_valid_corner(c2);
self.corner_table.assert_valid_corner(c2n);
self.corner_table.assert_valid_corner(c2p);
println!("after split:");
self.corner_table.dbg_triangle_context("c0 (t0)", c0);
self.corner_table.dbg_triangle_context("c1 (t1)", c1);
self.corner_table.dbg_triangle_context("c3 (t3)", c3);
self.corner_table.dbg_triangle_context("c2 (t2)", c2);
}
}
}