mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
Rename the Group type to Graphic everywhere (#3009)
This commit is contained in:
@@ -14,7 +14,7 @@ use std::hash::Hash;
|
||||
/// Some [`ArtboardData`] with some optional clipping bounds that can be exported.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Artboard {
|
||||
pub group: Table<Graphic>,
|
||||
pub content: Table<Graphic>,
|
||||
pub label: String,
|
||||
pub location: IVec2,
|
||||
pub dimensions: IVec2,
|
||||
@@ -31,7 +31,7 @@ impl Default for Artboard {
|
||||
impl Artboard {
|
||||
pub fn new(location: IVec2, dimensions: IVec2) -> Self {
|
||||
Self {
|
||||
group: Table::new(),
|
||||
content: Table::new(),
|
||||
label: "Artboard".to_string(),
|
||||
location: location.min(location + dimensions),
|
||||
dimensions: dimensions.abs(),
|
||||
@@ -47,7 +47,7 @@ impl BoundingBox for Artboard {
|
||||
if self.clip {
|
||||
Some(artboard_bounds)
|
||||
} else {
|
||||
[self.group.bounding_box(transform, include_stroke), Some(artboard_bounds)]
|
||||
[self.content.bounding_box(transform, include_stroke), Some(artboard_bounds)]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.reduce(Quad::combine_bounds)
|
||||
@@ -56,7 +56,7 @@ impl BoundingBox for Artboard {
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Artboard>, D::Error> {
|
||||
pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Artboard>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Default, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
@@ -84,7 +84,7 @@ pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
||||
}
|
||||
table
|
||||
}
|
||||
EitherFormat::ArtboardTable(artboard_group_table) => artboard_group_table,
|
||||
EitherFormat::ArtboardTable(artboard_table) => artboard_table,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ async fn create_artboard<T: Into<Table<Graphic>> + 'n>(
|
||||
footprint.translate(location.as_dvec2());
|
||||
new_ctx = new_ctx.with_footprint(footprint);
|
||||
}
|
||||
let group = content.eval(new_ctx.into_context()).await.into();
|
||||
let content = content.eval(new_ctx.into_context()).await.into();
|
||||
|
||||
let dimensions = dimensions.as_ivec2().max(IVec2::ONE);
|
||||
|
||||
@@ -128,7 +128,7 @@ async fn create_artboard<T: Into<Table<Graphic>> + 'n>(
|
||||
let dimensions = dimensions.abs();
|
||||
|
||||
Table::new_from_element(Artboard {
|
||||
group,
|
||||
content,
|
||||
label,
|
||||
location,
|
||||
dimensions,
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::hash::Hash;
|
||||
/// The possible forms of graphical content that can be rendered by the Render node into either an image or SVG syntax.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Graphic {
|
||||
Group(Table<Graphic>),
|
||||
Graphic(Table<Graphic>),
|
||||
Vector(Table<Vector>),
|
||||
RasterCPU(Table<Raster<CPU>>),
|
||||
RasterGPU(Table<Raster<GPU>>),
|
||||
@@ -21,14 +21,14 @@ pub enum Graphic {
|
||||
|
||||
impl Default for Graphic {
|
||||
fn default() -> Self {
|
||||
Self::Group(Default::default())
|
||||
Self::Graphic(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
// Group
|
||||
// Graphic
|
||||
impl From<Table<Graphic>> for Graphic {
|
||||
fn from(group: Table<Graphic>) -> Self {
|
||||
Graphic::Group(group)
|
||||
fn from(graphic: Table<Graphic>) -> Self {
|
||||
Graphic::Graphic(graphic)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,16 +111,16 @@ impl From<DAffine2> for Table<Graphic> {
|
||||
}
|
||||
|
||||
impl Graphic {
|
||||
pub fn as_group(&self) -> Option<&Table<Graphic>> {
|
||||
pub fn as_graphic(&self) -> Option<&Table<Graphic>> {
|
||||
match self {
|
||||
Graphic::Group(group) => Some(group),
|
||||
Graphic::Graphic(graphic) => Some(graphic),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_group_mut(&mut self) -> Option<&mut Table<Graphic>> {
|
||||
pub fn as_graphic_mut(&mut self) -> Option<&mut Table<Graphic>> {
|
||||
match self {
|
||||
Graphic::Group(group) => Some(group),
|
||||
Graphic::Graphic(graphic) => Some(graphic),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ impl Graphic {
|
||||
pub fn had_clip_enabled(&self) -> bool {
|
||||
match self {
|
||||
Graphic::Vector(vector) => vector.iter().all(|row| row.alpha_blending.clip),
|
||||
Graphic::Group(group) => group.iter().all(|row| row.alpha_blending.clip),
|
||||
Graphic::Graphic(graphic) => graphic.iter().all(|row| row.alpha_blending.clip),
|
||||
Graphic::RasterCPU(raster) => raster.iter().all(|row| row.alpha_blending.clip),
|
||||
Graphic::RasterGPU(raster) => raster.iter().all(|row| row.alpha_blending.clip),
|
||||
}
|
||||
@@ -180,7 +180,7 @@ impl BoundingBox for Graphic {
|
||||
Graphic::Vector(vector) => vector.bounding_box(transform, include_stroke),
|
||||
Graphic::RasterCPU(raster) => raster.bounding_box(transform, include_stroke),
|
||||
Graphic::RasterGPU(raster) => raster.bounding_box(transform, include_stroke),
|
||||
Graphic::Group(group) => group.bounding_box(transform, include_stroke),
|
||||
Graphic::Graphic(graphic) => graphic.bounding_box(transform, include_stroke),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,7 +295,7 @@ async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten: bo
|
||||
|
||||
match current_element {
|
||||
// If we're allowed to recurse, flatten any graphics we encounter
|
||||
Graphic::Group(mut current_element) if recurse => {
|
||||
Graphic::Graphic(mut current_element) if recurse => {
|
||||
// Apply the parent graphic's transform to all child elements
|
||||
for graphic in current_element.iter_mut() {
|
||||
*graphic.transform = *current_row.transform * *graphic.transform;
|
||||
@@ -332,7 +332,7 @@ async fn flatten_vector(_: impl Ctx, content: Table<Graphic>) -> Table<Vector> {
|
||||
|
||||
match current_graphic {
|
||||
// If we're allowed to recurse, flatten any tables we encounter
|
||||
Graphic::Group(mut current_graphic_table) => {
|
||||
Graphic::Graphic(mut current_graphic_table) => {
|
||||
// Apply the parent graphic's transform to all child elements
|
||||
for graphic in current_graphic_table.iter_mut() {
|
||||
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
|
||||
@@ -418,7 +418,7 @@ impl<T: Clone> AtIndex for Table<T> {
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
pub fn migrate_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Graphic>, D::Error> {
|
||||
pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Graphic>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
|
||||
@@ -441,24 +441,24 @@ pub fn migrate_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resul
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::OldGraphicGroup(old) => {
|
||||
let mut group_table = Table::new();
|
||||
let mut graphic_table = Table::new();
|
||||
for (graphic, source_node_id) in old.elements {
|
||||
group_table.push(TableRow {
|
||||
graphic_table.push(TableRow {
|
||||
element: graphic,
|
||||
transform: old.transform,
|
||||
alpha_blending: old.alpha_blending,
|
||||
source_node_id,
|
||||
});
|
||||
}
|
||||
group_table
|
||||
graphic_table
|
||||
}
|
||||
EitherFormat::Table(value) => {
|
||||
// Try to deserialize as either table format
|
||||
if let Ok(old_table) = serde_json::from_value::<Table<GraphicGroup>>(value.clone()) {
|
||||
let mut group_table = Table::new();
|
||||
let mut graphic_table = Table::new();
|
||||
for row in old_table.iter() {
|
||||
for (graphic, source_node_id) in &row.element.elements {
|
||||
group_table.push(TableRow {
|
||||
graphic_table.push(TableRow {
|
||||
element: graphic.clone(),
|
||||
transform: *row.transform,
|
||||
alpha_blending: *row.alpha_blending,
|
||||
@@ -466,7 +466,7 @@ pub fn migrate_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resul
|
||||
});
|
||||
}
|
||||
}
|
||||
group_table
|
||||
graphic_table
|
||||
} else if let Ok(new_table) = serde_json::from_value::<Table<Graphic>>(value) {
|
||||
new_table
|
||||
} else {
|
||||
|
||||
@@ -356,7 +356,7 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
|
||||
GraphicElement::RasterFrame(RasterFrame::ImageFrame(image)) => Self {
|
||||
image: image.iter().next().unwrap().element.clone(),
|
||||
},
|
||||
_ => panic!("Expected Image, found {:?}", element),
|
||||
_ => panic!("Expected Image, found {element:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ impl<T: RenderComplexity> RenderComplexity for Table<T> {
|
||||
|
||||
impl RenderComplexity for Artboard {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.group.render_complexity()
|
||||
self.content.render_complexity()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Graphic {
|
||||
fn render_complexity(&self) -> usize {
|
||||
match self {
|
||||
Self::Group(table) => table.render_complexity(),
|
||||
Self::Graphic(table) => table.render_complexity(),
|
||||
Self::Vector(table) => table.render_complexity(),
|
||||
Self::RasterCPU(table) => table.render_complexity(),
|
||||
Self::RasterGPU(table) => table.render_complexity(),
|
||||
|
||||
@@ -122,7 +122,7 @@ impl ClickTarget {
|
||||
}
|
||||
|
||||
// Check if shape is entirely within selection
|
||||
let any_point_from_subpath = subpath.manipulator_groups().first().map(|group| group.anchor);
|
||||
let any_point_from_subpath = subpath.manipulator_groups().first().map(|manipulators| manipulators.anchor);
|
||||
any_point_from_subpath.is_some_and(|shape_point| bezier_iter().map(|bezier| bezier.winding(shape_point)).sum::<i32>() != 0)
|
||||
}
|
||||
ClickTargetType::FreePoint(point) => bezier_iter().map(|bezier: bezier_rs::Bezier| bezier.winding(point.position)).sum::<i32>() != 0,
|
||||
|
||||
@@ -188,9 +188,9 @@ pub fn bezpath_to_manipulator_groups(bezpath: &BezPath) -> (Vec<ManipulatorGroup
|
||||
ManipulatorGroup::new(point_to_dvec2(point2), Some(point_to_dvec2(point1)), None)
|
||||
}
|
||||
kurbo::PathEl::ClosePath => {
|
||||
if let Some(last_group) = manipulator_groups.pop() {
|
||||
if let Some(first_group) = manipulator_groups.first_mut() {
|
||||
first_group.out_handle = last_group.in_handle;
|
||||
if let Some(last_manipulators) = manipulator_groups.pop() {
|
||||
if let Some(first_manipulators) = manipulator_groups.first_mut() {
|
||||
first_manipulators.out_handle = last_manipulators.in_handle;
|
||||
}
|
||||
}
|
||||
is_closed = true;
|
||||
|
||||
@@ -811,13 +811,13 @@ impl Vector {
|
||||
/// Construct a [`bezier_rs::Bezier`] curve from an iterator of segments with (handles, start point, end point) independently of discontinuities.
|
||||
pub fn subpath_from_segments_ignore_discontinuities(&self, segments: impl Iterator<Item = (BezierHandles, usize, usize)>) -> Option<bezier_rs::Subpath<PointId>> {
|
||||
let mut first_point = None;
|
||||
let mut groups = Vec::new();
|
||||
let mut manipulators_list = Vec::new();
|
||||
let mut last: Option<(usize, BezierHandles)> = None;
|
||||
|
||||
for (handle, start, end) in segments {
|
||||
first_point = Some(first_point.unwrap_or(start));
|
||||
|
||||
groups.push(ManipulatorGroup {
|
||||
manipulators_list.push(ManipulatorGroup {
|
||||
anchor: self.point_domain.positions()[start],
|
||||
in_handle: last.and_then(|(_, handle)| handle.end()),
|
||||
out_handle: handle.start(),
|
||||
@@ -827,13 +827,13 @@ impl Vector {
|
||||
last = Some((end, handle));
|
||||
}
|
||||
|
||||
let closed = groups.len() > 1 && last.map(|(point, _)| point) == first_point;
|
||||
let closed = manipulators_list.len() > 1 && last.map(|(point, _)| point) == first_point;
|
||||
|
||||
if let Some((end, last_handle)) = last {
|
||||
if closed {
|
||||
groups[0].in_handle = last_handle.end();
|
||||
manipulators_list[0].in_handle = last_handle.end();
|
||||
} else {
|
||||
groups.push(ManipulatorGroup {
|
||||
manipulators_list.push(ManipulatorGroup {
|
||||
anchor: self.point_domain.positions()[end],
|
||||
in_handle: last_handle.end(),
|
||||
out_handle: None,
|
||||
@@ -842,7 +842,7 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
Some(bezier_rs::Subpath::new(groups, closed))
|
||||
Some(bezier_rs::Subpath::new(manipulators_list, closed))
|
||||
}
|
||||
|
||||
/// Construct a [`bezier_rs::Bezier`] curve for each region, skipping invalid regions.
|
||||
@@ -905,7 +905,7 @@ impl Vector {
|
||||
|
||||
/// Construct a [`bezier_rs::Bezier`] curve for stroke.
|
||||
pub fn stroke_bezier_paths(&self) -> impl Iterator<Item = bezier_rs::Subpath<PointId>> {
|
||||
self.build_stroke_path_iter().map(|(group, closed)| bezier_rs::Subpath::new(group, closed))
|
||||
self.build_stroke_path_iter().map(|(manipulators_list, closed)| bezier_rs::Subpath::new(manipulators_list, closed))
|
||||
}
|
||||
|
||||
/// Construct and return an iterator of Vec of `(bezier_rs::ManipulatorGroup<PointId>], bool)` for stroke.
|
||||
@@ -916,15 +916,15 @@ impl Vector {
|
||||
|
||||
/// Construct a [`kurbo::BezPath`] curve for stroke.
|
||||
pub fn stroke_bezpath_iter(&self) -> impl Iterator<Item = kurbo::BezPath> {
|
||||
self.build_stroke_path_iter().map(|(group, closed)| {
|
||||
self.build_stroke_path_iter().map(|(manipulators_list, closed)| {
|
||||
let mut bezpath = kurbo::BezPath::new();
|
||||
let mut out_handle;
|
||||
|
||||
let Some(first) = group.first() else { return bezpath };
|
||||
let Some(first) = manipulators_list.first() else { return bezpath };
|
||||
bezpath.move_to(dvec2_to_point(first.anchor));
|
||||
out_handle = first.out_handle;
|
||||
|
||||
for manipulator in group.iter().skip(1) {
|
||||
for manipulator in manipulators_list.iter().skip(1) {
|
||||
match (out_handle, manipulator.in_handle) {
|
||||
(Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(manipulator.anchor)),
|
||||
(None, None) => bezpath.line_to(dvec2_to_point(manipulator.anchor)),
|
||||
@@ -954,7 +954,7 @@ impl Vector {
|
||||
|
||||
pub fn manipulator_group_id(&self, id: impl Into<PointId>) -> Option<ManipulatorGroup<PointId>> {
|
||||
let id = id.into();
|
||||
self.manipulator_groups().find(|group| group.id == id)
|
||||
self.manipulator_groups().find(|manipulators| manipulators.id == id)
|
||||
}
|
||||
|
||||
pub fn transform(&mut self, transform: DAffine2) {
|
||||
@@ -1047,13 +1047,13 @@ impl Iterator for StrokePathIter<'_> {
|
||||
|
||||
// There will always be one (seeing as we checked above)
|
||||
let mut point_index = current_start;
|
||||
let mut groups = Vec::new();
|
||||
let mut manipulators_list = Vec::new();
|
||||
let mut in_handle = None;
|
||||
let mut closed = false;
|
||||
loop {
|
||||
let Some(val) = self.points[point_index].take_first() else {
|
||||
// Dead end
|
||||
groups.push(ManipulatorGroup {
|
||||
manipulators_list.push(ManipulatorGroup {
|
||||
anchor: self.vector.point_domain.positions()[point_index],
|
||||
in_handle,
|
||||
out_handle: None,
|
||||
@@ -1072,7 +1072,7 @@ impl Iterator for StrokePathIter<'_> {
|
||||
} else {
|
||||
self.vector.segment_domain.end_point()[val.segment_index]
|
||||
};
|
||||
groups.push(ManipulatorGroup {
|
||||
manipulators_list.push(ManipulatorGroup {
|
||||
anchor: self.vector.point_domain.positions()[point_index],
|
||||
in_handle,
|
||||
out_handle: handles.start(),
|
||||
@@ -1085,12 +1085,12 @@ impl Iterator for StrokePathIter<'_> {
|
||||
self.points[next_point_index].take_eq(val.flipped());
|
||||
if next_point_index == current_start {
|
||||
closed = true;
|
||||
groups[0].in_handle = in_handle;
|
||||
manipulators_list[0].in_handle = in_handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Some((groups, closed))
|
||||
Some((manipulators_list, closed))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ async fn assign_colors<T>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<Graphic>, Table<Vector>)]
|
||||
#[widget(ParsedWidgetOverride::Hidden)]
|
||||
/// The vector elements, or group of vector elements, to apply the fill and/or stroke style to.
|
||||
/// The content with vector paths to apply the fill and/or stroke style to.
|
||||
mut content: T,
|
||||
#[default(true)]
|
||||
/// Whether to style the fill.
|
||||
@@ -118,7 +118,7 @@ async fn fill<F: Into<Fill> + 'n + Send, V>(
|
||||
Table<Graphic>,
|
||||
Table<Graphic>
|
||||
)]
|
||||
/// The vector elements, or group of vector elements, to apply the fill to.
|
||||
/// The content with vector paths to apply the fill style to.
|
||||
mut content: V,
|
||||
#[implementations(
|
||||
Fill,
|
||||
@@ -156,7 +156,7 @@ where
|
||||
async fn stroke<C: Into<Option<Color>> + 'n + Send, V>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<Vector>, Table<Vector>, Table<Graphic>, Table<Graphic>)]
|
||||
/// The vector elements, or group of vector elements, to apply the stroke to.
|
||||
/// The content with vector paths to apply the stroke style to.
|
||||
mut content: Table<V>,
|
||||
#[implementations(
|
||||
Option<Color>,
|
||||
@@ -447,7 +447,7 @@ async fn round_corners(
|
||||
let source_node_id = source.source_node_id;
|
||||
let source = source.element;
|
||||
|
||||
let upstream_group = source.upstream_group.clone();
|
||||
let upstream_nested_layers = source.upstream_nested_layers.clone();
|
||||
|
||||
// Flip the roundness to help with user intuition
|
||||
let roundness = 1. - roundness;
|
||||
@@ -532,7 +532,7 @@ async fn round_corners(
|
||||
result.append_bezpath(rounded_subpath);
|
||||
}
|
||||
|
||||
result.upstream_group = upstream_group;
|
||||
result.upstream_nested_layers = upstream_nested_layers;
|
||||
|
||||
TableRow {
|
||||
element: result,
|
||||
@@ -687,18 +687,18 @@ async fn auto_tangents(
|
||||
for mut subpath in source.stroke_bezier_paths() {
|
||||
subpath.apply_transform(transform);
|
||||
|
||||
let groups = subpath.manipulator_groups();
|
||||
if groups.len() < 2 {
|
||||
let manipulators_list = subpath.manipulator_groups();
|
||||
if manipulators_list.len() < 2 {
|
||||
// Not enough points for softening or handle removal
|
||||
result.append_subpath(subpath, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut new_groups = Vec::with_capacity(groups.len());
|
||||
let mut new_manipulators_list = Vec::with_capacity(manipulators_list.len());
|
||||
let is_closed = subpath.closed();
|
||||
|
||||
for i in 0..groups.len() {
|
||||
let curr = &groups[i];
|
||||
for i in 0..manipulators_list.len() {
|
||||
let curr = &manipulators_list[i];
|
||||
|
||||
if preserve_existing {
|
||||
// Check if this point has handles that are meaningfully different from the anchor
|
||||
@@ -706,15 +706,15 @@ async fn auto_tangents(
|
||||
|| (curr.out_handle.is_some() && !curr.out_handle.unwrap().abs_diff_eq(curr.anchor, 1e-5));
|
||||
|
||||
// If the point already has handles, or if it's an endpoint of an open path, keep it as is.
|
||||
if has_handles || (!is_closed && (i == 0 || i == groups.len() - 1)) {
|
||||
new_groups.push(*curr);
|
||||
if has_handles || (!is_closed && (i == 0 || i == manipulators_list.len() - 1)) {
|
||||
new_manipulators_list.push(*curr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If spread is 0, remove handles for this point, making it a sharp corner.
|
||||
if spread == 0. {
|
||||
new_groups.push(ManipulatorGroup {
|
||||
new_manipulators_list.push(ManipulatorGroup {
|
||||
anchor: curr.anchor,
|
||||
in_handle: None,
|
||||
out_handle: None,
|
||||
@@ -724,12 +724,12 @@ async fn auto_tangents(
|
||||
}
|
||||
|
||||
// Get previous and next points for auto-tangent calculation
|
||||
let prev_idx = if i == 0 { if is_closed { groups.len() - 1 } else { i } } else { i - 1 };
|
||||
let next_idx = if i == groups.len() - 1 { if is_closed { 0 } else { i } } else { i + 1 };
|
||||
let prev_idx = if i == 0 { if is_closed { manipulators_list.len() - 1 } else { i } } else { i - 1 };
|
||||
let next_idx = if i == manipulators_list.len() - 1 { if is_closed { 0 } else { i } } else { i + 1 };
|
||||
|
||||
let prev = groups[prev_idx].anchor;
|
||||
let prev = manipulators_list[prev_idx].anchor;
|
||||
let curr_pos = curr.anchor;
|
||||
let next = groups[next_idx].anchor;
|
||||
let next = manipulators_list[next_idx].anchor;
|
||||
|
||||
// Calculate directions from current point to adjacent points
|
||||
let dir_prev = (prev - curr_pos).normalize_or_zero();
|
||||
@@ -738,7 +738,7 @@ async fn auto_tangents(
|
||||
// Check if we have valid directions (e.g., points are not coincident)
|
||||
if dir_prev.length_squared() < 1e-5 || dir_next.length_squared() < 1e-5 {
|
||||
// Fallback: keep the original manipulator group (which has no active handles here)
|
||||
new_groups.push(*curr);
|
||||
new_manipulators_list.push(*curr);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -758,7 +758,7 @@ async fn auto_tangents(
|
||||
let out_length = (next - curr_pos).length() / 3. * spread;
|
||||
|
||||
// Create new manipulator group with calculated auto-tangents
|
||||
new_groups.push(ManipulatorGroup {
|
||||
new_manipulators_list.push(ManipulatorGroup {
|
||||
anchor: curr_pos,
|
||||
in_handle: Some(curr_pos + handle_dir * in_length),
|
||||
out_handle: Some(curr_pos - handle_dir * out_length),
|
||||
@@ -766,7 +766,7 @@ async fn auto_tangents(
|
||||
});
|
||||
}
|
||||
|
||||
let mut softened_bezpath = bezpath_from_manipulator_groups(&new_groups, is_closed);
|
||||
let mut softened_bezpath = bezpath_from_manipulator_groups(&new_manipulators_list, is_closed);
|
||||
softened_bezpath.apply_affine(Affine::new(transform.inverse().to_cols_array()));
|
||||
result.append_bezpath(softened_bezpath);
|
||||
}
|
||||
@@ -985,7 +985,7 @@ where
|
||||
output.element.style = row.element.style.clone();
|
||||
}
|
||||
}
|
||||
Graphic::Group(graphic) => {
|
||||
Graphic::Graphic(graphic) => {
|
||||
let mut graphic = graphic.clone();
|
||||
for row in graphic.iter_mut() {
|
||||
*row.transform = *current_element.transform * *row.transform;
|
||||
@@ -1032,7 +1032,7 @@ async fn sample_polyline(
|
||||
region_domain: Default::default(),
|
||||
colinear_manipulators: Default::default(),
|
||||
style: std::mem::take(&mut row.element.style),
|
||||
upstream_group: std::mem::take(&mut row.element.upstream_group),
|
||||
upstream_nested_layers: std::mem::take(&mut row.element.upstream_nested_layers),
|
||||
};
|
||||
// Transfer the stroke transform from the input vector content to the result.
|
||||
result.style.set_stroke_transform(row.transform);
|
||||
@@ -1343,7 +1343,7 @@ async fn spline(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||
|
||||
let mut segment_domain = SegmentDomain::default();
|
||||
for (manipulator_groups, closed) in row.element.stroke_manipulator_groups() {
|
||||
let positions = manipulator_groups.iter().map(|group| group.anchor).collect::<Vec<_>>();
|
||||
let positions = manipulator_groups.iter().map(|manipulators| manipulators.anchor).collect::<Vec<_>>();
|
||||
let closed = closed && positions.len() > 2;
|
||||
|
||||
// Compute control point handles for Bezier spline.
|
||||
@@ -2075,7 +2075,14 @@ mod test {
|
||||
let bounding_box = super::bounding_box((), vector_node_from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY))).await;
|
||||
let bounding_box = bounding_box.iter().next().unwrap().element;
|
||||
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
||||
let manipulator_groups_anchors = bounding_box.region_manipulator_groups().next().unwrap().1.iter().map(|group| group.anchor).collect::<Vec<DVec2>>();
|
||||
let manipulator_groups_anchors = bounding_box
|
||||
.region_manipulator_groups()
|
||||
.next()
|
||||
.unwrap()
|
||||
.1
|
||||
.iter()
|
||||
.map(|manipulators| manipulators.anchor)
|
||||
.collect::<Vec<DVec2>>();
|
||||
|
||||
assert_eq!(&manipulator_groups_anchors[..4], &[DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.),]);
|
||||
|
||||
@@ -2086,7 +2093,14 @@ mod test {
|
||||
let bounding_box = BoundingBoxNode { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
|
||||
let bounding_box = bounding_box.iter().next().unwrap().element;
|
||||
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
||||
let manipulator_groups_anchors = bounding_box.region_manipulator_groups().next().unwrap().1.iter().map(|group| group.anchor).collect::<Vec<DVec2>>();
|
||||
let manipulator_groups_anchors = bounding_box
|
||||
.region_manipulator_groups()
|
||||
.next()
|
||||
.unwrap()
|
||||
.1
|
||||
.iter()
|
||||
.map(|manipulators| manipulators.anchor)
|
||||
.collect::<Vec<DVec2>>();
|
||||
|
||||
let expected_bounding_box = [DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.)];
|
||||
for i in 0..4 {
|
||||
@@ -2108,7 +2122,7 @@ mod test {
|
||||
|
||||
for (index, (_, manipulator_groups)) in flattened_copy_to_points.region_manipulator_groups().enumerate() {
|
||||
let offset = expected_points[index];
|
||||
let manipulator_groups_anchors = manipulator_groups.iter().map(|group| group.anchor).collect::<Vec<DVec2>>();
|
||||
let manipulator_groups_anchors = manipulator_groups.iter().map(|manipulators| manipulators.anchor).collect::<Vec<DVec2>>();
|
||||
assert_eq!(
|
||||
&manipulator_groups_anchors,
|
||||
&[offset + DVec2::NEG_ONE, offset + DVec2::new(1., -1.), offset + DVec2::ONE, offset + DVec2::new(-1., 1.),]
|
||||
|
||||
@@ -29,8 +29,10 @@ pub struct Vector {
|
||||
pub segment_domain: SegmentDomain,
|
||||
pub region_domain: RegionDomain,
|
||||
|
||||
// Used to store the upstream group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||
pub upstream_group: Option<Table<Graphic>>,
|
||||
/// Used to store the upstream group/folder of nested layers during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved for the child layers.
|
||||
/// Without this, the tools would be working with a collapsed version of the data which has no reference to the original child layers that were booleaned together, resulting in the inner layers not being editable.
|
||||
#[serde(alias = "upstream_group")]
|
||||
pub upstream_nested_layers: Option<Table<Graphic>>,
|
||||
}
|
||||
|
||||
impl Default for Vector {
|
||||
@@ -41,7 +43,7 @@ impl Default for Vector {
|
||||
point_domain: PointDomain::new(),
|
||||
segment_domain: SegmentDomain::new(),
|
||||
region_domain: RegionDomain::new(),
|
||||
upstream_group: None,
|
||||
upstream_nested_layers: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,15 +470,12 @@ pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resu
|
||||
|
||||
pub style: PathStyle,
|
||||
|
||||
/// A list of all manipulator groups (referenced in `subpaths`) that have colinear handles (where they're locked at 180° angles from one another).
|
||||
/// This gets read in `graph_operation_message_handler.rs` by calling `inputs.as_mut_slice()` (search for the string `"Shape does not have both `subpath` and `colinear_manipulators` inputs"` to find it).
|
||||
pub colinear_manipulators: Vec<[HandleId; 2]>,
|
||||
|
||||
pub point_domain: PointDomain,
|
||||
pub segment_domain: SegmentDomain,
|
||||
pub region_domain: RegionDomain,
|
||||
|
||||
// Used to store the upstream group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||
pub upstream_graphic_group: Option<Table<Graphic>>,
|
||||
}
|
||||
|
||||
@@ -498,7 +497,7 @@ pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resu
|
||||
point_domain: old.point_domain,
|
||||
segment_domain: old.segment_domain,
|
||||
region_domain: old.region_domain,
|
||||
upstream_group: old.upstream_graphic_group,
|
||||
upstream_nested_layers: old.upstream_graphic_group,
|
||||
});
|
||||
*vector_table.iter_mut().next().unwrap().transform = old.transform;
|
||||
*vector_table.iter_mut().next().unwrap().alpha_blending = old.alpha_blending;
|
||||
|
||||
@@ -34,9 +34,9 @@ pub enum BooleanOperation {
|
||||
#[node_macro::node(category(""))]
|
||||
async fn boolean_operation<I: Into<Table<Graphic>> + 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The group of paths to perform the boolean operation on. Nested groups are automatically flattened.
|
||||
/// The table of vector paths to perform the boolean operation on. Nested tables are automatically flattened.
|
||||
#[implementations(Table<Graphic>, Table<Vector>)]
|
||||
group_of_paths: I,
|
||||
content: I,
|
||||
/// Which boolean operation to perform on the paths.
|
||||
///
|
||||
/// Union combines all paths while cutting out overlapping areas (even the interiors of a single path).
|
||||
@@ -45,10 +45,10 @@ async fn boolean_operation<I: Into<Table<Graphic>> + 'n + Send + Clone>(
|
||||
/// Difference cuts away the overlapping areas shared by every path, leaving only the non-overlapping areas.
|
||||
operation: BooleanOperation,
|
||||
) -> Table<Vector> {
|
||||
let group_of_paths = group_of_paths.into();
|
||||
let content = content.into();
|
||||
|
||||
// The first index is the bottom of the stack
|
||||
let mut result_vector_table = boolean_operation_on_vector_table(flatten_vector(&group_of_paths).iter(), operation);
|
||||
let mut result_vector_table = boolean_operation_on_vector_table(flatten_vector(&content).iter(), operation);
|
||||
|
||||
// Replace the transformation matrix with a mutation of the vector points themselves
|
||||
if let Some(result_vector) = result_vector_table.iter_mut().next() {
|
||||
@@ -57,7 +57,7 @@ async fn boolean_operation<I: Into<Table<Graphic>> + 'n + Send + Clone>(
|
||||
|
||||
Vector::transform(result_vector.element, transform);
|
||||
result_vector.element.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
result_vector.element.upstream_group = Some(group_of_paths.clone());
|
||||
result_vector.element.upstream_nested_layers = Some(content.clone());
|
||||
|
||||
// Clean up the boolean operation result by merging duplicated points
|
||||
result_vector.element.merge_by_distance_spatial(*result_vector.transform, 0.0001);
|
||||
@@ -221,13 +221,13 @@ fn difference<'a>(vector: impl DoubleEndedIterator<Item = TableRowRef<'a, Vector
|
||||
boolean_operation_on_vector_table(union.iter().chain(std::iter::once(any_intersection.as_ref())), BooleanOperation::SubtractFront)
|
||||
}
|
||||
|
||||
fn flatten_vector(group_table: &Table<Graphic>) -> Table<Vector> {
|
||||
group_table
|
||||
fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||
graphic_table
|
||||
.iter()
|
||||
.flat_map(|element| {
|
||||
match element.element.clone() {
|
||||
Graphic::Vector(vector) => {
|
||||
// Apply the parent group's transform to each element of the vector table
|
||||
// Apply the parent graphic's transform to each element of the vector table
|
||||
vector
|
||||
.into_iter()
|
||||
.map(|mut sub_vector| {
|
||||
@@ -250,7 +250,7 @@ fn flatten_vector(group_table: &Table<Graphic>) -> Table<Vector> {
|
||||
TableRow { element, ..Default::default() }
|
||||
};
|
||||
|
||||
// Apply the parent group's transform to each raster element
|
||||
// Apply the parent graphic's transform to each raster element
|
||||
image.iter().map(|row| make_row(*element.transform * *row.transform)).collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::RasterGPU(image) => {
|
||||
@@ -266,17 +266,17 @@ fn flatten_vector(group_table: &Table<Graphic>) -> Table<Vector> {
|
||||
TableRow { element, ..Default::default() }
|
||||
};
|
||||
|
||||
// Apply the parent group's transform to each raster element
|
||||
// Apply the parent graphic's transform to each raster element
|
||||
image.iter().map(|row| make_row(*element.transform * *row.transform)).collect::<Vec<_>>()
|
||||
}
|
||||
Graphic::Group(mut group) => {
|
||||
// Apply the parent group's transform to each element of inner group
|
||||
for sub_element in group.iter_mut() {
|
||||
Graphic::Graphic(mut graphic) => {
|
||||
// Apply the parent graphic's transform to each element of inner table
|
||||
for sub_element in graphic.iter_mut() {
|
||||
*sub_element.transform = *element.transform * *sub_element.transform;
|
||||
}
|
||||
|
||||
// Recursively flatten the inner group into the vector table
|
||||
let unioned = boolean_operation_on_vector_table(flatten_vector(&group).iter(), BooleanOperation::Union);
|
||||
// Recursively flatten the inner table into the output vector table
|
||||
let unioned = boolean_operation_on_vector_table(flatten_vector(&graphic).iter(), BooleanOperation::Union);
|
||||
|
||||
unioned.into_iter().collect::<Vec<_>>()
|
||||
}
|
||||
@@ -329,7 +329,7 @@ fn from_path(path_data: &[Path]) -> Vector {
|
||||
|
||||
for path in path_data.iter().filter(|path| !path.is_empty()) {
|
||||
let cubics: Vec<[DVec2; 4]> = path.iter().map(|segment| segment.to_cubic()).collect();
|
||||
let mut groups = Vec::new();
|
||||
let mut manipulators_list = Vec::new();
|
||||
let mut current_start = None;
|
||||
|
||||
for (index, cubic) in cubics.iter().enumerate() {
|
||||
@@ -337,27 +337,27 @@ fn from_path(path_data: &[Path]) -> Vector {
|
||||
|
||||
if current_start.is_none() || !is_close(start, current_start.unwrap()) {
|
||||
// Start a new subpath
|
||||
if !groups.is_empty() {
|
||||
all_subpaths.push(Subpath::new(std::mem::take(&mut groups), true));
|
||||
if !manipulators_list.is_empty() {
|
||||
all_subpaths.push(Subpath::new(std::mem::take(&mut manipulators_list), true));
|
||||
}
|
||||
// Use the correct in-handle (None) and out-handle for the start point
|
||||
groups.push(ManipulatorGroup::new(start, None, Some(handle1)));
|
||||
manipulators_list.push(ManipulatorGroup::new(start, None, Some(handle1)));
|
||||
} else {
|
||||
// Update the out-handle of the previous point
|
||||
if let Some(last) = groups.last_mut() {
|
||||
if let Some(last) = manipulators_list.last_mut() {
|
||||
last.out_handle = Some(handle1);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the end point with the correct in-handle and out-handle (None)
|
||||
groups.push(ManipulatorGroup::new(end, Some(handle2), None));
|
||||
manipulators_list.push(ManipulatorGroup::new(end, Some(handle2), None));
|
||||
|
||||
current_start = Some(end);
|
||||
|
||||
// Check if this is the last segment
|
||||
if index == cubics.len() - 1 {
|
||||
all_subpaths.push(Subpath::new(groups, true));
|
||||
groups = Vec::new(); // Reset groups for the next path
|
||||
all_subpaths.push(Subpath::new(manipulators_list, true));
|
||||
manipulators_list = Vec::new(); // Reset manipulators for the next path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,37 +390,13 @@ impl NodeInput {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
#[derive(Clone, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
/// Represents the implementation of a node, which can be a nested [`NodeNetwork`], a proto [`ProtoNodeIdentifier`], or `Extract`.
|
||||
pub enum OldDocumentNodeImplementation {
|
||||
/// This describes a (document) node built out of a subgraph of other (document) nodes.
|
||||
///
|
||||
/// A nested [`NodeNetwork`] that is flattened by the [`NodeNetwork::flatten`] function.
|
||||
Network(OldNodeNetwork),
|
||||
/// This describes a (document) node implemented as a proto node.
|
||||
///
|
||||
/// A proto node identifier which can be found in `node_registry.rs`.
|
||||
#[serde(alias = "Unresolved")] // TODO: Eventually remove this alias document upgrade code
|
||||
#[serde(alias = "Unresolved")]
|
||||
ProtoNode(ProtoNodeIdentifier),
|
||||
/// The Extract variant is a tag which tells the compilation process to do something special. It invokes language-level functionality built for use by the ExtractNode to enable metaprogramming.
|
||||
/// When the ExtractNode is compiled, it gets replaced by a value node containing a representation of the source code for the function/lambda of the document node that's fed into the ExtractNode
|
||||
/// (but only that one document node, not upstream nodes).
|
||||
///
|
||||
/// This is explained in more detail here: <https://www.youtube.com/watch?v=72KJa3jQClo>
|
||||
///
|
||||
/// Currently we use it for GPU execution, where a node has to get "extracted" to its source code representation and stored as a value that can be given to the GpuCompiler node at runtime
|
||||
/// (to become a compute shader). Future use could involve the addition of an InjectNode to convert the source code form back into an executable node, enabling metaprogramming in the node graph.
|
||||
/// We would use an assortment of nodes that operate on Graphene source code (just data, no different from any other data flowing through the graph) to make graph transformations.
|
||||
///
|
||||
/// We use this for dealing with macros in a syntactic way of modifying the node graph from within the graph itself. Just like we often deal with lambdas to represent a whole group of
|
||||
/// operations/code/logic, this allows us to basically deal with a lambda at a meta/source-code level, because we need to pass the GPU SPIR-V compiler the source code for a lambda,
|
||||
/// not the executable logic of a lambda.
|
||||
///
|
||||
/// This is analogous to how Rust macros operate at the level of source code, not executable code. When we speak of source code, that represents Graphene's source code in the form of a
|
||||
/// DocumentNode network, not the text form of Rust's source code. (Analogous to the token stream/AST of a Rust macro.)
|
||||
///
|
||||
/// `DocumentNode`s with a `DocumentNodeImplementation::Extract` are converted into a `ClonedNode` that returns the `DocumentNode` specified by the single `NodeInput::Node`. The referenced node
|
||||
/// (specified by the single `NodeInput::Node`) is removed from the network, and any `NodeInput::Node`s used by the referenced node are replaced with a generically typed network input.
|
||||
Extract,
|
||||
}
|
||||
|
||||
@@ -915,6 +891,7 @@ impl NodeNetwork {
|
||||
warn!("The node which was supposed to be flattened does not exist in the network, id {node_id} network {self:#?}");
|
||||
return;
|
||||
};
|
||||
|
||||
// If the node is hidden, replace it with an identity node
|
||||
let identity_node = DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into());
|
||||
if !node.visible && node.implementation != identity_node {
|
||||
|
||||
@@ -187,10 +187,10 @@ tagged_value! {
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[serde(alias = "ImageFrame", alias = "RasterData")]
|
||||
Raster(Table<Raster<CPU>>),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::graphic::migrate_group"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[serde(alias = "GraphicGroup")]
|
||||
Group(Table<Graphic>),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::artboard::migrate_artboard_group"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::graphic::migrate_graphic"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[serde(alias = "GraphicGroup", alias = "Group")]
|
||||
Graphic(Table<Graphic>),
|
||||
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::artboard::migrate_artboard"))] // TODO: Eventually remove this migration document upgrade code
|
||||
#[serde(alias = "ArtboardGroup")]
|
||||
Artboard(Table<Artboard>),
|
||||
// ============
|
||||
|
||||
@@ -4,7 +4,7 @@ use graphene_core::vector::PointId;
|
||||
|
||||
pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
|
||||
let mut subpaths = Vec::new();
|
||||
let mut groups = Vec::new();
|
||||
let mut manipulators_list = Vec::new();
|
||||
|
||||
let mut points = path.data().points().iter();
|
||||
let to_vec = |p: &usvg::tiny_skia_path::Point| DVec2::new(p.x as f64, p.y as f64);
|
||||
@@ -12,36 +12,36 @@ pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
|
||||
for verb in path.data().verbs() {
|
||||
match verb {
|
||||
usvg::tiny_skia_path::PathVerb::Move => {
|
||||
subpaths.push(Subpath::new(std::mem::take(&mut groups), false));
|
||||
subpaths.push(Subpath::new(std::mem::take(&mut manipulators_list), false));
|
||||
let Some(start) = points.next().map(to_vec) else { continue };
|
||||
groups.push(ManipulatorGroup::new(start, Some(start), Some(start)));
|
||||
manipulators_list.push(ManipulatorGroup::new(start, Some(start), Some(start)));
|
||||
}
|
||||
usvg::tiny_skia_path::PathVerb::Line => {
|
||||
let Some(end) = points.next().map(to_vec) else { continue };
|
||||
groups.push(ManipulatorGroup::new(end, Some(end), Some(end)));
|
||||
manipulators_list.push(ManipulatorGroup::new(end, Some(end), Some(end)));
|
||||
}
|
||||
usvg::tiny_skia_path::PathVerb::Quad => {
|
||||
let Some(handle) = points.next().map(to_vec) else { continue };
|
||||
let Some(end) = points.next().map(to_vec) else { continue };
|
||||
if let Some(last) = groups.last_mut() {
|
||||
if let Some(last) = manipulators_list.last_mut() {
|
||||
last.out_handle = Some(last.anchor + (2. / 3.) * (handle - last.anchor));
|
||||
}
|
||||
groups.push(ManipulatorGroup::new(end, Some(end + (2. / 3.) * (handle - end)), Some(end)));
|
||||
manipulators_list.push(ManipulatorGroup::new(end, Some(end + (2. / 3.) * (handle - end)), Some(end)));
|
||||
}
|
||||
usvg::tiny_skia_path::PathVerb::Cubic => {
|
||||
let Some(first_handle) = points.next().map(to_vec) else { continue };
|
||||
let Some(second_handle) = points.next().map(to_vec) else { continue };
|
||||
let Some(end) = points.next().map(to_vec) else { continue };
|
||||
if let Some(last) = groups.last_mut() {
|
||||
if let Some(last) = manipulators_list.last_mut() {
|
||||
last.out_handle = Some(first_handle);
|
||||
}
|
||||
groups.push(ManipulatorGroup::new(end, Some(second_handle), Some(end)));
|
||||
manipulators_list.push(ManipulatorGroup::new(end, Some(second_handle), Some(end)));
|
||||
}
|
||||
usvg::tiny_skia_path::PathVerb::Close => {
|
||||
subpaths.push(Subpath::new(std::mem::take(&mut groups), true));
|
||||
subpaths.push(Subpath::new(std::mem::take(&mut manipulators_list), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
subpaths.push(Subpath::new(groups, false));
|
||||
subpaths.push(Subpath::new(manipulators_list, false));
|
||||
subpaths
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ pub trait Render: BoundingBox + RenderComplexity {
|
||||
// TODO: Store all click targets in a vec which contains the AABB, click target, and path
|
||||
// fn add_click_targets(&self, click_targets: &mut Vec<([DVec2; 2], ClickTarget, Vec<NodeId>)>, current_path: Option<NodeId>) {}
|
||||
|
||||
/// Recursively iterate over data in the render (including groups upstream from vector data in the case of a boolean operation) to collect the footprints, click targets, and vector modify.
|
||||
/// Recursively iterate over data in the render (including nested layer stacks upstream of a vector node, in the case of a boolean operation) to collect the footprints, click targets, and vector modify.
|
||||
fn collect_metadata(&self, _metadata: &mut RenderMetadata, _footprint: Footprint, _element_id: Option<NodeId>) {}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
@@ -365,7 +365,7 @@ impl Render for Table<Graphic> {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(group_id) = element_id {
|
||||
if let Some(element_id) = element_id {
|
||||
let mut all_upstream_click_targets = Vec::new();
|
||||
|
||||
for row in self.iter() {
|
||||
@@ -379,7 +379,7 @@ impl Render for Table<Graphic> {
|
||||
all_upstream_click_targets.extend(new_click_targets);
|
||||
}
|
||||
|
||||
metadata.click_targets.insert(group_id, all_upstream_click_targets);
|
||||
metadata.click_targets.insert(element_id, all_upstream_click_targets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,9 +764,9 @@ impl Render for Table<Vector> {
|
||||
metadata.click_targets.entry(element_id).or_insert(click_targets);
|
||||
}
|
||||
|
||||
if let Some(upstream_group) = &vector.upstream_group {
|
||||
if let Some(upstream_nested_layers) = &vector.upstream_nested_layers {
|
||||
footprint.transform *= transform;
|
||||
upstream_group.collect_metadata(metadata, footprint, None);
|
||||
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -813,6 +813,7 @@ impl Render for Table<Vector> {
|
||||
|
||||
impl Render for Artboard {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
// Rectangle for the artboard
|
||||
if !render_params.hide_artboards {
|
||||
// Background
|
||||
render.leaf_tag("rect", |attributes| {
|
||||
@@ -827,7 +828,7 @@ impl Render for Artboard {
|
||||
});
|
||||
}
|
||||
|
||||
// Content group (includes the artwork but not the background)
|
||||
// Artwork
|
||||
render.parent_tag(
|
||||
// SVG group tag
|
||||
"g",
|
||||
@@ -851,9 +852,9 @@ impl Render for Artboard {
|
||||
attributes.push("clip-path", selector);
|
||||
}
|
||||
},
|
||||
// Artboard content
|
||||
// Artwork content
|
||||
|render| {
|
||||
self.group.render_svg(render, render_params);
|
||||
self.content.render_svg(render, render_params);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -875,9 +876,9 @@ impl Render for Artboard {
|
||||
let blend_mode = peniko::BlendMode::new(peniko::Mix::Clip, peniko::Compose::SrcOver);
|
||||
scene.push_layer(blend_mode, 1., kurbo::Affine::new(transform.to_cols_array()), &rect);
|
||||
}
|
||||
// Since the group's transform is right multiplied in when rendering the group, we just need to right multiply by the offset here.
|
||||
// Since the content's transform is right multiplied in when rendering the content, we just need to right multiply by the artboard offset here.
|
||||
let child_transform = transform * DAffine2::from_translation(self.location.as_dvec2());
|
||||
self.group.render_to_vello(scene, child_transform, context, render_params);
|
||||
self.content.render_to_vello(scene, child_transform, context, render_params);
|
||||
if self.clip {
|
||||
scene.pop_layer();
|
||||
}
|
||||
@@ -894,7 +895,7 @@ impl Render for Artboard {
|
||||
}
|
||||
}
|
||||
footprint.transform *= self.transform();
|
||||
self.group.collect_metadata(metadata, footprint, None);
|
||||
self.content.collect_metadata(metadata, footprint, None);
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
@@ -1140,27 +1141,27 @@ impl Render for Table<Raster<GPU>> {
|
||||
impl Render for Graphic {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
match self {
|
||||
Graphic::Graphic(graphic) => graphic.render_svg(render, render_params),
|
||||
Graphic::Vector(vector) => vector.render_svg(render, render_params),
|
||||
Graphic::RasterCPU(raster) => raster.render_svg(render, render_params),
|
||||
Graphic::RasterGPU(_) => (),
|
||||
Graphic::Group(group) => group.render_svg(render, render_params),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "vello")]
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||
match self {
|
||||
Graphic::Graphic(graphic) => graphic.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::Vector(vector) => vector.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::RasterCPU(raster) => raster.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::RasterGPU(raster) => raster.render_to_vello(scene, transform, context, render_params),
|
||||
Graphic::Group(group) => group.render_to_vello(scene, transform, context, render_params),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
||||
if let Some(element_id) = element_id {
|
||||
match self {
|
||||
Graphic::Group(_) => {
|
||||
Graphic::Graphic(_) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
}
|
||||
Graphic::Vector(vector) => {
|
||||
@@ -1191,26 +1192,26 @@ impl Render for Graphic {
|
||||
}
|
||||
|
||||
match self {
|
||||
Graphic::Graphic(graphic) => graphic.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::Vector(vector) => vector.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::RasterCPU(raster) => raster.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::RasterGPU(raster) => raster.collect_metadata(metadata, footprint, element_id),
|
||||
Graphic::Group(group) => group.collect_metadata(metadata, footprint, element_id),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
match self {
|
||||
Graphic::Graphic(graphic) => graphic.add_upstream_click_targets(click_targets),
|
||||
Graphic::Vector(vector) => vector.add_upstream_click_targets(click_targets),
|
||||
Graphic::RasterCPU(raster) => raster.add_upstream_click_targets(click_targets),
|
||||
Graphic::RasterGPU(raster) => raster.add_upstream_click_targets(click_targets),
|
||||
Graphic::Group(group) => group.add_upstream_click_targets(click_targets),
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
match self {
|
||||
Graphic::Graphic(graphic) => graphic.contains_artboard(),
|
||||
Graphic::Vector(vector) => vector.contains_artboard(),
|
||||
Graphic::Group(group) => group.contains_artboard(),
|
||||
Graphic::RasterCPU(raster) => raster.contains_artboard(),
|
||||
Graphic::RasterGPU(raster) => raster.contains_artboard(),
|
||||
}
|
||||
@@ -1218,8 +1219,8 @@ impl Render for Graphic {
|
||||
|
||||
fn new_ids_from_hash(&mut self, reference: Option<NodeId>) {
|
||||
match self {
|
||||
Graphic::Graphic(graphic) => graphic.new_ids_from_hash(reference),
|
||||
Graphic::Vector(vector) => vector.new_ids_from_hash(reference),
|
||||
Graphic::Group(group) => group.new_ids_from_hash(reference),
|
||||
Graphic::RasterCPU(_) => (),
|
||||
Graphic::RasterGPU(_) => (),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user