diff --git a/partitioner/.gitignore b/partitioner/.gitignore deleted file mode 100644 index f2f9e58e..00000000 --- a/partitioner/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock \ No newline at end of file diff --git a/partitioner/Cargo.toml b/partitioner/Cargo.toml deleted file mode 100644 index d9bc9568..00000000 --- a/partitioner/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "pathfinder_partitioner" -version = "0.2.0" -authors = ["Patrick Walton "] - -[lib] -name = "pathfinder_partitioner" - -[dependencies] -arrayvec = "0.4" -bincode = "1.0" -bit-vec = "0.4" -byteorder = "1.2" -env_logger = "0.5" -half = "1.0" -log = "0.3" -lyon_geom = "0.12" -lyon_path = "0.12" -serde = "1.0" -serde_derive = "1.0" - -[dependencies.euclid] -version = "0.19" -features = ["serde"] - -[dependencies.pathfinder_geometry] -path = "../geometry" diff --git a/partitioner/LICENSE-APACHE b/partitioner/LICENSE-APACHE deleted file mode 100644 index 16fe87b0..00000000 --- a/partitioner/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/partitioner/LICENSE-MIT b/partitioner/LICENSE-MIT deleted file mode 100644 index 25597d58..00000000 --- a/partitioner/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2010 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/partitioner/src/builder.rs b/partitioner/src/builder.rs deleted file mode 100644 index 4790f0f4..00000000 --- a/partitioner/src/builder.rs +++ /dev/null @@ -1,194 +0,0 @@ -// pathfinder/partitioner/src/builder.rs -// -// Copyright © 2018 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use arrayvec::ArrayVec; -use euclid::{Angle, Point2D, Vector2D}; -use lyon_geom::{CubicBezierSegment, QuadraticBezierSegment}; -use lyon_path::builder::{FlatPathBuilder, PathBuilder}; -use pathfinder_builder::cubic_to_quadratic::CubicToQuadraticSegmentIter; -use std::ops::Range; - -const TANGENT_PARAMETER_TOLERANCE: f32 = 0.001; - -const DEFAULT_APPROX_TOLERANCE: f32 = 0.001; - -// TODO(pcwalton): A better debug. -#[derive(Debug)] -pub struct Builder { - pub endpoints: Vec, - pub subpath_ranges: Vec>, - pub approx_tolerance: f32, -} - -impl Builder { - #[inline] - pub fn new() -> Builder { - Builder { - endpoints: vec![], - subpath_ranges: vec![], - approx_tolerance: DEFAULT_APPROX_TOLERANCE, - } - } - - #[inline] - pub fn set_approx_tolerance(&mut self, tolerance: f32) { - self.approx_tolerance = tolerance - } - - #[inline] - fn current_subpath_index(&self) -> Option { - if self.subpath_ranges.is_empty() { - None - } else { - Some(self.subpath_ranges.len() as u32 - 1) - } - } - - fn add_endpoint(&mut self, ctrl: Option>, to: Point2D) { - let current_subpath_index = match self.current_subpath_index() { - None => return, - Some(current_subpath_index) => current_subpath_index, - }; - - self.endpoints.push(Endpoint { - to: to, - ctrl: ctrl, - subpath_index: current_subpath_index, - }); - } - - #[inline] - pub fn end_subpath(&mut self) { - let last_endpoint_index = self.endpoints.len() as u32; - if let Some(current_subpath) = self.subpath_ranges.last_mut() { - current_subpath.end = last_endpoint_index - } - } - - #[inline] - fn first_position_of_subpath(&self) -> Option> { - self.subpath_ranges - .last() - .map(|subpath_range| self.endpoints[subpath_range.start as usize].to) - } -} - -impl FlatPathBuilder for Builder { - type PathType = (); - - #[inline] - fn build(self) {} - - #[inline] - fn build_and_reset(&mut self) { - self.endpoints.clear(); - self.subpath_ranges.clear(); - } - - #[inline] - fn current_position(&self) -> Point2D { - match self.endpoints.last() { - None => Point2D::zero(), - Some(endpoint) => endpoint.to, - } - } - - fn close(&mut self) { - let first_position_of_subpath = match self.first_position_of_subpath() { - None => return, - Some(first_position_of_subpath) => first_position_of_subpath, - }; - - if first_position_of_subpath == self.current_position() { - return - } - - self.add_endpoint(None, first_position_of_subpath); - self.end_subpath(); - } - - fn move_to(&mut self, to: Point2D) { - self.end_subpath(); - let last_endpoint_index = self.endpoints.len() as u32; - self.subpath_ranges.push(last_endpoint_index..last_endpoint_index); - self.add_endpoint(None, to); - } - - #[inline] - fn line_to(&mut self, to: Point2D) { - self.add_endpoint(None, to); - } -} - -impl PathBuilder for Builder { - fn quadratic_bezier_to(&mut self, ctrl: Point2D, to: Point2D) { - let segment = QuadraticBezierSegment { - from: self.current_position(), - ctrl: ctrl, - to: to, - }; - - //self.add_endpoint(Some(ctrl), to); - - // Split at X tangent. - let mut worklist: ArrayVec<[QuadraticBezierSegment; 2]> = ArrayVec::new(); - match segment.local_x_extremum_t() { - Some(t) if t > TANGENT_PARAMETER_TOLERANCE && - t < 1.0 - TANGENT_PARAMETER_TOLERANCE => { - let subsegments = segment.split(t); - worklist.push(subsegments.0); - worklist.push(subsegments.1); - } - _ => worklist.push(segment), - } - - // Split at Y tangent. - for segment in worklist { - match segment.local_y_extremum_t() { - Some(t) if t > TANGENT_PARAMETER_TOLERANCE && - t < 1.0 - TANGENT_PARAMETER_TOLERANCE => { - let subsegments = segment.split(t); - self.add_endpoint(Some(subsegments.0.ctrl), subsegments.0.to); - self.add_endpoint(Some(subsegments.1.ctrl), subsegments.1.to); - } - _ => self.add_endpoint(Some(segment.ctrl), segment.to), - } - } - } - - fn cubic_bezier_to(&mut self, ctrl1: Point2D, ctrl2: Point2D, to: Point2D) { - let cubic_segment = CubicBezierSegment { - from: self.current_position(), - ctrl1: ctrl1, - ctrl2: ctrl2, - to: to, - }; - - for quadratic_segment in CubicToQuadraticSegmentIter::new(&cubic_segment, - self.approx_tolerance) { - self.quadratic_bezier_to(quadratic_segment.ctrl, quadratic_segment.to) - } - } - - fn arc(&mut self, - _center: Point2D, - _radii: Vector2D, - _angle: Angle, - _x_rotation: Angle) { - panic!("TODO: Support arcs in the Pathfinder builder!") - } -} - -#[derive(Clone, Copy, Debug)] -pub struct Endpoint { - pub to: Point2D, - pub ctrl: Option>, - pub subpath_index: u32, -} diff --git a/partitioner/src/lib.rs b/partitioner/src/lib.rs deleted file mode 100644 index edbd5697..00000000 --- a/partitioner/src/lib.rs +++ /dev/null @@ -1,164 +0,0 @@ -// pathfinder/partitioner/src/lib.rs -// -// Copyright © 2018 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Processes paths into *mesh libraries*, which are vertex buffers ready to be uploaded to the -//! GPU and rendered with the supplied shaders. -//! -//! *Partitioning* is the process of cutting up a filled Bézier path into *B-quads*. A B-quad is -//! the core primitive that Pathfinder renders; it is a trapezoid-like shape that consists of -//! vertical sides on the left and right and Bézier curve segments and/or lines on the top and -//! bottom. Path partitioning is typically O(*n* log *n*) in the number of path commands. -//! -//! If you have a static set of paths (for example, one specific font), you may wish to run the -//! partitioner as a preprocessing step and store the resulting mesh library on disk. To aid this -//! use case, mesh libraries can be serialized into a simple binary format. Of course, meshes can -//! also be generated dynamically and rendered on the fly. - -extern crate arrayvec; -extern crate bincode; -extern crate bit_vec; -extern crate byteorder; -extern crate env_logger; -extern crate euclid; -extern crate lyon_path; -extern crate pathfinder_geometry; -extern crate serde; - -use lyon_path::geom as lyon_geom; - -#[macro_use] -extern crate log; -#[macro_use] -extern crate serde_derive; - -use euclid::Point2D; -use std::u32; - -pub mod builder; -pub mod mesh; -pub mod mesh_pack; -pub mod partitioner; - -/// The fill rule. -#[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] -pub enum FillRule { - EvenOdd = 0, - Winding = 1, -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct BQuad { - pub upper_left_vertex_index: u32, - pub upper_right_vertex_index: u32, - pub upper_control_point_vertex_index: u32, - pad0: u32, - pub lower_left_vertex_index: u32, - pub lower_right_vertex_index: u32, - pub lower_control_point_vertex_index: u32, - pad1: u32, -} - -impl BQuad { - #[inline] - pub fn new(upper_left_vertex_index: u32, - upper_control_point_vertex_index: u32, - upper_right_vertex_index: u32, - lower_left_vertex_index: u32, - lower_control_point_vertex_index: u32, - lower_right_vertex_index: u32) - -> BQuad { - BQuad { - upper_left_vertex_index: upper_left_vertex_index, - upper_control_point_vertex_index: upper_control_point_vertex_index, - upper_right_vertex_index: upper_right_vertex_index, - lower_left_vertex_index: lower_left_vertex_index, - lower_control_point_vertex_index: lower_control_point_vertex_index, - lower_right_vertex_index: lower_right_vertex_index, - pad0: 0, - pad1: 0, - } - } - - #[inline] - pub fn offset(&mut self, delta: u32) { - self.upper_left_vertex_index += delta; - self.upper_right_vertex_index += delta; - self.lower_left_vertex_index += delta; - self.lower_right_vertex_index += delta; - if self.upper_control_point_vertex_index < u32::MAX { - self.upper_control_point_vertex_index += delta; - } - if self.lower_control_point_vertex_index < u32::MAX { - self.lower_control_point_vertex_index += delta; - } - } -} - -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] -pub struct BQuadVertexPositions { - pub upper_left_vertex_position: Point2D, - pub upper_control_point_position: Point2D, - pub upper_right_vertex_position: Point2D, - pub lower_right_vertex_position: Point2D, - pub lower_control_point_position: Point2D, - pub lower_left_vertex_position: Point2D, -} - -#[derive(Clone, Copy, PartialEq, Debug)] -#[repr(u8)] -pub(crate) enum BVertexKind { - Endpoint0, - Endpoint1, - ConvexControlPoint, - ConcaveControlPoint, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -#[repr(C)] -pub struct BVertexLoopBlinnData { - pub tex_coord: [u8; 2], - pub sign: i8, - pad: u8, -} - -impl BVertexLoopBlinnData { - #[inline] - pub(crate) fn new(kind: BVertexKind) -> BVertexLoopBlinnData { - let (tex_coord, sign) = match kind { - BVertexKind::Endpoint0 => ([0, 0], 0), - BVertexKind::Endpoint1 => ([2, 2], 0), - BVertexKind::ConcaveControlPoint => ([1, 0], 1), - BVertexKind::ConvexControlPoint => ([1, 0], -1), - }; - BVertexLoopBlinnData { - tex_coord: tex_coord, - sign: sign, - pad: 0, - } - } - - pub(crate) fn control_point(left_endpoint_position: &Point2D, - control_point_position: &Point2D, - right_endpoint_position: &Point2D, - bottom: bool) - -> BVertexLoopBlinnData { - let control_point_vector = *control_point_position - *left_endpoint_position; - let right_vector = *right_endpoint_position - *left_endpoint_position; - let determinant = right_vector.cross(control_point_vector); - let endpoint_kind = if (determinant < 0.0) ^ bottom { - BVertexKind::ConvexControlPoint - } else { - BVertexKind::ConcaveControlPoint - }; - BVertexLoopBlinnData::new(endpoint_kind) - } -} diff --git a/partitioner/src/mesh.rs b/partitioner/src/mesh.rs deleted file mode 100644 index 0dd0943d..00000000 --- a/partitioner/src/mesh.rs +++ /dev/null @@ -1,396 +0,0 @@ -// pathfinder/partitioner/src/mesh.rs -// -// Copyright © 2018 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use euclid::approxeq::ApproxEq; -use euclid::{Point2D, Rect, Size2D, Vector2D}; -use lyon_path::PathEvent; -use pathfinder_geometry::normals::PathNormals; -use pathfinder_geometry::segments::{self, SegmentIter}; -use std::f32; -use std::u32; - -use {BQuad, BQuadVertexPositions, BVertexLoopBlinnData}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Mesh { - pub b_quads: Vec, - // FIXME(pcwalton): Merge with `b_vertex_positions` below. - pub b_quad_vertex_positions: Vec, - pub b_quad_vertex_interior_indices: Vec, - pub b_vertex_positions: Vec>, - pub b_vertex_loop_blinn_data: Vec, - pub b_boxes: Vec, - pub stencil_segments: Vec, - pub stencil_normals: Vec, - pub tile_metadata: Option, -} - -impl Mesh { - #[inline] - pub fn new() -> Mesh { - Mesh { - b_quads: vec![], - b_quad_vertex_positions: vec![], - b_quad_vertex_interior_indices: vec![], - b_vertex_positions: vec![], - b_vertex_loop_blinn_data: vec![], - b_boxes: vec![], - stencil_segments: vec![], - stencil_normals: vec![], - tile_metadata: None, - } - } - - pub fn clear(&mut self) { - self.b_quads.clear(); - self.b_quad_vertex_positions.clear(); - self.b_quad_vertex_interior_indices.clear(); - self.b_vertex_positions.clear(); - self.b_vertex_loop_blinn_data.clear(); - self.b_boxes.clear(); - self.stencil_segments.clear(); - self.stencil_normals.clear(); - self.tile_metadata = None; - } - - pub(crate) fn add_b_vertex(&mut self, - position: &Point2D, - loop_blinn_data: &BVertexLoopBlinnData) { - self.b_vertex_positions.push(*position); - self.b_vertex_loop_blinn_data.push(*loop_blinn_data); - } - - pub(crate) fn add_b_quad(&mut self, b_quad: &BQuad) { - let BQuadVertexPositions { - upper_left_vertex_position: ul, - upper_right_vertex_position: ur, - lower_left_vertex_position: ll, - lower_right_vertex_position: lr, - .. - } = self.get_b_quad_vertex_positions(b_quad); - - if ul.x.approx_eq(&ur.x) || ll.x.approx_eq(&lr.x) { - return - } - - self.b_quads.push(*b_quad); - - self.add_b_quad_vertex_positions(b_quad); - self.add_b_box(b_quad); - } - - fn add_b_quad_vertex_positions(&mut self, b_quad: &BQuad) { - let b_quad_vertex_positions = self.get_b_quad_vertex_positions(b_quad); - let first_b_quad_vertex_position_index = (self.b_quad_vertex_positions.len() as u32) * 6; - self.push_b_quad_vertex_position_interior_indices(first_b_quad_vertex_position_index, - &b_quad_vertex_positions); - self.b_quad_vertex_positions.push(b_quad_vertex_positions); - } - - fn add_b_box(&mut self, b_quad: &BQuad) { - let BQuadVertexPositions { - upper_left_vertex_position: ul, - upper_control_point_position: uc, - upper_right_vertex_position: ur, - lower_left_vertex_position: ll, - lower_control_point_position: lc, - lower_right_vertex_position: lr, - } = self.get_b_quad_vertex_positions(b_quad); - - let rect = Rect::from_points([ul, uc, ur, ll, lc, lr].into_iter()); - - let (edge_ucl, edge_urc, edge_ulr) = (uc - ul, ur - uc, ul - ur); - let (edge_lcl, edge_lrc, edge_llr) = (lc - ll, lr - lc, ll - lr); - - let (edge_len_ucl, edge_len_urc) = (edge_ucl.length(), edge_urc.length()); - let (edge_len_lcl, edge_len_lrc) = (edge_lcl.length(), edge_lrc.length()); - let (edge_len_ulr, edge_len_llr) = (edge_ulr.length(), edge_llr.length()); - - let (uv_upper, uv_lower, sign_upper, sign_lower, mode_upper, mode_lower); - - if edge_len_ucl < 0.01 || edge_len_urc < 0.01 || edge_len_ulr < 0.01 || - edge_ucl.dot(-edge_ulr) > 0.9999 * edge_len_ucl * edge_len_ulr { - uv_upper = Uv::line(&rect, &ul, &ur); - sign_upper = -1.0; - mode_upper = -1.0; - } else { - uv_upper = Uv::curve(&rect, &ul, &uc, &ur); - sign_upper = (edge_ucl.cross(-edge_ulr)).signum(); - mode_upper = 1.0; - } - - if edge_len_lcl < 0.01 || edge_len_lrc < 0.01 || edge_len_llr < 0.01 || - edge_lcl.dot(-edge_llr) > 0.9999 * edge_len_lcl * edge_len_llr { - uv_lower = Uv::line(&rect, &ll, &lr); - sign_lower = 1.0; - mode_lower = -1.0; - } else { - uv_lower = Uv::curve(&rect, &ll, &lc, &lr); - sign_lower = -(edge_lcl.cross(-edge_llr)).signum(); - mode_lower = 1.0; - } - - let b_box = BBox { - upper_left_position: rect.origin, - lower_right_position: rect.bottom_right(), - upper_left_uv_upper: uv_upper.origin, - upper_left_uv_lower: uv_lower.origin, - d_upper_uv_dx: uv_upper.d_uv_dx, - d_lower_uv_dx: uv_lower.d_uv_dx, - d_upper_uv_dy: uv_upper.d_uv_dy, - d_lower_uv_dy: uv_lower.d_uv_dy, - upper_sign: sign_upper, - lower_sign: sign_lower, - upper_mode: mode_upper, - lower_mode: mode_lower, - }; - - self.b_boxes.push(b_box); - } - - fn get_b_quad_vertex_positions(&self, b_quad: &BQuad) -> BQuadVertexPositions { - let ul = self.b_vertex_positions[b_quad.upper_left_vertex_index as usize]; - let ur = self.b_vertex_positions[b_quad.upper_right_vertex_index as usize]; - let ll = self.b_vertex_positions[b_quad.lower_left_vertex_index as usize]; - let lr = self.b_vertex_positions[b_quad.lower_right_vertex_index as usize]; - - let mut b_quad_vertex_positions = BQuadVertexPositions { - upper_left_vertex_position: ul, - upper_control_point_position: ul.lerp(ur, 0.5), - upper_right_vertex_position: ur, - lower_left_vertex_position: ll, - lower_control_point_position: ll.lerp(lr, 0.5), - lower_right_vertex_position: lr, - }; - - if b_quad.upper_control_point_vertex_index != u32::MAX { - let uc = &self.b_vertex_positions[b_quad.upper_control_point_vertex_index as usize]; - b_quad_vertex_positions.upper_control_point_position = *uc; - } - - if b_quad.lower_control_point_vertex_index != u32::MAX { - let lc = &self.b_vertex_positions[b_quad.lower_control_point_vertex_index as usize]; - b_quad_vertex_positions.lower_control_point_position = *lc; - } - - b_quad_vertex_positions - } - - fn push_b_quad_vertex_position_interior_indices(&mut self, - first_vertex_index: u32, - b_quad: &BQuadVertexPositions) { - let upper_curve_is_concave = - (b_quad.upper_right_vertex_position - b_quad.upper_left_vertex_position).cross( - b_quad.upper_control_point_position - b_quad.upper_left_vertex_position) > 0.0; - let lower_curve_is_concave = - (b_quad.lower_left_vertex_position - b_quad.lower_right_vertex_position).cross( - b_quad.lower_control_point_position - b_quad.lower_right_vertex_position) > 0.0; - - let indices: &'static [u32] = match (upper_curve_is_concave, lower_curve_is_concave) { - (false, false) => &[UL, UR, LL, UR, LR, LL], - (true, false) => &[UL, UC, LL, UC, LR, LL, UR, LR, UC], - (false, true) => &[UL, LC, LL, UL, UR, LC, UR, LR, LC], - (true, true) => &[UL, UC, LL, UC, LC, LL, UR, LC, UC, UR, LR, LC], - }; - - self.b_quad_vertex_interior_indices - .extend(indices.into_iter().map(|index| index + first_vertex_index)); - - const UL: u32 = 0; - const UC: u32 = 1; - const UR: u32 = 2; - const LR: u32 = 3; - const LC: u32 = 4; - const LL: u32 = 5; - } - - pub fn push_stencil_segments(&mut self, stream: I) where I: Iterator { - let segment_iter = SegmentIter::new(stream); - for segment in segment_iter { - match segment { - segments::Segment::Line(line_segment) => { - self.stencil_segments.push(StencilSegment { - from: line_segment.from, - ctrl: line_segment.from.lerp(line_segment.to, 0.5), - to: line_segment.to, - }) - } - segments::Segment::Quadratic(quadratic_segment) => { - self.stencil_segments.push(StencilSegment { - from: quadratic_segment.from, - ctrl: quadratic_segment.ctrl, - to: quadratic_segment.to, - }) - } - segments::Segment::Cubic(..) => { - panic!("push_stencil_segments(): Convert cubics to quadratics first!") - } - segments::Segment::EndSubpath(..) => {} - } - } - } - - /// Computes vertex normals necessary for emboldening and/or stem darkening. This is intended - /// for stencil-and-cover. - pub fn push_stencil_normals(&mut self, stream: I) where I: Iterator { - let mut normals = PathNormals::new(); - normals.add_path(stream); - self.stencil_normals.extend(normals.normals().iter().map(|normals| { - StencilNormals { - from: normals.from, - ctrl: normals.ctrl, - to: normals.to, - } - })) - } -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct BBox { - pub upper_left_position: Point2D, - pub lower_right_position: Point2D, - pub upper_left_uv_upper: Point2D, - pub upper_left_uv_lower: Point2D, - pub d_upper_uv_dx: Vector2D, - pub d_lower_uv_dx: Vector2D, - pub d_upper_uv_dy: Vector2D, - pub d_lower_uv_dy: Vector2D, - pub upper_sign: f32, - pub lower_sign: f32, - pub upper_mode: f32, - pub lower_mode: f32, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct StencilSegment { - pub from: Point2D, - pub ctrl: Point2D, - pub to: Point2D, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct StencilNormals { - pub from: Vector2D, - pub ctrl: Vector2D, - pub to: Vector2D, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct TileMetadata { - pub origin: Point2D, - pub path_index: u32, -} - -#[derive(Clone, Copy, Debug)] -struct CornerValues { - upper_left: Point2D, - upper_right: Point2D, - lower_left: Point2D, - lower_right: Point2D, -} - -#[derive(Clone, Copy, Debug)] -struct Uv { - origin: Point2D, - d_uv_dx: Vector2D, - d_uv_dy: Vector2D, -} - -impl Uv { - fn from_values(origin: &Point2D, origin_right: &Point2D, origin_down: &Point2D) - -> Uv { - Uv { - origin: *origin, - d_uv_dx: *origin_right - *origin, - d_uv_dy: *origin_down - *origin, - } - } - - fn curve(rect: &Rect, left: &Point2D, ctrl: &Point2D, right: &Point2D) - -> Uv { - let origin_right = rect.top_right(); - let origin_down = rect.bottom_left(); - - let (lambda_origin, denom) = to_barycentric(left, ctrl, right, &rect.origin); - let (lambda_origin_right, _) = to_barycentric(left, ctrl, right, &origin_right); - let (lambda_origin_down, _) = to_barycentric(left, ctrl, right, &origin_down); - - let uv_origin = lambda_to_uv(&lambda_origin, denom); - let uv_origin_right = lambda_to_uv(&lambda_origin_right, denom); - let uv_origin_down = lambda_to_uv(&lambda_origin_down, denom); - - return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down); - - // https://gamedev.stackexchange.com/a/23745 - fn to_barycentric(a: &Point2D, b: &Point2D, c: &Point2D, p: &Point2D) - -> ([f64; 2], f64) { - let (a, b, c, p) = (a.to_f64(), b.to_f64(), c.to_f64(), p.to_f64()); - let (v0, v1, v2) = (b - a, c - a, p - a); - let (d00, d01) = (v0.dot(v0), v0.dot(v1)); - let d11 = v1.dot(v1); - let (d20, d21) = (v2.dot(v0), v2.dot(v1)); - let denom = d00 * d11 - d01 * d01; - ([(d11 * d20 - d01 * d21), (d00 * d21 - d01 * d20)], denom) - } - - fn lambda_to_uv(lambda: &[f64; 2], denom: f64) -> Point2D { - (Point2D::new(lambda[0] * 0.5 + lambda[1], lambda[1]) / denom).to_f32() - } - } - - fn line(rect: &Rect, left: &Point2D, right: &Point2D) -> Uv { - let (values, line_bounds); - if f32::abs(left.y - right.y) < 0.01 { - values = CornerValues { - upper_left: Point2D::new(0.0, 0.5), - upper_right: Point2D::new(0.5, 1.0), - lower_right: Point2D::new(1.0, 0.5), - lower_left: Point2D::new(0.5, 0.0), - }; - line_bounds = Rect::new(*left + Vector2D::new(0.0, -1.0), - Size2D::new(right.x - left.x, 2.0)); - } else { - if left.y < right.y { - values = CornerValues { - upper_left: Point2D::new(1.0, 1.0), - upper_right: Point2D::new(0.0, 1.0), - lower_left: Point2D::new(1.0, 0.0), - lower_right: Point2D::new(0.0, 0.0), - }; - } else { - values = CornerValues { - upper_left: Point2D::new(0.0, 1.0), - upper_right: Point2D::new(1.0, 1.0), - lower_left: Point2D::new(0.0, 0.0), - lower_right: Point2D::new(1.0, 0.0), - }; - } - line_bounds = Rect::from_points([*left, *right].into_iter()); - } - - let origin_right = rect.top_right(); - let origin_down = rect.bottom_left(); - - let uv_origin = bilerp(&line_bounds, &values, &rect.origin); - let uv_origin_right = bilerp(&line_bounds, &values, &origin_right); - let uv_origin_down = bilerp(&line_bounds, &values, &origin_down); - - return Uv::from_values(&uv_origin, &uv_origin_right, &uv_origin_down); - - fn bilerp(rect: &Rect, values: &CornerValues, position: &Point2D) - -> Point2D { - let upper = values.upper_left.lerp(values.upper_right, - (position.x - rect.min_x()) / rect.size.width); - let lower = values.lower_left.lerp(values.lower_right, - (position.x - rect.min_x()) / rect.size.width); - upper.lerp(lower, (position.y - rect.min_y()) / rect.size.height) - } - } -} diff --git a/partitioner/src/mesh_pack.rs b/partitioner/src/mesh_pack.rs deleted file mode 100644 index 3f844329..00000000 --- a/partitioner/src/mesh_pack.rs +++ /dev/null @@ -1,96 +0,0 @@ -// pathfinder/partitioner/src/mesh_pack.rs -// -// Copyright © 2018 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use bincode; -use byteorder::{LittleEndian, WriteBytesExt}; -use mesh::{Mesh, TileMetadata}; -use serde::Serialize; -use std::io::{self, ErrorKind, Seek, SeekFrom, Write}; -use std::u32; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MeshPack { - pub meshes: Vec, -} - -impl MeshPack { - #[inline] - pub fn new() -> MeshPack { - MeshPack { - meshes: vec![], - } - } - - #[inline] - pub fn push(&mut self, mesh: Mesh) { - self.meshes.push(mesh) - } - - /// Writes this mesh pack to a RIFF file. - /// - /// RIFF is a dead-simple extensible binary format documented here: - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ee415713(v=vs.85).aspx - pub fn serialize_into(&self, writer: &mut W) -> io::Result<()> where W: Write + Seek { - // `PFMP` for "Pathfinder Mesh Pack". - try!(writer.write_all(b"RIFF\0\0\0\0PFMP")); - - // NB: The RIFF spec requires that all chunks be padded to an even byte offset. However, - // for us, this is guaranteed by construction because each instance of all of the data that - // we're writing has a byte size that is a multiple of 4. So we don't bother with doing it - // explicitly here. - for mesh in &self.meshes { - try!(write_chunk(writer, b"mesh", |writer| { - try!(write_simple_chunk(writer, b"bqua", &mesh.b_quads)); - try!(write_simple_chunk(writer, b"bqvp", &mesh.b_quad_vertex_positions)); - try!(write_simple_chunk(writer, b"bqii", &mesh.b_quad_vertex_interior_indices)); - try!(write_simple_chunk(writer, b"bbox", &mesh.b_boxes)); - try!(write_simple_chunk(writer, b"sseg", &mesh.stencil_segments)); - try!(write_simple_chunk(writer, b"snor", &mesh.stencil_normals)); - match mesh.tile_metadata { - None => try!(write_simple_chunk::<_, TileMetadata>(writer, b"tile", &[])), - Some(metadata) => try!(write_simple_chunk(writer, b"tile", &[metadata])), - } - Ok(()) - })); - } - - let total_length = try!(writer.seek(SeekFrom::Current(0))); - try!(writer.seek(SeekFrom::Start(4))); - try!(writer.write_u32::((total_length - 8) as u32)); - return Ok(()); - - fn write_chunk(writer: &mut W, tag: &[u8; 4], mut closure: F) -> io::Result<()> - where W: Write + Seek, F: FnMut(&mut W) -> io::Result<()> { - try!(writer.write_all(tag)); - try!(writer.write_all(b"\0\0\0\0")); - - let start_position = try!(writer.seek(SeekFrom::Current(0))); - try!(closure(writer)); - - let end_position = try!(writer.seek(SeekFrom::Current(0))); - try!(writer.seek(SeekFrom::Start(start_position - 4))); - try!(writer.write_u32::((end_position - start_position) as u32)); - try!(writer.seek(SeekFrom::Start(end_position))); - Ok(()) - } - - fn write_simple_chunk(writer: &mut W, tag: &[u8; 4], data: &[T]) -> io::Result<()> - where W: Write + Seek, T: Serialize { - write_chunk(writer, tag, |writer| { - for datum in data { - try!(bincode::serialize_into(&mut *writer, datum).map_err(|_| { - io::Error::from(ErrorKind::Other) - })); - } - Ok(()) - }) - } - } -} diff --git a/partitioner/src/partitioner.rs b/partitioner/src/partitioner.rs deleted file mode 100644 index 1dafb096..00000000 --- a/partitioner/src/partitioner.rs +++ /dev/null @@ -1,1240 +0,0 @@ -// pathfinder/partitioner/src/partitioner.rs -// -// Copyright © 2018 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use bit_vec::BitVec; -use euclid::approxeq::ApproxEq; -use euclid::{Point2D, Vector2D}; -use log::LogLevel; -use lyon_geom::{LineSegment, QuadraticBezierSegment}; -use std::collections::BinaryHeap; -use std::cmp::Ordering; -use std::f32; -use std::iter; -use std::ops::{Add, AddAssign}; -use std::u32; - -use builder::Builder; -use mesh::Mesh; -use {BQuad, BVertexLoopBlinnData, BVertexKind, FillRule}; - -const MAX_B_QUAD_SUBDIVISIONS: u8 = 8; - -const INTERSECTION_TOLERANCE: f32 = 0.001; - -pub struct Partitioner { - path: Builder, - fill_rule: FillRule, - - mesh: Mesh, - - heap: BinaryHeap, - visited_points: BitVec, - active_edges: Vec, - vertex_normals: Vec, -} - -impl Partitioner { - #[inline] - pub fn new() -> Partitioner { - Partitioner { - path: Builder::new(), - fill_rule: FillRule::Winding, - - mesh: Mesh::new(), - - heap: BinaryHeap::new(), - visited_points: BitVec::new(), - active_edges: vec![], - vertex_normals: vec![], - } - } - - #[inline] - pub fn mesh(&self) -> &Mesh { - &self.mesh - } - - #[inline] - pub fn mesh_mut(&mut self) -> &mut Mesh { - &mut self.mesh - } - - #[inline] - pub fn into_mesh(self) -> Mesh { - self.mesh - } - - #[inline] - pub fn builder(&self) -> &Builder { - &self.path - } - - #[inline] - pub fn builder_mut(&mut self) -> &mut Builder { - &mut self.path - } - - pub fn partition(&mut self, fill_rule: FillRule) { - self.path.end_subpath(); - - self.heap.clear(); - self.active_edges.clear(); - - self.fill_rule = fill_rule; - - // FIXME(pcwalton): Right now, this bit vector uses too much memory. - self.visited_points = BitVec::from_elem(self.path.endpoints.len(), false); - - self.init_heap(); - - while self.process_next_point() {} - - debug_assert_eq!(self.mesh.b_vertex_loop_blinn_data.len(), - self.mesh.b_vertex_positions.len()); - } - - fn process_next_point(&mut self) -> bool { - let point = match self.heap.peek() { - Some(point) => *point, - None => return false, - }; - - if self.already_visited_point(&point) { - self.heap.pop(); - return true - } - - if log_enabled!(LogLevel::Debug) { - let position = self.path.endpoints[point.endpoint_index as usize].to; - debug!("processing point {}: {:?}", point.endpoint_index, position); - debug!("... active edges at {}:", position.x); - for (active_edge_index, active_edge) in self.active_edges.iter().enumerate() { - let y = self.solve_active_edge_y_for_x(position.x, active_edge); - debug!("... ... edge {}: {:?} @ ({}, {})", - active_edge_index, - active_edge, - position.x, - y); - } - } - - self.mark_point_as_visited(&point); - - self.sort_active_edge_list_and_emit_self_intersections(point.endpoint_index); - - let matching_active_edges = self.find_right_point_in_active_edge_list(point.endpoint_index); - match matching_active_edges.count { - 0 => self.process_min_endpoint(point.endpoint_index), - 1 => { - self.process_regular_endpoint(point.endpoint_index, - matching_active_edges.indices[0]) - } - 2 => self.process_max_endpoint(point.endpoint_index, matching_active_edges.indices), - _ => debug_assert!(false), - } - - true - } - - fn process_min_endpoint(&mut self, endpoint_index: u32) { - debug!("... MIN point"); - - let next_active_edge_index = self.find_point_between_active_edges(endpoint_index); - - let endpoint = self.path.endpoints[endpoint_index as usize]; - self.emit_b_quads_around_active_edge(next_active_edge_index, endpoint.to.x); - - self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index); - - let prev_endpoint_index = self.prev_endpoint_of(endpoint_index); - let next_endpoint_index = self.next_endpoint_of(endpoint_index); - let new_point = self.create_point_from_endpoint(next_endpoint_index); - *self.heap.peek_mut().unwrap() = new_point; - if next_endpoint_index != prev_endpoint_index { - let new_point = self.create_point_from_endpoint(prev_endpoint_index); - self.heap.push(new_point) - } - } - - fn process_regular_endpoint(&mut self, endpoint_index: u32, active_edge_index: u32) { - debug!("... REGULAR point: active edge {}", active_edge_index); - - let endpoint = self.path.endpoints[endpoint_index as usize]; - let bottom = self.emit_b_quads_around_active_edge(active_edge_index, endpoint.to.x) == - BQuadEmissionResult::BQuadEmittedAbove; - - let prev_endpoint_index = self.prev_endpoint_of(endpoint_index); - let next_endpoint_index = self.next_endpoint_of(endpoint_index); - - { - let active_edge = &mut self.active_edges[active_edge_index as usize]; - let endpoint_position = self.path - .endpoints[active_edge.right_endpoint_index as usize] - .to; - - // If we already made a B-vertex point for this endpoint, reuse it instead of making a - // new one. - let old_left_position = - self.mesh.b_vertex_positions[active_edge.left_vertex_index as usize]; - let should_update = (endpoint_position - old_left_position).square_length() > - f32::approx_epsilon(); - if should_update { - active_edge.left_vertex_index = self.mesh.b_vertex_loop_blinn_data.len() as u32; - active_edge.control_point_vertex_index = active_edge.left_vertex_index + 1; - - // FIXME(pcwalton): Normal - self.mesh.add_b_vertex(&endpoint_position, - &BVertexLoopBlinnData::new(active_edge.endpoint_kind())); - - active_edge.toggle_parity(); - } - - if active_edge.left_to_right { - active_edge.right_endpoint_index = next_endpoint_index; - } else { - active_edge.right_endpoint_index = prev_endpoint_index; - } - } - - let right_endpoint_index = self.active_edges[active_edge_index as usize] - .right_endpoint_index; - let new_point = self.create_point_from_endpoint(right_endpoint_index); - *self.heap.peek_mut().unwrap() = new_point; - - let control_point = if self.active_edges[active_edge_index as usize].left_to_right { - self.control_point_before_endpoint(next_endpoint_index) - } else { - self.control_point_after_endpoint(prev_endpoint_index) - }; - - match control_point { - None => { - self.active_edges[active_edge_index as usize].control_point_vertex_index = u32::MAX - } - Some(ref control_point_position) => { - self.active_edges[active_edge_index as usize].control_point_vertex_index = - self.mesh.b_vertex_loop_blinn_data.len() as u32; - - let left_vertex_index = self.active_edges[active_edge_index as usize] - .left_vertex_index; - let control_point_b_vertex_loop_blinn_data = BVertexLoopBlinnData::control_point( - &self.mesh.b_vertex_positions[left_vertex_index as usize], - &control_point_position, - &new_point.position, - bottom); - - // FIXME(pcwalton): Normal - self.mesh.add_b_vertex(control_point_position, - &control_point_b_vertex_loop_blinn_data); - } - } - } - - fn process_max_endpoint(&mut self, endpoint_index: u32, active_edge_indices: [u32; 2]) { - debug!("... MAX point: active edges {:?}", active_edge_indices); - - debug_assert!(active_edge_indices[0] < active_edge_indices[1], - "Matching active edge indices in wrong order when processing MAX point"); - - let endpoint = self.path.endpoints[endpoint_index as usize]; - - // TODO(pcwalton): Collapse the two duplicate endpoints that this will create together if - // possible (i.e. if they have the same parity). - self.emit_b_quads_around_active_edge(active_edge_indices[0], endpoint.to.x); - self.emit_b_quads_around_active_edge(active_edge_indices[1], endpoint.to.x); - - // Add supporting interior triangles if necessary. - self.heap.pop(); - - // FIXME(pcwalton): This is twice as slow as it needs to be. - self.active_edges.remove(active_edge_indices[1] as usize); - self.active_edges.remove(active_edge_indices[0] as usize); - } - - fn sort_active_edge_list_and_emit_self_intersections(&mut self, endpoint_index: u32) { - let x = self.path.endpoints[endpoint_index as usize].to.x; - loop { - let mut swapped = false; - for lower_active_edge_index in 1..(self.active_edges.len() as u32) { - let upper_active_edge_index = lower_active_edge_index - 1; - - if self.active_edges_are_ordered(upper_active_edge_index, - lower_active_edge_index, - x) { - continue - } - - if let Some(crossing_point) = - self.crossing_point_for_active_edge(upper_active_edge_index, x) { - debug!("found SELF-INTERSECTION point for active edges {} & {}", - upper_active_edge_index, - lower_active_edge_index); - self.emit_b_quads_around_active_edge(upper_active_edge_index, crossing_point.x); - self.emit_b_quads_around_active_edge(lower_active_edge_index, crossing_point.x); - } else { - warn!("swapped active edges {} & {} without finding intersection; rendering \ - will probably be incorrect", - upper_active_edge_index, - lower_active_edge_index); - } - - self.active_edges.swap(upper_active_edge_index as usize, - lower_active_edge_index as usize); - swapped = true; - } - - if !swapped { - break - } - } - } - - 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()); - self.active_edges.insert(next_active_edge_index as usize, ActiveEdge::default()); - - let prev_endpoint_index = self.prev_endpoint_of(endpoint_index); - let next_endpoint_index = self.next_endpoint_of(endpoint_index); - - let new_active_edges = &mut self.active_edges[next_active_edge_index as usize.. - next_active_edge_index as usize + 2]; - - let left_vertex_index = self.mesh.b_vertex_loop_blinn_data.len() as u32; - 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.path.endpoints[endpoint_index as usize].to; - self.mesh.add_b_vertex(&position, - &BVertexLoopBlinnData::new(BVertexKind::Endpoint0)); - - new_active_edges[0].toggle_parity(); - new_active_edges[1].toggle_parity(); - - let endpoint = &self.path.endpoints[endpoint_index as usize]; - let prev_endpoint = &self.path.endpoints[prev_endpoint_index as usize]; - let next_endpoint = &self.path.endpoints[next_endpoint_index as usize]; - - let prev_vector = prev_endpoint.to - endpoint.to; - let next_vector = next_endpoint.to - endpoint.to; - - let (upper_control_point, lower_control_point); - if prev_vector.cross(next_vector) >= 0.0 { - new_active_edges[0].right_endpoint_index = prev_endpoint_index; - new_active_edges[1].right_endpoint_index = next_endpoint_index; - new_active_edges[0].left_to_right = false; - new_active_edges[1].left_to_right = true; - - upper_control_point = self.path.endpoints[endpoint_index as usize].ctrl; - lower_control_point = self.path.endpoints[next_endpoint_index as usize].ctrl; - } else { - new_active_edges[0].right_endpoint_index = next_endpoint_index; - new_active_edges[1].right_endpoint_index = prev_endpoint_index; - new_active_edges[0].left_to_right = true; - new_active_edges[1].left_to_right = false; - - upper_control_point = self.path.endpoints[next_endpoint_index as usize].ctrl; - lower_control_point = self.path.endpoints[endpoint_index as usize].ctrl; - } - - match upper_control_point { - None => new_active_edges[0].control_point_vertex_index = u32::MAX, - Some(control_point_position) => { - new_active_edges[0].control_point_vertex_index = - self.mesh.b_vertex_loop_blinn_data.len() as u32; - - let right_vertex_position = - self.path.endpoints[new_active_edges[0].right_endpoint_index as usize].to; - let control_point_b_vertex_loop_blinn_data = - BVertexLoopBlinnData::control_point(&position, - &control_point_position, - &right_vertex_position, - false); - - // FIXME(pcwalton): Normal - self.mesh.add_b_vertex(&control_point_position, - &control_point_b_vertex_loop_blinn_data) - } - } - - match lower_control_point { - None => new_active_edges[1].control_point_vertex_index = u32::MAX, - Some(control_point_position) => { - new_active_edges[1].control_point_vertex_index = - self.mesh.b_vertex_loop_blinn_data.len() as u32; - - let right_vertex_position = - self.path.endpoints[new_active_edges[1].right_endpoint_index as usize].to; - let control_point_b_vertex_loop_blinn_data = - BVertexLoopBlinnData::control_point(&position, - &control_point_position, - &right_vertex_position, - true); - - // FIXME(pcwalton): Normal - self.mesh.add_b_vertex(&control_point_position, - &control_point_b_vertex_loop_blinn_data) - } - } - } - - fn active_edges_are_ordered(&mut self, - prev_active_edge_index: u32, - next_active_edge_index: u32, - x: f32) - -> bool { - let prev_active_edge = &self.active_edges[prev_active_edge_index as usize]; - let next_active_edge = &self.active_edges[next_active_edge_index as usize]; - if prev_active_edge.right_endpoint_index == next_active_edge.right_endpoint_index { - // Always ordered. - // FIXME(pcwalton): Is this true? - return true - } - - // TODO(pcwalton): See if we can speed this up. It's trickier than it seems, due to path - // self intersection! - let prev_active_edge_t = self.solve_active_edge_t_for_x(x, prev_active_edge); - let next_active_edge_t = self.solve_active_edge_t_for_x(x, next_active_edge); - let prev_active_edge_y = self.sample_active_edge(prev_active_edge_t, prev_active_edge).y; - let next_active_edge_y = self.sample_active_edge(next_active_edge_t, next_active_edge).y; - prev_active_edge_y <= next_active_edge_y - } - - fn init_heap(&mut self) { - for endpoint_index in 0..(self.path.endpoints.len() as u32) { - if let EndpointClass::Min = self.classify_endpoint(endpoint_index) { - let new_point = self.create_point_from_endpoint(endpoint_index); - self.heap.push(new_point) - } - } - } - - fn bounding_active_edges_for_fill(&self, active_edge_index: u32) -> (u32, u32) { - match self.fill_rule { - FillRule::EvenOdd if active_edge_index % 2 == 1 => { - (active_edge_index - 1, active_edge_index) - } - FillRule::EvenOdd if (active_edge_index as usize) + 1 == self.active_edges.len() => { - (active_edge_index, active_edge_index) - } - FillRule::EvenOdd => (active_edge_index, active_edge_index + 1), - - FillRule::Winding => { - let (mut winding_number, mut upper_active_edge_index) = (0, 0); - for (active_edge_index, active_edge) in - self.active_edges[0..active_edge_index as usize].iter().enumerate() { - if winding_number == 0 { - upper_active_edge_index = active_edge_index as u32 - } - winding_number += active_edge.winding_number() - } - if winding_number == 0 { - upper_active_edge_index = active_edge_index as u32 - } - - let mut lower_active_edge_index = active_edge_index; - for (active_edge_index, active_edge) in - self.active_edges.iter().enumerate().skip(active_edge_index as usize) { - winding_number += active_edge.winding_number(); - if winding_number == 0 { - lower_active_edge_index = active_edge_index as u32; - break - } - } - - (upper_active_edge_index, lower_active_edge_index) - } - } - } - - fn emit_b_quads_around_active_edge(&mut self, active_edge_index: u32, right_x: f32) - -> BQuadEmissionResult { - if (active_edge_index as usize) >= self.active_edges.len() { - return BQuadEmissionResult::NoBQuadEmitted - } - - // TODO(pcwalton): Assert that the green X position is the same on both edges. - let (upper_active_edge_index, lower_active_edge_index) = - self.bounding_active_edges_for_fill(active_edge_index); - debug!("... bounding active edges for fill = [{},{}] around {}", - upper_active_edge_index, - lower_active_edge_index, - active_edge_index); - - let emission_result = BQuadEmissionResult::new(active_edge_index, - upper_active_edge_index, - lower_active_edge_index); - if emission_result == BQuadEmissionResult::NoBQuadEmitted { - return emission_result - } - - if !self.should_subdivide_active_edge_at(upper_active_edge_index, right_x) || - !self.should_subdivide_active_edge_at(lower_active_edge_index, right_x) { - return emission_result - } - - let upper_curve = self.subdivide_active_edge_at(upper_active_edge_index, - right_x, - SubdivisionType::Upper); - for active_edge_index in (upper_active_edge_index + 1)..lower_active_edge_index { - if self.should_subdivide_active_edge_at(active_edge_index, right_x) { - self.subdivide_active_edge_at(active_edge_index, right_x, SubdivisionType::Inside); - self.active_edges[active_edge_index as usize].toggle_parity(); - } - } - let lower_curve = self.subdivide_active_edge_at(lower_active_edge_index, - right_x, - SubdivisionType::Lower); - - self.emit_b_quads(upper_active_edge_index, - lower_active_edge_index, - &upper_curve, - &lower_curve, - 0); - - emission_result - } - - /// Toggles parity at the end. - fn emit_b_quads(&mut self, - upper_active_edge_index: u32, - lower_active_edge_index: u32, - upper_subdivision: &SubdividedActiveEdge, - lower_subdivision: &SubdividedActiveEdge, - iteration: u8) { - let upper_shape = upper_subdivision.shape(&self.mesh.b_vertex_loop_blinn_data); - let lower_shape = lower_subdivision.shape(&self.mesh.b_vertex_loop_blinn_data); - - // Make sure the convex hulls of the two curves do not intersect. If they do, subdivide and - // recurse. - if iteration < MAX_B_QUAD_SUBDIVISIONS { - // TODO(pcwalton): Handle concave-line convex hull intersections. - if let (Some(upper_curve), Some(lower_curve)) = - (upper_subdivision.to_curve(&self.mesh.b_vertex_positions), - lower_subdivision.to_curve(&self.mesh.b_vertex_positions)) { - // TODO(pcwalton): Handle concave-concave convex hull intersections. - if upper_shape == Shape::Concave && - lower_curve.baseline() - .to_line() - .signed_distance_to_point(&upper_curve.ctrl) > - f32::approx_epsilon() { - let (upper_left_subsubdivision, upper_right_subsubdivision) = - self.subdivide_active_edge_again_at_t(&upper_subdivision, - 0.5, - false); - let midpoint_x = - self.mesh - .b_vertex_positions[upper_left_subsubdivision.middle_point as usize].x; - let (lower_left_subsubdivision, lower_right_subsubdivision) = - self.subdivide_active_edge_again_at_x(&lower_subdivision, - midpoint_x, - true); - - self.emit_b_quads(upper_active_edge_index, - lower_active_edge_index, - &upper_left_subsubdivision, - &lower_left_subsubdivision, - iteration + 1); - self.emit_b_quads(upper_active_edge_index, - lower_active_edge_index, - &upper_right_subsubdivision, - &lower_right_subsubdivision, - iteration + 1); - return; - } - - if lower_shape == Shape::Concave && - upper_curve.baseline() - .to_line() - .signed_distance_to_point(&lower_curve.ctrl) < - -f32::approx_epsilon() { - let (lower_left_subsubdivision, lower_right_subsubdivision) = - self.subdivide_active_edge_again_at_t(&lower_subdivision, - 0.5, - true); - let midpoint_x = - self.mesh - .b_vertex_positions[lower_left_subsubdivision.middle_point as usize].x; - let (upper_left_subsubdivision, upper_right_subsubdivision) = - self.subdivide_active_edge_again_at_x(&upper_subdivision, - midpoint_x, - false); - - self.emit_b_quads(upper_active_edge_index, - lower_active_edge_index, - &upper_left_subsubdivision, - &lower_left_subsubdivision, - iteration + 1); - self.emit_b_quads(upper_active_edge_index, - lower_active_edge_index, - &upper_right_subsubdivision, - &lower_right_subsubdivision, - iteration + 1); - return; - } - } - } - - debug!("... emitting B-quad: UL {} BL {} UR {} BR {}", - upper_subdivision.left_curve_left, - lower_subdivision.left_curve_left, - upper_subdivision.middle_point, - lower_subdivision.middle_point); - - { - let upper_active_edge = &mut self.active_edges[upper_active_edge_index as usize]; - self.mesh.b_vertex_loop_blinn_data[upper_subdivision.middle_point as usize] = - BVertexLoopBlinnData::new(upper_active_edge.endpoint_kind()); - upper_active_edge.toggle_parity(); - } - { - let lower_active_edge = &mut self.active_edges[lower_active_edge_index as usize]; - self.mesh.b_vertex_loop_blinn_data[lower_subdivision.middle_point as usize] = - BVertexLoopBlinnData::new(lower_active_edge.endpoint_kind()); - lower_active_edge.toggle_parity(); - } - - 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.mesh.add_b_quad(&b_quad); - } - - fn subdivide_active_edge_again_at_t(&mut self, - subdivision: &SubdividedActiveEdge, - t: f32, - bottom: bool) - -> (SubdividedActiveEdge, SubdividedActiveEdge) { - let curve = subdivision.to_curve(&self.mesh.b_vertex_positions) - .expect("subdivide_active_edge_again_at_t(): not a curve!"); - let (left_curve, right_curve) = curve.assume_monotonic().split(t); - - let left_control_point_index = self.mesh.b_vertex_positions.len() as u32; - let midpoint_index = left_control_point_index + 1; - let right_control_point_index = midpoint_index + 1; - self.mesh.b_vertex_positions.extend([ - left_curve.segment().ctrl, - left_curve.segment().to, - right_curve.segment().ctrl, - ].into_iter()); - - // Initially, assume that the parity is false. We will modify the Loop-Blinn data later if - // that is incorrect. - self.mesh.b_vertex_loop_blinn_data.extend([ - BVertexLoopBlinnData::control_point(&left_curve.segment().from, - &left_curve.segment().ctrl, - &left_curve.segment().to, - bottom), - BVertexLoopBlinnData::new(BVertexKind::Endpoint0), - BVertexLoopBlinnData::control_point(&right_curve.segment().from, - &right_curve.segment().ctrl, - &right_curve.segment().to, - bottom), - ].into_iter()); - - // FIXME(pcwalton): Normal - - (SubdividedActiveEdge { - left_curve_left: subdivision.left_curve_left, - left_curve_control_point: left_control_point_index, - middle_point: midpoint_index, - }, SubdividedActiveEdge { - left_curve_left: midpoint_index, - left_curve_control_point: right_control_point_index, - middle_point: subdivision.middle_point, - }) - } - - fn subdivide_active_edge_again_at_x(&mut self, - subdivision: &SubdividedActiveEdge, - x: f32, - bottom: bool) - -> (SubdividedActiveEdge, SubdividedActiveEdge) { - let curve = subdivision.to_curve(&self.mesh.b_vertex_positions) - .expect("subdivide_active_edge_again_at_x(): not a curve!"); - let t = curve.assume_monotonic().solve_t_for_x(x); - self.subdivide_active_edge_again_at_t(subdivision, t, bottom) - } - - fn already_visited_point(&self, point: &Point) -> bool { - // FIXME(pcwalton): This makes the visited vector too big. - let index = point.endpoint_index as usize; - match self.visited_points.get(index) { - None => false, - Some(visited) => visited, - } - } - - fn mark_point_as_visited(&mut self, point: &Point) { - // FIXME(pcwalton): This makes the visited vector too big. - self.visited_points.set(point.endpoint_index as usize, true) - } - - fn find_right_point_in_active_edge_list(&self, endpoint_index: u32) -> MatchingActiveEdges { - let mut matching_active_edges = MatchingActiveEdges { - indices: [0, 0], - count: 0, - }; - - for (active_edge_index, active_edge) in self.active_edges.iter().enumerate() { - if active_edge.right_endpoint_index == endpoint_index { - matching_active_edges.indices[matching_active_edges.count as usize] = - active_edge_index as u32; - matching_active_edges.count += 1; - if matching_active_edges.count == 2 { - break - } - } - } - - matching_active_edges - } - - fn classify_endpoint(&self, endpoint_index: u32) -> EndpointClass { - // Create temporary points just for the comparison. - let point = self.create_point_from_endpoint(endpoint_index); - let prev_point = self.create_point_from_endpoint(self.prev_endpoint_of(endpoint_index)); - let next_point = self.create_point_from_endpoint(self.next_endpoint_of(endpoint_index)); - - // Remember to reverse, because the comparison is reversed (as the heap is a max-heap). - match (prev_point.cmp(&point).reverse(), next_point.cmp(&point).reverse()) { - (Ordering::Less, Ordering::Less) => EndpointClass::Max, - (Ordering::Less, _) | (_, Ordering::Less) => EndpointClass::Regular, - (_, _) => EndpointClass::Min, - } - } - - fn find_point_between_active_edges(&self, endpoint_index: u32) -> u32 { - let endpoint = &self.path.endpoints[endpoint_index as usize]; - match self.active_edges.iter().position(|active_edge| { - self.solve_active_edge_y_for_x(endpoint.to.x, active_edge) > endpoint.to.y - }) { - Some(active_edge_index) => active_edge_index as u32, - None => self.active_edges.len() as u32, - } - } - - fn solve_active_edge_t_for_x(&self, x: f32, active_edge: &ActiveEdge) -> f32 { - let left_vertex_position = - &self.mesh.b_vertex_positions[active_edge.left_vertex_index as usize]; - let right_endpoint_position = - &self.path.endpoints[active_edge.right_endpoint_index as usize].to; - match active_edge.control_point_vertex_index { - u32::MAX => { - LineSegment { - from: *left_vertex_position, - to: *right_endpoint_position, - }.solve_t_for_x(x) - } - control_point_vertex_index => { - let control_point = &self.mesh - .b_vertex_positions[control_point_vertex_index as usize]; - let segment = QuadraticBezierSegment { - from: *left_vertex_position, - ctrl: *control_point, - to: *right_endpoint_position, - }; - segment.assume_monotonic().solve_t_for_x(x) - } - } - } - - fn solve_active_edge_y_for_x(&self, x: f32, active_edge: &ActiveEdge) -> f32 { - self.sample_active_edge(self.solve_active_edge_t_for_x(x, active_edge), active_edge).y - } - - fn sample_active_edge(&self, t: f32, active_edge: &ActiveEdge) -> Point2D { - let left_vertex_position = - &self.mesh.b_vertex_positions[active_edge.left_vertex_index as usize]; - let right_endpoint_position = - &self.path.endpoints[active_edge.right_endpoint_index as usize].to; - match active_edge.control_point_vertex_index { - u32::MAX => { - left_vertex_position.to_vector() - .lerp(right_endpoint_position.to_vector(), t) - .to_point() - } - control_point_vertex_index => { - let control_point = &self.mesh - .b_vertex_positions[control_point_vertex_index as usize]; - QuadraticBezierSegment { - from: *left_vertex_position, - ctrl: *control_point, - to: *right_endpoint_position, - }.sample(t) - } - } - } - - fn crossing_point_for_active_edge(&self, upper_active_edge_index: u32, max_x: f32) - -> Option> { - let lower_active_edge_index = upper_active_edge_index + 1; - - let upper_active_edge = &self.active_edges[upper_active_edge_index as usize]; - let lower_active_edge = &self.active_edges[lower_active_edge_index as usize]; - if upper_active_edge.left_vertex_index == lower_active_edge.left_vertex_index || - upper_active_edge.right_endpoint_index == lower_active_edge.right_endpoint_index { - return None - } - - let upper_left_vertex_position = - &self.mesh.b_vertex_positions[upper_active_edge.left_vertex_index as usize]; - let upper_right_endpoint_position = - &self.path.endpoints[upper_active_edge.right_endpoint_index as usize].to; - let lower_left_vertex_position = - &self.mesh.b_vertex_positions[lower_active_edge.left_vertex_index as usize]; - let lower_right_endpoint_position = - &self.path.endpoints[lower_active_edge.right_endpoint_index as usize].to; - - match (upper_active_edge.control_point_vertex_index, - lower_active_edge.control_point_vertex_index) { - (u32::MAX, u32::MAX) => { - let (upper_line, _) = LineSegment { - from: *upper_left_vertex_position, - to: *upper_right_endpoint_position, - }.split_at_x(max_x); - let (lower_line, _) = LineSegment { - from: *lower_left_vertex_position, - to: *lower_right_endpoint_position, - }.split_at_x(max_x); - upper_line.intersection(&lower_line) - } - - (upper_control_point_vertex_index, u32::MAX) => { - let upper_control_point = - &self.mesh.b_vertex_positions[upper_control_point_vertex_index as usize]; - let (upper_curve, _) = QuadraticBezierSegment { - from: *upper_left_vertex_position, - ctrl: *upper_control_point, - to: *upper_right_endpoint_position, - }.assume_monotonic().split_at_x(max_x); - let (lower_line, _) = LineSegment { - from: *lower_left_vertex_position, - to: *lower_right_endpoint_position, - }.split_at_x(max_x); - upper_curve.segment().line_segment_intersections(&lower_line).pop() - } - - (u32::MAX, lower_control_point_vertex_index) => { - let lower_control_point = - &self.mesh.b_vertex_positions[lower_control_point_vertex_index as usize]; - let (lower_curve, _) = QuadraticBezierSegment { - from: *lower_left_vertex_position, - ctrl: *lower_control_point, - to: *lower_right_endpoint_position, - }.assume_monotonic().split_at_x(max_x); - let (upper_line, _) = LineSegment { - from: *upper_left_vertex_position, - to: *upper_right_endpoint_position, - }.split_at_x(max_x); - lower_curve.segment().line_segment_intersections(&upper_line).pop() - } - - (upper_control_point_vertex_index, lower_control_point_vertex_index) => { - let upper_control_point = - &self.mesh.b_vertex_positions[upper_control_point_vertex_index as usize]; - let lower_control_point = - &self.mesh.b_vertex_positions[lower_control_point_vertex_index as usize]; - let (upper_curve, _) = QuadraticBezierSegment { - from: *upper_left_vertex_position, - ctrl: *upper_control_point, - to: *upper_right_endpoint_position, - }.assume_monotonic().split_at_x(max_x); - let (lower_curve, _) = QuadraticBezierSegment { - from: *lower_left_vertex_position, - ctrl: *lower_control_point, - to: *lower_right_endpoint_position, - }.assume_monotonic().split_at_x(max_x); - upper_curve.first_intersection(0.0..1.0, - &lower_curve, - 0.0..1.0, - INTERSECTION_TOLERANCE) - } - } - } - - fn should_subdivide_active_edge_at(&self, active_edge_index: u32, x: f32) -> bool { - let left_curve_left = self.active_edges[active_edge_index as usize].left_vertex_index; - let left_point_position = self.mesh.b_vertex_positions[left_curve_left as usize]; - x - left_point_position.x >= f32::approx_epsilon() - } - - /// Does *not* toggle parity. You must do this after calling this function. - fn subdivide_active_edge_at(&mut self, - active_edge_index: u32, - x: f32, - subdivision_type: SubdivisionType) - -> SubdividedActiveEdge { - let left_curve_left = self.active_edges[active_edge_index as usize].left_vertex_index; - let left_point_position = self.mesh.b_vertex_positions[left_curve_left as usize]; - - let t = self.solve_active_edge_t_for_x(x, &self.active_edges[active_edge_index as usize]); - - let bottom = subdivision_type == SubdivisionType::Lower; - let active_edge = &mut self.active_edges[active_edge_index as usize]; - - let left_curve_control_point_vertex_index; - match active_edge.control_point_vertex_index { - u32::MAX => { - let right_point = - self.path.endpoints[active_edge.right_endpoint_index as usize].to; - let middle_point = left_point_position.to_vector() - .lerp(right_point.to_vector(), t); - - // FIXME(pcwalton): Normal - active_edge.left_vertex_index = self.mesh.b_vertex_loop_blinn_data.len() as u32; - self.mesh.add_b_vertex(&middle_point.to_point(), - &BVertexLoopBlinnData::new(active_edge.endpoint_kind())); - - left_curve_control_point_vertex_index = u32::MAX; - } - _ => { - let left_endpoint_position = - self.mesh.b_vertex_positions[active_edge.left_vertex_index as usize]; - let control_point_position = - self.mesh - .b_vertex_positions[active_edge.control_point_vertex_index as usize]; - let right_endpoint_position = - self.path.endpoints[active_edge.right_endpoint_index as usize].to; - let (left_curve, right_curve) = QuadraticBezierSegment { - from: left_endpoint_position, - ctrl: control_point_position, - to: right_endpoint_position, - }.split(t); - - left_curve_control_point_vertex_index = - self.mesh.b_vertex_loop_blinn_data.len() as u32; - 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; - - // FIXME(pcwalton): Normals - self.mesh - .add_b_vertex(&left_curve.ctrl, - &BVertexLoopBlinnData::control_point(&left_curve.from, - &left_curve.ctrl, - &left_curve.to, - bottom)); - self.mesh.add_b_vertex(&left_curve.to, - &BVertexLoopBlinnData::new(active_edge.endpoint_kind())); - self.mesh - .add_b_vertex(&right_curve.ctrl, - &BVertexLoopBlinnData::control_point(&right_curve.from, - &right_curve.ctrl, - &right_curve.to, - bottom)); - } - } - - SubdividedActiveEdge { - left_curve_left: left_curve_left, - left_curve_control_point: left_curve_control_point_vertex_index, - middle_point: active_edge.left_vertex_index, - } - } - - // FIXME(pcwalton): This creates incorrect normals for vertical lines. I think we should - // probably calculate normals for the path vertices first and then lerp them to calculate these - // B-vertex normals. That would be simpler, faster, and more correct, I suspect. - 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.mesh.b_vertex_positions[left_vertex_index as usize]; - let right_vertex_position = &self.mesh.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.path.endpoints[endpoint_index as usize]; - let subpath = &self.path.subpath_ranges[endpoint.subpath_index as usize]; - if endpoint_index > subpath.start { - endpoint_index - 1 - } else { - subpath.end - 1 - } - } - - fn next_endpoint_of(&self, endpoint_index: u32) -> u32 { - let endpoint = &self.path.endpoints[endpoint_index as usize]; - let subpath = &self.path.subpath_ranges[endpoint.subpath_index as usize]; - if endpoint_index + 1 < subpath.end { - endpoint_index + 1 - } else { - subpath.start - } - } - - fn create_point_from_endpoint(&self, endpoint_index: u32) -> Point { - Point { - position: self.path.endpoints[endpoint_index as usize].to, - endpoint_index: endpoint_index, - } - } - - fn control_point_before_endpoint(&self, endpoint_index: u32) -> Option> { - self.path.endpoints[endpoint_index as usize].ctrl - } - - fn control_point_after_endpoint(&self, endpoint_index: u32) -> Option> { - self.control_point_before_endpoint(self.next_endpoint_of(endpoint_index)) - } -} - -#[derive(Debug, Clone, Copy)] -struct Point { - position: Point2D, - endpoint_index: u32, -} - -impl PartialEq for Point { - #[inline] - fn eq(&self, other: &Point) -> bool { - self.position == other.position && self.endpoint_index == other.endpoint_index - } - #[inline] - fn ne(&self, other: &Point) -> bool { - self.position != other.position || self.endpoint_index != other.endpoint_index - } -} - -impl Eq for Point {} - -impl PartialOrd for Point { - #[inline] - fn partial_cmp(&self, other: &Point) -> Option { - // Reverse, because `std::collections::BinaryHeap` is a *max*-heap! - match other.position.x.partial_cmp(&self.position.x) { - None | Some(Ordering::Equal) => {} - Some(ordering) => return Some(ordering), - } - match other.position.y.partial_cmp(&self.position.y) { - None | Some(Ordering::Equal) => {} - Some(ordering) => return Some(ordering), - } - other.endpoint_index.partial_cmp(&self.endpoint_index) - } -} - -impl Ord for Point { - #[inline] - fn cmp(&self, other: &Point) -> Ordering { - self.partial_cmp(other).unwrap_or(Ordering::Equal) - } -} - -#[derive(Debug, Clone, Copy)] -struct ActiveEdge { - left_vertex_index: u32, - control_point_vertex_index: u32, - right_endpoint_index: u32, - left_to_right: bool, - parity: bool, -} - -impl Default for ActiveEdge { - fn default() -> ActiveEdge { - ActiveEdge { - left_vertex_index: 0, - control_point_vertex_index: u32::MAX, - right_endpoint_index: 0, - left_to_right: false, - parity: false, - } - } -} - -impl ActiveEdge { - fn toggle_parity(&mut self) { - self.parity = !self.parity - } - - fn endpoint_kind(&self) -> BVertexKind { - if !self.parity { - BVertexKind::Endpoint0 - } else { - BVertexKind::Endpoint1 - } - } - - #[inline] - fn winding_number(&self) -> i32 { - if self.left_to_right { - 1 - } else { - -1 - } - } -} - -#[derive(Debug, Clone, Copy)] -struct SubdividedActiveEdge { - left_curve_left: u32, - left_curve_control_point: u32, - middle_point: u32, -} - -impl SubdividedActiveEdge { - fn shape(&self, b_vertex_loop_blinn_data: &[BVertexLoopBlinnData]) -> Shape { - if self.left_curve_control_point == u32::MAX { - Shape::Flat - } else if b_vertex_loop_blinn_data[self.left_curve_control_point as usize].sign < 0 { - Shape::Convex - } else { - Shape::Concave - } - } - - fn to_curve(&self, b_vertex_positions: &[Point2D]) - -> Option> { - if self.left_curve_control_point == u32::MAX { - None - } else { - Some(QuadraticBezierSegment { - from: b_vertex_positions[self.left_curve_left as usize], - ctrl: b_vertex_positions[self.left_curve_control_point as usize], - to: b_vertex_positions[self.middle_point as usize], - }) - } - } -} - -#[derive(Debug, Clone, Copy)] -enum EndpointClass { - Min, - Regular, - Max, -} - -#[derive(Debug, Clone, Copy)] -struct MatchingActiveEdges { - indices: [u32; 2], - count: u8, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum Shape { - Flat, - Convex, - Concave, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum BQuadEmissionResult { - NoBQuadEmitted, - BQuadEmittedBelow, - BQuadEmittedAbove, - BQuadEmittedAround, -} - -impl BQuadEmissionResult { - fn new(active_edge_index: u32, upper_active_edge_index: u32, lower_active_edge_index: u32) - -> BQuadEmissionResult { - if upper_active_edge_index == lower_active_edge_index { - BQuadEmissionResult::NoBQuadEmitted - } else if upper_active_edge_index == active_edge_index { - BQuadEmissionResult::BQuadEmittedBelow - } else if lower_active_edge_index == active_edge_index { - BQuadEmissionResult::BQuadEmittedAbove - } else { - BQuadEmissionResult::BQuadEmittedAround - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum SubdivisionType { - Upper, - 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, -} - -impl VertexNormal { - fn new(vertex_a: &Point2D, vertex_b: &Point2D) -> VertexNormal { - let vector = *vertex_a - *vertex_b; - VertexNormal { - sum: Vector2D::new(-vector.y, vector.x).normalize(), - } - } - - fn zero() -> VertexNormal { - VertexNormal { - sum: Vector2D::zero(), - } - } -} - -impl Add for VertexNormal { - type Output = VertexNormal; - fn add(self, rhs: VertexNormal) -> VertexNormal { - VertexNormal { - sum: self.sum + rhs.sum, - } - } -} - -impl AddAssign for VertexNormal { - fn add_assign(&mut self, rhs: VertexNormal) { - *self = *self + rhs - } -} diff --git a/utils/frontend/Cargo.toml b/utils/frontend/Cargo.toml deleted file mode 100644 index 88d24ab8..00000000 --- a/utils/frontend/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "pathfinder" -version = "0.2.0" -authors = ["Patrick Walton "] - -[dependencies] -clap = "2.27" -lyon_path = "0.12" - -[dependencies.font-kit] -git = "https://github.com/pcwalton/font-kit" -features = ["loader-freetype-default"] - -[dependencies.pathfinder_geometry] -path = "../../geometry" - -[dependencies.pathfinder_partitioner] -path = "../../partitioner" diff --git a/utils/frontend/src/main.rs b/utils/frontend/src/main.rs deleted file mode 100644 index 131927ba..00000000 --- a/utils/frontend/src/main.rs +++ /dev/null @@ -1,111 +0,0 @@ -// pathfinder/utils/frontend/main.rs -// -// Copyright © 2017 The Pathfinder Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Pathfinder is built as a set of modular Rust crates and accompanying shaders. Depending on how -//! you plan to use Pathfinder, you may need to link against many of these crates, or you may not -//! need to link against any of them and and only use the shaders at runtime. -//! -//! Typically, if you need to generate paths at runtime or load fonts on the fly, then you will -//! need to use the `pathfinder_partitioner` and/or `pathfinder_font_renderer` crates. If your app -//! instead uses a fixed set of paths or fonts, then you may wish to consider running the -//! Pathfinder command-line tool as part of your build process. Note that in the latter case you -//! may not need to ship any Rust code at all! -//! -//! This crate defines the `pathfinder` command line tool. It takes a font as an argument and -//! produces *mesh libraries* for the glyphs you wish to include. A *mesh library* is essentially a -//! simple storage format for VBOs. To render these paths, you can directly upload these VBOs to -//! the GPU and render them using the shaders provided. - -extern crate clap; -extern crate font_kit; -extern crate lyon_path; -extern crate pathfinder_geometry; -extern crate pathfinder_partitioner; - -use clap::{App, Arg}; -use font_kit::font::Font; -use font_kit::hinting::HintingOptions; -use lyon_path::PathEvent; -use lyon_path::builder::{FlatPathBuilder, PathBuilder}; -use lyon_path::default::Path as LyonPath; -use pathfinder_geometry::FillRule; -use pathfinder_geometry::mesh_pack::MeshPack; -use pathfinder_geometry::partitioner::Partitioner; -use std::fs::File; -use std::path::{Path, PathBuf}; -use std::process; - -fn convert_font(font_path: &Path, output_path: &Path) -> Result<(), ()> { - let font = try!(Font::from_path(font_path, 0).map_err(drop)); - let glyph_count = font.glyph_count(); - - let mut paths: Vec<(u16, Vec)> = vec![]; - let mut mesh_pack = MeshPack::new(); - - for glyph_index in 0..glyph_count { - let mut path_builder = LyonPath::builder(); - if font.outline(glyph_index, HintingOptions::None, &mut path_builder).is_err() { - continue - } - let path = path_builder.build(); - - let mut partitioner = Partitioner::new(); - - let path_index = (glyph_index + 1) as u16; - partitioner.mesh_mut().push_stencil_segments(path.iter()); - path.iter().for_each(|event| partitioner.builder_mut().path_event(event)); - partitioner.partition(FillRule::Winding); - partitioner.builder_mut().build_and_reset(); - - paths.push((path_index, path.iter().collect())); - mesh_pack.push(partitioner.into_mesh()); - } - - let mut output_file = try!(File::create(output_path).map_err(drop)); - mesh_pack.serialize_into(&mut output_file).map_err(drop) -} - -pub fn main() { - let app = App::new("Pathfinder Build Utility") - .version("0.1") - .author("The Pathfinder Project Developers") - .about("Builds meshes from fonts for use with Pathfinder") - .arg(Arg::with_name("FONT-PATH").help("The `.ttf` or `.otf` font file to use") - .required(true) - .index(1)) - .arg(Arg::with_name("OUTPUT-PATH").help("The `.pfml` mesh library to produce").index(2)); - let matches = app.get_matches(); - - let font_path = matches.value_of("FONT-PATH").unwrap(); - let font_path = Path::new(font_path); - - let output_path = match matches.value_of("OUTPUT-PATH") { - Some(output_path) => PathBuf::from(output_path), - None => { - match font_path.file_stem() { - None => { - eprintln!("error: No valid input path specified"); - process::exit(1) - } - Some(output_path) => { - let mut output_path = PathBuf::from(output_path); - output_path.set_extension("pfml"); - output_path - } - } - } - }; - - if convert_font(font_path, &output_path).is_err() { - // TODO(pcwalton): Better error handling. - eprintln!("error: Failed"); - process::exit(1) - } -}