1use crate::error::Error;
2use crate::model::AbstractObject;
3use crate::model::common::{ApplyTransform, Triangulate, Triangulation};
4use crate::model::geometry::DirectPosition;
5use crate::model::geometry::primitives::{AbstractRingKind, Shell};
6use crate::model::geometry::primitives::{
7 AbstractRingProperty, AbstractSurfaceProperty, LinearRing, Polygon, Solid, TriangulatedSurface,
8};
9use crate::model::geometry::primitives::{AbstractSurfaceKind, ShellProperty};
10use nalgebra::{Isometry3, Point3, Rotation3, Scale3, Transform3, Vector3};
11use std::fmt;
12
13#[derive(Debug, Clone, PartialEq, Default)]
21pub struct Envelope {
22 abstract_object: AbstractObject,
23 lower_corner: DirectPosition,
24 upper_corner: DirectPosition,
25 srs_name: Option<String>,
26 srs_dimension: Option<u8>,
27}
28
29impl Envelope {
30 pub fn new(lower_corner: DirectPosition, upper_corner: DirectPosition) -> Result<Self, Error> {
49 if lower_corner.x() > upper_corner.x() {
50 return Err(Error::InvalidEnvelopeBounds {
51 axis: "x",
52 lower: lower_corner.x(),
53 upper: upper_corner.x(),
54 });
55 }
56 if lower_corner.y() > upper_corner.y() {
57 return Err(Error::InvalidEnvelopeBounds {
58 axis: "y",
59 lower: lower_corner.y(),
60 upper: upper_corner.y(),
61 });
62 }
63 if lower_corner.z() > upper_corner.z() {
64 return Err(Error::InvalidEnvelopeBounds {
65 axis: "z",
66 lower: lower_corner.z(),
67 upper: upper_corner.z(),
68 });
69 }
70
71 Ok(Self {
72 abstract_object: AbstractObject::default(),
73 lower_corner,
74 upper_corner,
75 srs_name: None,
76 srs_dimension: None,
77 })
78 }
79
80 pub fn new_unchecked(lower_corner: DirectPosition, upper_corner: DirectPosition) -> Self {
88 debug_assert!(
89 {
90 let lc: Point3<f64> = lower_corner.into();
91 let uc: Point3<f64> = upper_corner.into();
92 lc <= uc
93 },
94 "lower_corner must be <= upper_corner"
95 );
96
97 Self {
98 abstract_object: AbstractObject::default(),
99 lower_corner,
100 upper_corner,
101 srs_name: None,
102 srs_dimension: None,
103 }
104 }
105
106 pub fn lower_corner(&self) -> &DirectPosition {
108 &self.lower_corner
109 }
110
111 pub fn upper_corner(&self) -> &DirectPosition {
113 &self.upper_corner
114 }
115
116 pub fn set_lower_corner(&mut self, lower_corner: DirectPosition) {
117 self.lower_corner = lower_corner;
118 }
119
120 pub fn set_upper_corner(&mut self, upper_corner: DirectPosition) {
121 self.upper_corner = upper_corner;
122 }
123
124 pub fn srs_name(&self) -> Option<&str> {
127 self.srs_name.as_deref()
128 }
129
130 pub fn srs_dimension(&self) -> Option<u8> {
133 self.srs_dimension
134 }
135
136 pub fn set_srs_name(&mut self, srs_name: impl Into<String>) {
138 self.srs_name = Some(srs_name.into());
139 }
140
141 pub fn set_srs_name_opt(&mut self, srs_name: Option<String>) {
143 self.srs_name = srs_name;
144 }
145
146 pub fn clear_srs_name(&mut self) {
148 self.srs_name = None;
149 }
150
151 pub fn set_srs_dimension(&mut self, srs_dimension: u8) {
153 self.srs_dimension = Some(srs_dimension);
154 }
155
156 pub fn set_srs_dimension_opt(&mut self, srs_dimension: Option<u8>) {
158 self.srs_dimension = srs_dimension;
159 }
160
161 pub fn clear_srs_dimension(&mut self) {
163 self.srs_dimension = None;
164 }
165
166 pub fn size(&self) -> Vector3<f64> {
168 let lower_corner_point: Point3<f64> = self.lower_corner.into();
169 let upper_corner_point: Point3<f64> = self.upper_corner.into();
170 upper_corner_point - lower_corner_point
171 }
172
173 pub fn size_x(&self) -> f64 {
175 self.upper_corner.x() - self.lower_corner.x()
176 }
177
178 pub fn size_y(&self) -> f64 {
180 self.upper_corner.y() - self.lower_corner.y()
181 }
182
183 pub fn size_z(&self) -> f64 {
185 self.upper_corner.z() - self.lower_corner.z()
186 }
187
188 pub fn volume(&self) -> f64 {
192 self.size_x() * self.size_y() * self.size_z()
193 }
194
195 pub fn is_point(&self) -> bool {
197 self.lower_corner == self.upper_corner
198 }
199
200 #[allow(clippy::nonminimal_bool)]
202 pub fn is_linear(&self) -> bool {
203 let nx = self.size_x() > 0.0;
204 let ny = self.size_y() > 0.0;
205 let nz = self.size_z() > 0.0;
206 (nx && !ny && !nz) || (!nx && ny && !nz) || (!nx && !ny && nz)
207 }
208
209 #[allow(clippy::nonminimal_bool)]
211 pub fn is_surface(&self) -> bool {
212 let nx = self.size_x() > 0.0;
213 let ny = self.size_y() > 0.0;
214 let nz = self.size_z() > 0.0;
215 (nx && ny && !nz) || (nx && !ny && nz) || (!nx && ny && nz)
216 }
217
218 pub fn is_volume(&self) -> bool {
220 self.size_x() > 0.0 && self.size_y() > 0.0 && self.size_z() > 0.0
221 }
222
223 fn non_zero_extents(&self) -> u8 {
224 [self.size_x(), self.size_y(), self.size_z()]
225 .iter()
226 .filter(|&&s| s > 0.0)
227 .count() as u8
228 }
229
230 pub fn center(&self) -> DirectPosition {
234 DirectPosition::new(
235 self.lower_corner.x() + self.size_x() / 2.0,
236 self.lower_corner.y() + self.size_y() / 2.0,
237 self.lower_corner.z() + self.size_z() / 2.0,
238 )
239 .expect("envelope corners are finite")
240 }
241
242 pub fn contains(&self, point: &DirectPosition) -> bool {
244 let lower_corner: Point3<f64> = self.lower_corner.into();
245 let upper_corner: Point3<f64> = self.upper_corner.into();
246 let point: Point3<f64> = (*point).into();
247
248 lower_corner <= point && point <= upper_corner
249 }
250
251 pub fn contains_envelope(&self, envelope: &Envelope) -> bool {
253 self.contains(&envelope.lower_corner) && self.contains(&envelope.upper_corner)
254 }
255
256 pub fn contains_envelope_partially(&self, envelope: &Envelope) -> bool {
264 self.lower_corner.x() <= envelope.upper_corner.x()
265 && self.upper_corner.x() >= envelope.lower_corner.x()
266 && self.lower_corner.y() <= envelope.upper_corner.y()
267 && self.upper_corner.y() >= envelope.lower_corner.y()
268 && self.lower_corner.z() <= envelope.upper_corner.z()
269 && self.upper_corner.z() >= envelope.lower_corner.z()
270 }
271
272 pub fn enlarge(&self, distance: f64) -> Result<Envelope, Error> {
282 let lower_corner = DirectPosition::new(
283 self.lower_corner.x() - distance,
284 self.lower_corner.y() - distance,
285 self.lower_corner.z() - distance,
286 )?;
287 let upper_corner = DirectPosition::new(
288 self.upper_corner.x() + distance,
289 self.upper_corner.y() + distance,
290 self.upper_corner.z() + distance,
291 )?;
292
293 Envelope::new(lower_corner, upper_corner)
294 }
295}
296
297impl Envelope {
298 pub fn from_envelopes(envelopes: &[Self]) -> Option<Self> {
303 let first = envelopes.first()?;
304
305 let (lower, upper) = envelopes.iter().skip(1).fold(
306 (first.lower_corner, first.upper_corner),
307 |(lo, hi), e| {
308 let new_lo = DirectPosition::new(
309 lo.x().min(e.lower_corner.x()),
310 lo.y().min(e.lower_corner.y()),
311 lo.z().min(e.lower_corner.z()),
312 )
313 .unwrap();
314 let new_hi = DirectPosition::new(
315 hi.x().max(e.upper_corner.x()),
316 hi.y().max(e.upper_corner.y()),
317 hi.z().max(e.upper_corner.z()),
318 )
319 .unwrap();
320 (new_lo, new_hi)
321 },
322 );
323
324 Some(Envelope::new_unchecked(lower, upper))
325 }
326
327 pub fn from_points(points: &[DirectPosition]) -> Result<Self, Error> {
333 if points.is_empty() {
334 return Err(Error::TooFewElements {
335 geometry: "Envelope::from_points",
336 minimum: 1,
337 spec: None,
338 id: None,
339 detail: None,
340 });
341 }
342
343 let first = &points[0];
344 let (mut min_x, mut min_y, mut min_z) = (first.x(), first.y(), first.z());
345 let (mut max_x, mut max_y, mut max_z) = (first.x(), first.y(), first.z());
346
347 for point in points.iter().skip(1) {
348 min_x = min_x.min(point.x());
349 min_y = min_y.min(point.y());
350 min_z = min_z.min(point.z());
351 max_x = max_x.max(point.x());
352 max_y = max_y.max(point.y());
353 max_z = max_z.max(point.z());
354 }
355
356 let lower_corner = DirectPosition::new(min_x, min_y, min_z)?;
357 let upper_corner = DirectPosition::new(max_x, max_y, max_z)?;
358
359 Ok(Self::new_unchecked(lower_corner, upper_corner))
360 }
361}
362
363impl Envelope {
364 pub fn to_solid(&self) -> Result<Solid, Error> {
373 if !self.is_volume() {
374 return Err(Error::NotAVolume {
375 non_zero_extents: self.non_zero_extents(),
376 });
377 }
378
379 let (lx, ly, lz) = (
380 self.lower_corner.x(),
381 self.lower_corner.y(),
382 self.lower_corner.z(),
383 );
384 let (hx, hy, hz) = (
385 self.upper_corner.x(),
386 self.upper_corner.y(),
387 self.upper_corner.z(),
388 );
389
390 let p000 = DirectPosition::new(lx, ly, lz).expect("envelope corners are finite");
391 let p100 = DirectPosition::new(hx, ly, lz).expect("envelope corners are finite");
392 let p110 = DirectPosition::new(hx, hy, lz).expect("envelope corners are finite");
393 let p010 = DirectPosition::new(lx, hy, lz).expect("envelope corners are finite");
394 let p001 = DirectPosition::new(lx, ly, hz).expect("envelope corners are finite");
395 let p101 = DirectPosition::new(hx, ly, hz).expect("envelope corners are finite");
396 let p111 = DirectPosition::new(hx, hy, hz).expect("envelope corners are finite");
397 let p011 = DirectPosition::new(lx, hy, hz).expect("envelope corners are finite");
398
399 let face_rings: [Vec<DirectPosition>; 6] = [
400 vec![p000, p010, p110, p100], vec![p001, p101, p111, p011], vec![p000, p100, p101, p001], vec![p010, p011, p111, p110], vec![p000, p001, p011, p010], vec![p100, p110, p111, p101], ];
407
408 let members: Vec<AbstractSurfaceProperty> = face_rings
409 .into_iter()
410 .map(|points| {
411 let ring = LinearRing::new(points).ok()?;
412 let polygon = Polygon::new(
413 Some(AbstractRingProperty::from_object(
414 AbstractRingKind::LinearRing(ring),
415 )),
416 vec![],
417 )
418 .ok()?;
419 Some(AbstractSurfaceProperty::from_object(
420 AbstractSurfaceKind::Polygon(polygon),
421 ))
422 })
423 .collect::<Option<_>>()
424 .expect("envelope corners are finite and valid");
425 let shell = Shell::new(members).expect("envelope is valid");
426 let shell_property = ShellProperty::from_object(shell);
427
428 let solid = Solid::new(Some(shell_property)).expect("envelope is valid");
429 Ok(solid)
430 }
431
432 pub fn to_polygon(&self) -> Result<Polygon, Error> {
441 if !self.is_surface() {
442 return Err(Error::NotASurface {
443 non_zero_extents: self.non_zero_extents(),
444 });
445 }
446
447 let (lx, ly, lz) = (
448 self.lower_corner.x(),
449 self.lower_corner.y(),
450 self.lower_corner.z(),
451 );
452 let (hx, hy, hz) = (
453 self.upper_corner.x(),
454 self.upper_corner.y(),
455 self.upper_corner.z(),
456 );
457
458 let points = if self.size_z() == 0.0 {
459 vec![
461 DirectPosition::new(lx, ly, lz).expect("envelope corners are finite"),
462 DirectPosition::new(hx, ly, lz).expect("envelope corners are finite"),
463 DirectPosition::new(hx, hy, lz).expect("envelope corners are finite"),
464 DirectPosition::new(lx, hy, lz).expect("envelope corners are finite"),
465 ]
466 } else if self.size_y() == 0.0 {
467 vec![
469 DirectPosition::new(lx, ly, lz).expect("envelope corners are finite"),
470 DirectPosition::new(lx, ly, hz).expect("envelope corners are finite"),
471 DirectPosition::new(hx, ly, hz).expect("envelope corners are finite"),
472 DirectPosition::new(hx, ly, lz).expect("envelope corners are finite"),
473 ]
474 } else {
475 vec![
477 DirectPosition::new(lx, ly, lz).expect("envelope corners are finite"),
478 DirectPosition::new(lx, hy, lz).expect("envelope corners are finite"),
479 DirectPosition::new(lx, hy, hz).expect("envelope corners are finite"),
480 DirectPosition::new(lx, ly, hz).expect("envelope corners are finite"),
481 ]
482 };
483
484 let ring = LinearRing::new(points).expect("envelope corners are finite and valid");
485 Polygon::new(
486 Some(AbstractRingProperty::from_object(
487 AbstractRingKind::LinearRing(ring),
488 )),
489 vec![],
490 )
491 .map_err(|_| Error::NotASurface {
492 non_zero_extents: self.non_zero_extents(),
493 })
494 }
495
496 pub fn to_triangulated_surface(&self) -> Result<TriangulatedSurface, Error> {
505 if self.is_surface() {
506 self.to_polygon()?
507 .triangulate()
508 .map(Triangulation::into_surface)
509 } else if self.is_volume() {
510 self.to_solid()?
511 .exterior()
512 .as_ref()
513 .expect("must be created")
514 .object()
515 .expect("must be created")
516 .triangulate()
517 .map(Triangulation::into_surface)
518 } else {
519 Err(Error::NotSurfaceOrVolume {
520 non_zero_extents: self.non_zero_extents(),
521 })
522 }
523 }
524}
525
526impl ApplyTransform for Envelope {
527 fn apply_transform(&mut self, transform: Transform3<f64>) {
528 let transformed_lower_corner: Point3<f64> = transform * Point3::from(self.lower_corner);
529 let transformed_upper_corner: Point3<f64> = transform * Point3::from(self.upper_corner);
530
531 self.lower_corner = DirectPosition::new(
532 transformed_lower_corner.x.min(transformed_upper_corner.x),
533 transformed_lower_corner.y.min(transformed_upper_corner.y),
534 transformed_lower_corner.z.min(transformed_upper_corner.z),
535 )
536 .expect("envelope corners are finite");
537 self.upper_corner = DirectPosition::new(
538 transformed_lower_corner.x.max(transformed_upper_corner.x),
539 transformed_lower_corner.y.max(transformed_upper_corner.y),
540 transformed_lower_corner.z.max(transformed_upper_corner.z),
541 )
542 .expect("envelope corners are finite");
543 }
544
545 fn apply_isometry(&mut self, isometry: Isometry3<f64>) {
569 let transformed_lower_corner: Point3<f64> = isometry * Point3::from(self.lower_corner);
570 let transformed_upper_corner: Point3<f64> = isometry * Point3::from(self.upper_corner);
571
572 self.lower_corner = DirectPosition::new(
573 transformed_lower_corner.x.min(transformed_upper_corner.x),
574 transformed_lower_corner.y.min(transformed_upper_corner.y),
575 transformed_lower_corner.z.min(transformed_upper_corner.z),
576 )
577 .expect("envelope corners are finite");
578 self.upper_corner = DirectPosition::new(
579 transformed_lower_corner.x.max(transformed_upper_corner.x),
580 transformed_lower_corner.y.max(transformed_upper_corner.y),
581 transformed_lower_corner.z.max(transformed_upper_corner.z),
582 )
583 .expect("envelope corners are finite");
584 }
585
586 fn apply_translation(&mut self, vector: Vector3<f64>) {
592 self.lower_corner = DirectPosition::new(
593 self.lower_corner.x() + vector.x,
594 self.lower_corner.y() + vector.y,
595 self.lower_corner.z() + vector.z,
596 )
597 .expect("envelope corners are finite");
598 self.upper_corner = DirectPosition::new(
599 self.upper_corner.x() + vector.x,
600 self.upper_corner.y() + vector.y,
601 self.upper_corner.z() + vector.z,
602 )
603 .expect("envelope corners are finite");
604 }
605
606 fn apply_rotation(&mut self, rotation: Rotation3<f64>) {
611 let transformed_lower_corner: Point3<f64> = rotation * Point3::from(self.lower_corner);
612 let transformed_upper_corner: Point3<f64> = rotation * Point3::from(self.upper_corner);
613
614 self.lower_corner = DirectPosition::new(
615 transformed_lower_corner.x.min(transformed_upper_corner.x),
616 transformed_lower_corner.y.min(transformed_upper_corner.y),
617 transformed_lower_corner.z.min(transformed_upper_corner.z),
618 )
619 .expect("envelope corners are finite");
620 self.upper_corner = DirectPosition::new(
621 transformed_lower_corner.x.max(transformed_upper_corner.x),
622 transformed_lower_corner.y.max(transformed_upper_corner.y),
623 transformed_lower_corner.z.max(transformed_upper_corner.z),
624 )
625 .expect("envelope corners are finite");
626 }
627
628 fn apply_scale(&mut self, scale: Scale3<f64>) {
634 let transformed_lower_corner = Point3::new(
635 self.lower_corner.x() * scale.vector.x,
636 self.lower_corner.y() * scale.vector.y,
637 self.lower_corner.z() * scale.vector.z,
638 );
639 let transformed_upper_corner = Point3::new(
640 self.upper_corner.x() * scale.vector.x,
641 self.upper_corner.y() * scale.vector.y,
642 self.upper_corner.z() * scale.vector.z,
643 );
644
645 self.lower_corner = DirectPosition::new(
646 transformed_lower_corner.x.min(transformed_upper_corner.x),
647 transformed_lower_corner.y.min(transformed_upper_corner.y),
648 transformed_lower_corner.z.min(transformed_upper_corner.z),
649 )
650 .expect("envelope corners are finite");
651 self.upper_corner = DirectPosition::new(
652 transformed_lower_corner.x.max(transformed_upper_corner.x),
653 transformed_lower_corner.y.max(transformed_upper_corner.y),
654 transformed_lower_corner.z.max(transformed_upper_corner.z),
655 )
656 .expect("envelope corners are finite");
657 }
658}
659
660impl fmt::Display for Envelope {
661 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
662 write!(
663 f,
664 "Envelope[{}, {}, {} -> {}, {}, {}]",
665 self.lower_corner.x(),
666 self.lower_corner.y(),
667 self.lower_corner.z(),
668 self.upper_corner.x(),
669 self.upper_corner.y(),
670 self.upper_corner.z()
671 )
672 }
673}
674
675#[cfg(test)]
676mod tests {
677 use super::*;
678
679 fn pos(x: f64, y: f64, z: f64) -> DirectPosition {
680 DirectPosition::new(x, y, z).unwrap()
681 }
682
683 fn env(lx: f64, ly: f64, lz: f64, ux: f64, uy: f64, uz: f64) -> Envelope {
684 Envelope::new(pos(lx, ly, lz), pos(ux, uy, uz)).unwrap()
685 }
686
687 #[test]
688 fn apply_translation_shifts_both_corners() {
689 let mut envelope = env(0.0, 0.0, 0.0, 1.0, 2.0, 3.0);
690
691 envelope.apply_translation(Vector3::new(10.0, -5.0, 1.0));
692
693 assert_eq!(envelope, env(10.0, -5.0, 1.0, 11.0, -3.0, 4.0));
694 }
695
696 #[test]
697 fn apply_rotation_refits_axis_aligned_box() {
698 use std::f64::consts::FRAC_PI_2;
699
700 let mut envelope = env(0.0, 0.0, 0.0, 1.0, 2.0, 3.0);
701
702 envelope.apply_rotation(Rotation3::from_euler_angles(0.0, 0.0, FRAC_PI_2));
704
705 assert!((envelope.size_x() - 2.0).abs() < 1e-10);
706 assert!((envelope.size_y() - 1.0).abs() < 1e-10);
707 assert!((envelope.size_z() - 3.0).abs() < 1e-10);
708 }
709
710 #[test]
711 fn apply_scale_refits_on_mirroring_negative_scale() {
712 let mut envelope = env(1.0, 1.0, 1.0, 2.0, 3.0, 4.0);
713
714 envelope.apply_scale(Scale3::new(-1.0, 2.0, 1.0));
716
717 assert_eq!(envelope, env(-2.0, 2.0, 1.0, -1.0, 6.0, 4.0));
718 }
719
720 #[test]
721 fn from_envelopes_empty_returns_none() {
722 let result = Envelope::from_envelopes(&[]);
723 assert!(result.is_none());
724 }
725
726 #[test]
727 fn from_envelopes_single_returns_same_envelope() {
728 let e = env(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
729 let result = Envelope::from_envelopes(&[e.clone()]).unwrap();
730
731 assert_eq!(result, e);
732 }
733
734 #[test]
735 fn from_envelopes_two_disjoint() {
736 let a = env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
737 let b = env(5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
738 let result = Envelope::from_envelopes(&[a, b]).unwrap();
739
740 assert_eq!(result, env(0.0, 0.0, 0.0, 8.0, 9.0, 10.0));
741 }
742
743 #[test]
744 fn from_envelopes_overlapping() {
745 let a = env(0.0, 0.0, 0.0, 5.0, 5.0, 5.0);
746 let b = env(3.0, 3.0, 3.0, 7.0, 7.0, 7.0);
747 let result = Envelope::from_envelopes(&[a, b]).unwrap();
748
749 assert_eq!(result, env(0.0, 0.0, 0.0, 7.0, 7.0, 7.0));
750 }
751
752 #[test]
753 fn from_envelopes_one_contains_the_other() {
754 let outer = env(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
755 let inner = env(2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
756 let result = Envelope::from_envelopes(&[outer.clone(), inner]).unwrap();
757
758 assert_eq!(result, outer);
759 }
760
761 #[test]
762 fn from_envelopes_multiple() {
763 let a = env(0.0, 10.0, 20.0, 1.0, 11.0, 21.0);
764 let b = env(-5.0, 8.0, 25.0, 2.0, 12.0, 30.0);
765 let c = env(1.0, 9.0, 18.0, 3.0, 15.0, 22.0);
766 let result = Envelope::from_envelopes(&[a, b, c]).unwrap();
767
768 assert_eq!(result, env(-5.0, 8.0, 18.0, 3.0, 15.0, 30.0));
769 }
770
771 #[test]
772 fn from_envelopes_with_negative_coords() {
773 let a = env(-10.0, -20.0, -30.0, -1.0, -2.0, -3.0);
774 let b = env(-5.0, -25.0, -15.0, 0.0, -1.0, 0.0);
775 let result = Envelope::from_envelopes(&[a, b]).unwrap();
776
777 assert_eq!(result, env(-10.0, -25.0, -30.0, 0.0, -1.0, 0.0));
778 }
779
780 #[test]
781 fn from_envelopes_zero_volume_envelopes() {
782 let a = env(1.0, 1.0, 1.0, 1.0, 1.0, 1.0); let b = env(3.0, 3.0, 3.0, 3.0, 3.0, 3.0); let result = Envelope::from_envelopes(&[a, b]).unwrap();
785
786 assert_eq!(result, env(1.0, 1.0, 1.0, 3.0, 3.0, 3.0));
787 }
788
789 #[test]
790 fn is_point_when_corners_equal() {
791 let e = env(1.0, 2.0, 3.0, 1.0, 2.0, 3.0);
792 assert!(e.is_point());
793 assert!(!e.is_linear());
794 assert!(!e.is_surface());
795 assert!(!e.is_volume());
796 }
797
798 #[test]
799 fn is_linear_along_x() {
800 let e = env(0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
801 assert!(!e.is_point());
802 assert!(e.is_linear());
803 assert!(!e.is_surface());
804 assert!(!e.is_volume());
805 }
806
807 #[test]
808 fn is_linear_along_y() {
809 let e = env(0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
810 assert!(e.is_linear());
811 }
812
813 #[test]
814 fn is_linear_along_z() {
815 let e = env(0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
816 assert!(e.is_linear());
817 }
818
819 #[test]
820 fn is_surface_xy_plane() {
821 let e = env(0.0, 0.0, 0.0, 1.0, 1.0, 0.0);
822 assert!(!e.is_point());
823 assert!(!e.is_linear());
824 assert!(e.is_surface());
825 assert!(!e.is_volume());
826 }
827
828 #[test]
829 fn is_surface_xz_plane() {
830 let e = env(0.0, 0.0, 0.0, 1.0, 0.0, 1.0);
831 assert!(e.is_surface());
832 }
833
834 #[test]
835 fn is_surface_yz_plane() {
836 let e = env(0.0, 0.0, 0.0, 0.0, 1.0, 1.0);
837 assert!(e.is_surface());
838 }
839
840 #[test]
841 fn is_volume_all_extents_nonzero() {
842 let e = env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
843 assert!(!e.is_point());
844 assert!(!e.is_linear());
845 assert!(!e.is_surface());
846 assert!(e.is_volume());
847 }
848
849 #[test]
850 fn to_polygon_returns_err_for_point() {
851 assert_eq!(
852 env(1.0, 1.0, 1.0, 1.0, 1.0, 1.0).to_polygon(),
853 Err(Error::NotASurface {
854 non_zero_extents: 0
855 })
856 );
857 }
858
859 #[test]
860 fn to_polygon_returns_err_for_linear() {
861 assert_eq!(
862 env(0.0, 0.0, 0.0, 1.0, 0.0, 0.0).to_polygon(),
863 Err(Error::NotASurface {
864 non_zero_extents: 1
865 })
866 );
867 }
868
869 #[test]
870 fn to_polygon_returns_err_for_volume() {
871 assert_eq!(
872 env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0).to_polygon(),
873 Err(Error::NotASurface {
874 non_zero_extents: 3
875 })
876 );
877 }
878
879 #[test]
880 fn to_polygon_xy_plane() {
881 assert!(env(0.0, 0.0, 0.0, 2.0, 3.0, 0.0).to_polygon().is_ok());
882 }
883
884 #[test]
885 fn to_polygon_xz_plane() {
886 assert!(env(0.0, 0.0, 0.0, 2.0, 0.0, 3.0).to_polygon().is_ok());
887 }
888
889 #[test]
890 fn to_polygon_yz_plane() {
891 assert!(env(0.0, 0.0, 0.0, 0.0, 2.0, 3.0).to_polygon().is_ok());
892 }
893
894 #[test]
895 fn to_triangulated_surface_returns_err_for_point() {
896 assert_eq!(
897 env(0.0, 0.0, 0.0, 0.0, 0.0, 0.0).to_triangulated_surface(),
898 Err(Error::NotSurfaceOrVolume {
899 non_zero_extents: 0
900 })
901 );
902 }
903
904 #[test]
905 fn to_triangulated_surface_returns_err_for_linear() {
906 assert_eq!(
907 env(0.0, 0.0, 0.0, 1.0, 0.0, 0.0).to_triangulated_surface(),
908 Err(Error::NotSurfaceOrVolume {
909 non_zero_extents: 1
910 })
911 );
912 }
913
914 #[test]
915 fn to_triangulated_surface_surface_has_two_triangles() {
916 let result = env(0.0, 0.0, 0.0, 2.0, 3.0, 0.0)
917 .to_triangulated_surface()
918 .unwrap();
919 assert_eq!(result.triangles().len(), 2);
920 }
921
922 #[test]
923 fn to_triangulated_surface_volume_has_twelve_triangles() {
924 let result = env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
925 .to_triangulated_surface()
926 .unwrap();
927 assert_eq!(result.triangles().len(), 12);
928 }
929
930 #[test]
931 fn envelope_contains() {
932 let lower_corner = DirectPosition::new(1.0, 2.0, 3.0).unwrap();
933 let upper_corner = DirectPosition::new(2.0, 3.0, 4.0).unwrap();
934 let envelope = Envelope::new(lower_corner, upper_corner).unwrap();
935 let point_a = DirectPosition::new(1.5, 2.5, 3.5).unwrap();
936 let point_b = DirectPosition::new(2.5, 3.5, 4.5).unwrap();
937
938 assert!(envelope.contains(&point_a));
939 assert!(!envelope.contains(&point_b));
940 }
941
942 #[test]
943 fn contains_envelope_partially_true_for_overlapping_boxes() {
944 let a = env(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
945 let b = env(5.0, 5.0, 5.0, 15.0, 15.0, 15.0);
946 assert!(a.contains_envelope_partially(&b));
947 assert!(b.contains_envelope_partially(&a));
948 }
949
950 #[test]
951 fn contains_envelope_partially_true_for_disjoint_boxes() {
952 let a = env(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
953 let b = env(20.0, 20.0, 20.0, 30.0, 30.0, 30.0);
954 assert!(!a.contains_envelope_partially(&b));
955 assert!(!b.contains_envelope_partially(&a));
956 }
957
958 #[test]
959 fn contains_envelope_partially_true_for_slab_crossing_with_no_corner_contained() {
960 let a = env(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
964 let slab = env(-5.0, -5.0, 4.0, 15.0, 15.0, 6.0);
965 assert!(a.contains_envelope_partially(&slab));
966 assert!(slab.contains_envelope_partially(&a));
967 }
968
969 #[test]
970 fn contains_envelope_partially_true_when_touching_at_boundary() {
971 let a = env(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
972 let b = env(10.0, 10.0, 10.0, 20.0, 20.0, 20.0);
973 assert!(a.contains_envelope_partially(&b));
974 }
975}