Bug 1678454 - Update plane-split dependency to 0.17.1 r=jrmuizel

Differential Revision: https://phabricator.services.mozilla.com/D97784
This commit is contained in:
Dzmitry Malyshau 2020-11-20 19:52:15 +00:00
parent c9720a8ee7
commit 09630ed233
13 changed files with 386 additions and 314 deletions

4
Cargo.lock generated
View file

@ -3864,9 +3864,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "plane-split"
version = "0.17.0"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2211e7ccc9b6260779dd9bad59f7b10889d6361974623b9e405afd7e7e764654"
checksum = "f3f7075ec146b897b6e0faca47adeb7ed3d4f6eaa8145bf19db17311081b3f63"
dependencies = [
"binary-space-partition",
"euclid",

6
gfx/wr/Cargo.lock generated
View file

@ -1175,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "plane-split"
version = "0.17.0"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1815,7 +1815,7 @@ dependencies = [
"malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"plane-split 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plane-split 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
"png 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2180,7 +2180,7 @@ dependencies = [
"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum plane-split 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2211e7ccc9b6260779dd9bad59f7b10889d6361974623b9e405afd7e7e764654"
"checksum plane-split 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f7075ec146b897b6e0faca47adeb7ed3d4f6eaa8145bf19db17311081b3f63"
"checksum png 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)" = "910f09135b1ed14bb16be445a8c23ddf0777eca485fbfc7cee00d81fecab158a"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"bbf7428797ff113d822633c126ccf5e0e7584fe15c825048952623fdd99ab677","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"558d046c8c9ad79f23adef1e4b26c28c4599dcba56dadd60e2e7dc3a03ec2806","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"34c92cff76488fea23bee209fed62eee2db8c9687991c6413605961782e1317e","src/clip.rs":"e9ac9102f85709336088f3887b53273958aa9b2505d7ab06ba9a3e1e720aa540","src/lib.rs":"62613b64dacbfcd4727c5ce57e09bd9289db5a678f86dfe8544f1efc0f3f62b0","src/polygon.rs":"b8458d3c01dc06918b8410f363b19f15542522f1037cdd731f08b65e92a822bd","tests/clip.rs":"1a17f850cd770dbfb08c61779bf11f36f39e4315a00d3101c459e882b7982269","tests/main.rs":"5ebe911ddbb0e5449aacf060b977083c2bc43e3d8e5d5a3279d7e4950529f4a7","tests/split.rs":"fac076a403b87de1bdd0d11a54d36d0b02753868fc9cde5e5c5c1e03297da4ff"},"package":"2211e7ccc9b6260779dd9bad59f7b10889d6361974623b9e405afd7e7e764654"}
{"files":{"Cargo.toml":"36e50e6fbc0d7e1b9587eacee54d75076503acf6d385e769ed606262dc4ff79b","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"558d046c8c9ad79f23adef1e4b26c28c4599dcba56dadd60e2e7dc3a03ec2806","benches/split.rs":"47dee7456a887b260af1a42590074ea2f4959c41344c636163434925d20ee903","rustfmt.toml":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/bsp.rs":"5c4f9685348b04cd716050a74f4fcabde8c64cca1e7f7c50734e26301f467f64","src/clip.rs":"df110afc471a350a70241e6f2b2fcf1d967ad012745ae82ddec43a8934982fda","src/lib.rs":"c56c64dc8d405a5a2875efd9579a55cf6f1820eddaa466f6c2c120114d8736c2","src/polygon.rs":"02ed361aca6b44c22121718f874a98e346f310cdcccbe23fd8a5ec1211a46371","tests/clip.rs":"f49b3ae961c4a69714f1eccc575304b8d8d823f018d51a0841fa70214ce7c309","tests/main.rs":"2f4999f199e456cba234b60d252329fcc0c081158168484468002a184d65cc07","tests/split.rs":"84ff0f87e4d37710ee952397f78c95450945baddd7b8f57b0b0422fff50ecc64"},"package":"f3f7075ec146b897b6e0faca47adeb7ed3d4f6eaa8145bf19db17311081b3f63"}

View file

@ -11,8 +11,9 @@
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "plane-split"
version = "0.17.0"
version = "0.17.1"
authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
description = "Plane splitting"
documentation = "https://docs.rs/plane-split"

View file

@ -4,9 +4,9 @@ extern crate euclid;
extern crate plane_split;
extern crate test;
use std::sync::Arc;
use euclid::vec3;
use plane_split::{BspSplitter, Splitter, make_grid};
use plane_split::{make_grid, BspSplitter, Splitter};
use std::sync::Arc;
#[bench]
fn bench_bsp(b: &mut test::Bencher) {

0
third_party/rust/plane-split/rustfmt.toml vendored Executable file
View file

View file

@ -1,31 +1,34 @@
use {Intersection, Plane, Polygon, Splitter};
use is_zero;
use crate::{is_zero, Intersection, Plane, Polygon, Splitter};
use binary_space_partition::{BspNode, Plane as BspPlane, PlaneCut};
use euclid::{Point3D, Vector3D};
use euclid::approxeq::ApproxEq;
use euclid::{approxeq::ApproxEq, Point3D, Vector3D};
use num_traits::{Float, One, Zero};
use std::{fmt, iter, ops};
impl<T, U, A> BspPlane for Polygon<T, U, A> 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 + Float,
impl<T, U, A> BspPlane for Polygon<T, U, A>
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
+ Float,
U: fmt::Debug,
A: Copy + fmt::Debug,
{
fn cut(&self, mut poly: Self) -> PlaneCut<Self> {
debug!("\tCutting anchor {:?} by {:?}", poly.anchor, self.anchor);
trace!("\t\tbase {:?}", self.plane);
log::debug!("\tCutting anchor {:?} by {:?}", poly.anchor, self.anchor);
log::trace!("\t\tbase {:?}", self.plane);
//Note: we treat `self` as a plane, and `poly` as a concrete polygon here
let (intersection, dist) = match self.plane.intersect(&poly.plane) {
None => {
let ndot = self.plane.normal.dot(poly.plane.normal);
debug!("\t\tNormals are aligned with {:?}", ndot);
log::debug!("\t\tNormals are aligned with {:?}", ndot);
let dist = self.plane.offset - ndot * poly.plane.offset;
(Intersection::Coplanar, dist)
}
@ -45,11 +48,11 @@ impl<T, U, A> BspPlane for Polygon<T, U, A> where
// This is done to avoid mistakenly ordering items that should be on the same
// plane but end up slightly different due to the floating point precision.
Intersection::Coplanar if is_zero(dist) => {
debug!("\t\tCoplanar at {:?}", dist);
log::debug!("\t\tCoplanar at {:?}", dist);
PlaneCut::Sibling(poly)
}
Intersection::Coplanar | Intersection::Outside => {
debug!("\t\tOutside at {:?}", dist);
log::debug!("\t\tOutside at {:?}", dist);
if dist > T::zero() {
PlaneCut::Cut {
front: vec![poly],
@ -63,7 +66,7 @@ impl<T, U, A> BspPlane for Polygon<T, U, A> where
}
}
Intersection::Inside(line) => {
debug!("\t\tCut across {:?}", line);
log::debug!("\t\tCut across {:?}", line);
let (res_add1, res_add2) = poly.split_with_normal(&line, &self.plane.normal);
let mut front = Vec::new();
let mut back = Vec::new();
@ -75,19 +78,16 @@ impl<T, U, A> BspPlane for Polygon<T, U, A> where
{
let dist = self.plane.signed_distance_sum_to(&sub);
if dist > T::zero() {
trace!("\t\t\tdist {:?} -> front: {:?}", dist, sub);
log::trace!("\t\t\tdist {:?} -> front: {:?}", dist, sub);
front.push(sub)
} else {
trace!("\t\t\tdist {:?} -> back: {:?}", dist, sub);
log::trace!("\t\t\tdist {:?} -> back: {:?}", dist, sub);
back.push(sub)
}
}
PlaneCut::Cut {
front,
back,
}
},
PlaneCut::Cut { front, back }
}
}
}
@ -96,7 +96,6 @@ impl<T, U, A> BspPlane for Polygon<T, U, A> where
}
}
/// Binary Space Partitioning splitter, uses a BSP tree.
pub struct BspSplitter<T, U, A> {
tree: BspNode<Polygon<T, U, A>>,
@ -113,11 +112,18 @@ impl<T, U, A> BspSplitter<T, U, A> {
}
}
impl<T, U, A> Splitter<T, U, A> for BspSplitter<T, U, A> 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,
impl<T, U, A> Splitter<T, U, A> for BspSplitter<T, U, A>
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,
U: fmt::Debug,
A: Copy + fmt::Debug + Default,
{

View file

@ -1,12 +1,10 @@
use {Intersection, NegativeHemisphereError, Plane, Polygon};
use crate::{Intersection, NegativeHemisphereError, Plane, Polygon};
use euclid::{Trig, Rect, Scale, Transform3D, Vector3D};
use euclid::approxeq::ApproxEq;
use euclid::{approxeq::ApproxEq, Rect, Scale, Transform3D, Trig, Vector3D};
use num_traits::{Float, One, Zero};
use std::{fmt, iter, mem, ops};
/// A helper object to clip polygons by a number of planes.
#[derive(Debug)]
pub struct Clipper<T, U, A> {
@ -16,13 +14,20 @@ pub struct Clipper<T, U, A> {
}
impl<
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,
U: fmt::Debug,
A: Copy + fmt::Debug,
> Clipper<T, U, A> {
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,
U: fmt::Debug,
A: Copy + fmt::Debug,
> Clipper<T, U, A>
{
/// Create a new clipper object.
pub fn new() -> Self {
Clipper {
@ -49,33 +54,26 @@ impl<
Some(bounds) => {
let mx = Vector3D::new(t.m11, t.m21, t.m31);
let left = bounds.origin.x;
let plane_left = Plane::from_unnormalized(
mx - mw * Scale::new(left),
t.m41 - t.m44 * left,
)?;
let plane_left =
Plane::from_unnormalized(mx - mw * Scale::new(left), t.m41 - t.m44 * left)?;
let right = bounds.origin.x + bounds.size.width;
let plane_right = Plane::from_unnormalized(
mw * Scale::new(right) - mx,
t.m44 * right - t.m41,
)?;
let plane_right =
Plane::from_unnormalized(mw * Scale::new(right) - mx, t.m44 * right - t.m41)?;
let my = Vector3D::new(t.m12, t.m22, t.m32);
let top = bounds.origin.y;
let plane_top = Plane::from_unnormalized(
my - mw * Scale::new(top),
t.m42 - t.m44 * top,
)?;
let plane_top =
Plane::from_unnormalized(my - mw * Scale::new(top), t.m42 - t.m44 * top)?;
let bottom = bounds.origin.y + bounds.size.height;
let plane_bottom = Plane::from_unnormalized(
mw * Scale::new(bottom) - my,
t.m44 * bottom - t.m42,
)?;
let plane_bottom =
Plane::from_unnormalized(mw * Scale::new(bottom) - my, t.m44 * bottom - t.m42)?;
Some(plane_left
.into_iter()
.chain(plane_right)
.chain(plane_top)
.chain(plane_bottom)
Some(
plane_left
.into_iter()
.chain(plane_right)
.chain(plane_top)
.chain(plane_bottom),
)
}
None => None,
@ -84,8 +82,7 @@ impl<
Ok(bounds_iter_maybe
.into_iter()
.flat_map(|pi| pi)
.chain(plane_positive)
)
.chain(plane_positive))
}
/// Add a clipping plane to the list. The plane will clip everything behind it,
@ -96,7 +93,7 @@ impl<
/// Clip specified polygon by the contained planes, return the fragmented polygons.
pub fn clip(&mut self, polygon: Polygon<T, U, A>) -> &[Polygon<T, U, A>] {
debug!("\tClipping {:?}", polygon);
log::debug!("\tClipping {:?}", polygon);
self.results.clear();
self.results.push(polygon);
@ -112,17 +109,15 @@ impl<
iter::once(poly)
.chain(res1)
.chain(res2)
.filter(|p| clip.signed_distance_sum_to(p) > T::zero())
.filter(|p| clip.signed_distance_sum_to(p) > T::zero()),
);
continue
continue;
}
Intersection::Coplanar => {
let ndot = poly.plane.normal.dot(clip.normal);
clip.offset - ndot * poly.plane.offset
}
Intersection::Outside => {
clip.signed_distance_sum_to(&poly)
}
Intersection::Outside => clip.signed_distance_sum_to(&poly),
};
if dist > T::zero() {
@ -157,7 +152,8 @@ impl<
self.clips.pop();
}
let polys = self.results
let polys = self
.results
.drain(..)
.flat_map(move |poly| poly.transform(transform));
Ok(polys)

View file

@ -10,18 +10,11 @@ the resulting sub-polygons by depth and avoid transparency blending issues.
*/
#![warn(missing_docs)]
extern crate binary_space_partition;
extern crate euclid;
#[macro_use]
extern crate log;
extern crate num_traits;
mod bsp;
mod clip;
mod polygon;
use euclid::{Point3D, Scale, Vector3D};
use euclid::approxeq::ApproxEq;
use euclid::{approxeq::ApproxEq, Point3D, Scale, Vector3D};
use num_traits::{Float, One, Zero};
use std::ops;
@ -30,20 +23,26 @@ pub use self::bsp::BspSplitter;
pub use self::clip::Clipper;
pub use self::polygon::{Intersection, LineProjection, Polygon};
fn is_zero<T>(value: T) -> bool where
T: Copy + Zero + ApproxEq<T> + ops::Mul<T, Output=T> {
fn is_zero<T>(value: T) -> bool
where
T: Copy + Zero + ApproxEq<T> + ops::Mul<T, Output = T>,
{
//HACK: this is rough, but the original Epsilon is too strict
(value * value).approx_eq(&T::zero())
}
fn is_zero_vec<T, U>(vec: Vector3D<T, U>) -> bool where
T: Copy + Zero + ApproxEq<T> +
ops::Add<T, Output=T> + ops::Sub<T, Output=T> + ops::Mul<T, Output=T> {
fn is_zero_vec<T, U>(vec: Vector3D<T, U>) -> bool
where
T: Copy
+ Zero
+ ApproxEq<T>
+ ops::Add<T, Output = T>
+ ops::Sub<T, Output = T>
+ ops::Mul<T, Output = T>,
{
vec.dot(vec).approx_eq(&T::zero())
}
/// A generic line.
#[derive(Debug)]
pub struct Line<T, U> {
@ -53,9 +52,15 @@ pub struct Line<T, U> {
pub dir: Vector3D<T, U>,
}
impl<T, U> Line<T, U> where
T: Copy + One + Zero + ApproxEq<T> +
ops::Add<T, Output=T> + ops::Sub<T, Output=T> + ops::Mul<T, Output=T>
impl<T, U> Line<T, U>
where
T: Copy
+ One
+ Zero
+ ApproxEq<T>
+ ops::Add<T, Output = T>
+ ops::Sub<T, Output = T>
+ ops::Mul<T, Output = T>,
{
/// Check if the line has consistent parameters.
pub fn is_valid(&self) -> bool {
@ -64,17 +69,14 @@ impl<T, U> Line<T, U> where
/// Check if two lines match each other.
pub fn matches(&self, other: &Self) -> bool {
let diff = self.origin - other.origin;
is_zero_vec(self.dir.cross(other.dir)) &&
is_zero_vec(self.dir.cross(diff))
is_zero_vec(self.dir.cross(other.dir)) && is_zero_vec(self.dir.cross(diff))
}
/// Intersect an edge given by the end points.
/// Returns the fraction of the edge where the intersection occurs.
fn intersect_edge(
&self,
edge: ops::Range<Point3D<T, U>>,
) -> Option<T>
where T: ops::Div<T, Output=T>
fn intersect_edge(&self, edge: ops::Range<Point3D<T, U>>) -> Option<T>
where
T: ops::Div<T, Output = T>,
{
let edge_vec = edge.end - edge.start;
let origin_vec = self.origin - edge.start;
@ -93,7 +95,6 @@ impl<T, U> Line<T, U> where
}
}
/// An infinite plane in 3D space, defined by equation:
/// dot(v, normal) + offset = 0
/// When used for plane splitting, it's defining a hemisphere
@ -122,14 +123,22 @@ impl<T: Clone, U> Clone for Plane<T, U> {
pub struct NegativeHemisphereError;
impl<
T: Copy + Zero + One + Float + ApproxEq<T> +
ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
ops::Mul<T, Output=T> + ops::Div<T, Output=T>,
U,
> Plane<T, U> {
T: Copy
+ Zero
+ One
+ Float
+ ApproxEq<T>
+ ops::Sub<T, Output = T>
+ ops::Add<T, Output = T>
+ ops::Mul<T, Output = T>
+ ops::Div<T, Output = T>,
U,
> Plane<T, U>
{
/// Construct a new plane from unnormalized equation.
pub fn from_unnormalized(
normal: Vector3D<T, U>, offset: T
normal: Vector3D<T, U>,
offset: T,
) -> Result<Option<Self>, NegativeHemisphereError> {
let square_len = normal.square_length();
if square_len < T::approx_epsilon() * T::approx_epsilon() {
@ -163,7 +172,9 @@ impl<
/// Compute the distance across the line to the plane plane,
/// starting from the line origin.
pub fn distance_to_line(&self, line: &Line<T, U>) -> T
where T: ops::Neg<Output=T> {
where
T: ops::Neg<Output = T>,
{
self.signed_distance_to(&line.origin) / -self.normal.dot(line.dir)
}
@ -197,11 +208,10 @@ impl<
let w = self.normal.dot(other.normal);
let divisor = T::one() - w * w;
if divisor < T::approx_epsilon() * T::approx_epsilon() {
return None
return None;
}
let origin = Point3D::origin() +
self.normal * ((other.offset * w - self.offset) / divisor) -
other.normal* ((other.offset - self.offset * w) / divisor);
let origin = Point3D::origin() + self.normal * ((other.offset * w - self.offset) / divisor)
- other.normal * ((other.offset - self.offset * w) / divisor);
let cross_dir = self.normal.cross(other.normal);
// note: the cross product isn't too close to zero
@ -214,8 +224,6 @@ impl<
}
}
/// Generic plane splitter interface
pub trait Splitter<T, U, A> {
/// Reset the splitter results.
@ -230,11 +238,7 @@ pub trait Splitter<T, U, A> {
fn sort(&mut self, view: Vector3D<T, U>) -> &[Polygon<T, U, A>];
/// Process a set of polygons at once.
fn solve(
&mut self,
input: &[Polygon<T, U, A>],
view: Vector3D<T, U>,
) -> &[Polygon<T, U, A>]
fn solve(&mut self, input: &[Polygon<T, U, A>], view: Vector3D<T, U>) -> &[Polygon<T, U, A>]
where
T: Clone,
U: Clone,
@ -248,14 +252,13 @@ pub trait Splitter<T, U, A> {
}
}
/// Helper method used for benchmarks and tests.
/// Constructs a 3D grid of polygons.
#[doc(hidden)]
pub fn make_grid(count: usize) -> Vec<Polygon<f32, (), usize>> {
let mut polys: Vec<Polygon<f32, (), usize>> = Vec::with_capacity(count*3);
let mut polys: Vec<Polygon<f32, (), usize>> = Vec::with_capacity(count * 3);
let len = count as f32;
polys.extend((0 .. count).map(|i| Polygon {
polys.extend((0..count).map(|i| Polygon {
points: [
Point3D::new(0.0, i as f32, 0.0),
Point3D::new(len, i as f32, 0.0),
@ -268,7 +271,7 @@ pub fn make_grid(count: usize) -> Vec<Polygon<f32, (), usize>> {
},
anchor: 0,
}));
polys.extend((0 .. count).map(|i| Polygon {
polys.extend((0..count).map(|i| Polygon {
points: [
Point3D::new(i as f32, 0.0, 0.0),
Point3D::new(i as f32, len, 0.0),
@ -281,7 +284,7 @@ pub fn make_grid(count: usize) -> Vec<Polygon<f32, (), usize>> {
},
anchor: 0,
}));
polys.extend((0 .. count).map(|i| Polygon {
polys.extend((0..count).map(|i| Polygon {
points: [
Point3D::new(0.0, 0.0, i as f32),
Point3D::new(len, 0.0, i as f32),

View file

@ -1,26 +1,28 @@
use {Line, Plane, is_zero};
use crate::{is_zero, Line, Plane};
use euclid::default::Point2D;
use euclid::{Transform3D, Point3D, Vector3D, Rect};
use euclid::approxeq::ApproxEq;
use euclid::Trig;
use euclid::{approxeq::ApproxEq, default::Point2D, Point3D, Rect, Transform3D, Trig, Vector3D};
use num_traits::{Float, One, Zero};
use std::{fmt, iter, mem, ops};
/// The projection of a `Polygon` on a line.
pub struct LineProjection<T> {
/// Projected value of each point in the polygon.
pub markers: [T; 4],
}
impl<T> LineProjection<T> where
T : Copy + PartialOrd + ops::Sub<T, Output=T> + ops::Add<T, Output=T>
impl<T> LineProjection<T>
where
T: Copy + PartialOrd + ops::Sub<T, Output = T> + ops::Add<T, Output = T>,
{
/// Get the min/max of the line projection markers.
pub fn get_bounds(&self) -> (T, T) {
let (mut a, mut b, mut c, mut d) = (self.markers[0], self.markers[1], self.markers[2], self.markers[3]);
let (mut a, mut b, mut c, mut d) = (
self.markers[0],
self.markers[1],
self.markers[2],
self.markers[3],
);
// bitonic sort of 4 elements
// we could not just use `min/max` since they require `Ord` bound
//TODO: make it nicer
@ -49,14 +51,21 @@ impl<T> LineProjection<T> where
let span = self.get_bounds();
let other_span = other.get_bounds();
// compute the total footprint
let left = if span.0 < other_span.0 { span.0 } else { other_span.0 };
let right = if span.1 > other_span.1 { span.1 } else { other_span.1 };
let left = if span.0 < other_span.0 {
span.0
} else {
other_span.0
};
let right = if span.1 > other_span.1 {
span.1
} else {
other_span.1
};
// they intersect if the footprint is smaller than the sum
right - left < span.1 - span.0 + other_span.1 - other_span.0
}
}
/// Polygon intersection results.
pub enum Intersection<T> {
/// Polygons are coplanar, including the case of being on the same plane.
@ -84,7 +93,6 @@ impl<T> Intersection<T> {
}
}
/// A convex polygon with 4 points lying on a plane.
#[derive(Debug, PartialEq)]
pub struct Polygon<T, U, A> {
@ -102,9 +110,9 @@ impl<T: Clone, U, A: Copy> Clone for Polygon<T, U, A> {
Polygon {
points: [
self.points[0].clone(),
self.points[1].clone(),
self.points[2].clone(),
self.points[3].clone(),
self.points[1].clone(),
self.points[2].clone(),
self.points[3].clone(),
],
plane: self.plane.clone(),
anchor: self.anchor,
@ -112,27 +120,31 @@ impl<T: Clone, U, A: Copy> Clone for Polygon<T, U, A> {
}
}
impl<T, U, A> Polygon<T, U, A> 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,
impl<T, U, A> Polygon<T, U, A>
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,
U: fmt::Debug,
A: Copy,
{
/// Construct a polygon from points that are already transformed.
/// Return None if the polygon doesn't contain any space.
pub fn from_points(
points: [Point3D<T, U>; 4],
anchor: A,
) -> Option<Self> {
pub fn from_points(points: [Point3D<T, U>; 4], anchor: A) -> Option<Self> {
let edge1 = points[1] - points[0];
let edge2 = points[2] - points[0];
let edge3 = points[3] - points[0];
let edge4 = points[3] - points[1];
if edge2.square_length() < T::epsilon() || edge4.square_length() < T::epsilon() {
return None
return None;
}
// one of them can be zero for redundant polygons produced by plane splitting
@ -148,15 +160,11 @@ impl<T, U, A> Polygon<T, U, A> where
normal_rough2 / square_length2.sqrt()
};
let offset = -points[0].to_vector()
.dot(normal);
let offset = -points[0].to_vector().dot(normal);
Some(Polygon {
points,
plane: Plane {
normal,
offset,
},
plane: Plane { normal, offset },
anchor,
})
}
@ -188,7 +196,7 @@ impl<T, U, A> Polygon<T, U, A> where
anchor: A,
) -> Option<Self>
where
T: Trig + ops::Neg<Output=T>,
T: Trig + ops::Neg<Output = T>,
{
let min = rect.min();
let max = rect.max();
@ -210,7 +218,7 @@ impl<T, U, A> Polygon<T, U, A> where
anchor: A,
) -> Option<Self>
where
T: Trig + ops::Neg<Output=T>,
T: Trig + ops::Neg<Output = T>,
{
let min = rect.min();
let max = rect.max();
@ -230,15 +238,12 @@ impl<T, U, A> Polygon<T, U, A> where
None
} else {
let normal = normal_raw / normal_sql.sqrt();
let offset = -Vector3D::new(transform.m41, transform.m42, transform.m43)
.dot(normal) / transform.m44;
let offset = -Vector3D::new(transform.m41, transform.m42, transform.m43).dot(normal)
/ transform.m44;
Some(Polygon {
points,
plane: Plane {
normal,
offset,
},
plane: Plane { normal, offset },
anchor,
})
}
@ -266,9 +271,7 @@ impl<T, U, A> Polygon<T, U, A> where
}
/// Transform a polygon by an affine transform (preserving straight lines).
pub fn transform<V>(
&self, transform: &Transform3D<T, U, V>
) -> Option<Polygon<T, V, A>>
pub fn transform<V>(&self, transform: &Transform3D<T, U, V>) -> Option<Polygon<T, V, A>>
where
T: Trig,
V: fmt::Debug,
@ -289,7 +292,8 @@ impl<T, U, A> Polygon<T, U, A> where
/// Check if all the points are indeed placed on the plane defined by
/// the normal and offset, and the winding order is consistent.
pub fn is_valid(&self) -> bool {
let is_planar = self.points
let is_planar = self
.points
.iter()
.all(|p| is_zero(self.plane.signed_distance_to(p)));
let edges = [
@ -309,8 +313,8 @@ impl<T, U, A> Polygon<T, U, A> where
/// Check if the polygon doesn't contain any space. This may happen
/// after a sequence of splits, and such polygons should be discarded.
pub fn is_empty(&self) -> bool {
(self.points[0] - self.points[2]).square_length() < T::epsilon() ||
(self.points[1] - self.points[3]).square_length() < T::epsilon()
(self.points[0] - self.points[2]).square_length() < T::epsilon()
|| (self.points[1] - self.points[3]).square_length() < T::epsilon()
}
/// Check if this polygon contains another one.
@ -335,13 +339,13 @@ impl<T, U, A> Polygon<T, U, A> where
/// Compute the line of intersection with an infinite plane.
pub fn intersect_plane(&self, other: &Plane<T, U>) -> Intersection<Line<T, U>> {
if other.are_outside(&self.points) {
debug!("\t\tOutside of the plane");
return Intersection::Outside
log::debug!("\t\tOutside of the plane");
return Intersection::Outside;
}
match self.plane.intersect(&other) {
Some(line) => Intersection::Inside(line),
None => {
debug!("\t\tCoplanar");
log::debug!("\t\tCoplanar");
Intersection::Coplanar
}
}
@ -350,8 +354,8 @@ impl<T, U, A> Polygon<T, U, A> where
/// Compute the line of intersection with another polygon.
pub fn intersect(&self, other: &Self) -> Intersection<Line<T, U>> {
if self.plane.are_outside(&other.points) || other.plane.are_outside(&self.points) {
debug!("\t\tOne is completely outside of the other");
return Intersection::Outside
log::debug!("\t\tOne is completely outside of the other");
return Intersection::Outside;
}
match self.plane.intersect(&other.plane) {
Some(line) => {
@ -361,12 +365,12 @@ impl<T, U, A> Polygon<T, U, A> where
Intersection::Inside(line)
} else {
// projections on the line don't intersect
debug!("\t\tProjection is outside");
log::debug!("\t\tProjection is outside");
Intersection::Outside
}
}
None => {
debug!("\t\tCoplanar");
log::debug!("\t\tCoplanar");
Intersection::Coplanar
}
}
@ -379,7 +383,7 @@ impl<T, U, A> Polygon<T, U, A> where
) -> (Option<Self>, Option<Self>) {
//TODO: can be optimized for when the polygon has a redundant 4th vertex
//TODO: can be simplified greatly if only working with triangles
debug!("\t\tReached complex case [{}, {}]", first.0, second.0);
log::debug!("\t\tReached complex case [{}, {}]", first.0, second.0);
let base = first.0;
assert!(base < self.points.len());
match second.0 - first.0 {
@ -392,7 +396,7 @@ impl<T, U, A> Polygon<T, U, A> where
self.points[(base + 2) & 3],
self.points[base],
],
.. self.clone()
..self.clone()
};
// triangle on the near side of the diagonal
let other2 = Polygon {
@ -402,15 +406,10 @@ impl<T, U, A> Polygon<T, U, A> where
self.points[base],
self.points[base],
],
.. self.clone()
..self.clone()
};
// triangle being cut out
self.points = [
first.1,
self.points[(base + 1) & 3],
second.1,
second.1,
];
self.points = [first.1, self.points[(base + 1) & 3], second.1, second.1];
(Some(other1), Some(other2))
}
2 => {
@ -422,7 +421,7 @@ impl<T, U, A> Polygon<T, U, A> where
self.points[(base + 2) & 3],
second.1,
],
.. self.clone()
..self.clone()
};
// rect on the near side
self.points = [
@ -442,7 +441,7 @@ impl<T, U, A> Polygon<T, U, A> where
self.points[(base + 3) & 3],
second.1,
],
.. self.clone()
..self.clone()
};
// triangle on the far side of the diagonal
let other2 = Polygon {
@ -452,15 +451,10 @@ impl<T, U, A> Polygon<T, U, A> where
self.points[(base + 3) & 3],
self.points[(base + 3) & 3],
],
.. self.clone()
..self.clone()
};
// triangle being cut out
self.points = [
first.1,
second.1,
self.points[base],
self.points[base],
];
self.points = [first.1, second.1, self.points[base], self.points[base]];
(Some(other1), Some(other2))
}
_ => panic!("Unexpected indices {} {}", first.0, second.0),
@ -471,24 +465,29 @@ impl<T, U, A> Polygon<T, U, A> where
/// Will do nothing if the line doesn't belong to the polygon plane.
#[deprecated(note = "Use split_with_normal instead")]
pub fn split(&mut self, line: &Line<T, U>) -> (Option<Self>, Option<Self>) {
debug!("\tSplitting");
log::debug!("\tSplitting");
// check if the cut is within the polygon plane first
if !is_zero(self.plane.normal.dot(line.dir)) ||
!is_zero(self.plane.signed_distance_to(&line.origin)) {
debug!("\t\tDoes not belong to the plane, normal dot={:?}, origin distance={:?}",
self.plane.normal.dot(line.dir), self.plane.signed_distance_to(&line.origin));
return (None, None)
if !is_zero(self.plane.normal.dot(line.dir))
|| !is_zero(self.plane.signed_distance_to(&line.origin))
{
log::debug!(
"\t\tDoes not belong to the plane, normal dot={:?}, origin distance={:?}",
self.plane.normal.dot(line.dir),
self.plane.signed_distance_to(&line.origin)
);
return (None, None);
}
// compute the intersection points for each edge
let mut cuts = [None; 4];
for ((&b, &a), cut) in self.points
for ((&b, &a), cut) in self
.points
.iter()
.cycle()
.skip(1)
.zip(self.points.iter())
.zip(cuts.iter_mut())
{
if let Some(t) = line.intersect_edge(a .. b) {
if let Some(t) = line.intersect_edge(a..b) {
if t >= T::zero() && t < T::one() {
*cut = Some(a + (b - a) * t);
}
@ -499,7 +498,7 @@ impl<T, U, A> Polygon<T, U, A> where
Some(pos) => pos,
None => return (None, None),
};
let second = match cuts[first+1 ..].iter().position(|c| c.is_some()) {
let second = match cuts[first + 1..].iter().position(|c| c.is_some()) {
Some(pos) => first + 1 + pos,
None => return (None, None),
};
@ -514,9 +513,11 @@ impl<T, U, A> Polygon<T, U, A> where
/// forms the side direction here, and figuring out the actual line of split isn't needed.
/// Will do nothing if the line doesn't belong to the polygon plane.
pub fn split_with_normal(
&mut self, line: &Line<T, U>, normal: &Vector3D<T, U>,
&mut self,
line: &Line<T, U>,
normal: &Vector3D<T, U>,
) -> (Option<Self>, Option<Self>) {
debug!("\tSplitting with normal");
log::debug!("\tSplitting with normal");
// figure out which side of the split does each point belong to
let mut sides = [T::zero(); 4];
let (mut cut_positive, mut cut_negative) = (None, None);
@ -548,8 +549,15 @@ impl<T, U, A> Polygon<T, U, A> where
// Given that we are intersecting two straight lines, the triangles on both
// sides of intersection are alike, so distances along the [point0, point1] line
// are proportional to the side vector lengths we just computed: (side0, side1).
let point = (*point0 * side1.abs() + point1.to_vector() * side0.abs()) / (side0 - side1).abs();
debug_assert_eq!(*cut, None);
let point =
(*point0 * side1.abs() + point1.to_vector() * side0.abs()) / (side0 - side1).abs();
if cut.is_some() {
// We don't expect that the direction changes more than once, unless
// the polygon is close to redundant, and we hit precision issues when
// computing the sides.
log::warn!("Splitting failed due to precision issues: {:?}", sides);
break;
}
*cut = Some((i, point));
}
// form new polygons
@ -563,3 +571,27 @@ impl<T, U, A> Polygon<T, U, A> where
}
}
}
#[test]
fn test_split_precision() {
// regression test for https://bugzilla.mozilla.org/show_bug.cgi?id=1678454
let mut polygon = Polygon::<_, (), ()> {
points: [
Point3D::new(300.0102, 150.00958, 0.0),
Point3D::new(606.0, 306.0, 0.0),
Point3D::new(300.21954, 150.11946, 0.0),
Point3D::new(300.08844, 150.05064, 0.0),
],
plane: Plane {
normal: Vector3D::zero(),
offset: 0.0,
},
anchor: (),
};
let line = Line {
origin: Point3D::new(3.0690663, -5.8472385, 0.0),
dir: Vector3D::new(0.8854436, 0.46474677, -0.0),
};
let normal = Vector3D::new(0.46474662, -0.8854434, -0.0006389789);
polygon.split_with_normal(&line, &normal);
}

View file

@ -1,16 +1,13 @@
extern crate euclid;
extern crate plane_split;
use euclid::{point3, rect, vec3};
use euclid::{Angle, Rect, Transform3D};
use euclid::{point3, rect, vec3, Angle, Rect, Transform3D};
use plane_split::{Clipper, Plane, Polygon};
use std::f32::consts::FRAC_PI_4;
#[test]
fn clip_in() {
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), 20.0).unwrap().unwrap();
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), 20.0)
.unwrap()
.unwrap();
let mut clipper = Clipper::new();
clipper.add(plane);
@ -22,7 +19,8 @@ fn clip_in() {
point3(-10.0, 10.0, 0.0),
],
0,
).unwrap();
)
.unwrap();
let results = clipper.clip(poly.clone());
assert_eq!(results[0], poly);
@ -31,7 +29,9 @@ fn clip_in() {
#[test]
fn clip_out() {
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), -20.0).unwrap().unwrap();
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), -20.0)
.unwrap()
.unwrap();
let mut clipper = Clipper::new();
clipper.add(plane);
@ -43,7 +43,8 @@ fn clip_out() {
point3(-10.0, 10.0, 0.0),
],
0,
).unwrap();
)
.unwrap();
let results = clipper.clip(poly);
assert!(results.is_empty());
@ -66,7 +67,8 @@ fn clip_parallel() {
point3(-10.0, 10.0, 0.0),
],
0,
).unwrap();
)
.unwrap();
let results = clipper.clip(poly);
assert!(results.is_empty());
@ -74,7 +76,9 @@ fn clip_parallel() {
#[test]
fn clip_repeat() {
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), 0.0).unwrap().unwrap();
let plane: Plane<f32, ()> = Plane::from_unnormalized(vec3(1.0, 0.0, 1.0), 0.0)
.unwrap()
.unwrap();
let mut clipper = Clipper::new();
clipper.add(plane.clone());
clipper.add(plane.clone());
@ -87,7 +91,8 @@ fn clip_repeat() {
point3(-10.0, 10.0, 0.0),
],
0,
).unwrap();
)
.unwrap();
let results = clipper.clip(poly);
assert_eq!(results.len(), 1);
@ -98,8 +103,7 @@ fn clip_repeat() {
fn clip_transformed() {
let t_rot: Transform3D<f32, (), ()> =
Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
let t_div: Transform3D<f32, (), ()> =
Transform3D::perspective(5.0);
let t_div: Transform3D<f32, (), ()> = Transform3D::perspective(5.0);
let transform = t_rot.then(&t_div);
let polygon = Polygon::from_rect(rect(-10.0, -10.0, 20.0, 20.0), 0);
@ -126,10 +130,7 @@ fn clip_badly_transformed() {
#[test]
fn clip_near_coplanar() {
let tx = Transform3D::<f32, (), ()>::new(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
-960.0, -625.0, 1.0, -1.0,
100.0, -2852.0, 0.0, 1.0,
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -960.0, -625.0, 1.0, -1.0, 100.0, -2852.0, 0.0, 1.0,
);
let mut clipper = Clipper::new();
let polygon = Polygon::from_rect(rect(0.0, 0.0, 1703.0, 4020.0), 0);

View file

@ -1,15 +1,22 @@
extern crate euclid;
extern crate plane_split;
use euclid::{Angle, Rect, Size2D, Transform3D, point2, point3, vec3};
use euclid::approxeq::ApproxEq;
use euclid::{approxeq::ApproxEq, point2, point3, vec3, Angle, Rect, Size2D, Transform3D};
use plane_split::{Intersection, Line, LineProjection, NegativeHemisphereError, Plane, Polygon};
#[test]
fn line_proj_bounds() {
assert_eq!((-5i8, 4), LineProjection { markers: [-5i8, 1, 4, 2] }.get_bounds());
assert_eq!((1f32, 4.0), LineProjection { markers: [4f32, 3.0, 2.0, 1.0] }.get_bounds());
assert_eq!(
(-5i8, 4),
LineProjection {
markers: [-5i8, 1, 4, 2]
}
.get_bounds()
);
assert_eq!(
(1f32, 4.0),
LineProjection {
markers: [4f32, 3.0, 2.0, 1.0]
}
.get_bounds()
);
}
#[test]
@ -77,7 +84,8 @@ fn test_transformed(rect: Rect<f32, ()>, transform: Transform3D<f32, (), ()>) {
assert!(poly.is_valid());
let inv_transform = transform.inverse().unwrap();
let poly2 = Polygon::from_transformed_rect_with_inverse(rect, &transform, &inv_transform, 0).unwrap();
let poly2 =
Polygon::from_transformed_rect_with_inverse(rect, &transform, &inv_transform, 0).unwrap();
assert_eq!(poly.points, poly2.points);
assert!(poly.plane.offset.approx_eq(&poly2.plane.offset));
assert!(poly.plane.normal.dot(poly2.plane.normal).approx_eq(&1.0));
@ -86,8 +94,7 @@ fn test_transformed(rect: Rect<f32, ()>, transform: Transform3D<f32, (), ()>) {
#[test]
fn from_transformed_rect() {
let rect = Rect::new(point2(10.0, 10.0), Size2D::new(20.0, 30.0));
let transform =
Transform3D::rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Angle::radians(5.0))
let transform = Transform3D::rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Angle::radians(5.0))
.pre_translate(vec3(0.0, 0.0, 10.0));
test_transformed(rect, transform);
}
@ -95,9 +102,7 @@ fn from_transformed_rect() {
#[test]
fn from_transformed_rect_perspective() {
let rect = Rect::new(point2(-10.0, -5.0), Size2D::new(20.0, 30.0));
let mut transform =
Transform3D::perspective(400.0)
.pre_translate(vec3(0.0, 0.0, 100.0));
let mut transform = Transform3D::perspective(400.0).pre_translate(vec3(0.0, 0.0, 100.0));
transform.m44 = 0.7; //for fun
test_transformed(rect, transform);
}
@ -129,17 +134,9 @@ fn are_outside() {
normal: vec3(0.0, 0.0, 1.0),
offset: -1.0,
};
assert!(plane.are_outside(&[
point3(0.0, 0.0, 1.1),
point3(1.0, 1.0, 2.0),
]));
assert!(plane.are_outside(&[
point3(0.5, 0.5, 1.0),
]));
assert!(!plane.are_outside(&[
point3(0.0, 0.0, 1.0),
point3(0.0, 0.0, -1.0),
]));
assert!(plane.are_outside(&[point3(0.0, 0.0, 1.1), point3(1.0, 1.0, 2.0),]));
assert!(plane.are_outside(&[point3(0.5, 0.5, 1.0),]));
assert!(!plane.are_outside(&[point3(0.0, 0.0, 1.0), point3(0.0, 0.0, -1.0),]));
}
#[test]
@ -179,8 +176,14 @@ fn intersect() {
};
assert!(intersection.is_valid());
// confirm the origin is on both planes
assert!(poly_a.plane.signed_distance_to(&intersection.origin).approx_eq(&0.0));
assert!(poly_b.plane.signed_distance_to(&intersection.origin).approx_eq(&0.0));
assert!(poly_a
.plane
.signed_distance_to(&intersection.origin)
.approx_eq(&0.0));
assert!(poly_b
.plane
.signed_distance_to(&intersection.origin)
.approx_eq(&0.0));
// confirm the direction is coplanar to both planes
assert!(poly_a.plane.normal.dot(intersection.dir).approx_eq(&0.0));
assert!(poly_b.plane.normal.dot(intersection.dir).approx_eq(&0.0));
@ -218,11 +221,7 @@ fn intersect() {
assert!(poly_a.intersect(&poly_d).is_outside());
}
fn test_cut(
poly_base: &Polygon<f32, (), usize>,
extra_count: u8,
line: Line<f32, ()>,
) {
fn test_cut(poly_base: &Polygon<f32, (), usize>, extra_count: u8, line: Line<f32, ()>) {
assert!(line.is_valid());
let normal = poly_base.plane.normal.cross(line.dir).normalize();
@ -256,40 +255,64 @@ fn split() {
};
// non-intersecting line
test_cut(&poly, 0, Line {
origin: point3(0.0, 1.0, 0.5),
dir: vec3(0.0, 1.0, 0.0),
});
test_cut(
&poly,
0,
Line {
origin: point3(0.0, 1.0, 0.5),
dir: vec3(0.0, 1.0, 0.0),
},
);
// simple cut (diff=2)
test_cut(&poly, 1, Line {
origin: point3(0.0, 1.0, 0.5),
dir: vec3(1.0, 0.0, 0.0),
});
test_cut(
&poly,
1,
Line {
origin: point3(0.0, 1.0, 0.5),
dir: vec3(1.0, 0.0, 0.0),
},
);
// complex cut (diff=1, wrapped)
test_cut(&poly, 2, Line {
origin: point3(0.0, 1.0, 0.5),
dir: vec3(0.5f32.sqrt(), 0.0, -0.5f32.sqrt()),
});
test_cut(
&poly,
2,
Line {
origin: point3(0.0, 1.0, 0.5),
dir: vec3(0.5f32.sqrt(), 0.0, -0.5f32.sqrt()),
},
);
// complex cut (diff=1, non-wrapped)
test_cut(&poly, 2, Line {
origin: point3(0.5, 1.0, 0.0),
dir: vec3(0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
});
test_cut(
&poly,
2,
Line {
origin: point3(0.5, 1.0, 0.0),
dir: vec3(0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
},
);
// complex cut (diff=3)
test_cut(&poly, 2, Line {
origin: point3(0.5, 1.0, 0.0),
dir: vec3(-0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
});
test_cut(
&poly,
2,
Line {
origin: point3(0.5, 1.0, 0.0),
dir: vec3(-0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
},
);
// perfect diagonal
test_cut(&poly, 1, Line {
origin: point3(0.0, 1.0, 0.0),
dir: vec3(0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
});
test_cut(
&poly,
1,
Line {
origin: point3(0.0, 1.0, 0.0),
dir: vec3(0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
},
);
}
#[test]
@ -303,8 +326,11 @@ fn plane_unnormalized() {
assert_eq!(plane, Err(NegativeHemisphereError));
plane = Plane::from_unnormalized(vec3(-3.0, 4.0, 0.0), 2.0);
assert_eq!(plane, Ok(Some(Plane {
normal: vec3(-3.0/5.0, 4.0/5.0, 0.0),
offset: 2.0/5.0,
})));
assert_eq!(
plane,
Ok(Some(Plane {
normal: vec3(-3.0 / 5.0, 4.0 / 5.0, 0.0),
offset: 2.0 / 5.0,
}))
);
}

View file

@ -1,17 +1,12 @@
extern crate binary_space_partition;
extern crate euclid;
extern crate plane_split;
use std::f32::consts::FRAC_PI_4;
use binary_space_partition::{Plane as Plane_, PlaneCut};
use euclid::{Angle, Transform3D, Rect, rect, vec3};
use plane_split::{BspSplitter, Polygon, Splitter, make_grid};
use euclid::{rect, vec3, Angle, Rect, Transform3D};
use plane_split::{make_grid, BspSplitter, Polygon, Splitter};
use std::f32::consts::FRAC_PI_4;
fn grid_impl(count: usize, splitter: &mut dyn Splitter<f32, (), usize>) {
let polys = make_grid(count);
let result = splitter.solve(&polys, vec3(0.0, 0.0, 1.0));
assert_eq!(result.len(), count + count*count + count*count*count);
assert_eq!(result.len(), count + count * count + count * count * count);
}
#[test]
@ -19,7 +14,6 @@ fn grid_bsp() {
grid_impl(2, &mut BspSplitter::new());
}
fn sort_rotation(splitter: &mut dyn Splitter<f32, (), usize>) {
let transform0: Transform3D<f32, (), ()> =
Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
@ -32,9 +26,12 @@ fn sort_rotation(splitter: &mut dyn Splitter<f32, (), usize>) {
let p1 = Polygon::from_transformed_rect(rect, transform0, 0);
let p2 = Polygon::from_transformed_rect(rect, transform1, 1);
let p3 = Polygon::from_transformed_rect(rect, transform2, 2);
assert!(p1.is_some() && p2.is_some() && p3.is_some(), "Cannot construct transformed polygons");
assert!(
p1.is_some() && p2.is_some() && p3.is_some(),
"Cannot construct transformed polygons"
);
let polys = [ p1.unwrap(), p2.unwrap(), p3.unwrap() ];
let polys = [p1.unwrap(), p2.unwrap(), p3.unwrap()];
let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
let ids: Vec<_> = result.iter().map(|poly| poly.anchor).collect();
assert_eq!(&ids, &[2, 1, 0, 1, 2]);
@ -45,16 +42,19 @@ fn rotation_bsp() {
sort_rotation(&mut BspSplitter::new());
}
fn sort_trivial(splitter: &mut dyn Splitter<f32, (), usize>) {
let anchors: Vec<_> = (0usize .. 10).collect();
let anchors: Vec<_> = (0usize..10).collect();
let rect: Rect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
let polys: Vec<_> = anchors.iter().map(|&anchor| {
let transform: Transform3D<f32, (), ()> = Transform3D::translation(0.0, 0.0, anchor as f32);
let poly = Polygon::from_transformed_rect(rect, transform, anchor);
assert!(poly.is_some(), "Cannot construct transformed polygons");
poly.unwrap()
}).collect();
let polys: Vec<_> = anchors
.iter()
.map(|&anchor| {
let transform: Transform3D<f32, (), ()> =
Transform3D::translation(0.0, 0.0, anchor as f32);
let poly = Polygon::from_transformed_rect(rect, transform, anchor);
assert!(poly.is_some(), "Cannot construct transformed polygons");
poly.unwrap()
})
.collect();
let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
let anchors1: Vec<_> = result.iter().map(|p| p.anchor).collect();
@ -68,7 +68,8 @@ fn sort_external(splitter: &mut dyn Splitter<f32, (), usize>) {
let rect0: Rect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
let poly0 = Polygon::from_rect(rect0, 0);
let poly1 = {
let transform0: Transform3D<f32, (), ()> = Transform3D::rotation(1.0, 0.0, 0.0, Angle::radians(2.0 * FRAC_PI_4));
let transform0: Transform3D<f32, (), ()> =
Transform3D::rotation(1.0, 0.0, 0.0, Angle::radians(2.0 * FRAC_PI_4));
let transform1: Transform3D<f32, (), ()> = Transform3D::translation(0.0, 100.0, 0.0);
Polygon::from_transformed_rect(rect0, transform0.then(&transform1), 1).unwrap()
};
@ -119,13 +120,19 @@ fn test_cut() {
// test grouping front
poly2.plane.offset += 0.1;
match poly.cut(poly2.clone()) {
PlaneCut::Cut { ref front, ref back } => assert_eq!((front.len(), back.len()), (1, 0)),
PlaneCut::Cut {
ref front,
ref back,
} => assert_eq!((front.len(), back.len()), (1, 0)),
PlaneCut::Sibling(_) => panic!("wrong sibling!"),
}
// test grouping back
poly2.plane.normal *= -1.0;
match poly.cut(poly2.clone()) {
PlaneCut::Cut { ref front, ref back } => assert_eq!((front.len(), back.len()), (0, 1)),
PlaneCut::Cut {
ref front,
ref back,
} => assert_eq!((front.len(), back.len()), (0, 1)),
PlaneCut::Sibling(_) => panic!("wrong sibling!"),
}
}