re-use bezier buffers for all sliders

This commit is contained in:
MaxOhn
2021-11-06 16:33:18 +01:00
parent 6a8c1eb532
commit 4b58c7466a
4 changed files with 64 additions and 32 deletions
+51 -19
View File
@@ -11,6 +11,13 @@ const BEZIER_TOLERANCE: f32 = 0.25;
const CATMULL_DETAIL: usize = 50;
const CIRCULAR_ARC_TOLERANCE: f32 = 0.1;
#[derive(Default)]
pub(crate) struct CurveBuffers {
vertices: Vec<Pos2>,
bezier: BezierBuffers,
}
#[derive(Default)]
struct BezierBuffers {
buf1: Vec<Pos2>,
buf2: Vec<Pos2>,
@@ -18,12 +25,22 @@ struct BezierBuffers {
}
impl BezierBuffers {
fn new(len: usize) -> Self {
Self {
buf1: vec![Pos2::zero(); len],
buf2: vec![Pos2::zero(); len],
buf3: vec![Pos2::zero(); len],
/// Fill the buffers with new elements until a
/// length of `len` is reached. Does nothing if `len`
/// is already smaller than the current buffer size.
fn extend_exact(&mut self, len: usize) {
if len <= self.buf1.len() {
return;
}
let additional = len - self.buf1.len();
self.buf1
.extend(iter::repeat(Pos2::zero()).take(additional));
self.buf2
.extend(iter::repeat(Pos2::zero()).take(additional));
self.buf3
.extend(iter::repeat(Pos2::zero()).take(additional));
}
}
@@ -41,8 +58,12 @@ pub(crate) struct Curve {
}
impl Curve {
pub(crate) fn new(points: &[PathControlPoint], expected_len: f32, buf: &mut Vec<Pos2>) -> Self {
let mut path = Self::calculate_path(points, buf);
pub(crate) fn new(
points: &[PathControlPoint],
expected_len: f32,
bufs: &mut CurveBuffers,
) -> Self {
let mut path = Self::calculate_path(points, bufs);
let lengths = Self::calculate_length(points, &mut path, expected_len);
Self { path, lengths }
@@ -98,11 +119,13 @@ impl Curve {
p0 + (p1 - p0) * w
}
fn calculate_path(points: &[PathControlPoint], vertices: &mut Vec<Pos2>) -> Vec<Pos2> {
fn calculate_path(points: &[PathControlPoint], bufs: &mut CurveBuffers) -> Vec<Pos2> {
if points.is_empty() {
return Vec::new();
}
let CurveBuffers { vertices, bezier } = bufs;
vertices.clear();
vertices.extend(points.iter().map(|p| p.pos));
@@ -118,7 +141,7 @@ impl Curve {
let segment_vertices = &vertices[start..i + 1];
let segment_kind = points[start].kind.unwrap_or(PathType::Linear);
Self::calculate_subpath(&mut path, segment_vertices, segment_kind);
Self::calculate_subpath(&mut path, segment_vertices, segment_kind, bezier);
// * Start the new segment at the current vertex
start = i;
@@ -194,25 +217,32 @@ impl Curve {
cumulative_len
}
fn calculate_subpath(path: &mut Vec<Pos2>, sub_points: &[Pos2], kind: PathType) {
fn calculate_subpath(
path: &mut Vec<Pos2>,
sub_points: &[Pos2],
kind: PathType,
bufs: &mut BezierBuffers,
) {
match kind {
PathType::Bezier => Self::approximate_bezier(path, sub_points),
PathType::Bezier => Self::approximate_bezier(path, sub_points, bufs),
PathType::Catmull => Self::approximate_catmull(path, sub_points),
PathType::Linear => Self::approximate_linear(path, sub_points),
PathType::PerfectCurve => {
if let [a, b, c] = sub_points {
Self::approximate_circular_arc(path, *a, *b, *c)
} else {
Self::approximate_bezier(path, sub_points)
if Self::approximate_circular_arc(path, *a, *b, *c) {
return;
}
}
Self::approximate_bezier(path, sub_points, bufs)
}
}
}
fn approximate_bezier(path: &mut Vec<Pos2>, points: &[Pos2]) {
let mut bufs = BezierBuffers::new(points.len()); // TODO: argument?
fn approximate_bezier(path: &mut Vec<Pos2>, points: &[Pos2], bufs: &mut BezierBuffers) {
bufs.extend_exact(points.len());
Self::approximate_bspline(path, points, &mut bufs);
Self::approximate_bspline(path, points, bufs);
}
fn approximate_catmull(path: &mut Vec<Pos2>, points: &[Pos2]) {
@@ -239,10 +269,10 @@ impl Curve {
path.extend(points)
}
fn approximate_circular_arc(path: &mut Vec<Pos2>, a: Pos2, b: Pos2, c: Pos2) {
fn approximate_circular_arc(path: &mut Vec<Pos2>, a: Pos2, b: Pos2, c: Pos2) -> bool {
let pr = match Self::circular_arc_properties(a, b, c) {
Some(pr) => pr,
None => return Self::approximate_bezier(path, &[a, b, c]),
None => return false,
};
// * We select the amount of points for the approximation by requiring the discrete curvature
@@ -272,6 +302,8 @@ impl Curve {
});
path.extend(subpath);
true
}
fn approximate_bspline(path: &mut Vec<Pos2>, points: &[Pos2], bufs: &mut BezierBuffers) {
+5 -5
View File
@@ -13,7 +13,7 @@ pub use pp::*;
use slider_state::SliderState;
use crate::{
curve::Curve,
curve::{Curve, CurveBuffers},
parse::{HitObjectKind, Pos2},
Beatmap, Mods, Strains,
};
@@ -45,7 +45,7 @@ pub fn stars(
let with_hr = mods.hr();
let mut ticks = Vec::new(); // using the same buffer for all sliders
let mut slider_state = SliderState::new(map);
let mut curve_buf = Vec::new();
let mut curve_bufs = CurveBuffers::default();
let mut fruits = 0;
let mut droplets = 0;
@@ -94,7 +94,7 @@ pub fn stars(
/ 100.0;
// Build the curve w.r.t. the curve points
let curve = Curve::new(control_points, *pixel_len, &mut curve_buf);
let curve = Curve::new(control_points, *pixel_len, &mut curve_bufs);
let mut current_distance = tick_distance;
let time_add = duration * (tick_distance / (*pixel_len * *repeats as f32));
@@ -286,7 +286,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
let with_hr = mods.hr();
let mut ticks = Vec::new(); // using the same buffer for all sliders
let mut slider_state = SliderState::new(map);
let mut curve_buf = Vec::new();
let mut curve_bufs = CurveBuffers::default();
// BUG: Incorrect object order on 2B maps that have fruits within sliders
let mut hit_objects = map
@@ -329,7 +329,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
/ 100.0;
// Build the curve w.r.t. the curve points
let curve = Curve::new(control_points, *pixel_len, &mut curve_buf);
let curve = Curve::new(control_points, *pixel_len, &mut curve_bufs);
let mut current_distance = tick_distance;
let time_add = duration * (tick_distance / (*pixel_len * *repeats as f32));
+5 -5
View File
@@ -21,7 +21,7 @@ use skill::Skill;
use skill_kind::SkillKind;
use slider_state::SliderState;
use crate::{Beatmap, Mods, Strains};
use crate::{curve::CurveBuffers, Beatmap, Mods, Strains};
const OBJECT_RADIUS: f32 = 64.0;
const SECTION_LEN: f32 = 400.0;
@@ -68,7 +68,7 @@ pub fn stars(
}
let mut slider_state = SliderState::new(map);
let mut curve_buf = Vec::new();
let mut curve_bufs = CurveBuffers::default();
let mut ticks_buf = Vec::new();
let mut hit_objects = map
@@ -84,7 +84,7 @@ pub fn stars(
&mut ticks_buf,
&mut diff_attributes,
&mut slider_state,
&mut curve_buf,
&mut curve_bufs,
)
})
.map(|mut h| {
@@ -241,7 +241,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
let mut slider_state = SliderState::new(map);
let mut ticks_buf = Vec::new();
let mut curve_buf = Vec::new();
let mut curve_bufs = CurveBuffers::default();
let mut hit_objects = map.hit_objects.iter().filter_map(|h| {
OsuObject::new(
@@ -252,7 +252,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
&mut ticks_buf,
&mut diff_attributes,
&mut slider_state,
&mut curve_buf,
&mut curve_bufs,
)
});
+3 -3
View File
@@ -2,7 +2,7 @@ use super::super::super::DifficultyAttributes;
use super::slider_state::SliderState;
use crate::{
curve::Curve,
curve::{Curve, CurveBuffers},
parse::{HitObject, HitObjectKind, Pos2},
Beatmap,
};
@@ -35,7 +35,7 @@ impl OsuObject {
ticks: &mut Vec<f32>,
attributes: &mut DifficultyAttributes,
slider_state: &mut SliderState,
slider_buf: &mut Vec<Pos2>,
curve_bufs: &mut CurveBuffers,
) -> Option<Self> {
attributes.max_combo += 1; // hitcircle, slider head, or spinner
@@ -68,7 +68,7 @@ impl OsuObject {
}
// Build the curve w.r.t. the curve points
let curve = Curve::new(control_points, *pixel_len, slider_buf);
let curve = Curve::new(control_points, *pixel_len, curve_bufs);
let velocity =
(BASE_SCORING_DISTANCE * map.slider_mult * slider_state.slider_velocity)