1use serde::{Deserialize, Serialize};
32use std::collections::BTreeMap;
33
34mod nlco;
35
36pub type TmFloat = f64;
38
39const DIST_TOL: TmFloat = 1.0e-4;
40const MIN_EDGE_LENGTH: TmFloat = 0.01;
41const DEPTH_NOT_SET: TmFloat = -999.0;
42const DEGREES: TmFloat = 0.017453292519943296;
43const PI: TmFloat = std::f64::consts::PI;
44const TWO_PI: TmFloat = 2.0 * std::f64::consts::PI;
45const CONVEXITY_TOL: TmFloat = 1.0e-4;
46const MOVE_TOL: TmFloat = 1.0e-6;
47const VERTEX_TOL: TmFloat = 0.003;
48const CREASE_AXIAL: i32 = 0;
49const CREASE_GUSSET: i32 = 1;
50const CREASE_RIDGE: i32 = 2;
51const CREASE_UNFOLDED_HINGE: i32 = 3;
52const CREASE_FOLDED_HINGE: i32 = 4;
53const CREASE_PSEUDOHINGE: i32 = 5;
54const FOLD_FLAT: i32 = 0;
55const FOLD_MOUNTAIN: i32 = 1;
56const FOLD_VALLEY: i32 = 2;
57const FOLD_BORDER: i32 = 3;
58const FACET_NOT_ORIENTED: i32 = 0;
59const FACET_WHITE_UP: i32 = 1;
60const FACET_COLOR_UP: i32 = 2;
61const ROOT_FLAG_INELIGIBLE: i32 = 0;
62const ROOT_FLAG_NOT_YET: i32 = 1;
63const ROOT_FLAG_ALREADY_ADDED: i32 = 2;
64
65#[derive(Debug, thiserror::Error)]
67pub enum TreeError {
68 #[error("parse error at byte {offset}: {message}")]
69 Parse { offset: usize, message: String },
70 #[error("bad {kind} reference index {index}; valid range is 1..={max}")]
71 BadReference {
72 kind: &'static str,
73 index: usize,
74 max: usize,
75 },
76 #[error("unsupported TreeMaker document version {0}")]
77 UnsupportedVersion(String),
78 #[error("unsupported operation: {0}")]
79 UnsupportedOperation(&'static str),
80 #[error("optimizer failed to converge: {0}")]
81 OptimizerConvergence(String),
82 #[error("invalid operation: {0}")]
83 InvalidOperation(&'static str),
84}
85
86impl TreeError {
87 pub fn code(&self) -> &'static str {
89 match self {
90 TreeError::Parse { .. } => "parse",
91 TreeError::BadReference { .. } => "bad_reference",
92 TreeError::UnsupportedVersion(_) => "unsupported_version",
93 TreeError::UnsupportedOperation(_) => "unsupported_operation",
94 TreeError::OptimizerConvergence(_) => "optimizer_convergence",
95 TreeError::InvalidOperation(_) => "invalid_operation",
96 }
97 }
98}
99
100pub type Result<T> = std::result::Result<T, TreeError>;
102
103#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
105pub struct Point {
106 pub x: TmFloat,
107 pub y: TmFloat,
108}
109
110impl Point {
111 pub fn distance(self, other: Self) -> TmFloat {
112 let dx = self.x - other.x;
113 let dy = self.y - other.y;
114 (dx * dx + dy * dy).sqrt()
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
120pub enum OwnerRef {
121 Tree,
122 Node(usize),
123 Path(usize),
124 Poly(usize),
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct Node {
130 pub index: usize,
131 pub label: String,
132 pub loc: Point,
133 pub depth: TmFloat,
134 pub elevation: TmFloat,
135 pub is_leaf: bool,
136 pub is_sub: bool,
137 pub is_border: bool,
138 pub is_pinned: bool,
139 pub is_polygon: bool,
140 pub is_junction: bool,
141 pub is_conditioned: bool,
142 pub owned_vertices: Vec<usize>,
143 pub edges: Vec<usize>,
144 pub leaf_paths: Vec<usize>,
145 pub owner: OwnerRef,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct Edge {
151 pub index: usize,
152 pub label: String,
153 pub length: TmFloat,
154 pub strain: TmFloat,
155 pub stiffness: TmFloat,
156 pub is_pinned: bool,
157 pub is_conditioned: bool,
158 pub nodes: Vec<usize>,
159}
160
161impl Edge {
162 pub fn strained_length(&self) -> TmFloat {
164 self.length * (1.0 + self.strain)
165 }
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct Path {
171 pub index: usize,
172 pub min_tree_length: TmFloat,
173 pub min_paper_length: TmFloat,
174 pub act_tree_length: TmFloat,
175 pub act_paper_length: TmFloat,
176 pub is_leaf: bool,
177 pub is_sub: bool,
178 pub is_feasible: bool,
179 pub is_active: bool,
180 pub is_border: bool,
181 pub is_polygon: bool,
182 pub is_conditioned: bool,
183 pub fwd_poly: Option<usize>,
184 pub bkd_poly: Option<usize>,
185 pub nodes: Vec<usize>,
186 pub edges: Vec<usize>,
187 pub outset_path: Option<usize>,
188 pub front_reduction: TmFloat,
189 pub back_reduction: TmFloat,
190 pub min_depth: TmFloat,
191 pub min_depth_dist: TmFloat,
192 pub owned_vertices: Vec<usize>,
193 pub owned_creases: Vec<usize>,
194 pub owner: OwnerRef,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct Poly {
200 pub index: usize,
201 pub centroid: Point,
202 pub is_sub_poly: bool,
203 pub ring_nodes: Vec<usize>,
204 pub ring_paths: Vec<usize>,
205 pub cross_paths: Vec<usize>,
206 pub inset_nodes: Vec<usize>,
207 pub spoke_paths: Vec<usize>,
208 pub ridge_path: Option<usize>,
209 pub node_locs: Vec<Point>,
210 pub local_root_vertices: Vec<usize>,
211 pub local_root_creases: Vec<usize>,
212 pub owned_nodes: Vec<usize>,
213 pub owned_paths: Vec<usize>,
214 pub owned_polys: Vec<usize>,
215 pub owned_creases: Vec<usize>,
216 pub owned_facets: Vec<usize>,
217 pub owner: OwnerRef,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct Vertex {
223 pub index: usize,
224 pub loc: Point,
225 pub elevation: TmFloat,
226 pub is_border: bool,
227 pub tree_node: Option<usize>,
228 pub left_pseudohinge_mate: Option<usize>,
229 pub right_pseudohinge_mate: Option<usize>,
230 pub creases: Vec<usize>,
231 pub depth: TmFloat,
232 pub discrete_depth: usize,
233 pub cc_flag: i32,
234 pub st_flag: i32,
235 pub owner: OwnerRef,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct Crease {
241 pub index: usize,
242 pub kind: i32,
243 pub vertices: Vec<usize>,
244 pub fwd_facet: Option<usize>,
245 pub bkd_facet: Option<usize>,
246 pub fold: i32,
247 pub cc_flag: i32,
248 pub st_flag: i32,
249 pub owner: OwnerRef,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct Facet {
255 pub index: usize,
256 pub centroid: Point,
257 pub is_well_formed: bool,
258 pub vertices: Vec<usize>,
259 pub creases: Vec<usize>,
260 pub corridor_edge: Option<usize>,
261 pub head_facets: Vec<usize>,
262 pub tail_facets: Vec<usize>,
263 pub order: usize,
264 pub color: i32,
265 pub owner: OwnerRef,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct Condition {
271 pub index: usize,
272 pub is_feasible: bool,
273 pub kind: ConditionKind,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
278#[serde(tag = "type", rename_all = "snake_case")]
279pub enum ConditionKind {
280 NodeCombo {
281 node: usize,
282 to_symmetry_line: bool,
283 to_paper_edge: bool,
284 to_paper_corner: bool,
285 x_fixed: bool,
286 x_fix_value: TmFloat,
287 y_fixed: bool,
288 y_fix_value: TmFloat,
289 },
290 NodeFixed {
291 node: usize,
292 x_fixed: bool,
293 y_fixed: bool,
294 x_fix_value: TmFloat,
295 y_fix_value: TmFloat,
296 },
297 NodeOnCorner {
298 node: usize,
299 },
300 NodeOnEdge {
301 node: usize,
302 },
303 NodeSymmetric {
304 node: usize,
305 },
306 NodesPaired {
307 node1: usize,
308 node2: usize,
309 },
310 NodesCollinear {
311 node1: usize,
312 node2: usize,
313 node3: usize,
314 },
315 EdgeLengthFixed {
316 edge: usize,
317 },
318 EdgesSameStrain {
319 edge1: usize,
320 edge2: usize,
321 },
322 PathCombo {
323 node1: usize,
324 node2: usize,
325 is_angle_fixed: bool,
326 angle: TmFloat,
327 is_angle_quant: bool,
328 quant: usize,
329 quant_offset: TmFloat,
330 },
331 PathActive {
332 node1: usize,
333 node2: usize,
334 },
335 PathAngleFixed {
336 node1: usize,
337 node2: usize,
338 angle: TmFloat,
339 },
340 PathAngleQuant {
341 node1: usize,
342 node2: usize,
343 quant: usize,
344 quant_offset: TmFloat,
345 },
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct Tree {
351 pub source_version: String,
352 pub paper_width: TmFloat,
353 pub paper_height: TmFloat,
354 pub scale: TmFloat,
355 pub has_symmetry: bool,
356 pub sym_loc: Point,
357 pub sym_angle: TmFloat,
358 pub is_feasible: bool,
359 pub is_polygon_valid: bool,
360 pub is_polygon_filled: bool,
361 pub is_vertex_depth_valid: bool,
362 pub is_facet_data_valid: bool,
363 pub is_local_root_connectable: bool,
364 pub needs_cleanup: bool,
365 pub nodes: Vec<Node>,
366 pub edges: Vec<Edge>,
367 pub paths: Vec<Path>,
368 pub polys: Vec<Poly>,
369 pub vertices: Vec<Vertex>,
370 pub creases: Vec<Crease>,
371 pub facets: Vec<Facet>,
372 pub conditions: Vec<Condition>,
373 pub owned_nodes: Vec<usize>,
374 pub owned_edges: Vec<usize>,
375 pub owned_paths: Vec<usize>,
376 pub owned_polys: Vec<usize>,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
381#[serde(rename_all = "snake_case")]
382pub enum CPStatus {
383 HasFullCp,
384 EdgesTooShort,
385 PolysNotValid,
386 PolysNotFilled,
387 PolysMultipleIbps,
388 VerticesLackDepth,
389 FacetsNotValid,
390 NotLocalRootConnectable,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
395pub struct CPStatusReport {
396 pub status: CPStatus,
397 pub bad_edges: Vec<usize>,
398 pub bad_polys: Vec<usize>,
399 pub bad_vertices: Vec<usize>,
400 pub bad_creases: Vec<usize>,
401 pub bad_facets: Vec<usize>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
406pub struct TreeSummary {
407 pub source_version: String,
408 pub paper_width: TmFloat,
409 pub paper_height: TmFloat,
410 pub scale: TmFloat,
411 pub has_symmetry: bool,
412 pub is_feasible: bool,
413 pub cp_status: CPStatus,
414 pub nodes: usize,
415 pub edges: usize,
416 pub paths: usize,
417 pub polys: usize,
418 pub vertices: usize,
419 pub creases: usize,
420 pub facets: usize,
421 pub conditions: usize,
422 pub leaf_nodes: usize,
423 pub leaf_paths: usize,
424 pub feasible_paths: usize,
425 pub active_paths: usize,
426 pub border_nodes: usize,
427 pub border_paths: usize,
428 pub polygon_nodes: usize,
429 pub polygon_paths: usize,
430 pub pinned_nodes: usize,
431 pub pinned_edges: usize,
432 pub conditioned_nodes: usize,
433 pub conditioned_edges: usize,
434 pub conditioned_paths: usize,
435 pub conditions_by_tag: BTreeMap<String, usize>,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
440pub struct OptimizationReport {
441 pub kind: OptimizationKind,
442 pub converged: bool,
443 pub old_scale: TmFloat,
444 pub new_scale: TmFloat,
445 pub is_feasible: bool,
446 pub message: String,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
451#[serde(rename_all = "snake_case")]
452pub enum OptimizationKind {
453 Scale,
454 Edge,
455 Strain,
456}
457
458#[derive(Debug, Clone)]
459struct RootNetwork {
460 discrete_depth: usize,
461 is_connectable: bool,
462 cc_vertices: Vec<usize>,
463 cc_creases: Vec<usize>,
464 cc_polys: Vec<usize>,
465 st_vertices: Vec<usize>,
466 st_creases: Vec<usize>,
467 cc0: Vec<usize>,
468 cc1: Vec<usize>,
469 cc2_st1: Vec<usize>,
470 cc2_st2: Vec<usize>,
471}
472
473impl RootNetwork {
474 fn new(discrete_depth: usize) -> Self {
475 Self {
476 discrete_depth,
477 is_connectable: false,
478 cc_vertices: Vec::new(),
479 cc_creases: Vec::new(),
480 cc_polys: Vec::new(),
481 st_vertices: Vec::new(),
482 st_creases: Vec::new(),
483 cc0: Vec::new(),
484 cc1: Vec::new(),
485 cc2_st1: Vec::new(),
486 cc2_st2: Vec::new(),
487 }
488 }
489}
490
491impl CPStatusReport {
492 fn new(status: CPStatus) -> Self {
493 Self {
494 status,
495 bad_edges: Vec::new(),
496 bad_polys: Vec::new(),
497 bad_vertices: Vec::new(),
498 bad_creases: Vec::new(),
499 bad_facets: Vec::new(),
500 }
501 }
502
503 fn with_bad_edges(mut self, bad_edges: Vec<usize>) -> Self {
504 self.bad_edges = bad_edges;
505 self
506 }
507
508 fn with_bad_polys(mut self, bad_polys: Vec<usize>) -> Self {
509 self.bad_polys = bad_polys;
510 self
511 }
512
513 fn with_bad_vertices(mut self, bad_vertices: Vec<usize>) -> Self {
514 self.bad_vertices = bad_vertices;
515 self
516 }
517
518 fn with_bad_creases(mut self, bad_creases: Vec<usize>) -> Self {
519 self.bad_creases = bad_creases;
520 self
521 }
522
523 fn with_bad_facets(mut self, bad_facets: Vec<usize>) -> Self {
524 self.bad_facets = bad_facets;
525 self
526 }
527}
528
529impl Tree {
530 pub fn from_tmd_str(input: &str) -> Result<Self> {
532 let mut reader = Reader::new(input);
533 reader.expect_tag("tree")?;
534 let version = reader.read_token("version")?;
535 let tree = match version.as_str() {
536 "4.0" => {
537 let mut tree = Self::read_v4(&mut reader, version)?;
538 tree.validate()?;
539 tree.cleanup_after_edit();
540 tree
541 }
542 "5.0" => {
543 let tree = Self::read_v5(&mut reader, version)?;
544 tree.validate()?;
545 tree
546 }
547 "3.0" => {
548 let mut tree = Self::read_v3(&mut reader, version)?;
549 tree.validate()?;
550 tree.cleanup_after_edit();
551 tree
552 }
553 _ => return Err(TreeError::UnsupportedVersion(version)),
554 };
555 Ok(tree)
556 }
557
558 pub fn to_tmd5_string(&self) -> String {
560 let mut out = Writer::new(10, "\n");
561 out.s("tree");
562 out.s("5.0");
563 out.f(self.paper_width);
564 out.f(self.paper_height);
565 out.f(self.scale);
566 out.b(self.has_symmetry);
567 out.point(self.sym_loc);
568 out.f(self.sym_angle);
569 out.b(self.is_feasible);
570 out.b(self.is_polygon_valid);
571 out.b(self.is_polygon_filled);
572 out.b(self.is_vertex_depth_valid);
573 out.b(self.is_facet_data_valid);
574 out.b(self.is_local_root_connectable);
575 out.b(self.needs_cleanup);
576 out.u(self.nodes.len());
577 out.u(self.edges.len());
578 out.u(self.paths.len());
579 out.u(self.polys.len());
580 out.u(self.vertices.len());
581 out.u(self.creases.len());
582 out.u(self.facets.len());
583 out.u(self.conditions.len());
584
585 for node in &self.nodes {
586 out.node_v5(node);
587 }
588 for edge in &self.edges {
589 out.edge(edge);
590 }
591 for path in &self.paths {
592 out.path_v5(path);
593 }
594 for poly in &self.polys {
595 out.poly_v5(poly);
596 }
597 for vertex in &self.vertices {
598 out.vertex_v5(vertex);
599 }
600 for crease in &self.creases {
601 out.crease_v5(crease);
602 }
603 for facet in &self.facets {
604 out.facet_v5(facet);
605 }
606 for condition in &self.conditions {
607 out.condition_v5(condition);
608 }
609 out.array(&self.owned_nodes);
610 out.array(&self.owned_edges);
611 out.array(&self.owned_paths);
612 out.array(&self.owned_polys);
613 out.finish()
614 }
615
616 pub fn export_v4_string(&self) -> String {
618 let mut out = Writer::new(6, "\r");
619 out.s("tree");
620 out.s("4.0");
621 out.f(self.paper_width);
622 out.f(self.paper_height);
623 out.f(self.scale);
624 out.b(self.has_symmetry);
625 out.point(self.sym_loc);
626 out.f(self.sym_angle);
627 out.u(self.nodes.len());
628 out.u(self.edges.len());
629 out.u(self.paths.len());
630 out.u(0);
631 out.u(0);
632 out.u(0);
633 out.u(self.conditions.len());
634 for node in &self.nodes {
635 out.node_v4(node);
636 }
637 for edge in &self.edges {
638 out.edge(edge);
639 }
640 for path in &self.paths {
641 out.path_v4(path);
642 }
643 for condition in &self.conditions {
644 out.condition_v4(condition);
645 }
646 out.array(&self.owned_nodes);
647 out.array(&self.owned_edges);
648 out.array(&self.owned_paths);
649 out.u(0);
650 out.finish()
651 }
652
653 pub fn summary(&self) -> TreeSummary {
655 let mut conditions_by_tag = BTreeMap::new();
656 for condition in &self.conditions {
657 *conditions_by_tag
658 .entry(condition.kind.tag().to_string())
659 .or_insert(0) += 1;
660 }
661 TreeSummary {
662 source_version: self.source_version.clone(),
663 paper_width: self.paper_width,
664 paper_height: self.paper_height,
665 scale: self.scale,
666 has_symmetry: self.has_symmetry,
667 is_feasible: self.is_feasible,
668 cp_status: self.cp_status(),
669 nodes: self.nodes.len(),
670 edges: self.edges.len(),
671 paths: self.paths.len(),
672 polys: self.polys.len(),
673 vertices: self.vertices.len(),
674 creases: self.creases.len(),
675 facets: self.facets.len(),
676 conditions: self.conditions.len(),
677 leaf_nodes: self.nodes.iter().filter(|n| n.is_leaf).count(),
678 leaf_paths: self.paths.iter().filter(|p| p.is_leaf).count(),
679 feasible_paths: self.paths.iter().filter(|p| p.is_feasible).count(),
680 active_paths: self.paths.iter().filter(|p| p.is_active).count(),
681 border_nodes: self.nodes.iter().filter(|n| n.is_border).count(),
682 border_paths: self.paths.iter().filter(|p| p.is_border).count(),
683 polygon_nodes: self.nodes.iter().filter(|n| n.is_polygon).count(),
684 polygon_paths: self.paths.iter().filter(|p| p.is_polygon).count(),
685 pinned_nodes: self.nodes.iter().filter(|n| n.is_pinned).count(),
686 pinned_edges: self.edges.iter().filter(|e| e.is_pinned).count(),
687 conditioned_nodes: self.nodes.iter().filter(|n| n.is_conditioned).count(),
688 conditioned_edges: self.edges.iter().filter(|e| e.is_conditioned).count(),
689 conditioned_paths: self.paths.iter().filter(|p| p.is_conditioned).count(),
690 conditions_by_tag,
691 }
692 }
693
694 pub fn is_feasible(&self) -> bool {
696 self.is_feasible
697 }
698
699 pub fn cp_status(&self) -> CPStatus {
701 if self
702 .edges
703 .iter()
704 .any(|edge| edge.strained_length() < MIN_EDGE_LENGTH)
705 {
706 return CPStatus::EdgesTooShort;
707 }
708 if !self.is_polygon_valid {
709 return CPStatus::PolysNotValid;
710 }
711 if !self.is_polygon_filled {
712 return CPStatus::PolysNotFilled;
713 }
714 if self.owned_polys.iter().copied().any(|poly_id| {
715 self.polys[poly_id - 1]
716 .ring_paths
717 .iter()
718 .filter(|path_id| !self.paths[**path_id - 1].is_active)
719 .count()
720 > 1
721 }) {
722 return CPStatus::PolysMultipleIbps;
723 }
724 if !self.is_vertex_depth_valid {
725 return CPStatus::VerticesLackDepth;
726 }
727 if !self.is_facet_data_valid {
728 return CPStatus::FacetsNotValid;
729 }
730 if !self.is_local_root_connectable {
731 return CPStatus::NotLocalRootConnectable;
732 }
733 CPStatus::HasFullCp
734 }
735
736 pub fn cp_status_report(&self) -> CPStatusReport {
738 let bad_edges: Vec<_> = self
739 .edges
740 .iter()
741 .filter(|edge| edge.strained_length() < MIN_EDGE_LENGTH)
742 .map(|edge| edge.index)
743 .collect();
744 if !bad_edges.is_empty() {
745 return CPStatusReport::new(CPStatus::EdgesTooShort).with_bad_edges(bad_edges);
746 }
747
748 if !self.is_polygon_valid {
749 return CPStatusReport::new(CPStatus::PolysNotValid);
750 }
751
752 if !self.is_polygon_filled {
753 let bad_polys = self
754 .owned_polys
755 .iter()
756 .copied()
757 .filter(|poly_id| self.polys[*poly_id - 1].owned_nodes.is_empty())
758 .collect();
759 return CPStatusReport::new(CPStatus::PolysNotFilled).with_bad_polys(bad_polys);
760 }
761
762 let bad_polys: Vec<_> = self
763 .owned_polys
764 .iter()
765 .copied()
766 .filter(|poly_id| {
767 self.polys[*poly_id - 1]
768 .ring_paths
769 .iter()
770 .filter(|path_id| !self.paths[**path_id - 1].is_active)
771 .count()
772 > 1
773 })
774 .collect();
775 if !bad_polys.is_empty() {
776 return CPStatusReport::new(CPStatus::PolysMultipleIbps).with_bad_polys(bad_polys);
777 }
778
779 if !self.is_vertex_depth_valid {
780 let bad_vertices = self
781 .vertices
782 .iter()
783 .filter(|vertex| vertex.depth == DEPTH_NOT_SET)
784 .map(|vertex| vertex.index)
785 .collect();
786 return CPStatusReport::new(CPStatus::VerticesLackDepth)
787 .with_bad_vertices(bad_vertices);
788 }
789
790 if !self.is_facet_data_valid {
791 let bad_vertices = self
792 .vertices
793 .iter()
794 .filter(|vertex| !vertex.is_border && vertex.creases.len() % 2 != 0)
795 .map(|vertex| vertex.index)
796 .collect();
797 let bad_facets = self
798 .facets
799 .iter()
800 .filter(|facet| !facet.is_well_formed)
801 .map(|facet| facet.index)
802 .collect();
803 return CPStatusReport::new(CPStatus::FacetsNotValid)
804 .with_bad_vertices(bad_vertices)
805 .with_bad_facets(bad_facets);
806 }
807
808 if !self.is_local_root_connectable {
809 let (bad_vertices, bad_creases) = self.why_not_local_root_connectable();
810 return CPStatusReport::new(CPStatus::NotLocalRootConnectable)
811 .with_bad_vertices(bad_vertices)
812 .with_bad_creases(bad_creases);
813 }
814
815 CPStatusReport::new(CPStatus::HasFullCp)
816 }
817
818 pub fn optimize_scale(&mut self) -> Result<OptimizationReport> {
820 let old_scale = self.scale;
821 let leaf_nodes = self.leaf_nodes_in_owned_order();
822 let num_vars = 1 + 2 * leaf_nodes.len();
823
824 let mut state = vec![0.0; num_vars];
825 state[0] = self.scale;
826 let mut node_offsets = vec![None; self.nodes.len() + 1];
827 for (i, node_id) in leaf_nodes.iter().copied().enumerate() {
828 let offset = 1 + 2 * i;
829 node_offsets[node_id] = Some(offset);
830 let loc = self.nodes[node_id - 1].loc;
831 state[offset] = loc.x;
832 state[offset + 1] = loc.y;
833 }
834
835 let mut optimizer = nlco::NlcoAlm::new(num_vars);
836 let mut lower_bounds = vec![0.0; num_vars];
837 let mut upper_bounds = vec![0.0; num_vars];
838 upper_bounds[0] = 2.0;
839 for i in 0..leaf_nodes.len() {
840 lower_bounds[1 + 2 * i] = 0.0;
841 lower_bounds[2 + 2 * i] = 0.0;
842 upper_bounds[1 + 2 * i] = self.paper_width;
843 upper_bounds[2 + 2 * i] = self.paper_height;
844 }
845 optimizer.set_bounds(lower_bounds, upper_bounds);
846 optimizer.set_objective(Box::new(ScaleObjective));
847 optimizer.add_linear_inequality(Box::new(nlco::OneVarFn::new(0, -1.0, 0.1 * self.scale)));
848
849 for path_id in &self.owned_paths {
850 let path = &self.paths[*path_id - 1];
851 if !path.is_leaf || self.has_path_active_base_condition(path) {
852 continue;
853 }
854 self.add_scale_path_constraint(
855 &mut optimizer,
856 &node_offsets,
857 path.nodes[0],
858 *path.nodes.last().unwrap(),
859 path.min_tree_length,
860 false,
861 );
862 }
863
864 for condition in &self.conditions {
865 self.add_scale_condition_constraints(&mut optimizer, &node_offsets, &condition.kind);
866 }
867
868 let inform = optimizer.minimize(&mut state);
869 if inform != 0 {
870 return Err(TreeError::OptimizerConvergence(format!(
871 "ALM returned result code {inform}"
872 )));
873 }
874
875 self.scale = state[0];
876 for (i, node_id) in leaf_nodes.into_iter().enumerate() {
877 let offset = 1 + 2 * i;
878 self.nodes[node_id - 1].loc = Point {
879 x: state[offset],
880 y: state[offset + 1],
881 };
882 }
883 self.cleanup_after_edit();
884
885 Ok(OptimizationReport {
886 kind: OptimizationKind::Scale,
887 converged: true,
888 old_scale,
889 new_scale: self.scale,
890 is_feasible: self.is_feasible,
891 message: "ALM scale optimization converged".to_string(),
892 })
893 }
894
895 pub fn optimize_edges(&mut self) -> Result<OptimizationReport> {
897 let old_scale = self.scale;
898 let moving_nodes = self.moving_nodes_for_edge_optimizer();
899 let stretchy_edges = self.stretchy_edges_for_edge_optimizer();
900 if moving_nodes.is_empty() {
901 return Err(TreeError::InvalidOperation(
902 "edge optimization has no moving leaf nodes",
903 ));
904 }
905 if stretchy_edges.is_empty() {
906 return Err(TreeError::InvalidOperation(
907 "edge optimization has no stretchy edges",
908 ));
909 }
910
911 let num_vars = 1 + 2 * moving_nodes.len();
912 let mut state = vec![0.0; num_vars];
913 let mut node_offsets = vec![None; self.nodes.len() + 1];
914 for (i, node_id) in moving_nodes.iter().copied().enumerate() {
915 let offset = 1 + 2 * i;
916 node_offsets[node_id] = Some(offset);
917 let loc = self.nodes[node_id - 1].loc;
918 state[offset] = loc.x;
919 state[offset + 1] = loc.y;
920 }
921 let stretchy_lookup = self.edge_lookup(&stretchy_edges);
922
923 let mut optimizer = nlco::NlcoAlm::new(num_vars);
924 let mut lower_bounds = vec![0.0; num_vars];
925 let mut upper_bounds = vec![0.0; num_vars];
926 lower_bounds[0] = -0.999;
927 upper_bounds[0] = 10.0;
928 for i in 0..moving_nodes.len() {
929 upper_bounds[1 + 2 * i] = self.paper_width;
930 upper_bounds[2 + 2 * i] = self.paper_height;
931 }
932 optimizer.set_bounds(lower_bounds, upper_bounds);
933 optimizer.set_objective(Box::new(ScaleObjective));
934
935 for path_id in &self.owned_paths {
936 let path = &self.paths[*path_id - 1];
937 if !path.is_leaf || self.has_path_active_base_condition(path) {
938 continue;
939 }
940 self.add_edge_path_constraint(
941 &mut optimizer,
942 &node_offsets,
943 &stretchy_lookup,
944 path,
945 false,
946 );
947 }
948 for condition in &self.conditions {
949 self.add_edge_condition_constraints(
950 &mut optimizer,
951 &node_offsets,
952 &stretchy_lookup,
953 &condition.kind,
954 );
955 }
956
957 let inform = optimizer.minimize(&mut state);
958 if inform != 0 {
959 return Err(TreeError::OptimizerConvergence(format!(
960 "ALM returned result code {inform}"
961 )));
962 }
963
964 for (i, node_id) in moving_nodes.into_iter().enumerate() {
965 let offset = 1 + 2 * i;
966 self.nodes[node_id - 1].loc = Point {
967 x: state[offset],
968 y: state[offset + 1],
969 };
970 }
971 for edge_id in stretchy_edges {
972 self.edges[edge_id - 1].strain = state[0];
973 }
974 self.cleanup_after_edit();
975
976 Ok(OptimizationReport {
977 kind: OptimizationKind::Edge,
978 converged: true,
979 old_scale,
980 new_scale: self.scale,
981 is_feasible: self.is_feasible,
982 message: "ALM edge strain optimization converged".to_string(),
983 })
984 }
985
986 pub fn optimize_strain(&mut self) -> Result<OptimizationReport> {
988 let old_scale = self.scale;
989 let moving_nodes = self.moving_nodes_for_strain_optimizer();
990 let stretchy_edges = self.owned_edges.clone();
991 if moving_nodes.is_empty() && stretchy_edges.is_empty() {
992 return Err(TreeError::InvalidOperation(
993 "strain optimization has no moving nodes or edges",
994 ));
995 }
996
997 let edge_offset = 2 * moving_nodes.len();
998 let num_vars = edge_offset + stretchy_edges.len();
999 let mut state = vec![0.0; num_vars];
1000 let mut node_offsets = vec![None; self.nodes.len() + 1];
1001 let mut edge_offsets = vec![None; self.edges.len() + 1];
1002
1003 for (i, node_id) in moving_nodes.iter().copied().enumerate() {
1004 let offset = 2 * i;
1005 node_offsets[node_id] = Some(offset);
1006 let loc = self.nodes[node_id - 1].loc;
1007 state[offset] = loc.x;
1008 state[offset + 1] = loc.y;
1009 }
1010 let mut stiffness = Vec::with_capacity(stretchy_edges.len());
1011 for (i, edge_id) in stretchy_edges.iter().copied().enumerate() {
1012 let offset = edge_offset + i;
1013 edge_offsets[edge_id] = Some(offset);
1014 state[offset] = self.edges[edge_id - 1].strain;
1015 let edge_stiffness = self.edges[edge_id - 1].stiffness;
1016 stiffness.push(if edge_stiffness <= 0.0 {
1017 1.0
1018 } else {
1019 edge_stiffness
1020 });
1021 }
1022
1023 let mut optimizer = nlco::NlcoAlm::new(num_vars);
1024 let mut lower_bounds = vec![0.0; num_vars];
1025 let mut upper_bounds = vec![0.0; num_vars];
1026 for i in 0..moving_nodes.len() {
1027 upper_bounds[2 * i] = self.paper_width;
1028 upper_bounds[2 * i + 1] = self.paper_height;
1029 }
1030 for i in edge_offset..num_vars {
1031 lower_bounds[i] = -0.999;
1032 upper_bounds[i] = 2.0;
1033 }
1034 optimizer.set_bounds(lower_bounds, upper_bounds);
1035 optimizer.set_objective(Box::new(StrainObjective {
1036 edge_offset,
1037 stiffness,
1038 }));
1039
1040 for path_id in &self.owned_paths {
1041 let path = &self.paths[*path_id - 1];
1042 if !path.is_leaf || self.has_path_active_base_condition(path) {
1043 continue;
1044 }
1045 self.add_strain_path_constraint(
1046 &mut optimizer,
1047 &node_offsets,
1048 &edge_offsets,
1049 path,
1050 false,
1051 );
1052 }
1053 for condition in &self.conditions {
1054 self.add_strain_condition_constraints(
1055 &mut optimizer,
1056 &node_offsets,
1057 &edge_offsets,
1058 &condition.kind,
1059 );
1060 }
1061
1062 let inform = optimizer.minimize(&mut state);
1063 if inform != 0 {
1064 return Err(TreeError::OptimizerConvergence(format!(
1065 "ALM returned result code {inform}"
1066 )));
1067 }
1068
1069 for (i, node_id) in moving_nodes.into_iter().enumerate() {
1070 let offset = 2 * i;
1071 self.nodes[node_id - 1].loc = Point {
1072 x: state[offset],
1073 y: state[offset + 1],
1074 };
1075 }
1076 for (i, edge_id) in stretchy_edges.into_iter().enumerate() {
1077 self.edges[edge_id - 1].strain = state[edge_offset + i];
1078 }
1079 self.cleanup_after_edit();
1080
1081 Ok(OptimizationReport {
1082 kind: OptimizationKind::Strain,
1083 converged: true,
1084 old_scale,
1085 new_scale: self.scale,
1086 is_feasible: self.is_feasible,
1087 message: "ALM strain optimization converged".to_string(),
1088 })
1089 }
1090
1091 pub fn build_tree_polys(&mut self) -> Result<()> {
1093 let leaf_paths = self.leaf_paths_in_owned_order();
1094 let border_nodes: Vec<usize> = self
1095 .owned_nodes
1096 .iter()
1097 .copied()
1098 .filter(|id| self.nodes[*id - 1].is_border)
1099 .collect();
1100 self.build_polys_from_paths(&leaf_paths, &border_nodes, OwnerRef::Tree)?;
1101
1102 let leaf_nodes = self.leaf_nodes_in_owned_order();
1103 let doomed: Vec<usize> = self
1104 .owned_polys
1105 .iter()
1106 .copied()
1107 .filter(|poly_id| {
1108 let Some(poly) = self.polys.get(poly_id.saturating_sub(1)) else {
1109 return true;
1110 };
1111 !self.poly_is_convex(poly) || self.poly_encloses_leaf_node(poly, &leaf_nodes)
1112 })
1113 .collect();
1114 self.delete_polys(&doomed);
1115 self.cleanup_after_edit();
1116 Ok(())
1117 }
1118
1119 pub fn build_polys_and_crease_pattern(&mut self) -> Result<()> {
1121 self.build_tree_polys()?;
1122 if self
1123 .edges
1124 .iter()
1125 .any(|edge| edge.strained_length() < MIN_EDGE_LENGTH)
1126 {
1127 return Ok(());
1128 }
1129
1130 let owned_polys = self.owned_polys.clone();
1131 for poly_id in owned_polys {
1132 self.build_poly_contents_geometry(poly_id)?;
1133 }
1134 self.cleanup_after_edit();
1135 Ok(())
1136 }
1137
1138 #[doc(hidden)]
1139 #[doc(hidden)]
1140 pub fn build_polygon_contents_for_oracle_tests(&mut self) -> Result<()> {
1141 self.build_tree_polys()?;
1142 if self
1143 .edges
1144 .iter()
1145 .any(|edge| edge.strained_length() < MIN_EDGE_LENGTH)
1146 {
1147 return Ok(());
1148 }
1149
1150 let owned_polys = self.owned_polys.clone();
1151 for poly_id in owned_polys {
1152 self.build_poly_contents_geometry(poly_id)?;
1153 }
1154 self.cleanup_after_edit();
1155 Ok(())
1156 }
1157
1158 fn read_v3(reader: &mut Reader<'_>, source_version: String) -> Result<Self> {
1159 let paper_width = reader.read_f64("paper width")?;
1160 let paper_height = reader.read_f64("paper height")?;
1161 let scale = reader.read_f64("scale")?;
1162 let has_symmetry = reader.read_bool("has symmetry")?;
1163 let sym_loc = reader.read_point("symmetry location")?;
1164 let sym_angle = reader.read_f64("symmetry angle")?;
1165 let num_nodes = reader.read_usize("node count")?;
1166 let num_edges = reader.read_usize("edge count")?;
1167 let num_paths = reader.read_usize("path count")?;
1168 let _num_polys = reader.read_usize("poly count")?;
1169
1170 let mut nodes = Vec::with_capacity(num_nodes);
1171 let mut edges = Vec::with_capacity(num_edges);
1172 let mut paths = Vec::with_capacity(num_paths);
1173 let mut conditions = Vec::new();
1174
1175 for _ in 0..num_nodes {
1176 nodes.push(reader.read_node_v3(&mut conditions)?);
1177 }
1178 for _ in 0..num_edges {
1179 edges.push(reader.read_edge_v3()?);
1180 }
1181 for _ in 0..num_paths {
1182 paths.push(reader.read_path_v3(&mut conditions)?);
1183 }
1184
1185 Ok(Self {
1186 source_version,
1187 paper_width,
1188 paper_height,
1189 scale,
1190 has_symmetry,
1191 sym_loc,
1192 sym_angle,
1193 is_feasible: false,
1194 is_polygon_valid: false,
1195 is_polygon_filled: false,
1196 is_vertex_depth_valid: false,
1197 is_facet_data_valid: false,
1198 is_local_root_connectable: false,
1199 needs_cleanup: false,
1200 nodes,
1201 edges,
1202 paths,
1203 polys: Vec::new(),
1204 vertices: Vec::new(),
1205 creases: Vec::new(),
1206 facets: Vec::new(),
1207 conditions,
1208 owned_nodes: (1..=num_nodes).collect(),
1209 owned_edges: (1..=num_edges).collect(),
1210 owned_paths: (1..=num_paths).collect(),
1211 owned_polys: Vec::new(),
1212 })
1213 }
1214
1215 fn read_v4(reader: &mut Reader<'_>, source_version: String) -> Result<Self> {
1216 let paper_width = reader.read_f64("paper width")?;
1217 let paper_height = reader.read_f64("paper height")?;
1218 let scale = reader.read_f64("scale")?;
1219 let has_symmetry = reader.read_bool("has symmetry")?;
1220 let sym_loc = reader.read_point("symmetry location")?;
1221 let sym_angle = reader.read_f64("symmetry angle")?;
1222 let num_nodes = reader.read_usize("node count")?;
1223 let num_edges = reader.read_usize("edge count")?;
1224 let num_paths = reader.read_usize("path count")?;
1225 let num_polys = reader.read_usize("poly count")?;
1226 let num_vertices = reader.read_usize("vertex count")?;
1227 let num_creases = reader.read_usize("crease count")?;
1228 let num_conditions = reader.read_usize("condition count")?;
1229
1230 let mut nodes = Vec::with_capacity(num_nodes);
1231 let mut edges = Vec::with_capacity(num_edges);
1232 let mut paths = Vec::with_capacity(num_paths);
1233 let mut polys = Vec::with_capacity(num_polys);
1234 let mut vertices = Vec::with_capacity(num_vertices);
1235 let mut creases = Vec::with_capacity(num_creases);
1236
1237 for _ in 0..num_nodes {
1238 nodes.push(reader.read_node_v4()?);
1239 }
1240 for _ in 0..num_edges {
1241 edges.push(reader.read_edge(true)?);
1242 }
1243 for _ in 0..num_paths {
1244 paths.push(reader.read_path_v4(num_polys)?);
1245 }
1246 for _ in 0..num_polys {
1247 polys.push(reader.read_poly_v4(num_paths)?);
1248 }
1249 for index in 1..=num_vertices {
1250 vertices.push(reader.read_vertex_v4(index)?);
1251 }
1252 for index in 1..=num_creases {
1253 creases.push(reader.read_crease_v4(index)?);
1254 }
1255
1256 let mut conditions = Vec::with_capacity(num_conditions);
1257 for i in 0..num_conditions {
1258 conditions.push(reader.read_condition_v4(i + 1)?);
1259 }
1260
1261 let owned_nodes = reader.read_index_array("owned nodes")?;
1262 let owned_edges = reader.read_index_array("owned edges")?;
1263 let owned_paths = reader.read_index_array("owned paths")?;
1264 let _owned_polys = reader.read_index_array("owned polys")?;
1265
1266 kill_v4_crease_pattern_refs(&mut nodes, &mut paths);
1267
1268 Ok(Self {
1269 source_version,
1270 paper_width,
1271 paper_height,
1272 scale,
1273 has_symmetry,
1274 sym_loc,
1275 sym_angle,
1276 is_feasible: false,
1277 is_polygon_valid: false,
1278 is_polygon_filled: false,
1279 is_vertex_depth_valid: false,
1280 is_facet_data_valid: false,
1281 is_local_root_connectable: false,
1282 needs_cleanup: false,
1283 nodes,
1284 edges,
1285 paths,
1286 polys: Vec::new(),
1287 vertices: Vec::new(),
1288 creases: Vec::new(),
1289 facets: Vec::new(),
1290 conditions,
1291 owned_nodes,
1292 owned_edges,
1293 owned_paths,
1294 owned_polys: Vec::new(),
1295 })
1296 }
1297
1298 fn read_v5(reader: &mut Reader<'_>, source_version: String) -> Result<Self> {
1299 let paper_width = reader.read_f64("paper width")?;
1300 let paper_height = reader.read_f64("paper height")?;
1301 let scale = reader.read_f64("scale")?;
1302 let has_symmetry = reader.read_bool("has symmetry")?;
1303 let sym_loc = reader.read_point("symmetry location")?;
1304 let sym_angle = reader.read_f64("symmetry angle")?;
1305 let is_feasible = reader.read_bool("feasible flag")?;
1306 let is_polygon_valid = reader.read_bool("polygon valid flag")?;
1307 let is_polygon_filled = reader.read_bool("polygon filled flag")?;
1308 let is_vertex_depth_valid = reader.read_bool("vertex depth valid flag")?;
1309 let is_facet_data_valid = reader.read_bool("facet data valid flag")?;
1310 let is_local_root_connectable = reader.read_bool("local root connectable flag")?;
1311 let needs_cleanup = reader.read_bool("needs cleanup flag")?;
1312 let num_nodes = reader.read_usize("node count")?;
1313 let num_edges = reader.read_usize("edge count")?;
1314 let num_paths = reader.read_usize("path count")?;
1315 let num_polys = reader.read_usize("poly count")?;
1316 let num_vertices = reader.read_usize("vertex count")?;
1317 let num_creases = reader.read_usize("crease count")?;
1318 let num_facets = reader.read_usize("facet count")?;
1319 let num_conditions = reader.read_usize("condition count")?;
1320
1321 let mut nodes = Vec::with_capacity(num_nodes);
1322 let mut edges = Vec::with_capacity(num_edges);
1323 let mut paths = Vec::with_capacity(num_paths);
1324 let mut polys = Vec::with_capacity(num_polys);
1325 let mut vertices = Vec::with_capacity(num_vertices);
1326 let mut creases = Vec::with_capacity(num_creases);
1327 let mut facets = Vec::with_capacity(num_facets);
1328
1329 for _ in 0..num_nodes {
1330 nodes.push(reader.read_node_v5()?);
1331 }
1332 for _ in 0..num_edges {
1333 edges.push(reader.read_edge(false)?);
1334 }
1335 for _ in 0..num_paths {
1336 paths.push(reader.read_path_v5(num_polys, num_paths)?);
1337 }
1338 for _ in 0..num_polys {
1339 polys.push(reader.read_poly_v5(num_paths)?);
1340 }
1341 for _ in 0..num_vertices {
1342 vertices.push(reader.read_vertex_v5(num_nodes, num_vertices)?);
1343 }
1344 for _ in 0..num_creases {
1345 creases.push(reader.read_crease_v5(num_facets)?);
1346 }
1347 for _ in 0..num_facets {
1348 facets.push(reader.read_facet_v5(num_edges)?);
1349 }
1350
1351 let mut conditions = Vec::with_capacity(num_conditions);
1352 for _ in 0..num_conditions {
1353 conditions.push(reader.read_condition_v5()?);
1354 }
1355
1356 let owned_nodes = reader.read_index_array("owned nodes")?;
1357 let owned_edges = reader.read_index_array("owned edges")?;
1358 let owned_paths = reader.read_index_array("owned paths")?;
1359 let owned_polys = reader.read_index_array("owned polys")?;
1360
1361 Ok(Self {
1362 source_version,
1363 paper_width,
1364 paper_height,
1365 scale,
1366 has_symmetry,
1367 sym_loc,
1368 sym_angle,
1369 is_feasible,
1370 is_polygon_valid,
1371 is_polygon_filled,
1372 is_vertex_depth_valid,
1373 is_facet_data_valid,
1374 is_local_root_connectable,
1375 needs_cleanup,
1376 nodes,
1377 edges,
1378 paths,
1379 polys,
1380 vertices,
1381 creases,
1382 facets,
1383 conditions,
1384 owned_nodes,
1385 owned_edges,
1386 owned_paths,
1387 owned_polys,
1388 })
1389 }
1390
1391 fn validate(&self) -> Result<()> {
1392 for id in &self.owned_nodes {
1393 self.check_ref("node", *id, self.nodes.len())?;
1394 }
1395 for id in &self.owned_edges {
1396 self.check_ref("edge", *id, self.edges.len())?;
1397 }
1398 for id in &self.owned_paths {
1399 self.check_ref("path", *id, self.paths.len())?;
1400 }
1401 for id in &self.owned_polys {
1402 self.check_ref("poly", *id, self.polys.len())?;
1403 }
1404 for node in &self.nodes {
1405 for id in &node.edges {
1406 self.check_ref("edge", *id, self.edges.len())?;
1407 }
1408 for id in &node.leaf_paths {
1409 self.check_ref("path", *id, self.paths.len())?;
1410 }
1411 for id in &node.owned_vertices {
1412 self.check_ref("vertex", *id, self.vertices.len())?;
1413 }
1414 self.check_owner(&node.owner)?;
1415 }
1416 for edge in &self.edges {
1417 for id in &edge.nodes {
1418 self.check_ref("node", *id, self.nodes.len())?;
1419 }
1420 }
1421 for path in &self.paths {
1422 for id in &path.nodes {
1423 self.check_ref("node", *id, self.nodes.len())?;
1424 }
1425 for id in &path.edges {
1426 self.check_ref("edge", *id, self.edges.len())?;
1427 }
1428 if let Some(id) = path.fwd_poly {
1429 self.check_ref("poly", id, self.polys.len())?;
1430 }
1431 if let Some(id) = path.bkd_poly {
1432 self.check_ref("poly", id, self.polys.len())?;
1433 }
1434 if let Some(id) = path.outset_path {
1435 self.check_ref("path", id, self.paths.len())?;
1436 }
1437 for id in &path.owned_vertices {
1438 self.check_ref("vertex", *id, self.vertices.len())?;
1439 }
1440 for id in &path.owned_creases {
1441 self.check_ref("crease", *id, self.creases.len())?;
1442 }
1443 self.check_owner(&path.owner)?;
1444 }
1445 for poly in &self.polys {
1446 for id in &poly.ring_nodes {
1447 self.check_ref("node", *id, self.nodes.len())?;
1448 }
1449 for id in &poly.inset_nodes {
1450 self.check_ref("node", *id, self.nodes.len())?;
1451 }
1452 for id in &poly.owned_nodes {
1453 self.check_ref("node", *id, self.nodes.len())?;
1454 }
1455 for id in poly
1456 .ring_paths
1457 .iter()
1458 .chain(&poly.cross_paths)
1459 .chain(&poly.spoke_paths)
1460 .chain(&poly.owned_paths)
1461 {
1462 self.check_ref("path", *id, self.paths.len())?;
1463 }
1464 if let Some(id) = poly.ridge_path {
1465 self.check_ref("path", id, self.paths.len())?;
1466 }
1467 for id in &poly.local_root_vertices {
1468 self.check_ref("vertex", *id, self.vertices.len())?;
1469 }
1470 for id in poly.local_root_creases.iter().chain(&poly.owned_creases) {
1471 self.check_ref("crease", *id, self.creases.len())?;
1472 }
1473 for id in &poly.owned_polys {
1474 self.check_ref("poly", *id, self.polys.len())?;
1475 }
1476 for id in &poly.owned_facets {
1477 self.check_ref("facet", *id, self.facets.len())?;
1478 }
1479 self.check_owner(&poly.owner)?;
1480 }
1481 for vertex in &self.vertices {
1482 if let Some(id) = vertex.tree_node {
1483 self.check_ref("node", id, self.nodes.len())?;
1484 }
1485 if let Some(id) = vertex.left_pseudohinge_mate {
1486 self.check_ref("vertex", id, self.vertices.len())?;
1487 }
1488 if let Some(id) = vertex.right_pseudohinge_mate {
1489 self.check_ref("vertex", id, self.vertices.len())?;
1490 }
1491 for id in &vertex.creases {
1492 self.check_ref("crease", *id, self.creases.len())?;
1493 }
1494 self.check_owner(&vertex.owner)?;
1495 }
1496 for crease in &self.creases {
1497 for id in &crease.vertices {
1498 self.check_ref("vertex", *id, self.vertices.len())?;
1499 }
1500 if let Some(id) = crease.fwd_facet {
1501 self.check_ref("facet", id, self.facets.len())?;
1502 }
1503 if let Some(id) = crease.bkd_facet {
1504 self.check_ref("facet", id, self.facets.len())?;
1505 }
1506 self.check_owner(&crease.owner)?;
1507 }
1508 for facet in &self.facets {
1509 for id in &facet.vertices {
1510 self.check_ref("vertex", *id, self.vertices.len())?;
1511 }
1512 for id in &facet.creases {
1513 self.check_ref("crease", *id, self.creases.len())?;
1514 }
1515 if let Some(id) = facet.corridor_edge {
1516 self.check_ref("edge", id, self.edges.len())?;
1517 }
1518 for id in facet.head_facets.iter().chain(&facet.tail_facets) {
1519 self.check_ref("facet", *id, self.facets.len())?;
1520 }
1521 self.check_owner(&facet.owner)?;
1522 }
1523 for condition in &self.conditions {
1524 condition.kind.validate_refs(self)?;
1525 }
1526 Ok(())
1527 }
1528
1529 fn check_ref(&self, kind: &'static str, index: usize, max: usize) -> Result<()> {
1530 if index == 0 || index > max {
1531 return Err(TreeError::BadReference { kind, index, max });
1532 }
1533 Ok(())
1534 }
1535
1536 fn check_owner(&self, owner: &OwnerRef) -> Result<()> {
1537 match *owner {
1538 OwnerRef::Tree => Ok(()),
1539 OwnerRef::Node(id) => self.check_ref("node", id, self.nodes.len()),
1540 OwnerRef::Path(id) => self.check_ref("path", id, self.paths.len()),
1541 OwnerRef::Poly(id) => self.check_ref("poly", id, self.polys.len()),
1542 }
1543 }
1544
1545 fn find_leaf_path_between(&self, node1: usize, node2: usize) -> Option<&Path> {
1546 self.paths.iter().find(|path| {
1547 path.is_leaf
1548 && matches!(
1549 path.nodes.first().copied().zip(path.nodes.last().copied()),
1550 Some((a, b)) if (a == node1 && b == node2) || (a == node2 && b == node1)
1551 )
1552 })
1553 }
1554
1555 fn leaf_nodes_in_owned_order(&self) -> Vec<usize> {
1556 self.owned_nodes
1557 .iter()
1558 .copied()
1559 .filter(|id| self.nodes[id - 1].is_leaf)
1560 .collect()
1561 }
1562
1563 fn has_path_active_base_condition(&self, path: &Path) -> bool {
1564 let Some((node1, node2)) = path.nodes.first().copied().zip(path.nodes.last().copied())
1565 else {
1566 return false;
1567 };
1568 self.conditions
1569 .iter()
1570 .any(|condition| match condition.kind {
1571 ConditionKind::PathActive { node1: a, node2: b }
1572 | ConditionKind::PathAngleFixed {
1573 node1: a, node2: b, ..
1574 }
1575 | ConditionKind::PathAngleQuant {
1576 node1: a, node2: b, ..
1577 } => (a == node1 && b == node2) || (a == node2 && b == node1),
1578 _ => false,
1579 })
1580 }
1581
1582 fn scale_offset(node_offsets: &[Option<usize>], node: usize) -> Option<usize> {
1583 node_offsets.get(node).copied().flatten()
1584 }
1585
1586 fn add_scale_path_constraint(
1587 &self,
1588 optimizer: &mut nlco::NlcoAlm,
1589 node_offsets: &[Option<usize>],
1590 node1: usize,
1591 node2: usize,
1592 min_tree_length: TmFloat,
1593 equality: bool,
1594 ) {
1595 let (Some(ix), Some(jx)) = (
1596 Self::scale_offset(node_offsets, node1),
1597 Self::scale_offset(node_offsets, node2),
1598 ) else {
1599 return;
1600 };
1601 let constraint = Box::new(nlco::PathFn1::new(ix, ix + 1, jx, jx + 1, min_tree_length));
1602 if equality {
1603 optimizer.add_nonlinear_equality(constraint);
1604 } else {
1605 optimizer.add_nonlinear_inequality(constraint);
1606 }
1607 }
1608
1609 fn add_scale_path_active_constraint(
1610 &self,
1611 optimizer: &mut nlco::NlcoAlm,
1612 node_offsets: &[Option<usize>],
1613 node1: usize,
1614 node2: usize,
1615 ) {
1616 if let Some(path) = self.find_leaf_path_between(node1, node2) {
1617 self.add_scale_path_constraint(
1618 optimizer,
1619 node_offsets,
1620 node1,
1621 node2,
1622 path.min_tree_length,
1623 true,
1624 );
1625 }
1626 }
1627
1628 fn add_scale_condition_constraints(
1629 &self,
1630 optimizer: &mut nlco::NlcoAlm,
1631 node_offsets: &[Option<usize>],
1632 kind: &ConditionKind,
1633 ) {
1634 match *kind {
1635 ConditionKind::NodeCombo {
1636 node,
1637 to_symmetry_line,
1638 to_paper_edge,
1639 to_paper_corner,
1640 x_fixed,
1641 x_fix_value,
1642 y_fixed,
1643 y_fix_value,
1644 } => {
1645 let Some(ix) = Self::scale_offset(node_offsets, node) else {
1646 return;
1647 };
1648 let iy = ix + 1;
1649 if self.has_symmetry && to_symmetry_line {
1650 optimizer.add_linear_equality(Box::new(nlco::StickToLineFn::new(
1651 ix,
1652 iy,
1653 self.sym_loc.x,
1654 self.sym_loc.y,
1655 self.sym_angle,
1656 )));
1657 }
1658 if to_paper_edge {
1659 optimizer.add_nonlinear_equality(Box::new(nlco::StickToEdgeFn::new(
1660 ix,
1661 iy,
1662 self.paper_width,
1663 self.paper_height,
1664 )));
1665 }
1666 if to_paper_corner {
1667 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
1668 ix,
1669 self.paper_width,
1670 )));
1671 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
1672 iy,
1673 self.paper_height,
1674 )));
1675 }
1676 if x_fixed {
1677 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
1678 ix,
1679 -1.0,
1680 x_fix_value,
1681 )));
1682 }
1683 if y_fixed {
1684 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
1685 iy,
1686 -1.0,
1687 y_fix_value,
1688 )));
1689 }
1690 }
1691 ConditionKind::NodeFixed {
1692 node,
1693 x_fixed,
1694 y_fixed,
1695 x_fix_value,
1696 y_fix_value,
1697 } => {
1698 let Some(ix) = Self::scale_offset(node_offsets, node) else {
1699 return;
1700 };
1701 let iy = ix + 1;
1702 if x_fixed {
1703 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
1704 ix,
1705 -1.0,
1706 x_fix_value,
1707 )));
1708 }
1709 if y_fixed {
1710 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
1711 iy,
1712 -1.0,
1713 y_fix_value,
1714 )));
1715 }
1716 }
1717 ConditionKind::NodeOnCorner { node } => {
1718 if let Some(ix) = Self::scale_offset(node_offsets, node) {
1719 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
1720 ix,
1721 self.paper_width,
1722 )));
1723 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
1724 ix + 1,
1725 self.paper_height,
1726 )));
1727 }
1728 }
1729 ConditionKind::NodeOnEdge { node } => {
1730 if let Some(ix) = Self::scale_offset(node_offsets, node) {
1731 optimizer.add_nonlinear_equality(Box::new(nlco::StickToEdgeFn::new(
1732 ix,
1733 ix + 1,
1734 self.paper_width,
1735 self.paper_height,
1736 )));
1737 }
1738 }
1739 ConditionKind::NodeSymmetric { node } => {
1740 if !self.has_symmetry {
1741 return;
1742 }
1743 if let Some(ix) = Self::scale_offset(node_offsets, node) {
1744 optimizer.add_linear_equality(Box::new(nlco::StickToLineFn::new(
1745 ix,
1746 ix + 1,
1747 self.sym_loc.x,
1748 self.sym_loc.y,
1749 self.sym_angle,
1750 )));
1751 }
1752 }
1753 ConditionKind::NodesPaired { node1, node2 } => {
1754 if !self.has_symmetry {
1755 return;
1756 }
1757 let (Some(ix), Some(jx)) = (
1758 Self::scale_offset(node_offsets, node1),
1759 Self::scale_offset(node_offsets, node2),
1760 ) else {
1761 return;
1762 };
1763 optimizer.add_linear_equality(Box::new(nlco::PairFn1A::new(
1764 ix,
1765 ix + 1,
1766 jx,
1767 jx + 1,
1768 self.sym_angle,
1769 )));
1770 optimizer.add_linear_equality(Box::new(nlco::PairFn1B::new(
1771 ix,
1772 ix + 1,
1773 jx,
1774 jx + 1,
1775 self.sym_loc.x,
1776 self.sym_loc.y,
1777 self.sym_angle,
1778 )));
1779 }
1780 ConditionKind::NodesCollinear {
1781 node1,
1782 node2,
1783 node3,
1784 } => {
1785 let (Some(ix), Some(jx), Some(kx)) = (
1786 Self::scale_offset(node_offsets, node1),
1787 Self::scale_offset(node_offsets, node2),
1788 Self::scale_offset(node_offsets, node3),
1789 ) else {
1790 return;
1791 };
1792 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn1::new(
1793 ix,
1794 ix + 1,
1795 jx,
1796 jx + 1,
1797 kx,
1798 kx + 1,
1799 )));
1800 }
1801 ConditionKind::EdgeLengthFixed { .. } | ConditionKind::EdgesSameStrain { .. } => {}
1802 ConditionKind::PathCombo {
1803 node1,
1804 node2,
1805 is_angle_fixed,
1806 angle,
1807 is_angle_quant,
1808 quant,
1809 quant_offset,
1810 } => {
1811 let (Some(ix), Some(jx)) = (
1812 Self::scale_offset(node_offsets, node1),
1813 Self::scale_offset(node_offsets, node2),
1814 ) else {
1815 return;
1816 };
1817 self.add_scale_path_active_constraint(optimizer, node_offsets, node1, node2);
1818 if is_angle_fixed {
1819 optimizer.add_linear_equality(Box::new(nlco::PathAngleFn1::new(
1820 ix,
1821 ix + 1,
1822 jx,
1823 jx + 1,
1824 angle,
1825 )));
1826 }
1827 if is_angle_quant {
1828 optimizer.add_nonlinear_equality(Box::new(nlco::QuantizeAngleFn1::new(
1829 ix,
1830 ix + 1,
1831 jx,
1832 jx + 1,
1833 quant,
1834 quant_offset,
1835 )));
1836 }
1837 }
1838 ConditionKind::PathActive { node1, node2 } => {
1839 self.add_scale_path_active_constraint(optimizer, node_offsets, node1, node2);
1840 }
1841 ConditionKind::PathAngleFixed {
1842 node1,
1843 node2,
1844 angle,
1845 } => {
1846 self.add_scale_path_active_constraint(optimizer, node_offsets, node1, node2);
1847 let (Some(ix), Some(jx)) = (
1848 Self::scale_offset(node_offsets, node1),
1849 Self::scale_offset(node_offsets, node2),
1850 ) else {
1851 return;
1852 };
1853 optimizer.add_linear_equality(Box::new(nlco::PathAngleFn1::new(
1854 ix,
1855 ix + 1,
1856 jx,
1857 jx + 1,
1858 angle,
1859 )));
1860 }
1861 ConditionKind::PathAngleQuant {
1862 node1,
1863 node2,
1864 quant,
1865 quant_offset,
1866 } => {
1867 self.add_scale_path_active_constraint(optimizer, node_offsets, node1, node2);
1868 let (Some(ix), Some(jx)) = (
1869 Self::scale_offset(node_offsets, node1),
1870 Self::scale_offset(node_offsets, node2),
1871 ) else {
1872 return;
1873 };
1874 optimizer.add_nonlinear_equality(Box::new(nlco::QuantizeAngleFn1::new(
1875 ix,
1876 ix + 1,
1877 jx,
1878 jx + 1,
1879 quant,
1880 quant_offset,
1881 )));
1882 }
1883 }
1884 }
1885
1886 fn moving_nodes_for_edge_optimizer(&self) -> Vec<usize> {
1887 self.owned_nodes
1888 .iter()
1889 .copied()
1890 .filter(|id| {
1891 let node = &self.nodes[id - 1];
1892 node.is_leaf && !node.is_pinned
1893 })
1894 .collect()
1895 }
1896
1897 fn edge_has_length_fixed_condition(&self, edge: usize) -> bool {
1898 self.conditions.iter().any(|condition| {
1899 matches!(condition.kind, ConditionKind::EdgeLengthFixed { edge: e } if e == edge)
1900 })
1901 }
1902
1903 fn stretchy_edges_for_edge_optimizer(&self) -> Vec<usize> {
1904 self.owned_edges
1905 .iter()
1906 .copied()
1907 .filter(|id| {
1908 let edge = &self.edges[id - 1];
1909 !edge.is_pinned && !self.edge_has_length_fixed_condition(*id)
1910 })
1911 .collect()
1912 }
1913
1914 fn edge_lookup(&self, edge_ids: &[usize]) -> Vec<bool> {
1915 let mut lookup = vec![false; self.edges.len() + 1];
1916 for id in edge_ids {
1917 lookup[*id] = true;
1918 }
1919 lookup
1920 }
1921
1922 fn node_loc(&self, node: usize) -> Point {
1923 self.nodes[node - 1].loc
1924 }
1925
1926 fn edge_fix_var_lengths(&self, path: &Path, stretchy_lookup: &[bool]) -> (TmFloat, TmFloat) {
1927 let mut lfix = 0.0;
1928 let mut lvar = 0.0;
1929 for edge_id in &path.edges {
1930 let edge = &self.edges[*edge_id - 1];
1931 let temp = edge.length * self.scale;
1932 if stretchy_lookup[*edge_id] {
1933 lfix += temp;
1934 lvar += temp;
1935 } else {
1936 lfix += (1.0 + edge.strain) * temp;
1937 }
1938 }
1939 (lfix, lvar)
1940 }
1941
1942 fn add_edge_path_constraint(
1943 &self,
1944 optimizer: &mut nlco::NlcoAlm,
1945 node_offsets: &[Option<usize>],
1946 stretchy_lookup: &[bool],
1947 path: &Path,
1948 equality: bool,
1949 ) {
1950 let node1 = path.nodes[0];
1951 let node2 = *path.nodes.last().unwrap();
1952 let ix = Self::scale_offset(node_offsets, node1);
1953 let jx = Self::scale_offset(node_offsets, node2);
1954 let (lfix, lvar) = self.edge_fix_var_lengths(path, stretchy_lookup);
1955 let constraint: Option<Box<dyn nlco::DifferentiableFn>> = match (ix, jx) {
1956 (Some(ix), Some(jx)) => Some(Box::new(nlco::StrainPathFn1::new(
1957 ix,
1958 ix + 1,
1959 jx,
1960 jx + 1,
1961 lfix,
1962 lvar,
1963 ))),
1964 (Some(ix), None) => {
1965 let loc = self.node_loc(node2);
1966 Some(Box::new(nlco::StrainPathFn2::new(
1967 ix,
1968 ix + 1,
1969 loc.x,
1970 loc.y,
1971 lfix,
1972 lvar,
1973 )))
1974 }
1975 (None, Some(jx)) => {
1976 let loc = self.node_loc(node1);
1977 Some(Box::new(nlco::StrainPathFn2::new(
1978 jx,
1979 jx + 1,
1980 loc.x,
1981 loc.y,
1982 lfix,
1983 lvar,
1984 )))
1985 }
1986 (None, None) if lvar != 0.0 => {
1987 let loc1 = self.node_loc(node1);
1988 let loc2 = self.node_loc(node2);
1989 Some(Box::new(nlco::StrainPathFn3::new(
1990 loc1.x, loc1.y, loc2.x, loc2.y, lfix, lvar,
1991 )))
1992 }
1993 (None, None) => None,
1994 };
1995 if let Some(constraint) = constraint {
1996 if equality {
1997 optimizer.add_nonlinear_equality(constraint);
1998 } else {
1999 optimizer.add_nonlinear_inequality(constraint);
2000 }
2001 }
2002 }
2003
2004 fn add_edge_path_active_constraint(
2005 &self,
2006 optimizer: &mut nlco::NlcoAlm,
2007 node_offsets: &[Option<usize>],
2008 stretchy_lookup: &[bool],
2009 node1: usize,
2010 node2: usize,
2011 ) {
2012 if let Some(path) = self.find_leaf_path_between(node1, node2) {
2013 self.add_edge_path_constraint(optimizer, node_offsets, stretchy_lookup, path, true);
2014 }
2015 }
2016
2017 fn add_edge_angle_constraints(
2018 &self,
2019 optimizer: &mut nlco::NlcoAlm,
2020 node_offsets: &[Option<usize>],
2021 node1: usize,
2022 node2: usize,
2023 fixed_angle: Option<TmFloat>,
2024 quant: Option<(usize, TmFloat)>,
2025 ) {
2026 let ix = Self::scale_offset(node_offsets, node1);
2027 let jx = Self::scale_offset(node_offsets, node2);
2028 if let Some(angle) = fixed_angle {
2029 match (ix, jx) {
2030 (Some(ix), Some(jx)) => optimizer.add_nonlinear_equality(Box::new(
2031 nlco::PathAngleFn1::new(ix, ix + 1, jx, jx + 1, angle),
2032 )),
2033 (Some(ix), None) => {
2034 let loc = self.node_loc(node2);
2035 optimizer.add_nonlinear_equality(Box::new(nlco::PathAngleFn2::new(
2036 ix,
2037 ix + 1,
2038 loc.x,
2039 loc.y,
2040 angle,
2041 )));
2042 }
2043 (None, Some(jx)) => {
2044 let loc = self.node_loc(node1);
2045 optimizer.add_nonlinear_equality(Box::new(nlco::PathAngleFn2::new(
2046 jx,
2047 jx + 1,
2048 loc.x,
2049 loc.y,
2050 angle,
2051 )));
2052 }
2053 (None, None) => {}
2054 }
2055 }
2056 if let Some((quant, quant_offset)) = quant {
2057 match (ix, jx) {
2058 (Some(ix), Some(jx)) => optimizer.add_nonlinear_equality(Box::new(
2059 nlco::QuantizeAngleFn1::new(ix, ix + 1, jx, jx + 1, quant, quant_offset),
2060 )),
2061 (Some(ix), None) => {
2062 let loc = self.node_loc(node2);
2063 optimizer.add_nonlinear_equality(Box::new(nlco::QuantizeAngleFn2::new(
2064 ix,
2065 ix + 1,
2066 loc.x,
2067 loc.y,
2068 quant,
2069 quant_offset,
2070 )));
2071 }
2072 (None, Some(jx)) => {
2073 let loc = self.node_loc(node1);
2074 optimizer.add_nonlinear_equality(Box::new(nlco::QuantizeAngleFn2::new(
2075 jx,
2076 jx + 1,
2077 loc.x,
2078 loc.y,
2079 quant,
2080 quant_offset,
2081 )));
2082 }
2083 (None, None) => {}
2084 }
2085 }
2086 }
2087
2088 fn add_pair_constraints(
2089 &self,
2090 optimizer: &mut nlco::NlcoAlm,
2091 node_offsets: &[Option<usize>],
2092 node1: usize,
2093 node2: usize,
2094 ) {
2095 if !self.has_symmetry {
2096 return;
2097 }
2098 let ix = Self::scale_offset(node_offsets, node1);
2099 let jx = Self::scale_offset(node_offsets, node2);
2100 match (ix, jx) {
2101 (Some(ix), Some(jx)) => {
2102 optimizer.add_linear_equality(Box::new(nlco::PairFn1A::new(
2103 ix,
2104 ix + 1,
2105 jx,
2106 jx + 1,
2107 self.sym_angle,
2108 )));
2109 optimizer.add_linear_equality(Box::new(nlco::PairFn1B::new(
2110 ix,
2111 ix + 1,
2112 jx,
2113 jx + 1,
2114 self.sym_loc.x,
2115 self.sym_loc.y,
2116 self.sym_angle,
2117 )));
2118 }
2119 (Some(ix), None) => {
2120 let loc = self.node_loc(node2);
2121 optimizer.add_linear_equality(Box::new(nlco::PairFn2A::new(
2122 ix,
2123 ix + 1,
2124 loc.x,
2125 loc.y,
2126 self.sym_angle,
2127 )));
2128 optimizer.add_linear_equality(Box::new(nlco::PairFn2B::new(
2129 ix,
2130 ix + 1,
2131 loc.x,
2132 loc.y,
2133 self.sym_loc.x,
2134 self.sym_loc.y,
2135 self.sym_angle,
2136 )));
2137 }
2138 (None, Some(jx)) => {
2139 let loc = self.node_loc(node1);
2140 optimizer.add_linear_equality(Box::new(nlco::PairFn2A::new(
2141 jx,
2142 jx + 1,
2143 loc.x,
2144 loc.y,
2145 self.sym_angle,
2146 )));
2147 optimizer.add_linear_equality(Box::new(nlco::PairFn2B::new(
2148 jx,
2149 jx + 1,
2150 loc.x,
2151 loc.y,
2152 self.sym_loc.x,
2153 self.sym_loc.y,
2154 self.sym_angle,
2155 )));
2156 }
2157 (None, None) => {}
2158 }
2159 }
2160
2161 fn add_collinear_constraints(
2162 &self,
2163 optimizer: &mut nlco::NlcoAlm,
2164 node_offsets: &[Option<usize>],
2165 node1: usize,
2166 node2: usize,
2167 node3: usize,
2168 ) {
2169 let ix = Self::scale_offset(node_offsets, node1);
2170 let jx = Self::scale_offset(node_offsets, node2);
2171 let kx = Self::scale_offset(node_offsets, node3);
2172 match (ix, jx, kx) {
2173 (Some(ix), Some(jx), Some(kx)) => {
2174 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn1::new(
2175 ix,
2176 ix + 1,
2177 jx,
2178 jx + 1,
2179 kx,
2180 kx + 1,
2181 )));
2182 }
2183 (Some(ix), Some(jx), None) => {
2184 let loc = self.node_loc(node3);
2185 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn2::new(
2186 ix,
2187 ix + 1,
2188 jx,
2189 jx + 1,
2190 loc.x,
2191 loc.y,
2192 )));
2193 }
2194 (Some(ix), None, Some(kx)) => {
2195 let loc = self.node_loc(node2);
2196 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn2::new(
2197 ix,
2198 ix + 1,
2199 kx,
2200 kx + 1,
2201 loc.x,
2202 loc.y,
2203 )));
2204 }
2205 (None, Some(jx), Some(kx)) => {
2206 let loc = self.node_loc(node1);
2207 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn2::new(
2208 jx,
2209 jx + 1,
2210 kx,
2211 kx + 1,
2212 loc.x,
2213 loc.y,
2214 )));
2215 }
2216 (Some(ix), None, None) => {
2217 let loc2 = self.node_loc(node2);
2218 let loc3 = self.node_loc(node3);
2219 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn3::new(
2220 ix,
2221 ix + 1,
2222 loc2.x,
2223 loc2.y,
2224 loc3.x,
2225 loc3.y,
2226 )));
2227 }
2228 (None, Some(jx), None) => {
2229 let loc1 = self.node_loc(node1);
2230 let loc3 = self.node_loc(node3);
2231 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn3::new(
2232 jx,
2233 jx + 1,
2234 loc1.x,
2235 loc1.y,
2236 loc3.x,
2237 loc3.y,
2238 )));
2239 }
2240 (None, None, Some(kx)) => {
2241 let loc1 = self.node_loc(node1);
2242 let loc2 = self.node_loc(node2);
2243 optimizer.add_nonlinear_equality(Box::new(nlco::CollinearFn3::new(
2244 kx,
2245 kx + 1,
2246 loc1.x,
2247 loc1.y,
2248 loc2.x,
2249 loc2.y,
2250 )));
2251 }
2252 (None, None, None) => {}
2253 }
2254 }
2255
2256 fn add_edge_condition_constraints(
2257 &self,
2258 optimizer: &mut nlco::NlcoAlm,
2259 node_offsets: &[Option<usize>],
2260 stretchy_lookup: &[bool],
2261 kind: &ConditionKind,
2262 ) {
2263 match *kind {
2264 ConditionKind::NodeCombo {
2265 node,
2266 to_symmetry_line,
2267 to_paper_edge,
2268 to_paper_corner,
2269 x_fixed,
2270 x_fix_value,
2271 y_fixed,
2272 y_fix_value,
2273 } => {
2274 let Some(ix) = Self::scale_offset(node_offsets, node) else {
2275 return;
2276 };
2277 let iy = ix + 1;
2278 if self.has_symmetry && to_symmetry_line {
2279 optimizer.add_linear_equality(Box::new(nlco::StickToLineFn::new(
2280 ix,
2281 iy,
2282 self.sym_loc.x,
2283 self.sym_loc.y,
2284 self.sym_angle,
2285 )));
2286 }
2287 if to_paper_edge {
2288 optimizer.add_nonlinear_equality(Box::new(nlco::StickToEdgeFn::new(
2289 ix,
2290 iy,
2291 self.paper_width,
2292 self.paper_height,
2293 )));
2294 }
2295 if to_paper_corner {
2296 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
2297 ix,
2298 self.paper_width,
2299 )));
2300 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
2301 iy,
2302 self.paper_height,
2303 )));
2304 }
2305 if x_fixed {
2306 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
2307 ix,
2308 -1.0,
2309 x_fix_value,
2310 )));
2311 }
2312 if y_fixed {
2313 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
2314 iy,
2315 -1.0,
2316 y_fix_value,
2317 )));
2318 }
2319 }
2320 ConditionKind::NodeFixed {
2321 node,
2322 x_fixed,
2323 y_fixed,
2324 x_fix_value,
2325 y_fix_value,
2326 } => {
2327 let Some(ix) = Self::scale_offset(node_offsets, node) else {
2328 return;
2329 };
2330 if x_fixed {
2331 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
2332 ix,
2333 -1.0,
2334 x_fix_value,
2335 )));
2336 }
2337 if y_fixed {
2338 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(
2339 ix + 1,
2340 -1.0,
2341 y_fix_value,
2342 )));
2343 }
2344 }
2345 ConditionKind::NodeOnCorner { node } => {
2346 if let Some(ix) = Self::scale_offset(node_offsets, node) {
2347 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
2348 ix,
2349 self.paper_width,
2350 )));
2351 optimizer.add_nonlinear_equality(Box::new(nlco::CornerFn::new(
2352 ix + 1,
2353 self.paper_height,
2354 )));
2355 }
2356 }
2357 ConditionKind::NodeOnEdge { node } => {
2358 if let Some(ix) = Self::scale_offset(node_offsets, node) {
2359 optimizer.add_nonlinear_equality(Box::new(nlco::StickToEdgeFn::new(
2360 ix,
2361 ix + 1,
2362 self.paper_width,
2363 self.paper_height,
2364 )));
2365 }
2366 }
2367 ConditionKind::NodeSymmetric { node } => {
2368 if !self.has_symmetry {
2369 return;
2370 }
2371 if let Some(ix) = Self::scale_offset(node_offsets, node) {
2372 optimizer.add_linear_equality(Box::new(nlco::StickToLineFn::new(
2373 ix,
2374 ix + 1,
2375 self.sym_loc.x,
2376 self.sym_loc.y,
2377 self.sym_angle,
2378 )));
2379 }
2380 }
2381 ConditionKind::NodesPaired { node1, node2 } => {
2382 self.add_pair_constraints(optimizer, node_offsets, node1, node2);
2383 }
2384 ConditionKind::NodesCollinear {
2385 node1,
2386 node2,
2387 node3,
2388 } => self.add_collinear_constraints(optimizer, node_offsets, node1, node2, node3),
2389 ConditionKind::EdgeLengthFixed { edge } => {
2390 if stretchy_lookup[edge] {
2391 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(0, 1.0, 0.0)));
2392 }
2393 }
2394 ConditionKind::EdgesSameStrain { .. } => {}
2395 ConditionKind::PathCombo {
2396 node1,
2397 node2,
2398 is_angle_fixed,
2399 angle,
2400 is_angle_quant,
2401 quant,
2402 quant_offset,
2403 } => {
2404 self.add_edge_path_active_constraint(
2405 optimizer,
2406 node_offsets,
2407 stretchy_lookup,
2408 node1,
2409 node2,
2410 );
2411 self.add_edge_angle_constraints(
2412 optimizer,
2413 node_offsets,
2414 node1,
2415 node2,
2416 is_angle_fixed.then_some(angle),
2417 is_angle_quant.then_some((quant, quant_offset)),
2418 );
2419 }
2420 ConditionKind::PathActive { node1, node2 } => {
2421 self.add_edge_path_active_constraint(
2422 optimizer,
2423 node_offsets,
2424 stretchy_lookup,
2425 node1,
2426 node2,
2427 );
2428 }
2429 ConditionKind::PathAngleFixed {
2430 node1,
2431 node2,
2432 angle,
2433 } => {
2434 self.add_edge_path_active_constraint(
2435 optimizer,
2436 node_offsets,
2437 stretchy_lookup,
2438 node1,
2439 node2,
2440 );
2441 self.add_edge_angle_constraints(
2442 optimizer,
2443 node_offsets,
2444 node1,
2445 node2,
2446 Some(angle),
2447 None,
2448 );
2449 }
2450 ConditionKind::PathAngleQuant {
2451 node1,
2452 node2,
2453 quant,
2454 quant_offset,
2455 } => {
2456 self.add_edge_path_active_constraint(
2457 optimizer,
2458 node_offsets,
2459 stretchy_lookup,
2460 node1,
2461 node2,
2462 );
2463 self.add_edge_angle_constraints(
2464 optimizer,
2465 node_offsets,
2466 node1,
2467 node2,
2468 None,
2469 Some((quant, quant_offset)),
2470 );
2471 }
2472 }
2473 }
2474
2475 fn moving_nodes_for_strain_optimizer(&self) -> Vec<usize> {
2476 self.owned_nodes
2477 .iter()
2478 .copied()
2479 .filter(|id| self.nodes[id - 1].is_leaf)
2480 .collect()
2481 }
2482
2483 fn strain_fix_var_lengths(
2484 &self,
2485 path: &Path,
2486 edge_offsets: &[Option<usize>],
2487 ) -> (TmFloat, Vec<usize>, Vec<TmFloat>) {
2488 let mut lfix = 0.0;
2489 let mut vi = Vec::new();
2490 let mut vf = Vec::new();
2491 for edge_id in &path.edges {
2492 let edge = &self.edges[*edge_id - 1];
2493 if let Some(offset) = edge_offsets[*edge_id] {
2494 vi.push(offset);
2495 let scaled_length = edge.length * self.scale;
2496 vf.push(scaled_length);
2497 lfix += scaled_length;
2498 } else {
2499 lfix += edge.strained_length() * self.scale;
2500 }
2501 }
2502 (lfix, vi, vf)
2503 }
2504
2505 fn add_strain_path_constraint(
2506 &self,
2507 optimizer: &mut nlco::NlcoAlm,
2508 node_offsets: &[Option<usize>],
2509 edge_offsets: &[Option<usize>],
2510 path: &Path,
2511 equality: bool,
2512 ) {
2513 let node1 = path.nodes[0];
2514 let node2 = *path.nodes.last().unwrap();
2515 let ix = Self::scale_offset(node_offsets, node1);
2516 let jx = Self::scale_offset(node_offsets, node2);
2517 let (lfix, vi, vf) = self.strain_fix_var_lengths(path, edge_offsets);
2518 let constraint: Option<Box<dyn nlco::DifferentiableFn>> = match (ix, jx) {
2519 (Some(ix), Some(jx)) => Some(Box::new(nlco::MultiStrainPathFn1::new(
2520 ix,
2521 ix + 1,
2522 jx,
2523 jx + 1,
2524 lfix,
2525 vi,
2526 vf,
2527 ))),
2528 (Some(ix), None) => {
2529 let loc = self.node_loc(node2);
2530 Some(Box::new(nlco::MultiStrainPathFn2::new(
2531 ix,
2532 ix + 1,
2533 loc.x,
2534 loc.y,
2535 lfix,
2536 vi,
2537 vf,
2538 )))
2539 }
2540 (None, Some(jx)) => {
2541 let loc = self.node_loc(node1);
2542 Some(Box::new(nlco::MultiStrainPathFn2::new(
2543 jx,
2544 jx + 1,
2545 loc.x,
2546 loc.y,
2547 lfix,
2548 vi,
2549 vf,
2550 )))
2551 }
2552 (None, None) if !vi.is_empty() => {
2553 let loc1 = self.node_loc(node1);
2554 let loc2 = self.node_loc(node2);
2555 Some(Box::new(nlco::MultiStrainPathFn3::new(
2556 loc1.x, loc1.y, loc2.x, loc2.y, lfix, vi, vf,
2557 )))
2558 }
2559 (None, None) => None,
2560 };
2561 if let Some(constraint) = constraint {
2562 if equality {
2563 optimizer.add_nonlinear_equality(constraint);
2564 } else {
2565 optimizer.add_nonlinear_inequality(constraint);
2566 }
2567 }
2568 }
2569
2570 fn add_strain_path_active_constraint(
2571 &self,
2572 optimizer: &mut nlco::NlcoAlm,
2573 node_offsets: &[Option<usize>],
2574 edge_offsets: &[Option<usize>],
2575 node1: usize,
2576 node2: usize,
2577 ) {
2578 if let Some(path) = self.find_leaf_path_between(node1, node2) {
2579 self.add_strain_path_constraint(optimizer, node_offsets, edge_offsets, path, true);
2580 }
2581 }
2582
2583 fn add_strain_condition_constraints(
2584 &self,
2585 optimizer: &mut nlco::NlcoAlm,
2586 node_offsets: &[Option<usize>],
2587 edge_offsets: &[Option<usize>],
2588 kind: &ConditionKind,
2589 ) {
2590 match *kind {
2591 ConditionKind::EdgeLengthFixed { edge } => {
2592 if let Some(offset) = edge_offsets[edge] {
2593 optimizer.add_linear_equality(Box::new(nlco::OneVarFn::new(offset, 1.0, 0.0)));
2594 }
2595 }
2596 ConditionKind::EdgesSameStrain { edge1, edge2 } => {
2597 if let (Some(offset1), Some(offset2)) = (edge_offsets[edge1], edge_offsets[edge2]) {
2598 optimizer.add_linear_equality(Box::new(nlco::TwoVarFn::new(
2599 offset1, 1.0, offset2, -1.0, 0.0,
2600 )));
2601 }
2602 }
2603 ConditionKind::PathCombo {
2604 node1,
2605 node2,
2606 is_angle_fixed,
2607 angle,
2608 is_angle_quant,
2609 quant,
2610 quant_offset,
2611 } => {
2612 self.add_strain_path_active_constraint(
2613 optimizer,
2614 node_offsets,
2615 edge_offsets,
2616 node1,
2617 node2,
2618 );
2619 self.add_edge_angle_constraints(
2620 optimizer,
2621 node_offsets,
2622 node1,
2623 node2,
2624 is_angle_fixed.then_some(angle),
2625 is_angle_quant.then_some((quant, quant_offset)),
2626 );
2627 }
2628 ConditionKind::PathActive { node1, node2 } => {
2629 self.add_strain_path_active_constraint(
2630 optimizer,
2631 node_offsets,
2632 edge_offsets,
2633 node1,
2634 node2,
2635 );
2636 }
2637 ConditionKind::PathAngleFixed {
2638 node1,
2639 node2,
2640 angle,
2641 } => {
2642 self.add_strain_path_active_constraint(
2643 optimizer,
2644 node_offsets,
2645 edge_offsets,
2646 node1,
2647 node2,
2648 );
2649 self.add_edge_angle_constraints(
2650 optimizer,
2651 node_offsets,
2652 node1,
2653 node2,
2654 Some(angle),
2655 None,
2656 );
2657 }
2658 ConditionKind::PathAngleQuant {
2659 node1,
2660 node2,
2661 quant,
2662 quant_offset,
2663 } => {
2664 self.add_strain_path_active_constraint(
2665 optimizer,
2666 node_offsets,
2667 edge_offsets,
2668 node1,
2669 node2,
2670 );
2671 self.add_edge_angle_constraints(
2672 optimizer,
2673 node_offsets,
2674 node1,
2675 node2,
2676 None,
2677 Some((quant, quant_offset)),
2678 );
2679 }
2680 _ => self.add_edge_condition_constraints(
2681 optimizer,
2682 node_offsets,
2683 &self.edge_lookup(&[]),
2684 kind,
2685 ),
2686 }
2687 }
2688
2689 fn build_polys_from_paths(
2690 &mut self,
2691 path_list: &[usize],
2692 border_nodes: &[usize],
2693 owner: OwnerRef,
2694 ) -> Result<()> {
2695 let mut polygon_paths = Vec::new();
2696 for path_id in path_list.iter().copied() {
2697 if !self.paths[path_id - 1].is_polygon {
2698 continue;
2699 }
2700 for existing_path in polygon_paths.iter().copied() {
2701 if self.paths_intersect_interior(path_id, existing_path) {
2702 self.paths[path_id - 1].is_polygon = false;
2703 break;
2704 }
2705 }
2706 if self.paths[path_id - 1].is_polygon {
2707 polygon_paths.push(path_id);
2708 }
2709 }
2710
2711 if polygon_paths.is_empty() || border_nodes.is_empty() {
2712 return Ok(());
2713 }
2714
2715 let centroid = self.node_centroid(border_nodes);
2716 for path_id in polygon_paths.iter().copied() {
2717 if self.can_start_poly_fwd(path_id, centroid) {
2718 self.build_poly_ring(path_id, true, owner.clone())?;
2719 }
2720 if self.can_start_poly_bkd(path_id, centroid) {
2721 self.build_poly_ring(path_id, false, owner.clone())?;
2722 }
2723 }
2724
2725 let owned_polys = self.owned_polys_for_owner(&owner);
2726 for poly_id in owned_polys {
2727 if self.polys[poly_id - 1].cross_paths.is_empty() {
2728 self.calc_poly_cross_paths(poly_id);
2729 }
2730 }
2731 Ok(())
2732 }
2733
2734 fn owned_polys_for_owner(&self, owner: &OwnerRef) -> Vec<usize> {
2735 match *owner {
2736 OwnerRef::Tree => self.owned_polys.clone(),
2737 OwnerRef::Poly(poly_id) => self
2738 .polys
2739 .get(poly_id.saturating_sub(1))
2740 .map(|poly| poly.owned_polys.clone())
2741 .unwrap_or_default(),
2742 _ => Vec::new(),
2743 }
2744 }
2745
2746 fn can_start_poly_fwd(&self, path_id: usize, centroid: Point) -> bool {
2747 let path = &self.paths[path_id - 1];
2748 if path.fwd_poly.is_some() {
2749 return false;
2750 }
2751 if !path.is_border {
2752 return true;
2753 }
2754 let (Some(front), Some(back)) = (path.nodes.first(), path.nodes.last()) else {
2755 return false;
2756 };
2757 are_ccw(
2758 self.nodes[*front - 1].loc,
2759 self.nodes[*back - 1].loc,
2760 centroid,
2761 )
2762 }
2763
2764 fn can_start_poly_bkd(&self, path_id: usize, centroid: Point) -> bool {
2765 let path = &self.paths[path_id - 1];
2766 if path.bkd_poly.is_some() {
2767 return false;
2768 }
2769 if !path.is_border {
2770 return true;
2771 }
2772 let (Some(front), Some(back)) = (path.nodes.first(), path.nodes.last()) else {
2773 return false;
2774 };
2775 are_cw(
2776 self.nodes[*front - 1].loc,
2777 self.nodes[*back - 1].loc,
2778 centroid,
2779 )
2780 }
2781
2782 fn build_poly_ring(&mut self, path_id: usize, fwd: bool, owner: OwnerRef) -> Result<()> {
2783 let poly_id = self.create_poly(owner);
2784 if fwd {
2785 self.paths[path_id - 1].fwd_poly = Some(poly_id);
2786 } else {
2787 self.paths[path_id - 1].bkd_poly = Some(poly_id);
2788 }
2789
2790 let path = &self.paths[path_id - 1];
2791 let first_node = if fwd {
2792 path.nodes[0]
2793 } else {
2794 *path
2795 .nodes
2796 .last()
2797 .ok_or(TreeError::InvalidOperation("polygon path has no nodes"))?
2798 };
2799 let mut this_node = if fwd {
2800 *path
2801 .nodes
2802 .last()
2803 .ok_or(TreeError::InvalidOperation("polygon path has no nodes"))?
2804 } else {
2805 path.nodes[0]
2806 };
2807 let mut this_path = path_id;
2808 let mut ring_nodes = vec![first_node];
2809 let mut ring_paths = vec![this_path];
2810
2811 let mut too_many = 0;
2812 loop {
2813 let (next_path, next_node) = self.next_polygon_path_and_node(this_path, this_node)?;
2814 ring_nodes.push(this_node);
2815 ring_paths.push(next_path);
2816 if self.paths[next_path - 1].nodes.first().copied() == Some(this_node) {
2817 self.paths[next_path - 1].fwd_poly = Some(poly_id);
2818 } else {
2819 self.paths[next_path - 1].bkd_poly = Some(poly_id);
2820 }
2821 this_path = next_path;
2822 this_node = next_node;
2823 too_many += 1;
2824 if next_node == first_node {
2825 break;
2826 }
2827 if too_many >= 100 {
2828 return Err(TreeError::InvalidOperation(
2829 "polygon ring walk exceeded TreeMaker guard",
2830 ));
2831 }
2832 }
2833
2834 self.polys[poly_id - 1].ring_nodes = ring_nodes;
2835 self.polys[poly_id - 1].ring_paths = ring_paths;
2836 self.calc_poly_contents(poly_id);
2837 Ok(())
2838 }
2839
2840 fn create_poly(&mut self, owner: OwnerRef) -> usize {
2841 let index = self.polys.len() + 1;
2842 self.polys.push(Poly {
2843 index,
2844 centroid: Point { x: 0.0, y: 0.0 },
2845 is_sub_poly: matches!(owner, OwnerRef::Poly(_)),
2846 ring_nodes: Vec::new(),
2847 ring_paths: Vec::new(),
2848 cross_paths: Vec::new(),
2849 inset_nodes: Vec::new(),
2850 spoke_paths: Vec::new(),
2851 ridge_path: None,
2852 node_locs: Vec::new(),
2853 local_root_vertices: Vec::new(),
2854 local_root_creases: Vec::new(),
2855 owned_nodes: Vec::new(),
2856 owned_paths: Vec::new(),
2857 owned_polys: Vec::new(),
2858 owned_creases: Vec::new(),
2859 owned_facets: Vec::new(),
2860 owner: owner.clone(),
2861 });
2862 match owner {
2863 OwnerRef::Tree => self.owned_polys.push(index),
2864 OwnerRef::Poly(poly_id) => {
2865 if let Some(poly) = self.polys.get_mut(poly_id.saturating_sub(1)) {
2866 poly.owned_polys.push(index);
2867 }
2868 }
2869 _ => {}
2870 }
2871 index
2872 }
2873
2874 fn next_polygon_path_and_node(
2875 &self,
2876 this_path: usize,
2877 this_node: usize,
2878 ) -> Result<(usize, usize)> {
2879 let path = &self.paths[this_path - 1];
2880 let mut that_node = path.nodes[0];
2881 if that_node == this_node {
2882 that_node = *path
2883 .nodes
2884 .last()
2885 .ok_or(TreeError::InvalidOperation("polygon path has no nodes"))?;
2886 }
2887 let this_angle = angle(point_sub(
2888 self.nodes[that_node - 1].loc,
2889 self.nodes[this_node - 1].loc,
2890 ));
2891
2892 let mut delta = TWO_PI;
2893 let mut next_path = None;
2894 let mut next_node = None;
2895 for candidate_path in self.nodes[this_node - 1].leaf_paths.iter().copied() {
2896 if candidate_path == this_path || !self.paths[candidate_path - 1].is_polygon {
2897 continue;
2898 }
2899 let candidate = &self.paths[candidate_path - 1];
2900 let mut candidate_node = candidate.nodes[0];
2901 if candidate_node == this_node {
2902 candidate_node = *candidate
2903 .nodes
2904 .last()
2905 .ok_or(TreeError::InvalidOperation("polygon path has no nodes"))?;
2906 }
2907 let candidate_angle = angle(point_sub(
2908 self.nodes[candidate_node - 1].loc,
2909 self.nodes[this_node - 1].loc,
2910 ));
2911 let mut new_delta = this_angle - candidate_angle;
2912 while new_delta < 0.0 {
2913 new_delta += TWO_PI;
2914 }
2915 while new_delta >= TWO_PI {
2916 new_delta -= TWO_PI;
2917 }
2918 if new_delta < delta {
2919 delta = new_delta;
2920 next_path = Some(candidate_path);
2921 next_node = Some(candidate_node);
2922 }
2923 }
2924
2925 match (next_path, next_node) {
2926 (Some(path), Some(node)) => Ok((path, node)),
2927 _ => Err(TreeError::InvalidOperation(
2928 "polygon path walk could not advance",
2929 )),
2930 }
2931 }
2932
2933 fn calc_poly_contents(&mut self, poly_id: usize) {
2934 let ring_nodes = self.polys[poly_id - 1].ring_nodes.clone();
2935 let mut centroid = Point { x: 0.0, y: 0.0 };
2936 let mut node_locs = Vec::with_capacity(ring_nodes.len());
2937 for node_id in ring_nodes {
2938 let loc = self.nodes[node_id - 1].loc;
2939 node_locs.push(loc);
2940 centroid.x += loc.x;
2941 centroid.y += loc.y;
2942 }
2943 if !node_locs.is_empty() {
2944 centroid.x /= node_locs.len() as TmFloat;
2945 centroid.y /= node_locs.len() as TmFloat;
2946 }
2947 let poly = &mut self.polys[poly_id - 1];
2948 poly.node_locs = node_locs;
2949 poly.centroid = centroid;
2950 }
2951
2952 fn calc_poly_cross_paths(&mut self, poly_id: usize) {
2953 let ring_nodes = self.polys[poly_id - 1].ring_nodes.clone();
2954 let owner_paths = match self.polys[poly_id - 1].owner {
2955 OwnerRef::Tree => self.owned_paths.clone(),
2956 OwnerRef::Poly(owner_id) => self
2957 .polys
2958 .get(owner_id.saturating_sub(1))
2959 .map(|poly| poly.owned_paths.clone())
2960 .unwrap_or_default(),
2961 _ => Vec::new(),
2962 };
2963 let mut cross_paths = Vec::new();
2964 let nn = ring_nodes.len();
2965 for i in 2..nn {
2966 for j in 0..i - 1 {
2967 if i == nn - 1 && j == 0 {
2968 continue;
2969 }
2970 if let Some(path_id) =
2971 self.find_any_path_in(&owner_paths, ring_nodes[i], ring_nodes[j])
2972 {
2973 push_unique(&mut cross_paths, path_id);
2974 }
2975 }
2976 }
2977 self.polys[poly_id - 1].cross_paths = cross_paths;
2978 }
2979
2980 fn build_poly_contents_geometry(&mut self, poly_id: usize) -> Result<()> {
2981 if !self.polys[poly_id - 1].owned_nodes.is_empty() {
2982 return Ok(());
2983 }
2984
2985 let ring_nodes = self.polys[poly_id - 1].ring_nodes.clone();
2986 let nn = ring_nodes.len();
2987 if nn < 3 {
2988 return Err(TreeError::InvalidOperation(
2989 "polygon contents require at least three ring nodes",
2990 ));
2991 }
2992
2993 if nn == 3 {
2994 let p1 = self.nodes[ring_nodes[0] - 1].loc;
2995 let p2 = self.nodes[ring_nodes[1] - 1].loc;
2996 let p3 = self.nodes[ring_nodes[2] - 1].loc;
2997 let node_id = self.create_sub_node(poly_id, incenter(p1, p2, p3));
2998 self.nodes[node_id - 1].is_junction = true;
2999 self.nodes[node_id - 1].elevation =
3000 self.nodes[ring_nodes[0] - 1].elevation + inradius(p1, p2, p3);
3001 self.polys[poly_id - 1].inset_nodes = vec![node_id, node_id, node_id];
3002
3003 for ring_node in ring_nodes {
3004 let path_id = self.create_sub_path(poly_id, ring_node, node_id, false);
3005 self.polys[poly_id - 1].spoke_paths.push(path_id);
3006 }
3007 } else {
3008 self.build_inset_poly_contents(poly_id)?;
3009 }
3010
3011 self.build_poly_creases_and_facets(poly_id)?;
3012 Ok(())
3013 }
3014
3015 fn build_inset_poly_contents(&mut self, poly_id: usize) -> Result<()> {
3016 let ring_nodes = self.polys[poly_id - 1].ring_nodes.clone();
3017 let nn = ring_nodes.len();
3018 let mut r = vec![Point { x: 0.0, y: 0.0 }; nn];
3019 let mut rp = vec![Point { x: 0.0, y: 0.0 }; nn];
3020 let mut rn = vec![Point { x: 0.0, y: 0.0 }; nn];
3021 let mut mr = vec![0.0; nn];
3022
3023 for i in 0..nn {
3024 let ip = (i + nn - 1) % nn;
3025 let inext = (i + 1) % nn;
3026 let nip = self.nodes[ring_nodes[ip] - 1].loc;
3027 let nii = self.nodes[ring_nodes[i] - 1].loc;
3028 let nin = self.nodes[ring_nodes[inext] - 1].loc;
3029 rp[i] = normalize(point_sub(nip, nii));
3030 rn[i] = normalize(point_sub(nin, nii));
3031 let bis = normalize(rotate_ccw90(point_sub(rn[i], rp[i])));
3032 r[i] = point_div(bis, inner(bis, rotate_ccw90(rn[i])));
3033 mr[i] = inner(r[i], rp[i]);
3034 }
3035
3036 let owner_paths = self.owner_paths_for_poly(poly_id);
3037 let h = self.calc_poly_inset_distance(poly_id, &owner_paths, &r, &rn, &mr)?;
3038
3039 let mut inset_nodes = Vec::with_capacity(nn);
3040 for i in 0..nn {
3041 let p = point_add(self.nodes[ring_nodes[i] - 1].loc, point_mul(r[i], h));
3042 inset_nodes.push(self.get_or_make_inset_node(poly_id, p));
3043 }
3044 self.polys[poly_id - 1].inset_nodes = inset_nodes.clone();
3045
3046 let owned_nodes = self.polys[poly_id - 1].owned_nodes.clone();
3047 for node_id in owned_nodes.iter().copied() {
3048 self.nodes[node_id - 1].elevation = self.nodes[ring_nodes[0] - 1].elevation + h;
3049 }
3050
3051 match owned_nodes.len() {
3052 0 => {
3053 return Err(TreeError::InvalidOperation(
3054 "polygon inset produced no owned nodes",
3055 ));
3056 }
3057 1 | 2 => {
3058 self.create_spoke_paths(poly_id, &ring_nodes, &inset_nodes);
3059 if owned_nodes.len() == 2 {
3060 let ridge =
3061 self.create_sub_path(poly_id, owned_nodes[0], owned_nodes[1], false);
3062 self.polys[poly_id - 1].ridge_path = Some(ridge);
3063 }
3064 }
3065 _ => {
3066 for dij in 1..nn {
3067 for i in 0..=nn - dij {
3068 let j = (i + dij) % nn;
3069 let ni = ring_nodes[i];
3070 let nj = ring_nodes[j];
3071 let rni = inset_nodes[i];
3072 let rnj = inset_nodes[j];
3073 if rni == rnj || self.find_leaf_path_between_any(rni, rnj).is_some() {
3074 continue;
3075 }
3076
3077 let outset_path = self
3078 .find_any_path_in(&owner_paths, ni, nj)
3079 .ok_or(TreeError::InvalidOperation("missing outset path"))?;
3080 let i_reduction = h * mr[i];
3081 let j_reduction = h * mr[j];
3082
3083 let (front, back, front_reduction, back_reduction) =
3084 if self.paths[outset_path - 1].nodes.first().copied() == Some(ni) {
3085 (rni, rnj, i_reduction, j_reduction)
3086 } else {
3087 (rnj, rni, j_reduction, i_reduction)
3088 };
3089 let path_id = self.create_sub_path(poly_id, front, back, true);
3090 let min_paper_length = self.paths[outset_path - 1].min_paper_length
3091 - (front_reduction + back_reduction);
3092 let act_paper_length =
3093 self.nodes[rni - 1].loc.distance(self.nodes[rnj - 1].loc);
3094 let outset_active = self.paths[outset_path - 1].is_active;
3095 let path = &mut self.paths[path_id - 1];
3096 path.outset_path = Some(outset_path);
3097 path.front_reduction = front_reduction;
3098 path.back_reduction = back_reduction;
3099 path.min_paper_length = min_paper_length;
3100 path.act_paper_length = act_paper_length;
3101 path.min_tree_length = min_paper_length / self.scale;
3102 path.act_tree_length = act_paper_length / self.scale;
3103 path.is_active =
3104 outset_active || is_tiny(act_paper_length - min_paper_length);
3105 path.is_border = dij == 1;
3106 path.is_polygon = path.is_active || path.is_border;
3107 }
3108 }
3109
3110 let owned_paths = self.polys[poly_id - 1].owned_paths.clone();
3111 self.build_polys_from_paths(&owned_paths, &inset_nodes, OwnerRef::Poly(poly_id))?;
3112
3113 let owned_polys = self.polys[poly_id - 1].owned_polys.clone();
3114 for sub_poly_id in owned_polys {
3115 self.build_poly_contents_geometry(sub_poly_id)?;
3116 }
3117
3118 self.create_spoke_paths(poly_id, &ring_nodes, &inset_nodes);
3119 }
3120 }
3121
3122 Ok(())
3123 }
3124
3125 fn calc_poly_inset_distance(
3126 &self,
3127 poly_id: usize,
3128 owner_paths: &[usize],
3129 r: &[Point],
3130 rn: &[Point],
3131 mr: &[TmFloat],
3132 ) -> Result<TmFloat> {
3133 let ring_nodes = &self.polys[poly_id - 1].ring_nodes;
3134 let nn = ring_nodes.len();
3135 let mut h = 1.0e10;
3136
3137 for i in 0..nn - 1 {
3138 for j in i + 1..nn {
3139 if are_parallel(r[i], r[j]) && inner(r[i], r[j]) > 0.0 {
3140 continue;
3141 }
3142
3143 let ni = self.nodes[ring_nodes[i] - 1].loc;
3144 let nj = self.nodes[ring_nodes[j] - 1].loc;
3145 if j == i + 1 || (i == 0 && j == nn - 1) {
3146 let Some(bi) = line_intersection_point_exact(ni, r[i], nj, r[j]) else {
3147 return Err(TreeError::InvalidOperation(
3148 "adjacent inset bisectors are parallel",
3149 ));
3150 };
3151 let h1 = inner(point_sub(bi, ni), rotate_ccw90(rn[i]));
3152 if h1 > 0.0 && h > h1 {
3153 h = h1;
3154 }
3155 } else {
3156 let path_id = self
3157 .find_any_path_in(owner_paths, ring_nodes[i], ring_nodes[j])
3158 .ok_or(TreeError::InvalidOperation("missing poly cross path"))?;
3159 let lij = self.paths[path_id - 1].min_paper_length;
3160 let u = point_sub(ni, nj);
3161 let v = point_sub(r[i], r[j]);
3162 let w = mr[i] + mr[j];
3163 let a = mag2(v) - w.powi(2);
3164 let b = inner(u, v) + lij * w;
3165 let c = mag2(u) - lij.powi(2);
3166 let d = b.powi(2) - a * c;
3167 if d < 0.0 {
3168 continue;
3169 }
3170
3171 let sd = d.sqrt();
3172 for h1 in [(-b + sd) / a, (-b - sd) / a] {
3173 let lijp = lij - h1 * w;
3174 if lijp > 0.0 && h1 > 0.0 && h > h1 {
3175 h = h1;
3176 }
3177 }
3178 }
3179 }
3180 }
3181
3182 if h == 1.0e10 {
3183 return Err(TreeError::InvalidOperation(
3184 "polygon inset distance was not found",
3185 ));
3186 }
3187 Ok(h)
3188 }
3189
3190 fn create_spoke_paths(&mut self, poly_id: usize, ring_nodes: &[usize], inset_nodes: &[usize]) {
3191 for (ring_node, inset_node) in ring_nodes.iter().copied().zip(inset_nodes.iter().copied()) {
3192 let path_id = self.create_sub_path(poly_id, ring_node, inset_node, false);
3193 self.polys[poly_id - 1].spoke_paths.push(path_id);
3194 }
3195 }
3196
3197 fn create_sub_node(&mut self, poly_id: usize, loc: Point) -> usize {
3198 let index = self.nodes.len() + 1;
3199 self.nodes.push(Node {
3200 index,
3201 label: String::new(),
3202 loc,
3203 depth: DEPTH_NOT_SET,
3204 elevation: 0.0,
3205 is_leaf: false,
3206 is_sub: true,
3207 is_border: false,
3208 is_pinned: false,
3209 is_polygon: false,
3210 is_junction: false,
3211 is_conditioned: false,
3212 owned_vertices: Vec::new(),
3213 edges: Vec::new(),
3214 leaf_paths: Vec::new(),
3215 owner: OwnerRef::Poly(poly_id),
3216 });
3217 self.polys[poly_id - 1].owned_nodes.push(index);
3218 index
3219 }
3220
3221 fn get_or_make_inset_node(&mut self, poly_id: usize, loc: Point) -> usize {
3222 let owned_nodes = self.polys[poly_id - 1].owned_nodes.clone();
3223 for node_id in owned_nodes {
3224 if self.nodes[node_id - 1].loc.distance(loc) < DIST_TOL {
3225 self.nodes[node_id - 1].is_junction = true;
3226 return node_id;
3227 }
3228 }
3229 self.create_sub_node(poly_id, loc)
3230 }
3231
3232 fn create_sub_path(
3233 &mut self,
3234 poly_id: usize,
3235 front_node: usize,
3236 back_node: usize,
3237 connect_leaf_path: bool,
3238 ) -> usize {
3239 let index = self.paths.len() + 1;
3240 self.paths.push(Path {
3241 index,
3242 min_tree_length: 0.0,
3243 min_paper_length: 0.0,
3244 act_tree_length: 0.0,
3245 act_paper_length: 0.0,
3246 is_leaf: false,
3247 is_sub: true,
3248 is_feasible: false,
3249 is_active: false,
3250 is_border: false,
3251 is_polygon: false,
3252 is_conditioned: false,
3253 fwd_poly: None,
3254 bkd_poly: None,
3255 nodes: vec![front_node, back_node],
3256 edges: Vec::new(),
3257 outset_path: None,
3258 front_reduction: 0.0,
3259 back_reduction: 0.0,
3260 min_depth: DEPTH_NOT_SET,
3261 min_depth_dist: DEPTH_NOT_SET,
3262 owned_vertices: Vec::new(),
3263 owned_creases: Vec::new(),
3264 owner: OwnerRef::Poly(poly_id),
3265 });
3266 self.polys[poly_id - 1].owned_paths.push(index);
3267 if connect_leaf_path {
3268 self.nodes[front_node - 1].leaf_paths.push(index);
3269 self.nodes[back_node - 1].leaf_paths.push(index);
3270 }
3271 index
3272 }
3273
3274 fn owner_paths_for_poly(&self, poly_id: usize) -> Vec<usize> {
3275 match self.polys[poly_id - 1].owner {
3276 OwnerRef::Tree => self.owned_paths.clone(),
3277 OwnerRef::Poly(owner_id) => self
3278 .polys
3279 .get(owner_id.saturating_sub(1))
3280 .map(|poly| poly.owned_paths.clone())
3281 .unwrap_or_default(),
3282 _ => Vec::new(),
3283 }
3284 }
3285
3286 fn find_leaf_path_between_any(&self, node1: usize, node2: usize) -> Option<usize> {
3287 self.nodes[node1 - 1]
3288 .leaf_paths
3289 .iter()
3290 .copied()
3291 .find(|path_id| {
3292 self.paths
3293 .get(path_id.saturating_sub(1))
3294 .and_then(|path| path.nodes.first().copied().zip(path.nodes.last().copied()))
3295 .is_some_and(|(a, b)| a == node2 || b == node2)
3296 })
3297 }
3298
3299 fn build_poly_creases_and_facets(&mut self, poly_id: usize) -> Result<()> {
3300 let nn = self.polys[poly_id - 1].ring_nodes.len();
3301
3302 for i in 0..nn {
3303 let front_node = self.polys[poly_id - 1].ring_nodes[i];
3304 let back_node = self.polys[poly_id - 1].ring_nodes[(i + 1) % nn];
3305 let path_id = self.polys[poly_id - 1].ring_paths[i];
3306
3307 if self.path_is_axial(path_id) || self.path_is_gusset(path_id) {
3308 self.build_self_vertices(path_id)?;
3309 }
3310
3311 if self.path_is_active_axial(path_id) || self.path_is_gusset(path_id) {
3312 let (_, ridge_paths) =
3313 self.get_ridgeline_nodes_and_paths(poly_id, front_node, back_node)?;
3314 let bottom_vertices = self.paths[path_id - 1].owned_vertices.clone();
3315 let p1 = self.nodes[front_node - 1].loc;
3316 let p2 = self.nodes[back_node - 1].loc;
3317 for bottom_vertex in bottom_vertices {
3318 let bottom_loc = self.vertices[bottom_vertex - 1].loc;
3319 let tree_node = self.vertices[bottom_vertex - 1].tree_node;
3320 for ridge_path in ridge_paths.iter().copied() {
3321 let q1 = self.nodes[self.paths[ridge_path - 1].nodes[0] - 1].loc;
3322 let q2 =
3323 self.nodes[*self.paths[ridge_path - 1].nodes.last().unwrap() - 1].loc;
3324 if let Some(q) = project_p_to_q(p1, p2, bottom_loc, q1, q2) {
3325 let top_vertex = self.get_or_make_path_vertex(ridge_path, q, tree_node);
3326 let outermost_poly = self.outermost_poly(poly_id);
3327 self.get_or_make_crease(
3328 OwnerRef::Poly(outermost_poly),
3329 bottom_vertex,
3330 top_vertex,
3331 CREASE_UNFOLDED_HINGE,
3332 )?;
3333 }
3334 }
3335 }
3336 }
3337 }
3338
3339 for i in 0..nn {
3340 let front_node = self.polys[poly_id - 1].ring_nodes[i];
3341 let back_node = self.polys[poly_id - 1].ring_nodes[(i + 1) % nn];
3342 let path_id = self.polys[poly_id - 1].ring_paths[i];
3343
3344 if self.path_is_axial(path_id) || self.path_is_gusset(path_id) {
3345 let ridge_vertices = self.get_ridgeline_vertices(poly_id, front_node, back_node)?;
3346 if let Some((&first, rest)) = ridge_vertices.split_first() {
3347 let outermost_poly = self.outermost_poly(poly_id);
3348 let mut ridge_vertex = first;
3349 for next_ridge_vertex in rest.iter().copied() {
3350 self.get_or_make_crease(
3351 OwnerRef::Poly(outermost_poly),
3352 ridge_vertex,
3353 next_ridge_vertex,
3354 CREASE_RIDGE,
3355 )?;
3356 ridge_vertex = next_ridge_vertex;
3357 }
3358 }
3359
3360 if self.path_is_axial(path_id) && !self.paths[path_id - 1].is_active {
3361 self.propagate_inactive_axial_hinges(poly_id, path_id, &ridge_vertices)?;
3362 }
3363 }
3364
3365 if self.path_is_axial(path_id) || self.path_is_gusset(path_id) {
3366 let crease_kind = if self.path_is_axial(path_id) {
3367 CREASE_AXIAL
3368 } else {
3369 CREASE_GUSSET
3370 };
3371 self.connect_self_vertices(path_id, crease_kind)?;
3372 }
3373 }
3374
3375 if self.polys[poly_id - 1].is_sub_poly {
3376 return Ok(());
3377 }
3378
3379 let mut facet_creases = self.polys[poly_id - 1].owned_creases.clone();
3380 for path_id in self.polys[poly_id - 1].ring_paths.clone() {
3381 for crease_id in self.paths[path_id - 1].owned_creases.iter().copied() {
3382 push_unique(&mut facet_creases, crease_id);
3383 }
3384 }
3385 self.build_facets_from_creases(poly_id, &facet_creases)
3386 }
3387
3388 fn path_is_axial(&self, path_id: usize) -> bool {
3389 let path = &self.paths[path_id - 1];
3390 path.is_leaf && path.is_polygon
3391 }
3392
3393 fn path_is_active_axial(&self, path_id: usize) -> bool {
3394 let path = &self.paths[path_id - 1];
3395 path.is_active && path.is_leaf
3396 }
3397
3398 fn path_is_gusset(&self, path_id: usize) -> bool {
3399 let path = &self.paths[path_id - 1];
3400 path.is_active && !path.is_border
3401 }
3402
3403 fn get_or_make_node_vertex(&mut self, node_id: usize) -> usize {
3404 if let Some(vertex_id) = self.nodes[node_id - 1].owned_vertices.first().copied() {
3405 return vertex_id;
3406 }
3407 let index = self.vertices.len() + 1;
3408 let node = &self.nodes[node_id - 1];
3409 self.vertices.push(Vertex {
3410 index,
3411 loc: node.loc,
3412 elevation: node.elevation,
3413 is_border: node.is_border,
3414 tree_node: (!node.is_sub).then_some(node_id),
3415 left_pseudohinge_mate: None,
3416 right_pseudohinge_mate: None,
3417 creases: Vec::new(),
3418 depth: DEPTH_NOT_SET,
3419 discrete_depth: usize::MAX,
3420 cc_flag: 0,
3421 st_flag: 0,
3422 owner: OwnerRef::Node(node_id),
3423 });
3424 self.nodes[node_id - 1].owned_vertices.push(index);
3425 index
3426 }
3427
3428 fn get_or_make_path_vertex(
3429 &mut self,
3430 path_id: usize,
3431 loc: Point,
3432 tree_node: Option<usize>,
3433 ) -> usize {
3434 let front_node = self.paths[path_id - 1].nodes[0];
3435 let back_node = *self.paths[path_id - 1].nodes.last().unwrap();
3436 let vertex_id = if vertices_same_loc(loc, self.nodes[front_node - 1].loc) {
3437 self.get_or_make_node_vertex(front_node)
3438 } else if vertices_same_loc(loc, self.nodes[back_node - 1].loc) {
3439 self.get_or_make_node_vertex(back_node)
3440 } else if let Some(vertex_id) = self.paths[path_id - 1]
3441 .owned_vertices
3442 .iter()
3443 .copied()
3444 .find(|vertex_id| vertices_same_loc(loc, self.vertices[*vertex_id - 1].loc))
3445 {
3446 vertex_id
3447 } else {
3448 self.make_path_vertex(path_id, loc, tree_node)
3449 };
3450
3451 if self.vertices[vertex_id - 1].tree_node.is_none() && tree_node.is_some() {
3452 self.vertices[vertex_id - 1].tree_node = tree_node;
3453 }
3454 vertex_id
3455 }
3456
3457 fn make_path_vertex(&mut self, path_id: usize, loc: Point, tree_node: Option<usize>) -> usize {
3458 let front_node = self.paths[path_id - 1].nodes[0];
3459 let back_node = *self.paths[path_id - 1].nodes.last().unwrap();
3460 let p1 = self.nodes[front_node - 1].loc;
3461 let p2 = self.nodes[back_node - 1].loc;
3462 let dist_p = loc.distance(p1);
3463 let x = dist_p / p2.distance(p1);
3464 let elevation = (1.0 - x) * self.nodes[front_node - 1].elevation
3465 + x * self.nodes[back_node - 1].elevation;
3466
3467 let index = self.vertices.len() + 1;
3468 self.vertices.push(Vertex {
3469 index,
3470 loc,
3471 elevation,
3472 is_border: self.paths[path_id - 1].is_border,
3473 tree_node,
3474 left_pseudohinge_mate: None,
3475 right_pseudohinge_mate: None,
3476 creases: Vec::new(),
3477 depth: DEPTH_NOT_SET,
3478 discrete_depth: usize::MAX,
3479 cc_flag: 0,
3480 st_flag: 0,
3481 owner: OwnerRef::Path(path_id),
3482 });
3483
3484 let insert_at = self.paths[path_id - 1]
3485 .owned_vertices
3486 .iter()
3487 .position(|vertex_id| dist_p < self.vertices[*vertex_id - 1].loc.distance(p1));
3488 if let Some(pos) = insert_at {
3489 self.paths[path_id - 1].owned_vertices.insert(pos, index);
3490 } else {
3491 self.paths[path_id - 1].owned_vertices.push(index);
3492 }
3493
3494 let split = self.paths[path_id - 1]
3495 .owned_creases
3496 .iter()
3497 .copied()
3498 .find_map(|crease_id| {
3499 let crease = &self.creases[crease_id - 1];
3500 let front_vertex = crease.vertices[0];
3501 let back_vertex = crease.vertices[1];
3502 let pc1 = self.vertices[front_vertex - 1].loc;
3503 let pc2 = self.vertices[back_vertex - 1].loc;
3504 let pc21 = point_sub(pc2, pc1);
3505 let x = inner(point_sub(loc, pc1), pc21) / mag2(pc21);
3506 (x > 0.0 && x < 1.0).then_some((crease_id, front_vertex, back_vertex, crease.kind))
3507 });
3508 if let Some((crease_id, front_vertex, back_vertex, kind)) = split {
3509 let _ = self.create_crease(OwnerRef::Path(path_id), front_vertex, index, kind);
3510 let _ = self.create_crease(OwnerRef::Path(path_id), index, back_vertex, kind);
3511 self.delete_creases(&[crease_id]);
3512 }
3513
3514 index
3515 }
3516
3517 fn build_self_vertices(&mut self, path_id: usize) -> Result<()> {
3518 let front_node = self.paths[path_id - 1].nodes[0];
3519 let back_node = *self.paths[path_id - 1].nodes.last().unwrap();
3520 let front_vertex = self.get_or_make_node_vertex(front_node);
3521 let back_vertex = self.get_or_make_node_vertex(back_node);
3522
3523 if !self.paths[path_id - 1].owned_vertices.is_empty() {
3524 return Ok(());
3525 }
3526 if !self.paths[path_id - 1].is_active {
3527 return Ok(());
3528 }
3529
3530 let q1 = self.vertices[front_vertex - 1].loc;
3531 let q2 = self.vertices[back_vertex - 1].loc;
3532 let act_paper_length = self.paths[path_id - 1].act_paper_length;
3533 if act_paper_length == 0.0 {
3534 return Ok(());
3535 }
3536 let qu = point_div(point_sub(q2, q1), act_paper_length);
3537 let (max_outset_path, max_front_reduction, _) = self.max_outset_path(path_id);
3538 let mut cur_pos = -max_front_reduction;
3539 let edge_ids = self.paths[max_outset_path - 1].edges.clone();
3540 let node_ids = self.paths[max_outset_path - 1].nodes.clone();
3541 for (i, edge_id) in edge_ids.iter().copied().enumerate() {
3542 let cur_node = node_ids[i + 1];
3543 cur_pos += self.edges[edge_id - 1].strained_length() * self.scale;
3544 if cur_pos <= 0.0 {
3545 continue;
3546 }
3547 if cur_pos >= act_paper_length {
3548 break;
3549 }
3550 self.get_or_make_path_vertex(
3551 path_id,
3552 point_add(q1, point_mul(qu, cur_pos)),
3553 Some(cur_node),
3554 );
3555 }
3556 Ok(())
3557 }
3558
3559 fn max_outset_path(&self, path_id: usize) -> (usize, TmFloat, TmFloat) {
3560 if let Some(outset_path) = self.paths[path_id - 1].outset_path {
3561 let (max_path, front, back) = self.max_outset_path(outset_path);
3562 (
3563 max_path,
3564 front + self.paths[path_id - 1].front_reduction,
3565 back + self.paths[path_id - 1].back_reduction,
3566 )
3567 } else {
3568 (path_id, 0.0, 0.0)
3569 }
3570 }
3571
3572 fn connect_self_vertices(&mut self, path_id: usize, kind: i32) -> Result<()> {
3573 let front_node = self.paths[path_id - 1].nodes[0];
3574 let back_node = *self.paths[path_id - 1].nodes.last().unwrap();
3575 let mut front_vertex = self
3576 .nodes
3577 .get(front_node - 1)
3578 .and_then(|node| node.owned_vertices.first().copied())
3579 .ok_or(TreeError::InvalidOperation("path front vertex missing"))?;
3580 for back_vertex in self.paths[path_id - 1].owned_vertices.clone() {
3581 self.get_or_make_crease(OwnerRef::Path(path_id), front_vertex, back_vertex, kind)?;
3582 front_vertex = back_vertex;
3583 }
3584 let back_vertex = self
3585 .nodes
3586 .get(back_node - 1)
3587 .and_then(|node| node.owned_vertices.first().copied())
3588 .ok_or(TreeError::InvalidOperation("path back vertex missing"))?;
3589 self.get_or_make_crease(OwnerRef::Path(path_id), front_vertex, back_vertex, kind)?;
3590 Ok(())
3591 }
3592
3593 fn get_ridgeline_nodes_and_paths(
3594 &self,
3595 poly_id: usize,
3596 front_node: usize,
3597 back_node: usize,
3598 ) -> Result<(Vec<usize>, Vec<usize>)> {
3599 let poly = &self.polys[poly_id - 1];
3600 let front_offset = poly
3601 .ring_nodes
3602 .iter()
3603 .position(|node| *node == front_node)
3604 .ok_or(TreeError::InvalidOperation(
3605 "ridgeline front node not in poly",
3606 ))?;
3607 let back_offset = poly
3608 .ring_nodes
3609 .iter()
3610 .position(|node| *node == back_node)
3611 .ok_or(TreeError::InvalidOperation(
3612 "ridgeline back node not in poly",
3613 ))?;
3614
3615 let mut ridge_nodes = vec![front_node];
3616 let mut ridge_paths = vec![poly.spoke_paths[front_offset]];
3617 match poly.owned_nodes.len() {
3618 1 => ridge_nodes.push(poly.owned_nodes[0]),
3619 2 => {
3620 let front_inset = poly.inset_nodes[front_offset];
3621 let back_inset = poly.inset_nodes[back_offset];
3622 ridge_nodes.push(front_inset);
3623 if front_inset != back_inset {
3624 ridge_paths.push(
3625 poly.ridge_path
3626 .ok_or(TreeError::InvalidOperation("ridgeline path missing"))?,
3627 );
3628 ridge_nodes.push(back_inset);
3629 }
3630 }
3631 _ => {
3632 let front_inset = poly.inset_nodes[front_offset];
3633 let back_inset = poly.inset_nodes[back_offset];
3634 if front_inset == back_inset {
3635 ridge_nodes.push(front_inset);
3636 } else {
3637 let sub_poly = poly
3638 .owned_polys
3639 .iter()
3640 .copied()
3641 .find(|sub_poly_id| {
3642 let sub_poly = &self.polys[*sub_poly_id - 1];
3643 sub_poly.ring_nodes.contains(&front_inset)
3644 && sub_poly.ring_nodes.contains(&back_inset)
3645 })
3646 .ok_or(TreeError::InvalidOperation("ridgeline subpoly missing"))?;
3647 let (sub_nodes, sub_paths) =
3648 self.get_ridgeline_nodes_and_paths(sub_poly, front_inset, back_inset)?;
3649 ridge_nodes.extend(sub_nodes);
3650 ridge_paths.extend(sub_paths);
3651 }
3652 }
3653 }
3654 ridge_paths.push(poly.spoke_paths[back_offset]);
3655 ridge_nodes.push(back_node);
3656 Ok((ridge_nodes, ridge_paths))
3657 }
3658
3659 fn get_ridgeline_vertices(
3660 &mut self,
3661 poly_id: usize,
3662 front_node: usize,
3663 back_node: usize,
3664 ) -> Result<Vec<usize>> {
3665 let (ridge_nodes, ridge_paths) =
3666 self.get_ridgeline_nodes_and_paths(poly_id, front_node, back_node)?;
3667 let p1 = self.nodes[front_node - 1].loc;
3668 let p2 = self.nodes[back_node - 1].loc;
3669 let mut vertices = Vec::new();
3670 for ridge_node in ridge_nodes {
3671 if self.nodes[ridge_node - 1].is_junction {
3672 self.get_or_make_node_vertex(ridge_node);
3673 }
3674 if let Some(vertex_id) = self.nodes[ridge_node - 1].owned_vertices.first().copied() {
3675 vertices.push(vertex_id);
3676 }
3677 }
3678 for ridge_path in ridge_paths {
3679 vertices.extend(self.paths[ridge_path - 1].owned_vertices.iter().copied());
3680 }
3681 vertices.sort_by(|a, b| {
3682 sortable_ridge_vertex_value(self.vertices[*a - 1].loc, p1, p2).total_cmp(
3683 &sortable_ridge_vertex_value(self.vertices[*b - 1].loc, p1, p2),
3684 )
3685 });
3686 Ok(vertices)
3687 }
3688
3689 fn propagate_inactive_axial_hinges(
3690 &mut self,
3691 poly_id: usize,
3692 path_id: usize,
3693 ridge_vertices: &[usize],
3694 ) -> Result<()> {
3695 if ridge_vertices.len() < 3 {
3696 return Ok(());
3697 }
3698 let front_node = self.paths[path_id - 1].nodes[0];
3699 let back_node = *self.paths[path_id - 1].nodes.last().unwrap();
3700 let front_vertex = self
3701 .nodes
3702 .get(front_node - 1)
3703 .and_then(|node| node.owned_vertices.first().copied())
3704 .ok_or(TreeError::InvalidOperation(
3705 "inactive axial front vertex missing",
3706 ))?;
3707 let back_vertex = self
3708 .nodes
3709 .get(back_node - 1)
3710 .and_then(|node| node.owned_vertices.first().copied())
3711 .ok_or(TreeError::InvalidOperation(
3712 "inactive axial back vertex missing",
3713 ))?;
3714 let p1 = self.vertices[front_vertex - 1].loc;
3715 let p2 = self.vertices[back_vertex - 1].loc;
3716 let mut crease0 = None;
3717 let mut crease1 = None;
3718 let mut crease2 = None;
3719
3720 for m in 1..ridge_vertices.len() - 1 {
3721 let ridge_vertex = ridge_vertices[m];
3722 let tree_node = self.vertices[ridge_vertex - 1].tree_node;
3723 let mut needs_crease = tree_node.is_some();
3724 let mut kind = CREASE_UNFOLDED_HINGE;
3725 if !needs_crease {
3726 let prev_tree_node = self.vertices[ridge_vertices[m - 1] - 1].tree_node;
3727 let next_tree_node = self.vertices[ridge_vertices[m + 1] - 1].tree_node;
3728 if prev_tree_node.is_some() && prev_tree_node == next_tree_node {
3729 needs_crease = true;
3730 kind = CREASE_PSEUDOHINGE;
3731 }
3732 }
3733 if !needs_crease {
3734 continue;
3735 }
3736 if let Some(p) = project_q_to_p(self.vertices[ridge_vertex - 1].loc, p1, p2) {
3737 let bottom_vertex = self.get_or_make_path_vertex(path_id, p, tree_node);
3738 crease2 = crease1;
3739 crease1 = crease0;
3740 crease0 = Some(self.get_or_make_crease(
3741 OwnerRef::Poly(self.outermost_poly(poly_id)),
3742 bottom_vertex,
3743 ridge_vertex,
3744 kind,
3745 )?);
3746 }
3747 if let (Some(c0), Some(c1), Some(c2)) = (crease0, crease1, crease2)
3748 && self.creases[c0 - 1].kind == CREASE_UNFOLDED_HINGE
3749 && self.creases[c1 - 1].kind == CREASE_PSEUDOHINGE
3750 && self.creases[c2 - 1].kind == CREASE_UNFOLDED_HINGE
3751 {
3752 let mate0 = self.lower_crease_vertex(c0);
3753 let mate2 = self.lower_crease_vertex(c2);
3754 self.vertices[mate0 - 1].right_pseudohinge_mate = Some(mate2);
3755 self.vertices[mate2 - 1].left_pseudohinge_mate = Some(mate0);
3756 }
3757 }
3758 Ok(())
3759 }
3760
3761 fn outermost_poly(&self, mut poly_id: usize) -> usize {
3762 while let OwnerRef::Poly(owner_id) = self.polys[poly_id - 1].owner {
3763 poly_id = owner_id;
3764 }
3765 poly_id
3766 }
3767
3768 fn lower_crease_vertex(&self, crease_id: usize) -> usize {
3769 let crease = &self.creases[crease_id - 1];
3770 let v1 = crease.vertices[0];
3771 let v2 = crease.vertices[1];
3772 if self.vertices[v1 - 1].elevation < self.vertices[v2 - 1].elevation {
3773 v1
3774 } else {
3775 v2
3776 }
3777 }
3778
3779 fn get_or_make_crease(
3780 &mut self,
3781 owner: OwnerRef,
3782 vertex1: usize,
3783 vertex2: usize,
3784 kind: i32,
3785 ) -> Result<usize> {
3786 let owned_creases = self.owned_creases_for_owner(&owner);
3787 if let Some(crease_id) = owned_creases.into_iter().find(|crease_id| {
3788 let crease = &self.creases[*crease_id - 1];
3789 matches!(
3790 crease.vertices.first().copied().zip(crease.vertices.last().copied()),
3791 Some((a, b)) if (a == vertex1 && b == vertex2) || (a == vertex2 && b == vertex1)
3792 )
3793 }) {
3794 return Ok(crease_id);
3795 }
3796 self.create_crease(owner, vertex1, vertex2, kind)
3797 }
3798
3799 fn create_crease(
3800 &mut self,
3801 owner: OwnerRef,
3802 vertex1: usize,
3803 vertex2: usize,
3804 kind: i32,
3805 ) -> Result<usize> {
3806 if vertex1 == vertex2 {
3807 return Err(TreeError::InvalidOperation(
3808 "crease endpoints must be distinct vertices",
3809 ));
3810 }
3811 let index = self.creases.len() + 1;
3812 self.creases.push(Crease {
3813 index,
3814 kind,
3815 vertices: vec![vertex1, vertex2],
3816 fwd_facet: None,
3817 bkd_facet: None,
3818 fold: FOLD_FLAT,
3819 cc_flag: 0,
3820 st_flag: 0,
3821 owner: owner.clone(),
3822 });
3823 match owner {
3824 OwnerRef::Path(path_id) => self.paths[path_id - 1].owned_creases.push(index),
3825 OwnerRef::Poly(poly_id) => self.polys[poly_id - 1].owned_creases.push(index),
3826 _ => {}
3827 }
3828 self.vertices[vertex1 - 1].creases.push(index);
3829 self.vertices[vertex2 - 1].creases.push(index);
3830 Ok(index)
3831 }
3832
3833 fn owned_creases_for_owner(&self, owner: &OwnerRef) -> Vec<usize> {
3834 match *owner {
3835 OwnerRef::Path(path_id) => self
3836 .paths
3837 .get(path_id.saturating_sub(1))
3838 .map(|path| path.owned_creases.clone())
3839 .unwrap_or_default(),
3840 OwnerRef::Poly(poly_id) => self
3841 .polys
3842 .get(poly_id.saturating_sub(1))
3843 .map(|poly| poly.owned_creases.clone())
3844 .unwrap_or_default(),
3845 _ => Vec::new(),
3846 }
3847 }
3848
3849 fn build_facets_from_creases(&mut self, poly_id: usize, crease_list: &[usize]) -> Result<()> {
3850 if crease_list.is_empty() {
3851 return Ok(());
3852 }
3853 for crease_id in crease_list.iter().copied() {
3854 if self.can_start_facet_fwd(poly_id, crease_id) {
3855 self.build_facet_ring(poly_id, crease_id, true)?;
3856 }
3857 if self.can_start_facet_bkd(poly_id, crease_id) {
3858 self.build_facet_ring(poly_id, crease_id, false)?;
3859 }
3860 }
3861 Ok(())
3862 }
3863
3864 fn can_start_facet_fwd(&self, poly_id: usize, crease_id: usize) -> bool {
3865 let crease = &self.creases[crease_id - 1];
3866 if crease.fwd_facet.is_some() {
3867 return false;
3868 }
3869 if crease.kind != CREASE_AXIAL {
3870 return true;
3871 }
3872 are_ccw(
3873 self.vertices[crease.vertices[0] - 1].loc,
3874 self.vertices[crease.vertices[1] - 1].loc,
3875 self.polys[poly_id - 1].centroid,
3876 )
3877 }
3878
3879 fn can_start_facet_bkd(&self, poly_id: usize, crease_id: usize) -> bool {
3880 let crease = &self.creases[crease_id - 1];
3881 if crease.bkd_facet.is_some() {
3882 return false;
3883 }
3884 if crease.kind != CREASE_AXIAL {
3885 return true;
3886 }
3887 are_cw(
3888 self.vertices[crease.vertices[0] - 1].loc,
3889 self.vertices[crease.vertices[1] - 1].loc,
3890 self.polys[poly_id - 1].centroid,
3891 )
3892 }
3893
3894 fn build_facet_ring(&mut self, poly_id: usize, crease_id: usize, fwd: bool) -> Result<()> {
3895 let facet_id = self.create_facet(poly_id);
3896 if fwd {
3897 self.creases[crease_id - 1].fwd_facet = Some(facet_id);
3898 } else {
3899 self.creases[crease_id - 1].bkd_facet = Some(facet_id);
3900 }
3901
3902 let first_vertex = if fwd {
3903 self.creases[crease_id - 1].vertices[0]
3904 } else {
3905 self.creases[crease_id - 1].vertices[1]
3906 };
3907 let mut this_vertex = if fwd {
3908 self.creases[crease_id - 1].vertices[1]
3909 } else {
3910 self.creases[crease_id - 1].vertices[0]
3911 };
3912 let mut this_crease = crease_id;
3913 let mut vertices = vec![first_vertex];
3914 let mut creases = vec![this_crease];
3915 let mut too_many = 0;
3916
3917 loop {
3918 let (next_crease, next_vertex) =
3919 self.next_crease_and_vertex(this_crease, this_vertex)?;
3920 vertices.push(this_vertex);
3921 creases.push(next_crease);
3922 if self.creases[next_crease - 1].vertices.first().copied() == Some(this_vertex) {
3923 self.creases[next_crease - 1].fwd_facet = Some(facet_id);
3924 } else {
3925 self.creases[next_crease - 1].bkd_facet = Some(facet_id);
3926 }
3927 this_crease = next_crease;
3928 this_vertex = next_vertex;
3929 too_many += 1;
3930 if next_vertex == first_vertex {
3931 break;
3932 }
3933 if too_many >= 100 {
3934 return Err(TreeError::InvalidOperation(
3935 "facet ring walk exceeded TreeMaker guard",
3936 ));
3937 }
3938 }
3939
3940 self.facets[facet_id - 1].vertices = vertices;
3941 self.facets[facet_id - 1].creases = creases;
3942 self.calc_facet_contents(facet_id);
3943 Ok(())
3944 }
3945
3946 fn create_facet(&mut self, poly_id: usize) -> usize {
3947 let index = self.facets.len() + 1;
3948 self.facets.push(Facet {
3949 index,
3950 centroid: Point { x: 0.0, y: 0.0 },
3951 is_well_formed: true,
3952 vertices: Vec::new(),
3953 creases: Vec::new(),
3954 corridor_edge: None,
3955 head_facets: Vec::new(),
3956 tail_facets: Vec::new(),
3957 order: usize::MAX,
3958 color: 0,
3959 owner: OwnerRef::Poly(poly_id),
3960 });
3961 self.polys[poly_id - 1].owned_facets.push(index);
3962 index
3963 }
3964
3965 fn next_crease_and_vertex(
3966 &self,
3967 this_crease: usize,
3968 this_vertex: usize,
3969 ) -> Result<(usize, usize)> {
3970 let crease = &self.creases[this_crease - 1];
3971 let mut that_vertex = crease.vertices[0];
3972 if that_vertex == this_vertex {
3973 that_vertex = crease.vertices[1];
3974 }
3975 let this_angle = angle(point_sub(
3976 self.vertices[that_vertex - 1].loc,
3977 self.vertices[this_vertex - 1].loc,
3978 ));
3979
3980 let mut delta = TWO_PI;
3981 let mut next_crease = None;
3982 let mut next_vertex = None;
3983 for candidate_crease in self.vertices[this_vertex - 1].creases.iter().copied() {
3984 if candidate_crease == this_crease {
3985 continue;
3986 }
3987 let candidate = &self.creases[candidate_crease - 1];
3988 let mut candidate_vertex = candidate.vertices[0];
3989 if candidate_vertex == this_vertex {
3990 candidate_vertex = candidate.vertices[1];
3991 }
3992 let next_angle = angle(point_sub(
3993 self.vertices[candidate_vertex - 1].loc,
3994 self.vertices[this_vertex - 1].loc,
3995 ));
3996 let mut new_delta = this_angle - next_angle;
3997 while new_delta < 0.0 {
3998 new_delta += TWO_PI;
3999 }
4000 while new_delta >= TWO_PI {
4001 new_delta -= TWO_PI;
4002 }
4003 if new_delta < delta {
4004 delta = new_delta;
4005 next_crease = Some(candidate_crease);
4006 next_vertex = Some(candidate_vertex);
4007 }
4008 }
4009
4010 match (next_crease, next_vertex) {
4011 (Some(crease), Some(vertex)) => Ok((crease, vertex)),
4012 _ => Err(TreeError::InvalidOperation(
4013 "facet crease walk could not advance",
4014 )),
4015 }
4016 }
4017
4018 fn calc_facet_contents(&mut self, facet_id: usize) {
4019 let mut centroid = Point { x: 0.0, y: 0.0 };
4020 let vertices = self.facets[facet_id - 1].vertices.clone();
4021 for vertex_id in vertices.iter().copied() {
4022 centroid = point_add(centroid, self.vertices[vertex_id - 1].loc);
4023 }
4024 if !vertices.is_empty() {
4025 centroid = point_div(centroid, vertices.len() as TmFloat);
4026 }
4027 self.facets[facet_id - 1].centroid = centroid;
4028 self.facets[facet_id - 1].is_well_formed = true;
4029
4030 let num_vertices = self.facets[facet_id - 1].vertices.len();
4031 let mut rotations = 0;
4032 while self.facets[facet_id - 1]
4033 .creases
4034 .first()
4035 .is_some_and(|crease_id| !self.crease_is_axial_or_gusset(*crease_id))
4036 {
4037 self.facets[facet_id - 1].vertices.rotate_left(1);
4038 self.facets[facet_id - 1].creases.rotate_left(1);
4039 rotations += 1;
4040 if rotations >= num_vertices {
4041 self.facets[facet_id - 1].is_well_formed = false;
4042 break;
4043 }
4044 }
4045 }
4046
4047 fn crease_is_axial_or_gusset(&self, crease_id: usize) -> bool {
4048 matches!(
4049 self.creases[crease_id - 1].kind,
4050 CREASE_AXIAL | CREASE_GUSSET
4051 )
4052 }
4053
4054 fn find_any_path_in(&self, path_ids: &[usize], node1: usize, node2: usize) -> Option<usize> {
4055 path_ids.iter().copied().find(|path_id| {
4056 self.paths
4057 .get(path_id.saturating_sub(1))
4058 .and_then(|path| path.nodes.first().copied().zip(path.nodes.last().copied()))
4059 .is_some_and(|(a, b)| (a == node1 && b == node2) || (a == node2 && b == node1))
4060 })
4061 }
4062
4063 fn paths_intersect_interior(&self, path1: usize, path2: usize) -> bool {
4064 let path1 = &self.paths[path1 - 1];
4065 let path2 = &self.paths[path2 - 1];
4066 let Some((path1_front, path1_back)) = path1
4067 .nodes
4068 .first()
4069 .copied()
4070 .zip(path1.nodes.last().copied())
4071 else {
4072 return false;
4073 };
4074 let Some((path2_front, path2_back)) = path2
4075 .nodes
4076 .first()
4077 .copied()
4078 .zip(path2.nodes.last().copied())
4079 else {
4080 return false;
4081 };
4082 if path1_front == path2_front
4083 || path1_front == path2_back
4084 || path1_back == path2_front
4085 || path1_back == path2_back
4086 {
4087 return false;
4088 }
4089
4090 let p = self.nodes[path1_front - 1].loc;
4091 let rp = point_sub(self.nodes[path1_back - 1].loc, p);
4092 let q = self.nodes[path2_front - 1].loc;
4093 let rq = point_sub(self.nodes[path2_back - 1].loc, q);
4094 let Some((tp, tq)) = line_intersection_params(p, rp, q, rq) else {
4095 return false;
4096 };
4097 if tp <= 0.0 || tp >= 1.0 || tq <= 0.0 || tq >= 1.0 {
4098 return false;
4099 }
4100 true
4101 }
4102
4103 fn node_centroid(&self, node_ids: &[usize]) -> Point {
4104 let mut centroid = Point { x: 0.0, y: 0.0 };
4105 for node_id in node_ids {
4106 let loc = self.nodes[*node_id - 1].loc;
4107 centroid.x += loc.x;
4108 centroid.y += loc.y;
4109 }
4110 centroid.x /= node_ids.len() as TmFloat;
4111 centroid.y /= node_ids.len() as TmFloat;
4112 centroid
4113 }
4114
4115 fn cleanup_after_edit(&mut self) {
4116 self.is_feasible = false;
4117 self.is_polygon_valid = false;
4118 self.is_polygon_filled = false;
4119 self.is_vertex_depth_valid = false;
4120 self.is_facet_data_valid = false;
4121 self.is_local_root_connectable = false;
4122
4123 self.kill_invalid_conditions();
4124
4125 if self.owned_nodes.is_empty() {
4126 self.needs_cleanup = false;
4127 return;
4128 }
4129
4130 for node_id in self.owned_nodes.iter().copied() {
4131 let node = &mut self.nodes[node_id - 1];
4132 node.loc.x = node.loc.x.clamp(0.0, self.paper_width);
4133 node.loc.y = node.loc.y.clamp(0.0, self.paper_height);
4134 node.is_border = false;
4135 node.is_pinned = false;
4136 node.is_polygon = false;
4137 node.is_conditioned = false;
4138 }
4139
4140 for edge_id in self.owned_edges.iter().copied() {
4141 let edge = &mut self.edges[edge_id - 1];
4142 edge.is_pinned = false;
4143 edge.is_conditioned = false;
4144 }
4145
4146 let node_locs: Vec<Point> = self.nodes.iter().map(|n| n.loc).collect();
4147 let edge_lengths: Vec<TmFloat> = self.edges.iter().map(Edge::strained_length).collect();
4148 for path_id in self.owned_paths.iter().copied() {
4149 let path = &mut self.paths[path_id - 1];
4150 path.min_tree_length = path
4151 .edges
4152 .iter()
4153 .filter_map(|id| edge_lengths.get(id.saturating_sub(1)))
4154 .sum();
4155 path.min_paper_length = path.min_tree_length * self.scale;
4156 if path.is_leaf && path.nodes.len() >= 2 {
4157 let a = node_locs[path.nodes[0] - 1];
4158 let b = node_locs[*path.nodes.last().unwrap() - 1];
4159 path.act_paper_length = a.distance(b);
4160 path.act_tree_length = path.act_paper_length / self.scale;
4161 path.is_feasible = path.act_paper_length >= path.min_paper_length - DIST_TOL;
4162 path.is_active = (path.act_paper_length - path.min_paper_length).abs() < DIST_TOL;
4163 } else {
4164 path.act_paper_length = 0.0;
4165 path.act_tree_length = 0.0;
4166 path.is_feasible = false;
4167 path.is_active = false;
4168 }
4169 path.is_border = false;
4170 path.is_polygon = false;
4171 path.is_conditioned = false;
4172 }
4173
4174 let leaf_nodes = self.leaf_nodes_in_owned_order();
4175 let leaf_paths = self.leaf_paths_in_owned_order();
4176 let leaf_paths_feasible = leaf_paths
4177 .iter()
4178 .copied()
4179 .all(|path_id| self.paths[path_id - 1].is_feasible);
4180 let condition_feasibilities: Vec<bool> = self
4181 .conditions
4182 .iter()
4183 .map(|condition| condition.kind.calc_feasibility(self))
4184 .collect();
4185 for (condition, feasible) in self
4186 .conditions
4187 .iter_mut()
4188 .zip(condition_feasibilities.iter().copied())
4189 {
4190 condition.is_feasible = feasible;
4191 }
4192 let conditions_feasible = condition_feasibilities.into_iter().all(|feasible| feasible);
4193 self.is_feasible = leaf_paths_feasible && conditions_feasible;
4194 self.rebuild_conditioned_flags();
4195 self.calc_border_nodes_and_paths(&leaf_nodes);
4196 self.calc_pinned_nodes_and_edges(&leaf_nodes, &leaf_paths);
4197 self.calc_polygon_network(&leaf_nodes, &leaf_paths);
4198 self.calc_polygon_validity(&leaf_nodes);
4199 self.kill_orphan_vertices_and_creases();
4200 self.promote_first_tree_node_to_root();
4201 self.renumber_part_indices();
4202 self.clear_crease_pattern_cleanup_data();
4203 self.calc_polygon_filled();
4204 if !self.is_polygon_filled {
4205 self.needs_cleanup = false;
4206 return;
4207 }
4208 self.calc_depth_and_bend();
4209 self.calc_vertex_depth_validity();
4210 if !self.is_vertex_depth_valid {
4211 self.needs_cleanup = false;
4212 return;
4213 }
4214 self.calc_facet_data_validity();
4215 if !self.is_facet_data_valid {
4216 self.needs_cleanup = false;
4217 return;
4218 }
4219 self.calc_facet_corridor_edges();
4220 self.calc_facet_order();
4221 if !self.is_local_root_connectable {
4222 self.needs_cleanup = false;
4223 return;
4224 }
4225 self.calc_facet_color();
4226 self.calc_fold_directions();
4227 self.needs_cleanup = false;
4228 }
4229
4230 fn kill_invalid_conditions(&mut self) {
4231 let mut conditions = std::mem::take(&mut self.conditions);
4232 conditions.retain(|condition| self.condition_is_valid(&condition.kind));
4233 for (i, condition) in conditions.iter_mut().enumerate() {
4234 condition.index = i + 1;
4235 }
4236 self.conditions = conditions;
4237 }
4238
4239 fn condition_is_valid(&self, kind: &ConditionKind) -> bool {
4240 let node_is_leaf = |node: usize| {
4241 node.checked_sub(1)
4242 .and_then(|index| self.nodes.get(index))
4243 .is_some_and(|node| node.is_leaf)
4244 };
4245 let node_exists = |node: usize| node > 0 && node <= self.nodes.len();
4246 let edge_exists = |edge: usize| edge > 0 && edge <= self.edges.len();
4247 let path_exists_between =
4248 |node1: usize, node2: usize| self.find_leaf_path_between(node1, node2).is_some();
4249
4250 match *kind {
4251 ConditionKind::NodeCombo { node, .. }
4252 | ConditionKind::NodeFixed { node, .. }
4253 | ConditionKind::NodeOnCorner { node }
4254 | ConditionKind::NodeOnEdge { node }
4255 | ConditionKind::NodeSymmetric { node } => node_is_leaf(node),
4256 ConditionKind::NodesPaired { node1, node2 } => {
4257 node_is_leaf(node1) && node_is_leaf(node2)
4258 }
4259 ConditionKind::NodesCollinear {
4260 node1,
4261 node2,
4262 node3,
4263 } => node_is_leaf(node1) && node_is_leaf(node2) && node_is_leaf(node3),
4264 ConditionKind::EdgeLengthFixed { edge } => edge_exists(edge),
4265 ConditionKind::EdgesSameStrain { edge1, edge2 } => {
4266 edge_exists(edge1) && edge_exists(edge2)
4267 }
4268 ConditionKind::PathActive { node1, node2 }
4269 | ConditionKind::PathAngleFixed { node1, node2, .. }
4270 | ConditionKind::PathAngleQuant { node1, node2, .. } => {
4271 node_exists(node1) && node_exists(node2) && path_exists_between(node1, node2)
4272 }
4273 ConditionKind::PathCombo { node1, node2, .. } => {
4274 node_exists(node1) && node_exists(node2) && path_exists_between(node1, node2)
4275 }
4276 }
4277 }
4278
4279 fn leaf_paths_in_owned_order(&self) -> Vec<usize> {
4280 self.owned_paths
4281 .iter()
4282 .copied()
4283 .filter(|id| self.paths[id - 1].is_leaf)
4284 .collect()
4285 }
4286
4287 fn calc_border_nodes_and_paths(&mut self, leaf_nodes: &[usize]) {
4288 if leaf_nodes.len() < 3 {
4289 return;
4290 }
4291
4292 let start_pt = Point { x: -1.0, y: -1.0 };
4293 let Some((&start_node, _)) = leaf_nodes
4294 .iter()
4295 .map(|id| (id, angle(point_sub(self.nodes[*id - 1].loc, start_pt))))
4296 .min_by(|(_, a), (_, b)| a.total_cmp(b))
4297 else {
4298 return;
4299 };
4300
4301 let mut border_nodes = vec![start_node];
4302 let mut prev_node = start_node;
4303 let mut prev_pt = start_pt;
4304 let mut this_node = start_node;
4305 let mut this_pt = self.nodes[this_node - 1].loc;
4306
4307 loop {
4308 let mut best_node = None;
4309 let mut best_angle = TWO_PI;
4310 let mut best_dist = TmFloat::INFINITY;
4311
4312 for node_id in leaf_nodes.iter().copied() {
4313 if node_id == prev_node || node_id == this_node {
4314 continue;
4315 }
4316 let the_pt = self.nodes[node_id - 1].loc;
4317 let the_angle = angle_change(prev_pt, this_pt, the_pt);
4318 if the_angle < -PI / 2.0 {
4319 continue;
4320 }
4321 let the_dist = this_pt.distance(the_pt);
4322 if the_angle < best_angle - CONVEXITY_TOL
4323 || ((the_angle - best_angle).abs() < CONVEXITY_TOL && the_dist < best_dist)
4324 {
4325 best_node = Some(node_id);
4326 best_angle = the_angle;
4327 best_dist = the_dist;
4328 }
4329 }
4330
4331 let Some(next_node) = best_node else {
4332 return;
4333 };
4334 if next_node == start_node {
4335 break;
4336 }
4337 border_nodes.push(next_node);
4338 prev_node = this_node;
4339 prev_pt = this_pt;
4340 this_node = next_node;
4341 this_pt = self.nodes[this_node - 1].loc;
4342 }
4343
4344 if let Some(first) = border_nodes.first().copied() {
4345 self.nodes[first - 1].is_border = true;
4346 }
4347 for i in 1..border_nodes.len() {
4348 let prev = border_nodes[i - 1];
4349 let next = border_nodes[i];
4350 self.nodes[next - 1].is_border = true;
4351 if let Some(path_id) = self.leaf_path_id_between(prev, next) {
4352 self.paths[path_id - 1].is_border = true;
4353 }
4354 }
4355 if let Some(path_id) = self.leaf_path_id_between(
4356 *border_nodes.last().unwrap(),
4357 *border_nodes.first().unwrap(),
4358 ) {
4359 self.paths[path_id - 1].is_border = true;
4360 }
4361 }
4362
4363 fn calc_pinned_nodes_and_edges(&mut self, leaf_nodes: &[usize], leaf_paths: &[usize]) {
4364 for node_id in leaf_nodes.iter().copied() {
4365 self.nodes[node_id - 1].is_pinned = self.calc_is_pinned_node(node_id);
4366 }
4367
4368 for path_id in leaf_paths.iter().copied() {
4369 let path = &self.paths[path_id - 1];
4370 if !path.is_active || path.nodes.len() < 2 {
4371 continue;
4372 }
4373 let node1 = path.nodes[0];
4374 let node2 = *path.nodes.last().unwrap();
4375 if self.nodes[node1 - 1].is_pinned && self.nodes[node2 - 1].is_pinned {
4376 let edge_ids = path.edges.clone();
4377 for edge_id in edge_ids {
4378 self.edges[edge_id - 1].is_pinned = true;
4379 }
4380 }
4381 }
4382 }
4383
4384 fn calc_is_pinned_node(&self, node_id: usize) -> bool {
4385 let node = &self.nodes[node_id - 1];
4386 let mut angles = Vec::new();
4387 for path_id in &node.leaf_paths {
4388 let path = &self.paths[*path_id - 1];
4389 if !path.is_active || path.nodes.len() < 2 {
4390 continue;
4391 }
4392 let other = if path.nodes[0] == node_id {
4393 *path.nodes.last().unwrap()
4394 } else {
4395 path.nodes[0]
4396 };
4397 angles.push(angle(point_sub(self.nodes[other - 1].loc, node.loc)));
4398 }
4399
4400 if is_tiny(node.loc.x) {
4401 angles.push(-PI);
4402 }
4403 if is_tiny(node.loc.x - self.paper_width) {
4404 angles.push(0.0);
4405 }
4406 if is_tiny(node.loc.y - self.paper_height) {
4407 angles.push(PI / 2.0);
4408 }
4409 if is_tiny(node.loc.y) {
4410 angles.push(-PI / 2.0);
4411 }
4412
4413 if angles.len() < 2 {
4414 return false;
4415 }
4416 angles.sort_by(TmFloat::total_cmp);
4417 for i in 0..angles.len() - 1 {
4418 if angles[i + 1] - angles[i] > PI + CONVEXITY_TOL {
4419 return false;
4420 }
4421 }
4422 angles[0] - angles[angles.len() - 1] + PI <= CONVEXITY_TOL
4423 }
4424
4425 fn calc_polygon_network(&mut self, leaf_nodes: &[usize], leaf_paths: &[usize]) {
4426 for path_id in leaf_paths.iter().copied() {
4427 let path = &mut self.paths[path_id - 1];
4428 path.is_polygon = path.is_active || (path.is_border && path.is_feasible);
4429 }
4430
4431 for node_id in leaf_nodes.iter().copied() {
4432 let node = &mut self.nodes[node_id - 1];
4433 if node.is_pinned || node.is_border {
4434 node.is_polygon = true;
4435 }
4436 }
4437
4438 for path_id in leaf_paths.iter().copied() {
4439 let path = &self.paths[path_id - 1];
4440 if path.is_feasible || path.nodes.len() < 2 {
4441 continue;
4442 }
4443 let node1 = path.nodes[0];
4444 let node2 = *path.nodes.last().unwrap();
4445 self.nodes[node1 - 1].is_polygon = false;
4446 self.nodes[node1 - 1].is_pinned = false;
4447 self.nodes[node2 - 1].is_polygon = false;
4448 self.nodes[node2 - 1].is_pinned = false;
4449 }
4450
4451 loop {
4452 let mut something_changed = false;
4453
4454 for path_id in leaf_paths.iter().copied() {
4455 let path = &self.paths[path_id - 1];
4456 if !path.is_polygon || path.nodes.len() < 2 {
4457 continue;
4458 }
4459 let node1 = path.nodes[0];
4460 let node2 = *path.nodes.last().unwrap();
4461 if !(self.nodes[node1 - 1].is_polygon && self.nodes[node2 - 1].is_polygon) {
4462 self.paths[path_id - 1].is_polygon = false;
4463 something_changed = true;
4464 }
4465 }
4466
4467 for node_id in leaf_nodes.iter().copied() {
4468 if !self.nodes[node_id - 1].is_polygon {
4469 continue;
4470 }
4471 let poly_paths = self.nodes[node_id - 1]
4472 .leaf_paths
4473 .iter()
4474 .filter(|path_id| self.paths[**path_id - 1].is_polygon)
4475 .count();
4476 if poly_paths < 2 {
4477 self.nodes[node_id - 1].is_polygon = false;
4478 something_changed = true;
4479 }
4480 }
4481
4482 if !something_changed {
4483 break;
4484 }
4485 }
4486
4487 let doomed_polys: Vec<usize> = self
4488 .polys
4489 .iter()
4490 .filter(|poly| !self.calc_poly_is_valid(poly.index, leaf_nodes))
4491 .map(|poly| poly.index)
4492 .collect();
4493 if !doomed_polys.is_empty() {
4494 self.delete_polys(&doomed_polys);
4495 }
4496 }
4497
4498 fn calc_polygon_validity(&mut self, leaf_nodes: &[usize]) {
4499 self.is_polygon_valid = true;
4500 for node_id in leaf_nodes.iter().copied() {
4501 let polygon_paths = self.nodes[node_id - 1]
4502 .leaf_paths
4503 .iter()
4504 .filter(|path_id| self.paths[**path_id - 1].is_polygon)
4505 .count();
4506 if polygon_paths < 2 {
4507 self.is_polygon_valid = false;
4508 return;
4509 }
4510 }
4511
4512 for path_id in self.owned_paths.iter().copied() {
4513 let path = &self.paths[path_id - 1];
4514 if !path.is_polygon {
4515 continue;
4516 }
4517 if path.is_border {
4518 if path.fwd_poly.is_none() && path.bkd_poly.is_none() {
4519 self.is_polygon_valid = false;
4520 return;
4521 }
4522 } else if path.fwd_poly.is_none() || path.bkd_poly.is_none() {
4523 self.is_polygon_valid = false;
4524 return;
4525 }
4526 }
4527 }
4528
4529 fn calc_poly_is_valid(&self, poly_id: usize, leaf_nodes: &[usize]) -> bool {
4530 let Some(poly) = self.polys.get(poly_id.saturating_sub(1)) else {
4531 return false;
4532 };
4533 if poly.is_sub_poly {
4534 return true;
4535 }
4536 if poly.node_locs.len() != poly.ring_nodes.len() {
4537 return false;
4538 }
4539 for (i, node_id) in poly.ring_nodes.iter().copied().enumerate() {
4540 let Some(node) = self.nodes.get(node_id.saturating_sub(1)) else {
4541 return false;
4542 };
4543 if poly.node_locs[i].distance(node.loc) > MOVE_TOL {
4544 return false;
4545 }
4546 }
4547 for path_id in poly.ring_paths.iter().copied() {
4548 let Some(path) = self.paths.get(path_id.saturating_sub(1)) else {
4549 return false;
4550 };
4551 if !path.is_polygon {
4552 return false;
4553 }
4554 }
4555 if !self.poly_is_convex(poly) {
4556 return false;
4557 }
4558 !self.poly_encloses_leaf_node(poly, leaf_nodes)
4559 }
4560
4561 fn poly_is_convex(&self, poly: &Poly) -> bool {
4562 let n = poly.ring_nodes.len();
4563 if n < 3 {
4564 return false;
4565 }
4566 for i in 0..n - 2 {
4567 let Some(p1) = self
4568 .nodes
4569 .get(poly.ring_nodes[i].saturating_sub(1))
4570 .map(|node| node.loc)
4571 else {
4572 return false;
4573 };
4574 let Some(p2) = self
4575 .nodes
4576 .get(poly.ring_nodes[(i + 1) % n].saturating_sub(1))
4577 .map(|node| node.loc)
4578 else {
4579 return false;
4580 };
4581 let Some(p3) = self
4582 .nodes
4583 .get(poly.ring_nodes[(i + 2) % n].saturating_sub(1))
4584 .map(|node| node.loc)
4585 else {
4586 return false;
4587 };
4588 if angle_change(p1, p2, p3) < -CONVEXITY_TOL {
4589 return false;
4590 }
4591 }
4592 true
4593 }
4594
4595 fn poly_encloses_leaf_node(&self, poly: &Poly, leaf_nodes: &[usize]) -> bool {
4596 for node_id in leaf_nodes.iter().copied() {
4597 if poly.ring_nodes.contains(&node_id) {
4598 continue;
4599 }
4600 let Some(node) = self.nodes.get(node_id.saturating_sub(1)) else {
4601 continue;
4602 };
4603 if self.poly_convex_encloses(poly, node.loc) {
4604 return true;
4605 }
4606 }
4607 false
4608 }
4609
4610 fn poly_convex_encloses(&self, poly: &Poly, point: Point) -> bool {
4611 for path_id in poly.ring_paths.iter().copied() {
4612 let Some(path) = self.paths.get(path_id.saturating_sub(1)) else {
4613 return false;
4614 };
4615 let Some((node1, node2)) = path.nodes.first().zip(path.nodes.last()) else {
4616 return false;
4617 };
4618 let Some(p1) = self.nodes.get(node1.saturating_sub(1)).map(|node| node.loc) else {
4619 return false;
4620 };
4621 let Some(p2) = self.nodes.get(node2.saturating_sub(1)).map(|node| node.loc) else {
4622 return false;
4623 };
4624 let mut q = rotate_ccw90(point_sub(p2, p1));
4625 if inner(point_sub(poly.centroid, p1), q) < 0.0 {
4626 q.x *= -1.0;
4627 q.y *= -1.0;
4628 }
4629 if inner(point_sub(point, p1), q) < 0.0 {
4630 return false;
4631 }
4632 }
4633 true
4634 }
4635
4636 fn kill_orphan_vertices_and_creases(&mut self) {
4637 let mut doomed_creases: Vec<usize> = self
4638 .creases
4639 .iter()
4640 .filter(|crease| self.crease_is_orphan(crease))
4641 .map(|crease| crease.index)
4642 .collect();
4643
4644 let doomed_vertices: Vec<usize> = self
4645 .vertices
4646 .iter()
4647 .filter(|vertex| self.vertex_is_orphan(vertex))
4648 .map(|vertex| vertex.index)
4649 .collect();
4650
4651 for crease in &self.creases {
4652 if crease
4653 .vertices
4654 .iter()
4655 .any(|vertex_id| doomed_vertices.contains(vertex_id))
4656 {
4657 doomed_creases.push(crease.index);
4658 }
4659 }
4660 doomed_creases.sort_unstable();
4661 doomed_creases.dedup();
4662
4663 self.delete_creases(&doomed_creases);
4664 self.delete_vertices(&doomed_vertices);
4665 }
4666
4667 fn crease_is_orphan(&self, crease: &Crease) -> bool {
4668 match crease.owner {
4669 OwnerRef::Poly(poly_id) => poly_id == 0 || poly_id > self.polys.len(),
4670 OwnerRef::Path(path_id) => self
4671 .paths
4672 .get(path_id.saturating_sub(1))
4673 .is_none_or(|path| !path.is_sub && !self.path_is_incident_to_filled_poly(path_id)),
4674 _ => true,
4675 }
4676 }
4677
4678 fn vertex_is_orphan(&self, vertex: &Vertex) -> bool {
4679 match vertex.owner {
4680 OwnerRef::Node(node_id) => {
4681 let Some(node) = self.nodes.get(node_id.saturating_sub(1)) else {
4682 return true;
4683 };
4684 if node.is_sub {
4685 return false;
4686 }
4687 if !node.is_leaf {
4688 return true;
4689 }
4690 !node
4691 .leaf_paths
4692 .iter()
4693 .copied()
4694 .any(|path_id| self.path_is_incident_to_filled_poly(path_id))
4695 }
4696 OwnerRef::Path(path_id) => self
4697 .paths
4698 .get(path_id.saturating_sub(1))
4699 .is_none_or(|path| !path.is_sub && !self.path_is_incident_to_filled_poly(path_id)),
4700 _ => true,
4701 }
4702 }
4703
4704 fn path_is_incident_to_filled_poly(&self, path_id: usize) -> bool {
4705 let Some(path) = self.paths.get(path_id.saturating_sub(1)) else {
4706 return false;
4707 };
4708 path.fwd_poly
4709 .and_then(|poly_id| self.polys.get(poly_id.saturating_sub(1)))
4710 .is_some_and(|poly| !poly.owned_nodes.is_empty())
4711 || path
4712 .bkd_poly
4713 .and_then(|poly_id| self.polys.get(poly_id.saturating_sub(1)))
4714 .is_some_and(|poly| !poly.owned_nodes.is_empty())
4715 }
4716
4717 fn delete_polys(&mut self, doomed: &[usize]) {
4718 if doomed.is_empty() {
4719 return;
4720 }
4721
4722 let mut doomed_flags = ids_to_flags(doomed, self.polys.len());
4723 loop {
4724 let mut changed = false;
4725 for poly in &self.polys {
4726 if doomed_flags[poly.index] {
4727 continue;
4728 }
4729 if let OwnerRef::Poly(owner) = poly.owner
4730 && doomed_flags.get(owner).copied().unwrap_or(false)
4731 {
4732 doomed_flags[poly.index] = true;
4733 changed = true;
4734 }
4735 }
4736 if !changed {
4737 break;
4738 }
4739 }
4740
4741 let doomed_polys: Vec<usize> = doomed_flags
4742 .iter()
4743 .enumerate()
4744 .skip(1)
4745 .filter_map(|(id, doomed)| doomed.then_some(id))
4746 .collect();
4747 let doomed_facets: Vec<usize> = self
4748 .facets
4749 .iter()
4750 .filter_map(|facet| match facet.owner {
4751 OwnerRef::Poly(poly_id) if doomed_flags.get(poly_id).copied().unwrap_or(false) => {
4752 Some(facet.index)
4753 }
4754 _ => None,
4755 })
4756 .collect();
4757 let doomed_creases: Vec<usize> = self
4758 .creases
4759 .iter()
4760 .filter_map(|crease| match crease.owner {
4761 OwnerRef::Poly(poly_id) if doomed_flags.get(poly_id).copied().unwrap_or(false) => {
4762 Some(crease.index)
4763 }
4764 _ => None,
4765 })
4766 .collect();
4767
4768 self.delete_facets(&doomed_facets);
4769 self.delete_creases(&doomed_creases);
4770
4771 let map = keep_map(self.polys.len(), &doomed_polys);
4772 for path in &mut self.paths {
4773 remap_option(&mut path.fwd_poly, &map);
4774 remap_option(&mut path.bkd_poly, &map);
4775 }
4776 for poly in &mut self.polys {
4777 remap_vec(&mut poly.owned_polys, &map);
4778 remap_owner(&mut poly.owner, PartKind::Poly, &map);
4779 }
4780 for node in &mut self.nodes {
4781 remap_owner(&mut node.owner, PartKind::Poly, &map);
4782 }
4783 for path in &mut self.paths {
4784 remap_owner(&mut path.owner, PartKind::Poly, &map);
4785 }
4786 for crease in &mut self.creases {
4787 remap_owner(&mut crease.owner, PartKind::Poly, &map);
4788 }
4789 for facet in &mut self.facets {
4790 remap_owner(&mut facet.owner, PartKind::Poly, &map);
4791 }
4792 remap_vec(&mut self.owned_polys, &map);
4793
4794 self.polys = self
4795 .polys
4796 .drain(..)
4797 .filter(|poly| map[poly.index].is_some())
4798 .enumerate()
4799 .map(|(i, mut poly)| {
4800 poly.index = i + 1;
4801 poly
4802 })
4803 .collect();
4804 }
4805
4806 fn delete_facets(&mut self, doomed: &[usize]) {
4807 if doomed.is_empty() {
4808 return;
4809 }
4810 let map = keep_map(self.facets.len(), doomed);
4811 for crease in &mut self.creases {
4812 remap_option(&mut crease.fwd_facet, &map);
4813 remap_option(&mut crease.bkd_facet, &map);
4814 }
4815 for poly in &mut self.polys {
4816 remap_vec(&mut poly.owned_facets, &map);
4817 }
4818 for facet in &mut self.facets {
4819 remap_vec(&mut facet.head_facets, &map);
4820 remap_vec(&mut facet.tail_facets, &map);
4821 }
4822 self.facets = self
4823 .facets
4824 .drain(..)
4825 .filter(|facet| map[facet.index].is_some())
4826 .enumerate()
4827 .map(|(i, mut facet)| {
4828 facet.index = i + 1;
4829 facet
4830 })
4831 .collect();
4832 }
4833
4834 fn delete_creases(&mut self, doomed: &[usize]) {
4835 if doomed.is_empty() {
4836 return;
4837 }
4838 let map = keep_map(self.creases.len(), doomed);
4839 for vertex in &mut self.vertices {
4840 remap_vec(&mut vertex.creases, &map);
4841 }
4842 for path in &mut self.paths {
4843 remap_vec(&mut path.owned_creases, &map);
4844 }
4845 for poly in &mut self.polys {
4846 remap_vec(&mut poly.local_root_creases, &map);
4847 remap_vec(&mut poly.owned_creases, &map);
4848 }
4849 for facet in &mut self.facets {
4850 remap_vec(&mut facet.creases, &map);
4851 }
4852 self.creases = self
4853 .creases
4854 .drain(..)
4855 .filter(|crease| map[crease.index].is_some())
4856 .enumerate()
4857 .map(|(i, mut crease)| {
4858 crease.index = i + 1;
4859 crease
4860 })
4861 .collect();
4862 }
4863
4864 fn delete_vertices(&mut self, doomed: &[usize]) {
4865 if doomed.is_empty() {
4866 return;
4867 }
4868 let map = keep_map(self.vertices.len(), doomed);
4869 for node in &mut self.nodes {
4870 remap_vec(&mut node.owned_vertices, &map);
4871 }
4872 for path in &mut self.paths {
4873 remap_vec(&mut path.owned_vertices, &map);
4874 }
4875 for poly in &mut self.polys {
4876 remap_vec(&mut poly.local_root_vertices, &map);
4877 }
4878 for vertex in &mut self.vertices {
4879 remap_option(&mut vertex.left_pseudohinge_mate, &map);
4880 remap_option(&mut vertex.right_pseudohinge_mate, &map);
4881 }
4882 for crease in &mut self.creases {
4883 remap_vec(&mut crease.vertices, &map);
4884 }
4885 for facet in &mut self.facets {
4886 remap_vec(&mut facet.vertices, &map);
4887 }
4888 self.vertices = self
4889 .vertices
4890 .drain(..)
4891 .filter(|vertex| map[vertex.index].is_some())
4892 .enumerate()
4893 .map(|(i, mut vertex)| {
4894 vertex.index = i + 1;
4895 vertex
4896 })
4897 .collect();
4898 }
4899
4900 fn promote_first_tree_node_to_root(&mut self) {
4901 let Some(pos) = self.nodes.iter().position(|node| !node.is_sub) else {
4902 return;
4903 };
4904 if pos == 0 {
4905 return;
4906 }
4907 let old_order: Vec<usize> = std::iter::once(pos + 1)
4908 .chain((1..=self.nodes.len()).filter(|id| *id != pos + 1))
4909 .collect();
4910 let mut map = vec![None; self.nodes.len() + 1];
4911 for (new_index, old_index) in old_order.iter().copied().enumerate() {
4912 map[old_index] = Some(new_index + 1);
4913 }
4914
4915 let old_nodes = self.nodes.clone();
4916 self.nodes = old_order
4917 .iter()
4918 .map(|old_index| old_nodes[*old_index - 1].clone())
4919 .collect();
4920
4921 for node in &mut self.nodes {
4922 node.index = map[node.index].expect("node reorder map");
4923 remap_owner(&mut node.owner, PartKind::Node, &map);
4924 }
4925 for edge in &mut self.edges {
4926 remap_vec(&mut edge.nodes, &map);
4927 }
4928 for path in &mut self.paths {
4929 remap_vec(&mut path.nodes, &map);
4930 }
4931 for poly in &mut self.polys {
4932 remap_vec(&mut poly.ring_nodes, &map);
4933 remap_vec(&mut poly.inset_nodes, &map);
4934 remap_vec(&mut poly.owned_nodes, &map);
4935 }
4936 for vertex in &mut self.vertices {
4937 remap_option(&mut vertex.tree_node, &map);
4938 remap_owner(&mut vertex.owner, PartKind::Node, &map);
4939 }
4940 for condition in &mut self.conditions {
4941 condition.kind.remap_nodes(&map);
4942 }
4943 remap_vec(&mut self.owned_nodes, &map);
4944 }
4945
4946 fn renumber_part_indices(&mut self) {
4947 for (i, node) in self.nodes.iter_mut().enumerate() {
4948 node.index = i + 1;
4949 }
4950 for (i, edge) in self.edges.iter_mut().enumerate() {
4951 edge.index = i + 1;
4952 }
4953 for (i, path) in self.paths.iter_mut().enumerate() {
4954 path.index = i + 1;
4955 }
4956 for (i, poly) in self.polys.iter_mut().enumerate() {
4957 poly.index = i + 1;
4958 }
4959 for (i, vertex) in self.vertices.iter_mut().enumerate() {
4960 vertex.index = i + 1;
4961 }
4962 for (i, crease) in self.creases.iter_mut().enumerate() {
4963 crease.index = i + 1;
4964 }
4965 for (i, facet) in self.facets.iter_mut().enumerate() {
4966 facet.index = i + 1;
4967 }
4968 for (i, condition) in self.conditions.iter_mut().enumerate() {
4969 condition.index = i + 1;
4970 }
4971 }
4972
4973 fn clear_crease_pattern_cleanup_data(&mut self) {
4974 for vertex in &mut self.vertices {
4975 vertex.depth = DEPTH_NOT_SET;
4976 vertex.discrete_depth = usize::MAX;
4977 }
4978 for crease in &mut self.creases {
4979 crease.fold = 0;
4980 }
4981 for facet in &mut self.facets {
4982 facet.corridor_edge = None;
4983 facet.head_facets.clear();
4984 facet.tail_facets.clear();
4985 facet.order = usize::MAX;
4986 facet.color = 0;
4987 }
4988 }
4989
4990 fn calc_polygon_filled(&mut self) {
4991 self.is_polygon_filled = false;
4992 if self.owned_polys.is_empty() {
4993 return;
4994 }
4995 for poly_id in self.owned_polys.iter().copied() {
4996 let Some(poly) = self.polys.get(poly_id.saturating_sub(1)) else {
4997 return;
4998 };
4999 if poly.owned_nodes.is_empty() {
5000 return;
5001 }
5002 }
5003 self.is_polygon_filled = true;
5004 }
5005
5006 fn calc_depth_and_bend(&mut self) {
5007 if !self.is_polygon_valid || self.nodes.is_empty() {
5008 return;
5009 }
5010
5011 let root_node = self
5012 .nodes
5013 .iter()
5014 .position(|node| !node.is_sub)
5015 .map(|i| i + 1);
5016 let Some(root_node) = root_node else {
5017 return;
5018 };
5019 self.nodes[root_node - 1].depth = 0.0;
5020 for path_id in self.owned_paths.iter().copied() {
5021 let path = &self.paths[path_id - 1];
5022 if path.nodes.first().copied() == Some(root_node) {
5023 if let Some(back_node) = path.nodes.last().copied() {
5024 self.nodes[back_node - 1].depth = path.min_paper_length;
5025 }
5026 } else if path.nodes.last().copied() == Some(root_node)
5027 && let Some(front_node) = path.nodes.first().copied()
5028 {
5029 self.nodes[front_node - 1].depth = path.min_paper_length;
5030 }
5031 }
5032
5033 for path in &mut self.paths {
5034 path.min_depth = DEPTH_NOT_SET;
5035 path.min_depth_dist = 0.0;
5036 }
5037
5038 for path_id in 1..=self.paths.len() {
5039 if !self.paths[path_id - 1].is_leaf {
5040 continue;
5041 }
5042 let node_ids = self.paths[path_id - 1].nodes.clone();
5043 let edge_ids = self.paths[path_id - 1].edges.clone();
5044 if node_ids.is_empty() {
5045 continue;
5046 }
5047 let mut min_depth = self.nodes[node_ids[0] - 1].depth;
5048 let mut min_depth_dist = 0.0;
5049 for j in 1..node_ids.len() {
5050 let node_depth = self.nodes[node_ids[j] - 1].depth;
5051 if min_depth > node_depth {
5052 min_depth = node_depth;
5053 min_depth_dist +=
5054 self.edges[edge_ids[j - 1] - 1].strained_length() * self.scale;
5055 }
5056 }
5057 self.paths[path_id - 1].min_depth = min_depth;
5058 self.paths[path_id - 1].min_depth_dist = min_depth_dist;
5059 }
5060
5061 for path_id in 1..=self.paths.len() {
5062 if !self.path_is_gusset(path_id) {
5063 continue;
5064 }
5065 let (max_outset_path, max_front_reduction, _) = self.max_outset_path(path_id);
5066 self.paths[path_id - 1].min_depth = self.paths[max_outset_path - 1].min_depth;
5067 self.paths[path_id - 1].min_depth_dist =
5068 self.paths[max_outset_path - 1].min_depth_dist - max_front_reduction;
5069 }
5070
5071 for vertex in &mut self.vertices {
5072 vertex.depth = DEPTH_NOT_SET;
5073 }
5074
5075 for poly_id in 1..=self.polys.len() {
5076 let nn = self.polys[poly_id - 1].ring_nodes.len();
5077 for j in 0..nn {
5078 let front_node = self.polys[poly_id - 1].ring_nodes[j];
5079 let back_node = self.polys[poly_id - 1].ring_nodes[(j + 1) % nn];
5080 let path_id = self.polys[poly_id - 1].ring_paths[j];
5081 if !(self.path_is_active_axial(path_id) || self.path_is_gusset(path_id)) {
5082 continue;
5083 }
5084 if let Ok(ridge_vertices) =
5085 self.get_ridgeline_vertices(poly_id, front_node, back_node)
5086 {
5087 for vertex_id in ridge_vertices {
5088 self.set_vertex_depth_from_path(path_id, vertex_id);
5089 }
5090 }
5091 for vertex_id in self.paths[path_id - 1].owned_vertices.clone() {
5092 self.set_vertex_depth_from_path(path_id, vertex_id);
5093 }
5094 }
5095 }
5096
5097 for path_id in self.owned_paths.clone() {
5098 if !self.paths[path_id - 1].is_border || self.paths[path_id - 1].is_active {
5099 continue;
5100 }
5101 for vertex_id in self.paths[path_id - 1].owned_vertices.clone() {
5102 for crease_id in self.vertices[vertex_id - 1].creases.clone() {
5103 if self.crease_is_hinge(crease_id) {
5104 let ridge_vertex = self.other_crease_vertex(crease_id, vertex_id);
5105 self.vertices[vertex_id - 1].depth = self.vertices[ridge_vertex - 1].depth;
5106 break;
5107 }
5108 }
5109 }
5110 }
5111
5112 for vertex_id in 1..=self.vertices.len() {
5113 if let Some(tree_node) = self.vertices[vertex_id - 1].tree_node {
5114 self.vertices[vertex_id - 1].discrete_depth = self.calc_discrete_depth(tree_node);
5115 } else {
5116 self.vertices[vertex_id - 1].discrete_depth = usize::MAX;
5117 }
5118 }
5119
5120 if self
5121 .vertices
5122 .iter()
5123 .any(|vertex| vertex.depth == DEPTH_NOT_SET)
5124 {
5125 return;
5126 }
5127
5128 for poly_id in self.owned_polys.clone() {
5129 self.calc_poly_bend(poly_id);
5130 }
5131 }
5132
5133 fn set_vertex_depth_from_path(&mut self, path_id: usize, vertex_id: usize) {
5134 let path = &self.paths[path_id - 1];
5135 let p = self.vertices[vertex_id - 1].loc;
5136 let p1 = self.nodes[path.nodes[0] - 1].loc;
5137 let p2 = self.nodes[*path.nodes.last().unwrap() - 1].loc;
5138 let d = inner(point_sub(p, p1), point_sub(p2, p1)) / p2.distance(p1);
5139 self.vertices[vertex_id - 1].depth = if d < path.min_depth_dist {
5140 path.min_depth + path.min_depth_dist - d
5141 } else {
5142 path.min_depth + d - path.min_depth_dist
5143 };
5144 }
5145
5146 fn calc_discrete_depth(&self, node_id: usize) -> usize {
5147 let root_node = self
5148 .nodes
5149 .iter()
5150 .position(|node| !node.is_sub)
5151 .map(|i| i + 1);
5152 if root_node == Some(node_id) {
5153 return 0;
5154 }
5155 let Some(root_node) = root_node else {
5156 return usize::MAX;
5157 };
5158 self.find_any_path_in(&self.owned_paths, root_node, node_id)
5159 .map(|path_id| self.paths[path_id - 1].edges.len())
5160 .unwrap_or(usize::MAX)
5161 }
5162
5163 fn calc_poly_bend(&mut self, poly_id: usize) {
5164 for crease_id in self.polys[poly_id - 1].owned_creases.clone() {
5165 self.calc_crease_bend(crease_id);
5166 }
5167
5168 let mut all_vertices = Vec::new();
5169 for crease_id in self.polys[poly_id - 1].owned_creases.clone() {
5170 for vertex_id in self.creases[crease_id - 1].vertices.clone() {
5171 push_unique(&mut all_vertices, vertex_id);
5172 }
5173 }
5174 for node_id in self.polys[poly_id - 1].ring_nodes.clone() {
5175 if let Some(vertex_id) = self.nodes[node_id - 1].owned_vertices.first().copied() {
5176 push_unique(&mut all_vertices, vertex_id);
5177 }
5178 }
5179
5180 self.polys[poly_id - 1].local_root_vertices.clear();
5181 self.polys[poly_id - 1].local_root_creases.clear();
5182 let mut min_discrete_depth = usize::MAX;
5183 for vertex_id in all_vertices {
5184 let discrete_depth = self.vertices[vertex_id - 1].discrete_depth;
5185 if min_discrete_depth > discrete_depth {
5186 min_discrete_depth = discrete_depth;
5187 self.polys[poly_id - 1].local_root_vertices.clear();
5188 self.polys[poly_id - 1].local_root_creases.clear();
5189 }
5190 if min_discrete_depth == discrete_depth {
5191 push_unique(&mut self.polys[poly_id - 1].local_root_vertices, vertex_id);
5192 for crease_id in self.vertices[vertex_id - 1].creases.clone() {
5193 if self.crease_is_hinge(crease_id)
5194 && self.polys[poly_id - 1].owned_creases.contains(&crease_id)
5195 {
5196 push_unique(&mut self.polys[poly_id - 1].local_root_creases, crease_id);
5197 }
5198 }
5199 }
5200 }
5201 }
5202
5203 fn calc_crease_bend(&mut self, crease_id: usize) {
5204 if !self.crease_is_hinge(crease_id)
5205 || self.creases[crease_id - 1].kind == CREASE_PSEUDOHINGE
5206 {
5207 return;
5208 }
5209 let v0 = self.creases[crease_id - 1].vertices[0];
5210 let v1 = self.creases[crease_id - 1].vertices[1];
5211 let bottom = if self.vertices[v0 - 1].elevation > self.vertices[v1 - 1].elevation {
5212 v1
5213 } else {
5214 v0
5215 };
5216 let ag_creases: Vec<usize> = self.vertices[bottom - 1]
5217 .creases
5218 .iter()
5219 .copied()
5220 .filter(|id| self.crease_is_axial_or_gusset(*id))
5221 .take(2)
5222 .collect();
5223 if ag_creases.len() < 2 {
5224 return;
5225 }
5226 let vertex1 = self.other_crease_vertex(ag_creases[0], bottom);
5227 let vertex3 = self.other_crease_vertex(ag_creases[1], bottom);
5228 let depth1 = self.vertices[vertex1 - 1].depth;
5229 let depth2 = self.vertices[bottom - 1].depth;
5230 let depth3 = self.vertices[vertex3 - 1].depth;
5231 if (depth1 > depth2 && depth2 < depth3) || (depth1 < depth2 && depth2 > depth3) {
5232 self.creases[crease_id - 1].kind = CREASE_FOLDED_HINGE;
5233 } else {
5234 self.creases[crease_id - 1].kind = CREASE_UNFOLDED_HINGE;
5235 }
5236 }
5237
5238 fn calc_vertex_depth_validity(&mut self) {
5239 self.is_vertex_depth_valid = false;
5240 if self.vertices.is_empty() {
5241 return;
5242 }
5243 if self
5244 .vertices
5245 .iter()
5246 .any(|vertex| vertex.depth == DEPTH_NOT_SET)
5247 {
5248 return;
5249 }
5250 self.is_vertex_depth_valid = true;
5251 }
5252
5253 fn calc_facet_data_validity(&mut self) {
5254 self.is_facet_data_valid = false;
5255 if self.facets.is_empty() {
5256 return;
5257 }
5258 if self.facets.iter().any(|facet| !facet.is_well_formed) {
5259 return;
5260 }
5261 if self
5262 .vertices
5263 .iter()
5264 .any(|vertex| !vertex.is_border && vertex.creases.len() % 2 != 0)
5265 {
5266 return;
5267 }
5268 self.is_facet_data_valid = true;
5269 }
5270
5271 fn calc_facet_corridor_edges(&mut self) {
5272 for poly_id in self.owned_polys.clone() {
5273 self.calc_poly_facet_corridor_edges(poly_id);
5274 }
5275 }
5276
5277 fn calc_poly_facet_corridor_edges(&mut self, poly_id: usize) {
5278 for facet_id in self.polys[poly_id - 1].owned_facets.clone() {
5279 if self.facets[facet_id - 1].corridor_edge.is_some() {
5280 continue;
5281 }
5282 if !self.facet_is_axial(facet_id) {
5283 continue;
5284 }
5285 let Some(bottom_crease) = self.facet_bottom_crease(facet_id) else {
5286 continue;
5287 };
5288 let vertices = self.creases[bottom_crease - 1].vertices.clone();
5289 if vertices.len() < 2 {
5290 continue;
5291 }
5292 let (Some(node1), Some(node2)) = (
5293 self.vertices[vertices[0] - 1].tree_node,
5294 self.vertices[vertices[1] - 1].tree_node,
5295 ) else {
5296 continue;
5297 };
5298 let Some(edge_id) = self.edge_between_nodes(node1, node2) else {
5299 continue;
5300 };
5301 self.set_facet_corridor_edge(poly_id, facet_id, edge_id);
5302 }
5303 }
5304
5305 fn set_facet_corridor_edge(&mut self, poly_id: usize, facet_id: usize, edge_id: usize) {
5306 self.facets[facet_id - 1].corridor_edge = Some(edge_id);
5307 let crease_ids = self.facets[facet_id - 1].creases.clone();
5308 for crease_id in crease_ids {
5309 if self.crease_is_regular_hinge(crease_id) {
5310 continue;
5311 }
5312 let Some(other_facet) = self.crease_other_facet(crease_id, facet_id) else {
5313 continue;
5314 };
5315 if !self.polys[poly_id - 1].owned_facets.contains(&other_facet) {
5316 continue;
5317 }
5318 if self.facets[other_facet - 1].corridor_edge.is_some() {
5319 continue;
5320 }
5321 self.set_facet_corridor_edge(poly_id, other_facet, edge_id);
5322 }
5323 }
5324
5325 fn edge_between_nodes(&self, node1: usize, node2: usize) -> Option<usize> {
5326 self.nodes
5327 .get(node1.saturating_sub(1))?
5328 .edges
5329 .iter()
5330 .copied()
5331 .find(|edge_id| {
5332 self.edges
5333 .get(edge_id.saturating_sub(1))
5334 .is_some_and(|edge| edge.nodes.contains(&node2))
5335 })
5336 }
5337
5338 fn calc_facet_order(&mut self) {
5339 let mut root_networks = self.calc_root_networks();
5340 let num_depth_zero = root_networks
5341 .iter()
5342 .filter(|network| network.discrete_depth == 0)
5343 .count();
5344
5345 self.is_local_root_connectable = true;
5346 for network in &root_networks {
5347 if network.discrete_depth != 0 && !network.is_connectable {
5348 self.is_local_root_connectable = false;
5349 }
5350 }
5351 self.is_local_root_connectable &= num_depth_zero == 1;
5352 if !self.is_local_root_connectable {
5353 return;
5354 }
5355
5356 for network in &root_networks {
5357 self.connect_facet_graph(network);
5358 }
5359
5360 let Some(global_index) = root_networks
5361 .iter()
5362 .position(|network| network.discrete_depth == 0)
5363 else {
5364 self.is_local_root_connectable = false;
5365 return;
5366 };
5367 let mut global_root_network = root_networks.remove(global_index);
5368
5369 while !root_networks.is_empty() {
5370 let mut absorbed_index = None;
5371 let mut absorbed_vertex = None;
5372 for (i, network) in root_networks.iter().enumerate() {
5373 if let Some(vertex_id) = self.root_network_can_absorb(&global_root_network, network)
5374 {
5375 absorbed_index = Some(i);
5376 absorbed_vertex = Some(vertex_id);
5377 break;
5378 }
5379 }
5380 let (Some(index), Some(vertex_id)) = (absorbed_index, absorbed_vertex) else {
5381 self.is_local_root_connectable = false;
5382 return;
5383 };
5384 self.vertex_swap_links(vertex_id);
5385 let network = root_networks.remove(index);
5386 for poly_id in network.cc_polys {
5387 push_unique(&mut global_root_network.cc_polys, poly_id);
5388 }
5389 }
5390
5391 self.root_network_break_one_link(&global_root_network);
5392
5393 for facet in &mut self.facets {
5394 facet.order = usize::MAX;
5395 }
5396 let source_facet = self
5397 .facets
5398 .iter()
5399 .find(|facet| self.facet_is_source(facet.index))
5400 .map(|facet| facet.index);
5401 let Some(source_facet) = source_facet else {
5402 self.is_local_root_connectable = false;
5403 return;
5404 };
5405 let mut next_order = 0;
5406 self.calc_facet_order_recursive(source_facet, &mut next_order);
5407 }
5408
5409 fn calc_root_networks(&mut self) -> Vec<RootNetwork> {
5410 for poly_id in self.owned_polys.clone() {
5411 self.calc_local_facet_order(poly_id);
5412 }
5413
5414 let mut local_root_vertices = Vec::new();
5415 let mut local_root_creases = Vec::new();
5416 for poly_id in self.owned_polys.clone() {
5417 for vertex_id in self.polys[poly_id - 1].local_root_vertices.clone() {
5418 push_unique(&mut local_root_vertices, vertex_id);
5419 }
5420 for crease_id in self.polys[poly_id - 1].local_root_creases.clone() {
5421 push_unique(&mut local_root_creases, crease_id);
5422 }
5423 }
5424
5425 for vertex in &mut self.vertices {
5426 vertex.cc_flag = ROOT_FLAG_INELIGIBLE;
5427 vertex.st_flag = ROOT_FLAG_INELIGIBLE;
5428 }
5429 for crease in &mut self.creases {
5430 crease.cc_flag = ROOT_FLAG_INELIGIBLE;
5431 crease.st_flag = ROOT_FLAG_INELIGIBLE;
5432 }
5433
5434 for vertex_id in local_root_vertices.iter().copied() {
5435 self.vertices[vertex_id - 1].cc_flag = ROOT_FLAG_NOT_YET;
5436 self.vertices[vertex_id - 1].st_flag = ROOT_FLAG_NOT_YET;
5437 }
5438 for crease_id in local_root_creases.iter().copied() {
5439 self.creases[crease_id - 1].cc_flag = ROOT_FLAG_NOT_YET;
5440 self.creases[crease_id - 1].st_flag = ROOT_FLAG_NOT_YET;
5441 }
5442
5443 let mut root_networks = Vec::new();
5444 for vertex_id in local_root_vertices {
5445 if root_networks
5446 .iter()
5447 .any(|network: &RootNetwork| network.cc_vertices.contains(&vertex_id))
5448 {
5449 continue;
5450 }
5451 let discrete_depth = self.vertices[vertex_id - 1].discrete_depth;
5452 let mut network = RootNetwork::new(discrete_depth);
5453 self.try_add_vertex_to_connected_component(&mut network, vertex_id);
5454 root_networks.push(network);
5455 }
5456
5457 for network in &mut root_networks {
5458 if let Some(vertex_id) = network.cc_vertices.first().copied() {
5459 self.try_add_vertex_to_spanning_tree(network, vertex_id);
5460 }
5461 }
5462
5463 for network in &mut root_networks {
5464 self.classify_root_network_vertices(network);
5465 }
5466
5467 root_networks
5468 }
5469
5470 fn why_not_local_root_connectable(&self) -> (Vec<usize>, Vec<usize>) {
5471 let mut tree = self.clone();
5472 let root_networks = tree.calc_root_networks();
5473 let mut bad_vertices = Vec::new();
5474 let mut bad_creases = Vec::new();
5475 let mut zero_depth_network: Option<usize> = None;
5476
5477 for (network_index, network) in root_networks.iter().enumerate() {
5478 if network.discrete_depth == 0 {
5479 if let Some(zero_index) = zero_depth_network {
5480 let zero_network = &root_networks[zero_index];
5481 for vertex_id in network.cc_vertices.iter().copied() {
5482 push_unique(&mut bad_vertices, vertex_id);
5483 }
5484 for crease_id in network.cc_creases.iter().copied() {
5485 push_unique(&mut bad_creases, crease_id);
5486 }
5487 for vertex_id in zero_network.cc_vertices.iter().copied() {
5488 push_unique(&mut bad_vertices, vertex_id);
5489 }
5490 for crease_id in zero_network.cc_creases.iter().copied() {
5491 push_unique(&mut bad_creases, crease_id);
5492 }
5493 } else {
5494 zero_depth_network = Some(network_index);
5495 }
5496 } else if !network.is_connectable {
5497 for vertex_id in network.cc_vertices.iter().copied() {
5498 push_unique(&mut bad_vertices, vertex_id);
5499 }
5500 for crease_id in network.cc_creases.iter().copied() {
5501 push_unique(&mut bad_creases, crease_id);
5502 }
5503 }
5504 }
5505
5506 (bad_vertices, bad_creases)
5507 }
5508
5509 fn calc_local_facet_order(&mut self, poly_id: usize) {
5510 for facet_id in self.polys[poly_id - 1].owned_facets.clone() {
5511 self.facets[facet_id - 1].head_facets.clear();
5512 self.facets[facet_id - 1].tail_facets.clear();
5513 }
5514
5515 let Some(start_vertex) = self.polys[poly_id - 1]
5516 .local_root_vertices
5517 .iter()
5518 .copied()
5519 .find(|vertex_id| self.vertex_is_axial(*vertex_id))
5520 else {
5521 return;
5522 };
5523 let Some(start_crease) = self.incident_interior_crease(poly_id, start_vertex) else {
5524 return;
5525 };
5526 let Some(start_facet) = self.crease_right_non_pseudohinge_facet(start_crease) else {
5527 return;
5528 };
5529
5530 let mut cur_facet = start_facet;
5531 let mut guard = 0;
5532 loop {
5533 let Some(next_facet) = self.facet_right_non_pseudohinge_facet(cur_facet) else {
5534 return;
5535 };
5536 self.facet_link_to(cur_facet, next_facet);
5537 let Some(bottom_crease) = self.facet_bottom_crease(cur_facet) else {
5538 return;
5539 };
5540 self.build_corridor_links(bottom_crease, cur_facet);
5541 cur_facet = next_facet;
5542 if cur_facet == start_facet {
5543 break;
5544 }
5545 guard += 1;
5546 if guard > self.facets.len().saturating_mul(4).max(100) {
5547 return;
5548 }
5549 }
5550 }
5551
5552 fn incident_interior_crease(&self, poly_id: usize, vertex_id: usize) -> Option<usize> {
5553 self.vertices[vertex_id - 1]
5554 .creases
5555 .iter()
5556 .copied()
5557 .find(|crease_id| {
5558 (self.crease_is_hinge(*crease_id)
5559 || self.creases[*crease_id - 1].kind == CREASE_RIDGE)
5560 && self.polys[poly_id - 1].owned_creases.contains(crease_id)
5561 })
5562 }
5563
5564 fn build_corridor_links(&mut self, from_crease: usize, from_facet: usize) {
5565 let Some(bottom_crease) = self.facet_bottom_crease(from_facet) else {
5566 return;
5567 };
5568 if bottom_crease == from_crease {
5569 for next_crease in self.facets[from_facet - 1].creases.clone() {
5570 if self.creases[next_crease - 1].kind != CREASE_RIDGE {
5571 continue;
5572 }
5573 let Some(next_facet) = self.crease_other_facet(next_crease, from_facet) else {
5574 continue;
5575 };
5576 if self.creases[bottom_crease - 1].kind == CREASE_AXIAL {
5577 if self.facet_left_facet(from_facet) == Some(next_facet) {
5578 continue;
5579 }
5580 if self.facet_right_facet(from_facet) == Some(next_facet) {
5581 continue;
5582 }
5583 }
5584 if self.facets_are_linked(from_facet, next_facet) {
5585 continue;
5586 }
5587 self.facet_link_to(from_facet, next_facet);
5588 self.build_corridor_links(next_crease, next_facet);
5589 }
5590 } else if self.creases[bottom_crease - 1].kind == CREASE_GUSSET {
5591 let Some(next_facet) = self.crease_other_facet(bottom_crease, from_facet) else {
5592 return;
5593 };
5594 self.facet_link_to(from_facet, next_facet);
5595 self.build_corridor_links(bottom_crease, next_facet);
5596 } else {
5597 if !self.facet_is_pseudohinge(from_facet) {
5598 return;
5599 }
5600 let Some(mut ph_crease) = self.facet_left_crease(from_facet) else {
5601 return;
5602 };
5603 let next_facet = if self.creases[ph_crease - 1].kind == CREASE_PSEUDOHINGE {
5604 self.crease_left_facet(ph_crease)
5605 } else {
5606 let Some(right_crease) = self.facet_right_crease(from_facet) else {
5607 return;
5608 };
5609 ph_crease = right_crease;
5610 if self.creases[ph_crease - 1].kind != CREASE_PSEUDOHINGE {
5611 return;
5612 }
5613 self.crease_right_facet(ph_crease)
5614 };
5615 let Some(next_facet) = next_facet else {
5616 return;
5617 };
5618 self.facet_link_to(from_facet, next_facet);
5619 let Some(next_bottom) = self.facet_bottom_crease(next_facet) else {
5620 return;
5621 };
5622 self.build_corridor_links(next_bottom, next_facet);
5623 }
5624 }
5625
5626 fn try_add_vertex_to_connected_component(
5627 &mut self,
5628 network: &mut RootNetwork,
5629 vertex_id: usize,
5630 ) {
5631 if self.vertices[vertex_id - 1].cc_flag == ROOT_FLAG_INELIGIBLE {
5632 return;
5633 }
5634 if self.vertices[vertex_id - 1].cc_flag == ROOT_FLAG_ALREADY_ADDED {
5635 return;
5636 }
5637 self.vertices[vertex_id - 1].cc_flag = ROOT_FLAG_ALREADY_ADDED;
5638 network.cc_vertices.push(vertex_id);
5639
5640 for crease_id in self.vertices[vertex_id - 1].creases.clone() {
5641 self.try_add_crease_to_connected_component(network, crease_id);
5642 }
5643 if let Some(mate_vertex) = self.vertices[vertex_id - 1].left_pseudohinge_mate {
5644 self.try_add_vertex_to_connected_component(network, mate_vertex);
5645 }
5646 if let Some(mate_vertex) = self.vertices[vertex_id - 1].right_pseudohinge_mate {
5647 self.try_add_vertex_to_connected_component(network, mate_vertex);
5648 }
5649
5650 if let Some(node_id) = self.vertices[vertex_id - 1].tree_node
5651 && self.nodes[node_id - 1].is_leaf
5652 {
5653 for crease_id in self.vertices[vertex_id - 1].creases.clone() {
5654 if self.creases[crease_id - 1].kind != CREASE_RIDGE {
5655 continue;
5656 }
5657 if let OwnerRef::Poly(poly_id) = self.creases[crease_id - 1].owner {
5658 push_unique(&mut network.cc_polys, poly_id);
5659 }
5660 }
5661 }
5662 }
5663
5664 fn try_add_crease_to_connected_component(
5665 &mut self,
5666 network: &mut RootNetwork,
5667 crease_id: usize,
5668 ) {
5669 if !self.crease_is_hinge(crease_id) {
5670 return;
5671 }
5672 if self.creases[crease_id - 1].cc_flag == ROOT_FLAG_INELIGIBLE {
5673 return;
5674 }
5675 if self.creases[crease_id - 1].cc_flag == ROOT_FLAG_ALREADY_ADDED {
5676 return;
5677 }
5678 self.creases[crease_id - 1].cc_flag = ROOT_FLAG_ALREADY_ADDED;
5679 network.cc_creases.push(crease_id);
5680 if let OwnerRef::Poly(poly_id) = self.creases[crease_id - 1].owner {
5681 push_unique(&mut network.cc_polys, poly_id);
5682 }
5683 let vertices = self.creases[crease_id - 1].vertices.clone();
5684 for vertex_id in vertices {
5685 self.try_add_vertex_to_connected_component(network, vertex_id);
5686 }
5687 }
5688
5689 fn try_add_vertex_to_spanning_tree(&mut self, network: &mut RootNetwork, vertex_id: usize) {
5690 if self.vertices[vertex_id - 1].st_flag == ROOT_FLAG_INELIGIBLE {
5691 return;
5692 }
5693 if self.vertices[vertex_id - 1].st_flag == ROOT_FLAG_ALREADY_ADDED {
5694 return;
5695 }
5696 self.vertices[vertex_id - 1].st_flag = ROOT_FLAG_ALREADY_ADDED;
5697 network.st_vertices.push(vertex_id);
5698
5699 for crease_id in self.vertices[vertex_id - 1].creases.clone() {
5700 self.try_add_crease_to_spanning_tree(network, crease_id);
5701 }
5702 if let Some(mate_vertex) = self.vertices[vertex_id - 1].left_pseudohinge_mate {
5703 self.try_add_vertex_to_spanning_tree(network, mate_vertex);
5704 }
5705 if let Some(mate_vertex) = self.vertices[vertex_id - 1].right_pseudohinge_mate {
5706 self.try_add_vertex_to_spanning_tree(network, mate_vertex);
5707 }
5708 }
5709
5710 fn try_add_crease_to_spanning_tree(&mut self, network: &mut RootNetwork, crease_id: usize) {
5711 if !self.crease_is_hinge(crease_id) {
5712 return;
5713 }
5714 if self.creases[crease_id - 1].cc_flag == ROOT_FLAG_INELIGIBLE {
5715 return;
5716 }
5717 if self.creases[crease_id - 1].st_flag == ROOT_FLAG_ALREADY_ADDED {
5718 return;
5719 }
5720 let v1 = self.creases[crease_id - 1].vertices[0];
5721 let v2 = self.creases[crease_id - 1].vertices[1];
5722 let c1 = self.vertices[v1 - 1].st_flag == ROOT_FLAG_ALREADY_ADDED;
5723 let c2 = self.vertices[v2 - 1].st_flag == ROOT_FLAG_ALREADY_ADDED;
5724 if c1 && c2 {
5725 return;
5726 }
5727 self.creases[crease_id - 1].st_flag = ROOT_FLAG_ALREADY_ADDED;
5728 network.st_creases.push(crease_id);
5729 if !c1 {
5730 self.try_add_vertex_to_spanning_tree(network, v1);
5731 }
5732 if !c2 {
5733 self.try_add_vertex_to_spanning_tree(network, v2);
5734 }
5735 }
5736
5737 fn classify_root_network_vertices(&self, network: &mut RootNetwork) {
5738 for vertex_id in network.cc_vertices.iter().copied() {
5739 if !self.vertex_is_axial(vertex_id) {
5740 continue;
5741 }
5742 let cc_degree = self.vertex_degree(vertex_id, &network.cc_creases);
5743 let st_degree = self.vertex_degree(vertex_id, &network.st_creases);
5744 match cc_degree {
5745 0 => network.cc0.push(vertex_id),
5746 1 => network.cc1.push(vertex_id),
5747 2 if st_degree == 1 => network.cc2_st1.push(vertex_id),
5748 2 if st_degree == 2 => network.cc2_st2.push(vertex_id),
5749 _ => {}
5750 }
5751 let axial_degree = self.vertex_num_hinge_creases(vertex_id);
5752 network.is_connectable |= axial_degree == 2 && cc_degree == 1;
5753 }
5754 }
5755
5756 fn connect_facet_graph(&mut self, network: &RootNetwork) {
5757 if let Some(vertex_id) = network.cc0.first().copied() {
5758 for crease_id in self.vertices[vertex_id - 1].creases.clone() {
5759 if self.creases[crease_id - 1].kind == CREASE_RIDGE
5760 && let (Some(fwd), Some(bkd)) = (
5761 self.creases[crease_id - 1].fwd_facet,
5762 self.creases[crease_id - 1].bkd_facet,
5763 )
5764 {
5765 self.facet_unlink(fwd, bkd);
5766 }
5767 }
5768
5769 let mut needs_skip = !self.vertices[vertex_id - 1].is_border;
5770 for crease_id in self.vertices[vertex_id - 1].creases.clone() {
5771 if self.crease_is_border(crease_id)
5772 || self.creases[crease_id - 1].kind != CREASE_AXIAL
5773 {
5774 continue;
5775 }
5776 if needs_skip {
5777 needs_skip = false;
5778 } else if let (Some(fwd), Some(bkd)) = (
5779 self.creases[crease_id - 1].fwd_facet,
5780 self.creases[crease_id - 1].bkd_facet,
5781 ) {
5782 self.facet_link(fwd, bkd);
5783 }
5784 }
5785 return;
5786 }
5787
5788 for vertex_id in network.cc2_st2.iter().copied() {
5789 self.vertex_swap_links(vertex_id);
5790 }
5791 }
5792
5793 fn root_network_can_absorb(
5794 &self,
5795 global_network: &RootNetwork,
5796 network: &RootNetwork,
5797 ) -> Option<usize> {
5798 for poly_id in global_network.cc_polys.iter().copied() {
5799 for path_id in self.polys[poly_id - 1].ring_paths.iter().copied() {
5800 for vertex_id in self.paths[path_id - 1].owned_vertices.iter().copied() {
5801 if self.vertices[vertex_id - 1].discrete_depth != network.discrete_depth {
5802 continue;
5803 }
5804 if network.cc1.contains(&vertex_id) {
5805 return Some(vertex_id);
5806 }
5807 }
5808 }
5809 }
5810 None
5811 }
5812
5813 fn root_network_break_one_link(&mut self, network: &RootNetwork) {
5814 if !network.cc0.is_empty() {
5815 return;
5816 }
5817 if let Some(vertex_id) = network.cc1.first().copied() {
5818 let Some(crease_id) = self.vertex_hinge_crease(vertex_id) else {
5819 return;
5820 };
5821 let (Some(left), Some(right)) = (
5822 self.crease_left_non_pseudohinge_facet(crease_id),
5823 self.crease_right_non_pseudohinge_facet(crease_id),
5824 ) else {
5825 return;
5826 };
5827 self.facet_unlink(left, right);
5828 return;
5829 }
5830
5831 let Some(vertex_id) = network.cc2_st1.first().copied() else {
5832 return;
5833 };
5834 let hinges = self.vertex_hinge_creases(vertex_id);
5835 let Some(crease_id) = hinges.first().copied() else {
5836 return;
5837 };
5838 if let (Some(fwd), Some(bkd)) = (
5839 self.creases[crease_id - 1].fwd_facet,
5840 self.creases[crease_id - 1].bkd_facet,
5841 ) {
5842 self.facet_unlink(fwd, bkd);
5843 }
5844 }
5845
5846 fn calc_facet_order_recursive(&mut self, facet_id: usize, next_order: &mut usize) {
5847 if self.facets[facet_id - 1].order != usize::MAX {
5848 return;
5849 }
5850 if self.facets[facet_id - 1]
5851 .tail_facets
5852 .iter()
5853 .any(|tail| self.facets[*tail - 1].order == usize::MAX)
5854 {
5855 return;
5856 }
5857 self.facets[facet_id - 1].order = *next_order;
5858 *next_order += 1;
5859 for head_facet in self.facets[facet_id - 1].head_facets.clone() {
5860 self.calc_facet_order_recursive(head_facet, next_order);
5861 }
5862 }
5863
5864 fn calc_facet_color(&mut self) {
5865 let mut source_facet = None;
5866 for facet_id in 1..=self.facets.len() {
5867 self.facets[facet_id - 1].color = FACET_NOT_ORIENTED;
5868 if self.facet_is_source(facet_id) {
5869 source_facet = Some(facet_id);
5870 }
5871 }
5872 if let Some(source_facet) = source_facet {
5873 self.calc_facet_color_recursive(source_facet, FACET_COLOR_UP);
5874 }
5875 }
5876
5877 fn calc_facet_color_recursive(&mut self, facet_id: usize, color: i32) {
5878 if self.facets[facet_id - 1].color != FACET_NOT_ORIENTED {
5879 return;
5880 }
5881 self.facets[facet_id - 1].color = color;
5882 for crease_id in self.facets[facet_id - 1].creases.clone() {
5883 let Some(other_facet) = self.crease_other_facet(crease_id, facet_id) else {
5884 continue;
5885 };
5886 if self.facets[other_facet - 1].color != FACET_NOT_ORIENTED {
5887 continue;
5888 }
5889 let other_color = match self.creases[crease_id - 1].kind {
5890 CREASE_AXIAL | CREASE_GUSSET | CREASE_RIDGE | CREASE_FOLDED_HINGE
5891 | CREASE_PSEUDOHINGE => opposite_facet_color(color),
5892 CREASE_UNFOLDED_HINGE => color,
5893 _ => continue,
5894 };
5895 self.calc_facet_color_recursive(other_facet, other_color);
5896 }
5897 }
5898
5899 fn calc_fold_directions(&mut self) {
5900 for crease_id in 1..=self.creases.len() {
5901 self.calc_crease_fold(crease_id);
5902 }
5903 }
5904
5905 fn calc_crease_fold(&mut self, crease_id: usize) {
5906 let (Some(fwd), Some(bkd)) = (
5907 self.creases[crease_id - 1].fwd_facet,
5908 self.creases[crease_id - 1].bkd_facet,
5909 ) else {
5910 self.creases[crease_id - 1].fold = FOLD_BORDER;
5911 return;
5912 };
5913 let fwd_facet = &self.facets[fwd - 1];
5914 let bkd_facet = &self.facets[bkd - 1];
5915 self.creases[crease_id - 1].fold = if fwd_facet.color == bkd_facet.color {
5916 FOLD_FLAT
5917 } else if fwd_facet.color == FACET_COLOR_UP {
5918 if fwd_facet.order > bkd_facet.order {
5919 FOLD_MOUNTAIN
5920 } else {
5921 FOLD_VALLEY
5922 }
5923 } else if fwd_facet.order > bkd_facet.order {
5924 FOLD_VALLEY
5925 } else {
5926 FOLD_MOUNTAIN
5927 };
5928 }
5929
5930 fn other_crease_vertex(&self, crease_id: usize, vertex_id: usize) -> usize {
5931 let crease = &self.creases[crease_id - 1];
5932 if crease.vertices[0] == vertex_id {
5933 crease.vertices[1]
5934 } else {
5935 crease.vertices[0]
5936 }
5937 }
5938
5939 fn crease_is_hinge(&self, crease_id: usize) -> bool {
5940 matches!(
5941 self.creases[crease_id - 1].kind,
5942 CREASE_UNFOLDED_HINGE | CREASE_FOLDED_HINGE | CREASE_PSEUDOHINGE
5943 )
5944 }
5945
5946 fn crease_is_regular_hinge(&self, crease_id: usize) -> bool {
5947 matches!(
5948 self.creases[crease_id - 1].kind,
5949 CREASE_UNFOLDED_HINGE | CREASE_FOLDED_HINGE
5950 )
5951 }
5952
5953 fn crease_is_border(&self, crease_id: usize) -> bool {
5954 let crease = &self.creases[crease_id - 1];
5955 crease.fwd_facet.is_none() || crease.bkd_facet.is_none()
5956 }
5957
5958 fn crease_other_facet(&self, crease_id: usize, facet_id: usize) -> Option<usize> {
5959 let crease = &self.creases[crease_id - 1];
5960 if crease.fwd_facet == Some(facet_id) {
5961 crease.bkd_facet
5962 } else if crease.bkd_facet == Some(facet_id) {
5963 crease.fwd_facet
5964 } else {
5965 None
5966 }
5967 }
5968
5969 fn crease_left_facet(&self, crease_id: usize) -> Option<usize> {
5970 let crease = &self.creases[crease_id - 1];
5971 if let Some(fwd) = crease.fwd_facet
5972 && self.facet_right_crease(fwd) == Some(crease_id)
5973 {
5974 return Some(fwd);
5975 }
5976 if let Some(bkd) = crease.bkd_facet
5977 && self.facet_right_crease(bkd) == Some(crease_id)
5978 {
5979 return Some(bkd);
5980 }
5981 None
5982 }
5983
5984 fn crease_right_facet(&self, crease_id: usize) -> Option<usize> {
5985 let crease = &self.creases[crease_id - 1];
5986 if let Some(fwd) = crease.fwd_facet
5987 && self.facet_left_crease(fwd) == Some(crease_id)
5988 {
5989 return Some(fwd);
5990 }
5991 if let Some(bkd) = crease.bkd_facet
5992 && self.facet_left_crease(bkd) == Some(crease_id)
5993 {
5994 return Some(bkd);
5995 }
5996 None
5997 }
5998
5999 fn crease_left_non_pseudohinge_facet(&self, crease_id: usize) -> Option<usize> {
6000 let mut facet_id = self.crease_left_facet(crease_id)?;
6001 let mut guard = 0;
6002 while self.facet_is_pseudohinge(facet_id) {
6003 facet_id = self.facet_left_facet(facet_id)?;
6004 guard += 1;
6005 if guard > self.facets.len() {
6006 return None;
6007 }
6008 }
6009 Some(facet_id)
6010 }
6011
6012 fn crease_right_non_pseudohinge_facet(&self, crease_id: usize) -> Option<usize> {
6013 let mut facet_id = self.crease_right_facet(crease_id)?;
6014 let mut guard = 0;
6015 while self.facet_is_pseudohinge(facet_id) {
6016 facet_id = self.facet_right_facet(facet_id)?;
6017 guard += 1;
6018 if guard > self.facets.len() {
6019 return None;
6020 }
6021 }
6022 Some(facet_id)
6023 }
6024
6025 fn facet_bottom_crease(&self, facet_id: usize) -> Option<usize> {
6026 self.facets
6027 .get(facet_id.saturating_sub(1))?
6028 .creases
6029 .first()
6030 .copied()
6031 }
6032
6033 fn facet_left_crease(&self, facet_id: usize) -> Option<usize> {
6034 self.facets
6035 .get(facet_id.saturating_sub(1))?
6036 .creases
6037 .last()
6038 .copied()
6039 }
6040
6041 fn facet_right_crease(&self, facet_id: usize) -> Option<usize> {
6042 self.facets
6043 .get(facet_id.saturating_sub(1))?
6044 .creases
6045 .get(1)
6046 .copied()
6047 }
6048
6049 fn facet_is_axial(&self, facet_id: usize) -> bool {
6050 self.facet_bottom_crease(facet_id)
6051 .is_some_and(|crease_id| self.creases[crease_id - 1].kind == CREASE_AXIAL)
6052 }
6053
6054 fn facet_is_pseudohinge(&self, facet_id: usize) -> bool {
6055 self.facet_left_crease(facet_id)
6056 .is_some_and(|crease_id| self.creases[crease_id - 1].kind == CREASE_PSEUDOHINGE)
6057 || self
6058 .facet_right_crease(facet_id)
6059 .is_some_and(|crease_id| self.creases[crease_id - 1].kind == CREASE_PSEUDOHINGE)
6060 }
6061
6062 fn facet_left_facet(&self, facet_id: usize) -> Option<usize> {
6063 self.crease_left_facet(self.facet_left_crease(facet_id)?)
6064 }
6065
6066 fn facet_right_facet(&self, facet_id: usize) -> Option<usize> {
6067 self.crease_right_facet(self.facet_right_crease(facet_id)?)
6068 }
6069
6070 fn facet_right_non_pseudohinge_facet(&self, facet_id: usize) -> Option<usize> {
6071 let mut other_facet = self.facet_right_facet(facet_id)?;
6072 let mut guard = 0;
6073 while self.facet_is_pseudohinge(other_facet) {
6074 other_facet = self.facet_right_facet(other_facet)?;
6075 guard += 1;
6076 if guard > self.facets.len() {
6077 return None;
6078 }
6079 }
6080 Some(other_facet)
6081 }
6082
6083 fn facet_is_source(&self, facet_id: usize) -> bool {
6084 let facet = &self.facets[facet_id - 1];
6085 !facet.head_facets.is_empty() && facet.tail_facets.is_empty()
6086 }
6087
6088 fn facet_is_sink(&self, facet_id: usize) -> bool {
6089 let facet = &self.facets[facet_id - 1];
6090 !facet.tail_facets.is_empty() && facet.head_facets.is_empty()
6091 }
6092
6093 fn facet_link_to(&mut self, tail_facet: usize, head_facet: usize) {
6094 self.facets[tail_facet - 1].head_facets.push(head_facet);
6095 self.facets[head_facet - 1].tail_facets.push(tail_facet);
6096 }
6097
6098 fn facets_are_linked(&self, facet1: usize, facet2: usize) -> bool {
6099 self.facets[facet1 - 1].head_facets.contains(&facet2)
6100 || self.facets[facet2 - 1].head_facets.contains(&facet1)
6101 }
6102
6103 fn facet_link(&mut self, facet1: usize, facet2: usize) {
6104 if self.facet_is_sink(facet1) {
6105 self.facet_link_to(facet1, facet2);
6106 } else {
6107 self.facet_link_to(facet2, facet1);
6108 }
6109 }
6110
6111 fn facet_unlink(&mut self, facet1: usize, facet2: usize) {
6112 if let Some(pos) = self.facets[facet1 - 1]
6113 .head_facets
6114 .iter()
6115 .position(|id| *id == facet2)
6116 {
6117 self.facets[facet1 - 1].head_facets.remove(pos);
6118 if let Some(pos) = self.facets[facet2 - 1]
6119 .tail_facets
6120 .iter()
6121 .position(|id| *id == facet1)
6122 {
6123 self.facets[facet2 - 1].tail_facets.remove(pos);
6124 }
6125 return;
6126 }
6127
6128 if let Some(pos) = self.facets[facet1 - 1]
6129 .tail_facets
6130 .iter()
6131 .position(|id| *id == facet2)
6132 {
6133 self.facets[facet1 - 1].tail_facets.remove(pos);
6134 if let Some(pos) = self.facets[facet2 - 1]
6135 .head_facets
6136 .iter()
6137 .position(|id| *id == facet1)
6138 {
6139 self.facets[facet2 - 1].head_facets.remove(pos);
6140 }
6141 }
6142 }
6143
6144 fn vertex_is_axial(&self, vertex_id: usize) -> bool {
6145 self.vertices[vertex_id - 1]
6146 .creases
6147 .iter()
6148 .any(|crease_id| self.creases[*crease_id - 1].kind == CREASE_AXIAL)
6149 }
6150
6151 fn vertex_degree(&self, vertex_id: usize, crease_list: &[usize]) -> usize {
6152 self.vertices[vertex_id - 1]
6153 .creases
6154 .iter()
6155 .filter(|crease_id| crease_list.contains(crease_id))
6156 .count()
6157 }
6158
6159 fn vertex_num_hinge_creases(&self, vertex_id: usize) -> usize {
6160 self.vertices[vertex_id - 1]
6161 .creases
6162 .iter()
6163 .filter(|crease_id| self.crease_is_hinge(**crease_id))
6164 .count()
6165 }
6166
6167 fn vertex_hinge_crease(&self, vertex_id: usize) -> Option<usize> {
6168 self.vertices[vertex_id - 1]
6169 .creases
6170 .iter()
6171 .copied()
6172 .find(|crease_id| self.crease_is_hinge(*crease_id))
6173 }
6174
6175 fn vertex_hinge_creases(&self, vertex_id: usize) -> Vec<usize> {
6176 self.vertices[vertex_id - 1]
6177 .creases
6178 .iter()
6179 .copied()
6180 .filter(|crease_id| self.crease_is_hinge(*crease_id))
6181 .take(2)
6182 .collect()
6183 }
6184
6185 fn vertex_swap_links(&mut self, vertex_id: usize) {
6186 if !self.vertex_is_axial(vertex_id) || self.vertex_num_hinge_creases(vertex_id) < 2 {
6187 return;
6188 }
6189 let hinge_creases = self.vertex_hinge_creases(vertex_id);
6190 if hinge_creases.len() < 2 {
6191 return;
6192 }
6193 let crease1 = hinge_creases[0];
6194 let crease2 = hinge_creases[1];
6195 let (Some(facet_a), Some(facet_b), Some(facet_c), Some(facet_d)) = (
6196 self.crease_left_facet(crease1),
6197 self.crease_right_facet(crease1),
6198 self.crease_right_facet(crease2),
6199 self.crease_left_facet(crease2),
6200 ) else {
6201 return;
6202 };
6203 self.facet_unlink(facet_a, facet_b);
6204 self.facet_unlink(facet_c, facet_d);
6205 self.facet_link_to(facet_a, facet_c);
6206 self.facet_link_to(facet_d, facet_b);
6207 }
6208
6209 fn leaf_path_id_between(&self, node1: usize, node2: usize) -> Option<usize> {
6210 self.paths
6211 .iter()
6212 .find(|path| {
6213 path.is_leaf
6214 && matches!(
6215 path.nodes.first().copied().zip(path.nodes.last().copied()),
6216 Some((a, b)) if (a == node1 && b == node2) || (a == node2 && b == node1)
6217 )
6218 })
6219 .map(|path| path.index)
6220 }
6221
6222 fn rebuild_conditioned_flags(&mut self) {
6223 let mut conditioned_nodes = vec![false; self.nodes.len()];
6224 let mut conditioned_edges = vec![false; self.edges.len()];
6225 let mut conditioned_paths = vec![false; self.paths.len()];
6226
6227 for condition in &self.conditions {
6228 condition.kind.collect_conditioned_parts(
6229 self,
6230 &mut conditioned_nodes,
6231 &mut conditioned_edges,
6232 &mut conditioned_paths,
6233 );
6234 }
6235
6236 for (node, conditioned) in self.nodes.iter_mut().zip(conditioned_nodes) {
6237 node.is_conditioned = conditioned;
6238 }
6239 for (edge, conditioned) in self.edges.iter_mut().zip(conditioned_edges) {
6240 edge.is_conditioned = conditioned;
6241 }
6242 for (path, conditioned) in self.paths.iter_mut().zip(conditioned_paths) {
6243 path.is_conditioned = conditioned;
6244 }
6245 }
6246}
6247
6248struct Reader<'a> {
6249 bytes: &'a [u8],
6250 pos: usize,
6251}
6252
6253impl<'a> Reader<'a> {
6254 fn new(input: &'a str) -> Self {
6255 Self {
6256 bytes: input.as_bytes(),
6257 pos: 0,
6258 }
6259 }
6260
6261 fn expect_tag(&mut self, expected: &'static str) -> Result<()> {
6262 let tag = self.read_token("tag")?;
6263 if tag != expected {
6264 return Err(self.err(format!("expected tag {expected:?}, found {tag:?}")));
6265 }
6266 Ok(())
6267 }
6268
6269 fn read_token(&mut self, label: &'static str) -> Result<String> {
6270 self.skip_leading_ws();
6271 if self.pos >= self.bytes.len() {
6272 return Err(self.err(format!("expected {label}, found end of input")));
6273 }
6274 let start = self.pos;
6275 while self.pos < self.bytes.len() && !self.bytes[self.pos].is_ascii_whitespace() {
6276 self.pos += 1;
6277 }
6278 if start == self.pos {
6279 return Err(self.err(format!("expected {label}")));
6280 }
6281 let token = std::str::from_utf8(&self.bytes[start..self.pos])
6282 .map_err(|_| self.err(format!("{label} was not UTF-8")))?
6283 .to_string();
6284 self.consume_trailing_space();
6285 Ok(token)
6286 }
6287
6288 fn read_line_field(&mut self, label: &'static str) -> Result<String> {
6289 if self.pos >= self.bytes.len() {
6290 return Err(self.err(format!("expected {label}, found end of input")));
6291 }
6292 let mut out = Vec::new();
6293 while self.pos < self.bytes.len() {
6294 let b = self.bytes[self.pos];
6295 self.pos += 1;
6296 match b {
6297 b'\r' => {
6298 if self.pos < self.bytes.len() && self.bytes[self.pos] == b'\n' {
6299 self.pos += 1;
6300 }
6301 break;
6302 }
6303 b'\n' => break,
6304 b'\\' => {
6305 let Some(next) = self.bytes.get(self.pos).copied() else {
6306 return Err(self.err("dangling escape in C string".to_string()));
6307 };
6308 self.pos += 1;
6309 match next {
6310 b'n' => out.push(b'\n'),
6311 b'r' => out.push(b'\r'),
6312 b'\\' => out.push(b'\\'),
6313 other => {
6314 return Err(self.err(format!(
6315 "bad escape sequence \\{} in {label}",
6316 other as char
6317 )));
6318 }
6319 }
6320 }
6321 other => out.push(other),
6322 }
6323 }
6324 String::from_utf8(out).map_err(|_| self.err(format!("{label} was not UTF-8")))
6325 }
6326
6327 fn read_raw_line(&mut self, label: &'static str) -> Result<String> {
6328 if self.pos >= self.bytes.len() {
6329 return Err(self.err(format!("expected {label}, found end of input")));
6330 }
6331 let start = self.pos;
6332 while self.pos < self.bytes.len() {
6333 match self.bytes[self.pos] {
6334 b'\r' => {
6335 let end = self.pos;
6336 self.pos += 1;
6337 if self.pos < self.bytes.len() && self.bytes[self.pos] == b'\n' {
6338 self.pos += 1;
6339 }
6340 return std::str::from_utf8(&self.bytes[start..end])
6341 .map(|s| s.to_string())
6342 .map_err(|_| self.err(format!("{label} was not UTF-8")));
6343 }
6344 b'\n' => {
6345 let end = self.pos;
6346 self.pos += 1;
6347 return std::str::from_utf8(&self.bytes[start..end])
6348 .map(|s| s.to_string())
6349 .map_err(|_| self.err(format!("{label} was not UTF-8")));
6350 }
6351 _ => self.pos += 1,
6352 }
6353 }
6354 std::str::from_utf8(&self.bytes[start..self.pos])
6355 .map(|s| s.to_string())
6356 .map_err(|_| self.err(format!("{label} was not UTF-8")))
6357 }
6358
6359 fn read_usize(&mut self, label: &'static str) -> Result<usize> {
6360 let offset = self.pos;
6361 let token = self.read_token(label)?;
6362 token.parse::<usize>().map_err(|_| TreeError::Parse {
6363 offset,
6364 message: format!("expected unsigned integer for {label}, found {token:?}"),
6365 })
6366 }
6367
6368 fn read_i32(&mut self, label: &'static str) -> Result<i32> {
6369 let offset = self.pos;
6370 let token = self.read_token(label)?;
6371 token.parse::<i32>().map_err(|_| TreeError::Parse {
6372 offset,
6373 message: format!("expected integer for {label}, found {token:?}"),
6374 })
6375 }
6376
6377 fn read_f64(&mut self, label: &'static str) -> Result<TmFloat> {
6378 let offset = self.pos;
6379 let token = self.read_token(label)?;
6380 if token.starts_with("NAN") {
6381 return Ok(0.0);
6382 }
6383 token.parse::<TmFloat>().map_err(|_| TreeError::Parse {
6384 offset,
6385 message: format!("expected float for {label}, found {token:?}"),
6386 })
6387 }
6388
6389 fn read_bool(&mut self, label: &'static str) -> Result<bool> {
6390 Ok(self.read_token(label)? == "true")
6391 }
6392
6393 fn read_point(&mut self, label: &'static str) -> Result<Point> {
6394 Ok(Point {
6395 x: self.read_f64(label)?,
6396 y: self.read_f64(label)?,
6397 })
6398 }
6399
6400 fn read_index_array(&mut self, label: &'static str) -> Result<Vec<usize>> {
6401 let n = self.read_usize(label)?;
6402 let mut values = Vec::with_capacity(n);
6403 for _ in 0..n {
6404 values.push(self.read_usize(label)?);
6405 }
6406 Ok(values)
6407 }
6408
6409 fn read_optional_index_in_range(
6410 &mut self,
6411 label: &'static str,
6412 max: usize,
6413 ) -> Result<Option<usize>> {
6414 match self.read_usize(label)? {
6415 0 => Ok(None),
6416 n if n <= max => Ok(Some(n)),
6417 _ => Ok(None),
6418 }
6419 }
6420
6421 fn read_point_array(&mut self, label: &'static str) -> Result<Vec<Point>> {
6422 let n = self.read_usize(label)?;
6423 let mut points = Vec::with_capacity(n);
6424 for _ in 0..n {
6425 points.push(self.read_point(label)?);
6426 }
6427 Ok(points)
6428 }
6429
6430 fn read_node_v3(&mut self, conditions: &mut Vec<Condition>) -> Result<Node> {
6431 self.expect_tag("node")?;
6432 let index = self.read_usize("node index")?;
6433 let label = self.read_line_field("node label")?;
6434 let loc = self.read_point("node location")?;
6435
6436 let node_is_symmetric = self.read_bool("node symmetric flag")?;
6437 if node_is_symmetric {
6438 push_condition(conditions, ConditionKind::NodeSymmetric { node: index });
6439 }
6440
6441 let node_is_paired = self.read_bool("node paired flag")?;
6442 let paired_node = self.read_usize("paired node")?;
6443 if node_is_paired && index > paired_node {
6444 push_condition(
6445 conditions,
6446 ConditionKind::NodesPaired {
6447 node1: index,
6448 node2: paired_node,
6449 },
6450 );
6451 }
6452
6453 let x_fixed = self.read_bool("node x fixed flag")?;
6454 let y_fixed = self.read_bool("node y fixed flag")?;
6455 let x_fix_value = self.read_f64("node x fixed value")?;
6456 let y_fix_value = self.read_f64("node y fixed value")?;
6457 if x_fixed || y_fixed {
6458 push_condition(
6459 conditions,
6460 ConditionKind::NodeFixed {
6461 node: index,
6462 x_fixed,
6463 y_fixed,
6464 x_fix_value: if x_fixed { x_fix_value } else { 0.0 },
6465 y_fix_value: if y_fixed { y_fix_value } else { 0.0 },
6466 },
6467 );
6468 }
6469
6470 let node_stick_to_edge = self.read_bool("node stick-to-edge flag")?;
6471 if node_stick_to_edge {
6472 push_condition(conditions, ConditionKind::NodeOnEdge { node: index });
6473 }
6474
6475 let node_is_collinear = self.read_bool("node collinear flag")?;
6476 let collinear_node1 = self.read_usize("collinear node 1")?;
6477 let collinear_node2 = self.read_usize("collinear node 2")?;
6478 if node_is_collinear && index > collinear_node1 && index > collinear_node2 {
6479 push_condition(
6480 conditions,
6481 ConditionKind::NodesCollinear {
6482 node1: index,
6483 node2: collinear_node1,
6484 node3: collinear_node2,
6485 },
6486 );
6487 }
6488
6489 Ok(Node {
6490 index,
6491 label,
6492 loc,
6493 depth: DEPTH_NOT_SET,
6494 elevation: 0.0,
6495 is_leaf: self.read_bool("node leaf flag")?,
6496 is_sub: false,
6497 is_border: self.read_bool("node border flag")?,
6498 is_pinned: self.read_bool("node pinned flag")?,
6499 is_polygon: self.read_bool("node polygon flag")?,
6500 is_junction: false,
6501 is_conditioned: false,
6502 owned_vertices: Vec::new(),
6503 edges: self.read_index_array("node edges")?,
6504 leaf_paths: self.read_index_array("node leaf paths")?,
6505 owner: OwnerRef::Tree,
6506 })
6507 }
6508
6509 fn read_node_v4(&mut self) -> Result<Node> {
6510 self.expect_tag("node")?;
6511 Ok(Node {
6512 index: self.read_usize("node index")?,
6513 label: self.read_line_field("node label")?,
6514 loc: self.read_point("node location")?,
6515 depth: DEPTH_NOT_SET,
6516 elevation: 0.0,
6517 is_leaf: self.read_bool("node leaf flag")?,
6518 is_sub: self.read_bool("node sub flag")?,
6519 is_border: self.read_bool("node border flag")?,
6520 is_pinned: self.read_bool("node pinned flag")?,
6521 is_polygon: self.read_bool("node polygon flag")?,
6522 is_junction: false,
6523 is_conditioned: self.read_bool("node conditioned flag")?,
6524 owned_vertices: self.read_index_array("node owned vertices")?,
6525 edges: self.read_index_array("node edges")?,
6526 leaf_paths: self.read_index_array("node leaf paths")?,
6527 owner: self.read_node_owner()?,
6528 })
6529 }
6530
6531 fn read_node_v5(&mut self) -> Result<Node> {
6532 self.expect_tag("node")?;
6533 Ok(Node {
6534 index: self.read_usize("node index")?,
6535 label: self.read_line_field("node label")?,
6536 loc: self.read_point("node location")?,
6537 depth: self.read_f64("node depth")?,
6538 elevation: self.read_f64("node elevation")?,
6539 is_leaf: self.read_bool("node leaf flag")?,
6540 is_sub: self.read_bool("node sub flag")?,
6541 is_border: self.read_bool("node border flag")?,
6542 is_pinned: self.read_bool("node pinned flag")?,
6543 is_polygon: self.read_bool("node polygon flag")?,
6544 is_junction: self.read_bool("node junction flag")?,
6545 is_conditioned: self.read_bool("node conditioned flag")?,
6546 edges: self.read_index_array("node edges")?,
6547 leaf_paths: self.read_index_array("node leaf paths")?,
6548 owned_vertices: self.read_index_array("node owned vertices")?,
6549 owner: self.read_node_owner()?,
6550 })
6551 }
6552
6553 fn read_edge_v3(&mut self) -> Result<Edge> {
6554 self.expect_tag("edge")?;
6555 Ok(Edge {
6556 index: self.read_usize("edge index")?,
6557 label: self.read_line_field("edge label")?,
6558 length: self.read_f64("edge length")?,
6559 strain: 0.0,
6560 stiffness: 0.0,
6561 is_pinned: self.read_bool("edge pinned flag")?,
6562 is_conditioned: false,
6563 nodes: self.read_index_array("edge nodes")?,
6564 })
6565 }
6566
6567 fn read_edge(&mut self, repair_zero_stiffness: bool) -> Result<Edge> {
6568 self.expect_tag("edge")?;
6569 let index = self.read_usize("edge index")?;
6570 let label = self.read_line_field("edge label")?;
6571 let length = self.read_f64("edge length")?;
6572 let strain = self.read_f64("edge strain")?;
6573 let mut stiffness = self.read_f64("edge stiffness")?;
6574 if repair_zero_stiffness && stiffness == 0.0 {
6575 stiffness = 1.0;
6576 }
6577 let edge = Edge {
6578 index,
6579 label,
6580 length,
6581 strain,
6582 stiffness,
6583 is_pinned: self.read_bool("edge pinned flag")?,
6584 is_conditioned: self.read_bool("edge conditioned flag")?,
6585 nodes: self.read_index_array("edge nodes")?,
6586 };
6587 Ok(edge)
6588 }
6589
6590 fn read_path_v3(&mut self, conditions: &mut Vec<Condition>) -> Result<Path> {
6591 self.expect_tag("path")?;
6592 let index = self.read_usize("path index")?;
6593 let min_tree_length = self.read_f64("path min tree length")?;
6594 let path_fixed_length = self.read_bool("path fixed length flag")?;
6595 let path_fixed_length_value = self.read_f64("path fixed length value")?;
6596 let path_fixed_angle = self.read_bool("path fixed angle flag")?;
6597 let _path_fixed_angle_value = self.read_f64("path fixed angle value")?;
6598 let is_leaf = self.read_bool("path leaf flag")?;
6599 let is_active = self.read_bool("path active flag")?;
6600 let is_border = self.read_bool("path border flag")?;
6601 let is_polygon = self.read_bool("path polygon flag")?;
6602 let _legacy_fwd_poly = self.read_usize("path fwd poly")?;
6603 let _legacy_bkd_poly = self.read_usize("path bkd poly")?;
6604 let nodes = self.read_index_array("path nodes")?;
6605 let edges = self.read_index_array("path edges")?;
6606
6607 if let Some((node1, node2)) = nodes.first().copied().zip(nodes.last().copied()) {
6608 if path_fixed_length && min_tree_length == path_fixed_length_value {
6609 push_condition(conditions, ConditionKind::PathActive { node1, node2 });
6610 }
6611 if path_fixed_angle {
6612 push_condition(
6615 conditions,
6616 ConditionKind::PathAngleFixed {
6617 node1,
6618 node2,
6619 angle: 1.0,
6620 },
6621 );
6622 }
6623 }
6624
6625 Ok(Path {
6626 index,
6627 min_tree_length,
6628 min_paper_length: 0.0,
6629 act_tree_length: 0.0,
6630 act_paper_length: 0.0,
6631 is_leaf,
6632 is_sub: false,
6633 is_feasible: false,
6634 is_active,
6635 is_border,
6636 is_polygon,
6637 is_conditioned: false,
6638 fwd_poly: None,
6639 bkd_poly: None,
6640 nodes,
6641 edges,
6642 outset_path: None,
6643 front_reduction: 0.0,
6644 back_reduction: 0.0,
6645 min_depth: DEPTH_NOT_SET,
6646 min_depth_dist: DEPTH_NOT_SET,
6647 owned_vertices: Vec::new(),
6648 owned_creases: Vec::new(),
6649 owner: OwnerRef::Tree,
6650 })
6651 }
6652
6653 fn read_path_v4(&mut self, poly_count: usize) -> Result<Path> {
6654 self.expect_tag("path")?;
6655 Ok(Path {
6656 index: self.read_usize("path index")?,
6657 min_tree_length: self.read_f64("path min tree length")?,
6658 min_paper_length: self.read_f64("path min paper length")?,
6659 act_tree_length: 0.0,
6660 act_paper_length: 0.0,
6661 is_leaf: self.read_bool("path leaf flag")?,
6662 is_sub: self.read_bool("path sub flag")?,
6663 is_feasible: false,
6664 is_active: self.read_bool("path active flag")?,
6665 is_border: self.read_bool("path border flag")?,
6666 is_polygon: self.read_bool("path polygon flag")?,
6667 is_conditioned: self.read_bool("path conditioned flag")?,
6668 owned_vertices: self.read_index_array("path owned vertices")?,
6669 fwd_poly: self.read_optional_index_in_range("path fwd poly", poly_count)?,
6670 bkd_poly: self.read_optional_index_in_range("path bkd poly", poly_count)?,
6671 nodes: self.read_index_array("path nodes")?,
6672 edges: self.read_index_array("path edges")?,
6673 owner: self.read_path_owner()?,
6674 outset_path: None,
6675 front_reduction: 0.0,
6676 back_reduction: 0.0,
6677 min_depth: DEPTH_NOT_SET,
6678 min_depth_dist: DEPTH_NOT_SET,
6679 owned_creases: Vec::new(),
6680 })
6681 }
6682
6683 fn read_path_v5(&mut self, poly_count: usize, path_count: usize) -> Result<Path> {
6684 self.expect_tag("path")?;
6685 Ok(Path {
6686 index: self.read_usize("path index")?,
6687 min_tree_length: self.read_f64("path min tree length")?,
6688 min_paper_length: self.read_f64("path min paper length")?,
6689 act_tree_length: self.read_f64("path actual tree length")?,
6690 act_paper_length: self.read_f64("path actual paper length")?,
6691 is_leaf: self.read_bool("path leaf flag")?,
6692 is_sub: self.read_bool("path sub flag")?,
6693 is_feasible: self.read_bool("path feasible flag")?,
6694 is_active: self.read_bool("path active flag")?,
6695 is_border: self.read_bool("path border flag")?,
6696 is_polygon: self.read_bool("path polygon flag")?,
6697 is_conditioned: self.read_bool("path conditioned flag")?,
6698 fwd_poly: self.read_optional_index_in_range("path fwd poly", poly_count)?,
6699 bkd_poly: self.read_optional_index_in_range("path bkd poly", poly_count)?,
6700 nodes: self.read_index_array("path nodes")?,
6701 edges: self.read_index_array("path edges")?,
6702 outset_path: self.read_optional_index_in_range("path outset", path_count)?,
6703 front_reduction: self.read_f64("path front reduction")?,
6704 back_reduction: self.read_f64("path back reduction")?,
6705 min_depth: self.read_f64("path min depth")?,
6706 min_depth_dist: self.read_f64("path min depth distance")?,
6707 owned_vertices: self.read_index_array("path owned vertices")?,
6708 owned_creases: self.read_index_array("path owned creases")?,
6709 owner: self.read_path_owner()?,
6710 })
6711 }
6712
6713 fn read_poly_v4(&mut self, path_count: usize) -> Result<Poly> {
6714 self.expect_tag("poly")?;
6715 let index = self.read_usize("poly index")?;
6716 let centroid = self.read_point("poly centroid")?;
6717 let node_locs = self.read_point_array("poly node locations")?;
6718 let is_sub_poly = self.read_bool("poly sub flag")?;
6719 let owned_nodes = self.read_index_array("poly owned nodes")?;
6720 let owned_paths = self.read_index_array("poly owned paths")?;
6721 let owned_polys = self.read_index_array("poly owned polys")?;
6722 let owned_creases = self.read_index_array("poly owned creases")?;
6723 let ring_nodes = self.read_index_array("poly ring nodes")?;
6724 let ring_paths = self.read_index_array("poly ring paths")?;
6725 let cross_paths = self.read_index_array("poly cross paths")?;
6726 let inset_nodes = self.read_index_array("poly inset nodes")?;
6727 let spoke_paths = self.read_index_array("poly spoke paths")?;
6728 let ridge_path = self.read_optional_index_in_range("poly ridge path", path_count)?;
6729 let owner = self.read_poly_owner()?;
6730 Ok(Poly {
6731 index,
6732 centroid,
6733 is_sub_poly,
6734 ring_nodes,
6735 ring_paths,
6736 cross_paths,
6737 inset_nodes,
6738 spoke_paths,
6739 ridge_path,
6740 node_locs,
6741 local_root_vertices: Vec::new(),
6742 local_root_creases: Vec::new(),
6743 owned_nodes,
6744 owned_paths,
6745 owned_polys,
6746 owned_creases,
6747 owned_facets: Vec::new(),
6748 owner,
6749 })
6750 }
6751
6752 fn read_poly_v5(&mut self, path_count: usize) -> Result<Poly> {
6753 self.expect_tag("poly")?;
6754 let index = self.read_usize("poly index")?;
6755 let centroid = self.read_point("poly centroid")?;
6756 let is_sub_poly = self.read_bool("poly sub flag")?;
6757 Ok(Poly {
6758 index,
6759 centroid,
6760 is_sub_poly,
6761 ring_nodes: self.read_index_array("poly ring nodes")?,
6762 ring_paths: self.read_index_array("poly ring paths")?,
6763 cross_paths: self.read_index_array("poly cross paths")?,
6764 inset_nodes: self.read_index_array("poly inset nodes")?,
6765 spoke_paths: self.read_index_array("poly spoke paths")?,
6766 ridge_path: self.read_optional_index_in_range("poly ridge path", path_count)?,
6767 node_locs: self.read_point_array("poly node locations")?,
6768 local_root_vertices: self.read_index_array("poly local root vertices")?,
6769 local_root_creases: self.read_index_array("poly local root creases")?,
6770 owned_nodes: self.read_index_array("poly owned nodes")?,
6771 owned_paths: self.read_index_array("poly owned paths")?,
6772 owned_polys: self.read_index_array("poly owned polys")?,
6773 owned_creases: self.read_index_array("poly owned creases")?,
6774 owned_facets: self.read_index_array("poly owned facets")?,
6775 owner: self.read_poly_owner()?,
6776 })
6777 }
6778
6779 fn read_vertex_v4(&mut self, index: usize) -> Result<Vertex> {
6780 self.expect_tag("vrtx")?;
6781 Ok(Vertex {
6782 index,
6783 loc: self.read_point("vertex location")?,
6784 elevation: 0.0,
6785 is_border: false,
6786 tree_node: None,
6787 left_pseudohinge_mate: None,
6788 right_pseudohinge_mate: None,
6789 creases: self.read_index_array("vertex creases")?,
6790 depth: DEPTH_NOT_SET,
6791 discrete_depth: 0,
6792 cc_flag: 0,
6793 st_flag: 0,
6794 owner: self.read_vertex_owner()?,
6795 })
6796 }
6797
6798 fn read_vertex_v5(&mut self, node_count: usize, vertex_count: usize) -> Result<Vertex> {
6799 self.expect_tag("vrtx")?;
6800 let index = self.read_usize("vertex index")?;
6801 Ok(Vertex {
6802 index,
6803 loc: self.read_point("vertex location")?,
6804 elevation: self.read_f64("vertex elevation")?,
6805 is_border: self.read_bool("vertex border flag")?,
6806 tree_node: self.read_optional_index_in_range("vertex tree node", node_count)?,
6807 left_pseudohinge_mate: self
6808 .read_optional_index_in_range("vertex left pseudohinge mate", vertex_count)?,
6809 right_pseudohinge_mate: self
6810 .read_optional_index_in_range("vertex right pseudohinge mate", vertex_count)?,
6811 creases: self.read_index_array("vertex creases")?,
6812 depth: self.read_f64("vertex depth")?,
6813 discrete_depth: self.read_usize("vertex discrete depth")?,
6814 cc_flag: self.read_i32("vertex cc flag")?,
6815 st_flag: self.read_i32("vertex st flag")?,
6816 owner: self.read_vertex_owner()?,
6817 })
6818 }
6819
6820 fn read_crease_v4(&mut self, index: usize) -> Result<Crease> {
6821 self.expect_tag("crse")?;
6822 Ok(Crease {
6823 index,
6824 kind: self.read_i32("crease kind")?,
6825 vertices: self.read_index_array("crease vertices")?,
6826 fwd_facet: None,
6827 bkd_facet: None,
6828 fold: 0,
6829 cc_flag: 0,
6830 st_flag: 0,
6831 owner: self.read_crease_owner()?,
6832 })
6833 }
6834
6835 fn read_crease_v5(&mut self, facet_count: usize) -> Result<Crease> {
6836 self.expect_tag("crse")?;
6837 let index = self.read_usize("crease index")?;
6838 Ok(Crease {
6839 index,
6840 kind: self.read_i32("crease kind")?,
6841 vertices: self.read_index_array("crease vertices")?,
6842 fwd_facet: self.read_optional_index_in_range("crease fwd facet", facet_count)?,
6843 bkd_facet: self.read_optional_index_in_range("crease bkd facet", facet_count)?,
6844 fold: self.read_i32("crease fold")?,
6845 cc_flag: self.read_i32("crease cc flag")?,
6846 st_flag: self.read_i32("crease st flag")?,
6847 owner: self.read_crease_owner()?,
6848 })
6849 }
6850
6851 fn read_facet_v5(&mut self, edge_count: usize) -> Result<Facet> {
6852 self.expect_tag("fact")?;
6853 let index = self.read_usize("facet index")?;
6854 Ok(Facet {
6855 index,
6856 centroid: self.read_point("facet centroid")?,
6857 is_well_formed: self.read_bool("facet well-formed flag")?,
6858 vertices: self.read_index_array("facet vertices")?,
6859 creases: self.read_index_array("facet creases")?,
6860 corridor_edge: self.read_optional_index_in_range("facet corridor edge", edge_count)?,
6861 head_facets: self.read_index_array("facet head facets")?,
6862 tail_facets: self.read_index_array("facet tail facets")?,
6863 order: self.read_usize("facet order")?,
6864 color: self.read_i32("facet color")?,
6865 owner: self.read_facet_owner()?,
6866 })
6867 }
6868
6869 fn read_condition_v4(&mut self, index: usize) -> Result<Condition> {
6870 let tag = self.read_token("condition tag")?;
6871 if tag.len() > 4 {
6872 return Err(self.err(format!("bad condition tag {tag:?}")));
6873 }
6874 let n = self.read_usize("condition line count")?;
6875 validate_condition_rest_len(&tag, n)?;
6876 let mut raw_lines = Vec::with_capacity(n);
6877 for _ in 0..n {
6878 raw_lines.push(self.read_raw_line("condition field")?);
6879 }
6880 let kind = ConditionKind::from_stream(&tag, &raw_lines)?;
6881 Ok(Condition {
6882 index,
6883 is_feasible: true,
6884 kind,
6885 })
6886 }
6887
6888 fn read_condition_v5(&mut self) -> Result<Condition> {
6889 let tag = self.read_token("condition tag")?;
6890 if tag.len() > 4 {
6891 return Err(self.err(format!("bad condition tag {tag:?}")));
6892 }
6893 let index = self.read_usize("condition index")?;
6894 let is_feasible = self.read_bool("condition feasibility")?;
6895 let n = self.read_usize("condition line count")?;
6896 validate_condition_rest_len(&tag, n)?;
6897 let mut raw_lines = Vec::with_capacity(n);
6898 for _ in 0..n {
6899 raw_lines.push(self.read_raw_line("condition field")?);
6900 }
6901 let kind = ConditionKind::from_stream(&tag, &raw_lines)?;
6902 Ok(Condition {
6903 index,
6904 is_feasible,
6905 kind,
6906 })
6907 }
6908
6909 fn read_node_owner(&mut self) -> Result<OwnerRef> {
6910 if self.read_usize("node owner is poly")? == 1 {
6911 Ok(OwnerRef::Poly(self.read_usize("node owner poly")?))
6912 } else {
6913 Ok(OwnerRef::Tree)
6914 }
6915 }
6916
6917 fn read_path_owner(&mut self) -> Result<OwnerRef> {
6918 if self.read_usize("path owner is poly")? == 1 {
6919 Ok(OwnerRef::Poly(self.read_usize("path owner poly")?))
6920 } else {
6921 Ok(OwnerRef::Tree)
6922 }
6923 }
6924
6925 fn read_poly_owner(&mut self) -> Result<OwnerRef> {
6926 if self.read_usize("poly owner is poly")? == 1 {
6927 Ok(OwnerRef::Poly(self.read_usize("poly owner poly")?))
6928 } else {
6929 Ok(OwnerRef::Tree)
6930 }
6931 }
6932
6933 fn read_vertex_owner(&mut self) -> Result<OwnerRef> {
6934 if self.read_usize("vertex owner is node")? == 1 {
6935 Ok(OwnerRef::Node(self.read_usize("vertex owner node")?))
6936 } else {
6937 Ok(OwnerRef::Path(self.read_usize("vertex owner path")?))
6938 }
6939 }
6940
6941 fn read_crease_owner(&mut self) -> Result<OwnerRef> {
6942 if self.read_usize("crease owner is poly")? == 1 {
6943 Ok(OwnerRef::Poly(self.read_usize("crease owner poly")?))
6944 } else {
6945 Ok(OwnerRef::Path(self.read_usize("crease owner path")?))
6946 }
6947 }
6948
6949 fn read_facet_owner(&mut self) -> Result<OwnerRef> {
6950 Ok(OwnerRef::Poly(self.read_usize("facet owner poly")?))
6951 }
6952
6953 fn skip_leading_ws(&mut self) {
6954 while self.pos < self.bytes.len() && self.bytes[self.pos].is_ascii_whitespace() {
6955 self.pos += 1;
6956 }
6957 }
6958
6959 fn consume_trailing_space(&mut self) {
6960 while self.pos < self.bytes.len()
6961 && (self.bytes[self.pos] == b' ' || self.bytes[self.pos] == b'\t')
6962 {
6963 self.pos += 1;
6964 }
6965 if self.pos < self.bytes.len() {
6966 match self.bytes[self.pos] {
6967 b'\n' => self.pos += 1,
6968 b'\r' => {
6969 self.pos += 1;
6970 if self.pos < self.bytes.len() && self.bytes[self.pos] == b'\n' {
6971 self.pos += 1;
6972 }
6973 }
6974 _ => {}
6975 }
6976 }
6977 }
6978
6979 fn err(&self, message: String) -> TreeError {
6980 TreeError::Parse {
6981 offset: self.pos,
6982 message,
6983 }
6984 }
6985}
6986
6987struct Writer {
6988 precision: usize,
6989 eol: &'static str,
6990 out: String,
6991}
6992
6993impl Writer {
6994 fn new(precision: usize, eol: &'static str) -> Self {
6995 Self {
6996 precision,
6997 eol,
6998 out: String::new(),
6999 }
7000 }
7001
7002 fn finish(self) -> String {
7003 self.out
7004 }
7005
7006 fn line(&mut self, value: impl AsRef<str>) {
7007 self.out.push_str(value.as_ref());
7008 self.out.push_str(self.eol);
7009 }
7010
7011 fn s(&mut self, value: &str) {
7012 self.line(value);
7013 }
7014
7015 fn cstr(&mut self, value: &str) {
7016 let mut escaped = String::new();
7017 for ch in value.chars() {
7018 match ch {
7019 '\n' => escaped.push_str("\\n"),
7020 '\r' => escaped.push_str("\\r"),
7021 '\\' => escaped.push_str("\\\\"),
7022 _ => escaped.push(ch),
7023 }
7024 }
7025 self.line(escaped);
7026 }
7027
7028 fn u(&mut self, value: usize) {
7029 self.line(value.to_string());
7030 }
7031
7032 fn i(&mut self, value: i32) {
7033 self.line(value.to_string());
7034 }
7035
7036 fn f(&mut self, value: TmFloat) {
7037 self.line(fmt_float(value, self.precision));
7038 }
7039
7040 fn b(&mut self, value: bool) {
7041 self.line(if value { "true" } else { "false" });
7042 }
7043
7044 fn point(&mut self, value: Point) {
7045 self.f(value.x);
7046 self.f(value.y);
7047 }
7048
7049 fn array(&mut self, values: &[usize]) {
7050 self.u(values.len());
7051 for value in values {
7052 self.u(*value);
7053 }
7054 }
7055
7056 fn point_array(&mut self, values: &[Point]) {
7057 self.u(values.len());
7058 for value in values {
7059 self.point(*value);
7060 }
7061 }
7062
7063 fn owner_node_or_tree(&mut self, owner: &OwnerRef) {
7064 match owner {
7065 OwnerRef::Poly(id) => {
7066 self.u(1);
7067 self.u(*id);
7068 }
7069 _ => self.u(0),
7070 }
7071 }
7072
7073 fn owner_vertex(&mut self, owner: &OwnerRef) {
7074 match owner {
7075 OwnerRef::Node(id) => {
7076 self.u(1);
7077 self.u(*id);
7078 }
7079 OwnerRef::Path(id) => {
7080 self.u(0);
7081 self.u(*id);
7082 }
7083 _ => {
7084 self.u(0);
7085 self.u(0);
7086 }
7087 }
7088 }
7089
7090 fn owner_crease(&mut self, owner: &OwnerRef) {
7091 match owner {
7092 OwnerRef::Poly(id) => {
7093 self.u(1);
7094 self.u(*id);
7095 }
7096 OwnerRef::Path(id) => {
7097 self.u(0);
7098 self.u(*id);
7099 }
7100 _ => {
7101 self.u(0);
7102 self.u(0);
7103 }
7104 }
7105 }
7106
7107 fn owner_facet(&mut self, owner: &OwnerRef) {
7108 match owner {
7109 OwnerRef::Poly(id) => self.u(*id),
7110 _ => self.u(0),
7111 }
7112 }
7113
7114 fn node_v4(&mut self, node: &Node) {
7115 self.s("node");
7116 self.u(node.index);
7117 self.cstr(&node.label);
7118 self.point(node.loc);
7119 self.b(node.is_leaf);
7120 self.b(node.is_sub);
7121 self.b(node.is_border);
7122 self.b(node.is_pinned);
7123 self.b(node.is_polygon);
7124 self.b(node.is_conditioned);
7125 self.array(&node.owned_vertices);
7126 self.array(&node.edges);
7127 self.array(&node.leaf_paths);
7128 self.owner_node_or_tree(&node.owner);
7129 }
7130
7131 fn node_v5(&mut self, node: &Node) {
7132 self.s("node");
7133 self.u(node.index);
7134 self.cstr(&node.label);
7135 self.point(node.loc);
7136 self.f(node.depth);
7137 self.f(node.elevation);
7138 self.b(node.is_leaf);
7139 self.b(node.is_sub);
7140 self.b(node.is_border);
7141 self.b(node.is_pinned);
7142 self.b(node.is_polygon);
7143 self.b(node.is_junction);
7144 self.b(node.is_conditioned);
7145 self.array(&node.edges);
7146 self.array(&node.leaf_paths);
7147 self.array(&node.owned_vertices);
7148 self.owner_node_or_tree(&node.owner);
7149 }
7150
7151 fn edge(&mut self, edge: &Edge) {
7152 self.s("edge");
7153 self.u(edge.index);
7154 self.cstr(&edge.label);
7155 self.f(edge.length);
7156 self.f(edge.strain);
7157 self.f(edge.stiffness);
7158 self.b(edge.is_pinned);
7159 self.b(edge.is_conditioned);
7160 self.array(&edge.nodes);
7161 }
7162
7163 fn path_v4(&mut self, path: &Path) {
7164 self.s("path");
7165 self.u(path.index);
7166 self.f(path.min_tree_length);
7167 self.f(path.min_paper_length);
7168 self.b(path.is_leaf);
7169 self.b(path.is_sub);
7170 self.b(path.is_active);
7171 self.b(path.is_border);
7172 self.b(path.is_polygon);
7173 self.b(path.is_conditioned);
7174 self.array(&path.owned_vertices);
7175 self.u(path.fwd_poly.unwrap_or(0));
7176 self.u(path.bkd_poly.unwrap_or(0));
7177 self.array(&path.nodes);
7178 self.array(&path.edges);
7179 self.owner_node_or_tree(&path.owner);
7180 }
7181
7182 fn path_v5(&mut self, path: &Path) {
7183 self.s("path");
7184 self.u(path.index);
7185 self.f(path.min_tree_length);
7186 self.f(path.min_paper_length);
7187 self.f(path.act_tree_length);
7188 self.f(path.act_paper_length);
7189 self.b(path.is_leaf);
7190 self.b(path.is_sub);
7191 self.b(path.is_feasible);
7192 self.b(path.is_active);
7193 self.b(path.is_border);
7194 self.b(path.is_polygon);
7195 self.b(path.is_conditioned);
7196 self.u(path.fwd_poly.unwrap_or(0));
7197 self.u(path.bkd_poly.unwrap_or(0));
7198 self.array(&path.nodes);
7199 self.array(&path.edges);
7200 self.u(path.outset_path.unwrap_or(0));
7201 self.f(path.front_reduction);
7202 self.f(path.back_reduction);
7203 self.f(path.min_depth);
7204 self.f(path.min_depth_dist);
7205 self.array(&path.owned_vertices);
7206 self.array(&path.owned_creases);
7207 self.owner_node_or_tree(&path.owner);
7208 }
7209
7210 fn poly_v5(&mut self, poly: &Poly) {
7211 self.s("poly");
7212 self.u(poly.index);
7213 self.point(poly.centroid);
7214 self.b(poly.is_sub_poly);
7215 self.array(&poly.ring_nodes);
7216 self.array(&poly.ring_paths);
7217 self.array(&poly.cross_paths);
7218 self.array(&poly.inset_nodes);
7219 self.array(&poly.spoke_paths);
7220 self.u(poly.ridge_path.unwrap_or(0));
7221 self.point_array(&poly.node_locs);
7222 self.array(&poly.local_root_vertices);
7223 self.array(&poly.local_root_creases);
7224 self.array(&poly.owned_nodes);
7225 self.array(&poly.owned_paths);
7226 self.array(&poly.owned_polys);
7227 self.array(&poly.owned_creases);
7228 self.array(&poly.owned_facets);
7229 self.owner_node_or_tree(&poly.owner);
7230 }
7231
7232 fn vertex_v5(&mut self, vertex: &Vertex) {
7233 self.s("vrtx");
7234 self.u(vertex.index);
7235 self.point(vertex.loc);
7236 self.f(vertex.elevation);
7237 self.b(vertex.is_border);
7238 self.u(vertex.tree_node.unwrap_or(0));
7239 self.u(vertex.left_pseudohinge_mate.unwrap_or(0));
7240 self.u(vertex.right_pseudohinge_mate.unwrap_or(0));
7241 self.array(&vertex.creases);
7242 self.f(vertex.depth);
7243 self.u(vertex.discrete_depth);
7244 self.i(vertex.cc_flag);
7245 self.i(vertex.st_flag);
7246 self.owner_vertex(&vertex.owner);
7247 }
7248
7249 fn crease_v5(&mut self, crease: &Crease) {
7250 self.s("crse");
7251 self.u(crease.index);
7252 self.i(crease.kind);
7253 self.array(&crease.vertices);
7254 self.u(crease.fwd_facet.unwrap_or(0));
7255 self.u(crease.bkd_facet.unwrap_or(0));
7256 self.i(crease.fold);
7257 self.i(crease.cc_flag);
7258 self.i(crease.st_flag);
7259 self.owner_crease(&crease.owner);
7260 }
7261
7262 fn facet_v5(&mut self, facet: &Facet) {
7263 self.s("fact");
7264 self.u(facet.index);
7265 self.point(facet.centroid);
7266 self.b(facet.is_well_formed);
7267 self.array(&facet.vertices);
7268 self.array(&facet.creases);
7269 self.u(facet.corridor_edge.unwrap_or(0));
7270 self.array(&facet.head_facets);
7271 self.array(&facet.tail_facets);
7272 self.u(facet.order);
7273 self.i(facet.color);
7274 self.owner_facet(&facet.owner);
7275 }
7276
7277 fn condition_v4(&mut self, condition: &Condition) {
7278 self.s(condition.kind.tag());
7279 let lines = condition.kind.stream_lines(self.precision);
7280 self.u(lines.len());
7281 for line in &lines {
7282 self.line(line);
7283 }
7284 }
7285
7286 fn condition_v5(&mut self, condition: &Condition) {
7287 self.s(condition.kind.tag());
7288 self.u(condition.index);
7289 self.b(condition.is_feasible);
7290 let lines = condition.kind.stream_lines(self.precision);
7291 self.u(lines.len());
7292 for line in &lines {
7293 self.line(line);
7294 }
7295 }
7296}
7297
7298struct ScaleObjective;
7299
7300impl nlco::DifferentiableFn for ScaleObjective {
7301 fn func(&self, x: &[f64]) -> f64 {
7302 -x[0]
7303 }
7304
7305 fn grad(&self, _x: &[f64], grad: &mut [f64]) {
7306 grad.fill(0.0);
7307 grad[0] = -1.0;
7308 }
7309}
7310
7311struct StrainObjective {
7312 edge_offset: usize,
7313 stiffness: Vec<TmFloat>,
7314}
7315
7316impl nlco::DifferentiableFn for StrainObjective {
7317 fn func(&self, x: &[f64]) -> f64 {
7318 let mut value = 0.0;
7319 for (i, x_i) in x.iter().enumerate().skip(self.edge_offset) {
7320 value += self.stiffness[i - self.edge_offset] * x_i.powi(2);
7321 }
7322 value
7323 }
7324
7325 fn grad(&self, x: &[f64], grad: &mut [f64]) {
7326 grad.fill(0.0);
7327 for (i, x_i) in x.iter().enumerate().skip(self.edge_offset) {
7328 grad[i] = 2.0 * self.stiffness[i - self.edge_offset] * *x_i;
7329 }
7330 }
7331}
7332
7333impl ConditionKind {
7334 fn from_stream(tag: &str, lines: &[String]) -> Result<Self> {
7335 validate_condition_rest_len(tag, lines.len())?;
7336 let kind = match tag {
7337 "CNxn" => Self::NodeCombo {
7338 node: parse_condition_usize(&lines[0], "condition node")?,
7339 to_symmetry_line: parse_condition_bool(&lines[1]),
7340 to_paper_edge: parse_condition_bool(&lines[2]),
7341 to_paper_corner: parse_condition_bool(&lines[3]),
7342 x_fixed: parse_condition_bool(&lines[4]),
7343 x_fix_value: parse_condition_f64(&lines[5], "condition x fixed value")?,
7344 y_fixed: parse_condition_bool(&lines[6]),
7345 y_fix_value: parse_condition_f64(&lines[7], "condition y fixed value")?,
7346 },
7347 "CNfn" => Self::NodeFixed {
7348 node: parse_condition_usize(&lines[0], "condition node")?,
7349 x_fixed: parse_condition_bool(&lines[1]),
7350 y_fixed: parse_condition_bool(&lines[2]),
7351 x_fix_value: parse_condition_f64(&lines[3], "condition x fixed value")?,
7352 y_fix_value: parse_condition_f64(&lines[4], "condition y fixed value")?,
7353 },
7354 "CNkn" => Self::NodeOnCorner {
7355 node: parse_condition_usize(&lines[0], "condition node")?,
7356 },
7357 "CNen" => Self::NodeOnEdge {
7358 node: parse_condition_usize(&lines[0], "condition node")?,
7359 },
7360 "CNsn" => Self::NodeSymmetric {
7361 node: parse_condition_usize(&lines[0], "condition node")?,
7362 },
7363 "CNpn" => Self::NodesPaired {
7364 node1: parse_condition_usize(&lines[0], "condition node 1")?,
7365 node2: parse_condition_usize(&lines[1], "condition node 2")?,
7366 },
7367 "CNcn" => Self::NodesCollinear {
7368 node1: parse_condition_usize(&lines[0], "condition node 1")?,
7369 node2: parse_condition_usize(&lines[1], "condition node 2")?,
7370 node3: parse_condition_usize(&lines[2], "condition node 3")?,
7371 },
7372 "CNfe" => Self::EdgeLengthFixed {
7373 edge: parse_condition_usize(&lines[0], "condition edge")?,
7374 },
7375 "CNes" => Self::EdgesSameStrain {
7376 edge1: parse_condition_usize(&lines[0], "condition edge 1")?,
7377 edge2: parse_condition_usize(&lines[1], "condition edge 2")?,
7378 },
7379 "CNap" => Self::PathActive {
7380 node1: parse_condition_usize(&lines[0], "condition node 1")?,
7381 node2: parse_condition_usize(&lines[1], "condition node 2")?,
7382 },
7383 "CNfp" => Self::PathAngleFixed {
7384 node1: parse_condition_usize(&lines[0], "condition node 1")?,
7385 node2: parse_condition_usize(&lines[1], "condition node 2")?,
7386 angle: parse_condition_f64(&lines[2], "condition angle")?,
7387 },
7388 "CNqp" => Self::PathAngleQuant {
7389 node1: parse_condition_usize(&lines[0], "condition node 1")?,
7390 node2: parse_condition_usize(&lines[1], "condition node 2")?,
7391 quant: parse_condition_usize(&lines[2], "condition quantization")?,
7392 quant_offset: parse_condition_f64(&lines[3], "condition quantization offset")?,
7393 },
7394 "CNxp" => Self::PathCombo {
7395 node1: parse_condition_usize(&lines[0], "condition node 1")?,
7396 node2: parse_condition_usize(&lines[1], "condition node 2")?,
7397 is_angle_fixed: parse_condition_bool(&lines[2]),
7398 angle: parse_condition_f64(&lines[3], "condition angle")?,
7399 is_angle_quant: parse_condition_bool(&lines[4]),
7400 quant: parse_condition_usize(&lines[5], "condition quantization")?,
7401 quant_offset: parse_condition_f64(&lines[6], "condition quantization offset")?,
7402 },
7403 _ => unreachable!("validate_condition_rest_len accepted an unrecognized tag"),
7404 };
7405 Ok(kind)
7406 }
7407
7408 fn tag(&self) -> &'static str {
7409 match self {
7410 Self::NodeCombo { .. } => "CNxn",
7411 Self::NodeFixed { .. } => "CNfn",
7412 Self::NodeOnCorner { .. } => "CNkn",
7413 Self::NodeOnEdge { .. } => "CNen",
7414 Self::NodeSymmetric { .. } => "CNsn",
7415 Self::NodesPaired { .. } => "CNpn",
7416 Self::NodesCollinear { .. } => "CNcn",
7417 Self::EdgeLengthFixed { .. } => "CNfe",
7418 Self::EdgesSameStrain { .. } => "CNes",
7419 Self::PathCombo { .. } => "CNxp",
7420 Self::PathActive { .. } => "CNap",
7421 Self::PathAngleFixed { .. } => "CNfp",
7422 Self::PathAngleQuant { .. } => "CNqp",
7423 }
7424 }
7425
7426 fn stream_lines(&self, precision: usize) -> Vec<String> {
7427 match self {
7428 Self::NodeCombo {
7429 node,
7430 to_symmetry_line,
7431 to_paper_edge,
7432 to_paper_corner,
7433 x_fixed,
7434 x_fix_value,
7435 y_fixed,
7436 y_fix_value,
7437 } => vec![
7438 node.to_string(),
7439 bool_stream(*to_symmetry_line),
7440 bool_stream(*to_paper_edge),
7441 bool_stream(*to_paper_corner),
7442 bool_stream(*x_fixed),
7443 fmt_float(*x_fix_value, precision),
7444 bool_stream(*y_fixed),
7445 fmt_float(*y_fix_value, precision),
7446 ],
7447 Self::NodeFixed {
7448 node,
7449 x_fixed,
7450 y_fixed,
7451 x_fix_value,
7452 y_fix_value,
7453 } => vec![
7454 node.to_string(),
7455 bool_stream(*x_fixed),
7456 bool_stream(*y_fixed),
7457 fmt_float(*x_fix_value, precision),
7458 fmt_float(*y_fix_value, precision),
7459 ],
7460 Self::NodeOnCorner { node }
7461 | Self::NodeOnEdge { node }
7462 | Self::NodeSymmetric { node } => vec![node.to_string()],
7463 Self::NodesPaired { node1, node2 } | Self::PathActive { node1, node2 } => {
7464 vec![node1.to_string(), node2.to_string()]
7465 }
7466 Self::NodesCollinear {
7467 node1,
7468 node2,
7469 node3,
7470 } => vec![node1.to_string(), node2.to_string(), node3.to_string()],
7471 Self::EdgeLengthFixed { edge } => vec![edge.to_string()],
7472 Self::EdgesSameStrain { edge1, edge2 } => {
7473 vec![edge1.to_string(), edge2.to_string()]
7474 }
7475 Self::PathCombo {
7476 node1,
7477 node2,
7478 is_angle_fixed,
7479 angle,
7480 is_angle_quant,
7481 quant,
7482 quant_offset,
7483 } => vec![
7484 node1.to_string(),
7485 node2.to_string(),
7486 bool_stream(*is_angle_fixed),
7487 fmt_float(*angle, precision),
7488 bool_stream(*is_angle_quant),
7489 quant.to_string(),
7490 fmt_float(*quant_offset, precision),
7491 ],
7492 Self::PathAngleFixed {
7493 node1,
7494 node2,
7495 angle,
7496 } => vec![
7497 node1.to_string(),
7498 node2.to_string(),
7499 fmt_float(*angle, precision),
7500 ],
7501 Self::PathAngleQuant {
7502 node1,
7503 node2,
7504 quant,
7505 quant_offset,
7506 } => vec![
7507 node1.to_string(),
7508 node2.to_string(),
7509 quant.to_string(),
7510 fmt_float(*quant_offset, precision),
7511 ],
7512 }
7513 }
7514
7515 fn validate_refs(&self, tree: &Tree) -> Result<()> {
7516 match self {
7517 Self::NodeCombo { node, .. }
7518 | Self::NodeFixed { node, .. }
7519 | Self::NodeOnCorner { node }
7520 | Self::NodeOnEdge { node }
7521 | Self::NodeSymmetric { node } => tree.check_ref("node", *node, tree.nodes.len()),
7522 Self::NodesPaired { node1, node2 }
7523 | Self::PathActive { node1, node2 }
7524 | Self::PathAngleFixed { node1, node2, .. }
7525 | Self::PathAngleQuant { node1, node2, .. }
7526 | Self::PathCombo { node1, node2, .. } => {
7527 tree.check_ref("node", *node1, tree.nodes.len())?;
7528 tree.check_ref("node", *node2, tree.nodes.len())
7529 }
7530 Self::NodesCollinear {
7531 node1,
7532 node2,
7533 node3,
7534 } => {
7535 tree.check_ref("node", *node1, tree.nodes.len())?;
7536 tree.check_ref("node", *node2, tree.nodes.len())?;
7537 tree.check_ref("node", *node3, tree.nodes.len())
7538 }
7539 Self::EdgeLengthFixed { edge } => tree.check_ref("edge", *edge, tree.edges.len()),
7540 Self::EdgesSameStrain { edge1, edge2 } => {
7541 tree.check_ref("edge", *edge1, tree.edges.len())?;
7542 tree.check_ref("edge", *edge2, tree.edges.len())
7543 }
7544 }
7545 }
7546
7547 fn calc_feasibility(&self, tree: &Tree) -> bool {
7548 match self {
7549 Self::NodeCombo {
7550 node,
7551 to_symmetry_line,
7552 to_paper_edge,
7553 to_paper_corner,
7554 x_fixed,
7555 x_fix_value,
7556 y_fixed,
7557 y_fix_value,
7558 } => {
7559 let Some(loc) = node_loc(tree, *node) else {
7560 return false;
7561 };
7562 if tree.has_symmetry
7563 && *to_symmetry_line
7564 && !is_tiny(stick_to_line(loc, tree.sym_loc, tree.sym_angle))
7565 {
7566 return false;
7567 }
7568 if *to_paper_edge
7569 && !is_tiny(stick_to_edge(loc, tree.paper_width, tree.paper_height))
7570 {
7571 return false;
7572 }
7573 if *to_paper_corner {
7574 if !is_tiny(corner_coord(loc.x, tree.paper_width)) {
7575 return false;
7576 }
7577 if !is_tiny(corner_coord(loc.y, tree.paper_width)) {
7580 return false;
7581 }
7582 }
7583 if *x_fixed && !is_tiny(*x_fix_value - loc.x) {
7584 return false;
7585 }
7586 !*y_fixed || is_tiny(*y_fix_value - loc.y)
7587 }
7588 Self::NodeFixed {
7589 node,
7590 x_fixed,
7591 y_fixed,
7592 x_fix_value,
7593 y_fix_value,
7594 } => {
7595 let Some(loc) = node_loc(tree, *node) else {
7596 return false;
7597 };
7598 if *x_fixed && !is_tiny(*x_fix_value - loc.x) {
7599 return false;
7600 }
7601 !*y_fixed || is_tiny(*y_fix_value - loc.y)
7602 }
7603 Self::NodeOnCorner { node } => node_loc(tree, *node).is_some_and(|loc| {
7604 is_tiny(corner_coord(loc.x, tree.paper_width))
7605 && is_tiny(corner_coord(loc.y, tree.paper_height))
7606 }),
7607 Self::NodeOnEdge { node } => node_loc(tree, *node).is_some_and(|loc| {
7608 is_tiny(stick_to_edge(loc, tree.paper_width, tree.paper_height))
7609 }),
7610 Self::NodeSymmetric { node } => {
7611 tree.has_symmetry
7612 && node_loc(tree, *node).is_some_and(|loc| {
7613 is_tiny(stick_to_line(loc, tree.sym_loc, tree.sym_angle))
7614 })
7615 }
7616 Self::NodesPaired { node1, node2 } => {
7617 if !tree.has_symmetry {
7618 return false;
7619 }
7620 let (Some(a), Some(b)) = (node_loc(tree, *node1), node_loc(tree, *node2)) else {
7621 return false;
7622 };
7623 is_tiny(pair_fn_1a(a, b, tree.sym_angle))
7624 && is_tiny(pair_fn_1b(a, b, tree.sym_loc, tree.sym_angle))
7625 }
7626 Self::NodesCollinear {
7627 node1,
7628 node2,
7629 node3,
7630 } => {
7631 let (Some(a), Some(b), Some(c)) = (
7632 node_loc(tree, *node1),
7633 node_loc(tree, *node2),
7634 node_loc(tree, *node3),
7635 ) else {
7636 return false;
7637 };
7638 is_tiny(collinear_fn_1(a, b, c))
7639 }
7640 Self::EdgeLengthFixed { edge } => {
7641 edge_ref(tree, *edge).is_some_and(|edge| is_tiny(edge.strain))
7642 }
7643 Self::EdgesSameStrain { edge1, edge2 } => {
7644 let (Some(a), Some(b)) = (edge_ref(tree, *edge1), edge_ref(tree, *edge2)) else {
7645 return false;
7646 };
7647 is_tiny(a.strain - b.strain)
7648 }
7649 Self::PathActive { node1, node2 } => tree
7650 .find_leaf_path_between(*node1, *node2)
7651 .is_some_and(|path| path.is_active),
7652 Self::PathAngleFixed {
7653 node1,
7654 node2,
7655 angle,
7656 } => path_active_with_nodes(tree, *node1, *node2)
7657 .is_some_and(|(a, b)| is_tiny(path_angle_fn_1(a, b, *angle))),
7658 Self::PathAngleQuant {
7659 node1,
7660 node2,
7661 quant,
7662 quant_offset,
7663 } => path_active_with_nodes(tree, *node1, *node2)
7664 .is_some_and(|(a, b)| is_tiny(quantize_angle_fn_1(a, b, *quant, *quant_offset))),
7665 Self::PathCombo {
7666 node1,
7667 node2,
7668 is_angle_fixed,
7669 angle,
7670 is_angle_quant,
7671 quant,
7672 quant_offset,
7673 } => {
7674 let Some((a, b)) = path_active_with_nodes(tree, *node1, *node2) else {
7675 return false;
7676 };
7677 if *is_angle_fixed && !is_tiny(path_angle_fn_1(a, b, *angle)) {
7678 return false;
7679 }
7680 !*is_angle_quant || is_tiny(quantize_angle_fn_1(a, b, *quant, *quant_offset))
7681 }
7682 }
7683 }
7684
7685 fn collect_conditioned_parts(
7686 &self,
7687 tree: &Tree,
7688 nodes: &mut [bool],
7689 edges: &mut [bool],
7690 paths: &mut [bool],
7691 ) {
7692 match *self {
7693 Self::NodeCombo { node, .. }
7694 | Self::NodeFixed { node, .. }
7695 | Self::NodeOnCorner { node }
7696 | Self::NodeOnEdge { node }
7697 | Self::NodeSymmetric { node } => mark_1_based(nodes, node),
7698 Self::NodesPaired { node1, node2 } => {
7699 mark_1_based(nodes, node1);
7700 mark_1_based(nodes, node2);
7701 }
7702 Self::PathActive { node1, node2 }
7703 | Self::PathAngleFixed { node1, node2, .. }
7704 | Self::PathAngleQuant { node1, node2, .. }
7705 | Self::PathCombo { node1, node2, .. } => {
7706 mark_1_based(nodes, node1);
7707 mark_1_based(nodes, node2);
7708 if let Some(path) = tree.find_leaf_path_between(node1, node2) {
7709 mark_1_based(paths, path.index);
7710 }
7711 }
7712 Self::NodesCollinear {
7713 node1,
7714 node2,
7715 node3,
7716 } => {
7717 mark_1_based(nodes, node1);
7718 mark_1_based(nodes, node2);
7719 mark_1_based(nodes, node3);
7720 }
7721 Self::EdgeLengthFixed { edge } => mark_1_based(edges, edge),
7722 Self::EdgesSameStrain { edge1, edge2 } => {
7723 mark_1_based(edges, edge1);
7724 mark_1_based(edges, edge2);
7725 }
7726 }
7727 }
7728
7729 fn remap_nodes(&mut self, map: &[Option<usize>]) {
7730 match self {
7731 Self::NodeCombo { node, .. }
7732 | Self::NodeFixed { node, .. }
7733 | Self::NodeOnCorner { node }
7734 | Self::NodeOnEdge { node }
7735 | Self::NodeSymmetric { node } => remap_value(node, map),
7736 Self::NodesPaired { node1, node2 }
7737 | Self::PathActive { node1, node2 }
7738 | Self::PathAngleFixed { node1, node2, .. }
7739 | Self::PathAngleQuant { node1, node2, .. }
7740 | Self::PathCombo { node1, node2, .. } => {
7741 remap_value(node1, map);
7742 remap_value(node2, map);
7743 }
7744 Self::NodesCollinear {
7745 node1,
7746 node2,
7747 node3,
7748 } => {
7749 remap_value(node1, map);
7750 remap_value(node2, map);
7751 remap_value(node3, map);
7752 }
7753 Self::EdgeLengthFixed { .. } | Self::EdgesSameStrain { .. } => {}
7754 }
7755 }
7756}
7757
7758fn push_condition(conditions: &mut Vec<Condition>, kind: ConditionKind) {
7759 conditions.push(Condition {
7760 index: conditions.len() + 1,
7761 is_feasible: true,
7762 kind,
7763 });
7764}
7765
7766fn kill_v4_crease_pattern_refs(nodes: &mut [Node], paths: &mut [Path]) {
7767 for node in nodes {
7768 node.owned_vertices.clear();
7769 if matches!(node.owner, OwnerRef::Poly(_)) {
7770 node.owner = OwnerRef::Tree;
7771 }
7772 }
7773 for path in paths {
7774 path.owned_vertices.clear();
7775 path.owned_creases.clear();
7776 path.fwd_poly = None;
7777 path.bkd_poly = None;
7778 path.outset_path = None;
7779 if matches!(path.owner, OwnerRef::Poly(_)) {
7780 path.owner = OwnerRef::Tree;
7781 }
7782 }
7783}
7784
7785#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7786enum PartKind {
7787 Node,
7788 Path,
7789 Poly,
7790}
7791
7792fn ids_to_flags(ids: &[usize], len: usize) -> Vec<bool> {
7793 let mut flags = vec![false; len + 1];
7794 for id in ids {
7795 if *id > 0 && *id <= len {
7796 flags[*id] = true;
7797 }
7798 }
7799 flags
7800}
7801
7802fn keep_map(len: usize, doomed: &[usize]) -> Vec<Option<usize>> {
7803 let doomed = ids_to_flags(doomed, len);
7804 let mut map = vec![None; len + 1];
7805 let mut next = 1;
7806 for old in 1..=len {
7807 if !doomed[old] {
7808 map[old] = Some(next);
7809 next += 1;
7810 }
7811 }
7812 map
7813}
7814
7815fn remap_value(value: &mut usize, map: &[Option<usize>]) {
7816 if let Some(Some(mapped)) = map.get(*value) {
7817 *value = *mapped;
7818 }
7819}
7820
7821fn remap_option(value: &mut Option<usize>, map: &[Option<usize>]) {
7822 *value = value.and_then(|id| map.get(id).copied().flatten());
7823}
7824
7825fn remap_vec(values: &mut Vec<usize>, map: &[Option<usize>]) {
7826 let mapped = values
7827 .iter()
7828 .filter_map(|id| map.get(*id).copied().flatten())
7829 .collect();
7830 *values = mapped;
7831}
7832
7833fn remap_owner(owner: &mut OwnerRef, kind: PartKind, map: &[Option<usize>]) {
7834 match owner {
7835 OwnerRef::Node(id) if kind == PartKind::Node => {
7836 if let Some(mapped) = map.get(*id).copied().flatten() {
7837 *id = mapped;
7838 } else {
7839 *owner = OwnerRef::Tree;
7840 }
7841 }
7842 OwnerRef::Path(id) if kind == PartKind::Path => {
7843 if let Some(mapped) = map.get(*id).copied().flatten() {
7844 *id = mapped;
7845 } else {
7846 *owner = OwnerRef::Tree;
7847 }
7848 }
7849 OwnerRef::Poly(id) if kind == PartKind::Poly => {
7850 if let Some(mapped) = map.get(*id).copied().flatten() {
7851 *id = mapped;
7852 } else {
7853 *owner = OwnerRef::Tree;
7854 }
7855 }
7856 _ => {}
7857 }
7858}
7859
7860fn mark_1_based(flags: &mut [bool], index: usize) {
7861 if let Some(flag) = index.checked_sub(1).and_then(|pos| flags.get_mut(pos)) {
7862 *flag = true;
7863 }
7864}
7865
7866fn bool_stream(value: bool) -> String {
7867 (if value { "true" } else { "false" }).to_string()
7868}
7869
7870fn parse_condition_usize(value: &str, label: &'static str) -> Result<usize> {
7871 value.parse::<usize>().map_err(|_| TreeError::Parse {
7872 offset: 0,
7873 message: format!("expected unsigned integer for {label}, found {value:?}"),
7874 })
7875}
7876
7877fn parse_condition_f64(value: &str, label: &'static str) -> Result<TmFloat> {
7878 if value.starts_with("NAN") {
7879 return Ok(0.0);
7880 }
7881 value.parse::<TmFloat>().map_err(|_| TreeError::Parse {
7882 offset: 0,
7883 message: format!("expected float for {label}, found {value:?}"),
7884 })
7885}
7886
7887fn parse_condition_bool(value: &str) -> bool {
7888 value == "true"
7889}
7890
7891fn is_tiny(value: TmFloat) -> bool {
7892 value.abs() < DIST_TOL
7893}
7894
7895fn point_sub(a: Point, b: Point) -> Point {
7896 Point {
7897 x: a.x - b.x,
7898 y: a.y - b.y,
7899 }
7900}
7901
7902fn point_add(a: Point, b: Point) -> Point {
7903 Point {
7904 x: a.x + b.x,
7905 y: a.y + b.y,
7906 }
7907}
7908
7909fn point_mul(a: Point, scale: TmFloat) -> Point {
7910 Point {
7911 x: a.x * scale,
7912 y: a.y * scale,
7913 }
7914}
7915
7916fn point_div(a: Point, scale: TmFloat) -> Point {
7917 Point {
7918 x: a.x / scale,
7919 y: a.y / scale,
7920 }
7921}
7922
7923fn rotate_ccw90(p: Point) -> Point {
7924 Point { x: -p.y, y: p.x }
7925}
7926
7927fn inner(a: Point, b: Point) -> TmFloat {
7928 a.x * b.x + a.y * b.y
7929}
7930
7931fn mag2(p: Point) -> TmFloat {
7932 p.x.powi(2) + p.y.powi(2)
7933}
7934
7935fn mag(p: Point) -> TmFloat {
7936 mag2(p).sqrt()
7937}
7938
7939fn normalize(p: Point) -> Point {
7940 point_div(p, mag(p))
7941}
7942
7943fn orientation_2d(p1: Point, p2: Point, p3: Point) -> TmFloat {
7944 let p13 = point_sub(p1, p3);
7945 let p23 = point_sub(p2, p3);
7946 p13.x * p23.y - p13.y * p23.x
7947}
7948
7949fn are_cw(p1: Point, p2: Point, p3: Point) -> bool {
7950 orientation_2d(p1, p2, p3) < 0.0
7951}
7952
7953fn are_ccw(p1: Point, p2: Point, p3: Point) -> bool {
7954 orientation_2d(p1, p2, p3) > 0.0
7955}
7956
7957fn are_parallel(p: Point, q: Point) -> bool {
7958 inner(p, rotate_ccw90(q)) == 0.0
7959}
7960
7961fn line_intersection_params(
7962 p: Point,
7963 rp: Point,
7964 q: Point,
7965 rq: Point,
7966) -> Option<(TmFloat, TmFloat)> {
7967 let eps = TmFloat::EPSILON.sqrt();
7968 let rrpq = inner(rotate_ccw90(rp), rq);
7969 if rrpq.abs() < eps {
7970 return None;
7971 }
7972 let rqp = rotate_ccw90(point_sub(q, p));
7973 Some((inner(rqp, rq) / rrpq, inner(rqp, rp) / rrpq))
7974}
7975
7976fn line_intersection_point_exact(p: Point, rp: Point, q: Point, rq: Point) -> Option<Point> {
7977 let rrpq = inner(rotate_ccw90(rp), rq);
7978 if rrpq == 0.0 {
7979 return None;
7980 }
7981 let tp = inner(rotate_ccw90(point_sub(q, p)), rq) / rrpq;
7982 Some(point_add(p, point_mul(rp, tp)))
7983}
7984
7985fn incenter(p1: Point, p2: Point, p3: Point) -> Point {
7986 let l12 = p1.distance(p2);
7987 let l23 = p2.distance(p3);
7988 let l31 = p3.distance(p1);
7989 point_div(
7990 point_add(
7991 point_add(point_mul(p3, l12), point_mul(p1, l23)),
7992 point_mul(p2, l31),
7993 ),
7994 l12 + l23 + l31,
7995 )
7996}
7997
7998fn inradius(p1: Point, p2: Point, p3: Point) -> TmFloat {
7999 let a = p1.distance(p2);
8000 let b = p2.distance(p3);
8001 let c = p3.distance(p1);
8002 0.5 * (((b + c - a) * (c + a - b) * (a + b - c)) / (a + b + c)).sqrt()
8003}
8004
8005fn vertices_same_loc(p1: Point, p2: Point) -> bool {
8006 p1.distance(p2) < VERTEX_TOL
8007}
8008
8009fn project_p_to_q(p1: Point, p2: Point, p: Point, q1: Point, q2: Point) -> Option<Point> {
8010 let rq = point_sub(q2, q1);
8011 let dq = mag(rq);
8012 let up = normalize(point_sub(p2, p1));
8013 let denom = inner(up, rq);
8014 if denom == 0.0 {
8015 return None;
8016 }
8017 let d = dq * inner(up, point_sub(p, q1)) / denom;
8018 let q = point_add(q1, point_mul(normalize(rq), d));
8019 (d > -0.9 * DIST_TOL && d < dq + 0.9 * DIST_TOL).then_some(q)
8020}
8021
8022fn project_q_to_p(q: Point, p1: Point, p2: Point) -> Option<Point> {
8023 let rp = point_sub(p2, p1);
8024 let up = normalize(rp);
8025 let d = inner(up, point_sub(q, p1));
8026 let dp = inner(up, rp);
8027 let p = point_add(p1, point_mul(up, d));
8028 (d > -0.9 * DIST_TOL && d < dp + 0.9 * DIST_TOL).then_some(p)
8029}
8030
8031fn sortable_ridge_vertex_value(vertex: Point, front: Point, back: Point) -> TmFloat {
8032 let pu = normalize(point_sub(back, front));
8033 let pv = rotate_ccw90(pu);
8034 let dm = point_mul(point_add(back, front), 0.5);
8035 let dp = point_sub(vertex, dm);
8036 let du = inner(dp, pu);
8037 let dv = inner(dp, pv);
8038 du.atan2(dv)
8039}
8040
8041fn push_unique(values: &mut Vec<usize>, value: usize) {
8042 if !values.contains(&value) {
8043 values.push(value);
8044 }
8045}
8046
8047fn angle(p: Point) -> TmFloat {
8048 p.y.atan2(p.x)
8049}
8050
8051fn angle_change(p1: Point, p2: Point, p3: Point) -> TmFloat {
8052 let a = angle(point_sub(p3, p2)) - angle(point_sub(p2, p1));
8053 if a < -PI {
8054 a + TWO_PI
8055 } else if a >= PI {
8056 a - TWO_PI
8057 } else {
8058 a
8059 }
8060}
8061
8062fn node_loc(tree: &Tree, index: usize) -> Option<Point> {
8063 tree.nodes.get(index.checked_sub(1)?).map(|node| node.loc)
8064}
8065
8066fn edge_ref(tree: &Tree, index: usize) -> Option<&Edge> {
8067 tree.edges.get(index.checked_sub(1)?)
8068}
8069
8070fn path_active_with_nodes(tree: &Tree, node1: usize, node2: usize) -> Option<(Point, Point)> {
8071 tree.find_leaf_path_between(node1, node2)
8072 .filter(|path| path.is_active)?;
8073 Some((node_loc(tree, node1)?, node_loc(tree, node2)?))
8074}
8075
8076fn corner_coord(value: TmFloat, width_or_height: TmFloat) -> TmFloat {
8077 value * (value - width_or_height)
8078}
8079
8080fn stick_to_edge(loc: Point, width: TmFloat, height: TmFloat) -> TmFloat {
8081 10.0 * loc.x * (loc.x - width) * loc.y * (loc.y - height)
8082}
8083
8084fn stick_to_line(loc: Point, point: Point, angle: TmFloat) -> TmFloat {
8085 let radians = angle * DEGREES;
8086 (-loc.x + point.x) * radians.sin() + (loc.y - point.y) * radians.cos()
8087}
8088
8089fn pair_fn_1a(a: Point, b: Point, angle: TmFloat) -> TmFloat {
8090 let radians = angle * DEGREES;
8091 (a.x - b.x) * radians.cos() + (a.y - b.y) * radians.sin()
8092}
8093
8094fn pair_fn_1b(a: Point, b: Point, point: Point, angle: TmFloat) -> TmFloat {
8095 let radians = angle * DEGREES;
8096 (-a.x - b.x + 2.0 * point.x) * radians.sin() + (a.y + b.y - 2.0 * point.y) * radians.cos()
8097}
8098
8099fn collinear_fn_1(a: Point, b: Point, c: Point) -> TmFloat {
8100 (b.y - a.y) * (c.x - b.x) - (c.y - b.y) * (b.x - a.x)
8101}
8102
8103fn path_angle_fn_1(a: Point, b: Point, angle: TmFloat) -> TmFloat {
8104 let radians = angle * DEGREES;
8105 (a.x - b.x) * radians.sin() + (b.y - a.y) * radians.cos()
8106}
8107
8108fn quantize_angle_fn_1(a: Point, b: Point, quant: usize, quant_offset: TmFloat) -> TmFloat {
8109 if quant == 0 {
8110 return TmFloat::NAN;
8111 }
8112 let dx = a.x - b.x;
8113 let dy = a.y - b.y;
8114 let r2 = dx * dx + dy * dy;
8115 let f1 = r2.powf(-0.5 * quant as TmFloat);
8116 let offset = quant_offset * DEGREES;
8117 let step = 180.0 * DEGREES / quant as TmFloat;
8118 let mut f2 = 1.0;
8119 for k in 0..quant {
8120 let angle = k as TmFloat * step - offset;
8121 f2 *= dx * angle.sin() - dy * angle.cos();
8122 }
8123 2.0_f64.powi(quant as i32 - 1) * f1 * f2
8124}
8125
8126fn fmt_float(value: TmFloat, precision: usize) -> String {
8127 format!("{value:.precision$}")
8128}
8129
8130fn validate_condition_rest_len(tag: &str, got: usize) -> Result<()> {
8131 let expected = match tag {
8132 "CNxn" => 8,
8133 "CNfn" => 5,
8134 "CNkn" | "CNen" | "CNsn" | "CNfe" => 1,
8135 "CNpn" | "CNes" | "CNap" => 2,
8136 "CNcn" | "CNfp" => 3,
8137 "CNqp" => 4,
8138 "CNxp" => 7,
8139 _ => {
8140 return Err(TreeError::UnsupportedOperation(
8141 "unrecognized condition tags are not preserved until condition parsing is ported",
8142 ));
8143 }
8144 };
8145 if got != expected {
8146 return Err(TreeError::Parse {
8147 offset: 0,
8148 message: format!("condition {tag} expected {expected} rest lines, found {got}"),
8149 });
8150 }
8151 Ok(())
8152}
8153
8154fn opposite_facet_color(color: i32) -> i32 {
8155 if color == FACET_WHITE_UP {
8156 FACET_COLOR_UP
8157 } else {
8158 FACET_WHITE_UP
8159 }
8160}
8161
8162#[cfg(test)]
8163mod tests {
8164 use super::*;
8165
8166 const FIXTURE_1: &str = include_str!("../testdata/tmModelTester_1.tmd5");
8167 const FIXTURE_2: &str = include_str!("../testdata/tmModelTester_2.tmd5");
8168 const FIXTURE_4: &str = include_str!("../testdata/tmModelTester_4.tmd5");
8169 const FIXTURE_5: &str = include_str!("../testdata/tmModelTester_5.tmd5");
8170 const FIXTURE_V3: &str = include_str!("../testdata/minimal_v3.tmd");
8171 const FIXTURE_CP_V4: &str = include_str!("../testdata/minimal_cp_v4.tmd4");
8172 const FIXTURE_CP_V5: &str = include_str!("../testdata/minimal_cp_v5.tmd5");
8173
8174 #[test]
8175 fn parses_v4_fixture_summary() {
8176 let tree = Tree::from_tmd_str(FIXTURE_1).unwrap();
8177 let summary = tree.summary();
8178 assert_eq!(summary.source_version, "4.0");
8179 assert_eq!(summary.nodes, 4);
8180 assert_eq!(summary.edges, 3);
8181 assert_eq!(summary.paths, 6);
8182 assert_eq!(summary.leaf_nodes, 3);
8183 assert_eq!(summary.leaf_paths, 3);
8184 assert!(summary.is_feasible);
8185 }
8186
8187 #[test]
8188 fn parses_larger_fixtures() {
8189 let tree2 = Tree::from_tmd_str(FIXTURE_2).unwrap();
8190 let tree5 = Tree::from_tmd_str(FIXTURE_5).unwrap();
8191 assert!(tree2.summary().nodes > 10);
8192 assert!(tree5.summary().conditions > 0);
8193 }
8194
8195 #[test]
8196 fn parses_v3_and_translates_legacy_conditions() {
8197 let tree = Tree::from_tmd_str(FIXTURE_V3).unwrap();
8198 let summary = tree.summary();
8199 assert_eq!(summary.source_version, "3.0");
8200 assert_eq!(summary.nodes, 2);
8201 assert_eq!(summary.edges, 1);
8202 assert_eq!(summary.paths, 1);
8203 assert_eq!(summary.conditions, 3);
8204 assert_eq!(summary.conditioned_nodes, 2);
8205 assert_eq!(summary.conditioned_paths, 1);
8206 assert_eq!(summary.conditions_by_tag.get("CNfn"), Some(&2));
8207 assert_eq!(summary.conditions_by_tag.get("CNap"), Some(&1));
8208 assert!(summary.is_feasible);
8209 }
8210
8211 #[test]
8212 fn v3_fixed_angle_preserves_501_import_behavior() {
8213 let text = FIXTURE_V3.replacen("false\n0.0000000000\ntrue", "true\n45.0000000000\ntrue", 1);
8214 let tree = Tree::from_tmd_str(&text).unwrap();
8215 let angle = tree.conditions.iter().find_map(|condition| {
8216 if let ConditionKind::PathAngleFixed { angle, .. } = condition.kind {
8217 Some(angle)
8218 } else {
8219 None
8220 }
8221 });
8222 assert_eq!(angle, Some(1.0));
8223 }
8224
8225 #[test]
8226 fn parses_and_round_trips_v5_crease_pattern_payload() {
8227 let tree = Tree::from_tmd_str(FIXTURE_CP_V5).unwrap();
8228 let summary = tree.summary();
8229 assert_eq!(summary.source_version, "5.0");
8230 assert_eq!(summary.polys, 1);
8231 assert_eq!(summary.vertices, 2);
8232 assert_eq!(summary.creases, 1);
8233 assert_eq!(summary.facets, 1);
8234 assert_eq!(tree.polys[0].ring_nodes, vec![1, 2]);
8235 assert_eq!(tree.vertices[0].owner, OwnerRef::Node(1));
8236 assert_eq!(tree.creases[0].owner, OwnerRef::Path(1));
8237 assert_eq!(tree.facets[0].owner, OwnerRef::Poly(1));
8238
8239 let reparsed = Tree::from_tmd_str(&tree.to_tmd5_string()).unwrap();
8240 assert_eq!(reparsed.summary().polys, 1);
8241 assert_eq!(reparsed.summary().vertices, 2);
8242 assert_eq!(reparsed.summary().creases, 1);
8243 assert_eq!(reparsed.summary().facets, 1);
8244 }
8245
8246 #[test]
8247 fn cleanup_after_optimizer_removes_stale_crease_pattern_payload() {
8248 let mut tree = Tree::from_tmd_str(FIXTURE_CP_V5).unwrap();
8249 assert_eq!(tree.summary().polys, 1);
8250
8251 tree.optimize_scale().unwrap();
8252 let summary = tree.summary();
8253 assert_eq!(summary.polys, 0);
8254 assert_eq!(summary.vertices, 0);
8255 assert_eq!(summary.creases, 0);
8256 assert_eq!(summary.facets, 0);
8257 assert!(!tree.is_polygon_valid);
8258 assert!(!tree.is_polygon_filled);
8259 assert!(!tree.is_vertex_depth_valid);
8260 assert!(!tree.is_facet_data_valid);
8261 }
8262
8263 #[test]
8264 fn consumes_and_discards_v4_crease_pattern_payload() {
8265 let tree = Tree::from_tmd_str(FIXTURE_CP_V4).unwrap();
8266 let summary = tree.summary();
8267 assert_eq!(summary.source_version, "4.0");
8268 assert_eq!(summary.nodes, 2);
8269 assert_eq!(summary.edges, 1);
8270 assert_eq!(summary.paths, 1);
8271 assert_eq!(summary.polys, 0);
8272 assert_eq!(summary.vertices, 0);
8273 assert_eq!(summary.creases, 0);
8274 assert_eq!(tree.nodes[0].owned_vertices.len(), 0);
8275 assert_eq!(tree.paths[0].fwd_poly, None);
8276 assert_eq!(tree.paths[0].owned_vertices.len(), 0);
8277 }
8278
8279 #[test]
8280 fn round_trips_through_v5_writer() {
8281 let tree = Tree::from_tmd_str(FIXTURE_1).unwrap();
8282 let serialized = tree.to_tmd5_string();
8283 let reparsed = Tree::from_tmd_str(&serialized).unwrap();
8284 assert_eq!(tree.summary().nodes, reparsed.summary().nodes);
8285 assert_eq!(tree.summary().edges, reparsed.summary().edges);
8286 assert_eq!(tree.summary().paths, reparsed.summary().paths);
8287 assert_eq!(tree.summary().leaf_paths, reparsed.summary().leaf_paths);
8288 }
8289
8290 #[test]
8291 fn scale_optimizer_uses_alm_port() {
8292 let mut tree = Tree::from_tmd_str(FIXTURE_1).unwrap();
8293 let report = tree.optimize_scale().unwrap();
8294 assert!(report.converged);
8295 assert!(tree.is_feasible());
8296 assert!((report.new_scale - 0.517637).abs() < 1.0e-4);
8297 }
8298
8299 #[test]
8300 fn edge_optimizer_uses_alm_port() {
8301 let mut tree = Tree::from_tmd_str(FIXTURE_4).unwrap();
8302 let report = tree.optimize_edges().unwrap();
8303 assert!(report.converged);
8304 assert!(tree.is_feasible());
8305 let max_strain = tree
8306 .edges
8307 .iter()
8308 .map(|edge| edge.strain)
8309 .fold(TmFloat::NEG_INFINITY, TmFloat::max);
8310 assert!((max_strain - 0.573142).abs() < 1.0e-4);
8311 }
8312
8313 #[test]
8314 fn strain_optimizer_uses_alm_port() {
8315 let mut tree = Tree::from_tmd_str(FIXTURE_5).unwrap();
8316 let report = tree.optimize_strain().unwrap();
8317 assert!(report.converged);
8318 assert!(tree.is_feasible());
8319 let weighted = tree
8320 .edges
8321 .iter()
8322 .map(|edge| edge.stiffness * edge.strain.powi(2))
8323 .sum::<TmFloat>()
8324 / tree.edges.len() as TmFloat;
8325 assert!((100.0 * weighted.sqrt() - 3.580266).abs() < 1.0e-4);
8326 }
8327
8328 #[test]
8329 fn builds_polys_and_crease_pattern_payload() {
8330 let mut tree = Tree::from_tmd_str(FIXTURE_1).unwrap();
8331 tree.build_polys_and_crease_pattern().unwrap();
8332 assert!(!tree.vertices.is_empty());
8333 assert!(!tree.creases.is_empty());
8334 assert!(!tree.facets.is_empty());
8335 }
8336
8337 #[test]
8338 fn cp_status_report_identifies_bad_parts() {
8339 let mut tree = Tree::from_tmd_str(FIXTURE_1).unwrap();
8340 tree.build_polys_and_crease_pattern().unwrap();
8341 let report = tree.cp_status_report();
8342 assert_eq!(report.status, CPStatus::PolysMultipleIbps);
8343 assert_eq!(report.bad_polys, vec![1]);
8344 assert!(report.bad_edges.is_empty());
8345 assert!(report.bad_vertices.is_empty());
8346 assert!(report.bad_creases.is_empty());
8347 assert!(report.bad_facets.is_empty());
8348 }
8349}