mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 19:08:05 +08:00
Instance tables refactor part 5: unwrap GraphicGroup as multi-row Instance<GraphicElement> tables and move up transforms (#2363)
* Just group * Partly working but without transforms * Remove Transform/TransformMut from GraphicElement and GraphicGroupTable * Fix layers and flattening * Fix transform group handling on the remaining nodes * Change collect metadata * Add transform on vector data. TODO: Remove duplicate transform * Small code tidying-up * Add concatenate node? * Remove ignore_modifications which is always false * Improve transforms * Mostly fix the nested transform cage angle (except leaf layers and skew) * WIP attempt to integrate skew * Fix nesting bounding box * Avoid setting the transform * Fix stroke transforms * Renderer cleanup * Fix tests for repeated elements not given unique point IDs * Suppress cargo-deny warning * Fix upgrade code for graphic group data * Work around rendering issue in Isometric Fountain --------- Co-authored-by: Adam <adamgerhant@gmail.com> Co-authored-by: hypercube <0hypercube@gmail.com>
This commit is contained in:
@@ -2,14 +2,13 @@ use crate::application_io::{ImageTexture, TextureFrameTable};
|
||||
use crate::instances::Instances;
|
||||
use crate::raster::image::{Image, ImageFrameTable};
|
||||
use crate::raster::BlendMode;
|
||||
use crate::transform::{Transform, TransformMut};
|
||||
use crate::transform::TransformMut;
|
||||
use crate::uuid::NodeId;
|
||||
use crate::vector::{VectorData, VectorDataTable};
|
||||
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||
|
||||
use dyn_any::DynAny;
|
||||
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use glam::{DAffine2, IVec2};
|
||||
use std::hash::Hash;
|
||||
|
||||
@@ -52,83 +51,84 @@ pub fn migrate_graphic_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
||||
transform: DAffine2,
|
||||
alpha_blending: AlphaBlending,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GraphicGroup {
|
||||
elements: Vec<(GraphicElement, Option<NodeId>)>,
|
||||
}
|
||||
pub type OldGraphicGroupTable = Instances<GraphicGroup>;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EitherFormat {
|
||||
GraphicGroup(GraphicGroup),
|
||||
OldGraphicGroup(OldGraphicGroup),
|
||||
GraphicGroupTable(GraphicGroupTable),
|
||||
InstanceTable(serde_json::Value),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::GraphicGroup(graphic_group) => GraphicGroupTable::new(graphic_group),
|
||||
EitherFormat::OldGraphicGroup(old) => {
|
||||
let mut graphic_group_table = GraphicGroupTable::new(GraphicGroup { elements: old.elements });
|
||||
*graphic_group_table.one_instance_mut().transform = old.transform;
|
||||
*graphic_group_table.one_instance_mut().alpha_blending = old.alpha_blending;
|
||||
let mut graphic_group_table = GraphicGroupTable::empty();
|
||||
for (graphic_element, source_node_id) in old.elements {
|
||||
let last = graphic_group_table.push(graphic_element);
|
||||
*last.source_node_id = source_node_id;
|
||||
*last.transform = old.transform;
|
||||
*last.alpha_blending = old.alpha_blending;
|
||||
}
|
||||
graphic_group_table
|
||||
}
|
||||
EitherFormat::GraphicGroupTable(graphic_group_table) => graphic_group_table,
|
||||
EitherFormat::InstanceTable(value) => {
|
||||
// Try to deserialize as either table format
|
||||
if let Ok(old_table) = serde_json::from_value::<OldGraphicGroupTable>(value.clone()) {
|
||||
let mut graphic_group_table = GraphicGroupTable::empty();
|
||||
for instance in old_table.instances() {
|
||||
for (graphic_element, source_node_id) in &instance.instance.elements {
|
||||
let new_row = graphic_group_table.push(graphic_element.clone());
|
||||
*new_row.source_node_id = *source_node_id;
|
||||
*new_row.transform = *instance.transform;
|
||||
*new_row.alpha_blending = *instance.alpha_blending;
|
||||
}
|
||||
}
|
||||
graphic_group_table
|
||||
} else if let Ok(new_table) = serde_json::from_value::<GraphicGroupTable>(value) {
|
||||
new_table
|
||||
} else {
|
||||
return Err(serde::de::Error::custom("Failed to deserialize GraphicGroupTable"));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub type GraphicGroupTable = Instances<GraphicGroup>;
|
||||
// TODO: Rename to GraphicElementTable
|
||||
pub type GraphicGroupTable = Instances<GraphicElement>;
|
||||
|
||||
/// A list of [`GraphicElement`]s
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GraphicGroup {
|
||||
elements: Vec<(GraphicElement, Option<NodeId>)>,
|
||||
}
|
||||
|
||||
impl core::hash::Hash for GraphicGroup {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.elements.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicGroup {
|
||||
pub fn new(elements: Vec<GraphicElement>) -> Self {
|
||||
Self {
|
||||
elements: elements.into_iter().map(|element| (element, None)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GraphicGroup> for GraphicGroupTable {
|
||||
fn from(graphic_group: GraphicGroup) -> Self {
|
||||
Self::new(graphic_group)
|
||||
}
|
||||
}
|
||||
impl From<VectorData> for GraphicGroupTable {
|
||||
fn from(vector_data: VectorData) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::VectorData(VectorDataTable::new(vector_data))]))
|
||||
Self::new(GraphicElement::VectorData(VectorDataTable::new(vector_data)))
|
||||
}
|
||||
}
|
||||
impl From<VectorDataTable> for GraphicGroupTable {
|
||||
fn from(vector_data: VectorDataTable) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::VectorData(vector_data)]))
|
||||
Self::new(GraphicElement::VectorData(vector_data))
|
||||
}
|
||||
}
|
||||
impl From<Image<Color>> for GraphicGroupTable {
|
||||
fn from(image: Image<Color>) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::ImageFrame(ImageFrameTable::new(image)))]))
|
||||
Self::new(GraphicElement::RasterFrame(RasterFrame::ImageFrame(ImageFrameTable::new(image))))
|
||||
}
|
||||
}
|
||||
impl From<ImageFrameTable<Color>> for GraphicGroupTable {
|
||||
fn from(image_frame: ImageFrameTable<Color>) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::ImageFrame(image_frame))]))
|
||||
Self::new(GraphicElement::RasterFrame(RasterFrame::ImageFrame(image_frame)))
|
||||
}
|
||||
}
|
||||
impl From<ImageTexture> for GraphicGroupTable {
|
||||
fn from(image_texture: ImageTexture) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::TextureFrame(TextureFrameTable::new(image_texture)))]))
|
||||
Self::new(GraphicElement::RasterFrame(RasterFrame::TextureFrame(TextureFrameTable::new(image_texture))))
|
||||
}
|
||||
}
|
||||
impl From<TextureFrameTable> for GraphicGroupTable {
|
||||
fn from(texture_frame: TextureFrameTable) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::TextureFrame(texture_frame))]))
|
||||
Self::new(GraphicElement::RasterFrame(RasterFrame::TextureFrame(texture_frame)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,8 +278,8 @@ pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
||||
EitherFormat::ArtboardGroup(artboard_group) => {
|
||||
let mut table = ArtboardGroupTable::empty();
|
||||
for (artboard, source_node_id) in artboard_group.artboards {
|
||||
table.push(artboard);
|
||||
*table.instances_mut().last().unwrap().source_node_id = source_node_id;
|
||||
let pushed = table.push(artboard);
|
||||
*pushed.source_node_id = source_node_id;
|
||||
}
|
||||
table
|
||||
}
|
||||
@@ -290,23 +290,40 @@ pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D)
|
||||
pub type ArtboardGroupTable = Instances<Artboard>;
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn layer(_: impl Ctx, stack: GraphicGroupTable, mut element: GraphicElement, node_path: Vec<NodeId>) -> GraphicGroupTable {
|
||||
let mut stack = stack;
|
||||
|
||||
if stack.transform().matrix2.determinant() != 0. {
|
||||
*element.transform_mut() = stack.transform().inverse() * element.transform();
|
||||
} else {
|
||||
stack.one_instance_mut().instance.clear();
|
||||
*stack.transform_mut() = DAffine2::IDENTITY;
|
||||
}
|
||||
|
||||
async fn layer(_: impl Ctx, mut stack: GraphicGroupTable, element: GraphicElement, node_path: Vec<NodeId>) -> GraphicGroupTable {
|
||||
// Get the penultimate element of the node path, or None if the path is too short
|
||||
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
stack.one_instance_mut().instance.push((element, encapsulating_node_id));
|
||||
let pushed = stack.push(element);
|
||||
*pushed.source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
stack
|
||||
}
|
||||
|
||||
// TODO: Once we have nicely working spreadsheet tables, test this and make it nicely user-facing and move it from "Debug" to "General"
|
||||
#[node_macro::node(category("Debug"))]
|
||||
async fn concatenate<T: Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
GraphicGroupTable,
|
||||
VectorDataTable,
|
||||
ImageFrameTable<Color>,
|
||||
TextureFrameTable,
|
||||
)]
|
||||
from: Instances<T>,
|
||||
#[expose]
|
||||
#[implementations(
|
||||
GraphicGroupTable,
|
||||
VectorDataTable,
|
||||
ImageFrameTable<Color>,
|
||||
TextureFrameTable,
|
||||
)]
|
||||
mut to: Instances<T>,
|
||||
) -> Instances<T> {
|
||||
for instance in from.instances() {
|
||||
to.push_instance(instance);
|
||||
}
|
||||
to
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
async fn to_element<Data: Into<GraphicElement> + 'n>(
|
||||
_: impl Ctx,
|
||||
@@ -337,38 +354,39 @@ async fn to_group<Data: Into<GraphicGroupTable> + 'n>(
|
||||
|
||||
#[node_macro::node(category("General"))]
|
||||
async fn flatten_group(_: impl Ctx, group: GraphicGroupTable, fully_flatten: bool) -> GraphicGroupTable {
|
||||
fn flatten_group(result_group: &mut GraphicGroupTable, current_group_table: GraphicGroupTable, fully_flatten: bool) {
|
||||
let mut collection_group = GraphicGroup::default();
|
||||
let current_group_elements = current_group_table.one_instance().instance.elements.clone();
|
||||
// TODO: Avoid mutable reference, instead return a new GraphicGroupTable?
|
||||
fn flatten_group(output_group_table: &mut GraphicGroupTable, current_group_table: GraphicGroupTable, fully_flatten: bool, recursion_depth: usize) {
|
||||
for current_instance in current_group_table.instances() {
|
||||
let current_element = current_instance.instance.clone();
|
||||
let reference = *current_instance.source_node_id;
|
||||
|
||||
for (element, reference) in current_group_elements {
|
||||
if let GraphicElement::GraphicGroup(mut nested_group_table) = element {
|
||||
*nested_group_table.transform_mut() = nested_group_table.transform() * current_group_table.transform();
|
||||
let recurse = fully_flatten || recursion_depth == 0;
|
||||
|
||||
let mut sub_group_table = GraphicGroupTable::default();
|
||||
if fully_flatten {
|
||||
flatten_group(&mut sub_group_table, nested_group_table, fully_flatten);
|
||||
} else {
|
||||
let nested_group_table_transform = nested_group_table.transform();
|
||||
for (collection_element, _) in &mut nested_group_table.one_instance_mut().instance.elements {
|
||||
*collection_element.transform_mut() = nested_group_table_transform * collection_element.transform();
|
||||
match current_element {
|
||||
// If we're allowed to recurse, flatten any GraphicGroups we encounter
|
||||
GraphicElement::GraphicGroup(mut current_element) if recurse => {
|
||||
// Apply the parent group's transform to all child elements
|
||||
for graphic_element in current_element.instances_mut() {
|
||||
*graphic_element.transform = *current_instance.transform * *graphic_element.transform;
|
||||
}
|
||||
sub_group_table = nested_group_table;
|
||||
}
|
||||
|
||||
collection_group.append(&mut sub_group_table.one_instance_mut().instance.elements);
|
||||
} else {
|
||||
collection_group.push((element, reference));
|
||||
flatten_group(output_group_table, current_element, fully_flatten, recursion_depth + 1);
|
||||
}
|
||||
// Handle any leaf elements we encounter, which can be either non-GraphicGroup elements or GraphicGroups that we don't want to flatten
|
||||
_ => {
|
||||
let pushed = output_group_table.push(current_element);
|
||||
*pushed.source_node_id = reference;
|
||||
// Apply the parent group's transform to the leaf element
|
||||
*pushed.transform = *current_instance.transform * *pushed.transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_group.one_instance_mut().instance.append(&mut collection_group.elements);
|
||||
}
|
||||
|
||||
let mut flat_group = GraphicGroupTable::default();
|
||||
flatten_group(&mut flat_group, group, fully_flatten);
|
||||
let mut output = GraphicGroupTable::default();
|
||||
flatten_group(&mut output, group, fully_flatten, 0);
|
||||
|
||||
flat_group
|
||||
output
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
@@ -411,8 +429,8 @@ async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artb
|
||||
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
|
||||
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
artboards.push(artboard);
|
||||
*artboards.instances_mut().last().unwrap().source_node_id = encapsulating_node_id;
|
||||
let pushed = artboards.push(artboard);
|
||||
*pushed.source_node_id = encapsulating_node_id;
|
||||
|
||||
artboards
|
||||
}
|
||||
@@ -450,46 +468,8 @@ impl From<VectorDataTable> for GraphicElement {
|
||||
GraphicElement::VectorData(vector_data)
|
||||
}
|
||||
}
|
||||
// TODO: Remove this one
|
||||
impl From<GraphicGroup> for GraphicElement {
|
||||
fn from(graphic_group: GraphicGroup) -> Self {
|
||||
GraphicElement::GraphicGroup(GraphicGroupTable::new(graphic_group))
|
||||
}
|
||||
}
|
||||
impl From<GraphicGroupTable> for GraphicElement {
|
||||
fn from(graphic_group: GraphicGroupTable) -> Self {
|
||||
GraphicElement::GraphicGroup(graphic_group)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for GraphicGroup {
|
||||
type Target = Vec<(GraphicElement, Option<NodeId>)>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.elements
|
||||
}
|
||||
}
|
||||
impl DerefMut for GraphicGroup {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.elements
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a helper trait used for the Into Implementation.
|
||||
/// We can't just implement this for all for which from is implemented
|
||||
/// as that would conflict with the implementation for `Self`
|
||||
trait ToGraphicElement: Into<GraphicElement> {}
|
||||
|
||||
impl ToGraphicElement for VectorDataTable {}
|
||||
impl ToGraphicElement for ImageFrameTable<Color> {}
|
||||
impl ToGraphicElement for ImageTexture {}
|
||||
|
||||
impl<T> From<T> for GraphicGroup
|
||||
where
|
||||
T: ToGraphicElement,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
elements: (vec![(value.into(), None)]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user