mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Fix most known issues with migrations failing to open documents from the past year (#3148)
This commit is contained in:
parent
89c9cf1352
commit
a2c0693038
18 changed files with 313 additions and 62 deletions
|
|
@ -1786,7 +1786,7 @@ impl DocumentMessageHandler {
|
|||
pub fn deserialize_document(serialized_content: &str) -> Result<Self, EditorError> {
|
||||
let document_message_handler = serde_json::from_str::<DocumentMessageHandler>(serialized_content)
|
||||
.or_else(|e| {
|
||||
log::warn!("failed to directly load document with the following error: {e}. Trying old DocumentMessageHandler");
|
||||
log::warn!("Failed to directly load document with the following error: {e}. Trying old DocumentMessageHandler.");
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OldDocumentMessageHandler {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,13 @@ pub enum EditorError {
|
|||
#[error("The operation caused a document error:\n{0:?}")]
|
||||
Document(String),
|
||||
|
||||
#[error("This document was created in an older version of the editor.\n\nBackwards compatibility is, regrettably, not present in the current alpha release.\n\nTechnical details:\n{0:?}")]
|
||||
#[error(
|
||||
"This document was created in an older version of the editor.\n\
|
||||
\n\
|
||||
Full backwards compatibility is not guaranteed in the current alpha release.\n\
|
||||
\n\
|
||||
If this document is critical, ask for support in Graphite's Discord community."
|
||||
)]
|
||||
DocumentDeserialization(String),
|
||||
|
||||
#[error("{0}")]
|
||||
|
|
|
|||
|
|
@ -568,7 +568,7 @@ impl NodeNetworkInterface {
|
|||
let skip_footprint = 1;
|
||||
|
||||
let Some(input_type) = std::iter::once(node_types.call_argument.clone()).chain(node_types.inputs.clone()).nth(input_index + skip_footprint) else {
|
||||
log::error!("Could not get type for {node_id_path:?}, input: {input_index}");
|
||||
// log::warn!("Could not get type for {node_id_path:?}, input: {input_index}");
|
||||
return (concrete!(()), TypeSource::Error("could not get the protonode's input"));
|
||||
};
|
||||
|
||||
|
|
@ -2629,7 +2629,7 @@ impl NodeNetworkInterface {
|
|||
InputConnector::Node { node_id, input_index } => {
|
||||
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { return };
|
||||
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
|
||||
log::error!("Node metadata must exist on node: {input:?}");
|
||||
// log::warn!("Node metadata must exist on node: {input:?}");
|
||||
return;
|
||||
};
|
||||
let wire_update = WirePathUpdate {
|
||||
|
|
@ -2721,7 +2721,7 @@ impl NodeNetworkInterface {
|
|||
return;
|
||||
};
|
||||
let Some(input_metadata) = node_metadata.persistent_metadata.input_metadata.get_mut(*input_index) else {
|
||||
log::error!("Node metadata must exist on node: {input:?}");
|
||||
// log::warn!("Node metadata must exist on node: {input:?}");
|
||||
return;
|
||||
};
|
||||
input_metadata.transient_metadata.wire = TransientMetadata::Unloaded;
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
|||
node: graphene_std::math_nodes::root::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::RootNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::absolute_value::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::AbsoluteValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::logarithm::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::LogarithmNode"],
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ pub enum PortfolioMessage {
|
|||
document_is_saved: bool,
|
||||
document_serialized_content: String,
|
||||
to_front: bool,
|
||||
select_after_open: bool,
|
||||
},
|
||||
ToggleResetNodesToDefinitionsOnOpen,
|
||||
PasteIntoFolder {
|
||||
|
|
|
|||
|
|
@ -422,17 +422,16 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||
document_path,
|
||||
document_serialized_content,
|
||||
} => {
|
||||
let document_id = DocumentId(generate_uuid());
|
||||
responses.add(PortfolioMessage::OpenDocumentFileWithId {
|
||||
document_id,
|
||||
document_id: DocumentId(generate_uuid()),
|
||||
document_name,
|
||||
document_path,
|
||||
document_is_auto_saved: false,
|
||||
document_is_saved: true,
|
||||
document_serialized_content,
|
||||
to_front: false,
|
||||
select_after_open: true,
|
||||
});
|
||||
responses.add(PortfolioMessage::SelectDocument { document_id });
|
||||
}
|
||||
PortfolioMessage::ToggleResetNodesToDefinitionsOnOpen => {
|
||||
self.reset_node_definitions_on_open = !self.reset_node_definitions_on_open;
|
||||
|
|
@ -446,6 +445,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||
document_is_saved,
|
||||
document_serialized_content,
|
||||
to_front,
|
||||
select_after_open,
|
||||
} => {
|
||||
// Upgrade the document being opened to use fresh copies of all nodes
|
||||
let reset_node_definitions_on_open = reset_node_definitions_on_open || document_migration_reset_node_definition(&document_serialized_content);
|
||||
|
|
@ -540,6 +540,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||
|
||||
// Load the document into the portfolio so it opens in the editor
|
||||
self.load_document(document, document_id, self.layers_panel_open, responses, to_front);
|
||||
|
||||
if select_after_open {
|
||||
responses.add(PortfolioMessage::SelectDocument { document_id });
|
||||
}
|
||||
}
|
||||
PortfolioMessage::PasteIntoFolder { clipboard, parent, insert_index } => {
|
||||
let mut all_new_ids = Vec::new();
|
||||
|
|
@ -954,14 +958,15 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||
}
|
||||
PortfolioMessage::SubmitGraphRender { document_id, ignore_hash } => {
|
||||
let node_to_inspect = self.node_to_inspect();
|
||||
let result = self.executor.submit_node_graph_evaluation(
|
||||
self.documents.get_mut(&document_id).expect("Tried to render non-existent document"),
|
||||
document_id,
|
||||
ipp.viewport_bounds.size().as_uvec2(),
|
||||
timing_information,
|
||||
node_to_inspect,
|
||||
ignore_hash,
|
||||
);
|
||||
let Some(document) = self.documents.get_mut(&document_id) else {
|
||||
log::error!("Tried to render non-existent document");
|
||||
return;
|
||||
};
|
||||
let viewport_resolution = ipp.viewport_bounds.size().as_uvec2();
|
||||
|
||||
let result = self
|
||||
.executor
|
||||
.submit_node_graph_evaluation(document, document_id, viewport_resolution, timing_information, node_to_inspect, ignore_hash);
|
||||
|
||||
match result {
|
||||
Err(description) => {
|
||||
|
|
@ -1173,7 +1178,7 @@ impl PortfolioMessageHandler {
|
|||
|
||||
/// Returns an iterator over the open documents in order.
|
||||
pub fn ordered_document_iterator(&self) -> impl Iterator<Item = &DocumentMessageHandler> {
|
||||
self.document_ids.iter().map(|id| self.documents.get(id).expect("document id was not found in the document hashmap"))
|
||||
self.document_ids.iter().map(|id| self.documents.get(id).expect("Document id was not found in the document hashmap"))
|
||||
}
|
||||
|
||||
fn document_index(&self, document_id: DocumentId) -> usize {
|
||||
|
|
|
|||
|
|
@ -466,6 +466,7 @@ impl EditorHandle {
|
|||
document_is_saved,
|
||||
document_serialized_content,
|
||||
to_front,
|
||||
select_after_open: false,
|
||||
};
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,23 @@ use std::hash::Hasher;
|
|||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
// TODO: This is a temporary hack, be sure to not reuse this when the brush is being rewritten.
|
||||
// TODO: This is a temporary hack, be sure to not reuse this when the brush system is replaced/rewritten.
|
||||
static NEXT_BRUSH_CACHE_IMPL_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[derive(Clone, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
struct BrushCacheImpl {
|
||||
#[serde(default = "new_unique_id")]
|
||||
unique_id: u64,
|
||||
// The full previous input that was cached.
|
||||
#[serde(default)]
|
||||
prev_input: Vec<BrushStroke>,
|
||||
|
||||
// The strokes that have been fully processed and blended into the background.
|
||||
#[serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame_row")]
|
||||
#[serde(default, deserialize_with = "graphene_core::raster::image::migrate_image_frame_row")]
|
||||
background: TableRow<Raster<CPU>>,
|
||||
#[serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame_row")]
|
||||
#[serde(default, deserialize_with = "graphene_core::raster::image::migrate_image_frame_row")]
|
||||
blended_image: TableRow<Raster<CPU>>,
|
||||
#[serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame_row")]
|
||||
#[serde(default, deserialize_with = "graphene_core::raster::image::migrate_image_frame_row")]
|
||||
last_stroke_texture: TableRow<Raster<CPU>>,
|
||||
|
||||
// A cache for brush textures.
|
||||
|
|
@ -98,7 +100,7 @@ impl BrushCacheImpl {
|
|||
impl Default for BrushCacheImpl {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unique_id: NEXT_BRUSH_CACHE_IMPL_ID.fetch_add(1, Ordering::SeqCst),
|
||||
unique_id: new_unique_id(),
|
||||
prev_input: Vec::new(),
|
||||
background: Default::default(),
|
||||
blended_image: Default::default(),
|
||||
|
|
@ -120,6 +122,10 @@ impl Hash for BrushCacheImpl {
|
|||
}
|
||||
}
|
||||
|
||||
fn new_unique_id() -> u64 {
|
||||
NEXT_BRUSH_CACHE_IMPL_ID.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct BrushPlan {
|
||||
pub strokes: Vec<BrushStroke>,
|
||||
|
|
|
|||
|
|
@ -68,13 +68,22 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
|
|||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EitherFormat {
|
||||
enum ArtboardFormat {
|
||||
ArtboardGroup(ArtboardGroup),
|
||||
OldArtboardTable(OldTable<Artboard>),
|
||||
ArtboardTable(Table<Artboard>),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::ArtboardGroup(artboard_group) => {
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OldTable<T> {
|
||||
#[serde(alias = "instances", alias = "instance")]
|
||||
element: Vec<T>,
|
||||
transform: Vec<DAffine2>,
|
||||
alpha_blending: Vec<AlphaBlending>,
|
||||
}
|
||||
|
||||
Ok(match ArtboardFormat::deserialize(deserializer)? {
|
||||
ArtboardFormat::ArtboardGroup(artboard_group) => {
|
||||
let mut table = Table::new();
|
||||
for (artboard, source_node_id) in artboard_group.artboards {
|
||||
table.push(TableRow {
|
||||
|
|
@ -86,7 +95,18 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
|
|||
}
|
||||
table
|
||||
}
|
||||
EitherFormat::ArtboardTable(artboard_table) => artboard_table,
|
||||
ArtboardFormat::OldArtboardTable(old_table) => old_table
|
||||
.element
|
||||
.into_iter()
|
||||
.zip(old_table.transform.into_iter().zip(old_table.alpha_blending))
|
||||
.map(|(element, (transform, alpha_blending))| TableRow {
|
||||
element,
|
||||
transform,
|
||||
alpha_blending,
|
||||
source_node_id: None,
|
||||
})
|
||||
.collect(),
|
||||
ArtboardFormat::ArtboardTable(artboard_table) => artboard_table,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use crate::context::{CloneVarArgs, Context, ContextFeatures, Ctx, ExtractAll};
|
|||
use crate::gradient::GradientStops;
|
||||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::table::Table;
|
||||
use crate::transform::Footprint;
|
||||
use crate::uuid::NodeId;
|
||||
use crate::vector::Vector;
|
||||
use crate::{Graphic, OwnedContextImpl};
|
||||
|
|
@ -24,6 +25,7 @@ async fn context_modification<T>(
|
|||
Context -> f64,
|
||||
Context -> String,
|
||||
Context -> DAffine2,
|
||||
Context -> Footprint,
|
||||
Context -> DVec2,
|
||||
Context -> Vec<DVec2>,
|
||||
Context -> Vec<NodeId>,
|
||||
|
|
|
|||
|
|
@ -507,15 +507,34 @@ pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Res
|
|||
elements: Vec<(Graphic, Option<NodeId>)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OlderTable<T> {
|
||||
id: Vec<u64>,
|
||||
#[serde(alias = "instances", alias = "instance")]
|
||||
element: Vec<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OldTable<T> {
|
||||
id: Vec<u64>,
|
||||
#[serde(alias = "instances", alias = "instance")]
|
||||
element: Vec<T>,
|
||||
transform: Vec<DAffine2>,
|
||||
alpha_blending: Vec<AlphaBlending>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EitherFormat {
|
||||
enum GraphicFormat {
|
||||
OldGraphicGroup(OldGraphicGroup),
|
||||
OlderTableOldGraphicGroup(OlderTable<OldGraphicGroup>),
|
||||
OldTableOldGraphicGroup(OldTable<OldGraphicGroup>),
|
||||
OldTableGraphicGroup(OldTable<GraphicGroup>),
|
||||
Table(serde_json::Value),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::OldGraphicGroup(old) => {
|
||||
Ok(match GraphicFormat::deserialize(deserializer)? {
|
||||
GraphicFormat::OldGraphicGroup(old) => {
|
||||
let mut graphic_table = Table::new();
|
||||
for (graphic, source_node_id) in old.elements {
|
||||
graphic_table.push(TableRow {
|
||||
|
|
@ -527,7 +546,43 @@ pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Res
|
|||
}
|
||||
graphic_table
|
||||
}
|
||||
EitherFormat::Table(value) => {
|
||||
GraphicFormat::OlderTableOldGraphicGroup(old) => old
|
||||
.element
|
||||
.into_iter()
|
||||
.flat_map(|element| {
|
||||
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
|
||||
element: graphic,
|
||||
transform: element.transform,
|
||||
alpha_blending: element.alpha_blending,
|
||||
source_node_id,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
GraphicFormat::OldTableOldGraphicGroup(old) => old
|
||||
.element
|
||||
.into_iter()
|
||||
.flat_map(|element| {
|
||||
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
|
||||
element: graphic,
|
||||
transform: element.transform,
|
||||
alpha_blending: element.alpha_blending,
|
||||
source_node_id,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
GraphicFormat::OldTableGraphicGroup(old) => old
|
||||
.element
|
||||
.into_iter()
|
||||
.flat_map(|element| {
|
||||
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
|
||||
element: graphic,
|
||||
transform: Default::default(),
|
||||
alpha_blending: Default::default(),
|
||||
source_node_id,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
GraphicFormat::Table(value) => {
|
||||
// Try to deserialize as either table format
|
||||
if let Ok(old_table) = serde_json::from_value::<Table<GraphicGroup>>(value.clone()) {
|
||||
let mut graphic_table = Table::new();
|
||||
|
|
|
|||
|
|
@ -69,21 +69,21 @@ pub fn migrate_color<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resul
|
|||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EitherFormat {
|
||||
enum ColorFormat {
|
||||
Color(Color),
|
||||
OptionalColor(Option<Color>),
|
||||
ColorTable(Table<Color>),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::Color(color) => Table::new_from_element(color),
|
||||
EitherFormat::OptionalColor(color) => {
|
||||
Ok(match ColorFormat::deserialize(deserializer)? {
|
||||
ColorFormat::Color(color) => Table::new_from_element(color),
|
||||
ColorFormat::OptionalColor(color) => {
|
||||
if let Some(color) = color {
|
||||
Table::new_from_element(color)
|
||||
} else {
|
||||
Table::new()
|
||||
}
|
||||
}
|
||||
EitherFormat::ColorTable(color_table) => color_table,
|
||||
ColorFormat::ColorTable(color_table) => color_table,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,7 +218,6 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
||||
enum RasterFrame {
|
||||
/// A CPU-based bitmap image with a finite position and extent, equivalent to the SVG <image> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
|
||||
ImageFrame(Table<Image<Color>>),
|
||||
}
|
||||
impl<'de> serde::Deserialize<'de> for RasterFrame {
|
||||
|
|
@ -236,9 +235,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub enum GraphicElement {
|
||||
/// Equivalent to the SVG <g> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
|
||||
GraphicGroup(Table<GraphicElement>),
|
||||
/// A vector shape, equivalent to the SVG <path> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
|
||||
VectorData(Table<Vector>),
|
||||
RasterFrame(RasterFrame),
|
||||
}
|
||||
|
|
@ -283,11 +280,73 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
enum FormatVersions {
|
||||
Image(Image<Color>),
|
||||
OldImageFrame(OldImageFrame<Color>),
|
||||
OlderImageFrameTable(OlderTable<ImageFrame<Color>>),
|
||||
OldImageFrameTable(OldTable<ImageFrame<Color>>),
|
||||
OldImageTable(OldTable<Image<Color>>),
|
||||
OldRasterTable(OldTable<Raster<CPU>>),
|
||||
ImageFrameTable(Table<ImageFrame<Color>>),
|
||||
ImageTable(Table<Image<Color>>),
|
||||
RasterTable(Table<Raster<CPU>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OldTable<T> {
|
||||
#[serde(alias = "instances", alias = "instance")]
|
||||
element: Vec<T>,
|
||||
transform: Vec<DAffine2>,
|
||||
alpha_blending: Vec<AlphaBlending>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OlderTable<T> {
|
||||
id: Vec<u64>,
|
||||
#[serde(alias = "instances", alias = "instance")]
|
||||
element: Vec<T>,
|
||||
}
|
||||
|
||||
fn from_image_table(table: Table<Image<Color>>) -> Table<Raster<CPU>> {
|
||||
Table::new_from_element(Raster::new_cpu(table.iter().next().unwrap().element.clone()))
|
||||
}
|
||||
|
||||
fn old_table_to_new_table<T>(old_table: OldTable<T>) -> Table<T> {
|
||||
old_table
|
||||
.element
|
||||
.into_iter()
|
||||
.zip(old_table.transform.into_iter().zip(old_table.alpha_blending))
|
||||
.map(|(element, (transform, alpha_blending))| TableRow {
|
||||
element,
|
||||
transform,
|
||||
alpha_blending,
|
||||
source_node_id: None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn older_table_to_new_table<T>(old_table: OlderTable<T>) -> Table<T> {
|
||||
old_table
|
||||
.element
|
||||
.into_iter()
|
||||
.map(|element| TableRow {
|
||||
element,
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
source_node_id: None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn from_image_frame_table(image_frame: Table<ImageFrame<Color>>) -> Table<Raster<CPU>> {
|
||||
Table::new_from_element(Raster::new_cpu(
|
||||
image_frame
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap_or(Table::new_from_element(ImageFrame::default()).iter().next().unwrap())
|
||||
.element
|
||||
.image
|
||||
.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||
FormatVersions::Image(image) => Table::new_from_element(Raster::new_cpu(image)),
|
||||
FormatVersions::OldImageFrame(OldImageFrame { image, transform, alpha_blending }) => {
|
||||
|
|
@ -296,16 +355,12 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
*image_frame_table.iter_mut().next().unwrap().alpha_blending = alpha_blending;
|
||||
image_frame_table
|
||||
}
|
||||
FormatVersions::ImageFrameTable(image_frame) => Table::new_from_element(Raster::new_cpu(
|
||||
image_frame
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap_or(Table::new_from_element(ImageFrame::default()).iter().next().unwrap())
|
||||
.element
|
||||
.image
|
||||
.clone(),
|
||||
)),
|
||||
FormatVersions::ImageTable(table) => Table::new_from_element(Raster::new_cpu(table.iter().next().unwrap().element.clone())),
|
||||
FormatVersions::OlderImageFrameTable(old_table) => from_image_frame_table(older_table_to_new_table(old_table)),
|
||||
FormatVersions::OldImageFrameTable(old_table) => from_image_frame_table(old_table_to_new_table(old_table)),
|
||||
FormatVersions::OldImageTable(old_table) => from_image_table(old_table_to_new_table(old_table)),
|
||||
FormatVersions::OldRasterTable(old_table) => old_table_to_new_table(old_table),
|
||||
FormatVersions::ImageFrameTable(image_frame) => from_image_frame_table(image_frame),
|
||||
FormatVersions::ImageTable(table) => from_image_table(table),
|
||||
FormatVersions::RasterTable(table) => table,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -306,7 +306,10 @@ impl SegmentDomain {
|
|||
}
|
||||
|
||||
pub fn push(&mut self, id: SegmentId, start: usize, end: usize, handles: BezierHandles, stroke: StrokeId) {
|
||||
debug_assert!(!self.id.contains(&id), "Tried to push an existing point to a point domain");
|
||||
#[cfg(debug_assertions)]
|
||||
if self.id.contains(&id) {
|
||||
warn!("Tried to push an existing point to a point domain");
|
||||
}
|
||||
|
||||
self.id.push(id);
|
||||
self.start_point.push(start);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ pub use super::vector_modification::*;
|
|||
use crate::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use crate::math::quad::Quad;
|
||||
use crate::subpath::{BezierHandles, ManipulatorGroup, Subpath};
|
||||
use crate::table::Table;
|
||||
use crate::table::{Table, TableRow};
|
||||
use crate::transform::Transform;
|
||||
use crate::vector::click_target::{ClickTargetType, FreePoint};
|
||||
use crate::vector::misc::{HandleId, ManipulatorPointId};
|
||||
|
|
@ -490,18 +490,35 @@ pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resu
|
|||
pub upstream_graphic_group: Option<Table<Graphic>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OldTable<T> {
|
||||
#[serde(alias = "instances", alias = "instance")]
|
||||
element: Vec<T>,
|
||||
transform: Vec<DAffine2>,
|
||||
alpha_blending: Vec<AlphaBlending>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct OlderTable<T> {
|
||||
id: Vec<u64>,
|
||||
#[serde(alias = "instances", alias = "instance")]
|
||||
element: Vec<T>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum EitherFormat {
|
||||
enum VectorFormat {
|
||||
Vector(Vector),
|
||||
OldVectorData(OldVectorData),
|
||||
OldVectorTable(OldTable<Vector>),
|
||||
OlderVectorTable(OlderTable<Vector>),
|
||||
VectorTable(Table<Vector>),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::Vector(vector) => Table::new_from_element(vector),
|
||||
EitherFormat::OldVectorData(old) => {
|
||||
Ok(match VectorFormat::deserialize(deserializer)? {
|
||||
VectorFormat::Vector(vector) => Table::new_from_element(vector),
|
||||
VectorFormat::OldVectorData(old) => {
|
||||
let mut vector_table = Table::new_from_element(Vector {
|
||||
style: old.style,
|
||||
colinear_manipulators: old.colinear_manipulators,
|
||||
|
|
@ -514,7 +531,19 @@ pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Resu
|
|||
*vector_table.iter_mut().next().unwrap().alpha_blending = old.alpha_blending;
|
||||
vector_table
|
||||
}
|
||||
EitherFormat::VectorTable(vector_table) => vector_table,
|
||||
VectorFormat::OlderVectorTable(older_table) => older_table.element.into_iter().map(|element| TableRow { element, ..Default::default() }).collect(),
|
||||
VectorFormat::OldVectorTable(old_table) => old_table
|
||||
.element
|
||||
.into_iter()
|
||||
.zip(old_table.transform.into_iter().zip(old_table.alpha_blending))
|
||||
.map(|(element, (transform, alpha_blending))| TableRow {
|
||||
element,
|
||||
transform,
|
||||
alpha_blending,
|
||||
source_node_id: None,
|
||||
})
|
||||
.collect(),
|
||||
VectorFormat::VectorTable(vector_table) => vector_table,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub struct DocumentNode {
|
|||
#[cfg_attr(target_family = "wasm", serde(alias = "outputs"))]
|
||||
pub inputs: Vec<NodeInput>,
|
||||
/// Type of the argument which this node can be evaluated with.
|
||||
#[serde(alias = "manual_composition", default)]
|
||||
#[serde(default, alias = "manual_composition", deserialize_with = "migrate_call_argument")]
|
||||
pub call_argument: Type,
|
||||
// A nested document network or a proto-node identifier.
|
||||
pub implementation: DocumentNodeImplementation,
|
||||
|
|
@ -57,12 +57,12 @@ pub struct DocumentNode {
|
|||
/// However sometimes this is not desirable, for example in the case of a [`graphene_core::memo::MonitorNode`] that needs to be accessed outside of the graph.
|
||||
#[serde(default)]
|
||||
pub skip_deduplication: bool,
|
||||
/// The path to this node and its inputs and outputs as of when [`NodeNetwork::generate_node_paths`] was called.
|
||||
#[serde(skip)]
|
||||
pub original_location: OriginalLocation,
|
||||
/// List of Extract and Inject annotations for the Context.
|
||||
#[serde(default)]
|
||||
pub context_features: ContextDependencies,
|
||||
/// The path to this node and its inputs and outputs as of when [`NodeNetwork::generate_node_paths`] was called.
|
||||
#[serde(skip)]
|
||||
pub original_location: OriginalLocation,
|
||||
}
|
||||
|
||||
/// Represents the original location of a node input/output when [`NodeNetwork::generate_node_paths`] was called, allowing the types and errors to be derived.
|
||||
|
|
@ -1105,6 +1105,22 @@ impl<'a> Iterator for RecursiveNodeIter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn migrate_call_argument<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Type, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum CallArg {
|
||||
New(Type),
|
||||
Old(Option<Type>),
|
||||
}
|
||||
|
||||
Ok(match CallArg::deserialize(deserializer)? {
|
||||
CallArg::New(ty) => ty,
|
||||
CallArg::Old(ty) => ty.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ tagged_value! {
|
|||
#[serde(alias = "VectorData")]
|
||||
Vector(Table<Vector>),
|
||||
#[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")]
|
||||
#[serde(alias = "ImageFrame", alias = "RasterData", alias = "Image")]
|
||||
Raster(Table<Raster<CPU>>),
|
||||
#[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")]
|
||||
|
|
@ -361,7 +361,7 @@ impl TaggedValue {
|
|||
x if x == TypeId::of::<DVec2>() => to_dvec2(string).map(TaggedValue::DVec2)?,
|
||||
x if x == TypeId::of::<bool>() => FromStr::from_str(string).map(TaggedValue::Bool).ok()?,
|
||||
x if x == TypeId::of::<Table<Color>>() => to_color(string).map(|color| TaggedValue::Color(Table::new_from_element(color)))?,
|
||||
x if x == TypeId::of::<Color>() => to_color(string).map(|color| TaggedValue::ColorNotInTable(color))?,
|
||||
x if x == TypeId::of::<Color>() => to_color(string).map(TaggedValue::ColorNotInTable)?,
|
||||
x if x == TypeId::of::<Option<Color>>() => TaggedValue::ColorNotInTable(to_color(string)?),
|
||||
x if x == TypeId::of::<Fill>() => to_color(string).map(|color| TaggedValue::Fill(Fill::solid(color)))?,
|
||||
x if x == TypeId::of::<ReferencePoint>() => to_reference_point(string).map(TaggedValue::ReferencePoint)?,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use graphene_std::brush::brush_cache::BrushCache;
|
|||
use graphene_std::brush::brush_stroke::BrushStroke;
|
||||
use graphene_std::gradient::GradientStops;
|
||||
use graphene_std::table::Table;
|
||||
use graphene_std::transform::Footprint;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graphene_std::vector::Vector;
|
||||
#[cfg(feature = "gpu")]
|
||||
|
|
@ -153,13 +154,15 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Arc<WasmSurfaceHandle>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WindowHandle]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Option<WgpuSurface>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WindowHandle]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => SurfaceFrame]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => f64]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => f32]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => u32]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => u64]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => DVec2]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => String]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => DAffine2]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Footprint]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]),
|
||||
#[cfg(feature = "gpu")]
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => &WasmEditorApi]),
|
||||
|
|
@ -167,6 +170,51 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
|||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WgpuSurface]),
|
||||
#[cfg(feature = "gpu")]
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Option<f64>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Color]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Option<Color>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => [f64; 4]]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Graphic]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => glam::f32::Vec2]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => glam::f32::Affine2]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Stroke]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Gradient]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::text::Font]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec<BrushStroke>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => BrushCache]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => DocumentNode]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::ContextFeatures]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::curve::Curve]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::transform::Footprint]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Box<graphene_core::vector::VectorModification>]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Fill]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::blending::BlendMode]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::LuminanceCalculation]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::extract_xy::XY]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RedGreenBlue]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RedGreenBlueAlpha]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::animation::RealTimeMode]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::NoiseType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::FractalType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::CellularDistanceFunction]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::CellularReturnType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::DomainWarpType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RelativeAbsolute]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::SelectiveColorChoice]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::GridType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::ArcType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::MergeByDistanceAlgorithm]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::PointSpacingType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeCap]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeJoin]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeAlign]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::PaintOrder]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::FillType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::GradientType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::transform::ReferencePoint]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::CentroidType]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_path_bool::BooleanOperation]),
|
||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::text::TextAlign]),
|
||||
// =================
|
||||
// IMPURE MEMO NODES
|
||||
// =================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue