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

Handle the case where planes intersect but polygons do not #25

Merged
merged 1 commit into from Feb 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
@@ -1,2 +1,3 @@
target
Cargo.lock
.fuse_hidden*
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "plane-split"
version = "0.13.3"
version = "0.13.4"
description = "Plane splitting"
authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
license = "MPL-2.0"
Expand Down
38 changes: 23 additions & 15 deletions src/bsp.rs
Expand Up @@ -13,24 +13,31 @@ impl<T, U> BspPlane for Polygon<T, U> where
T: Copy + fmt::Debug + ApproxEq<T> +
ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
Zero + One + Float,
Zero + Float,
U: fmt::Debug,
{
fn cut(&self, mut poly: Self) -> PlaneCut<Self> {
debug!("\tCutting anchor {} by {}", poly.anchor, self.anchor);
trace!("\t\tbase {:?}", self.plane);

let ndot = self.plane.normal.dot(poly.plane.normal);
let (intersection, dist) = if ndot.approx_eq(&T::one()) {
debug!("\t\tNormals roughly point to the same direction");
(Intersection::Coplanar, self.plane.offset - poly.plane.offset)
} else if ndot.approx_eq(&-T::one()) {
debug!("\t\tNormals roughly point to opposite directions");
(Intersection::Coplanar, self.plane.offset + poly.plane.offset)
} else {
let is = self.intersect(&poly);
let dist = self.plane.signed_distance_sum_to(&poly);
(is, dist)
//Note: we treat `self` as a plane, and `poly` as a concrete polygon here
let (intersection, dist) = match self.plane.intersect(&poly.plane) {
None if self.plane.normal.dot(poly.plane.normal) > T::zero() => {
debug!("\t\tNormals roughly point to the same direction");
(Intersection::Coplanar, self.plane.offset - poly.plane.offset)
}
None => {
debug!("\t\tNormals roughly point to opposite directions");
(Intersection::Coplanar, self.plane.offset + poly.plane.offset)
}
Some(_) if self.plane.are_outside(&poly.points) => {
let dist = self.plane.signed_distance_sum_to(&poly);
(Intersection::Outside, dist)
}
Some(line) => {
//Note: distance isn't relevant here
(Intersection::Inside(line), T::zero())
}
};

match intersection {
Expand Down Expand Up @@ -66,11 +73,12 @@ impl<T, U> BspPlane for Polygon<T, U> where
.chain(res_add2)
.filter(|p| !p.is_empty())
{
if self.plane.signed_distance_sum_to(&sub) > T::zero() {
trace!("\t\t\tfront: {:?}", sub);
let dist = self.plane.signed_distance_sum_to(&sub);
if dist > T::zero() {
trace!("\t\t\tdist {:?} -> front: {:?}", dist, sub);
front.push(sub)
} else {
trace!("\t\t\tback: {:?}", sub);
trace!("\t\t\tdist {:?} -> back: {:?}", dist, sub);
back.push(sub)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/clip.rs
Expand Up @@ -95,6 +95,7 @@ impl<

/// Clip specified polygon by the contained planes, return the fragmented polygons.
pub fn clip(&mut self, polygon: Polygon<T, U>) -> &[Polygon<T, U>] {
debug!("\tClipping {:?}", polygon);
self.results.clear();
self.results.push(polygon);

Expand Down
10 changes: 5 additions & 5 deletions src/lib.rs
Expand Up @@ -162,13 +162,9 @@ impl<
.all(|p| self.signed_distance_to(p) * d0 > T::zero())
}

//TODO(breaking): turn this into Result<Line, DotProduct>
/// Compute the line of intersection with another plane.
pub fn intersect(&self, other: &Self) -> Option<Line<T, U>> {
let cross_dir = self.normal.cross(other.normal);
if cross_dir.dot(cross_dir) < T::approx_epsilon() {
return None
}

// compute any point on the intersection between planes
// (n1, v) + d1 = 0
// (n2, v) + d2 = 0
Expand All @@ -184,6 +180,10 @@ impl<
self.normal * ((other.offset * w - self.offset) * factor) -
other.normal* ((other.offset - self.offset * w) * factor);

let cross_dir = self.normal.cross(other.normal);
// note: the cross product isn't too close to zero
// due to the previous check

Some(Line {
origin,
dir: cross_dir.normalize(),
Expand Down
24 changes: 23 additions & 1 deletion tests/split.rs
Expand Up @@ -60,14 +60,36 @@ fn sort_trivial(splitter: &mut Splitter<f32, ()>) {
let anchors1: Vec<_> = result.iter().map(|p| p.anchor).collect();
let mut anchors2 = anchors1.clone();
anchors2.sort_by_key(|&a| -(a as i32));
assert_eq!(anchors1, anchors2); //make sure Z is sorted backwards
//make sure Z is sorted backwards
assert_eq!(anchors1, anchors2);
}

fn sort_external(splitter: &mut Splitter<f32, ()>) {
let rect0: TypedRect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
let poly0 = Polygon::from_rect(rect0, 0);
let poly1 = {
let transform0: TypedTransform3D<f32, (), ()> = TypedTransform3D::create_rotation(1.0, 0.0, 0.0, Angle::radians(2.0 * FRAC_PI_4));
let transform1: TypedTransform3D<f32, (), ()> = TypedTransform3D::create_translation(0.0, 100.0, 0.0);
Polygon::from_transformed_rect(rect0, transform1.pre_mul(&transform0), 1).unwrap()
};

let result = splitter.solve(&[poly0, poly1], vec3(1.0, 1.0, 0.0).normalize());
let anchors: Vec<_> = result.iter().map(|p| p.anchor).collect();
// make sure the second polygon is split in half around the plane of the first one,
// even if geometrically their polygons don't intersect.
assert_eq!(anchors, vec![1, 0, 1]);
}

#[test]
fn trivial_bsp() {
sort_trivial(&mut BspSplitter::new());
}

#[test]
fn external_bsp() {
sort_external(&mut BspSplitter::new());
}

#[test]
fn test_cut() {
let rect: TypedRect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
Expand Down