mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 23:38:06 +08:00
Instance tables refactor part 6: unwrap VectorData and ImageFrame from single-row to multi-row tables (#2684)
* Start refactoring the boolean operations code * Switch to iterators in the boolean operations code * Make boolean operations work on rows of a table, not Vecs of single-row tables * Remove more .transform() * Simplify brush code * Attempt to remove .transform() by using Instance<Image<Color>> in brush code, but a regression is introduced * Improve blend_image_closure * Simplify * Remove leading underscore from type arguments * Remove .transform() from ImageFrameTable<P> and fix Mask node behavior on stencils not fully overlapping its target image * Remove more .one_instance_ref() * Fully remove .one_instance_ref() and improve the 'Combine Channels' node robustness * Fully remove .once_instance_mut() * Fix tests * Remove .one_empty_image() * Make Instances<T>::default() return an empty table for images, but still not yet vector --------- Co-authored-by: hypercube <0hypercube@gmail.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::transform::Transform;
|
||||
use graphene_core::transform::TransformMut;
|
||||
use graphene_core::RasterFrame;
|
||||
use graphene_core::instances::{Instance, InstanceRef};
|
||||
use graphene_core::vector::misc::BooleanOperation;
|
||||
use graphene_core::vector::style::Fill;
|
||||
pub use graphene_core::vector::*;
|
||||
@@ -10,207 +10,245 @@ pub use path_bool as path_bool_lib;
|
||||
use path_bool::{FillRule, PathBooleanOperation};
|
||||
use std::ops::Mul;
|
||||
|
||||
// TODO: Fix boolean ops to work by removing .transform() and .one_instnace_*() calls,
|
||||
// TODO: since before we used a Vec of single-row tables and now we use a single table
|
||||
// TODO: with multiple rows while still assuming a single row for the boolean operations.
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn boolean_operation(_: impl Ctx, group_of_paths: GraphicGroupTable, operation: BooleanOperation) -> VectorDataTable {
|
||||
fn flatten_vector_data(graphic_group_table: &GraphicGroupTable) -> Vec<VectorDataTable> {
|
||||
graphic_group_table
|
||||
.instance_ref_iter()
|
||||
.map(|element| match element.instance.clone() {
|
||||
GraphicElement::VectorData(mut vector_data) => {
|
||||
// Apply the parent group's transform to each element of vector data
|
||||
for sub_vector_data in vector_data.instance_mut_iter() {
|
||||
*sub_vector_data.transform = *element.transform * *sub_vector_data.transform;
|
||||
}
|
||||
|
||||
vector_data
|
||||
}
|
||||
GraphicElement::RasterFrame(mut image) => {
|
||||
// Apply the parent group's transform to each element of raster data
|
||||
match &mut image {
|
||||
graphene_core::RasterFrame::ImageFrame(image) => {
|
||||
for instance in image.instance_mut_iter() {
|
||||
*instance.transform = *element.transform * *instance.transform;
|
||||
}
|
||||
}
|
||||
graphene_core::RasterFrame::TextureFrame(image) => {
|
||||
for instance in image.instance_mut_iter() {
|
||||
*instance.transform = *element.transform * *instance.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(image.transform());
|
||||
|
||||
// Create a vector data table from the rectangular subpath, with a default black fill
|
||||
let mut vector_data = VectorData::from_subpath(subpath);
|
||||
vector_data.style.set_fill(Fill::Solid(Color::BLACK));
|
||||
VectorDataTable::new(vector_data)
|
||||
}
|
||||
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() {
|
||||
*sub_element.transform = *element.transform * *sub_element.transform;
|
||||
}
|
||||
|
||||
// Recursively flatten the inner group into vector data
|
||||
boolean_operation_on_vector_data(&flatten_vector_data(&graphic_group), BooleanOperation::Union)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn subtract<'a>(vector_data: impl Iterator<Item = &'a VectorDataTable>) -> VectorDataTable {
|
||||
let mut vector_data = vector_data.into_iter();
|
||||
let mut result = vector_data.next().cloned().unwrap_or_default();
|
||||
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 = result.transform().inverse() * lower_vector_data.transform();
|
||||
|
||||
let result = result.one_instance_mut().instance;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_subtract(upper_path_string, lower_path_string) };
|
||||
let boolean_operation_result = from_path(&boolean_operation_string);
|
||||
|
||||
result.colinear_manipulators = boolean_operation_result.colinear_manipulators;
|
||||
result.point_domain = boolean_operation_result.point_domain;
|
||||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
|
||||
next_vector_data = vector_data.next();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn boolean_operation_on_vector_data(vector_data_table: &[VectorDataTable], boolean_operation: BooleanOperation) -> VectorDataTable {
|
||||
match boolean_operation {
|
||||
BooleanOperation::Union => {
|
||||
// Reverse vector data so that the result style is the style of the first vector data
|
||||
let mut vector_data_table = vector_data_table.iter().rev();
|
||||
let mut result_vector_data_table = vector_data_table.next().cloned().unwrap_or_default();
|
||||
|
||||
// Loop over all vector data and union it with the result
|
||||
let default = VectorDataTable::default();
|
||||
let mut second_vector_data = Some(vector_data_table.next().unwrap_or(&default));
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let transform_of_lower_into_space_of_upper = result_vector_data_table.transform().inverse() * lower_vector_data.transform();
|
||||
|
||||
let result_vector_data = result_vector_data_table.one_instance_mut().instance;
|
||||
|
||||
let upper_path_string = to_path(result_vector_data, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_union(upper_path_string, lower_path_string) };
|
||||
let boolean_operation_result = from_path(&boolean_operation_string);
|
||||
|
||||
result_vector_data.colinear_manipulators = boolean_operation_result.colinear_manipulators;
|
||||
result_vector_data.point_domain = boolean_operation_result.point_domain;
|
||||
result_vector_data.segment_domain = boolean_operation_result.segment_domain;
|
||||
result_vector_data.region_domain = boolean_operation_result.region_domain;
|
||||
|
||||
second_vector_data = vector_data_table.next();
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
}
|
||||
BooleanOperation::SubtractFront => subtract(vector_data_table.iter()),
|
||||
BooleanOperation::SubtractBack => subtract(vector_data_table.iter().rev()),
|
||||
BooleanOperation::Intersect => {
|
||||
let mut vector_data = vector_data_table.iter().rev();
|
||||
let mut result = vector_data.next().cloned().unwrap_or_default();
|
||||
let default = VectorDataTable::default();
|
||||
let mut second_vector_data = Some(vector_data.next().unwrap_or(&default));
|
||||
|
||||
// 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 = result.transform().inverse() * lower_vector_data.transform();
|
||||
|
||||
let result = result.one_instance_mut().instance;
|
||||
|
||||
let upper_path_string = to_path(result, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, transform_of_lower_into_space_of_upper);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
let boolean_operation_result = from_path(&boolean_operation_string);
|
||||
|
||||
result.colinear_manipulators = boolean_operation_result.colinear_manipulators;
|
||||
result.point_domain = boolean_operation_result.point_domain;
|
||||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
second_vector_data = vector_data.next();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
BooleanOperation::Difference => {
|
||||
let mut vector_data_iter = vector_data_table.iter().rev();
|
||||
let mut any_intersection = VectorDataTable::default();
|
||||
let default = VectorDataTable::default();
|
||||
let mut second_vector_data = Some(vector_data_iter.next().unwrap_or(&default));
|
||||
|
||||
// Find where all vector data intersect at least once
|
||||
while let Some(lower_vector_data) = second_vector_data {
|
||||
let all_other_vector_data = boolean_operation_on_vector_data(&vector_data_table.iter().filter(|v| v != &lower_vector_data).cloned().collect::<Vec<_>>(), BooleanOperation::Union);
|
||||
let all_other_vector_data_instance = all_other_vector_data.one_instance_ref();
|
||||
|
||||
let transform_of_lower_into_space_of_upper = all_other_vector_data.transform().inverse() * lower_vector_data.transform();
|
||||
|
||||
let upper_path_string = to_path(all_other_vector_data_instance.instance, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(lower_vector_data.one_instance_ref().instance, 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 boolean_intersection_result = VectorDataTable::new(from_path(&boolean_intersection_string));
|
||||
*boolean_intersection_result.transform_mut() = *all_other_vector_data_instance.transform;
|
||||
|
||||
boolean_intersection_result.one_instance_mut().instance.style = all_other_vector_data_instance.instance.style.clone();
|
||||
*boolean_intersection_result.one_instance_mut().alpha_blending = *all_other_vector_data_instance.alpha_blending;
|
||||
|
||||
let transform_of_lower_into_space_of_upper = boolean_intersection_result.one_instance_mut().transform.inverse() * any_intersection.transform();
|
||||
|
||||
let upper_path_string = to_path(boolean_intersection_result.one_instance_mut().instance, DAffine2::IDENTITY);
|
||||
let lower_path_string = to_path(any_intersection.one_instance_mut().instance, 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.one_instance_mut().instance = union_result;
|
||||
|
||||
*any_intersection.transform_mut() = boolean_intersection_result.transform();
|
||||
any_intersection.one_instance_mut().instance.style = boolean_intersection_result.one_instance_mut().instance.style.clone();
|
||||
any_intersection.one_instance_mut().alpha_blending = boolean_intersection_result.one_instance_mut().alpha_blending;
|
||||
|
||||
second_vector_data = vector_data_iter.next();
|
||||
}
|
||||
// Subtract the area where they intersect at least once from the union of all vector data
|
||||
let union = boolean_operation_on_vector_data(vector_data_table, BooleanOperation::Union);
|
||||
boolean_operation_on_vector_data(&[union, any_intersection], BooleanOperation::SubtractFront)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The first index is the bottom of the stack
|
||||
let mut result_vector_data_table = boolean_operation_on_vector_data(&flatten_vector_data(&group_of_paths), operation);
|
||||
let mut result_vector_data_table = boolean_operation_on_vector_data_table(flatten_vector_data(&group_of_paths).instance_ref_iter(), operation);
|
||||
|
||||
// Replace the transformation matrix with a mutation of the vector points themselves
|
||||
let result_vector_data_table_transform = result_vector_data_table.transform();
|
||||
*result_vector_data_table.transform_mut() = DAffine2::IDENTITY;
|
||||
let result_vector_data = result_vector_data_table.one_instance_mut().instance;
|
||||
VectorData::transform(result_vector_data, result_vector_data_table_transform);
|
||||
result_vector_data.style.set_stroke_transform(DAffine2::IDENTITY);
|
||||
result_vector_data.upstream_graphic_group = Some(group_of_paths.clone());
|
||||
if let Some(result_vector_data) = result_vector_data_table.instance_mut_iter().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());
|
||||
}
|
||||
|
||||
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 {
|
||||
match boolean_operation {
|
||||
BooleanOperation::Union => union(vector_data),
|
||||
BooleanOperation::SubtractFront => subtract(vector_data),
|
||||
BooleanOperation::SubtractBack => subtract(vector_data.rev()),
|
||||
BooleanOperation::Intersect => intersect(vector_data),
|
||||
BooleanOperation::Difference => difference(vector_data),
|
||||
}
|
||||
}
|
||||
|
||||
fn union<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, VectorData>>) -> VectorDataTable {
|
||||
// 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::empty();
|
||||
result_vector_data_table.push(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");
|
||||
|
||||
// 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()));
|
||||
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 result = &mut first_instance.instance;
|
||||
|
||||
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);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_union(upper_path_string, lower_path_string) };
|
||||
let boolean_operation_result = from_path(&boolean_operation_string);
|
||||
|
||||
result.colinear_manipulators = boolean_operation_result.colinear_manipulators;
|
||||
result.point_domain = boolean_operation_result.point_domain;
|
||||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
|
||||
second_vector_data = vector_data_reversed.next();
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
}
|
||||
|
||||
fn subtract<'a>(vector_data: impl Iterator<Item = InstanceRef<'a, VectorData>>) -> VectorDataTable {
|
||||
let mut vector_data = vector_data.into_iter();
|
||||
|
||||
let mut result_vector_data_table = VectorDataTable::empty();
|
||||
result_vector_data_table.push(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 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 result = &mut first_instance.instance;
|
||||
|
||||
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);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_subtract(upper_path_string, lower_path_string) };
|
||||
let boolean_operation_result = from_path(&boolean_operation_string);
|
||||
|
||||
result.colinear_manipulators = boolean_operation_result.colinear_manipulators;
|
||||
result.point_domain = boolean_operation_result.point_domain;
|
||||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
|
||||
next_vector_data = vector_data.next();
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
}
|
||||
|
||||
fn intersect<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, VectorData>>) -> VectorDataTable {
|
||||
let mut vector_data = vector_data.rev();
|
||||
|
||||
let mut result_vector_data_table = VectorDataTable::empty();
|
||||
result_vector_data_table.push(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 default = Instance::default();
|
||||
let mut second_vector_data = Some(vector_data.next().unwrap_or(default.to_instance_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 result = &mut first_instance.instance;
|
||||
|
||||
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);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let boolean_operation_string = unsafe { boolean_intersect(upper_path_string, lower_path_string) };
|
||||
let boolean_operation_result = from_path(&boolean_operation_string);
|
||||
|
||||
result.colinear_manipulators = boolean_operation_result.colinear_manipulators;
|
||||
result.point_domain = boolean_operation_result.point_domain;
|
||||
result.segment_domain = boolean_operation_result.segment_domain;
|
||||
result.region_domain = boolean_operation_result.region_domain;
|
||||
second_vector_data = vector_data.next();
|
||||
}
|
||||
|
||||
result_vector_data_table
|
||||
}
|
||||
|
||||
fn difference<'a>(vector_data: impl DoubleEndedIterator<Item = InstanceRef<'a, VectorData>> + Clone) -> VectorDataTable {
|
||||
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()));
|
||||
|
||||
// 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 transform_of_lower_into_space_of_upper = first_instance.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);
|
||||
|
||||
#[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 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);
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
let union_result = from_path(&unsafe { boolean_union(upper_path_string, lower_path_string) });
|
||||
any_intersection.instance = union_result;
|
||||
|
||||
any_intersection.transform = boolean_intersection_result.transform;
|
||||
any_intersection.instance.style = boolean_intersection_result.instance.style.clone();
|
||||
any_intersection.alpha_blending = boolean_intersection_result.alpha_blending;
|
||||
|
||||
second_vector_data = vector_data_iter.next();
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
fn flatten_vector_data(graphic_group_table: &GraphicGroupTable) -> VectorDataTable {
|
||||
let mut result_table = VectorDataTable::empty();
|
||||
|
||||
for element in graphic_group_table.instance_ref_iter() {
|
||||
match element.instance.clone() {
|
||||
GraphicElement::VectorData(vector_data) => {
|
||||
// Apply the parent group's transform to each element of vector data
|
||||
for mut sub_vector_data in vector_data.instance_iter() {
|
||||
sub_vector_data.transform = *element.transform * sub_vector_data.transform;
|
||||
|
||||
result_table.push(sub_vector_data);
|
||||
}
|
||||
}
|
||||
GraphicElement::RasterFrame(image) => {
|
||||
let make_instance = |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));
|
||||
|
||||
Instance { instance, ..Default::default() }
|
||||
};
|
||||
|
||||
// Apply the parent group's transform to each element of raster data
|
||||
match image {
|
||||
RasterFrame::ImageFrame(image) => {
|
||||
for instance in image.instance_ref_iter() {
|
||||
result_table.push(make_instance(*element.transform * *instance.transform));
|
||||
}
|
||||
}
|
||||
RasterFrame::TextureFrame(image) => {
|
||||
for instance in image.instance_ref_iter() {
|
||||
result_table.push(make_instance(*element.transform * *instance.transform));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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() {
|
||||
*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);
|
||||
|
||||
for element in unioned.instance_iter() {
|
||||
result_table.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_table
|
||||
}
|
||||
|
||||
fn to_path(vector: &VectorData, transform: DAffine2) -> Vec<path_bool::PathSegment> {
|
||||
let mut path = Vec::new();
|
||||
for subpath in vector.stroke_bezier_paths() {
|
||||
|
||||
Reference in New Issue
Block a user