Calculate normals for B-vertices.
I'm planning to use this for fixing hairlines in XCAA.
This commit is contained in:
parent
e4b531f3a0
commit
bf3779bf89
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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),
|
|
@ -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.write_normals_to_library();
|
||||
|
||||
debug_assert_eq!(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() ==
|
||||
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,
|
||||
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))
|
||||
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],
|
||||
// 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),
|
||||
BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
|
||||
BVertexLoopBlinnData::control_point(&right_curve.endpoints[0],
|
||||
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),
|
||||
].into_iter());
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue