axiolid_arrangement/
edit.rs1use axiolid_core::Point2;
15use axiolid_guarantees::Sign;
16use axiolid_predicates::orient2d;
17
18use crate::entity::{HalfEdge, Vertex};
19use crate::id::{FaceId, HalfEdgeId, VertexId};
20use crate::Arrangement;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[non_exhaustive]
25pub enum EditError {
26 PointNotOnEdge,
28 VerticesNotOnCommonFace,
30 AlreadyConnected,
32 WouldSelfIntersect,
34 BordersUnboundedFace,
36}
37
38impl core::fmt::Display for EditError {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 match self {
41 Self::PointNotOnEdge => write!(f, "split point does not lie on the edge"),
42 Self::VerticesNotOnCommonFace => {
43 write!(f, "vertices do not share a face")
44 }
45 Self::AlreadyConnected => write!(f, "vertices are already connected"),
46 Self::WouldSelfIntersect => {
47 write!(f, "edit would make a face self-intersecting")
48 }
49 Self::BordersUnboundedFace => {
50 write!(f, "half-edge borders the unbounded face")
51 }
52 }
53 }
54}
55
56impl core::error::Error for EditError {}
57
58impl Arrangement {
59 pub fn add_vertex(&mut self, position: Point2) -> VertexId {
64 self.vertices.push(Vertex {
65 position,
66 outgoing: None,
67 });
68 VertexId::from_index(self.vertices.len() - 1)
69 }
70
71 pub fn split_edge(&mut self, edge: HalfEdgeId, at: Point2) -> Result<VertexId, EditError> {
81 let he = self.halfedges[edge.index()];
82 let twin = self.halfedges[he.twin.index()];
83 let from = self.vertices[he.origin.index()].position;
84 let to = self.vertices[twin.origin.index()].position;
85
86 if !matches!(decided(orient2d(from, to, at)), Sign::Zero) {
90 return Err(EditError::PointNotOnEdge);
91 }
92 let within = (at.x - from.x) * (to.x - at.x) + (at.y - from.y) * (to.y - at.y);
93 if within <= 0.0 {
94 return Err(EditError::PointNotOnEdge);
95 }
96
97 let new_vertex = self.add_vertex(at);
98 let a = HalfEdgeId::from_index(self.halfedges.len());
99 let b = HalfEdgeId::from_index(self.halfedges.len() + 1);
100
101 self.halfedges.push(HalfEdge {
103 origin: new_vertex,
104 twin: he.twin,
105 next: he.next,
106 prev: edge,
107 face: he.face,
108 });
109 self.halfedges.push(HalfEdge {
110 origin: new_vertex,
111 twin: edge,
112 next: twin.next,
113 prev: he.twin,
114 face: twin.face,
115 });
116
117 let next_of_edge = he.next;
118 let next_of_twin = twin.next;
119 self.halfedges[edge.index()].next = a;
120 self.halfedges[edge.index()].twin = b;
121 self.halfedges[he.twin.index()].next = b;
122 self.halfedges[he.twin.index()].twin = a;
123 self.halfedges[next_of_edge.index()].prev = a;
124 self.halfedges[next_of_twin.index()].prev = b;
125 self.vertices[new_vertex.index()].outgoing = Some(a);
126 Ok(new_vertex)
127 }
128
129 pub fn drag_vertex(&mut self, vertex: VertexId, to: Point2) -> Result<(), EditError> {
136 let original = self.vertices[vertex.index()].position;
137 self.vertices[vertex.index()].position = to;
138
139 let faces: Vec<FaceId> = self
142 .vertex_halfedges(vertex)
143 .into_iter()
144 .map(|h| self.halfedges[h.index()].face)
145 .collect();
146 for face in faces {
147 if face == FaceId::OUTER {
148 continue;
149 }
150 if !self.face_is_simple(face) {
151 self.vertices[vertex.index()].position = original;
152 return Err(EditError::WouldSelfIntersect);
153 }
154 }
155 Ok(())
156 }
157
158 fn face_is_simple(&self, face: FaceId) -> bool {
160 let outline = self.face_outline(face);
161 if outline.len() < 3 {
162 return false;
163 }
164 let area = self.face_area(face);
168 if area <= 0.0 {
169 return false;
170 }
171 let n = outline.len();
173 for i in 0..n {
174 let (a1, a2) = (outline[i], outline[(i + 1) % n]);
175 for j in (i + 2)..n {
176 if (j + 1) % n == i {
177 continue;
178 }
179 let (b1, b2) = (outline[j], outline[(j + 1) % n]);
180 if segments_cross(a1, a2, b1, b2) {
181 return false;
182 }
183 }
184 }
185 true
186 }
187}
188
189fn decided(certified: axiolid_guarantees::Certified) -> Sign {
191 match certified {
192 axiolid_guarantees::Certified::Certain { sign, .. } => sign,
193 _ => Sign::Zero,
194 }
195}
196
197fn segments_cross(a1: Point2, a2: Point2, b1: Point2, b2: Point2) -> bool {
199 let d1 = decided(orient2d(a1, a2, b1));
200 let d2 = decided(orient2d(a1, a2, b2));
201 let d3 = decided(orient2d(b1, b2, a1));
202 let d4 = decided(orient2d(b1, b2, a2));
203 d1 != d2 && d3 != d4 && d1 != Sign::Zero && d2 != Sign::Zero
204}