1use crate::{Color, Plane, Point, Tolerance, Vector, Xform};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(tag = "type", rename = "Polyline")]
10pub struct Polyline {
11 pub guid: String,
12 pub name: String,
13 pub points: Vec<Point>,
14 pub plane: Plane,
15 pub width: f32,
16 pub linecolor: Color,
17 #[serde(default = "Xform::identity")]
18 pub xform: Xform,
19}
20
21impl Default for Polyline {
22 fn default() -> Self {
23 Self {
24 guid: Uuid::new_v4().to_string(),
25 name: "my_polyline".to_string(),
26 points: Vec::new(),
27 plane: Plane::default(),
28 width: 1.0,
29 linecolor: Color::white(),
30 xform: Xform::identity(),
31 }
32 }
33}
34
35impl Polyline {
36 pub fn new(points: Vec<Point>) -> Self {
42 let plane = if points.len() >= 3 {
44 Plane::from_points(points.clone())
45 } else {
46 Plane::default()
47 };
48
49 Self {
50 guid: Uuid::new_v4().to_string(),
51 name: "my_polyline".to_string(),
52 points,
53 plane,
54 width: 1.0,
55 linecolor: Color::white(),
56 xform: Xform::identity(),
57 }
58 }
59
60 pub fn len(&self) -> usize {
62 self.points.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.points.is_empty()
68 }
69
70 pub fn segment_count(&self) -> usize {
73 if self.points.len() > 1 {
74 self.points.len() - 1
75 } else {
76 0
77 }
78 }
79
80 pub fn length(&self) -> f32 {
82 let mut total_length = 0.0;
83 for i in 0..self.segment_count() {
84 let mut segment_vector = self.points[i + 1].clone() - self.points[i].clone();
85 total_length += segment_vector.magnitude();
86 }
87 total_length
88 }
89
90 pub fn get_point(&self, index: usize) -> Option<&Point> {
92 self.points.get(index)
93 }
94
95 pub fn get_point_mut(&mut self, index: usize) -> Option<&mut Point> {
97 self.points.get_mut(index)
98 }
99
100 pub fn add_point(&mut self, point: Point) {
102 self.points.push(point);
103 if self.points.len() == 3 {
105 self.plane = Plane::from_points(self.points.clone());
106 }
107 }
108
109 pub fn insert_point(&mut self, index: usize, point: Point) {
111 self.points.insert(index, point);
112 if self.points.len() == 3 {
114 self.plane = Plane::from_points(self.points.clone());
115 }
116 }
117
118 pub fn remove_point(&mut self, index: usize) -> Option<Point> {
120 if index < self.points.len() {
121 let point = self.points.remove(index);
122 if self.points.len() == 3 {
124 self.plane = Plane::from_points(self.points.clone());
125 }
126 Some(point)
127 } else {
128 None
129 }
130 }
131
132 pub fn reverse(&mut self) {
134 self.points.reverse();
135 self.plane.reverse();
136 }
137
138 pub fn reversed(&self) -> Self {
140 let mut reversed = self.clone();
141 reversed.reverse();
142 reversed
143 }
144
145 pub fn jsondump(&self) -> Result<String, Box<dyn std::error::Error>> {
147 let mut buf = Vec::new();
148 let formatter = serde_json::ser::PrettyFormatter::with_indent(b" ");
149 let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
150 self.serialize(&mut ser)?;
151 Ok(String::from_utf8(buf)?)
152 }
153
154 pub fn jsonload(json_data: &str) -> Result<Self, Box<dyn std::error::Error>> {
156 Ok(serde_json::from_str(json_data)?)
157 }
158
159 pub fn to_json(&self, filepath: &str) -> Result<(), Box<dyn std::error::Error>> {
161 let json = self.jsondump()?;
162 std::fs::write(filepath, json)?;
163 Ok(())
164 }
165
166 pub fn from_json(filepath: &str) -> Result<Self, Box<dyn std::error::Error>> {
168 let json = std::fs::read_to_string(filepath)?;
169 Self::jsonload(&json)
170 }
171
172 pub fn shift(&mut self, times: i32) {
178 if self.points.is_empty() {
179 return;
180 }
181 let len = self.points.len();
182 let shift_amount = ((times % len as i32) + len as i32) % len as i32;
183 self.points.rotate_left(shift_amount as usize);
184 }
185
186 pub fn length_squared(&self) -> f64 {
188 let mut length = 0.0f64;
189 for i in 0..self.segment_count() {
190 let segment = self.points[i + 1].clone() - self.points[i].clone();
191 length += segment.length_squared() as f64;
192 }
193 length
194 }
195
196 pub fn point_at_parameter(start: &Point, end: &Point, t: f64) -> Point {
198 let s = 1.0 - t;
199 let t_f32 = t as f32;
200 let s_f32 = s as f32;
201 Point::new(
202 if start.x() == end.x() {
203 start.x()
204 } else {
205 s_f32 * start.x() + t_f32 * end.x()
206 },
207 if start.y() == end.y() {
208 start.y()
209 } else {
210 s_f32 * start.y() + t_f32 * end.y()
211 },
212 if start.z() == end.z() {
213 start.z()
214 } else {
215 s_f32 * start.z() + t_f32 * end.z()
216 },
217 )
218 }
219
220 pub fn closest_point_to_line(point: &Point, line_start: &Point, line_end: &Point) -> f64 {
222 let d = line_end.clone() - line_start.clone();
223 let dod = d.length_squared();
224
225 if dod > 0.0 {
226 let t = if (point.clone() - line_start.clone()).length_squared()
227 <= (point.clone() - line_end.clone()).length_squared()
228 {
229 (point.clone() - line_start.clone()).dot(&d) / dod
230 } else {
231 1.0 + (point.clone() - line_end.clone()).dot(&d) / dod
232 };
233 t as f64
234 } else {
235 0.0
236 }
237 }
238
239 pub fn line_line_overlap(
241 line0_start: &Point,
242 line0_end: &Point,
243 line1_start: &Point,
244 line1_end: &Point,
245 ) -> Option<(Point, Point)> {
246 let mut t = [0.0, 1.0, 0.0, 0.0];
247 t[2] = Self::closest_point_to_line(line1_start, line0_start, line0_end);
248 t[3] = Self::closest_point_to_line(line1_end, line0_start, line0_end);
249
250 let do_overlap = !((t[2] < 0.0 && t[3] < 0.0) || (t[2] > 1.0 && t[3] > 1.0));
251 t.sort_by(|a, b| a.partial_cmp(b).unwrap());
252
253 let overlap_valid = (t[2] - t[1]).abs() > Tolerance::ZERO_TOLERANCE;
254
255 if do_overlap && overlap_valid {
256 Some((
257 Self::point_at_parameter(line0_start, line0_end, t[1]),
258 Self::point_at_parameter(line0_start, line0_end, t[2]),
259 ))
260 } else {
261 None
262 }
263 }
264
265 pub fn line_line_average(
267 line0_start: &Point,
268 line0_end: &Point,
269 line1_start: &Point,
270 line1_end: &Point,
271 ) -> (Point, Point) {
272 let output_start = Point::new(
273 (line0_start.x() + line1_start.x()) * 0.5,
274 (line0_start.y() + line1_start.y()) * 0.5,
275 (line0_start.z() + line1_start.z()) * 0.5,
276 );
277 let output_end = Point::new(
278 (line0_end.x() + line1_end.x()) * 0.5,
279 (line0_end.y() + line1_end.y()) * 0.5,
280 (line0_end.z() + line1_end.z()) * 0.5,
281 );
282 (output_start, output_end)
283 }
284
285 pub fn line_line_overlap_average(
287 line0_start: &Point,
288 line0_end: &Point,
289 line1_start: &Point,
290 line1_end: &Point,
291 ) -> (Point, Point) {
292 let line_a = Self::line_line_overlap(line0_start, line0_end, line1_start, line1_end);
293 let line_b = Self::line_line_overlap(line1_start, line1_end, line0_start, line0_end);
294
295 if let (Some((line_a_start, line_a_end)), Some((line_b_start, line_b_end))) =
296 (line_a, line_b)
297 {
298 let mid_line0_start = Point::new(
299 (line_a_start.x() + line_b_start.x()) * 0.5,
300 (line_a_start.y() + line_b_start.y()) * 0.5,
301 (line_a_start.z() + line_b_start.z()) * 0.5,
302 );
303 let mid_line0_end = Point::new(
304 (line_a_end.x() + line_b_end.x()) * 0.5,
305 (line_a_end.y() + line_b_end.y()) * 0.5,
306 (line_a_end.z() + line_b_end.z()) * 0.5,
307 );
308 let mid_line1_start = Point::new(
309 (line_a_start.x() + line_b_end.x()) * 0.5,
310 (line_a_start.y() + line_b_end.y()) * 0.5,
311 (line_a_start.z() + line_b_end.z()) * 0.5,
312 );
313 let mid_line1_end = Point::new(
314 (line_a_end.x() + line_b_start.x()) * 0.5,
315 (line_a_end.y() + line_b_start.y()) * 0.5,
316 (line_a_end.z() + line_b_start.z()) * 0.5,
317 );
318
319 let mid0_vec = mid_line0_end.clone() - mid_line0_start.clone();
320 let mid1_vec = mid_line1_end.clone() - mid_line1_start.clone();
321
322 if mid0_vec.length_squared() > mid1_vec.length_squared() {
323 (mid_line0_start, mid_line0_end)
324 } else {
325 (mid_line1_start, mid_line1_end)
326 }
327 } else {
328 Self::line_line_average(line0_start, line0_end, line1_start, line1_end)
329 }
330 }
331
332 pub fn line_from_projected_points(
334 line_start: &Point,
335 line_end: &Point,
336 points: &[Point],
337 ) -> Option<(Point, Point)> {
338 if points.is_empty() {
339 return None;
340 }
341
342 let mut t_values: Vec<f64> = points
343 .iter()
344 .map(|p| Self::closest_point_to_line(p, line_start, line_end))
345 .collect();
346
347 t_values.sort_by(|a, b| a.partial_cmp(b).unwrap());
348
349 let output_start = Self::point_at_parameter(line_start, line_end, t_values[0]);
350 let output_end =
351 Self::point_at_parameter(line_start, line_end, t_values[t_values.len() - 1]);
352
353 if (t_values[0] - t_values[t_values.len() - 1]).abs() > Tolerance::ZERO_TOLERANCE {
354 Some((output_start, output_end))
355 } else {
356 None
357 }
358 }
359
360 pub fn closest_distance_and_point(&self, point: &Point) -> (f64, usize, Point) {
362 let mut edge_id = 0;
363 let mut closest_distance = f64::MAX;
364 let mut best_t = 0.0;
365
366 for i in 0..self.segment_count() {
367 let t = Self::closest_point_to_line(point, &self.points[i], &self.points[i + 1]);
368 let point_on_segment =
369 Self::point_at_parameter(&self.points[i], &self.points[i + 1], t);
370 let distance = point.distance(&point_on_segment) as f64;
371
372 if distance < closest_distance {
373 closest_distance = distance;
374 edge_id = i;
375 best_t = t;
376 }
377
378 if closest_distance < Tolerance::ZERO_TOLERANCE {
379 break;
380 }
381 }
382
383 let closest_point =
384 Self::point_at_parameter(&self.points[edge_id], &self.points[edge_id + 1], best_t);
385 (closest_distance, edge_id, closest_point)
386 }
387
388 pub fn is_closed(&self) -> bool {
390 if self.points.len() < 2 {
391 return false;
392 }
393 self.points
394 .first()
395 .unwrap()
396 .distance(self.points.last().unwrap())
397 < Tolerance::ZERO_TOLERANCE as f32
398 }
399
400 pub fn center(&self) -> Point {
402 if self.points.is_empty() {
403 return Point::new(0.0, 0.0, 0.0);
404 }
405
406 let n = if self.is_closed() && self.points.len() > 1 {
407 self.points.len() - 1
408 } else {
409 self.points.len()
410 };
411
412 let mut sum_x = 0.0;
413 let mut sum_y = 0.0;
414 let mut sum_z = 0.0;
415
416 for i in 0..n {
417 sum_x += self.points[i].x();
418 sum_y += self.points[i].y();
419 sum_z += self.points[i].z();
420 }
421
422 Point::new(sum_x / n as f32, sum_y / n as f32, sum_z / n as f32)
423 }
424
425 pub fn center_vec(&self) -> Vector {
427 let center = self.center();
428 Vector::new(center.x(), center.y(), center.z())
429 }
430
431 pub fn get_average_plane(&self) -> (Point, Vector, Vector, Vector) {
433 let origin = self.center();
434
435 let x_axis = if self.points.len() >= 2 {
436 let mut x = self.points[1].clone() - self.points[0].clone();
437 x.normalize_self();
438 x
439 } else {
440 Vector::new(1.0, 0.0, 0.0)
441 };
442
443 let z_axis = self.average_normal();
444 let mut y_axis = z_axis.cross(&x_axis);
445 y_axis.normalize_self();
446
447 (origin, x_axis, y_axis, z_axis)
448 }
449
450 pub fn get_fast_plane(&self) -> (Point, Plane) {
452 let origin = if !self.points.is_empty() {
453 self.points[0].clone()
454 } else {
455 Point::new(0.0, 0.0, 0.0)
456 };
457
458 let average_normal = self.average_normal();
459 let plane = Plane::from_point_normal(origin.clone(), average_normal);
460 (origin, plane)
461 }
462
463 pub fn get_middle_line(
465 line0_start: &Point,
466 line0_end: &Point,
467 line1_start: &Point,
468 line1_end: &Point,
469 ) -> (Point, Point) {
470 let p0 = Point::new(
471 (line0_start.x() + line1_start.x()) * 0.5,
472 (line0_start.y() + line1_start.y()) * 0.5,
473 (line0_start.z() + line1_start.z()) * 0.5,
474 );
475 let p1 = Point::new(
476 (line0_end.x() + line1_end.x()) * 0.5,
477 (line0_end.y() + line1_end.y()) * 0.5,
478 (line0_end.z() + line1_end.z()) * 0.5,
479 );
480 (p0, p1)
481 }
482
483 pub fn extend_line(
485 line_start: &mut Point,
486 line_end: &mut Point,
487 distance0: f64,
488 distance1: f64,
489 ) {
490 let mut v = line_end.clone() - line_start.clone();
491 v.normalize_self();
492
493 *line_start = line_start.clone() - (v.clone() * distance0 as f32);
494 *line_end = line_end.clone() + (v * distance1 as f32);
495 }
496
497 pub fn scale_line(line_start: &mut Point, line_end: &mut Point, distance: f64) {
499 let v = line_end.clone() - line_start.clone();
500 *line_start = line_start.clone() + (v.clone() * distance as f32);
501 *line_end = line_end.clone() - (v * distance as f32);
502 }
503
504 pub fn extend_segment(
506 &mut self,
507 segment_id: usize,
508 dist0: f64,
509 dist1: f64,
510 proportion0: f64,
511 proportion1: f64,
512 ) {
513 if segment_id >= self.segment_count() {
514 return;
515 }
516
517 let mut p0 = self.points[segment_id].clone();
518 let mut p1 = self.points[segment_id + 1].clone();
519 let v = p1.clone() - p0.clone();
520
521 if proportion0 != 0.0 || proportion1 != 0.0 {
522 p0 -= v.clone() * proportion0 as f32;
523 p1 += v * proportion1 as f32;
524 } else {
525 let v_norm = v.normalize();
526 p0 -= v_norm.clone() * dist0 as f32;
527 p1 += v_norm * dist1 as f32;
528 }
529
530 self.points[segment_id] = p0;
531 self.points[segment_id + 1] = p1;
532
533 if self.is_closed() {
534 let len = self.points.len();
535 if segment_id == 0 {
536 let first = self.points[0].clone();
537 self.points[len - 1] = first;
538 } else if segment_id + 1 == len - 1 {
539 let last = self.points[len - 1].clone();
540 self.points[0] = last;
541 }
542 }
543 }
544
545 pub fn extend_segment_equally_static(
547 segment_start: &mut Point,
548 segment_end: &mut Point,
549 dist: f64,
550 proportion: f64,
551 ) {
552 if dist == 0.0 && proportion == 0.0 {
553 return;
554 }
555
556 let v = segment_end.clone() - segment_start.clone();
557
558 if proportion != 0.0 {
559 *segment_start = segment_start.clone() - (v.clone() * proportion as f32);
560 *segment_end = segment_end.clone() + (v * proportion as f32);
561 } else {
562 let mut v_norm = v;
563 v_norm.normalize_self();
564 *segment_start = segment_start.clone() - (v_norm.clone() * dist as f32);
565 *segment_end = segment_end.clone() + (v_norm * dist as f32);
566 }
567 }
568
569 pub fn extend_segment_equally(&mut self, segment_id: usize, dist: f64, proportion: f64) {
571 if segment_id >= self.segment_count() {
572 return;
573 }
574
575 let mut start = self.points[segment_id].clone();
577 let mut end = self.points[segment_id + 1].clone();
578 Self::extend_segment_equally_static(&mut start, &mut end, dist, proportion);
579 self.points[segment_id] = start;
580 self.points[segment_id + 1] = end;
581
582 if self.points.len() > 2 && self.is_closed() {
583 let len = self.points.len();
584 if segment_id == 0 {
585 self.points[len - 1] = self.points[0].clone();
586 } else if segment_id + 1 == len - 1 {
587 self.points[0] = self.points[len - 1].clone();
588 }
589 }
590 }
591
592 pub fn move_by(&mut self, direction: &Vector) {
594 for point in &mut self.points {
595 *point += direction.clone();
596 }
597 }
598
599 pub fn is_clockwise(&self, _plane: &Plane) -> bool {
601 if self.points.len() < 3 {
602 return false;
603 }
604
605 let mut sum = 0.0;
606 let n = if self.is_closed() {
607 self.points.len() - 1
608 } else {
609 self.points.len()
610 };
611
612 for i in 0..n {
613 let current = &self.points[i];
614 let next = &self.points[(i + 1) % n];
615 sum += (next.x() - current.x()) * (next.y() + current.y());
616 }
617
618 sum > 0.0
619 }
620
621 pub fn flip(&mut self) {
623 self.points.reverse();
624 }
625
626 pub fn get_convex_corners(&self) -> Vec<bool> {
628 if self.points.len() < 3 {
629 return Vec::new();
630 }
631
632 let closed = self.is_closed();
633 let normal = self.average_normal();
634 let n = if closed {
635 self.points.len() - 1
636 } else {
637 self.points.len()
638 };
639 let mut convex_corners = Vec::with_capacity(n);
640
641 for current in 0..n {
642 let prev = if current == 0 { n - 1 } else { current - 1 };
643 let next = if current == n - 1 { 0 } else { current + 1 };
644
645 let mut dir0 = self.points[current].clone() - self.points[prev].clone();
646 dir0.normalize_self();
647
648 let mut dir1 = self.points[next].clone() - self.points[current].clone();
649 dir1.normalize_self();
650
651 let mut cross = dir0.cross(&dir1);
652 cross.normalize_self();
653
654 let dot = cross.dot(&normal);
655 let is_convex = dot >= 0.0;
656 convex_corners.push(is_convex);
657 }
658
659 convex_corners
660 }
661
662 pub fn tween_two_polylines(
664 polyline0: &Polyline,
665 polyline1: &Polyline,
666 weight: f64,
667 ) -> Polyline {
668 if polyline0.points.len() != polyline1.points.len() {
669 return polyline0.clone();
670 }
671
672 let mut result = Polyline::default();
673 result.points.reserve(polyline0.points.len());
674
675 for i in 0..polyline0.points.len() {
676 let diff = polyline1.points[i].clone() - polyline0.points[i].clone();
677 let interpolated = polyline0.points[i].clone() + (diff * weight as f32);
678 result.points.push(interpolated);
679 }
680
681 result
682 }
683
684 fn average_normal(&self) -> Vector {
686 let len = self.points.len();
687 if len < 3 {
688 return Vector::new(0.0, 0.0, 1.0);
689 }
690
691 let closed = self.is_closed();
692 let n = if closed && len > 1 { len - 1 } else { len };
693
694 let mut average_normal = Vector::new(0.0, 0.0, 0.0);
695
696 for i in 0..n {
697 let prev = if i == 0 { n - 1 } else { i - 1 };
698 let next = (i + 1) % n;
699
700 let v1 = self.points[prev].clone() - self.points[i].clone();
701 let v2 = self.points[i].clone() - self.points[next].clone();
702 let cross = v1.cross(&v2);
703 average_normal += ✗
704 }
705
706 average_normal.normalize_self();
707 average_normal
708 }
709}
710
711impl AddAssign<&Vector> for Polyline {
712 fn add_assign(&mut self, other: &Vector) {
718 for p in &mut self.points {
719 *p += other.clone();
720 }
721 self.plane = Plane::new(
723 self.plane.origin() + other.clone(),
724 self.plane.x_axis(),
725 self.plane.y_axis(),
726 );
727 }
728}
729
730impl Add<&Vector> for Polyline {
731 type Output = Polyline;
732
733 fn add(self, other: &Vector) -> Polyline {
739 let mut result = self.clone();
740 result += other;
741 result
742 }
743}
744
745impl SubAssign<&Vector> for Polyline {
746 fn sub_assign(&mut self, other: &Vector) {
752 for p in &mut self.points {
753 *p -= other.clone();
754 }
755 self.plane = Plane::new(
757 self.plane.origin() - other.clone(),
758 self.plane.x_axis(),
759 self.plane.y_axis(),
760 );
761 }
762}
763
764impl Sub<&Vector> for Polyline {
765 type Output = Polyline;
766
767 fn sub(self, other: &Vector) -> Polyline {
773 let mut result = self.clone();
774 result -= other;
775 result
776 }
777}
778
779impl fmt::Display for Polyline {
780 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781 write!(
782 f,
783 "Polyline(guid={}, name={}, points={})",
784 self.guid,
785 self.name,
786 self.points.len()
787 )
788 }
789}
790
791#[cfg(test)]
792#[path = "polyline_test.rs"]
793mod polyline_test;