wip
This commit is contained in:
parent
670a6001fb
commit
cfd1965a42
|
@ -443,28 +443,31 @@ impl Contour {
|
||||||
|
|
||||||
fn segment_after(&self, point_index: usize) -> Segment {
|
fn segment_after(&self, point_index: usize) -> Segment {
|
||||||
debug_assert!(self.point_is_endpoint(point_index));
|
debug_assert!(self.point_is_endpoint(point_index));
|
||||||
|
|
||||||
|
let mut segment = Segment::new();
|
||||||
|
segment.from = self.points[point_index];
|
||||||
|
segment.flags |= SegmentFlags::HAS_ENDPOINTS;
|
||||||
|
|
||||||
let point1_index = self.add_to_point_index(point_index, 1);
|
let point1_index = self.add_to_point_index(point_index, 1);
|
||||||
if self.point_is_endpoint(point1_index) {
|
if self.point_is_endpoint(point1_index) {
|
||||||
return Segment::Line(LineSegment {
|
segment.to = self.points[point1_index];
|
||||||
from: self.points[point_index],
|
} else {
|
||||||
to: self.points[point1_index],
|
segment.ctrl0 = self.points[point1_index];
|
||||||
})
|
segment.flags |= SegmentFlags::HAS_CONTROL_POINT_0;
|
||||||
}
|
|
||||||
let point2_index = self.add_to_point_index(point_index, 2);
|
let point2_index = self.add_to_point_index(point_index, 2);
|
||||||
if self.point_is_endpoint(point2_index) {
|
if self.point_is_endpoint(point2_index) {
|
||||||
return Segment::Quadratic(QuadraticBezierSegment {
|
segment.to = self.points[point2_index];
|
||||||
from: self.points[point_index],
|
} else {
|
||||||
ctrl: self.points[point1_index],
|
segment.ctrl1 = self.points[point2_index];
|
||||||
to: self.points[point2_index],
|
segment.flags |= SegmentFlags::HAS_CONTROL_POINT_1;
|
||||||
})
|
|
||||||
}
|
|
||||||
let point3_index = self.add_to_point_index(point_index, 3);
|
let point3_index = self.add_to_point_index(point_index, 3);
|
||||||
Segment::Cubic(CubicBezierSegment {
|
segment.to = self.points[point3_index];
|
||||||
from: self.points[point_index],
|
}
|
||||||
ctrl1: self.points[point1_index],
|
}
|
||||||
ctrl2: self.points[point2_index],
|
|
||||||
to: self.points[point3_index],
|
segment
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn point_is_endpoint(&self, point_index: usize) -> bool {
|
fn point_is_endpoint(&self, point_index: usize) -> bool {
|
||||||
|
@ -473,7 +476,12 @@ impl Contour {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_point_index(&self, point_index: usize, addend: usize) -> usize {
|
fn add_to_point_index(&self, point_index: usize, addend: usize) -> usize {
|
||||||
(point_index + addend) % self.points.len()
|
let (index, limit) = (point_index + addend, self.points.len());
|
||||||
|
if index >= limit {
|
||||||
|
index - limit
|
||||||
|
} else {
|
||||||
|
index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,66 +492,89 @@ struct PointIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
enum Segment {
|
struct Segment {
|
||||||
None,
|
from: Point2D<f32>,
|
||||||
Line(LineSegment<f32>),
|
ctrl0: Point2D<f32>,
|
||||||
Quadratic(QuadraticBezierSegment<f32>),
|
ctrl1: Point2D<f32>,
|
||||||
Cubic(CubicBezierSegment<f32>),
|
to: Point2D<f32>,
|
||||||
|
flags: SegmentFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Segment {
|
impl Segment {
|
||||||
fn is_none(&self) -> bool {
|
fn new() -> Segment {
|
||||||
match *self {
|
Segment {
|
||||||
Segment::None => true,
|
from: Point2D::zero(),
|
||||||
_ => false,
|
ctrl0: Point2D::zero(),
|
||||||
|
ctrl1: Point2D::zero(),
|
||||||
|
to: Point2D::zero(),
|
||||||
|
flags: SegmentFlags::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endpoints(&self) -> (Point2D<f32>, Point2D<f32>) {
|
fn from_line(line: &LineSegment<f32>) -> Segment {
|
||||||
match *self {
|
Segment {
|
||||||
Segment::Line(ref line) => (line.from, line.to),
|
from: line.from,
|
||||||
Segment::Quadratic(ref curve) => (curve.from, curve.to),
|
ctrl0: Point2D::zero(),
|
||||||
Segment::Cubic(ref curve) => (curve.from, curve.to),
|
ctrl1: Point2D::zero(),
|
||||||
Segment::None => unreachable!(),
|
to: line.to,
|
||||||
|
flags: SegmentFlags::HAS_ENDPOINTS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn from_quadratic(curve: &QuadraticBezierSegment<f32>) -> Segment {
|
||||||
|
Segment {
|
||||||
|
from: curve.from,
|
||||||
|
ctrl0: curve.ctrl,
|
||||||
|
ctrl1: Point2D::zero(),
|
||||||
|
to: curve.to,
|
||||||
|
flags: SegmentFlags::HAS_ENDPOINTS | SegmentFlags::HAS_CONTROL_POINT_0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_cubic(curve: &CubicBezierSegment<f32>) -> Segment {
|
||||||
|
Segment {
|
||||||
|
from: curve.from,
|
||||||
|
ctrl0: curve.ctrl1,
|
||||||
|
ctrl1: curve.ctrl2,
|
||||||
|
to: curve.to,
|
||||||
|
flags: SegmentFlags::HAS_ENDPOINTS | SegmentFlags::HAS_CONTROL_POINT_0 |
|
||||||
|
SegmentFlags::HAS_CONTROL_POINT_1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_none(&self) -> bool {
|
||||||
|
!self.flags.contains(SegmentFlags::HAS_ENDPOINTS)
|
||||||
|
}
|
||||||
|
|
||||||
// Note: If we convert these to monotonic then we can optimize this method.
|
// Note: If we convert these to monotonic then we can optimize this method.
|
||||||
// TODO(pcwalton): Consider changing the representation of `Segment` to remove the code
|
// TODO(pcwalton): Consider changing the representation of `Segment` to remove the code
|
||||||
// duplication in the branches here?
|
// duplication in the branches here?
|
||||||
fn min_y(&self) -> f32 {
|
fn min_y(&self) -> f32 {
|
||||||
match *self {
|
let mut min_y = f32::min(self.from.y, self.to.y);
|
||||||
Segment::Line(ref line) => f32::min(line.from.y, line.to.y),
|
if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
|
||||||
Segment::Quadratic(ref curve) => {
|
min_y = f32::min(min_y, self.ctrl0.y)
|
||||||
f32::min(f32::min(curve.from.y, curve.ctrl.y), curve.to.y)
|
|
||||||
}
|
}
|
||||||
Segment::Cubic(ref curve) => {
|
if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
|
||||||
f32::min(f32::min(f32::min(curve.from.y, curve.ctrl1.y), curve.ctrl2.y),
|
min_y = f32::min(min_y, self.ctrl1.y)
|
||||||
curve.to.y)
|
|
||||||
}
|
|
||||||
Segment::None => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
min_y
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clip_y(&self, y: f32) -> ClippedSegments {
|
fn clip_y(&self, y: f32) -> ClippedSegments {
|
||||||
let (from, to) = self.endpoints();
|
if self.from.y < y && self.to.y < y {
|
||||||
if from.y < y && to.y < y {
|
|
||||||
return ClippedSegments { min: Some(*self), max: None }
|
return ClippedSegments { min: Some(*self), max: None }
|
||||||
}
|
}
|
||||||
if from.y > y && to.y > y {
|
if self.from.y > y && self.to.y > y {
|
||||||
return ClippedSegments { min: None, max: Some(*self) }
|
return ClippedSegments { min: None, max: Some(*self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
let (prev, next) = match *self {
|
let (prev, next) = if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
|
||||||
Segment::Line(ref line) => {
|
let curve = CubicBezierSegment {
|
||||||
let (prev, next) = line.split(line.solve_t_for_y(y));
|
from: self.from,
|
||||||
(Segment::Line(prev), Segment::Line(next))
|
ctrl1: self.ctrl0,
|
||||||
}
|
ctrl2: self.ctrl1,
|
||||||
Segment::Quadratic(ref curve) => {
|
to: self.to,
|
||||||
let (prev, next) = curve.split(curve.assume_monotonic().solve_t_for_y(y));
|
};
|
||||||
(Segment::Quadratic(prev), Segment::Quadratic(next))
|
|
||||||
}
|
|
||||||
Segment::Cubic(ref curve) => {
|
|
||||||
let swapped_curve = CubicBezierSegment {
|
let swapped_curve = CubicBezierSegment {
|
||||||
from: curve.from.yx(),
|
from: curve.from.yx(),
|
||||||
ctrl1: curve.ctrl1.yx(),
|
ctrl1: curve.ctrl1.yx(),
|
||||||
|
@ -552,12 +583,18 @@ impl Segment {
|
||||||
};
|
};
|
||||||
let (prev, next) = curve.split(
|
let (prev, next) = curve.split(
|
||||||
swapped_curve.assume_monotonic().solve_t_for_x(y, 0.0..1.0, TOLERANCE));
|
swapped_curve.assume_monotonic().solve_t_for_x(y, 0.0..1.0, TOLERANCE));
|
||||||
(Segment::Cubic(prev), Segment::Cubic(next))
|
(Segment::from_cubic(&prev), Segment::from_cubic(&next))
|
||||||
}
|
} else if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
|
||||||
Segment::None => unreachable!(),
|
let curve = QuadraticBezierSegment { from: self.from, ctrl: self.ctrl0, to: self.to };
|
||||||
|
let (prev, next) = curve.split(curve.assume_monotonic().solve_t_for_y(y));
|
||||||
|
(Segment::from_quadratic(&prev), Segment::from_quadratic(&next))
|
||||||
|
} else {
|
||||||
|
let line = LineSegment { from: self.from, to: self.to };
|
||||||
|
let (prev, next) = line.split(line.solve_t_for_y(y));
|
||||||
|
(Segment::from_line(&prev), Segment::from_line(&next))
|
||||||
};
|
};
|
||||||
|
|
||||||
if from.y <= to.y {
|
if self.from.y <= self.to.y {
|
||||||
return ClippedSegments { min: Some(prev), max: Some(next) };
|
return ClippedSegments { min: Some(prev), max: Some(next) };
|
||||||
} else {
|
} else {
|
||||||
return ClippedSegments { min: Some(next), max: Some(prev) };
|
return ClippedSegments { min: Some(next), max: Some(prev) };
|
||||||
|
@ -567,30 +604,23 @@ impl Segment {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate(&self, by: &Vector2D<f32>) -> Segment {
|
fn translate(&self, by: &Vector2D<f32>) -> Segment {
|
||||||
match *self {
|
let flags = self.flags;
|
||||||
Segment::Line(ref line) => {
|
let (from, to) = if flags.contains(SegmentFlags::HAS_ENDPOINTS) {
|
||||||
Segment::Line(LineSegment {
|
(self.from + *by, self.to + *by)
|
||||||
from: line.from + *by,
|
} else {
|
||||||
to: line.to + *by,
|
(Point2D::zero(), Point2D::zero())
|
||||||
})
|
};
|
||||||
}
|
let ctrl0 = if flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
|
||||||
Segment::Quadratic(ref curve) => {
|
self.ctrl0 + *by
|
||||||
Segment::Quadratic(QuadraticBezierSegment {
|
} else {
|
||||||
from: curve.from + *by,
|
Point2D::zero()
|
||||||
ctrl: curve.ctrl + *by,
|
};
|
||||||
to: curve.to + *by,
|
let ctrl1 = if flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
|
||||||
})
|
self.ctrl1 + *by
|
||||||
}
|
} else {
|
||||||
Segment::Cubic(ref curve) => {
|
Point2D::zero()
|
||||||
Segment::Cubic(CubicBezierSegment {
|
};
|
||||||
from: curve.from + *by,
|
Segment { from, ctrl0, ctrl1, to, flags }
|
||||||
ctrl1: curve.ctrl1 + *by,
|
|
||||||
ctrl2: curve.ctrl2 + *by,
|
|
||||||
to: curve.to + *by,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Segment::None => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,6 +629,14 @@ struct ClippedSegments {
|
||||||
max: Option<Segment>,
|
max: Option<Segment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
struct SegmentFlags: u8 {
|
||||||
|
const HAS_ENDPOINTS = 0x01;
|
||||||
|
const HAS_CONTROL_POINT_0 = 0x02;
|
||||||
|
const HAS_CONTROL_POINT_1 = 0x04;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tiling
|
// Tiling
|
||||||
|
|
||||||
const TILE_WIDTH: f32 = 4.0;
|
const TILE_WIDTH: f32 = 4.0;
|
||||||
|
@ -690,9 +728,8 @@ fn process_active_edge(active_edge: &mut Segment,
|
||||||
strip.push_segment(upper_segment);
|
strip.push_segment(upper_segment);
|
||||||
// FIXME(pcwalton): Assumes x-monotonicity!
|
// FIXME(pcwalton): Assumes x-monotonicity!
|
||||||
// FIXME(pcwalton): The min call below is a hack!
|
// FIXME(pcwalton): The min call below is a hack!
|
||||||
let (from, to) = upper_segment.endpoints();
|
let from_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.from.x));
|
||||||
let from_x = f32::max(0.0, f32::min(active_intervals.extent(), from.x));
|
let to_x = f32::max(0.0, f32::min(active_intervals.extent(), upper_segment.to.x));
|
||||||
let to_x = f32::max(0.0, f32::min(active_intervals.extent(), to.x));
|
|
||||||
if from_x < to_x {
|
if from_x < to_x {
|
||||||
active_intervals.add(IntervalRange::new(from_x, to_x, -1.0))
|
active_intervals.add(IntervalRange::new(from_x, to_x, -1.0))
|
||||||
} else {
|
} else {
|
||||||
|
@ -702,7 +739,7 @@ fn process_active_edge(active_edge: &mut Segment,
|
||||||
|
|
||||||
match clipped.max {
|
match clipped.max {
|
||||||
Some(lower_segment) => *active_edge = lower_segment,
|
Some(lower_segment) => *active_edge = lower_segment,
|
||||||
None => *active_edge = Segment::None,
|
None => *active_edge = Segment::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue