pub struct Path(pub Vec<PathCommand>);Expand description
Representation of an SVG
path, where every path
command is L (line to) or M
(move to).
A Path is essentially a series of PathCommands, and is implemented as a wrapper for
Vec<PathCommand>. That said, the first PathCommand should be of the
MoveTo variant in order to produce valid SVG.
§Examples
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::LineTo(Point::new(2.0, 4.0))
]);
assert!(pth.0.get(0).unwrap().almost_eq(&PathCommand::MoveTo(Point::new(1.0, 2.0)), 0.001));
assert!(pth.0.get(1).unwrap().almost_eq(&PathCommand::LineTo(Point::new(5.0, 3.0)), 0.001));
assert!(pth.0.get(2).unwrap().almost_eq(&PathCommand::LineTo(Point::new(2.0, 4.0)), 0.001));From<Vec<Point>> is implemented for Path; it converts the vector into a Path where the first
path command is a MoveTo and the rest are LineTo.
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::LineTo(Point::new(2.0, 4.0))
]);
let pth2: Path = vec![
Point::new(1.0, 2.0),
Point::new(5.0, 3.0),
Point::new(2.0, 4.0)
].into();
assert!(pth.almost_eq(&pth2, 0.001));Adding a Point to a Path will add the underlying points. Likewise
subtracting a Point from a Path subtracts the underlying points.
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::MoveTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(1.5, 3.5))
]);
let pth2 = pth.clone() + Point::new(1.0, -1.0);
let pth2_a = Path(vec![
PathCommand::MoveTo(Point::new(2.0, 1.0)),
PathCommand::LineTo(Point::new(6.0, 2.0)),
PathCommand::MoveTo(Point::new(3.0, 3.0)),
PathCommand::LineTo(Point::new(2.5, 2.5))
]);
assert!(pth2.almost_eq(&pth2_a, 0.001));
let pth3 = pth.clone() - Point::new(0.0, 0.5);
let pth3_a = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 1.5)),
PathCommand::LineTo(Point::new(5.0, 2.5)),
PathCommand::MoveTo(Point::new(2.0, 3.5)),
PathCommand::LineTo(Point::new(1.5, 3.0))
]);
assert!(pth3.almost_eq(&pth3_a, 0.001));
let pth4 = Point::new(5.0, 4.0) + pth;
let pth4_a = Path(vec![
PathCommand::MoveTo(Point::new(6.0, 6.0)),
PathCommand::LineTo(Point::new(10.0, 7.0)),
PathCommand::MoveTo(Point::new(7.0, 8.0)),
PathCommand::LineTo(Point::new(6.5, 7.5))
]);
assert!(pth4.almost_eq(&pth4_a, 0.001));Likewise, multiplying a Path by a f64 factor will multiply the
coordinates of the underlying Points.
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::MoveTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(1.5, 3.5))
]);
let pth2 = 2.0 * pth.clone();
let pth2_a = Path(vec![
PathCommand::MoveTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(10.0, 6.0)),
PathCommand::MoveTo(Point::new(4.0, 8.0)),
PathCommand::LineTo(Point::new(3.0, 7.0))
]);
assert!(pth2.almost_eq(&pth2_a, 0.001));Tuple Fields§
§0: Vec<PathCommand>Implementations§
Source§impl Path
impl Path
Sourcepub fn is_valid(&self) -> bool
pub fn is_valid(&self) -> bool
Returns true if all of the underlying Points are valid (have no
coordinates that are NaN or ±∞).
§Examples
use l_system_fractals::paths::{Path, Point};
let pth1 = Path::from(vec![
Point::new(3.0, 2.0),
Point::new(1.0, 1.0),
Point::new(4.0, 0.0)
]);
let pth2 = Path::from(vec![
Point::new(f64::NAN, 2.0),
Point::new(1.0, 1.0),
Point::new(4.0, 0.0)
]);
let pth3 = Path::from(vec![
Point::new(f64::INFINITY, 2.0),
Point::new(1.0, 1.0),
Point::new(4.0, 0.0)
]);
assert!(pth1.is_valid());
assert!(!pth2.is_valid());
assert!(!pth3.is_valid());Sourcepub fn err_if_invalid(self) -> Result<Self, LSystemError>
pub fn err_if_invalid(self) -> Result<Self, LSystemError>
Raises an error if any of the underlying Points are invalid (either
coordinate is NaN or ±∞).
The error raised is LSystemError::InvalidFloat.
§Examples
use l_system_fractals::paths::{Path, Point};
let pth1 = Path::from(vec![
Point::new(3.0, 2.0),
Point::new(1.0, 1.0),
Point::new(4.0, 0.0)
]);
let pth2 = Path::from(vec![
Point::new(f64::NAN, 2.0),
Point::new(1.0, 1.0),
Point::new(4.0, 0.0)
]);
let pth3 = Path::from(vec![
Point::new(f64::INFINITY, 2.0),
Point::new(1.0, 1.0),
Point::new(4.0, 0.0)
]);
assert!(pth1.err_if_invalid().is_ok());
assert!(pth2.err_if_invalid().is_err());
assert!(pth3.err_if_invalid().is_err());Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true if the path contains no points.
§Examples
use l_system_fractals::paths::{Path, Point};
let pts1: Vec<Point> = Vec::new();
let pth1: Path = pts1.into();
assert!(pth1.is_empty());
let pts2: Vec<Point> = vec![Point::new(1.0, 1.0)];
let pth2: Path = pts2.into();
assert!(!pth2.is_empty());Sourcepub fn bounding_box(&self) -> Result<BoundingBox, LSystemError>
pub fn bounding_box(&self) -> Result<BoundingBox, LSystemError>
Returns the smallest BoundingBox that contains all the valid Points in the Path.
§Examples
use std::f64::consts::PI;
use l_system_fractals::paths::{BoundingBox, Path, Point};
use l_system_fractals::num_validity::AlmostEq;
let pts: Vec<Point> = (0..15_839)
.map(|x| (x as f64) * PI / 7919.0 + PI / 4.0)
.map(
|x| Point::new(5.0 * x.cos() + 6.0, 5.0 * x.sin() + 6.0)
).collect();
let pth: Path = pts.into();
let bb1: BoundingBox = pth.bounding_box().unwrap();
let bb2 = BoundingBox {
upper_left: Point::new(1.0, 1.0),
lower_right: Point::new(11.0, 11.0)
};
assert!(bb1.almost_eq(&bb2, 0.00001));An LSystemError is returned if the Path is empty (or all points are invalid).
use l_system_fractals::paths::{Path, Point};
let pts1: Vec<Point> = Vec::new();
let pth1: Path = pts1.into();
assert!(pth1.bounding_box().is_err());Sourcepub fn rescale_horiz(&self, factor: f64) -> Self
pub fn rescale_horiz(&self, factor: f64) -> Self
Returns a new Path where all the underlying points have been rescaled horizontally by the
given factor.
§Example
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::MoveTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(1.5, 3.5))
]);
let pth2 = pth.rescale_horiz(2.0);
let pth2_a = Path(vec![
PathCommand::MoveTo(Point::new(2.0, 2.0)),
PathCommand::LineTo(Point::new(10.0, 3.0)),
PathCommand::MoveTo(Point::new(4.0, 4.0)),
PathCommand::LineTo(Point::new(3.0, 3.5))
]);
assert!(pth2.almost_eq(&pth2_a, 0.001));Sourcepub fn rescale_vert(&self, factor: f64) -> Self
pub fn rescale_vert(&self, factor: f64) -> Self
Returns a new Path where all the underlying points have been rescaled vertically by the
given factor.
§Example
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::MoveTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(1.5, 3.5))
]);
let pth2 = pth.rescale_vert(2.0);
let pth2_a = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 4.0)),
PathCommand::LineTo(Point::new(5.0, 6.0)),
PathCommand::MoveTo(Point::new(2.0, 8.0)),
PathCommand::LineTo(Point::new(1.5, 7.0))
]);
assert!(pth2.almost_eq(&pth2_a, 0.001));Sourcepub fn rescale(
&self,
max_width: f64,
max_height: f64,
border: f64,
) -> Result<(Self, BoundingBox), LSystemError>
pub fn rescale( &self, max_width: f64, max_height: f64, border: f64, ) -> Result<(Self, BoundingBox), LSystemError>
Rescales the Path so that, including a border of the specified size around the path, its
width and height are below the specified maximums.
Outputs a rescaled Path and a BoundingBox for the path and border.
Sourcepub fn svg_path_command_output(
&self,
offset: Point,
) -> Result<String, LSystemError>
pub fn svg_path_command_output( &self, offset: Point, ) -> Result<String, LSystemError>
Returns the SVG path commands for the Path.
The Path is first moved by the given offset.
If the Path is invalid, an LSystemError::InvalidFloat is returned.
§Example
use l_system_fractals::paths::{Path, PathCommand, Point};
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::MoveTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(1.5, 3.5))
]);
assert_eq!(
pth.svg_path_command_output(Point::new(0.0, 0.0)).unwrap(),
"M 1.00000,2.00000 L 5.00000,3.00000 M 2.00000,4.00000 L 1.50000,3.50000 ".to_string()
);
assert_eq!(
pth.svg_path_command_output(Point::new(1.0, 1.0)).unwrap(),
"M 2.00000,3.00000 L 6.00000,4.00000 M 3.00000,5.00000 L 2.50000,4.50000 ".to_string()
);Sourcepub fn svg_path_output(
&self,
offset: Point,
fill: &str,
stroke: &str,
stroke_width: f64,
) -> Result<String, LSystemError>
pub fn svg_path_output( &self, offset: Point, fill: &str, stroke: &str, stroke_width: f64, ) -> Result<String, LSystemError>
Returns the full SVG path element
syntax for the Path and
the specified offset and stroke, fill, and stroke-width attributes.
If the Path is invalid, an LSystemError::InvalidFloat is returned.
§Example
use l_system_fractals::paths::{Path, PathCommand, Point};
let pth = Path(vec![
PathCommand::MoveTo(Point::new(1.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 3.0)),
PathCommand::MoveTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(1.5, 3.5))
]);
let svg_text1 = pth.svg_path_output(
Point::new(0.0, 0.0),
"none",
"black",
0.25
).unwrap();
let svg_text2 = (
"<path fill=\"none\" stroke=\"black\" stroke-width=\"0.25000\" d=\"".to_string() +
"M 1.00000,2.00000 L 5.00000,3.00000 M 2.00000,4.00000 L 1.50000,3.50000 " +
"\" />"
);
assert_eq!(svg_text1, svg_text2);Sourcepub fn svg_output(
&self,
params: &PlotParameters,
) -> Result<String, LSystemError>
pub fn svg_output( &self, params: &PlotParameters, ) -> Result<String, LSystemError>
Produces the complete text of an SVG file containing the Path, rescaled to fit within
the specified maximum width and height.
Sourcepub fn concatenate(&self, other: &Self, connect: bool) -> Self
pub fn concatenate(&self, other: &Self, connect: bool) -> Self
Joins two paths.
If connect is true, then there will be a LineTo between the
last point of self and the first point of other in the returned Path. Otherwise,
there will be a MoveTo between these points.
§Examples
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth1: Path = Path(
vec![
PathCommand::MoveTo(Point::new(4.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 4.0)),
PathCommand::LineTo(Point::new(4.0, 1.0)),
]
).into();
let pth2: Path = Path(
vec![
PathCommand::MoveTo(Point::new(5.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 5.0)),
PathCommand::LineTo(Point::new(5.0, 2.0)),
]
).into();
let pth3: Path = Path(
vec![
PathCommand::MoveTo(Point::new(4.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 4.0)),
PathCommand::LineTo(Point::new(4.0, 1.0)),
// not connected: two components
PathCommand::MoveTo(Point::new(5.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 5.0)),
PathCommand::LineTo(Point::new(5.0, 2.0)),
]
).into();
let pth4: Path = Path(
vec![
PathCommand::MoveTo(Point::new(4.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 4.0)),
PathCommand::LineTo(Point::new(4.0, 1.0)),
// connected; one component
PathCommand::LineTo(Point::new(5.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 5.0)),
PathCommand::LineTo(Point::new(5.0, 2.0)),
]
).into();
assert!(pth1.concatenate(&pth2, false).almost_eq(&pth3, 0.001));
assert!(pth1.concatenate(&pth2, true).almost_eq(&pth4, 0.001));Sourcepub fn concatenate_matched_endpoints(&self, other: &Self) -> Self
pub fn concatenate_matched_endpoints(&self, other: &Self) -> Self
Joins two paths, with the second path translated so that its first point is the last point of the first path.
§Examples
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth1: Path = Path(
vec![
PathCommand::MoveTo(Point::new(4.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 4.0)),
PathCommand::LineTo(Point::new(4.0, 1.0)),
]
).into();
let pth2: Path = Path(
vec![
PathCommand::MoveTo(Point::new(5.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 5.0)),
PathCommand::LineTo(Point::new(5.0, 2.0)),
]
).into();
let pth3: Path = Path(
vec![
PathCommand::MoveTo(Point::new(4.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 4.0)),
PathCommand::LineTo(Point::new(4.0, 1.0)),
PathCommand::LineTo(Point::new(7.0, 1.0)),
PathCommand::LineTo(Point::new(7.0, 4.0)),
PathCommand::LineTo(Point::new(4.0, 1.0)),
]
).into();
assert!(
pth1
.concatenate_matched_endpoints(&pth2)
.almost_eq(&pth3, 0.001)
);Sourcepub fn rotate_about_origin(&self, angle: f64) -> Self
pub fn rotate_about_origin(&self, angle: f64) -> Self
Rotate the path about the origin by the specified angle.
§Example
use std::f64::consts::PI;
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth1: Path = Path(
vec![
PathCommand::MoveTo(Point::new(4.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 1.0)),
PathCommand::LineTo(Point::new(1.0, 4.0)),
PathCommand::LineTo(Point::new(4.0, 1.0)),
]
).into();
let pth2: Path = Path(
vec![
PathCommand::MoveTo(Point::from_polar(
17.0_f64.sqrt(),
1.0_f64.atan2(4.0) + PI / 4.0
)),
PathCommand::LineTo(Point::new(0.0, 2.0_f64.sqrt())),
PathCommand::LineTo(Point::from_polar(
17.0_f64.sqrt(),
4.0_f64.atan2(1.0) + PI / 4.0
)),
PathCommand::LineTo(Point::from_polar(
17.0_f64.sqrt(),
1.0_f64.atan2(4.0) + PI / 4.0
))
]
).into();
assert!(pth1.rotate_about_origin(PI / 4.0).almost_eq(&pth2, 0.001));Sourcepub fn rotate_about_first_point(&self, angle: f64) -> Self
pub fn rotate_about_first_point(&self, angle: f64) -> Self
Rotate the path about its first point by the specified angle.
§Example
use std::f64::consts::PI;
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth1: Path = Path(
vec![
PathCommand::MoveTo(Point::new(5.0, 4.0)),
PathCommand::LineTo(Point::new(2.0, 4.0)),
PathCommand::LineTo(Point::new(2.0, 7.0)),
PathCommand::LineTo(Point::new(5.0, 4.0)),
]
).into();
let pth2: Path = Path(
vec![
PathCommand::MoveTo(Point::new(5.0, 4.0)),
PathCommand::LineTo(Point::new(
5.0 - 3.0 / 2.0_f64.sqrt(),
4.0 - 3.0 / 2.0_f64.sqrt()
)),
PathCommand::LineTo(Point::new(
5.0 - 3.0 * 2.0_f64.sqrt(),
4.0
)),
PathCommand::LineTo(Point::new(5.0, 4.0))
]
).into();
assert!(pth1.rotate_about_first_point(PI / 4.0).almost_eq(&pth2, 0.001));Sourcepub fn rotate(&self, axis: &Point, angle: f64) -> Self
pub fn rotate(&self, axis: &Point, angle: f64) -> Self
Rotate the path about the specified point by the specified angle.
§Example
use std::f64::consts::PI;
use l_system_fractals::paths::{Path, PathCommand, Point};
use l_system_fractals::num_validity::AlmostEq;
let pth1: Path = Path(
vec![
PathCommand::MoveTo(Point::new(5.0, 8.0)),
PathCommand::LineTo(Point::new(2.0, 2.0)),
PathCommand::LineTo(Point::new(8.0, 2.0)),
PathCommand::LineTo(Point::new(5.0, 8.0)),
]
).into();
let axis = Point::new(5.0, 5.0);
let pth2: Path = Path(
vec![
PathCommand::MoveTo(Point::new(
5.0 - 1.5 * 3.0_f64.sqrt(),
6.5
)),
PathCommand::LineTo(Point::new(
5.0 - 3.0 * 2.0_f64.sqrt() * (7.0 * PI / 12.0).cos(),
5.0 - 3.0 * 2.0_f64.sqrt() * (7.0 * PI / 12.0).sin()
)),
PathCommand::LineTo(Point::new(
5.0 + 3.0 * 2.0_f64.sqrt() * (PI / 12.0).cos(),
5.0 + 3.0 * 2.0_f64.sqrt() * (PI / 12.0).sin()
)),
PathCommand::LineTo(Point::new(
5.0 - 1.5 * 3.0_f64.sqrt(),
6.5
))
]
).into();
assert!(pth1.rotate(&axis, PI / 3.0).almost_eq(&pth2, 0.001));