1#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
10pub struct ProtocolConfig {
11 pub max_parts: usize,
13 pub max_vertices_per_part: usize,
15 pub min_edge_length_squared: u128,
17 pub min_compactness_ppm: u128,
20 pub area_divisor: u128,
22}
23
24impl ProtocolConfig {
25 pub fn merca() -> Self {
27 Self {
28 max_parts: crate::constants::MAX_PARTS,
29 max_vertices_per_part: crate::constants::MAX_VERTICES_PER_PART,
30 min_edge_length_squared: crate::constants::MIN_EDGE_LENGTH_SQUARED,
31 min_compactness_ppm: crate::constants::MIN_COMPACTNESS_PPM,
32 area_divisor: crate::constants::AREA_DIVISOR,
33 }
34 }
35
36 pub fn permissive() -> Self {
38 Self {
39 max_parts: usize::MAX,
40 max_vertices_per_part: usize::MAX,
41 min_edge_length_squared: 0,
42 min_compactness_ppm: 0,
43 area_divisor: 1,
44 }
45 }
46}
47
48impl Default for ProtocolConfig {
49 fn default() -> Self {
50 Self::permissive()
51 }
52}
53
54#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
55pub enum Strategy {
56 AlreadyConvex,
57 ExactPartition,
58 Bayazit,
59 EarClipMerge,
60 Rotation { offset: usize, inner: Box<Strategy> },
61}
62
63#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
64pub struct Attempt {
65 pub strategy: Strategy,
66 pub rotation: usize,
67 pub outcome: Outcome,
68}
69
70#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
71pub enum Outcome {
72 Success { part_count: usize },
73 TooManyParts { count: usize },
74 ValidationFailed { errors: Vec<String> },
75 AlgorithmFailed { error: String },
76}
77
78#[derive(Clone, Debug)]
80pub struct DecomposeOptions {
81 pub allow_steiner: bool,
84 pub max_rotation_attempts: usize,
86 pub collect_trace: bool,
87 pub minimize_parts: bool,
100}
101
102impl Default for DecomposeOptions {
103 fn default() -> Self {
104 Self {
105 allow_steiner: true,
106 max_rotation_attempts: usize::MAX,
107 collect_trace: false,
108 minimize_parts: false,
109 }
110 }
111}
112
113#[derive(Clone, Debug)]
115pub struct DecomposeResult {
116 pub parts: Vec<Vec<[i64; 2]>>,
118 pub steiner_points: Vec<[i64; 2]>,
121 pub strategy: Strategy,
122 pub trace: Option<Vec<Attempt>>,
123}
124
125#[derive(Clone, Debug, PartialEq, Eq)]
127pub enum DecompError {
128 TooFewVertices,
130 NotSimple,
132 TooManyParts,
134 Failed(String),
136}
137
138impl std::fmt::Display for DecompError {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 match self {
141 DecompError::TooFewVertices => write!(f, "ring has fewer than 3 vertices"),
142 DecompError::NotSimple => write!(f, "ring is self-intersecting"),
143 DecompError::TooManyParts => write!(f, "decomposition exceeds MAX_PARTS"),
144 DecompError::Failed(s) => write!(f, "decomposition failed: {s}"),
145 }
146 }
147}
148
149#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
152pub enum TopologyError {
153 NotConnected { disconnected_parts: Vec<usize> },
156
157 HasHoles { boundary_components: usize },
160
161 VertexOnlyContact { part_a: usize, part_b: usize },
163
164 UnsupportedContact {
166 part_a: usize,
167 part_b: usize,
168 reason: String,
169 },
170
171 TooManyParts { count: usize, max: usize },
173
174 NotCompact {
176 compactness_ppm: u128,
177 min_ppm: u128,
178 },
179}
180
181impl std::fmt::Display for TopologyError {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 match self {
184 TopologyError::NotConnected { disconnected_parts } => {
185 write!(
186 f,
187 "parts are not all connected (disconnected: {disconnected_parts:?})"
188 )
189 }
190 TopologyError::HasHoles {
191 boundary_components,
192 } => {
193 write!(
194 f,
195 "multipart polygon has {boundary_components} boundary components (holes)"
196 )
197 }
198 TopologyError::VertexOnlyContact { part_a, part_b } => {
199 write!(f, "parts {part_a} and {part_b} have only vertex contact")
200 }
201 TopologyError::UnsupportedContact {
202 part_a,
203 part_b,
204 reason,
205 } => {
206 write!(
207 f,
208 "parts {part_a} and {part_b} have unsupported contact: {reason}"
209 )
210 }
211 TopologyError::TooManyParts { count, max } => {
212 write!(f, "too many parts ({count} > max {max})")
213 }
214 TopologyError::NotCompact {
215 compactness_ppm,
216 min_ppm,
217 } => {
218 write!(
219 f,
220 "polygon is not compact enough ({compactness_ppm} ppm < min {min_ppm} ppm)"
221 )
222 }
223 }
224 }
225}