Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement more set operations for CurveBoundaries #2057

Merged
merged 3 commits into from
Oct 16, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 110 additions & 4 deletions crates/fj-core/src/geometry/boundary/multiple.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::collections::VecDeque;

use fj_math::Point;

use crate::geometry::CurveBoundary;

use super::single::OneOrTwoBoundaries;

/// A collection of multiple [`CurveBoundary`] instances
///
/// Has a type parameter, `T`, which can be used to attach a payload to each
Expand Down Expand Up @@ -66,7 +66,7 @@ impl<T: CurveBoundariesPayload> CurveBoundaries<T> {
/// Create the union between this an another `CurveBoundaries` instance
pub fn union(mut self, other: impl Into<Self>) -> Self {
for (other_boundary, other_payload) in other.into().inner {
let mut overlapping_payloads = VecDeque::new();
let mut overlapping_payloads = Vec::new();

let mut i = 0;
loop {
Expand All @@ -76,7 +76,7 @@ impl<T: CurveBoundariesPayload> CurveBoundaries<T> {

if boundary.overlaps(&other_boundary) {
let payload = self.inner.swap_remove(i);
overlapping_payloads.push_back(payload);
overlapping_payloads.push(payload);
continue;
}

Expand Down Expand Up @@ -104,6 +104,71 @@ impl<T: CurveBoundariesPayload> CurveBoundaries<T> {
}
}

impl CurveBoundaries<()> {
/// Compute the difference between this instance and another one
///
/// # Implementation Note
///
/// This method is only available for `CurveBoundaries` instances without
/// payloads, simply because more wasn't needed so far. Support for payloads
/// can be added by expanding [`CurveBoundariesPayload`] accordingly, and
/// integrating the new method here.
pub fn difference(mut self, other: impl Into<Self>) -> Self {
for (other_boundary, ()) in other.into().inner {
let mut i = 0;

loop {
if i == self.inner.len() {
break;
}

let (boundary, ()) = self.inner.remove(i);

match boundary.difference(other_boundary) {
Some(OneOrTwoBoundaries::One(b)) => {
self.inner.insert(i, (b, ()));
i += 1;
}
Some(OneOrTwoBoundaries::Two([b1, b2])) => {
self.inner.insert(i, (b1, ()));
i += 1;

self.inner.insert(i, (b2, ()));
i += 1;
}
None => {
// Nothing to do!
//
// We already removed the original boundary above, and
// if the difference leaves no result, we don't need to
// add anything back.
//
// Don't need to update `i` either. Thanks to the
// removal, it already points to the next item.
}
}
}
}

self
}

/// Compute the symmetric difference between this instance and another one
///
/// # Implementation Note
///
/// This method is only available for `CurveBoundaries` instances without
/// payloads, simply because more wasn't needed so far. Support for payloads
/// can be added by expanding [`CurveBoundariesPayload`] accordingly, and
/// integrating the new method here.
pub fn symmetric_difference(self, other: impl Into<Self>) -> Self {
let other = other.into();
self.clone()
.difference(other.clone())
.union(other.difference(self))
}
}

impl<T: CurveBoundariesPayload> Default for CurveBoundaries<T> {
fn default() -> Self {
Self { inner: Vec::new() }
Expand Down Expand Up @@ -169,6 +234,47 @@ impl CurveBoundariesPayload for () {
mod tests {
use super::CurveBoundaries;

#[test]
fn difference() {
// There are already extensive tests for `CurveBoundary::difference`,
// and we don't need to repeat those here. The following tests just make
// sure that all of the possible return values of that method are
// handled correctly.

// Difference results in one boundary.
diff([[0., 2.]], [[1., 3.]], [[0., 1.]]);

// Difference results in two boundaries.
diff([[0., 3.]], [[1., 2.]], [[0., 1.], [2., 3.]]);

// Difference results in no boundaries.
diff([[1., 2.]], [[0., 3.]], []);

// And a combined one, to make sure that everything works with multiple
// boundaries in the inputs.
diff(
[[0., 2.], [4., 7.], [9., 10.]],
[[1., 3.], [5., 6.], [8., 11.]],
[[0., 1.], [4., 5.], [6., 7.]],
);

fn diff<const A: usize, const B: usize, const X: usize>(
a: [[f64; 2]; A],
b: [[f64; 2]; B],
x: [[f64; 2]; X],
) {
let a = a.map(|boundary| boundary.map(|v| [v]));
let b = b.map(|boundary| boundary.map(|v| [v]));
let x = x.map(|boundary| boundary.map(|v| [v]));

let a = CurveBoundaries::from_iter(a);
let b = CurveBoundaries::from_iter(b);
let x = CurveBoundaries::from_iter(x);

assert_eq!(a.difference(b), x);
}
}

#[test]
fn union() {
union([[0., 1.]], [[1., 2.]], [[0., 2.]]);
Expand Down