Calculate normals for B-vertices.

I'm planning to use this for fixing hairlines in XCAA.
This commit is contained in:
Patrick Walton 2017-10-27 15:12:33 -07:00
parent e4b531f3a0
commit bf3779bf89
6 changed files with 302 additions and 100 deletions

View File

@ -39,7 +39,11 @@ const SEGMENT_POINT_RADIUS: number = 3.0;
const SEGMENT_STROKE_WIDTH: number = 1.0;
const SEGMENT_CONTROL_POINT_STROKE_WIDTH: number = 1.0;
const NORMAL_LENGTH: number = 14.0;
const NORMAL_LENGTHS: NormalStyleParameter<number> = {
bVertex: 10.0,
edge: 14.0,
};
const NORMAL_ARROWHEAD_LENGTH: number = 4.0;
const NORMAL_ARROWHEAD_ANGLE: number = Math.PI * 5.0 / 6.0;
@ -48,9 +52,13 @@ const SEGMENT_POINT_FILL_STYLE: string = "rgb(0, 0, 128)";
const LIGHT_STROKE_STYLE: string = "rgb(192, 192, 192)";
const LINE_STROKE_STYLE: string = "rgb(0, 128, 0)";
const CURVE_STROKE_STYLE: string = "rgb(128, 0, 0)";
const NORMAL_STROKE_STYLE: string = '#cc5500';
const SEGMENT_CONTROL_POINT_STROKE_STYLE: string = "rgb(0, 0, 128)";
const NORMAL_STROKE_STYLES: NormalStyleParameter<string> = {
bVertex: '#e6aa00',
edge: '#cc5500',
};
const BUILTIN_URIS = {
font: BUILTIN_FONT_URI,
svg: BUILTIN_SVG_URI,
@ -60,6 +68,13 @@ const SVG_SCALE: number = 1.0;
type FileType = 'font' | 'svg';
type NormalType = 'edge' | 'bVertex';
interface NormalStyleParameter<T> {
edge: T;
bVertex: T;
}
interface NormalsTable<T> {
lowerCurve: T;
lowerLine: T;
@ -245,6 +260,7 @@ class MeshDebuggerView extends PathfinderView {
const bQuads = new Uint32Array(meshes.bQuads);
const positions = new Float32Array(meshes.bVertexPositions);
const bVertexNormals = new Float32Array(meshes.bVertexNormals);
const normals: NormalsTable<Float32Array> = {
lowerCurve: new Float32Array(0),
@ -301,44 +317,54 @@ class MeshDebuggerView extends PathfinderView {
invScaleFactor);
context.beginPath();
context.moveTo(upperLeftPosition[0], upperLeftPosition[1]);
context.moveTo(upperLeftPosition[0], -upperLeftPosition[1]);
if (upperControlPointPosition != null) {
context.strokeStyle = CURVE_STROKE_STYLE;
context.quadraticCurveTo(upperControlPointPosition[0],
upperControlPointPosition[1],
-upperControlPointPosition[1],
upperRightPosition[0],
upperRightPosition[1]);
-upperRightPosition[1]);
} else {
context.strokeStyle = LINE_STROKE_STYLE;
context.lineTo(upperRightPosition[0], upperRightPosition[1]);
context.lineTo(upperRightPosition[0], -upperRightPosition[1]);
}
context.stroke();
context.strokeStyle = LIGHT_STROKE_STYLE;
context.beginPath();
context.moveTo(upperRightPosition[0], upperRightPosition[1]);
context.lineTo(lowerRightPosition[0], lowerRightPosition[1]);
context.moveTo(upperRightPosition[0], -upperRightPosition[1]);
context.lineTo(lowerRightPosition[0], -lowerRightPosition[1]);
context.stroke();
context.beginPath();
context.moveTo(lowerRightPosition[0], lowerRightPosition[1]);
context.moveTo(lowerRightPosition[0], -lowerRightPosition[1]);
if (lowerControlPointPosition != null) {
context.strokeStyle = CURVE_STROKE_STYLE;
context.quadraticCurveTo(lowerControlPointPosition[0],
lowerControlPointPosition[1],
-lowerControlPointPosition[1],
lowerLeftPosition[0],
lowerLeftPosition[1]);
-lowerLeftPosition[1]);
} else {
context.strokeStyle = LINE_STROKE_STYLE;
context.lineTo(lowerLeftPosition[0], lowerLeftPosition[1]);
context.lineTo(lowerLeftPosition[0], -lowerLeftPosition[1]);
}
context.stroke();
context.strokeStyle = LIGHT_STROKE_STYLE;
context.beginPath();
context.moveTo(lowerLeftPosition[0], lowerLeftPosition[1]);
context.lineTo(upperLeftPosition[0], upperLeftPosition[1]);
context.moveTo(lowerLeftPosition[0], -lowerLeftPosition[1]);
context.lineTo(upperLeftPosition[0], -upperLeftPosition[1]);
context.stroke();
// Draw B-quad normals.
const lowerLeftNormal = bVertexNormals[lowerLeftIndex];
const lowerRightNormal = bVertexNormals[lowerRightIndex];
const upperLeftNormal = bVertexNormals[upperLeftIndex];
const upperRightNormal = bVertexNormals[upperRightIndex];
drawNormal(context, lowerLeftPosition, lowerLeftNormal, invScaleFactor, 'bVertex');
drawNormal(context, lowerRightPosition, lowerRightNormal, invScaleFactor, 'bVertex');
drawNormal(context, upperLeftPosition, upperLeftNormal, invScaleFactor, 'bVertex');
drawNormal(context, upperRightPosition, upperRightNormal, invScaleFactor, 'bVertex');
}
// Draw segments.
@ -363,10 +389,10 @@ class MeshDebuggerView extends PathfinderView {
}
}
function getPosition(positions: Float32Array, vertexIndex: number): Float32Array | null {
function getPosition(positions: Float32Array, vertexIndex: number): glmatrix.vec2 | null {
if (vertexIndex === UINT32_MAX)
return null;
return new Float32Array([positions[vertexIndex * 2 + 0], -positions[vertexIndex * 2 + 1]]);
return glmatrix.vec2.clone([positions[vertexIndex * 2 + 0], positions[vertexIndex * 2 + 1]]);
}
function getNormals(normals: NormalsTable<Float32Array>,
@ -426,10 +452,10 @@ function drawSegmentVertices(context: CanvasRenderingContext2D,
if (controlPoint != null)
drawSegmentControlPoint(context, controlPoint, invScaleFactor);
drawNormal(context, position0, normal0, invScaleFactor);
drawNormal(context, position1, normal1, invScaleFactor);
drawNormal(context, position0, normal0, invScaleFactor, 'edge');
drawNormal(context, position1, normal1, invScaleFactor, 'edge');
if (controlPoint != null && normalControlPoint != null)
drawNormal(context, controlPoint, normalControlPoint, invScaleFactor);
drawNormal(context, controlPoint, normalControlPoint, invScaleFactor, 'edge');
}
}
@ -443,15 +469,15 @@ function drawVertexIfNecessary(context: CanvasRenderingContext2D,
markedVertices[vertexIndex] = true;
context.beginPath();
context.moveTo(position[0], position[1]);
context.arc(position[0], position[1], POINT_RADIUS * invScaleFactor, 0, 2.0 * Math.PI);
context.moveTo(position[0], -position[1]);
context.arc(position[0], -position[1], POINT_RADIUS * invScaleFactor, 0, 2.0 * Math.PI);
context.fill();
context.save();
context.scale(invScaleFactor, invScaleFactor);
context.fillText("" + vertexIndex,
position[0] / invScaleFactor + POINT_LABEL_OFFSET[0],
position[1] / invScaleFactor + POINT_LABEL_OFFSET[1]);
-position[1] / invScaleFactor + POINT_LABEL_OFFSET[1]);
context.restore();
}
@ -490,14 +516,15 @@ function drawSegmentControlPoint(context: CanvasRenderingContext2D,
function drawNormal(context: CanvasRenderingContext2D,
position: glmatrix.vec2,
normalAngle: number,
invScaleFactor: number) {
const length = invScaleFactor * NORMAL_LENGTH;
invScaleFactor: number,
normalType: NormalType) {
const length = invScaleFactor * NORMAL_LENGTHS[normalType];
const arrowheadLength = invScaleFactor * NORMAL_ARROWHEAD_LENGTH;
const endpoint = glmatrix.vec2.clone([position[0] + length * Math.cos(normalAngle),
-position[1] + length * Math.sin(normalAngle)]);
context.save();
context.strokeStyle = NORMAL_STROKE_STYLE;
context.strokeStyle = NORMAL_STROKE_STYLES[normalType];
context.beginPath();
context.moveTo(position[0], -position[1]);
context.lineTo(endpoint[0], endpoint[1]);

View File

@ -65,6 +65,7 @@ const B_QUAD_FIELD_COUNT: number = B_QUAD_SIZE / UINT32_SIZE;
const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
bQuads: { type: 'Uint32', size: B_QUAD_FIELD_COUNT },
bVertexLoopBlinnData: { type: 'Uint32', size: 1 },
bVertexNormals: { type: 'Float32', size: 1 },
bVertexPathIDs: { type: 'Uint16', size: 1 },
bVertexPositions: { type: 'Float32', size: 2 },
coverCurveIndices: { type: 'Uint32', size: 1 },
@ -90,6 +91,7 @@ const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
const BUFFER_TYPES: Meshes<BufferType> = {
bQuads: 'ARRAY_BUFFER',
bVertexLoopBlinnData: 'ARRAY_BUFFER',
bVertexNormals: 'ARRAY_BUFFER',
bVertexPathIDs: 'ARRAY_BUFFER',
bVertexPositions: 'ARRAY_BUFFER',
coverCurveIndices: 'ELEMENT_ARRAY_BUFFER',
@ -122,6 +124,7 @@ const MESH_LIBRARY_FOURCC: string = 'PFML';
const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
bqua: 'bQuads',
bvlb: 'bVertexLoopBlinnData',
bvno: 'bVertexNormals',
bvpi: 'bVertexPathIDs',
bvpo: 'bVertexPositions',
cvci: 'coverCurveIndices',
@ -151,6 +154,7 @@ export interface Meshes<T> {
readonly bVertexPositions: T;
readonly bVertexPathIDs: T;
readonly bVertexLoopBlinnData: T;
readonly bVertexNormals: T;
readonly coverInteriorIndices: T;
readonly coverCurveIndices: T;
readonly edgeBoundingBoxPathIDs: T;
@ -176,6 +180,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer> {
readonly bVertexPositions: ArrayBuffer;
readonly bVertexPathIDs: ArrayBuffer;
readonly bVertexLoopBlinnData: ArrayBuffer;
readonly bVertexNormals: ArrayBuffer;
readonly coverInteriorIndices: ArrayBuffer;
readonly coverCurveIndices: ArrayBuffer;
readonly edgeBoundingBoxPathIDs: ArrayBuffer;
@ -357,6 +362,7 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer> {
readonly bVertexPositions: WebGLBuffer;
readonly bVertexPathIDs: WebGLBuffer;
readonly bVertexLoopBlinnData: WebGLBuffer;
readonly bVertexNormals: WebGLBuffer;
readonly coverInteriorIndices: WebGLBuffer;
readonly coverCurveIndices: WebGLBuffer;
readonly edgeBoundingBoxPathIDs: WebGLBuffer;

View File

@ -29,7 +29,7 @@ pub mod capi;
pub mod mesh_library;
pub mod partitioner;
mod bold;
mod normal;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]

View File

@ -17,7 +17,7 @@ use std::io::{self, ErrorKind, Seek, SeekFrom, Write};
use std::ops::Range;
use std::u32;
use bold;
use normal;
use {BQuad, BVertexLoopBlinnData};
#[derive(Debug, Clone)]
@ -26,6 +26,7 @@ pub struct MeshLibrary {
pub b_vertex_positions: Vec<Point2D<f32>>,
pub b_vertex_path_ids: Vec<u16>,
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
pub b_vertex_normals: Vec<f32>,
pub cover_indices: MeshLibraryCoverIndices,
pub edge_data: MeshLibraryEdgeData,
pub segments: MeshLibrarySegments,
@ -40,6 +41,7 @@ impl MeshLibrary {
b_vertex_positions: vec![],
b_vertex_path_ids: vec![],
b_vertex_loop_blinn_data: vec![],
b_vertex_normals: vec![],
cover_indices: MeshLibraryCoverIndices::new(),
edge_data: MeshLibraryEdgeData::new(),
segments: MeshLibrarySegments::new(),
@ -52,12 +54,24 @@ impl MeshLibrary {
self.b_vertex_positions.clear();
self.b_vertex_path_ids.clear();
self.b_vertex_loop_blinn_data.clear();
self.b_vertex_normals.clear();
self.cover_indices.clear();
self.edge_data.clear();
self.segments.clear();
self.segment_normals.clear();
}
pub(crate) fn add_b_vertex(&mut self,
position: &Point2D<f32>,
path_id: u16,
loop_blinn_data: &BVertexLoopBlinnData,
normal: f32) {
self.b_vertex_positions.push(*position);
self.b_vertex_path_ids.push(path_id);
self.b_vertex_loop_blinn_data.push(*loop_blinn_data);
self.b_vertex_normals.push(normal);
}
pub(crate) fn add_b_quad(&mut self, b_quad: &BQuad) {
self.b_quads.push(*b_quad);
@ -176,7 +190,7 @@ impl MeshLibrary {
/// Computes vertex normals necessary for emboldening and/or stem darkening.
pub fn push_normals<I>(&mut self, stream: I) where I: Iterator<Item = PathCommand> {
bold::push_normals(self, stream)
normal::push_normals(self, stream)
}
/// Writes this mesh library to a RIFF file.
@ -195,6 +209,7 @@ impl MeshLibrary {
try!(write_chunk(writer, b"bvpo", &self.b_vertex_positions));
try!(write_chunk(writer, b"bvpi", &self.b_vertex_path_ids));
try!(write_chunk(writer, b"bvlb", &self.b_vertex_loop_blinn_data));
try!(write_chunk(writer, b"bvno", &self.b_vertex_normals));
try!(write_chunk(writer, b"cvii", &self.cover_indices.interior_indices));
try!(write_chunk(writer, b"cvci", &self.cover_indices.curve_indices));
try!(write_chunk(writer, b"ebbv", &self.edge_data.bounding_box_vertex_positions));

View File

@ -1,4 +1,4 @@
// pathfinder/partitioner/src/bold.rs
// pathfinder/partitioner/src/normal.rs
//
// Copyright © 2017 The Pathfinder Project Developers.
//
@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Infrastructure to enable on-GPU emboldening and stem darkening.
//! Utility functions for vertex normals.
use euclid::{Point2D, Vector2D};
use pathfinder_path_utils::{PathCommand, PathSegment, PathSegmentStream};
@ -48,20 +48,20 @@ pub fn push_normals<I>(library: &mut MeshLibrary, stream: I)
};
}
let prev_vector = match prev_segment {
PathSegment::Line(endpoint_0, endpoint_1) => endpoint_1 - endpoint_0,
PathSegment::Curve(_, control_point, endpoint_1) => endpoint_1 - control_point,
};
let next_vector = match next_segment {
PathSegment::Line(endpoint_0, endpoint_1) => endpoint_1 - endpoint_0,
PathSegment::Curve(endpoint_0, control_point, _) => control_point - endpoint_0,
let next_vertex_normal = match (&prev_segment, &next_segment) {
(&PathSegment::Line(ref prev_endpoint, ref vertex_endpoint),
&PathSegment::Line(_, ref next_endpoint)) |
(&PathSegment::Curve(_, ref prev_endpoint, ref vertex_endpoint),
&PathSegment::Line(_, ref next_endpoint)) |
(&PathSegment::Line(ref prev_endpoint, ref vertex_endpoint),
&PathSegment::Curve(_, ref next_endpoint, _)) |
(&PathSegment::Curve(_, ref prev_endpoint, ref vertex_endpoint),
&PathSegment::Curve(_, ref next_endpoint, _)) => {
calculate_vertex_normal(prev_endpoint, vertex_endpoint, next_endpoint)
}
};
let prev_edge_normal = Vector2D::new(-prev_vector.y, prev_vector.x).normalize();
let next_edge_normal = Vector2D::new(-next_vector.y, next_vector.x).normalize();
let next_vertex_normal = (prev_edge_normal + next_edge_normal) * 0.5;
let next_vertex_angle = (-next_vertex_normal.y).atan2(next_vertex_normal.x);
let next_vertex_angle = calculate_normal_angle(&next_vertex_normal);
let prev_vertex_angle = if !is_first_segment {
match index_of_prev_segment.unwrap() {
@ -86,14 +86,12 @@ pub fn push_normals<I>(library: &mut MeshLibrary, stream: I)
endpoint_1: next_vertex_angle,
});
}
PathSegment::Curve(endpoint_0, control_point, _) => {
let prev_prev_vector = control_point - endpoint_0;
let prev_prev_edge_normal = Vector2D::new(-prev_prev_vector.y,
prev_prev_vector.x).normalize();
let control_point_vertex_normal = (prev_prev_edge_normal + prev_edge_normal) * 0.5;
PathSegment::Curve(endpoint_0, control_point, endpoint_1) => {
let control_point_vertex_normal = calculate_vertex_normal(&endpoint_0,
&control_point,
&endpoint_1);
let control_point_vertex_angle =
(-control_point_vertex_normal.y).atan2(control_point_vertex_normal.x);
calculate_normal_angle(&control_point_vertex_normal);
index_of_prev_segment =
Some(SegmentIndex::Curve(library.segment_normals.curve_normals.len()));
@ -120,6 +118,23 @@ pub fn push_normals<I>(library: &mut MeshLibrary, stream: I)
}
}
pub fn calculate_vertex_normal(prev_position: &Point2D<f32>,
vertex_position: &Point2D<f32>,
next_position: &Point2D<f32>)
-> Vector2D<f32> {
let prev_edge_vector = *vertex_position - *prev_position;
let next_edge_vector = *next_position - *vertex_position;
let prev_edge_normal = Vector2D::new(-prev_edge_vector.y, prev_edge_vector.x).normalize();
let next_edge_normal = Vector2D::new(-next_edge_vector.y, next_edge_vector.x).normalize();
(prev_edge_normal + next_edge_normal) * 0.5
}
pub fn calculate_normal_angle(normal: &Vector2D<f32>) -> f32 {
(-normal.y).atan2(normal.x)
}
#[derive(Clone, Copy, Debug)]
enum SegmentIndex {
Line(usize),

View File

@ -10,7 +10,7 @@
use bit_vec::BitVec;
use euclid::approxeq::ApproxEq;
use euclid::Point2D;
use euclid::{Point2D, Vector2D};
use log::LogLevel;
use pathfinder_path_utils::PathBuffer;
use pathfinder_path_utils::curve::Curve;
@ -19,9 +19,11 @@ use std::collections::BinaryHeap;
use std::cmp::Ordering;
use std::f32;
use std::iter;
use std::ops::{Add, AddAssign};
use std::u32;
use mesh_library::{MeshLibrary, MeshLibraryIndexRanges};
use normal;
use {BQuad, BVertexLoopBlinnData, BVertexKind, Endpoint, FillRule, Subpath};
const MAX_B_QUAD_SUBDIVISIONS: u8 = 8;
@ -38,6 +40,7 @@ pub struct Partitioner<'a> {
heap: BinaryHeap<Point>,
visited_points: BitVec,
active_edges: Vec<ActiveEdge>,
vertex_normals: Vec<VertexNormal>,
path_id: u16,
}
@ -56,6 +59,7 @@ impl<'a> Partitioner<'a> {
heap: BinaryHeap::new(),
visited_points: BitVec::new(),
active_edges: vec![],
vertex_normals: vec![],
path_id: 0,
}
}
@ -112,10 +116,14 @@ impl<'a> Partitioner<'a> {
while self.process_next_point() {}
debug_assert!(self.library.b_vertex_loop_blinn_data.len() ==
self.library.b_vertex_path_ids.len());
debug_assert!(self.library.b_vertex_loop_blinn_data.len() ==
self.library.b_vertex_positions.len());
self.write_normals_to_library();
debug_assert_eq!(self.library.b_vertex_loop_blinn_data.len(),
self.library.b_vertex_path_ids.len());
debug_assert_eq!(self.library.b_vertex_loop_blinn_data.len(),
self.library.b_vertex_positions.len());
debug_assert_eq!(self.library.b_vertex_loop_blinn_data.len(),
self.library.b_vertex_normals.len());
let end_lengths = self.library.snapshot_lengths();
MeshLibraryIndexRanges::new(&start_lengths, &end_lengths)
@ -209,10 +217,11 @@ impl<'a> Partitioner<'a> {
active_edge.left_vertex_index = self.library.b_vertex_loop_blinn_data.len() as u32;
active_edge.control_point_vertex_index = active_edge.left_vertex_index + 1;
self.library.b_vertex_positions.push(endpoint_position);
self.library.b_vertex_path_ids.push(self.path_id);
self.library.b_vertex_loop_blinn_data.push(BVertexLoopBlinnData::new(
active_edge.endpoint_kind()));
// FIXME(pcwalton): Normal
self.library.add_b_vertex(&endpoint_position,
self.path_id,
&BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
0.0);
active_edge.toggle_parity();
}
@ -251,9 +260,12 @@ impl<'a> Partitioner<'a> {
&control_point_position,
&new_point.position,
bottom);
self.library.b_vertex_positions.push(*control_point_position);
self.library.b_vertex_path_ids.push(self.path_id);
self.library.b_vertex_loop_blinn_data.push(control_point_b_vertex_loop_blinn_data);
// FIXME(pcwalton): Normal
self.library.add_b_vertex(control_point_position,
self.path_id,
&control_point_b_vertex_loop_blinn_data,
0.0);
}
}
}
@ -315,6 +327,25 @@ impl<'a> Partitioner<'a> {
}
}
fn write_normals_to_library(&mut self) {
for (b_vertex_index, vertex_normal) in self.vertex_normals.iter().enumerate() {
debug_assert!(b_vertex_index <= self.library.b_vertex_normals.len());
let angle = vertex_normal.angle();
if b_vertex_index == self.library.b_vertex_normals.len() {
self.library.b_vertex_normals.push(angle)
} else {
self.library.b_vertex_normals[b_vertex_index as usize] = angle
}
}
let remaining_b_vertex_count = self.library.b_vertex_positions.len() -
self.library.b_vertex_normals.len();
if remaining_b_vertex_count > 0 {
self.library.b_vertex_normals.extend(iter::repeat(0.0).take(remaining_b_vertex_count))
}
}
fn add_new_edges_for_min_point(&mut self, endpoint_index: u32, next_active_edge_index: u32) {
// FIXME(pcwalton): This is twice as slow as it needs to be.
self.active_edges.insert(next_active_edge_index as usize, ActiveEdge::default());
@ -330,11 +361,12 @@ impl<'a> Partitioner<'a> {
new_active_edges[0].left_vertex_index = left_vertex_index;
new_active_edges[1].left_vertex_index = left_vertex_index;
// FIXME(pcwalton): Normal
let position = self.endpoints[endpoint_index as usize].position;
self.library.b_vertex_positions.push(position);
self.library.b_vertex_path_ids.push(self.path_id);
self.library.b_vertex_loop_blinn_data
.push(BVertexLoopBlinnData::new(BVertexKind::Endpoint0));
self.library.add_b_vertex(&position,
self.path_id,
&BVertexLoopBlinnData::new(BVertexKind::Endpoint0),
0.0);
new_active_edges[0].toggle_parity();
new_active_edges[1].toggle_parity();
@ -382,9 +414,12 @@ impl<'a> Partitioner<'a> {
&control_point_position,
&right_vertex_position,
false);
self.library.b_vertex_positions.push(control_point_position);
self.library.b_vertex_path_ids.push(self.path_id);
self.library.b_vertex_loop_blinn_data.push(control_point_b_vertex_loop_blinn_data);
// FIXME(pcwalton): Normal
self.library.add_b_vertex(&control_point_position,
self.path_id,
&control_point_b_vertex_loop_blinn_data,
0.0)
}
}
@ -403,9 +438,12 @@ impl<'a> Partitioner<'a> {
&control_point_position,
&right_vertex_position,
true);
self.library.b_vertex_positions.push(control_point_position);
self.library.b_vertex_path_ids.push(self.path_id);
self.library.b_vertex_loop_blinn_data.push(control_point_b_vertex_loop_blinn_data);
// FIXME(pcwalton): Normal
self.library.add_b_vertex(&control_point_position,
self.path_id,
&control_point_b_vertex_loop_blinn_data,
0.0)
}
}
}
@ -737,12 +775,16 @@ impl<'a> Partitioner<'a> {
}
}
self.library.add_b_quad(&BQuad::new(upper_subdivision.left_curve_left,
upper_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
lower_subdivision.left_curve_left,
lower_subdivision.left_curve_control_point,
lower_subdivision.middle_point))
let b_quad = BQuad::new(upper_subdivision.left_curve_left,
upper_subdivision.left_curve_control_point,
upper_subdivision.middle_point,
lower_subdivision.left_curve_left,
lower_subdivision.left_curve_control_point,
lower_subdivision.middle_point);
self.update_vertex_normals_for_new_b_quad(&b_quad);
self.library.add_b_quad(&b_quad);
}
fn subdivide_active_edge_again_at_t(&mut self,
@ -779,6 +821,8 @@ impl<'a> Partitioner<'a> {
bottom),
].into_iter());
// FIXME(pcwalton): Normal
(SubdividedActiveEdge {
left_curve_left: subdivision.left_curve_left,
left_curve_control_point: left_control_point_index,
@ -1001,14 +1045,15 @@ impl<'a> Partitioner<'a> {
let path_id = self.library.b_vertex_path_ids[left_curve_left as usize];
let right_point = self.endpoints[active_edge.right_endpoint_index as usize]
.position;
let middle_point = left_point_position.to_vector().lerp(right_point.to_vector(), t);
let middle_point = left_point_position.to_vector()
.lerp(right_point.to_vector(), t);
// FIXME(pcwalton): Normal
active_edge.left_vertex_index = self.library.b_vertex_loop_blinn_data.len() as u32;
self.library.b_vertex_positions.push(middle_point.to_point());
self.library.b_vertex_path_ids.push(path_id);
self.library
.b_vertex_loop_blinn_data
.push(BVertexLoopBlinnData::new(active_edge.endpoint_kind()));
self.library.add_b_vertex(&middle_point.to_point(),
path_id,
&BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
0.0);
left_curve_control_point_vertex_index = u32::MAX;
}
@ -1030,23 +1075,27 @@ impl<'a> Partitioner<'a> {
active_edge.left_vertex_index = left_curve_control_point_vertex_index + 1;
active_edge.control_point_vertex_index = left_curve_control_point_vertex_index + 2;
self.library.b_vertex_positions.extend([
left_curve.control_point,
left_curve.endpoints[1],
right_curve.control_point,
].into_iter());
self.library.b_vertex_path_ids.extend(iter::repeat(self.path_id).take(3));
self.library.b_vertex_loop_blinn_data.extend([
BVertexLoopBlinnData::control_point(&left_curve.endpoints[0],
&left_curve.control_point,
&left_curve.endpoints[1],
bottom),
BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
BVertexLoopBlinnData::control_point(&right_curve.endpoints[0],
&right_curve.control_point,
&right_curve.endpoints[1],
bottom),
].into_iter());
// FIXME(pcwalton): Normals
self.library
.add_b_vertex(&left_curve.control_point,
self.path_id,
&BVertexLoopBlinnData::control_point(&left_curve.endpoints[0],
&left_curve.control_point,
&left_curve.endpoints[1],
bottom),
0.0);
self.library.add_b_vertex(&left_curve.endpoints[1],
self.path_id,
&BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
0.0);
self.library
.add_b_vertex(&right_curve.control_point,
self.path_id,
&BVertexLoopBlinnData::control_point(&right_curve.endpoints[0],
&right_curve.control_point,
&right_curve.endpoints[1],
bottom),
0.0);
}
}
@ -1057,6 +1106,55 @@ impl<'a> Partitioner<'a> {
}
}
fn update_vertex_normals_for_new_b_quad(&mut self, b_quad: &BQuad) {
self.update_vertex_normal_for_b_quad_edge(b_quad.upper_left_vertex_index,
b_quad.upper_control_point_vertex_index,
b_quad.upper_right_vertex_index);
self.update_vertex_normal_for_b_quad_edge(b_quad.lower_right_vertex_index,
b_quad.lower_control_point_vertex_index,
b_quad.lower_left_vertex_index);
}
fn update_vertex_normal_for_b_quad_edge(&mut self,
prev_vertex_index: u32,
control_point_vertex_index: u32,
next_vertex_index: u32) {
if control_point_vertex_index == u32::MAX {
let normal_vector = self.calculate_normal_for_edge(prev_vertex_index,
next_vertex_index);
self.update_normal_for_vertex(prev_vertex_index, &normal_vector);
self.update_normal_for_vertex(next_vertex_index, &normal_vector);
return
}
let prev_normal_vector = self.calculate_normal_for_edge(prev_vertex_index,
control_point_vertex_index);
let next_normal_vector = self.calculate_normal_for_edge(control_point_vertex_index,
next_vertex_index);
self.update_normal_for_vertex(prev_vertex_index, &prev_normal_vector);
self.update_normal_for_vertex(control_point_vertex_index, &prev_normal_vector);
self.update_normal_for_vertex(control_point_vertex_index, &next_normal_vector);
self.update_normal_for_vertex(next_vertex_index, &next_normal_vector);
}
fn update_normal_for_vertex(&mut self, vertex_index: u32, normal_vector: &VertexNormal) {
let vertex_normal_count = self.vertex_normals.len();
if vertex_index as usize >= vertex_normal_count {
let new_vertex_normal_count = vertex_index as usize - vertex_normal_count + 1;
self.vertex_normals
.extend(iter::repeat(VertexNormal::zero()).take(new_vertex_normal_count));
}
self.vertex_normals[vertex_index as usize] += *normal_vector
}
fn calculate_normal_for_edge(&self, left_vertex_index: u32, right_vertex_index: u32)
-> VertexNormal {
let left_vertex_position = &self.library.b_vertex_positions[left_vertex_index as usize];
let right_vertex_position = &self.library.b_vertex_positions[right_vertex_index as usize];
VertexNormal::new(left_vertex_position, right_vertex_position)
}
fn prev_endpoint_of(&self, endpoint_index: u32) -> u32 {
let endpoint = &self.endpoints[endpoint_index as usize];
let subpath = &self.subpaths[endpoint.subpath_index as usize];
@ -1257,3 +1355,44 @@ enum SubdivisionType {
Inside,
Lower,
}
/// TODO(pcwalton): This could possibly be improved:
/// https://en.wikipedia.org/wiki/Mean_of_circular_quantities
#[derive(Debug, Clone, Copy)]
struct VertexNormal {
sum: Vector2D<f32>,
}
impl VertexNormal {
fn new(vertex_a: &Point2D<f32>, vertex_b: &Point2D<f32>) -> VertexNormal {
let vector = *vertex_a - *vertex_b;
VertexNormal {
sum: Vector2D::new(-vector.y, vector.x).normalize(),
}
}
fn zero() -> VertexNormal {
VertexNormal {
sum: Vector2D::zero(),
}
}
fn angle(&self) -> f32 {
normal::calculate_normal_angle(&self.sum)
}
}
impl Add<VertexNormal> for VertexNormal {
type Output = VertexNormal;
fn add(self, rhs: VertexNormal) -> VertexNormal {
VertexNormal {
sum: self.sum + rhs.sum,
}
}
}
impl AddAssign<VertexNormal> for VertexNormal {
fn add_assign(&mut self, rhs: VertexNormal) {
*self = *self + rhs
}
}