Split B-quads whose curves' convex hulls intersect in most cases.
Avoids some rendering artefacts with Loop-Blinn, improving the tiger.
This commit is contained in:
parent
a126e1248e
commit
be1b100826
|
@ -82,8 +82,8 @@ button > svg path {
|
||||||
background: rgba(0, 0, 0, 0.75);
|
background: rgba(0, 0, 0, 0.75);
|
||||||
width: 17em;
|
width: 17em;
|
||||||
}
|
}
|
||||||
#pf-svg {
|
#pf-svg, #pf-svg * {
|
||||||
visibility: hidden;
|
visibility: hidden !important;
|
||||||
}
|
}
|
||||||
.pf-spinner {
|
.pf-spinner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
|
use euclid::approxeq::ApproxEq;
|
||||||
use euclid::Point2D;
|
use euclid::Point2D;
|
||||||
use geometry::{self, SubdividedQuadraticBezier};
|
use geometry::{self, SubdividedQuadraticBezier};
|
||||||
use log::LogLevel;
|
use log::LogLevel;
|
||||||
|
@ -24,6 +25,8 @@ use std::u32;
|
||||||
use {BQuad, BVertexLoopBlinnData, BVertexKind, CurveIndices, Endpoint, FillRule};
|
use {BQuad, BVertexLoopBlinnData, BVertexKind, CurveIndices, Endpoint, FillRule};
|
||||||
use {LineIndices, Subpath};
|
use {LineIndices, Subpath};
|
||||||
|
|
||||||
|
const MAX_B_QUAD_SUBDIVISIONS: u8 = 8;
|
||||||
|
|
||||||
pub struct Partitioner<'a> {
|
pub struct Partitioner<'a> {
|
||||||
endpoints: &'a [Endpoint],
|
endpoints: &'a [Endpoint],
|
||||||
control_points: &'a [Point2D<f32>],
|
control_points: &'a [Point2D<f32>],
|
||||||
|
@ -188,7 +191,7 @@ impl<'a> Partitioner<'a> {
|
||||||
let next_active_edge_index = self.find_point_between_active_edges(endpoint_index);
|
let next_active_edge_index = self.find_point_between_active_edges(endpoint_index);
|
||||||
|
|
||||||
let endpoint = &self.endpoints[endpoint_index as usize];
|
let endpoint = &self.endpoints[endpoint_index as usize];
|
||||||
self.emit_b_quad(next_active_edge_index, endpoint.position.x);
|
self.emit_b_quads_around_active_edge(next_active_edge_index, endpoint.position.x);
|
||||||
|
|
||||||
self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index);
|
self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index);
|
||||||
|
|
||||||
|
@ -206,7 +209,7 @@ impl<'a> Partitioner<'a> {
|
||||||
debug!("... REGULAR point: active edge {}", active_edge_index);
|
debug!("... REGULAR point: active edge {}", active_edge_index);
|
||||||
|
|
||||||
let endpoint = &self.endpoints[endpoint_index as usize];
|
let endpoint = &self.endpoints[endpoint_index as usize];
|
||||||
let bottom = self.emit_b_quad(active_edge_index, endpoint.position.x) ==
|
let bottom = self.emit_b_quads_around_active_edge(active_edge_index, endpoint.position.x) ==
|
||||||
BQuadEmissionResult::BQuadEmittedAbove;
|
BQuadEmissionResult::BQuadEmittedAbove;
|
||||||
|
|
||||||
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
|
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
|
||||||
|
@ -275,8 +278,8 @@ impl<'a> Partitioner<'a> {
|
||||||
|
|
||||||
let endpoint = &self.endpoints[endpoint_index as usize];
|
let endpoint = &self.endpoints[endpoint_index as usize];
|
||||||
|
|
||||||
self.emit_b_quad(active_edge_indices[0], endpoint.position.x);
|
self.emit_b_quads_around_active_edge(active_edge_indices[0], endpoint.position.x);
|
||||||
self.emit_b_quad(active_edge_indices[1], endpoint.position.x);
|
self.emit_b_quads_around_active_edge(active_edge_indices[1], endpoint.position.x);
|
||||||
|
|
||||||
self.heap.pop();
|
self.heap.pop();
|
||||||
|
|
||||||
|
@ -302,7 +305,11 @@ impl<'a> Partitioner<'a> {
|
||||||
debug!("found SELF-INTERSECTION point for active edges {} & {}",
|
debug!("found SELF-INTERSECTION point for active edges {} & {}",
|
||||||
upper_active_edge_index,
|
upper_active_edge_index,
|
||||||
lower_active_edge_index);
|
lower_active_edge_index);
|
||||||
self.emit_b_quad(lower_active_edge_index, crossing_point.x);
|
self.emit_b_quads_around_active_edge(lower_active_edge_index, crossing_point.x);
|
||||||
|
} else {
|
||||||
|
debug!("warning: swapped active edges {} & {} without finding intersection",
|
||||||
|
upper_active_edge_index,
|
||||||
|
lower_active_edge_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.active_edges.swap(upper_active_edge_index as usize,
|
self.active_edges.swap(upper_active_edge_index as usize,
|
||||||
|
@ -496,7 +503,8 @@ impl<'a> Partitioner<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_b_quad(&mut self, active_edge_index: u32, right_x: f32) -> BQuadEmissionResult {
|
fn emit_b_quads_around_active_edge(&mut self, active_edge_index: u32, right_x: f32)
|
||||||
|
-> BQuadEmissionResult {
|
||||||
if (active_edge_index as usize) >= self.active_edges.len() {
|
if (active_edge_index as usize) >= self.active_edges.len() {
|
||||||
return BQuadEmissionResult::NoBQuadEmitted
|
return BQuadEmissionResult::NoBQuadEmitted
|
||||||
}
|
}
|
||||||
|
@ -521,48 +529,144 @@ impl<'a> Partitioner<'a> {
|
||||||
SubdivisionType::Upper);
|
SubdivisionType::Upper);
|
||||||
for active_edge_index in (upper_active_edge_index + 1)..lower_active_edge_index {
|
for active_edge_index in (upper_active_edge_index + 1)..lower_active_edge_index {
|
||||||
self.subdivide_active_edge_at(active_edge_index, right_x, SubdivisionType::Inside);
|
self.subdivide_active_edge_at(active_edge_index, right_x, SubdivisionType::Inside);
|
||||||
|
self.active_edges[active_edge_index as usize].toggle_parity();
|
||||||
}
|
}
|
||||||
let lower_curve = self.subdivide_active_edge_at(lower_active_edge_index,
|
let lower_curve = self.subdivide_active_edge_at(lower_active_edge_index,
|
||||||
right_x,
|
right_x,
|
||||||
SubdivisionType::Lower);
|
SubdivisionType::Lower);
|
||||||
|
|
||||||
let upper_shape = upper_curve.shape(&self.b_vertex_loop_blinn_data);
|
self.emit_b_quads(upper_active_edge_index,
|
||||||
let lower_shape = lower_curve.shape(&self.b_vertex_loop_blinn_data);
|
lower_active_edge_index,
|
||||||
|
&upper_curve,
|
||||||
|
&lower_curve,
|
||||||
|
0);
|
||||||
|
|
||||||
|
emission_result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggles parity at the end.
|
||||||
|
fn emit_b_quads(&mut self,
|
||||||
|
upper_active_edge_index: u32,
|
||||||
|
lower_active_edge_index: u32,
|
||||||
|
upper_subdivision: &SubdividedActiveEdge,
|
||||||
|
lower_subdivision: &SubdividedActiveEdge,
|
||||||
|
iteration: u8) {
|
||||||
|
let upper_shape = upper_subdivision.shape(&self.b_vertex_loop_blinn_data);
|
||||||
|
let lower_shape = lower_subdivision.shape(&self.b_vertex_loop_blinn_data);
|
||||||
|
|
||||||
|
// Make sure the convex hulls of the two curves do not intersect. If they do, subdivide and
|
||||||
|
// recurse.
|
||||||
|
if iteration < MAX_B_QUAD_SUBDIVISIONS {
|
||||||
|
// TODO(pcwalton): Handle concave-line convex hull intersections.
|
||||||
|
if let (Some(upper_curve), Some(lower_curve)) =
|
||||||
|
(upper_subdivision.to_curve(&self.b_vertex_positions),
|
||||||
|
lower_subdivision.to_curve(&self.b_vertex_positions)) {
|
||||||
|
// TODO(pcwalton): Handle concave-concave convex hull intersections.
|
||||||
|
if upper_shape == Shape::Concave &&
|
||||||
|
lower_curve.baseline().side(&upper_curve.control_point) >
|
||||||
|
f32::approx_epsilon() {
|
||||||
|
let (upper_left_subsubdivision, upper_right_subsubdivision) =
|
||||||
|
self.subdivide_active_edge_again_at_t(&upper_subdivision,
|
||||||
|
0.5,
|
||||||
|
false);
|
||||||
|
let midpoint_x = self.b_vertex_positions[upper_left_subsubdivision.middle_point
|
||||||
|
as usize].x;
|
||||||
|
let (lower_left_subsubdivision, lower_right_subsubdivision) =
|
||||||
|
self.subdivide_active_edge_again_at_x(&lower_subdivision,
|
||||||
|
midpoint_x,
|
||||||
|
true);
|
||||||
|
|
||||||
|
self.emit_b_quads(upper_active_edge_index,
|
||||||
|
lower_active_edge_index,
|
||||||
|
&upper_left_subsubdivision,
|
||||||
|
&lower_left_subsubdivision,
|
||||||
|
iteration + 1);
|
||||||
|
self.emit_b_quads(upper_active_edge_index,
|
||||||
|
lower_active_edge_index,
|
||||||
|
&upper_right_subsubdivision,
|
||||||
|
&lower_right_subsubdivision,
|
||||||
|
iteration + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if lower_shape == Shape::Concave &&
|
||||||
|
upper_curve.baseline().side(&lower_curve.control_point) <
|
||||||
|
-f32::approx_epsilon() {
|
||||||
|
let (lower_left_subsubdivision, lower_right_subsubdivision) =
|
||||||
|
self.subdivide_active_edge_again_at_t(&lower_subdivision,
|
||||||
|
0.5,
|
||||||
|
true);
|
||||||
|
let midpoint_x = self.b_vertex_positions[lower_left_subsubdivision.middle_point
|
||||||
|
as usize].x;
|
||||||
|
let (upper_left_subsubdivision, upper_right_subsubdivision) =
|
||||||
|
self.subdivide_active_edge_again_at_x(&upper_subdivision,
|
||||||
|
midpoint_x,
|
||||||
|
false);
|
||||||
|
|
||||||
|
self.emit_b_quads(upper_active_edge_index,
|
||||||
|
lower_active_edge_index,
|
||||||
|
&upper_left_subsubdivision,
|
||||||
|
&lower_left_subsubdivision,
|
||||||
|
iteration + 1);
|
||||||
|
self.emit_b_quads(upper_active_edge_index,
|
||||||
|
lower_active_edge_index,
|
||||||
|
&upper_right_subsubdivision,
|
||||||
|
&lower_right_subsubdivision,
|
||||||
|
iteration + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match upper_shape {
|
match upper_shape {
|
||||||
Shape::Flat => {
|
Shape::Flat => {
|
||||||
self.edge_indices
|
self.edge_indices
|
||||||
.upper_line_indices
|
.upper_line_indices
|
||||||
.push(LineIndices::new(upper_curve.left_curve_left, upper_curve.middle_point))
|
.push(LineIndices::new(upper_subdivision.left_curve_left,
|
||||||
|
upper_subdivision.middle_point))
|
||||||
}
|
}
|
||||||
Shape::Convex | Shape::Concave => {
|
Shape::Convex | Shape::Concave => {
|
||||||
self.edge_indices
|
self.edge_indices
|
||||||
.upper_curve_indices
|
.upper_curve_indices
|
||||||
.push(CurveIndices::new(upper_curve.left_curve_left,
|
.push(CurveIndices::new(upper_subdivision.left_curve_left,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
upper_curve.middle_point))
|
upper_subdivision.middle_point))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match lower_shape {
|
match lower_shape {
|
||||||
Shape::Flat => {
|
Shape::Flat => {
|
||||||
self.edge_indices
|
self.edge_indices
|
||||||
.lower_line_indices
|
.lower_line_indices
|
||||||
.push(LineIndices::new(lower_curve.left_curve_left, lower_curve.middle_point))
|
.push(LineIndices::new(lower_subdivision.left_curve_left,
|
||||||
|
lower_subdivision.middle_point))
|
||||||
}
|
}
|
||||||
Shape::Convex | Shape::Concave => {
|
Shape::Convex | Shape::Concave => {
|
||||||
self.edge_indices
|
self.edge_indices
|
||||||
.lower_curve_indices
|
.lower_curve_indices
|
||||||
.push(CurveIndices::new(lower_curve.left_curve_left,
|
.push(CurveIndices::new(lower_subdivision.left_curve_left,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
lower_curve.middle_point))
|
lower_subdivision.middle_point))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("... emitting B-quad: UL {} BL {} UR {} BR {}",
|
debug!("... emitting B-quad: UL {} BL {} UR {} BR {}",
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.middle_point);
|
lower_subdivision.middle_point);
|
||||||
|
|
||||||
|
{
|
||||||
|
let upper_active_edge = &mut self.active_edges[upper_active_edge_index as usize];
|
||||||
|
self.b_vertex_loop_blinn_data[upper_subdivision.middle_point as usize] =
|
||||||
|
BVertexLoopBlinnData::new(upper_active_edge.endpoint_kind());
|
||||||
|
upper_active_edge.toggle_parity();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let lower_active_edge = &mut self.active_edges[lower_active_edge_index as usize];
|
||||||
|
self.b_vertex_loop_blinn_data[lower_subdivision.middle_point as usize] =
|
||||||
|
BVertexLoopBlinnData::new(lower_active_edge.endpoint_kind());
|
||||||
|
lower_active_edge.toggle_parity();
|
||||||
|
}
|
||||||
|
|
||||||
match (upper_shape, lower_shape) {
|
match (upper_shape, lower_shape) {
|
||||||
(Shape::Flat, Shape::Flat) |
|
(Shape::Flat, Shape::Flat) |
|
||||||
|
@ -570,25 +674,25 @@ impl<'a> Partitioner<'a> {
|
||||||
(Shape::Convex, Shape::Flat) |
|
(Shape::Convex, Shape::Flat) |
|
||||||
(Shape::Convex, Shape::Convex) => {
|
(Shape::Convex, Shape::Convex) => {
|
||||||
self.cover_indices.interior_indices.extend([
|
self.cover_indices.interior_indices.extend([
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
].into_iter());
|
].into_iter());
|
||||||
if upper_shape != Shape::Flat {
|
if upper_shape != Shape::Flat {
|
||||||
self.cover_indices.curve_indices.extend([
|
self.cover_indices.curve_indices.extend([
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
].into_iter())
|
].into_iter())
|
||||||
}
|
}
|
||||||
if lower_shape != Shape::Flat {
|
if lower_shape != Shape::Flat {
|
||||||
self.cover_indices.curve_indices.extend([
|
self.cover_indices.curve_indices.extend([
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
].into_iter())
|
].into_iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,26 +700,26 @@ impl<'a> Partitioner<'a> {
|
||||||
(Shape::Concave, Shape::Flat) |
|
(Shape::Concave, Shape::Flat) |
|
||||||
(Shape::Concave, Shape::Convex) => {
|
(Shape::Concave, Shape::Convex) => {
|
||||||
self.cover_indices.interior_indices.extend([
|
self.cover_indices.interior_indices.extend([
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
].into_iter());
|
].into_iter());
|
||||||
self.cover_indices.curve_indices.extend([
|
self.cover_indices.curve_indices.extend([
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
].into_iter());
|
].into_iter());
|
||||||
if lower_shape != Shape::Flat {
|
if lower_shape != Shape::Flat {
|
||||||
self.cover_indices.curve_indices.extend([
|
self.cover_indices.curve_indices.extend([
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
].into_iter())
|
].into_iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,64 +727,121 @@ impl<'a> Partitioner<'a> {
|
||||||
(Shape::Flat, Shape::Concave) |
|
(Shape::Flat, Shape::Concave) |
|
||||||
(Shape::Convex, Shape::Concave) => {
|
(Shape::Convex, Shape::Concave) => {
|
||||||
self.cover_indices.interior_indices.extend([
|
self.cover_indices.interior_indices.extend([
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
].into_iter());
|
].into_iter());
|
||||||
self.cover_indices.curve_indices.extend([
|
self.cover_indices.curve_indices.extend([
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
].into_iter());
|
].into_iter());
|
||||||
if upper_shape != Shape::Flat {
|
if upper_shape != Shape::Flat {
|
||||||
self.cover_indices.curve_indices.extend([
|
self.cover_indices.curve_indices.extend([
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
].into_iter())
|
].into_iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Shape::Concave, Shape::Concave) => {
|
(Shape::Concave, Shape::Concave) => {
|
||||||
self.cover_indices.interior_indices.extend([
|
self.cover_indices.interior_indices.extend([
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
].into_iter());
|
].into_iter());
|
||||||
self.cover_indices.curve_indices.extend([
|
self.cover_indices.curve_indices.extend([
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
upper_curve.left_curve_left,
|
upper_subdivision.left_curve_left,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
lower_curve.middle_point,
|
lower_subdivision.middle_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
].into_iter());
|
].into_iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.b_quads.push(BQuad::new(upper_curve.left_curve_left,
|
self.b_quads.push(BQuad::new(upper_subdivision.left_curve_left,
|
||||||
upper_curve.left_curve_control_point,
|
upper_subdivision.left_curve_control_point,
|
||||||
upper_curve.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_curve.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
lower_curve.left_curve_control_point,
|
lower_subdivision.left_curve_control_point,
|
||||||
lower_curve.middle_point));
|
lower_subdivision.middle_point))
|
||||||
|
}
|
||||||
|
|
||||||
emission_result
|
fn subdivide_active_edge_again_at_t(&mut self,
|
||||||
|
subdivision: &SubdividedActiveEdge,
|
||||||
|
t: f32,
|
||||||
|
bottom: bool)
|
||||||
|
-> (SubdividedActiveEdge, SubdividedActiveEdge) {
|
||||||
|
let curve = subdivision.to_curve(&self.b_vertex_positions)
|
||||||
|
.expect("subdivide_active_edge_again_at_t(): not a curve!");
|
||||||
|
let (left_curve, right_curve) = curve.subdivide(t);
|
||||||
|
|
||||||
|
let left_control_point_index = self.b_vertex_positions.len() as u32;
|
||||||
|
let midpoint_index = left_control_point_index + 1;
|
||||||
|
let right_control_point_index = midpoint_index + 1;
|
||||||
|
self.b_vertex_positions.extend([
|
||||||
|
left_curve.control_point,
|
||||||
|
left_curve.endpoints[1],
|
||||||
|
right_curve.control_point,
|
||||||
|
].into_iter());
|
||||||
|
|
||||||
|
self.b_vertex_path_ids.extend(iter::repeat(self.path_id).take(3));
|
||||||
|
|
||||||
|
// Initially, assume that the parity is false. We will modify the Loop-Blinn data later if
|
||||||
|
// that is incorrect.
|
||||||
|
self.b_vertex_loop_blinn_data.extend([
|
||||||
|
BVertexLoopBlinnData::control_point(&left_curve.endpoints[0],
|
||||||
|
&left_curve.control_point,
|
||||||
|
&left_curve.endpoints[1],
|
||||||
|
bottom),
|
||||||
|
BVertexLoopBlinnData::new(BVertexKind::Endpoint0),
|
||||||
|
BVertexLoopBlinnData::control_point(&right_curve.endpoints[0],
|
||||||
|
&right_curve.control_point,
|
||||||
|
&right_curve.endpoints[1],
|
||||||
|
bottom),
|
||||||
|
].into_iter());
|
||||||
|
|
||||||
|
(SubdividedActiveEdge {
|
||||||
|
left_curve_left: subdivision.left_curve_left,
|
||||||
|
left_curve_control_point: left_control_point_index,
|
||||||
|
middle_point: midpoint_index,
|
||||||
|
}, SubdividedActiveEdge {
|
||||||
|
left_curve_left: midpoint_index,
|
||||||
|
left_curve_control_point: right_control_point_index,
|
||||||
|
middle_point: subdivision.middle_point,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subdivide_active_edge_again_at_x(&mut self,
|
||||||
|
subdivision: &SubdividedActiveEdge,
|
||||||
|
x: f32,
|
||||||
|
bottom: bool)
|
||||||
|
-> (SubdividedActiveEdge, SubdividedActiveEdge) {
|
||||||
|
let curve = subdivision.to_curve(&self.b_vertex_positions)
|
||||||
|
.expect("subdivide_active_edge_again_at_x(): not a curve!");
|
||||||
|
let t = geometry::solve_quadratic_bezier_t_for_x(x,
|
||||||
|
&curve.endpoints[0],
|
||||||
|
&curve.control_point,
|
||||||
|
&curve.endpoints[1]);
|
||||||
|
self.subdivide_active_edge_again_at_t(subdivision, t, bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn already_visited_point(&self, point: &Point) -> bool {
|
fn already_visited_point(&self, point: &Point) -> bool {
|
||||||
|
@ -849,6 +1010,7 @@ impl<'a> Partitioner<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does *not* toggle parity. You must do this after calling this function.
|
||||||
fn subdivide_active_edge_at(&mut self,
|
fn subdivide_active_edge_at(&mut self,
|
||||||
active_edge_index: u32,
|
active_edge_index: u32,
|
||||||
x: f32,
|
x: f32,
|
||||||
|
@ -876,8 +1038,6 @@ impl<'a> Partitioner<'a> {
|
||||||
self.b_vertex_loop_blinn_data
|
self.b_vertex_loop_blinn_data
|
||||||
.push(BVertexLoopBlinnData::new(active_edge.endpoint_kind()));
|
.push(BVertexLoopBlinnData::new(active_edge.endpoint_kind()));
|
||||||
|
|
||||||
active_edge.toggle_parity();
|
|
||||||
|
|
||||||
left_curve_control_point_vertex_index = u32::MAX;
|
left_curve_control_point_vertex_index = u32::MAX;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -912,8 +1072,6 @@ impl<'a> Partitioner<'a> {
|
||||||
&right_endpoint_position,
|
&right_endpoint_position,
|
||||||
bottom),
|
bottom),
|
||||||
].into_iter());
|
].into_iter());
|
||||||
|
|
||||||
active_edge.toggle_parity();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,7 +1079,6 @@ impl<'a> Partitioner<'a> {
|
||||||
left_curve_left: left_curve_left,
|
left_curve_left: left_curve_left,
|
||||||
left_curve_control_point: left_curve_control_point_vertex_index,
|
left_curve_control_point: left_curve_control_point_vertex_index,
|
||||||
middle_point: active_edge.left_vertex_index,
|
middle_point: active_edge.left_vertex_index,
|
||||||
right_curve_control_point: active_edge.control_point_vertex_index,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,7 +1285,6 @@ struct SubdividedActiveEdge {
|
||||||
left_curve_left: u32,
|
left_curve_left: u32,
|
||||||
left_curve_control_point: u32,
|
left_curve_control_point: u32,
|
||||||
middle_point: u32,
|
middle_point: u32,
|
||||||
right_curve_control_point: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubdividedActiveEdge {
|
impl SubdividedActiveEdge {
|
||||||
|
@ -1141,6 +1297,16 @@ impl SubdividedActiveEdge {
|
||||||
Shape::Concave
|
Shape::Concave
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_curve(&self, b_vertex_positions: &[Point2D<f32>]) -> Option<Curve> {
|
||||||
|
if self.left_curve_control_point == u32::MAX {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Curve::new(&b_vertex_positions[self.left_curve_left as usize],
|
||||||
|
&b_vertex_positions[self.left_curve_control_point as usize],
|
||||||
|
&b_vertex_positions[self.middle_point as usize]))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
@ -16,6 +16,7 @@ use std::f32;
|
||||||
|
|
||||||
use PathSegment;
|
use PathSegment;
|
||||||
use intersection::{Intersect, Side};
|
use intersection::{Intersect, Side};
|
||||||
|
use line::Line;
|
||||||
|
|
||||||
pub struct Curve {
|
pub struct Curve {
|
||||||
pub endpoints: [Point2D<f32>; 2],
|
pub endpoints: [Point2D<f32>; 2],
|
||||||
|
@ -61,6 +62,11 @@ impl Curve {
|
||||||
(inflection_point_x, inflection_point_y)
|
(inflection_point_x, inflection_point_y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn baseline(&self) -> Line {
|
||||||
|
Line::new(&self.endpoints[0], &self.endpoints[1])
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn inflection_point_x(endpoint_x_0: f32, endpoint_x_1: f32, control_point_x: f32)
|
fn inflection_point_x(endpoint_x_0: f32, endpoint_x_1: f32, control_point_x: f32)
|
||||||
-> Option<f32> {
|
-> Option<f32> {
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub(crate) trait Intersect {
|
||||||
impl Side for Line {
|
impl Side for Line {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn side(&self, point: &Point2D<f32>) -> f32 {
|
fn side(&self, point: &Point2D<f32>) -> f32 {
|
||||||
self.to_vector().cross(*point - self.endpoints[0])
|
Line::side(self, point)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub mod intersection;
|
||||||
pub mod line;
|
pub mod line;
|
||||||
pub mod monotonic;
|
pub mod monotonic;
|
||||||
pub mod stroke;
|
pub mod stroke;
|
||||||
|
pub mod svg;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum PathSegment {
|
pub enum PathSegment {
|
||||||
|
@ -130,10 +131,12 @@ impl<'a> Iterator for PathBufferStream<'a> {
|
||||||
return Some(PathSegment::ClosePath)
|
return Some(PathSegment::ClosePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
let endpoint = &self.path_buffer.endpoints[self.endpoint_index as usize];
|
let endpoint_index = self.endpoint_index;
|
||||||
self.endpoint_index += 1;
|
self.endpoint_index += 1;
|
||||||
|
|
||||||
if self.endpoint_index == subpath.first_endpoint_index {
|
let endpoint = &self.path_buffer.endpoints[endpoint_index as usize];
|
||||||
|
|
||||||
|
if endpoint_index == subpath.first_endpoint_index {
|
||||||
return Some(PathSegment::MoveTo(endpoint.position))
|
return Some(PathSegment::MoveTo(endpoint.position))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,11 @@ impl Line {
|
||||||
self.endpoints[0].lerp(self.endpoints[1], t)
|
self.endpoints[0].lerp(self.endpoints[1], t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn side(&self, point: &Point2D<f32>) -> f32 {
|
||||||
|
self.to_vector().cross(*point - self.endpoints[0])
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn to_vector(&self) -> Vector2D<f32> {
|
pub(crate) fn to_vector(&self) -> Vector2D<f32> {
|
||||||
self.endpoints[1] - self.endpoints[0]
|
self.endpoints[1] - self.endpoints[0]
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// pathfinder/path-utils/src/svg.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.
|
||||||
|
|
||||||
|
//! Utilities for converting paths to SVG representations.
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
use PathSegment;
|
||||||
|
|
||||||
|
pub fn to_svg_description<W, S>(output: &mut W, stream: S) -> io::Result<()>
|
||||||
|
where W: Write, S: Iterator<Item = PathSegment> {
|
||||||
|
for segment in stream {
|
||||||
|
match segment {
|
||||||
|
PathSegment::MoveTo(point) => try!(write!(output, "M{},{} ", point.x, point.y)),
|
||||||
|
PathSegment::LineTo(point) => try!(write!(output, "L{},{} ", point.x, point.y)),
|
||||||
|
PathSegment::CurveTo(control_point, endpoint) => {
|
||||||
|
try!(write!(output, "Q{},{} {},{} ",
|
||||||
|
control_point.x, control_point.y,
|
||||||
|
endpoint.x, endpoint.y))
|
||||||
|
}
|
||||||
|
PathSegment::ClosePath => try!(output.write_all(b"z")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue