Skip to content

Commit

Permalink
Document path, and Context::close_to due to use in example
Browse files Browse the repository at this point in the history
  • Loading branch information
ElectrifyPro committed Dec 15, 2024
1 parent a0b248d commit a3a0040
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 0 deletions.
64 changes: 64 additions & 0 deletions cairo/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,70 @@ impl Context {
unsafe { ffi::cairo_new_sub_path(self.0.as_ptr()) }
}

/// Adds a line segment from the current point to the beginning of the current sub-path (the
/// most recent point passed to [`Context::move_to`]) to the path, and closes this sub-path.
/// After this call, the current point will be at the **joined endpoint of the sub-path**.
///
/// The behavior of [`Context::close_path`] is distinct from simply calling
/// [`Context::line_to`] with the equivalent coordinate in the case of stroking. When a closed
/// sub-path is stroked, there are no caps on the ends of the sub-path. Instead, there is a
/// line join connecting the final and initial segments of the sub-path.
///
/// If there is no current point before the call to [`Context::close_path`], this function will
/// have no effect.
///
/// **Note**: Calls to [`Context::close_path`] now (as of `cairo 1.2.4`) place an explicit
/// [`MoveTo`] element into the path immediately after the [`ClosePath`] element. This can
/// simplify path processing in some cases. See below for an example of this case.
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), cairo::Error> {
/// use cairo::{Context, Format, ImageSurface};
///
/// let surface = ImageSurface::create(Format::ARgb32, 100, 100).unwrap();
/// let ctx = Context::new(&surface).unwrap();
///
/// // paint the background black
/// ctx.paint()?;
///
/// // draw a 5-pointed star with a white fill, in similar manner to how a human would
/// let angle = std::f64::consts::TAU / 2.5;
/// ctx.set_source_rgb(1.0, 1.0, 1.0);
/// for i in 0..5 {
/// let x = 50.0 + 40.0 * (i as f64 * angle).cos();
/// let y = 50.0 + 40.0 * (i as f64 * angle).sin();
/// if i == 0 {
/// ctx.move_to(x, y);
/// } else {
/// ctx.line_to(x, y);
/// }
/// }
/// ctx.close_path();
///
/// // print the path details before filling (will clear the context path)
/// let path = ctx.copy_path()?;
/// for element in path.iter() {
/// println!("{:?}", element);
/// }
///
/// ctx.fill()?;
///
/// // output:
/// // MoveTo((90.0, 50.0))
/// // LineTo((17.640625, 73.51171875))
/// // LineTo((62.359375, 11.95703125))
/// // LineTo((62.359375, 88.04296875))
/// // LineTo((17.640625, 26.48828125))
/// // ClosePath
/// // MoveTo((90.0, 50.0))
/// # Ok(())
/// # }
/// ```
///
/// [`MoveTo`]: crate::PathSegment::MoveTo
/// [`ClosePath`]: crate::PathSegment::ClosePath
#[doc(alias = "cairo_close_path")]
pub fn close_path(&self) {
unsafe { ffi::cairo_close_path(self.0.as_ptr()) }
Expand Down
77 changes: 77 additions & 0 deletions cairo/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,65 @@ use std::{iter::FusedIterator, ptr};

use crate::{ffi, PathDataType};

/// A path of segments, representing a series of moveto, lineto, curveto, and closepath operations
/// that define a path to draw.
///
/// A [`Path`] can be thought of as a list of commands that define how to draw a particular shape,
/// akin to drawing a shape with pen and paper. Each command is represented by a [`PathSegment`],
/// which define a specific operation to perform with the pen.
///
/// Paths are created by calling [`Context::copy_path`] and [`Context::copy_path_flat`], which
/// returns a copy of the current path that the context is using. This allows you to inspect the
/// path that has been drawn so far.
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), cairo::Error> {
/// use cairo::{Context, Format, ImageSurface};
///
/// let surface = ImageSurface::create(Format::ARgb32, 100, 100).unwrap();
/// let ctx = Context::new(&surface).unwrap();
///
/// // paint the background black
/// ctx.paint()?;
///
/// // draw a 5-pointed star with a white fill, in similar manner to how a human would
/// let angle = std::f64::consts::TAU / 2.5;
/// ctx.set_source_rgb(1.0, 1.0, 1.0);
/// for i in 0..5 {
/// let x = 50.0 + 40.0 * (i as f64 * angle).cos();
/// let y = 50.0 + 40.0 * (i as f64 * angle).sin();
/// if i == 0 {
/// ctx.move_to(x, y);
/// } else {
/// ctx.line_to(x, y);
/// }
/// }
/// ctx.close_path();
///
/// // print the path details before filling (will clear the context path)
/// let path = ctx.copy_path()?;
/// for element in path.iter() {
/// println!("{:?}", element);
/// }
///
/// ctx.fill()?;
///
/// // output:
/// // MoveTo((90.0, 50.0))
/// // LineTo((17.640625, 73.51171875))
/// // LineTo((62.359375, 11.95703125))
/// // LineTo((62.359375, 88.04296875))
/// // LineTo((17.640625, 26.48828125))
/// // ClosePath
/// // MoveTo((90.0, 50.0))
/// # Ok(())
/// # }
/// ```
///
/// [`Context::copy_path`]: crate::Context::copy_path
/// [`Context::copy_path_flat`]: crate::Context::copy_path_flat
#[derive(Debug)]
#[doc(alias = "cairo_path_t")]
pub struct Path(ptr::NonNull<ffi::cairo_path_t>);
Expand All @@ -20,6 +79,7 @@ impl Path {
Path(ptr::NonNull::new_unchecked(pointer))
}

/// Returns an iterator over the segments of the path.
pub fn iter(&self) -> PathSegments {
use std::slice;

Expand Down Expand Up @@ -51,14 +111,31 @@ impl Drop for Path {
}
}

/// A segment of a path, representing a single moveto, lineto, curveto, or closepath operation.
///
/// See the documentation for [`Path`] for more information.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PathSegment {
/// Lift up the "pen" and move it to the given point, starting a new subpath.
MoveTo((f64, f64)),

/// Draw a straight line from the current point to the given point.
LineTo((f64, f64)),

/// Draw a cubic Bezier curve from the current point to the given point, using the two control
/// points to define the curve.
///
/// The first and second points are the control points, and the third point is the end point.
CurveTo((f64, f64), (f64, f64), (f64, f64)),

/// Draw a straight line from the current point to the starting point of the subpath, closing
/// it.
ClosePath,
}

/// An iterator over the segments of a [`Path`].
///
/// This struct is created by the [`Path::iter`] method.
pub struct PathSegments<'a> {
data: &'a [ffi::cairo_path_data],
i: usize,
Expand Down

0 comments on commit a3a0040

Please sign in to comment.