Make bilinear patch splitting a little smarter.
This way it doesn't just keep splitting on the same axis repeatedly, creating narrow, long patches..
This commit is contained in:
parent
95e7d6bdea
commit
ecbdf5d609
31
src/lerp.rs
31
src/lerp.rs
|
@ -3,7 +3,7 @@
|
||||||
use math3d::{Matrix4x4, Normal, Point, Vector};
|
use math3d::{Matrix4x4, Normal, Point, Vector};
|
||||||
|
|
||||||
/// Trait for allowing a type to be linearly interpolated.
|
/// Trait for allowing a type to be linearly interpolated.
|
||||||
pub trait Lerp {
|
pub trait Lerp: Copy {
|
||||||
fn lerp(self, other: Self, alpha: f32) -> Self;
|
fn lerp(self, other: Self, alpha: f32) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ pub fn lerp<T: Lerp>(a: T, b: T, alpha: f32) -> T {
|
||||||
|
|
||||||
/// Interpolates a slice of data as if each adjecent pair of elements
|
/// Interpolates a slice of data as if each adjecent pair of elements
|
||||||
/// represent a linear segment.
|
/// represent a linear segment.
|
||||||
pub fn lerp_slice<T: Lerp + Copy>(s: &[T], alpha: f32) -> T {
|
pub fn lerp_slice<T: Lerp>(s: &[T], alpha: f32) -> T {
|
||||||
debug_assert!(!s.is_empty());
|
debug_assert!(!s.is_empty());
|
||||||
debug_assert!(alpha >= 0.0);
|
debug_assert!(alpha >= 0.0);
|
||||||
debug_assert!(alpha <= 1.0);
|
debug_assert!(alpha <= 1.0);
|
||||||
|
@ -73,6 +73,33 @@ impl<T: Lerp> Lerp for (T, T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Lerp> Lerp for [T; 2] {
|
||||||
|
fn lerp(self, other: Self, alpha: f32) -> Self {
|
||||||
|
[self[0].lerp(other[0], alpha), self[1].lerp(other[1], alpha)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Lerp> Lerp for [T; 3] {
|
||||||
|
fn lerp(self, other: Self, alpha: f32) -> Self {
|
||||||
|
[
|
||||||
|
self[0].lerp(other[0], alpha),
|
||||||
|
self[1].lerp(other[1], alpha),
|
||||||
|
self[2].lerp(other[2], alpha),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Lerp> Lerp for [T; 4] {
|
||||||
|
fn lerp(self, other: Self, alpha: f32) -> Self {
|
||||||
|
[
|
||||||
|
self[0].lerp(other[0], alpha),
|
||||||
|
self[1].lerp(other[1], alpha),
|
||||||
|
self[2].lerp(other[2], alpha),
|
||||||
|
self[3].lerp(other[3], alpha),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Lerp for glam::Vec4 {
|
impl Lerp for glam::Vec4 {
|
||||||
fn lerp(self, other: glam::Vec4, alpha: f32) -> glam::Vec4 {
|
fn lerp(self, other: glam::Vec4, alpha: f32) -> glam::Vec4 {
|
||||||
(self * (1.0 - alpha)) + (other * alpha)
|
(self * (1.0 - alpha)) + (other * alpha)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use super::{point_order, PointOrder, Splitable, MAX_EDGE_DICE};
|
use super::{point_order, PointOrder, Splitable, MAX_EDGE_DICE};
|
||||||
use crate::{lerp::lerp, math::Point};
|
use crate::{
|
||||||
|
lerp::{lerp, lerp_slice},
|
||||||
|
math::Point,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct BilinearPatch<'a> {
|
pub struct BilinearPatch<'a> {
|
||||||
|
@ -41,8 +44,8 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
|
||||||
where
|
where
|
||||||
F: Fn(Point, Point) -> f32,
|
F: Fn(Point, Point) -> f32,
|
||||||
{
|
{
|
||||||
// Get the points of the sub-patch.
|
// Get the points of the sub-patch at time 0.5.
|
||||||
let patch = self.original.control_points[0];
|
let patch = lerp_slice(self.original.control_points, 0.5);
|
||||||
let points = [
|
let points = [
|
||||||
bilerp_point(patch, self.clip[0]),
|
bilerp_point(patch, self.clip[0]),
|
||||||
bilerp_point(patch, self.clip[1]),
|
bilerp_point(patch, self.clip[1]),
|
||||||
|
@ -58,9 +61,38 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
|
||||||
metric(points[3], points[0]),
|
metric(points[3], points[0]),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Do the split, if needed.
|
// Find an edge to split, if any.
|
||||||
for i in 0..4 {
|
let split_edge_index = {
|
||||||
if self.must_split[i] || edge_metric[i] > MAX_EDGE_DICE as f32 {
|
// Find the "longest" edge in terms of the metric.
|
||||||
|
let (edge_i, m) = edge_metric
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.max_by(|a, b| {
|
||||||
|
if a.1 > b.1 {
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
} else {
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Return an edge to split, if a split is needed.
|
||||||
|
if *m > MAX_EDGE_DICE as f32 {
|
||||||
|
// Split needed because of over-long edge.
|
||||||
|
Some(edge_i)
|
||||||
|
} else {
|
||||||
|
// Return the the first edge with "must_split" set, if any.
|
||||||
|
// Otherwise returns `None`.
|
||||||
|
self.must_split
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|a| *a.1)
|
||||||
|
.map(|a| a.0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do the split if needed
|
||||||
|
if let Some(i) = split_edge_index {
|
||||||
let edge_1 = (i, (i + 1) % 4);
|
let edge_1 = (i, (i + 1) % 4);
|
||||||
let edge_2 = ((i + 2) % 4, (i + 3) % 4);
|
let edge_2 = ((i + 2) % 4, (i + 3) % 4);
|
||||||
let new_must_split = {
|
let new_must_split = {
|
||||||
|
@ -72,9 +104,8 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
|
||||||
|
|
||||||
let midpoint_1 = lerp(self.clip[edge_1.0], self.clip[edge_1.1], 0.5);
|
let midpoint_1 = lerp(self.clip[edge_1.0], self.clip[edge_1.1], 0.5);
|
||||||
let midpoint_2 = {
|
let midpoint_2 = {
|
||||||
let alpha = if self.must_split[edge_2.0]
|
let alpha =
|
||||||
|| edge_metric[edge_2.0] > MAX_EDGE_DICE as f32
|
if self.must_split[edge_2.0] || edge_metric[edge_2.0] > MAX_EDGE_DICE as f32 {
|
||||||
{
|
|
||||||
0.5
|
0.5
|
||||||
} else {
|
} else {
|
||||||
let edge_2_dice_rate = edge_metric[edge_2.0].ceil();
|
let edge_2_dice_rate = edge_metric[edge_2.0].ceil();
|
||||||
|
@ -99,11 +130,10 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
|
||||||
patch_2.clip[edge_1.0] = midpoint_1;
|
patch_2.clip[edge_1.0] = midpoint_1;
|
||||||
patch_2.clip[edge_2.1] = midpoint_2;
|
patch_2.clip[edge_2.1] = midpoint_2;
|
||||||
|
|
||||||
return Some((patch_1, patch_2));
|
Some((patch_1, patch_2))
|
||||||
}
|
} else {
|
||||||
}
|
// No split
|
||||||
|
|
||||||
// No splitting needed to be done.
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user