如何使用贝塞尔曲线对 N 维向量的点进行插值?
How to interpolate points of an N-dimensional vector using Bézier curves?
我创建了一个音频频谱可视化工具,我需要更改频率的位置,以便较低的频率比较高的频率占据更多 space。
因此,它们之间有 spaces,我必须对它们进行插值。我实现了线性插值:
#[derive(Copy, Clone, Debug)]
struct Frequency {
pub volume: f32,
// unimportant
pub freq: f32,
/// Relative position of single frequency in range (0..=1)
///
/// Used to make lower freqs occupy more space than higher ones, to mimic human hearing
pub position: f32,
}
impl Frequency {
pub fn empty() -> Self {
Frequency {
volume: 0.0,
freq: 0.0,
position: 0.0,
}
}
}
fn interpolate(freqs: Vec<Frequency>, resolution: usize) -> Vec<Frequency> {
let mut o_buf: Vec<Frequency> = vec![Frequency::empty(); resolution];
let mut freqs = freqs.iter().peekable();
'interpolation: loop {
let start_freq: &Frequency = match freqs.next() {
Some(f) => f,
None => break 'interpolation,
};
let start: usize = (start_freq.position * o_buf.len() as f32) as usize;
let end_freq = match freqs.peek() {
Some(f) => f,
None => break 'interpolation,
};
let end: usize = (end_freq.position * o_buf.len() as f32) as usize;
if start < resolution && end < resolution {
for i in start..=end {
let pos: usize = i - start;
let gap_size = end - start;
let mut percentage: f32 = pos as f32 / gap_size as f32;
if percentage.is_nan() {
percentage = 0.5
} // if gap_size = 0
// interpolation
let volume: f32 =
(start_freq.volume * (1.0 - percentage)) + (end_freq.volume * percentage);
let position: f32 =
(start_freq.position * (1.0 - percentage)) + (end_freq.position * percentage);
let freq: f32 =
(start_freq.freq * (1.0 - percentage)) + (end_freq.freq * percentage);
if o_buf.len() > i && o_buf[i].volume < volume {
o_buf[i] = Frequency {
volume,
position,
freq,
};
}
}
}
}
o_buf
}
我对插值和贝塞尔曲线了解不多。能否修改此函数,使其不再线性插值而是使用贝塞尔曲线?
尝试使用这个三次公式
start_freq.position + (-2 * t ^ 3 + 3 * t ^ 2)(end_freq.position - start_freq.position);
t
是从 0.0 到 1.0 的百分比。
在深入挖掘之后,我发现了一个更好的用例方法。
感谢 this site's 的解释,函数现在看起来像这样:
#[derive(Copy, Clone, Debug)]
struct Frequency {
pub volume: f32,
// unimportant
pub freq: f32,
/// Relative position of single frequency in range (0..=1)
///
/// Used to make lower freqs occupy more space than higher ones, to mimic human hearing
pub position: f32,
}
impl Frequency {
pub fn empty() -> Self {
Frequency {
volume: 0.0,
freq: 0.0,
position: 0.0,
}
}
}
fn interpolate(mut fb: Vec<Frequency>, resolution: usize) -> Vec<Frequency> {
let mut o_buf: Vec<Frequency> = vec![Frequency::empty(); resolution];
fb.insert(0, Frequency::empty());
fb.push( Frequency::empty() );
if fb.len() > 4 {
for i in 0..fb.len() - 3 {
let y0 = fb[i].volume;
let y1 = fb[i+1].volume;
let y2 = fb[i+2].volume;
let y3 = fb[i+3].volume;
let start = ( fb[i+1].position * o_buf.len() as f32 ) as usize;
let end = ( fb[i+2].position * o_buf.len() as f32 ) as usize;
if start < resolution && end < resolution {
for i in start..=end {
let pos: usize = i - start;
let gap_size = end - start;
let mut percentage: f32 = pos as f32 / gap_size as f32;
if percentage.is_nan() {percentage = 0.5}
let t = percentage;
let t2 = percentage.powi(2);
// explanation: http://paulbourke.net/miscellaneous/interpolation/
// cubic volume interpolation
let a0 = y3 - y2 - y0 + y1;
let a1 = y0 - y1 - a0;
let a2 = y2 - y0;
let a3 = y1;
// math magic
let volume = a0 * t * t2 + a1 * t2 + a2 * t + a3;
// linear freq interpolation
let f1 = fb[i+1].freq;
let f2 = fb[i+2].freq;
let freq = f1 * (1.0 - t) + f2 * t;
// only if volume of frequency is louder to preserve information
if o_buf.len() > i && o_buf[i].volume < volume {
o_buf[i] = Frequency {
volume,
position: 0.0, // unneccessary
freq,
};
}
}
}
}
}
o_buf
}
我创建了一个音频频谱可视化工具,我需要更改频率的位置,以便较低的频率比较高的频率占据更多 space。
因此,它们之间有 spaces,我必须对它们进行插值。我实现了线性插值:
#[derive(Copy, Clone, Debug)]
struct Frequency {
pub volume: f32,
// unimportant
pub freq: f32,
/// Relative position of single frequency in range (0..=1)
///
/// Used to make lower freqs occupy more space than higher ones, to mimic human hearing
pub position: f32,
}
impl Frequency {
pub fn empty() -> Self {
Frequency {
volume: 0.0,
freq: 0.0,
position: 0.0,
}
}
}
fn interpolate(freqs: Vec<Frequency>, resolution: usize) -> Vec<Frequency> {
let mut o_buf: Vec<Frequency> = vec![Frequency::empty(); resolution];
let mut freqs = freqs.iter().peekable();
'interpolation: loop {
let start_freq: &Frequency = match freqs.next() {
Some(f) => f,
None => break 'interpolation,
};
let start: usize = (start_freq.position * o_buf.len() as f32) as usize;
let end_freq = match freqs.peek() {
Some(f) => f,
None => break 'interpolation,
};
let end: usize = (end_freq.position * o_buf.len() as f32) as usize;
if start < resolution && end < resolution {
for i in start..=end {
let pos: usize = i - start;
let gap_size = end - start;
let mut percentage: f32 = pos as f32 / gap_size as f32;
if percentage.is_nan() {
percentage = 0.5
} // if gap_size = 0
// interpolation
let volume: f32 =
(start_freq.volume * (1.0 - percentage)) + (end_freq.volume * percentage);
let position: f32 =
(start_freq.position * (1.0 - percentage)) + (end_freq.position * percentage);
let freq: f32 =
(start_freq.freq * (1.0 - percentage)) + (end_freq.freq * percentage);
if o_buf.len() > i && o_buf[i].volume < volume {
o_buf[i] = Frequency {
volume,
position,
freq,
};
}
}
}
}
o_buf
}
我对插值和贝塞尔曲线了解不多。能否修改此函数,使其不再线性插值而是使用贝塞尔曲线?
尝试使用这个三次公式
start_freq.position + (-2 * t ^ 3 + 3 * t ^ 2)(end_freq.position - start_freq.position);
t
是从 0.0 到 1.0 的百分比。
在深入挖掘之后,我发现了一个更好的用例方法。
感谢 this site's 的解释,函数现在看起来像这样:
#[derive(Copy, Clone, Debug)]
struct Frequency {
pub volume: f32,
// unimportant
pub freq: f32,
/// Relative position of single frequency in range (0..=1)
///
/// Used to make lower freqs occupy more space than higher ones, to mimic human hearing
pub position: f32,
}
impl Frequency {
pub fn empty() -> Self {
Frequency {
volume: 0.0,
freq: 0.0,
position: 0.0,
}
}
}
fn interpolate(mut fb: Vec<Frequency>, resolution: usize) -> Vec<Frequency> {
let mut o_buf: Vec<Frequency> = vec![Frequency::empty(); resolution];
fb.insert(0, Frequency::empty());
fb.push( Frequency::empty() );
if fb.len() > 4 {
for i in 0..fb.len() - 3 {
let y0 = fb[i].volume;
let y1 = fb[i+1].volume;
let y2 = fb[i+2].volume;
let y3 = fb[i+3].volume;
let start = ( fb[i+1].position * o_buf.len() as f32 ) as usize;
let end = ( fb[i+2].position * o_buf.len() as f32 ) as usize;
if start < resolution && end < resolution {
for i in start..=end {
let pos: usize = i - start;
let gap_size = end - start;
let mut percentage: f32 = pos as f32 / gap_size as f32;
if percentage.is_nan() {percentage = 0.5}
let t = percentage;
let t2 = percentage.powi(2);
// explanation: http://paulbourke.net/miscellaneous/interpolation/
// cubic volume interpolation
let a0 = y3 - y2 - y0 + y1;
let a1 = y0 - y1 - a0;
let a2 = y2 - y0;
let a3 = y1;
// math magic
let volume = a0 * t * t2 + a1 * t2 + a2 * t + a3;
// linear freq interpolation
let f1 = fb[i+1].freq;
let f2 = fb[i+2].freq;
let freq = f1 * (1.0 - t) + f2 * t;
// only if volume of frequency is louder to preserve information
if o_buf.len() > i && o_buf[i].volume < volume {
o_buf[i] = Frequency {
volume,
position: 0.0, // unneccessary
freq,
};
}
}
}
}
}
o_buf
}