Use the Citardauq Formula in the partitioner as well as the shader to avoid precision problems.

Addresses the issue with the Nimbus Sans "o" mentioned in #24.
This commit is contained in:
Patrick Walton 2017-09-01 23:45:51 -07:00
parent 632202e5eb
commit 444e7bbf96
6 changed files with 53 additions and 45 deletions

View File

@ -20,13 +20,13 @@ import {BUILTIN_FONT_URI, GlyphStorage, PathfinderGlyph} from "./text";
import { unwrapNull, UINT32_SIZE, UINT32_MAX } from "./utils";
import {PathfinderView} from "./view";
const CHARACTER: string = 'o';
const CHARACTER: string = 'r';
const FONT: string = 'nimbus-sans';
const FONT: string = 'eb-garamond';
const POINT_LABEL_FONT: string = "36px sans-serif";
const POINT_LABEL_FONT: string = "12px sans-serif";
const POINT_LABEL_OFFSET: glmatrix.vec2 = glmatrix.vec2.fromValues(12.0, 12.0);
const POINT_RADIUS: number = 6.0;
const POINT_RADIUS: number = 2.0;
class MeshDebuggerAppController extends AppController {
start() {

View File

@ -7,7 +7,9 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
app_units = "0.5"
base64 = "0.6"
bincode = "0.8"
env_logger = "0.3"
euclid = "0.15"
log = "0.3"
rocket = "0.3"
rocket_codegen = "0.3"
rocket_contrib = "0.3"

View File

@ -8,6 +8,7 @@
extern crate app_units;
extern crate base64;
extern crate bincode;
extern crate env_logger;
extern crate euclid;
extern crate fontsan;
extern crate pathfinder_font_renderer;
@ -615,6 +616,8 @@ impl<'a> Responder<'a> for Shader {
}
fn main() {
drop(env_logger::init());
rocket::ignite().mount("/", routes![
partition_font,
partition_svg_paths,

View File

@ -1,11 +1,17 @@
// partitionfinder/geometry.rs
// pathfinder/partitioner/src/geometry.rs
//
// Copyright © 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use euclid::approxeq::ApproxEq;
use euclid::{Point2D, Vector2D};
use std::cmp::Ordering;
const NEWTON_RAPHSON_ITERATIONS: u8 = 8;
pub(crate) trait ApproxOrdered {
fn approx_ordered(&self) -> bool;
}
@ -84,23 +90,6 @@ pub fn sample_quadratic_bezier(t: f32, p0: &Point2D<f32>, p1: &Point2D<f32>, p2:
p0.lerp(*p1, t).lerp(p1.lerp(*p2, t), t)
}
pub fn sample_quadratic_bezier_deriv(t: f32,
p0: &Point2D<f32>,
p1: &Point2D<f32>,
p2: &Point2D<f32>)
-> Vector2D<f32> {
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves
// FIXME(pcwalton): Can this be made faster?
((*p1 - *p0) * (1.0 - t) + (*p2 - *p1) * t) * 2.0
}
pub fn sample_quadratic_bezier_deriv_deriv(p0: &Point2D<f32>, p1: &Point2D<f32>, p2: &Point2D<f32>)
-> Vector2D<f32> {
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves
// FIXME(pcwalton): Can this be made faster?
(*p2 - *p1 * 2.0 + p0.to_vector()) * 2.0
}
pub fn solve_line_t_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
if b.x == a.x {
0.0
@ -109,28 +98,22 @@ pub fn solve_line_t_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
}
}
pub(crate) fn newton_raphson<F, DFDX>(f: F, dfdx: DFDX, mut x_guess: f32) -> f32
where F: Fn(f32) -> f32, DFDX: Fn(f32) -> f32 {
for _ in 0..NEWTON_RAPHSON_ITERATIONS {
let y = f(x_guess);
if y.approx_eq(&0.0) {
break
}
let yy = dfdx(x_guess);
x_guess -= y / yy
}
x_guess
}
// Use the Citardauq Formula to avoid precision problems.
//
// https://math.stackexchange.com/a/311397
pub fn solve_quadratic_bezier_t_for_x(x: f32,
p0: &Point2D<f32>,
p1: &Point2D<f32>,
p2: &Point2D<f32>)
-> f32 {
// TODO(pcwalton): Use the quadratic equation instead.
newton_raphson(|t| sample_quadratic_bezier(t, p0, p1, p2).x - x,
|t| sample_quadratic_bezier_deriv(t, p0, p1, p2).x,
0.5)
let (p0x, p1x, p2x, x) = (p0.x as f64, p1.x as f64, p2.x as f64, x as f64);
let a = p0x - 2.0 * p1x + p2x;
let b = -2.0 * p0x + 2.0 * p1x;
let c = p0x - x;
let t = 2.0 * c / (-b - (b * b - 4.0 * a * c).sqrt());
t.max(0.0).min(1.0) as f32
}
pub fn solve_quadratic_bezier_y_for_x(x: f32,

View File

@ -1,4 +1,12 @@
// partitionfinder/lib.rs
// pathfinder/partitioner/src/lib.rs
//
// Copyright © 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(alloc_jemalloc)]

View File

@ -1,4 +1,12 @@
// partitionfinder/partitioner.rs
// pathfinder/partitioner/src/partitioner.rs
//
// Copyright © 2017 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use bit_vec::BitVec;
use euclid::Point2D;
@ -448,8 +456,6 @@ impl<'a> Partitioner<'a> {
let upper_curve = self.subdivide_active_edge_at(upper_active_edge_index, right_x);
let lower_curve = self.subdivide_active_edge_at(lower_active_edge_index, right_x);
// NB: Order is important here—we depend on the provoking vertex!
let upper_shape = upper_curve.shape(&self.b_vertex_loop_blinn_data);
let lower_shape = lower_curve.shape(&self.b_vertex_loop_blinn_data);
@ -482,6 +488,12 @@ impl<'a> Partitioner<'a> {
}
}
debug!("... emitting B-quad: UL {} BL {} UR {} BR {}",
upper_curve.left_curve_left,
lower_curve.left_curve_left,
upper_curve.middle_point,
lower_curve.middle_point);
match (upper_shape, lower_shape) {
(Shape::Flat, Shape::Flat) |
(Shape::Flat, Shape::Convex) |