根据 Loop & Blinn 2005 对三次贝塞尔曲线进行分类

Str*_*ers 6 math graphics bezier 2d rust

我正在尝试展平贝塞尔曲线路径(删除所有交叉点并替换为端点)作为此处描述的渲染算法实现的一部分,并找到了 GPU Gems 3 Ch 中的 Loop 和 Blinn 描述的算法。25,对 $a3$ 的叉积进行校正以检测曲线自相交(循环)。

\n

该算法要求对表达式求值discr=d\xe2\x82\x81\xc2\xb2(3d\xe2\x82\x82\xc2\xb2-4d\xe2\x82\x81d\xe2\x82\x83),使得曲线自相交当且仅当discr < 0

\n

在哪里

\n

d\xe2\x82\x81=a\xe2\x82\x81-2a\xe2\x82\x82+3a\xe2\x82\x83,\n d\xe2\x82\x82=-a\xe2\x82\x82+3a\xe2\x82\x83,\nd\xe2\x82\x83=3a\xe2\x82\x83

\n

a\xe2\x82\x81=b\xe2\x82\x80\xc2\xb7(b\xe2\x82\x83\xe2\xa8\xafb\xe2\x82\x82),\n a\xe2\x82\x82=b\xe2\x82\x81\xc2\xb7(b\xe2\x82\x80\xe2\xa8\xafb\xe2\x82\x83),\na\xe2\x82\x83=b\xe2\x82\x82\xc2\xb7(b\xe2\x82\x81\xe2\xa8\xafb\xe2\x82\x80)

\n

我已经在下面的示例代码中实现了该算法,以及我能找到的其他一些算法。他们谁都不同意,而且全都错了。具体来说,我已经实施了

\n
    \n
  1. GPU Gems 3中描述的算法
  2. \n
  3. 与原始论文中描述的算法相同。
  4. \n
  5. 与 paper.js(问题)(草图)中实现的算法相同。
  6. \n
  7. Pomax' Bezier Primer 中描述的曲线规范化算法
  8. \n
\n

这三种算法的不一致表明存在重大误解。它是什么?(更重要的是)我自己如何识别这样的问题?

\n

我参考了这里这里的问题,以及Loop 和 Blinn 描述他们方法的论文,但没有效果。

\n
\n#[derive(Clone, Copy, Debug, PartialEq)]\nenum Kind {\n    Serpentine,\n    Cusp,\n    Loop,\n    Quadratic,\n    Line,\n}\n\n#[derive(Clone, Copy)]\npub struct Point {\n    x: f32,\n    y: f32,\n}\n\n#[derive(Clone, Copy)]\nstruct Vec3 {\n    x: f32,\n    y: f32,\n    z: f32,\n}\n\nimpl Vec3 {\n    fn from_point(p: Point) -> Vec3 {\n        Vec3 {\n            x: p.x,\n            y: p.y,\n            z: 1.0,\n        }\n    }\n\n    fn dot(&self, other: Vec3) -> f32 {\n        self.x * other.x + self.y * other.y + self.z * other.z\n    }\n\n    fn cross(&self, other: Vec3) -> Vec3 {\n        Vec3 {\n            x: self.y * other.z - self.z * other.y,\n            y: self.z * other.x - self.x * other.z,\n            z: self.x * other.y - self.y * other.x,\n        }\n    }\n}\n\nfn is_zero(f: f32) -> bool {\n    f.abs() < 0.000001\n}\n\nfn approx_eq(a: f32, b: f32) -> bool {\n    is_zero((a - b).abs())\n}\n\nfn normalize(a: f32, b: f32, c: f32) -> (f32, f32, f32) {\n    let len = (a * a + b * b + c * c).sqrt();\n    if is_zero(len) {\n        (0.0, 0.0, 0.0)\n    } else {\n        (a / len, b / len, c / len)\n    }\n}\n\npub fn classify(curve: &[Point; 4]) {\n    let p0 = Vec3::from_point(curve[0]);\n    let p1 = Vec3::from_point(curve[1]);\n    let p2 = Vec3::from_point(curve[2]);\n    let p3 = Vec3::from_point(curve[3]);\n\n    let loop_blinn = {\n        let det1 = -p3.dot(p2.cross(p0));\n        let det2 = p3.dot(p2.cross(p0));\n        let det3 = -p2.dot(p1.cross(p0));\n\n        let (det1, det2, det3) = normalize(det1, det2, det3);\n\n        let discr = det2 * det2 * (3.0 * det2 * det2 - 4.0 * det3 * det1);\n\n        if is_zero(det1) {\n            if !is_zero(det2) {\n                Kind::Cusp\n            } else if is_zero(det3) {\n                Kind::Line\n            } else {\n                Kind::Quadratic\n            }\n        } else if is_zero(discr) {\n            Kind::Cusp\n        } else if discr > 0.0 {\n            Kind::Serpentine\n        } else {\n            Kind::Loop\n        }\n    };\n\n    let gpu_gems = {\n        let a1 = p0.dot(p3.cross(p2));\n        let a2 = p1.dot(p0.cross(p3));\n        let a3 = p2.dot(p1.cross(p0));\n        let d1 = a1 - 2.0 * a2 + 3.0 * a3;\n        let d2 = -a2 + 3.0 * a3;\n        let d3 = 3.0 * a3;\n        let discr = d2 * d2 * (3.0 * d2 * d2 - 4.0 * d1 * d3);\n\n        if is_zero(d1) {\n            if is_zero(d2) && is_zero(d3) {\n                Kind::Line\n            } else {\n                Kind::Quadratic\n            }\n        } else if is_zero(discr) {\n            Kind::Cusp\n        } else if discr < 0.0 {\n            Kind::Serpentine\n        } else {\n            Kind::Loop\n        }\n    };\n\n    let paper_js = {\n        let x1 = p0.x;\n        let y1 = p0.y;\n        let x2 = p1.x;\n        let y2 = p1.y;\n        let x3 = p2.x;\n        let y3 = p2.y;\n        let x4 = p3.x;\n        let y4 = p3.y;\n\n        let a1 = x1 * (y4 - y3) + y1 * (x3 - x4) + x4 * y3 - x3 * y4;\n        let a2 = x2 * (y1 - y4) + y2 * (x4 - x1) + x1 * y4 - y1 * x4;\n        let a3 = x3 * (y2 - y1) + y3 * (x1 - x2) + x2 * y1 - y2 * x1;\n        let d3 = 3.0 * a3;\n        let d2 = d3 - a2;\n        let d1 = d2 - a2 + a1;\n        let (d1, d2, d3) = normalize(d1, d2, d3);\n\n        let d = 3.0 * d2 * d2 - 4.0 * d1 * d3;\n\n        if is_zero(d1) {\n            if is_zero(d2) {\n                if is_zero(d3) {\n                    Kind::Line\n                } else {\n                    Kind::Quadratic\n                }\n            } else {\n                Kind::Serpentine\n            }\n        } else if is_zero(d) {\n            Kind::Cusp\n        } else if d > 0.0 {\n            Kind::Serpentine\n        } else {\n            Kind::Loop\n        }\n    };\n\n    let pomax_primer = {\n        let y31 = p3.y / p1.y;\n        let y21 = p2.y / p1.y;\n        let x32 = (p3.x - p2.x * y31) / (p2.x - p1.x * y21);\n\n        let x = x32;\n        let y = y31 + x32 * (1.0 - y21);\n\n        let cusp_line = (-(x * x) + 2.0 * x + 3.0) / 4.0;\n        let loop_at_1 = ((3.0 * 4.0 * x - x * x).sqrt() - x) / 2.0;\n        let loop_at_0 = (-(x * x) + 3.0 * x) / 3.0;\n\n        if x > 1.0 || y > 1.0 {\n            Kind::Serpentine\n        } else if (0.0..1.0).contains(&x) {\n            if approx_eq(loop_at_1, y) {\n                Kind::Loop\n            } else if approx_eq(cusp_line, y) {\n                Kind::Cusp\n            } else if y < cusp_line || y > loop_at_1 {\n                Kind::Serpentine\n            } else {\n                Kind::Loop\n            }\n        } else if approx_eq(loop_at_0, y) {\n            Kind::Loop\n        } else if approx_eq(cusp_line, y) {\n            Kind::Cusp\n        } else if y < cusp_line || y > loop_at_0 {\n            Kind::Loop\n        } else {\n            Kind::Serpentine\n        }\n    };\n\n    println!("\\tMethod 1 (Loop Blinn 2005): {:?}", loop_blinn);\n    println!("\\tMethod 2 (GPU Gems 3.25):   {:?}", gpu_gems);\n    println!("\\tMethod 3 (Paper.js):        {:?}", paper_js);\n    println!("\\tMethod 4 (Pomax Primer):    {:?}", pomax_primer);\n}\n\npub fn main() {\n    let point = [\n        Point { x: 1.0, y: 1.0 },\n        Point { x: 1.0, y: 1.0 },\n        Point { x: 1.0, y: 1.0 },\n        Point { x: 1.0, y: 1.0 },\n    ];\n    println!("Expecting: Kind::Line");\n    classify(&point);\n\n    let line = [\n        Point { x: 1.0, y: 1.0 },\n        Point { x: 2.0, y: 1.0 },\n        Point { x: 3.0, y: 1.0 },\n        Point { x: 4.0, y: 1.0 },\n    ];\n    println!("Expecting: Kind::Line");\n    classify(&line);\n\n    // loop\n    let normal_loop = [\n        Point { x: 75.0, y: 98.0 },\n        Point { x: 195.0, y: 201.0 },\n        Point { x: 63.0, y: 198.0 },\n        Point { x: 135.0, y: 103.0 },\n    ];\n    println!("Expecting: Kind::Loop");\n    classify(&normal_loop);\n\n    let end_loop = [\n        Point { x: 120.0, y: 331.0 },\n        Point { x: 246.0, y: 261.0 },\n        Point { x: 187.0, y: 242.0 },\n        Point { x: 148.0, y: 314.0 },\n    ];\n    println!("Expecting: Kind::Loop");\n    classify(&end_loop);\n\n    // cusp\n    let cusp = [\n        Point { x: 272.0, y: 305.0 },\n        Point { x: 93.0, y: 223.0 },\n        Point { x: 221.0, y: 223.0 },\n        Point { x: 148.0, y: 314.0 },\n    ];\n    println!("Expecting: Kind::Cusp");\n    classify(&cusp);\n\n    // serpentine\n    let serpentine = [\n        Point { x: 148.0, y: 314.0 },\n        Point { x: 187.0, y: 242.0 },\n        Point { x: 246.0, y: 261.0 },\n        Point { x: 272.0, y: 305.0 },\n    ];\n    println!("Expecting: Kind::Serpentine");\n    classify(&serpentine);\n\n    let serpentine2 = [\n        Point { x: 110.0, y: 150.0 },\n        Point { x: 25.0, y: 190.0 },\n        Point { x: 210.0, y: 250.0 },\n        Point { x: 210.0, y: 30.0 },\n    ];\n    println!("Expecting: Kind::Serpentine");\n    classify(&serpentine2);\n}\n
Run Code Online (Sandbox Code Playgroud)\n