mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 15:08:12 +08:00
Migrate vector data and tools to use nodes (#1065)
* Add rendering to vector nodes * Add line, shape, rectange and freehand tool * Fix transforms, strokes and fills * Migrate spline tool * Remove blank lines * Fix test * Fix fill in properties * Select layers when filling * Properties panel transform around pivot * Fix select tool outlines * Select tool modifies node graph pivot * Add the pivot assist to the properties * Improve setting non existant fill UX * Cleanup hash function * Path and pen tools * Bug fixes * Disable boolean ops * Fix default handle smoothing on ellipses * Fix test and warnings --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
639a24d8ad
commit
959e790cdf
@@ -123,6 +123,20 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
let _ = write!(svg, r#"<path d="{} {}" {attributes}/>"#, curve_start_argument, curve_arguments.join(" "));
|
||||
}
|
||||
|
||||
/// Write the curve argument to the string (the d="..." part)
|
||||
pub fn subpath_to_svg(&self, svg: &mut String, transform: glam::DAffine2) -> std::fmt::Result {
|
||||
let start = transform.transform_point2(self[0].anchor);
|
||||
write!(svg, "{SVG_ARG_MOVE}{},{}", start.x, start.y)?;
|
||||
for bezier in self.iter() {
|
||||
bezier.apply_transformation(|pos| transform.transform_point2(pos)).write_curve_argument(svg)?;
|
||||
svg.push(' ');
|
||||
}
|
||||
if self.closed {
|
||||
svg.push_str(SVG_ARG_CLOSED);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Appends to the `svg` mutable string with an SVG shape representation of the handle lines.
|
||||
pub fn handle_lines_to_svg(&self, svg: &mut String, attributes: String) {
|
||||
let handle_lines: Vec<String> = self.iter().filter_map(|bezier| bezier.svg_handle_line_argument()).collect();
|
||||
@@ -178,7 +192,7 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
Self::from_anchors([corner1, DVec2::new(corner2.x, corner1.y), corner2, DVec2::new(corner1.x, corner2.y)], true)
|
||||
}
|
||||
|
||||
/// Constructs an elipse with `corner1` and `corner2` as the two corners of the bounding box.
|
||||
/// Constructs an ellipse with `corner1` and `corner2` as the two corners of the bounding box.
|
||||
pub fn new_ellipse(corner1: DVec2, corner2: DVec2) -> Self {
|
||||
let size = (corner1 - corner2).abs();
|
||||
let center = (corner1 + corner2) / 2.;
|
||||
@@ -192,10 +206,10 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
let handle_offset = size * HANDLE_OFFSET_FACTOR * 0.5;
|
||||
|
||||
let manipulator_groups = vec![
|
||||
ManipulatorGroup::new(top, Some(top + handle_offset * DVec2::X), Some(top - handle_offset * DVec2::X)),
|
||||
ManipulatorGroup::new(right, Some(right + handle_offset * DVec2::Y), Some(right - handle_offset * DVec2::Y)),
|
||||
ManipulatorGroup::new(bottom, Some(bottom - handle_offset * DVec2::X), Some(bottom + handle_offset * DVec2::X)),
|
||||
ManipulatorGroup::new(left, Some(left - handle_offset * DVec2::Y), Some(left + handle_offset * DVec2::Y)),
|
||||
ManipulatorGroup::new(top, Some(top - handle_offset * DVec2::X), Some(top + handle_offset * DVec2::X)),
|
||||
ManipulatorGroup::new(right, Some(right - handle_offset * DVec2::Y), Some(right + handle_offset * DVec2::Y)),
|
||||
ManipulatorGroup::new(bottom, Some(bottom + handle_offset * DVec2::X), Some(bottom - handle_offset * DVec2::X)),
|
||||
ManipulatorGroup::new(left, Some(left + handle_offset * DVec2::Y), Some(left - handle_offset * DVec2::Y)),
|
||||
];
|
||||
Self::new(manipulator_groups, true)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,36 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
self.closed
|
||||
}
|
||||
|
||||
/// Set if the subpath is closed.
|
||||
pub fn set_closed(&mut self, new_closed: bool) {
|
||||
self.closed = new_closed;
|
||||
}
|
||||
|
||||
/// Access a [ManipulatorGroup] from a [ManipulatorGroupId].
|
||||
pub fn manipulator_from_id(&self, id: ManipulatorGroupId) -> Option<&ManipulatorGroup<ManipulatorGroupId>> {
|
||||
self.manipulator_groups.iter().find(|manipulator_group| manipulator_group.id == id)
|
||||
}
|
||||
|
||||
/// Access a mutable [ManipulatorGroup] from a [ManipulatorGroupId].
|
||||
pub fn manipulator_mut_from_id(&mut self, id: ManipulatorGroupId) -> Option<&mut ManipulatorGroup<ManipulatorGroupId>> {
|
||||
self.manipulator_groups.iter_mut().find(|manipulator_group| manipulator_group.id == id)
|
||||
}
|
||||
|
||||
/// Access the index of a [ManipulatorGroup] from a [ManipulatorGroupId].
|
||||
pub fn manipulator_index_from_id(&self, id: ManipulatorGroupId) -> Option<usize> {
|
||||
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id)
|
||||
}
|
||||
|
||||
/// Insert a manipulator group at an index
|
||||
pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup<ManipulatorGroupId>) {
|
||||
self.manipulator_groups.insert(index, group)
|
||||
}
|
||||
|
||||
/// Remove a manipulator group at an index
|
||||
pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup<ManipulatorGroupId> {
|
||||
self.manipulator_groups.remove(index)
|
||||
}
|
||||
|
||||
/// Inserts a `ManipulatorGroup` at a certain point along the subpath based on the parametric `t`-value provided.
|
||||
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
||||
pub fn insert(&mut self, t: SubpathTValue) {
|
||||
|
||||
@@ -103,6 +103,13 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
self.iter().map(|bezier| bezier.bounding_box()).reduce(|bbox1, bbox2| [bbox1[0].min(bbox2[0]), bbox1[1].max(bbox2[1])])
|
||||
}
|
||||
|
||||
/// Return the min and max corners that represent the bounding box of the subpath, after a given affine transform.
|
||||
pub fn bounding_box_with_transform(&self, transform: glam::DAffine2) -> Option<[DVec2; 2]> {
|
||||
self.iter()
|
||||
.map(|bezier| bezier.apply_transformation(&|v| transform.transform_point2(v)).bounding_box())
|
||||
.reduce(|bbox1, bbox2| [bbox1[0].min(bbox2[0]), bbox1[1].max(bbox2[1])])
|
||||
}
|
||||
|
||||
/// Returns list of `t`-values representing the inflection points of the subpath.
|
||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||
/// <iframe frameBorder="0" width="100%" height="400px" src="https://graphite.rs/bezier-rs-demos#subpath/inflections/solo" title="Inflections Demo"></iframe>
|
||||
@@ -123,6 +130,11 @@ impl<ManipulatorGroupId: crate::Identifier> Subpath<ManipulatorGroupId> {
|
||||
// TODO: Consider the shared point between adjacent beziers.
|
||||
inflection_t_values
|
||||
}
|
||||
|
||||
/// Does a path contain a point? Based on the non zero winding
|
||||
pub fn contains_point(&self, target_point: DVec2) -> bool {
|
||||
self.iter().map(|bezier| bezier.winding(target_point)).sum::<i32>() != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user