|
| 1 | +// Copyright 2022 the raphlinus.github.io Authors |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +//! Program to generate figures for "Simplifying Bézier paths" blog. Run with |
| 5 | +//! figure type ("th" or "arc"), generates svg file as stdout. |
| 6 | +
|
| 7 | +use kurbo::{CubicBez, ParamCurve, ParamCurveArclen, ParamCurveDeriv, Point, Shape, Vec2}; |
| 8 | +use kurbo::common::solve_cubic; |
| 9 | + |
| 10 | +fn intersect(c: CubicBez, p: Point, tangent: Vec2) -> Vec<f64> { |
| 11 | + let p1 = 3.0 * (c.p1 - c.p0); |
| 12 | + let p2 = 3.0 * c.p2.to_vec2() - 6.0 * c.p1.to_vec2() + 3.0 * c.p0.to_vec2(); |
| 13 | + let p3 = (c.p3 - c.p0) - 3.0 * (c.p2 - c.p1); |
| 14 | + let c0 = (c.p0 - p).dot(tangent); |
| 15 | + let c1 = p1.dot(tangent); |
| 16 | + let c2 = p2.dot(tangent); |
| 17 | + let c3 = p3.dot(tangent); |
| 18 | + solve_cubic(c0, c1, c2, c3) |
| 19 | + .into_iter() |
| 20 | + .filter(|t| (0.0..=1.0).contains(t)) |
| 21 | + .collect() |
| 22 | +} |
| 23 | + |
| 24 | + |
| 25 | +fn main() { |
| 26 | + let fig = std::env::args().skip(1).next().expect("need figure type"); |
| 27 | + let c1 = CubicBez::new((10., 150.), (300., 150.), (310., 150.), (310., 450.)); |
| 28 | + let a: f64 = if fig == "arc2" { 40. } else { 400. }; |
| 29 | + let c2 = CubicBez::new((10., 150.), (300. + a, 150.), (310., 150. - a), (310., 450.)); |
| 30 | + let arclen = c1.arclen(1e-9); |
| 31 | + let arclen2 = c2.arclen(1e-9); |
| 32 | + println!("<svg width='450' height='480' xmlns='http://www.w3.org/2000/svg'>"); |
| 33 | + println!(" <!-- figure generated by simplify_figs program in _figures subdir -->"); |
| 34 | + println!(" <path d='{}' stroke='#000' fill='none'/>", c1.into_path(1e-9).to_svg()); |
| 35 | + println!(" <path d='{}' stroke='#000' fill='none'/>", c2.into_path(1e-9).to_svg()); |
| 36 | + const N: usize = 11; |
| 37 | + for i in 1..N { |
| 38 | + let s = (i as f64 / N as f64) * arclen; |
| 39 | + let t = c1.inv_arclen(s, 1e-9); |
| 40 | + let p = c1.eval(t); |
| 41 | + println!(" <circle cx='{}' cy='{}' r='2' fill='#008'/>", p.x, p.y); |
| 42 | + let t2; |
| 43 | + if fig == "th" { |
| 44 | + let d = c1.deriv().eval(t); |
| 45 | + let t2s = intersect(c2, p, d.to_vec2()); |
| 46 | + t2 = t2s[0]; |
| 47 | + } else { |
| 48 | + let s2 = (i as f64 / N as f64) * arclen2; |
| 49 | + t2 = c2.inv_arclen(s2, 1e-9); |
| 50 | + } |
| 51 | + let p2 = c2.eval(t2); |
| 52 | + if fig != "th" { |
| 53 | + println!(" <circle cx='{}' cy='{}' r='2' fill='#008'/>", p2.x, p2.y); |
| 54 | + } |
| 55 | + println!(" <line x1='{}' y1='{}' x2='{}' y2='{}' stroke='#000'/>", |
| 56 | + p.x, p.y, p2.x, p2.y); |
| 57 | + } |
| 58 | + println!("</svg>"); |
| 59 | +} |
0 commit comments