如何使用贝塞尔曲线对 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
}