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