mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Rename Instances<T> to Table<T> and the "instance" terminology to "TableRow" and "element" (#2981)
* Instances -> Table * instances.rs -> table.rs * Rename occurrances of the word "instances" * .instance -> .element * Instance* -> TableRow* * Rename Table and TableRow methods to not say "instance" * Remove presumed unused serde defaults now that tables default to length 0 not 1 * Rename occurences of the word "instance" * Un-alias the RasterDataTable<Storage>, VectorDataTable, GraphicGroupTable, ArtboardGroupTable typedefs * Move artboard type and node code out of graphic_element.rs to a new artboard.rs * Organize the TaggedValues * Fix tests * Fix prior regression with Image Value node not upgrading
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::instances::{Instance, InstanceRef};
|
||||
use graphene_core::table::{Table, TableRow, TableRowRef};
|
||||
use graphene_core::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::vector::{PointId, VectorData, VectorDataTable};
|
||||
use graphene_core::{Color, Ctx, GraphicElement, GraphicGroupTable};
|
||||
use graphene_core::vector::{PointId, VectorData};
|
||||
use graphene_core::{Color, Ctx, GraphicElement};
|
||||
pub use path_bool as path_bool_lib;
|
||||
use path_bool::{FillRule, PathBooleanOperation};
|
||||
use std::ops::Mul;
|
||||
@@ -32,10 +32,10 @@ pub enum BooleanOperation {
|
||||
|
||||
/// Combines the geometric forms of one or more closed paths into a new vector path that results from cutting or joining the paths by the chosen method.
|
||||
#[node_macro::node(category(""))]
|
||||
async fn boolean_operation<I: Into<GraphicGroupTable> + 'n + Send + Clone>(
|
||||
async fn boolean_operation<I: Into<Table<GraphicElement>> + 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The group of paths to perform the boolean operation on. Nested groups are automatically flattened.
|
||||
#[implementations(GraphicGroupTable, VectorDataTable)]
|
||||
#[implementations(Table<GraphicElement>, Table<VectorData>)]
|
||||
group_of_paths: I,
|
||||
/// Which boolean operation to perform on the paths.
|
||||
///
|
||||
@@ -44,29 +44,29 @@ async fn boolean_operation<I: Into<GraphicGroupTable> + 'n + Send + Clone>(
|
||||
/// Intersection cuts away all but the overlapping areas shared by every path.
|
||||
/// Difference cuts away the overlapping areas shared by every path, leaving only the non-overlapping areas.
|
||||
operation: BooleanOperation,
|
||||
) -> VectorDataTable {
|
||||
) -> Table<VectorData> {
|
||||
let group_of_paths = group_of_paths.into();
|
||||
|
||||
// The first index is the bottom of the stack
|
||||
let mut result_vector_data_table = boolean_operation_on_vector_data_table(flatten_vector_data(&group_of_paths).instance_ref_iter(), operation);
|
||||
let mut result_vector_data_table = boolean_operation_on_vector_data_table(flatten_vector_data(&group_of_paths).iter_ref(), operation);
|
||||
|
||||
// Replace the transformation matrix with a mutation of the vector points themselves
|
||||
if let Some(result_vector_data) = result_vector_data_table.instance_mut_iter().next() {
|
||||
if let Some(result_vector_data) = result_vector_data_table.iter_mut().next() {
|
||||
let transform = *result_vector_data.transform;
|
||||
*result_vector_data.transform = DAffine2::IDENTITY;
|
||||
|
||||
VectorData::transform(result_vector_data.instance, transform);
|
||||
result_vector_data.instance.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
result_vector_data.instance.upstream_graphic_group = Some(group_of_paths.clone());
|
||||
VectorData::transform(result_vector_data.element, transform);
|
||||
result_vector_data.element.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
result_vector_data.element.upstream_graphic_group = Some(group_of_paths.clone());
|
||||
|
||||
// Clean up the boolean operation result by merging duplicated points
|
||||
result_vector_data.instance.merge_by_distance_spatial(*result_vector_data.transform, 0.0001);
|
||||
result_vector_data.element.merge_by_distance_spatial(*result_vector_data.transform, 0.0001);
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
}
|
||||
|
||||
fn boolean_operation_on_vector_data_table<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, VectorData>> + Clone, boolean_operation: BooleanOperation) -> VectorDataTable {
|
||||
fn boolean_operation_on_vector_data_table<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>> + Clone, boolean_operation: BooleanOperation) -> Table<VectorData> {
|
||||
match boolean_operation {
|
||||
BooleanOperation::Union => union(vector_data),
|
||||
BooleanOperation::SubtractFront => subtract(vector_data),
|
||||
@@ -76,23 +76,23 @@ fn boolean_operation_on_vector_data_table<'a>(vector_data: impl DoubleEndedItera
|
||||
}
|
||||
}
|
||||
|
||||
fn union<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, VectorData>>) -> VectorDataTable {
|
||||
fn union<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>>) -> Table<VectorData> {
|
||||
// Reverse vector data so that the result style is the style of the first vector data
|
||||
let mut vector_data_reversed = vector_data.rev();
|
||||
|
||||
let mut result_vector_data_table = VectorDataTable::new_instance(vector_data_reversed.next().map(|x| x.to_instance_cloned()).unwrap_or_default());
|
||||
let mut first_instance = result_vector_data_table.instance_mut_iter().next().expect("Expected the one instance we just pushed");
|
||||
let mut result_vector_data_table = Table::new_from_row(vector_data_reversed.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_data_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
|
||||
// Loop over all vector data and union it with the result
|
||||
let default = Instance::default();
|
||||
let mut second_vector_data = Some(vector_data_reversed.next().unwrap_or(default.to_instance_ref()));
|
||||
let default = TableRow::default();
|
||||
let mut second_vector_data = Some(vector_data_reversed.next().unwrap_or(default.as_ref()));
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let transform_of_lower_into_space_of_upper = first_instance.transform.inverse() * *lower_vector_data.transform;
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
|
||||
let result = &mut first_instance.instance;
|
||||
let result = &mut first_row.element;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.instance, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_union(upper_path_string, lower_path_string) };
|
||||
@@ -109,21 +109,21 @@ fn union<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, Vector
|
||||
result_vector_data_table
|
||||
}
|
||||
|
||||
fn subtract<'a>(vector_data: impl Iterator<Item = InstanceRef<'a, VectorData>>) -> VectorDataTable {
|
||||
fn subtract<'a>(vector_data: impl Iterator<Item = TableRowRef<'a, VectorData>>) -> Table<VectorData> {
|
||||
let mut vector_data = vector_data.into_iter();
|
||||
|
||||
let mut result_vector_data_table = VectorDataTable::new_instance(vector_data.next().map(|x| x.to_instance_cloned()).unwrap_or_default());
|
||||
let mut first_instance = result_vector_data_table.instance_mut_iter().next().expect("Expected the one instance we just pushed");
|
||||
let mut result_vector_data_table = Table::new_from_row(vector_data.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_data_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
|
||||
let mut next_vector_data = vector_data.next();
|
||||
|
||||
while let Some(lower_vector_data) = next_vector_data {
|
||||
let transform_of_lower_into_space_of_upper = first_instance.transform.inverse() * *lower_vector_data.transform;
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
|
||||
let result = &mut first_instance.instance;
|
||||
let result = &mut first_row.element;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.instance, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_subtract(upper_path_string, lower_path_string) };
|
||||
@@ -140,23 +140,23 @@ fn subtract<'a>(vector_data: impl Iterator<Item = InstanceRef<'a, VectorData>>)
|
||||
result_vector_data_table
|
||||
}
|
||||
|
||||
fn intersect<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, VectorData>>) -> VectorDataTable {
|
||||
fn intersect<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>>) -> Table<VectorData> {
|
||||
let mut vector_data = vector_data.rev();
|
||||
|
||||
let mut result_vector_data_table = VectorDataTable::new_instance(vector_data.next().map(|x| x.to_instance_cloned()).unwrap_or_default());
|
||||
let mut first_instance = result_vector_data_table.instance_mut_iter().next().expect("Expected the one instance we just pushed");
|
||||
let mut result_vector_data_table = Table::new_from_row(vector_data.next().map(|x| x.into_cloned()).unwrap_or_default());
|
||||
let mut first_row = result_vector_data_table.iter_mut().next().expect("Expected the one row we just pushed");
|
||||
|
||||
let default = Instance::default();
|
||||
let mut second_vector_data = Some(vector_data.next().unwrap_or(default.to_instance_ref()));
|
||||
let default = TableRow::default();
|
||||
let mut second_vector_data = Some(vector_data.next().unwrap_or(default.as_ref()));
|
||||
|
||||
// For each vector data, set the result to the intersection of that data and the result
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let transform_of_lower_into_space_of_upper = first_instance.transform.inverse() * *lower_vector_data.transform;
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
|
||||
let result = &mut first_instance.instance;
|
||||
let result = &mut first_row.element;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.instance, transform_of_lower_into_space_of_upper);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
@@ -172,45 +172,45 @@ fn intersect<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, Ve
|
||||
result_vector_data_table
|
||||
}
|
||||
|
||||
fn difference<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, VectorData>> + Clone) -> VectorDataTable {
|
||||
fn difference<'a>(vector_data: impl DoubleEndedIterator<Item = TableRowRef<'a, VectorData>> + Clone) -> Table<VectorData> {
|
||||
let mut vector_data_iter = vector_data.clone().rev();
|
||||
let mut any_intersection = Instance::default();
|
||||
let default = Instance::default();
|
||||
let mut second_vector_data = Some(vector_data_iter.next().unwrap_or(default.to_instance_ref()));
|
||||
let mut any_intersection = TableRow::default();
|
||||
let default = TableRow::default();
|
||||
let mut second_vector_data = Some(vector_data_iter.next().unwrap_or(default.as_ref()));
|
||||
|
||||
// Find where all vector data intersect at least once
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let filtered_vector_data = vector_data.clone().filter(|v| *v != lower_vector_data).collect::<Vec<_>>().into_iter();
|
||||
let unioned = boolean_operation_on_vector_data_table(filtered_vector_data, BooleanOperation::Union);
|
||||
let first_instance = unioned.instance_ref_iter().next().expect("Expected at least one instance after the boolean union");
|
||||
let first_row = unioned.iter_ref().next().expect("Expected at least one row after the boolean union");
|
||||
|
||||
let transform_of_lower_into_space_of_upper = first_instance.transform.inverse() * *lower_vector_data.transform;
|
||||
let transform_of_lower_into_space_of_upper = first_row.transform.inverse() * *lower_vector_data.transform;
|
||||
|
||||
let upper_path_string = to_path(first_instance.instance, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.instance, transform_of_lower_into_space_of_upper);
|
||||
let upper_path_string = to_path(first_row.element, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_intersection_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
let mut instance = from_path(&boolean_intersection_string);
|
||||
instance.style = first_instance.instance.style.clone();
|
||||
let boolean_intersection_result = Instance {
|
||||
instance,
|
||||
transform: *first_instance.transform,
|
||||
alpha_blending: *first_instance.alpha_blending,
|
||||
source_node_id: *first_instance.source_node_id,
|
||||
let mut element = from_path(&boolean_intersection_string);
|
||||
element.style = first_row.element.style.clone();
|
||||
let boolean_intersection_result = TableRow {
|
||||
element,
|
||||
transform: *first_row.transform,
|
||||
alpha_blending: *first_row.alpha_blending,
|
||||
source_node_id: *first_row.source_node_id,
|
||||
};
|
||||
|
||||
let transform_of_lower_into_space_of_upper = boolean_intersection_result.transform.inverse() * any_intersection.transform;
|
||||
|
||||
let upper_path_string = to_path(&boolean_intersection_result.instance, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(&any_intersection.instance, transform_of_lower_into_space_of_upper);
|
||||
let upper_path_string = to_path(&boolean_intersection_result.element, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(&any_intersection.element, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let union_result = from_path(&unsafe { boolean_union(upper_path_string, lower_path_string) });
|
||||
any_intersection.instance = union_result;
|
||||
any_intersection.element = union_result;
|
||||
|
||||
any_intersection.transform = boolean_intersection_result.transform;
|
||||
any_intersection.instance.style = boolean_intersection_result.instance.style.clone();
|
||||
any_intersection.element.style = boolean_intersection_result.element.style.clone();
|
||||
any_intersection.alpha_blending = boolean_intersection_result.alpha_blending;
|
||||
|
||||
second_vector_data = vector_data_iter.next();
|
||||
@@ -218,18 +218,18 @@ fn difference<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, V
|
||||
|
||||
// Subtract the area where they intersect at least once from the union of all vector data
|
||||
let union = boolean_operation_on_vector_data_table(vector_data, BooleanOperation::Union);
|
||||
boolean_operation_on_vector_data_table(union.instance_ref_iter().chain(std::iter::once(any_intersection.to_instance_ref())), BooleanOperation::SubtractFront)
|
||||
boolean_operation_on_vector_data_table(union.iter_ref().chain(std::iter::once(any_intersection.as_ref())), BooleanOperation::SubtractFront)
|
||||
}
|
||||
|
||||
fn flatten_vector_data(graphic_group_table: &GraphicGroupTable) -> VectorDataTable {
|
||||
fn flatten_vector_data(graphic_group_table: &Table<GraphicElement>) -> Table<VectorData> {
|
||||
graphic_group_table
|
||||
.instance_ref_iter()
|
||||
.iter_ref()
|
||||
.flat_map(|element| {
|
||||
match element.instance.clone() {
|
||||
match element.element.clone() {
|
||||
GraphicElement::VectorData(vector_data) => {
|
||||
// Apply the parent group's transform to each element of vector data
|
||||
vector_data
|
||||
.instance_iter()
|
||||
.iter()
|
||||
.map(|mut sub_vector_data| {
|
||||
sub_vector_data.transform = *element.transform * sub_vector_data.transform;
|
||||
|
||||
@@ -238,47 +238,47 @@ fn flatten_vector_data(graphic_group_table: &GraphicGroupTable) -> VectorDataTab
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
GraphicElement::RasterDataCPU(image) => {
|
||||
let make_instance = |transform| {
|
||||
let make_row = |transform| {
|
||||
// Convert the image frame into a rectangular subpath with the image's transform
|
||||
let mut subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
|
||||
subpath.apply_transform(transform);
|
||||
|
||||
// Create a vector data table row from the rectangular subpath, with a default black fill
|
||||
let mut instance = VectorData::from_subpath(subpath);
|
||||
instance.style.set_fill(Fill::Solid(Color::BLACK));
|
||||
let mut element = VectorData::from_subpath(subpath);
|
||||
element.style.set_fill(Fill::Solid(Color::BLACK));
|
||||
|
||||
Instance { instance, ..Default::default() }
|
||||
TableRow { element, ..Default::default() }
|
||||
};
|
||||
|
||||
// Apply the parent group's transform to each element of raster data
|
||||
image.instance_ref_iter().map(|instance| make_instance(*element.transform * *instance.transform)).collect::<Vec<_>>()
|
||||
image.iter_ref().map(|row| make_row(*element.transform * *row.transform)).collect::<Vec<_>>()
|
||||
}
|
||||
GraphicElement::RasterDataGPU(image) => {
|
||||
let make_instance = |transform| {
|
||||
let make_row = |transform| {
|
||||
// Convert the image frame into a rectangular subpath with the image's transform
|
||||
let mut subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
|
||||
subpath.apply_transform(transform);
|
||||
|
||||
// Create a vector data table row from the rectangular subpath, with a default black fill
|
||||
let mut instance = VectorData::from_subpath(subpath);
|
||||
instance.style.set_fill(Fill::Solid(Color::BLACK));
|
||||
let mut element = VectorData::from_subpath(subpath);
|
||||
element.style.set_fill(Fill::Solid(Color::BLACK));
|
||||
|
||||
Instance { instance, ..Default::default() }
|
||||
TableRow { element, ..Default::default() }
|
||||
};
|
||||
|
||||
// Apply the parent group's transform to each element of raster data
|
||||
image.instance_ref_iter().map(|instance| make_instance(*element.transform * *instance.transform)).collect::<Vec<_>>()
|
||||
image.iter_ref().map(|row| make_row(*element.transform * *row.transform)).collect::<Vec<_>>()
|
||||
}
|
||||
GraphicElement::GraphicGroup(mut graphic_group) => {
|
||||
// Apply the parent group's transform to each element of inner group
|
||||
for sub_element in graphic_group.instance_mut_iter() {
|
||||
for sub_element in graphic_group.iter_mut() {
|
||||
*sub_element.transform = *element.transform * *sub_element.transform;
|
||||
}
|
||||
|
||||
// Recursively flatten the inner group into vector data
|
||||
let unioned = boolean_operation_on_vector_data_table(flatten_vector_data(&graphic_group).instance_ref_iter(), BooleanOperation::Union);
|
||||
let unioned = boolean_operation_on_vector_data_table(flatten_vector_data(&graphic_group).iter_ref(), BooleanOperation::Union);
|
||||
|
||||
unioned.instance_iter().collect::<Vec<_>>()
|
||||
unioned.iter().collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user