geop_core_topology/euler/
replace_face.rs1use crate::{
2 FaceId, Model, argument_validation::validate_pcurve_start_and_end, boundary::BoundaryType,
3};
4use geop_core_geometry::nurb_surface::NurbSurface3D;
5use geop_core_math::{geop_error::GeopResult, scalars::Scalar};
6
7impl<S: Scalar> Model<S> {
8 pub fn replace_face(
15 self: &mut Model<S>,
16 face_id: FaceId,
17 surface: NurbSurface3D<S>,
18 ) -> GeopResult<()> {
19 let face = self.get_face(face_id)?.clone();
20 for boundary in face.boundaries() {
21 let BoundaryType::Loop(anchor) = boundary else {
22 continue;
23 };
24 let mut cursor = anchor;
25 loop {
26 let coedge = self.get_coedge(cursor)?.clone();
27 let start = self.coedge_start_vertex(cursor)?.clone();
28 let end = self.coedge_end_vertex(cursor)?.clone();
29 validate_pcurve_start_and_end(&surface, &coedge.pcurve, &start.point, &end.point)?;
30
31 cursor = coedge.next;
32 if cursor == anchor {
33 break;
34 }
35 }
36 }
37
38 self.get_face_mut(face_id)?.surface = surface;
39 Ok(())
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use crate::{
46 Coedge, CoedgeGeometry, CoedgeId, Edge, Face, Model, Sense, ShellId, Vertex,
47 boundary::BoundaryType,
48 };
49 use geop_core_geometry::{
50 nurb_curve::{NurbCurve, NurbCurve2D, NurbCurve3D},
51 nurb_surface::NurbSurface3D,
52 };
53 use geop_core_math::{
54 for_all_scalars,
55 scalars::Scalar,
56 vector::{Vector3, Vector4},
57 };
58
59 fn line3<S: Scalar>(a: (f64, f64, f64), b: (f64, f64, f64)) -> NurbCurve3D<S> {
60 let p = |x: f64, y: f64, z: f64| {
61 Vector4::from_array([S::from_f64(x), S::from_f64(y), S::from_f64(z), S::ONE])
62 };
63 NurbCurve3D::try_new(
64 1,
65 vec![p(a.0, a.1, a.2), p(b.0, b.1, b.2)],
66 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
67 )
68 .unwrap()
69 }
70
71 fn line2<S: Scalar>(a: (f64, f64), b: (f64, f64)) -> NurbCurve2D<S> {
76 let p = |x: f64, y: f64| Vector3::from_array([S::from_f64(x), S::from_f64(y), S::ONE]);
77 NurbCurve::try_new(
78 1,
79 vec![p(a.0, a.1), p(b.0, b.1)],
80 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
81 )
82 .unwrap()
83 }
84
85 fn unit_square_surface<S: Scalar>() -> NurbSurface3D<S> {
86 let p =
87 |x: f64, y: f64| Vector4::from_array([S::from_f64(x), S::from_f64(y), S::ZERO, S::ONE]);
88 NurbSurface3D::try_new(
89 1,
90 1,
91 vec![p(0.0, 0.0), p(0.0, 1.0), p(1.0, 0.0), p(1.0, 1.0)],
92 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
93 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
94 )
95 .unwrap()
96 }
97
98 fn triangle_face_on_everything<S: Scalar>(model: &mut Model<S>) -> crate::FaceId {
102 let face_id = model.insert_face(Face {
103 surface: NurbSurface3D::everything(),
104 outer: BoundaryType::Vertex(crate::VertexId(0)),
105 holes: Vec::new(),
106 shell: ShellId(999),
107 });
108
109 let points_3d = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)];
110 let points_2d = [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)];
111 let n = points_3d.len();
112 let verts: Vec<_> = points_3d
113 .iter()
114 .map(|&(x, y, z)| {
115 model.insert_vertex(Vertex {
116 point: Vector3::from_array([S::from_f64(x), S::from_f64(y), S::from_f64(z)]),
117 })
118 })
119 .collect();
120 let edges: Vec<_> = (0..n)
121 .map(|i| {
122 model.insert_edge(Edge {
123 curve: line3(points_3d[i], points_3d[(i + 1) % n]),
124 start_vertex: verts[i],
125 end_vertex: verts[(i + 1) % n],
126 })
127 })
128 .collect();
129 let coedges: Vec<_> = (0..n)
130 .map(|i| {
131 model.insert_coedge(Coedge {
132 geometry: CoedgeGeometry::Edge(edges[i]),
133 sense: Sense::Forward,
134 pcurve: line2(points_2d[i], points_2d[(i + 1) % n]),
135 next: CoedgeId(0),
136 prev: CoedgeId(0),
137 face: face_id,
138 })
139 })
140 .collect();
141 for i in 0..n {
142 model.coedges.get_mut(&coedges[i]).unwrap().next = coedges[(i + 1) % n];
143 model.coedges.get_mut(&coedges[i]).unwrap().prev = coedges[(i + n - 1) % n];
144 }
145 model.faces.get_mut(&face_id).unwrap().outer = BoundaryType::Loop(coedges[0]);
146
147 face_id
148 }
149
150 fn check_replace_face_with_matching_surface_succeeds<S: Scalar>() {
151 let mut model = Model::<S>::new();
152 let face_id = triangle_face_on_everything(&mut model);
153 model.replace_face(face_id, unit_square_surface()).unwrap();
154 let p = model
158 .get_face(face_id)
159 .unwrap()
160 .surface
161 .evaluate(S::ONE, S::ZERO)
162 .unwrap();
163 assert!(p.could_be_equal(&Vector3::from_array([S::ONE, S::ZERO, S::ZERO])));
164 }
165 #[test]
166 fn replace_face_with_matching_surface_succeeds() {
167 for_all_scalars!(check_replace_face_with_matching_surface_succeeds);
168 }
169
170 fn check_replace_face_with_mismatched_surface_fails_and_does_not_mutate<S: Scalar>() {
171 let mut model = Model::<S>::new();
172 let face_id = triangle_face_on_everything(&mut model);
173
174 let p = |x: f64, y: f64| {
175 Vector4::from_array([S::from_f64(x), S::from_f64(y), S::from_f64(5.0), S::ONE])
176 };
177 let shifted_surface = NurbSurface3D::try_new(
178 1,
179 1,
180 vec![p(0.0, 0.0), p(0.0, 1.0), p(1.0, 0.0), p(1.0, 1.0)],
181 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
182 vec![S::ZERO, S::ZERO, S::ONE, S::ONE],
183 )
184 .unwrap();
185
186 assert!(model.replace_face(face_id, shifted_surface).is_err());
187 let p_check = model
190 .get_face(face_id)
191 .unwrap()
192 .surface
193 .evaluate(S::ZERO, S::ZERO)
194 .unwrap();
195 assert!(p_check.could_be_equal(&Vector3::from_array([
196 S::from_f64(123.0),
197 S::from_f64(456.0),
198 S::from_f64(789.0)
199 ])));
200 }
201 #[test]
202 fn replace_face_with_mismatched_surface_fails_and_does_not_mutate() {
203 for_all_scalars!(check_replace_face_with_mismatched_surface_fails_and_does_not_mutate);
204 }
205}