Merge pull request #335 from Veedrac/master
Change how point_queue and active_edges are sorted for performance
This commit is contained in:
commit
33cd3798d7
|
@ -8,7 +8,6 @@
|
||||||
// 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.
|
||||||
|
|
||||||
use crate::sorted_vector::SortedVector;
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_geometry::line_segment::LineSegment2F;
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||||
|
@ -16,7 +15,7 @@ use pathfinder_geometry::transform2d::Transform2F;
|
||||||
use pathfinder_geometry::vector::Vector2F;
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
use pathfinder_geometry::util as geometry_util;
|
use pathfinder_geometry::util as geometry_util;
|
||||||
use pathfinder_simd::default::F32x2;
|
use pathfinder_simd::default::F32x2;
|
||||||
use std::cmp::{Ordering, PartialOrd};
|
use std::cmp::Ordering;
|
||||||
use std::convert;
|
use std::convert;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -24,10 +23,10 @@ use std::mem;
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct Gradient {
|
pub struct Gradient {
|
||||||
pub geometry: GradientGeometry,
|
pub geometry: GradientGeometry,
|
||||||
stops: SortedVector<ColorStop>,
|
stops: Vec<ColorStop>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub struct ColorStop {
|
pub struct ColorStop {
|
||||||
pub offset: f32,
|
pub offset: f32,
|
||||||
pub color: ColorU,
|
pub color: ColorU,
|
||||||
|
@ -91,7 +90,7 @@ impl Hash for ColorStop {
|
||||||
impl Gradient {
|
impl Gradient {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn linear(line: LineSegment2F) -> Gradient {
|
pub fn linear(line: LineSegment2F) -> Gradient {
|
||||||
Gradient { geometry: GradientGeometry::Linear(line), stops: SortedVector::new() }
|
Gradient { geometry: GradientGeometry::Linear(line), stops: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -104,13 +103,16 @@ impl Gradient {
|
||||||
let transform = Transform2F::default();
|
let transform = Transform2F::default();
|
||||||
Gradient {
|
Gradient {
|
||||||
geometry: GradientGeometry::Radial { line: line.to_line(), radii, transform },
|
geometry: GradientGeometry::Radial { line: line.to_line(), radii, transform },
|
||||||
stops: SortedVector::new(),
|
stops: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add(&mut self, stop: ColorStop) {
|
pub fn add(&mut self, stop: ColorStop) {
|
||||||
self.stops.push(stop);
|
let index = self.stops.binary_search_by(|other| {
|
||||||
|
if other.offset <= stop.offset { Ordering::Less } else { Ordering::Greater }
|
||||||
|
}).unwrap_or_else(convert::identity);
|
||||||
|
self.stops.insert(index, stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A convenience method to add a color stop.
|
/// A convenience method to add a color stop.
|
||||||
|
@ -121,12 +123,12 @@ impl Gradient {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stops(&self) -> &[ColorStop] {
|
pub fn stops(&self) -> &[ColorStop] {
|
||||||
&self.stops.array
|
&self.stops
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stops_mut(&mut self) -> &mut [ColorStop] {
|
pub fn stops_mut(&mut self) -> &mut [ColorStop] {
|
||||||
&mut self.stops.array
|
&mut self.stops
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample(&self, mut t: f32) -> ColorU {
|
pub fn sample(&self, mut t: f32) -> ColorU {
|
||||||
|
@ -136,13 +138,14 @@ impl Gradient {
|
||||||
|
|
||||||
t = geometry_util::clamp(t, 0.0, 1.0);
|
t = geometry_util::clamp(t, 0.0, 1.0);
|
||||||
let last_index = self.stops.len() - 1;
|
let last_index = self.stops.len() - 1;
|
||||||
|
|
||||||
let upper_index = self.stops.binary_search_by(|stop| {
|
let upper_index = self.stops.binary_search_by(|stop| {
|
||||||
stop.offset.partial_cmp(&t).unwrap_or(Ordering::Less)
|
if stop.offset < t || stop.offset == 0.0 { Ordering::Less } else { Ordering::Greater }
|
||||||
}).unwrap_or_else(convert::identity).min(last_index);
|
}).unwrap_or_else(convert::identity).min(last_index);
|
||||||
let lower_index = if upper_index > 0 { upper_index - 1 } else { upper_index };
|
let lower_index = if upper_index > 0 { upper_index - 1 } else { upper_index };
|
||||||
|
|
||||||
let lower_stop = &self.stops.array[lower_index];
|
let lower_stop = &self.stops[lower_index];
|
||||||
let upper_stop = &self.stops.array[upper_index];
|
let upper_stop = &self.stops[upper_index];
|
||||||
|
|
||||||
let denom = upper_stop.offset - lower_stop.offset;
|
let denom = upper_stop.offset - lower_stop.offset;
|
||||||
if denom == 0.0 {
|
if denom == 0.0 {
|
||||||
|
@ -157,12 +160,12 @@ impl Gradient {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_opaque(&self) -> bool {
|
pub fn is_opaque(&self) -> bool {
|
||||||
self.stops.array.iter().all(|stop| stop.color.is_opaque())
|
self.stops.iter().all(|stop| stop.color.is_opaque())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_fully_transparent(&self) -> bool {
|
pub fn is_fully_transparent(&self) -> bool {
|
||||||
self.stops.array.iter().all(|stop| stop.color.is_fully_transparent())
|
self.stops.iter().all(|stop| stop.color.is_fully_transparent())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_transform(&mut self, new_transform: Transform2F) {
|
pub fn apply_transform(&mut self, new_transform: Transform2F) {
|
||||||
|
@ -203,3 +206,37 @@ impl RadialGradientLine for Vector2F {
|
||||||
LineSegment2F::new(self, self)
|
LineSegment2F::new(self, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::gradient::Gradient;
|
||||||
|
use pathfinder_color::ColorU;
|
||||||
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stable_order() {
|
||||||
|
let mut grad = Gradient::linear_from_points(Vector2F::default(), Vector2F::default());
|
||||||
|
for i in 0..110 {
|
||||||
|
grad.add_color_stop(ColorU::new(i, 0, 0, 1), (i % 11) as f32 / 10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that it sorted stably
|
||||||
|
assert!(grad.stops.windows(2).all(|w| {
|
||||||
|
w[0].offset < w[1].offset || w[0].color.r < w[1].color.r
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn never_sample_zero_width() {
|
||||||
|
let mut grad = Gradient::linear_from_points(Vector2F::default(), Vector2F::default());
|
||||||
|
for i in 0..110 {
|
||||||
|
let zero_width = (i == 0) || (11 <= i && i < 99) || (i == 109);
|
||||||
|
grad.add_color_stop(ColorU::new(if zero_width { 255 } else { 0 }, 0, 0, 1), (i % 11) as f32 / 10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..11 {
|
||||||
|
let sample = grad.sample(i as f32 / 10.0);
|
||||||
|
assert!(sample.r == 0, "{} {}", i, sample.r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ pub mod outline;
|
||||||
pub mod pattern;
|
pub mod pattern;
|
||||||
pub mod render_target;
|
pub mod render_target;
|
||||||
pub mod segment;
|
pub mod segment;
|
||||||
pub mod sorted_vector;
|
|
||||||
pub mod stroke;
|
pub mod stroke;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
// pathfinder/content/src/sorted_vector.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! A vector that maintains sorted order with insertion sort.
|
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::convert;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
|
||||||
pub struct SortedVector<T>
|
|
||||||
where
|
|
||||||
T: PartialOrd,
|
|
||||||
{
|
|
||||||
pub array: Vec<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> SortedVector<T>
|
|
||||||
where
|
|
||||||
T: PartialOrd,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> SortedVector<T> {
|
|
||||||
SortedVector { array: vec![] }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn push(&mut self, value: T) {
|
|
||||||
let index = self.binary_search_by(|other| {
|
|
||||||
other.partial_cmp(&value).unwrap_or(Ordering::Less)
|
|
||||||
}).unwrap_or_else(convert::identity);
|
|
||||||
self.array.insert(index, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn peek(&self) -> Option<&T> {
|
|
||||||
self.array.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn pop(&mut self) -> Option<T> {
|
|
||||||
self.array.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.array.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.array.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.array.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
|
|
||||||
where F: FnMut(&'a T) -> Ordering {
|
|
||||||
self.array.binary_search_by(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::sorted_vector::SortedVector;
|
|
||||||
use quickcheck;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sorted_vec() {
|
|
||||||
quickcheck::quickcheck(prop_sorted_vec as fn(Vec<i32>) -> bool);
|
|
||||||
|
|
||||||
fn prop_sorted_vec(mut values: Vec<i32>) -> bool {
|
|
||||||
let mut sorted_vec = SortedVector::new();
|
|
||||||
for &value in &values {
|
|
||||||
sorted_vec.push(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
values.sort();
|
|
||||||
let mut results = Vec::with_capacity(values.len());
|
|
||||||
while !sorted_vec.is_empty() {
|
|
||||||
results.push(sorted_vec.pop().unwrap());
|
|
||||||
}
|
|
||||||
results.reverse();
|
|
||||||
assert_eq!(&values, &results);
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,11 +15,9 @@ use pathfinder_content::effects::BlendMode;
|
||||||
use pathfinder_content::fill::FillRule;
|
use pathfinder_content::fill::FillRule;
|
||||||
use pathfinder_content::outline::{Contour, Outline, PointIndex};
|
use pathfinder_content::outline::{Contour, Outline, PointIndex};
|
||||||
use pathfinder_content::segment::Segment;
|
use pathfinder_content::segment::Segment;
|
||||||
use pathfinder_content::sorted_vector::SortedVector;
|
|
||||||
use pathfinder_geometry::line_segment::LineSegment2F;
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
use pathfinder_geometry::rect::{RectF, RectI};
|
||||||
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
|
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
// TODO(pcwalton): Make this configurable.
|
// TODO(pcwalton): Make this configurable.
|
||||||
|
@ -34,8 +32,9 @@ pub(crate) struct Tiler<'a, 'b> {
|
||||||
outline: &'a Outline,
|
outline: &'a Outline,
|
||||||
path_info: TilingPathInfo<'a>,
|
path_info: TilingPathInfo<'a>,
|
||||||
|
|
||||||
point_queue: SortedVector<QueuedEndpoint>,
|
point_queue: Vec<QueuedEndpoint>,
|
||||||
active_edges: SortedVector<ActiveEdge>,
|
next_point_queue: Vec<QueuedEndpoint>,
|
||||||
|
active_edges: Vec<ActiveEdge>,
|
||||||
old_active_edges: Vec<ActiveEdge>,
|
old_active_edges: Vec<ActiveEdge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +74,10 @@ impl<'a, 'b> Tiler<'a, 'b> {
|
||||||
outline,
|
outline,
|
||||||
path_info,
|
path_info,
|
||||||
|
|
||||||
point_queue: SortedVector::new(),
|
point_queue: Vec::new(),
|
||||||
active_edges: SortedVector::new(),
|
next_point_queue: Vec::new(),
|
||||||
old_active_edges: vec![],
|
active_edges: Vec::new(),
|
||||||
|
old_active_edges: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,18 +107,19 @@ impl<'a, 'b> Tiler<'a, 'b> {
|
||||||
self.process_old_active_edges(strip_origin_y);
|
self.process_old_active_edges(strip_origin_y);
|
||||||
|
|
||||||
// Add new active edges.
|
// Add new active edges.
|
||||||
let strip_max_y = ((i32::from(strip_origin_y) + 1) * TILE_HEIGHT as i32) as f32;
|
let strip_max_y = ((strip_origin_y + 1) * TILE_HEIGHT as i32) as f32;
|
||||||
while let Some(queued_endpoint) = self.point_queue.peek() {
|
while let Some(queued_endpoint) = self.point_queue.pop() {
|
||||||
// We're done when we see an endpoint that belongs to the next tile strip.
|
|
||||||
//
|
|
||||||
// Note that this test must be `>`, not `>=`, in order to make sure we don't miss
|
// Note that this test must be `>`, not `>=`, in order to make sure we don't miss
|
||||||
// active edges that lie precisely on the tile strip boundary.
|
// active edges that lie precisely on the tile strip boundary.
|
||||||
if queued_endpoint.y > strip_max_y {
|
if queued_endpoint.y > strip_max_y {
|
||||||
break;
|
self.next_point_queue.push(queued_endpoint);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.add_new_active_edge(strip_origin_y);
|
self.add_new_active_edge(strip_origin_y, queued_endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mem::swap(&mut self.point_queue, &mut self.next_point_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pack_and_cull(&mut self) {
|
fn pack_and_cull(&mut self) {
|
||||||
|
@ -180,16 +181,17 @@ impl<'a, 'b> Tiler<'a, 'b> {
|
||||||
let mut current_winding = 0;
|
let mut current_winding = 0;
|
||||||
|
|
||||||
debug_assert!(self.old_active_edges.is_empty());
|
debug_assert!(self.old_active_edges.is_empty());
|
||||||
mem::swap(&mut self.old_active_edges, &mut self.active_edges.array);
|
mem::swap(&mut self.old_active_edges, &mut self.active_edges);
|
||||||
|
|
||||||
// FIXME(pcwalton): Yuck.
|
// FIXME(pcwalton): Yuck.
|
||||||
let mut last_segment_x = -9999.0;
|
let mut last_segment_x = -9999.0;
|
||||||
|
|
||||||
let tile_top = (i32::from(tile_y) * TILE_HEIGHT as i32) as f32;
|
let tile_top = (tile_y * TILE_HEIGHT as i32) as f32;
|
||||||
|
|
||||||
debug!("---------- tile y {}({}) ----------", tile_y, tile_top);
|
debug!("---------- tile y {}({}) ----------", tile_y, tile_top);
|
||||||
debug!("old active edges: {:#?}", self.old_active_edges);
|
debug!("old active edges: {:#?}", self.old_active_edges);
|
||||||
|
|
||||||
|
self.old_active_edges.sort_unstable_by(|a, b| a.crossing.x().partial_cmp(&b.crossing.x()).unwrap());
|
||||||
for mut active_edge in self.old_active_edges.drain(..) {
|
for mut active_edge in self.old_active_edges.drain(..) {
|
||||||
// Determine x-intercept and winding.
|
// Determine x-intercept and winding.
|
||||||
let segment_x = active_edge.crossing.x();
|
let segment_x = active_edge.crossing.x();
|
||||||
|
@ -224,8 +226,8 @@ impl<'a, 'b> Tiler<'a, 'b> {
|
||||||
let segment_tile_x = f32::floor(segment_x) as i32 / TILE_WIDTH as i32;
|
let segment_tile_x = f32::floor(segment_x) as i32 / TILE_WIDTH as i32;
|
||||||
if current_tile_x < segment_tile_x && current_subtile_x > 0.0 {
|
if current_tile_x < segment_tile_x && current_subtile_x > 0.0 {
|
||||||
let current_x =
|
let current_x =
|
||||||
(i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
(current_tile_x * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
||||||
let tile_right_x = ((i32::from(current_tile_x) + 1) * TILE_WIDTH as i32) as f32;
|
let tile_right_x = ((current_tile_x + 1) * TILE_WIDTH as i32) as f32;
|
||||||
let current_tile_coords = vec2i(current_tile_x, tile_y);
|
let current_tile_coords = vec2i(current_tile_x, tile_y);
|
||||||
self.object_builder.add_active_fill(
|
self.object_builder.add_active_fill(
|
||||||
self.scene_builder,
|
self.scene_builder,
|
||||||
|
@ -259,10 +261,10 @@ impl<'a, 'b> Tiler<'a, 'b> {
|
||||||
// Do final subtile fill, if necessary.
|
// Do final subtile fill, if necessary.
|
||||||
debug_assert_eq!(current_tile_x, segment_tile_x);
|
debug_assert_eq!(current_tile_x, segment_tile_x);
|
||||||
let segment_subtile_x =
|
let segment_subtile_x =
|
||||||
segment_x - (i32::from(current_tile_x) * TILE_WIDTH as i32) as f32;
|
segment_x - (current_tile_x * TILE_WIDTH as i32) as f32;
|
||||||
if segment_subtile_x > current_subtile_x {
|
if segment_subtile_x > current_subtile_x {
|
||||||
let current_x =
|
let current_x =
|
||||||
(i32::from(current_tile_x) * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
(current_tile_x * TILE_WIDTH as i32) as f32 + current_subtile_x;
|
||||||
let current_tile_coords = vec2i(current_tile_x, tile_y);
|
let current_tile_coords = vec2i(current_tile_x, tile_y);
|
||||||
self.object_builder.add_active_fill(
|
self.object_builder.add_active_fill(
|
||||||
self.scene_builder,
|
self.scene_builder,
|
||||||
|
@ -287,9 +289,9 @@ impl<'a, 'b> Tiler<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_new_active_edge(&mut self, tile_y: i32) {
|
fn add_new_active_edge(&mut self, tile_y: i32, queued_endpoint: QueuedEndpoint) {
|
||||||
let outline = &self.outline;
|
let outline = &self.outline;
|
||||||
let point_index = self.point_queue.pop().unwrap().point_index;
|
let point_index = queued_endpoint.point_index;
|
||||||
|
|
||||||
let contour = &outline.contours()[point_index.contour() as usize];
|
let contour = &outline.contours()[point_index.contour() as usize];
|
||||||
|
|
||||||
|
@ -523,7 +525,7 @@ pub fn round_rect_out_to_tile_bounds(rect: RectF) -> RectI {
|
||||||
fn process_active_segment(
|
fn process_active_segment(
|
||||||
contour: &Contour,
|
contour: &Contour,
|
||||||
from_endpoint_index: u32,
|
from_endpoint_index: u32,
|
||||||
active_edges: &mut SortedVector<ActiveEdge>,
|
active_edges: &mut Vec<ActiveEdge>,
|
||||||
builder: &SceneBuilder,
|
builder: &SceneBuilder,
|
||||||
object_builder: &mut ObjectBuilder,
|
object_builder: &mut ObjectBuilder,
|
||||||
tile_y: i32,
|
tile_y: i32,
|
||||||
|
@ -539,21 +541,11 @@ fn process_active_segment(
|
||||||
|
|
||||||
// Queued endpoints
|
// Queued endpoints
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
struct QueuedEndpoint {
|
struct QueuedEndpoint {
|
||||||
point_index: PointIndex,
|
point_index: PointIndex,
|
||||||
y: f32,
|
y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for QueuedEndpoint {}
|
|
||||||
|
|
||||||
impl PartialOrd<QueuedEndpoint> for QueuedEndpoint {
|
|
||||||
fn partial_cmp(&self, other: &QueuedEndpoint) -> Option<Ordering> {
|
|
||||||
// NB: Reversed!
|
|
||||||
(other.y, other.point_index).partial_cmp(&(self.y, self.point_index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Active edges
|
// Active edges
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
@ -581,7 +573,8 @@ impl ActiveEdge {
|
||||||
builder: &SceneBuilder,
|
builder: &SceneBuilder,
|
||||||
object_builder: &mut ObjectBuilder,
|
object_builder: &mut ObjectBuilder,
|
||||||
tile_y: i32) {
|
tile_y: i32) {
|
||||||
let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32;
|
|
||||||
|
let tile_bottom = ((tile_y + 1) * TILE_HEIGHT as i32) as f32;
|
||||||
debug!(
|
debug!(
|
||||||
"process_active_edge({:#?}, tile_y={}({}))",
|
"process_active_edge({:#?}, tile_y={}({}))",
|
||||||
self, tile_y, tile_bottom
|
self, tile_y, tile_bottom
|
||||||
|
@ -625,11 +618,10 @@ impl ActiveEdge {
|
||||||
.as_cubic_segment()
|
.as_cubic_segment()
|
||||||
.is_flat(FLATTENING_TOLERANCE)
|
.is_flat(FLATTENING_TOLERANCE)
|
||||||
{
|
{
|
||||||
let next_t = 0.5 * split_t;
|
split_t *= 0.5;
|
||||||
let (before, after) = oriented_segment.as_cubic_segment().split(next_t);
|
let (before, after) = oriented_segment.as_cubic_segment().split(split_t);
|
||||||
before_segment = before;
|
before_segment = before;
|
||||||
after_segment = Some(after);
|
after_segment = Some(after);
|
||||||
split_t = next_t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -664,7 +656,7 @@ impl ActiveEdge {
|
||||||
object_builder: &mut ObjectBuilder,
|
object_builder: &mut ObjectBuilder,
|
||||||
tile_y: i32,
|
tile_y: i32,
|
||||||
) -> Option<LineSegment2F> {
|
) -> Option<LineSegment2F> {
|
||||||
let tile_bottom = ((i32::from(tile_y) + 1) * TILE_HEIGHT as i32) as f32;
|
let tile_bottom = ((tile_y + 1) * TILE_HEIGHT as i32) as f32;
|
||||||
debug!(
|
debug!(
|
||||||
"process_line_segment({:?}, tile_y={}) tile_bottom={}",
|
"process_line_segment({:?}, tile_y={}) tile_bottom={}",
|
||||||
line_segment, tile_y, tile_bottom
|
line_segment, tile_y, tile_bottom
|
||||||
|
@ -682,12 +674,6 @@ impl ActiveEdge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd<ActiveEdge> for ActiveEdge {
|
|
||||||
fn partial_cmp(&self, other: &ActiveEdge) -> Option<Ordering> {
|
|
||||||
self.crossing.x().partial_cmp(&other.crossing.x())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TileObjectPrimitive {
|
impl Default for TileObjectPrimitive {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> TileObjectPrimitive {
|
fn default() -> TileObjectPrimitive {
|
||||||
|
|
Loading…
Reference in New Issue