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_STROKE_WIDTH: number = 1.0;
|
||||||
const SEGMENT_CONTROL_POINT_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_LENGTH: number = 4.0;
|
||||||
const NORMAL_ARROWHEAD_ANGLE: number = Math.PI * 5.0 / 6.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 LIGHT_STROKE_STYLE: string = "rgb(192, 192, 192)";
|
||||||
const LINE_STROKE_STYLE: string = "rgb(0, 128, 0)";
|
const LINE_STROKE_STYLE: string = "rgb(0, 128, 0)";
|
||||||
const CURVE_STROKE_STYLE: string = "rgb(128, 0, 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 SEGMENT_CONTROL_POINT_STROKE_STYLE: string = "rgb(0, 0, 128)";
|
||||||
|
|
||||||
|
const NORMAL_STROKE_STYLES: NormalStyleParameter<string> = {
|
||||||
|
bVertex: '#e6aa00',
|
||||||
|
edge: '#cc5500',
|
||||||
|
};
|
||||||
|
|
||||||
const BUILTIN_URIS = {
|
const BUILTIN_URIS = {
|
||||||
font: BUILTIN_FONT_URI,
|
font: BUILTIN_FONT_URI,
|
||||||
svg: BUILTIN_SVG_URI,
|
svg: BUILTIN_SVG_URI,
|
||||||
|
@ -60,6 +68,13 @@ const SVG_SCALE: number = 1.0;
|
||||||
|
|
||||||
type FileType = 'font' | 'svg';
|
type FileType = 'font' | 'svg';
|
||||||
|
|
||||||
|
type NormalType = 'edge' | 'bVertex';
|
||||||
|
|
||||||
|
interface NormalStyleParameter<T> {
|
||||||
|
edge: T;
|
||||||
|
bVertex: T;
|
||||||
|
}
|
||||||
|
|
||||||
interface NormalsTable<T> {
|
interface NormalsTable<T> {
|
||||||
lowerCurve: T;
|
lowerCurve: T;
|
||||||
lowerLine: T;
|
lowerLine: T;
|
||||||
|
@ -245,6 +260,7 @@ class MeshDebuggerView extends PathfinderView {
|
||||||
|
|
||||||
const bQuads = new Uint32Array(meshes.bQuads);
|
const bQuads = new Uint32Array(meshes.bQuads);
|
||||||
const positions = new Float32Array(meshes.bVertexPositions);
|
const positions = new Float32Array(meshes.bVertexPositions);
|
||||||
|
const bVertexNormals = new Float32Array(meshes.bVertexNormals);
|
||||||
|
|
||||||
const normals: NormalsTable<Float32Array> = {
|
const normals: NormalsTable<Float32Array> = {
|
||||||
lowerCurve: new Float32Array(0),
|
lowerCurve: new Float32Array(0),
|
||||||
|
@ -301,44 +317,54 @@ class MeshDebuggerView extends PathfinderView {
|
||||||
invScaleFactor);
|
invScaleFactor);
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(upperLeftPosition[0], upperLeftPosition[1]);
|
context.moveTo(upperLeftPosition[0], -upperLeftPosition[1]);
|
||||||
if (upperControlPointPosition != null) {
|
if (upperControlPointPosition != null) {
|
||||||
context.strokeStyle = CURVE_STROKE_STYLE;
|
context.strokeStyle = CURVE_STROKE_STYLE;
|
||||||
context.quadraticCurveTo(upperControlPointPosition[0],
|
context.quadraticCurveTo(upperControlPointPosition[0],
|
||||||
upperControlPointPosition[1],
|
-upperControlPointPosition[1],
|
||||||
upperRightPosition[0],
|
upperRightPosition[0],
|
||||||
upperRightPosition[1]);
|
-upperRightPosition[1]);
|
||||||
} else {
|
} else {
|
||||||
context.strokeStyle = LINE_STROKE_STYLE;
|
context.strokeStyle = LINE_STROKE_STYLE;
|
||||||
context.lineTo(upperRightPosition[0], upperRightPosition[1]);
|
context.lineTo(upperRightPosition[0], -upperRightPosition[1]);
|
||||||
}
|
}
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.strokeStyle = LIGHT_STROKE_STYLE;
|
context.strokeStyle = LIGHT_STROKE_STYLE;
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(upperRightPosition[0], upperRightPosition[1]);
|
context.moveTo(upperRightPosition[0], -upperRightPosition[1]);
|
||||||
context.lineTo(lowerRightPosition[0], lowerRightPosition[1]);
|
context.lineTo(lowerRightPosition[0], -lowerRightPosition[1]);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(lowerRightPosition[0], lowerRightPosition[1]);
|
context.moveTo(lowerRightPosition[0], -lowerRightPosition[1]);
|
||||||
if (lowerControlPointPosition != null) {
|
if (lowerControlPointPosition != null) {
|
||||||
context.strokeStyle = CURVE_STROKE_STYLE;
|
context.strokeStyle = CURVE_STROKE_STYLE;
|
||||||
context.quadraticCurveTo(lowerControlPointPosition[0],
|
context.quadraticCurveTo(lowerControlPointPosition[0],
|
||||||
lowerControlPointPosition[1],
|
-lowerControlPointPosition[1],
|
||||||
lowerLeftPosition[0],
|
lowerLeftPosition[0],
|
||||||
lowerLeftPosition[1]);
|
-lowerLeftPosition[1]);
|
||||||
} else {
|
} else {
|
||||||
context.strokeStyle = LINE_STROKE_STYLE;
|
context.strokeStyle = LINE_STROKE_STYLE;
|
||||||
context.lineTo(lowerLeftPosition[0], lowerLeftPosition[1]);
|
context.lineTo(lowerLeftPosition[0], -lowerLeftPosition[1]);
|
||||||
}
|
}
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.strokeStyle = LIGHT_STROKE_STYLE;
|
context.strokeStyle = LIGHT_STROKE_STYLE;
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(lowerLeftPosition[0], lowerLeftPosition[1]);
|
context.moveTo(lowerLeftPosition[0], -lowerLeftPosition[1]);
|
||||||
context.lineTo(upperLeftPosition[0], upperLeftPosition[1]);
|
context.lineTo(upperLeftPosition[0], -upperLeftPosition[1]);
|
||||||
context.stroke();
|
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.
|
// 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)
|
if (vertexIndex === UINT32_MAX)
|
||||||
return null;
|
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>,
|
function getNormals(normals: NormalsTable<Float32Array>,
|
||||||
|
@ -426,10 +452,10 @@ function drawSegmentVertices(context: CanvasRenderingContext2D,
|
||||||
if (controlPoint != null)
|
if (controlPoint != null)
|
||||||
drawSegmentControlPoint(context, controlPoint, invScaleFactor);
|
drawSegmentControlPoint(context, controlPoint, invScaleFactor);
|
||||||
|
|
||||||
drawNormal(context, position0, normal0, invScaleFactor);
|
drawNormal(context, position0, normal0, invScaleFactor, 'edge');
|
||||||
drawNormal(context, position1, normal1, invScaleFactor);
|
drawNormal(context, position1, normal1, invScaleFactor, 'edge');
|
||||||
if (controlPoint != null && normalControlPoint != null)
|
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;
|
markedVertices[vertexIndex] = true;
|
||||||
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(position[0], position[1]);
|
context.moveTo(position[0], -position[1]);
|
||||||
context.arc(position[0], position[1], POINT_RADIUS * invScaleFactor, 0, 2.0 * Math.PI);
|
context.arc(position[0], -position[1], POINT_RADIUS * invScaleFactor, 0, 2.0 * Math.PI);
|
||||||
context.fill();
|
context.fill();
|
||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.scale(invScaleFactor, invScaleFactor);
|
context.scale(invScaleFactor, invScaleFactor);
|
||||||
context.fillText("" + vertexIndex,
|
context.fillText("" + vertexIndex,
|
||||||
position[0] / invScaleFactor + POINT_LABEL_OFFSET[0],
|
position[0] / invScaleFactor + POINT_LABEL_OFFSET[0],
|
||||||
position[1] / invScaleFactor + POINT_LABEL_OFFSET[1]);
|
-position[1] / invScaleFactor + POINT_LABEL_OFFSET[1]);
|
||||||
context.restore();
|
context.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,14 +516,15 @@ function drawSegmentControlPoint(context: CanvasRenderingContext2D,
|
||||||
function drawNormal(context: CanvasRenderingContext2D,
|
function drawNormal(context: CanvasRenderingContext2D,
|
||||||
position: glmatrix.vec2,
|
position: glmatrix.vec2,
|
||||||
normalAngle: number,
|
normalAngle: number,
|
||||||
invScaleFactor: number) {
|
invScaleFactor: number,
|
||||||
const length = invScaleFactor * NORMAL_LENGTH;
|
normalType: NormalType) {
|
||||||
|
const length = invScaleFactor * NORMAL_LENGTHS[normalType];
|
||||||
const arrowheadLength = invScaleFactor * NORMAL_ARROWHEAD_LENGTH;
|
const arrowheadLength = invScaleFactor * NORMAL_ARROWHEAD_LENGTH;
|
||||||
const endpoint = glmatrix.vec2.clone([position[0] + length * Math.cos(normalAngle),
|
const endpoint = glmatrix.vec2.clone([position[0] + length * Math.cos(normalAngle),
|
||||||
-position[1] + length * Math.sin(normalAngle)]);
|
-position[1] + length * Math.sin(normalAngle)]);
|
||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
context.strokeStyle = NORMAL_STROKE_STYLE;
|
context.strokeStyle = NORMAL_STROKE_STYLES[normalType];
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(position[0], -position[1]);
|
context.moveTo(position[0], -position[1]);
|
||||||
context.lineTo(endpoint[0], endpoint[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> = {
|
const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
|
||||||
bQuads: { type: 'Uint32', size: B_QUAD_FIELD_COUNT },
|
bQuads: { type: 'Uint32', size: B_QUAD_FIELD_COUNT },
|
||||||
bVertexLoopBlinnData: { type: 'Uint32', size: 1 },
|
bVertexLoopBlinnData: { type: 'Uint32', size: 1 },
|
||||||
|
bVertexNormals: { type: 'Float32', size: 1 },
|
||||||
bVertexPathIDs: { type: 'Uint16', size: 1 },
|
bVertexPathIDs: { type: 'Uint16', size: 1 },
|
||||||
bVertexPositions: { type: 'Float32', size: 2 },
|
bVertexPositions: { type: 'Float32', size: 2 },
|
||||||
coverCurveIndices: { type: 'Uint32', size: 1 },
|
coverCurveIndices: { type: 'Uint32', size: 1 },
|
||||||
|
@ -90,6 +91,7 @@ const MESH_TYPES: Meshes<MeshBufferTypeDescriptor> = {
|
||||||
const BUFFER_TYPES: Meshes<BufferType> = {
|
const BUFFER_TYPES: Meshes<BufferType> = {
|
||||||
bQuads: 'ARRAY_BUFFER',
|
bQuads: 'ARRAY_BUFFER',
|
||||||
bVertexLoopBlinnData: 'ARRAY_BUFFER',
|
bVertexLoopBlinnData: 'ARRAY_BUFFER',
|
||||||
|
bVertexNormals: 'ARRAY_BUFFER',
|
||||||
bVertexPathIDs: 'ARRAY_BUFFER',
|
bVertexPathIDs: 'ARRAY_BUFFER',
|
||||||
bVertexPositions: 'ARRAY_BUFFER',
|
bVertexPositions: 'ARRAY_BUFFER',
|
||||||
coverCurveIndices: 'ELEMENT_ARRAY_BUFFER',
|
coverCurveIndices: 'ELEMENT_ARRAY_BUFFER',
|
||||||
|
@ -122,6 +124,7 @@ const MESH_LIBRARY_FOURCC: string = 'PFML';
|
||||||
const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
|
const BUFFER_TYPE_FOURCCS: BufferTypeFourCCTable = {
|
||||||
bqua: 'bQuads',
|
bqua: 'bQuads',
|
||||||
bvlb: 'bVertexLoopBlinnData',
|
bvlb: 'bVertexLoopBlinnData',
|
||||||
|
bvno: 'bVertexNormals',
|
||||||
bvpi: 'bVertexPathIDs',
|
bvpi: 'bVertexPathIDs',
|
||||||
bvpo: 'bVertexPositions',
|
bvpo: 'bVertexPositions',
|
||||||
cvci: 'coverCurveIndices',
|
cvci: 'coverCurveIndices',
|
||||||
|
@ -151,6 +154,7 @@ export interface Meshes<T> {
|
||||||
readonly bVertexPositions: T;
|
readonly bVertexPositions: T;
|
||||||
readonly bVertexPathIDs: T;
|
readonly bVertexPathIDs: T;
|
||||||
readonly bVertexLoopBlinnData: T;
|
readonly bVertexLoopBlinnData: T;
|
||||||
|
readonly bVertexNormals: T;
|
||||||
readonly coverInteriorIndices: T;
|
readonly coverInteriorIndices: T;
|
||||||
readonly coverCurveIndices: T;
|
readonly coverCurveIndices: T;
|
||||||
readonly edgeBoundingBoxPathIDs: T;
|
readonly edgeBoundingBoxPathIDs: T;
|
||||||
|
@ -176,6 +180,7 @@ export class PathfinderMeshData implements Meshes<ArrayBuffer> {
|
||||||
readonly bVertexPositions: ArrayBuffer;
|
readonly bVertexPositions: ArrayBuffer;
|
||||||
readonly bVertexPathIDs: ArrayBuffer;
|
readonly bVertexPathIDs: ArrayBuffer;
|
||||||
readonly bVertexLoopBlinnData: ArrayBuffer;
|
readonly bVertexLoopBlinnData: ArrayBuffer;
|
||||||
|
readonly bVertexNormals: ArrayBuffer;
|
||||||
readonly coverInteriorIndices: ArrayBuffer;
|
readonly coverInteriorIndices: ArrayBuffer;
|
||||||
readonly coverCurveIndices: ArrayBuffer;
|
readonly coverCurveIndices: ArrayBuffer;
|
||||||
readonly edgeBoundingBoxPathIDs: ArrayBuffer;
|
readonly edgeBoundingBoxPathIDs: ArrayBuffer;
|
||||||
|
@ -357,6 +362,7 @@ export class PathfinderMeshBuffers implements Meshes<WebGLBuffer> {
|
||||||
readonly bVertexPositions: WebGLBuffer;
|
readonly bVertexPositions: WebGLBuffer;
|
||||||
readonly bVertexPathIDs: WebGLBuffer;
|
readonly bVertexPathIDs: WebGLBuffer;
|
||||||
readonly bVertexLoopBlinnData: WebGLBuffer;
|
readonly bVertexLoopBlinnData: WebGLBuffer;
|
||||||
|
readonly bVertexNormals: WebGLBuffer;
|
||||||
readonly coverInteriorIndices: WebGLBuffer;
|
readonly coverInteriorIndices: WebGLBuffer;
|
||||||
readonly coverCurveIndices: WebGLBuffer;
|
readonly coverCurveIndices: WebGLBuffer;
|
||||||
readonly edgeBoundingBoxPathIDs: WebGLBuffer;
|
readonly edgeBoundingBoxPathIDs: WebGLBuffer;
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub mod capi;
|
||||||
pub mod mesh_library;
|
pub mod mesh_library;
|
||||||
pub mod partitioner;
|
pub mod partitioner;
|
||||||
|
|
||||||
mod bold;
|
mod normal;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[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::ops::Range;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
|
||||||
use bold;
|
use normal;
|
||||||
use {BQuad, BVertexLoopBlinnData};
|
use {BQuad, BVertexLoopBlinnData};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -26,6 +26,7 @@ pub struct MeshLibrary {
|
||||||
pub b_vertex_positions: Vec<Point2D<f32>>,
|
pub b_vertex_positions: Vec<Point2D<f32>>,
|
||||||
pub b_vertex_path_ids: Vec<u16>,
|
pub b_vertex_path_ids: Vec<u16>,
|
||||||
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
|
pub b_vertex_loop_blinn_data: Vec<BVertexLoopBlinnData>,
|
||||||
|
pub b_vertex_normals: Vec<f32>,
|
||||||
pub cover_indices: MeshLibraryCoverIndices,
|
pub cover_indices: MeshLibraryCoverIndices,
|
||||||
pub edge_data: MeshLibraryEdgeData,
|
pub edge_data: MeshLibraryEdgeData,
|
||||||
pub segments: MeshLibrarySegments,
|
pub segments: MeshLibrarySegments,
|
||||||
|
@ -40,6 +41,7 @@ impl MeshLibrary {
|
||||||
b_vertex_positions: vec![],
|
b_vertex_positions: vec![],
|
||||||
b_vertex_path_ids: vec![],
|
b_vertex_path_ids: vec![],
|
||||||
b_vertex_loop_blinn_data: vec![],
|
b_vertex_loop_blinn_data: vec![],
|
||||||
|
b_vertex_normals: vec![],
|
||||||
cover_indices: MeshLibraryCoverIndices::new(),
|
cover_indices: MeshLibraryCoverIndices::new(),
|
||||||
edge_data: MeshLibraryEdgeData::new(),
|
edge_data: MeshLibraryEdgeData::new(),
|
||||||
segments: MeshLibrarySegments::new(),
|
segments: MeshLibrarySegments::new(),
|
||||||
|
@ -52,12 +54,24 @@ impl MeshLibrary {
|
||||||
self.b_vertex_positions.clear();
|
self.b_vertex_positions.clear();
|
||||||
self.b_vertex_path_ids.clear();
|
self.b_vertex_path_ids.clear();
|
||||||
self.b_vertex_loop_blinn_data.clear();
|
self.b_vertex_loop_blinn_data.clear();
|
||||||
|
self.b_vertex_normals.clear();
|
||||||
self.cover_indices.clear();
|
self.cover_indices.clear();
|
||||||
self.edge_data.clear();
|
self.edge_data.clear();
|
||||||
self.segments.clear();
|
self.segments.clear();
|
||||||
self.segment_normals.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) {
|
pub(crate) fn add_b_quad(&mut self, b_quad: &BQuad) {
|
||||||
self.b_quads.push(*b_quad);
|
self.b_quads.push(*b_quad);
|
||||||
|
|
||||||
|
@ -176,7 +190,7 @@ impl MeshLibrary {
|
||||||
|
|
||||||
/// Computes vertex normals necessary for emboldening and/or stem darkening.
|
/// Computes vertex normals necessary for emboldening and/or stem darkening.
|
||||||
pub fn push_normals<I>(&mut self, stream: I) where I: Iterator<Item = PathCommand> {
|
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.
|
/// 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"bvpo", &self.b_vertex_positions));
|
||||||
try!(write_chunk(writer, b"bvpi", &self.b_vertex_path_ids));
|
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"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"cvii", &self.cover_indices.interior_indices));
|
||||||
try!(write_chunk(writer, b"cvci", &self.cover_indices.curve_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));
|
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.
|
// Copyright © 2017 The Pathfinder Project Developers.
|
||||||
//
|
//
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! Infrastructure to enable on-GPU emboldening and stem darkening.
|
//! Utility functions for vertex normals.
|
||||||
|
|
||||||
use euclid::{Point2D, Vector2D};
|
use euclid::{Point2D, Vector2D};
|
||||||
use pathfinder_path_utils::{PathCommand, PathSegment, PathSegmentStream};
|
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 {
|
let next_vertex_normal = match (&prev_segment, &next_segment) {
|
||||||
PathSegment::Line(endpoint_0, endpoint_1) => endpoint_1 - endpoint_0,
|
(&PathSegment::Line(ref prev_endpoint, ref vertex_endpoint),
|
||||||
PathSegment::Curve(_, control_point, endpoint_1) => endpoint_1 - control_point,
|
&PathSegment::Line(_, ref next_endpoint)) |
|
||||||
};
|
(&PathSegment::Curve(_, ref prev_endpoint, ref vertex_endpoint),
|
||||||
let next_vector = match next_segment {
|
&PathSegment::Line(_, ref next_endpoint)) |
|
||||||
PathSegment::Line(endpoint_0, endpoint_1) => endpoint_1 - endpoint_0,
|
(&PathSegment::Line(ref prev_endpoint, ref vertex_endpoint),
|
||||||
PathSegment::Curve(endpoint_0, control_point, _) => control_point - endpoint_0,
|
&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_vertex_angle = calculate_normal_angle(&next_vertex_normal);
|
||||||
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 prev_vertex_angle = if !is_first_segment {
|
let prev_vertex_angle = if !is_first_segment {
|
||||||
match index_of_prev_segment.unwrap() {
|
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,
|
endpoint_1: next_vertex_angle,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
PathSegment::Curve(endpoint_0, control_point, _) => {
|
PathSegment::Curve(endpoint_0, control_point, endpoint_1) => {
|
||||||
let prev_prev_vector = control_point - endpoint_0;
|
let control_point_vertex_normal = calculate_vertex_normal(&endpoint_0,
|
||||||
let prev_prev_edge_normal = Vector2D::new(-prev_prev_vector.y,
|
&control_point,
|
||||||
prev_prev_vector.x).normalize();
|
&endpoint_1);
|
||||||
|
|
||||||
let control_point_vertex_normal = (prev_prev_edge_normal + prev_edge_normal) * 0.5;
|
|
||||||
let control_point_vertex_angle =
|
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 =
|
index_of_prev_segment =
|
||||||
Some(SegmentIndex::Curve(library.segment_normals.curve_normals.len()));
|
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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum SegmentIndex {
|
enum SegmentIndex {
|
||||||
Line(usize),
|
Line(usize),
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
use euclid::approxeq::ApproxEq;
|
use euclid::approxeq::ApproxEq;
|
||||||
use euclid::Point2D;
|
use euclid::{Point2D, Vector2D};
|
||||||
use log::LogLevel;
|
use log::LogLevel;
|
||||||
use pathfinder_path_utils::PathBuffer;
|
use pathfinder_path_utils::PathBuffer;
|
||||||
use pathfinder_path_utils::curve::Curve;
|
use pathfinder_path_utils::curve::Curve;
|
||||||
|
@ -19,9 +19,11 @@ use std::collections::BinaryHeap;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::ops::{Add, AddAssign};
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
|
||||||
use mesh_library::{MeshLibrary, MeshLibraryIndexRanges};
|
use mesh_library::{MeshLibrary, MeshLibraryIndexRanges};
|
||||||
|
use normal;
|
||||||
use {BQuad, BVertexLoopBlinnData, BVertexKind, Endpoint, FillRule, Subpath};
|
use {BQuad, BVertexLoopBlinnData, BVertexKind, Endpoint, FillRule, Subpath};
|
||||||
|
|
||||||
const MAX_B_QUAD_SUBDIVISIONS: u8 = 8;
|
const MAX_B_QUAD_SUBDIVISIONS: u8 = 8;
|
||||||
|
@ -38,6 +40,7 @@ pub struct Partitioner<'a> {
|
||||||
heap: BinaryHeap<Point>,
|
heap: BinaryHeap<Point>,
|
||||||
visited_points: BitVec,
|
visited_points: BitVec,
|
||||||
active_edges: Vec<ActiveEdge>,
|
active_edges: Vec<ActiveEdge>,
|
||||||
|
vertex_normals: Vec<VertexNormal>,
|
||||||
path_id: u16,
|
path_id: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +59,7 @@ impl<'a> Partitioner<'a> {
|
||||||
heap: BinaryHeap::new(),
|
heap: BinaryHeap::new(),
|
||||||
visited_points: BitVec::new(),
|
visited_points: BitVec::new(),
|
||||||
active_edges: vec![],
|
active_edges: vec![],
|
||||||
|
vertex_normals: vec![],
|
||||||
path_id: 0,
|
path_id: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,10 +116,14 @@ impl<'a> Partitioner<'a> {
|
||||||
|
|
||||||
while self.process_next_point() {}
|
while self.process_next_point() {}
|
||||||
|
|
||||||
debug_assert!(self.library.b_vertex_loop_blinn_data.len() ==
|
self.write_normals_to_library();
|
||||||
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());
|
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();
|
let end_lengths = self.library.snapshot_lengths();
|
||||||
MeshLibraryIndexRanges::new(&start_lengths, &end_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.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;
|
active_edge.control_point_vertex_index = active_edge.left_vertex_index + 1;
|
||||||
|
|
||||||
self.library.b_vertex_positions.push(endpoint_position);
|
// FIXME(pcwalton): Normal
|
||||||
self.library.b_vertex_path_ids.push(self.path_id);
|
self.library.add_b_vertex(&endpoint_position,
|
||||||
self.library.b_vertex_loop_blinn_data.push(BVertexLoopBlinnData::new(
|
self.path_id,
|
||||||
active_edge.endpoint_kind()));
|
&BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
|
||||||
|
0.0);
|
||||||
|
|
||||||
active_edge.toggle_parity();
|
active_edge.toggle_parity();
|
||||||
}
|
}
|
||||||
|
@ -251,9 +260,12 @@ impl<'a> Partitioner<'a> {
|
||||||
&control_point_position,
|
&control_point_position,
|
||||||
&new_point.position,
|
&new_point.position,
|
||||||
bottom);
|
bottom);
|
||||||
self.library.b_vertex_positions.push(*control_point_position);
|
|
||||||
self.library.b_vertex_path_ids.push(self.path_id);
|
// FIXME(pcwalton): Normal
|
||||||
self.library.b_vertex_loop_blinn_data.push(control_point_b_vertex_loop_blinn_data);
|
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) {
|
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.
|
// FIXME(pcwalton): This is twice as slow as it needs to be.
|
||||||
self.active_edges.insert(next_active_edge_index as usize, ActiveEdge::default());
|
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[0].left_vertex_index = left_vertex_index;
|
||||||
new_active_edges[1].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;
|
let position = self.endpoints[endpoint_index as usize].position;
|
||||||
self.library.b_vertex_positions.push(position);
|
self.library.add_b_vertex(&position,
|
||||||
self.library.b_vertex_path_ids.push(self.path_id);
|
self.path_id,
|
||||||
self.library.b_vertex_loop_blinn_data
|
&BVertexLoopBlinnData::new(BVertexKind::Endpoint0),
|
||||||
.push(BVertexLoopBlinnData::new(BVertexKind::Endpoint0));
|
0.0);
|
||||||
|
|
||||||
new_active_edges[0].toggle_parity();
|
new_active_edges[0].toggle_parity();
|
||||||
new_active_edges[1].toggle_parity();
|
new_active_edges[1].toggle_parity();
|
||||||
|
@ -382,9 +414,12 @@ impl<'a> Partitioner<'a> {
|
||||||
&control_point_position,
|
&control_point_position,
|
||||||
&right_vertex_position,
|
&right_vertex_position,
|
||||||
false);
|
false);
|
||||||
self.library.b_vertex_positions.push(control_point_position);
|
|
||||||
self.library.b_vertex_path_ids.push(self.path_id);
|
// FIXME(pcwalton): Normal
|
||||||
self.library.b_vertex_loop_blinn_data.push(control_point_b_vertex_loop_blinn_data);
|
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,
|
&control_point_position,
|
||||||
&right_vertex_position,
|
&right_vertex_position,
|
||||||
true);
|
true);
|
||||||
self.library.b_vertex_positions.push(control_point_position);
|
|
||||||
self.library.b_vertex_path_ids.push(self.path_id);
|
// FIXME(pcwalton): Normal
|
||||||
self.library.b_vertex_loop_blinn_data.push(control_point_b_vertex_loop_blinn_data);
|
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.left_curve_control_point,
|
||||||
upper_subdivision.middle_point,
|
upper_subdivision.middle_point,
|
||||||
lower_subdivision.left_curve_left,
|
lower_subdivision.left_curve_left,
|
||||||
lower_subdivision.left_curve_control_point,
|
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,
|
fn subdivide_active_edge_again_at_t(&mut self,
|
||||||
|
@ -779,6 +821,8 @@ impl<'a> Partitioner<'a> {
|
||||||
bottom),
|
bottom),
|
||||||
].into_iter());
|
].into_iter());
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Normal
|
||||||
|
|
||||||
(SubdividedActiveEdge {
|
(SubdividedActiveEdge {
|
||||||
left_curve_left: subdivision.left_curve_left,
|
left_curve_left: subdivision.left_curve_left,
|
||||||
left_curve_control_point: left_control_point_index,
|
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 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]
|
let right_point = self.endpoints[active_edge.right_endpoint_index as usize]
|
||||||
.position;
|
.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;
|
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.add_b_vertex(&middle_point.to_point(),
|
||||||
self.library.b_vertex_path_ids.push(path_id);
|
path_id,
|
||||||
self.library
|
&BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
|
||||||
.b_vertex_loop_blinn_data
|
0.0);
|
||||||
.push(BVertexLoopBlinnData::new(active_edge.endpoint_kind()));
|
|
||||||
|
|
||||||
left_curve_control_point_vertex_index = u32::MAX;
|
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.left_vertex_index = left_curve_control_point_vertex_index + 1;
|
||||||
active_edge.control_point_vertex_index = left_curve_control_point_vertex_index + 2;
|
active_edge.control_point_vertex_index = left_curve_control_point_vertex_index + 2;
|
||||||
|
|
||||||
self.library.b_vertex_positions.extend([
|
// FIXME(pcwalton): Normals
|
||||||
left_curve.control_point,
|
self.library
|
||||||
left_curve.endpoints[1],
|
.add_b_vertex(&left_curve.control_point,
|
||||||
right_curve.control_point,
|
self.path_id,
|
||||||
].into_iter());
|
&BVertexLoopBlinnData::control_point(&left_curve.endpoints[0],
|
||||||
self.library.b_vertex_path_ids.extend(iter::repeat(self.path_id).take(3));
|
&left_curve.control_point,
|
||||||
self.library.b_vertex_loop_blinn_data.extend([
|
&left_curve.endpoints[1],
|
||||||
BVertexLoopBlinnData::control_point(&left_curve.endpoints[0],
|
bottom),
|
||||||
&left_curve.control_point,
|
0.0);
|
||||||
&left_curve.endpoints[1],
|
self.library.add_b_vertex(&left_curve.endpoints[1],
|
||||||
bottom),
|
self.path_id,
|
||||||
BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
|
&BVertexLoopBlinnData::new(active_edge.endpoint_kind()),
|
||||||
BVertexLoopBlinnData::control_point(&right_curve.endpoints[0],
|
0.0);
|
||||||
&right_curve.control_point,
|
self.library
|
||||||
&right_curve.endpoints[1],
|
.add_b_vertex(&right_curve.control_point,
|
||||||
bottom),
|
self.path_id,
|
||||||
].into_iter());
|
&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 {
|
fn prev_endpoint_of(&self, endpoint_index: u32) -> u32 {
|
||||||
let endpoint = &self.endpoints[endpoint_index as usize];
|
let endpoint = &self.endpoints[endpoint_index as usize];
|
||||||
let subpath = &self.subpaths[endpoint.subpath_index as usize];
|
let subpath = &self.subpaths[endpoint.subpath_index as usize];
|
||||||
|
@ -1257,3 +1355,44 @@ enum SubdivisionType {
|
||||||
Inside,
|
Inside,
|
||||||
Lower,
|
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