Don't include the closing segment when printing a path as a string.

Closes #279.
Closes #282.
This commit is contained in:
Patrick Walton 2020-03-27 15:40:27 -07:00
parent 6239356d89
commit b6762cecb8
7 changed files with 55 additions and 15 deletions

View File

@ -44,6 +44,9 @@ const DEFAULT_FONT_SIZE: f32 = 10.0;
#[cfg_attr(not(feature = "pf-text"), path = "text_no_text.rs")] #[cfg_attr(not(feature = "pf-text"), path = "text_no_text.rs")]
mod text; mod text;
#[cfg(test)]
mod tests;
pub struct CanvasRenderingContext2D { pub struct CanvasRenderingContext2D {
scene: Scene, scene: Scene,
current_state: State, current_state: State,

19
canvas/src/tests.rs Normal file
View File

@ -0,0 +1,19 @@
// pathfinder/canvas/src/tests.rs
//
// For this file only, any copyright is dedicated to the Public Domain.
// https://creativecommons.org/publicdomain/zero/1.0/
use pathfinder_geometry::vector::Vector2F;
use super::Path2D;
#[test]
pub fn test_path2d_formatting() {
let mut path = Path2D::new();
path.move_to(Vector2F::new(0.0, 1.0));
path.line_to(Vector2F::new(2.0, 3.0));
assert_eq!(format!("{:?}", path), "M 0 1 L 2 3");
path.line_to(Vector2F::new(4.0, 5.0));
assert_eq!(format!("{:?}", path), "M 0 1 L 2 3 L 4 5");
path.close_path();
assert_eq!(format!("{:?}", path), "M 0 1 L 2 3 L 4 5 z");
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use crate::outline::{Contour, PointFlags, PushSegmentFlags}; use crate::outline::{Contour, ContourIterFlags, PointFlags, PushSegmentFlags};
use crate::segment::{CubicSegment, Segment}; use crate::segment::{CubicSegment, Segment};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
@ -200,7 +200,7 @@ where
} }
let input = self.contour_mut().take(); let input = self.contour_mut().take();
for segment in input.iter() { for segment in input.iter(ContourIterFlags::empty()) {
self.clip_segment_against(segment, &edge); self.clip_segment_against(segment, &edge);
} }
if input.is_closed() { if input.is_closed() {
@ -266,7 +266,7 @@ where
fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult { fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult {
let mut result = None; let mut result = None;
for segment in self.contour_mut().iter() { for segment in self.contour_mut().iter(ContourIterFlags::empty()) {
let location = edge.trivially_test_segment(&segment); let location = edge.trivially_test_segment(&segment);
match (result, location) { match (result, location) {
(None, EdgeRelativeLocation::Outside) => { (None, EdgeRelativeLocation::Outside) => {

View File

@ -10,7 +10,7 @@
//! Line dashing support. //! Line dashing support.
use crate::outline::{Contour, Outline, PushSegmentFlags}; use crate::outline::{Contour, ContourIterFlags, Outline, PushSegmentFlags};
use std::mem; use std::mem;
const EPSILON: f32 = 0.0001; const EPSILON: f32 = 0.0001;
@ -54,7 +54,8 @@ impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> {
} }
fn dash(&mut self) { fn dash(&mut self) {
let (mut iterator, mut queued_segment) = (self.input.iter(), None); let mut iterator = self.input.iter(ContourIterFlags::empty());
let mut queued_segment = None;
loop { loop {
if queued_segment.is_none() { if queued_segment.is_none() {
match iterator.next() { match iterator.next() {

View File

@ -282,10 +282,11 @@ impl Contour {
} }
#[inline] #[inline]
pub fn iter(&self) -> ContourIter { pub fn iter(&self, flags: ContourIterFlags) -> ContourIter {
ContourIter { ContourIter {
contour: self, contour: self,
index: 1, index: 1,
flags,
} }
} }
@ -743,7 +744,8 @@ impl Contour {
impl Debug for Contour { impl Debug for Contour {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
for (segment_index, segment) in self.iter().enumerate() { for (segment_index, segment) in self.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT)
.enumerate() {
if segment_index == 0 { if segment_index == 0 {
write!( write!(
formatter, formatter,
@ -821,6 +823,7 @@ impl PointIndex {
pub struct ContourIter<'a> { pub struct ContourIter<'a> {
contour: &'a Contour, contour: &'a Contour,
index: u32, index: u32,
flags: ContourIterFlags,
} }
impl<'a> Iterator for ContourIter<'a> { impl<'a> Iterator for ContourIter<'a> {
@ -829,8 +832,11 @@ impl<'a> Iterator for ContourIter<'a> {
#[inline] #[inline]
fn next(&mut self) -> Option<Segment> { fn next(&mut self) -> Option<Segment> {
let contour = self.contour; let contour = self.contour;
if (self.index == contour.len() && !self.contour.closed) || self.index == contour.len() + 1
{ let include_close_segment = self.contour.closed &&
!self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT);
if (self.index == contour.len() && !include_close_segment) ||
self.index == contour.len() + 1 {
return None; return None;
} }
@ -873,6 +879,12 @@ pub enum ArcDirection {
CCW, CCW,
} }
bitflags! {
pub struct ContourIterFlags: u8 {
const IGNORE_CLOSE_SEGMENT = 1;
}
}
#[inline] #[inline]
pub(crate) fn union_rect(bounds: &mut RectF, new_point: Vector2F, first: bool) { pub(crate) fn union_rect(bounds: &mut RectF, new_point: Vector2F, first: bool) {
if first { if first {

View File

@ -10,7 +10,7 @@
//! Utilities for converting path strokes to fills. //! Utilities for converting path strokes to fills.
use crate::outline::{ArcDirection, Contour, Outline, PushSegmentFlags}; use crate::outline::{ArcDirection, Contour, ContourIterFlags, Outline, PushSegmentFlags};
use crate::segment::Segment; use crate::segment::Segment;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
@ -161,7 +161,7 @@ impl<'a> ContourStrokeToFill<'a> {
} }
fn offset_forward(&mut self) { fn offset_forward(&mut self) {
for (segment_index, segment) in self.input.iter().enumerate() { for (segment_index, segment) in self.input.iter(ContourIterFlags::empty()).enumerate() {
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn // FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
// clockwise. Of course, we should just implement anticlockwise arcs to begin with... // clockwise. Of course, we should just implement anticlockwise arcs to begin with...
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join }; let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
@ -172,7 +172,7 @@ impl<'a> ContourStrokeToFill<'a> {
fn offset_backward(&mut self) { fn offset_backward(&mut self) {
let mut segments: Vec<_> = self let mut segments: Vec<_> = self
.input .input
.iter() .iter(ContourIterFlags::empty())
.map(|segment| segment.reversed()) .map(|segment| segment.reversed())
.collect(); .collect();
segments.reverse(); segments.reverse();

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use pathfinder_content::outline::ContourIterFlags;
use pathfinder_content::segment::SegmentKind; use pathfinder_content::segment::SegmentKind;
use pathfinder_renderer::paint::Paint; use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::Scene; use pathfinder_renderer::scene::Scene;
@ -87,7 +88,7 @@ fn export_pdf<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
} }
for contour in outline.contours() { for contour in outline.contours() {
for (segment_index, segment) in contour.iter().enumerate() { for (segment_index, segment) in contour.iter(ContourIterFlags::empty()).enumerate() {
if segment_index == 0 { if segment_index == 0 {
pdf.move_to(tr(segment.baseline.from())); pdf.move_to(tr(segment.baseline.from()));
} }
@ -103,7 +104,11 @@ fn export_pdf<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
let c2 = Vector2F::splat(2./3.) * c + Vector2F::splat(1./3.) * p; let c2 = Vector2F::splat(2./3.) * c + Vector2F::splat(1./3.) * p;
pdf.cubic_to(c1, c2, p); pdf.cubic_to(c1, c2, p);
} }
SegmentKind::Cubic => pdf.cubic_to(tr(segment.ctrl.from()), tr(segment.ctrl.to()), tr(segment.baseline.to())) SegmentKind::Cubic => {
pdf.cubic_to(tr(segment.ctrl.from()),
tr(segment.ctrl.to()),
tr(segment.baseline.to()))
}
} }
} }
@ -147,7 +152,7 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
} }
for contour in outline.contours() { for contour in outline.contours() {
for (segment_index, segment) in contour.iter().enumerate() { for (segment_index, segment) in contour.iter(ContourIterFlags::empty()).enumerate() {
if segment_index == 0 { if segment_index == 0 {
writeln!(writer, "{} moveto", P(segment.baseline.from()))?; writeln!(writer, "{} moveto", P(segment.baseline.from()))?;
} }