Merge branch 'master' into operation-tool

This commit is contained in:
0SlowPoke0
2025-08-26 02:40:37 +05:30
committed by GitHub
84 changed files with 642 additions and 1109 deletions
+1 -1
View File
@@ -442,7 +442,7 @@ mod test {
assert_eq!(layers_before_copy.len(), 3);
assert_eq!(layers_after_copy.len(), 6);
println!("{:?} {:?}", layers_after_copy, layers_before_copy);
println!("{layers_after_copy:?} {layers_before_copy:?}");
assert_eq!(layers_after_copy[5], shape_id);
}
@@ -1,9 +1,12 @@
use crate::messages::prelude::*;
use super::app_window_message_handler::AppWindowPlatform;
#[impl_message(Message, AppWindow)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum AppWindowMessage {
AppWindowMinimize,
AppWindowMaximize,
AppWindowUpdatePlatform { platform: AppWindowPlatform },
AppWindowClose,
}
@@ -6,34 +6,34 @@ use graphite_proc_macros::{ExtractField, message_handler_data};
pub struct AppWindowMessageHandler {
platform: AppWindowPlatform,
maximized: bool,
viewport_hole_punch_active: bool,
minimized: bool,
}
#[message_handler_data]
impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
fn process_message(&mut self, message: AppWindowMessage, responses: &mut std::collections::VecDeque<Message>, _: ()) {
match message {
AppWindowMessage::AppWindowMinimize => {
self.platform = if self.platform == AppWindowPlatform::Mac {
AppWindowPlatform::Windows
} else {
AppWindowPlatform::Mac
};
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
}
AppWindowMessage::AppWindowMaximize => {
self.maximized = !self.maximized;
responses.add(FrontendMessage::UpdateMaximized { maximized: self.maximized });
self.viewport_hole_punch_active = !self.viewport_hole_punch_active;
responses.add(FrontendMessage::UpdateViewportHolePunch {
active: self.viewport_hole_punch_active,
responses.add(FrontendMessage::UpdateWindowState {
maximized: self.maximized,
minimized: self.minimized,
});
}
AppWindowMessage::AppWindowClose => {
self.platform = AppWindowPlatform::Web;
AppWindowMessage::AppWindowMinimize => {
self.minimized = !self.minimized;
responses.add(FrontendMessage::UpdateWindowState {
maximized: self.maximized,
minimized: self.minimized,
});
}
AppWindowMessage::AppWindowUpdatePlatform { platform } => {
self.platform = platform;
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
}
AppWindowMessage::AppWindowClose => {
responses.add(FrontendMessage::CloseWindow);
}
}
}
@@ -329,9 +329,11 @@ pub enum FrontendMessage {
UpdatePlatform {
platform: AppWindowPlatform,
},
UpdateMaximized {
UpdateWindowState {
maximized: bool,
minimized: bool,
},
CloseWindow,
UpdateViewportHolePunch {
active: bool,
},
@@ -479,7 +479,7 @@ impl<const LENGTH: usize> Iterator for BitVectorIter<'_, LENGTH> {
impl<const LENGTH: usize> Display for BitVector<LENGTH> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for storage in self.0.iter().rev() {
write!(f, "{:0width$b}", storage, width = STORAGE_SIZE_BITS)?;
write!(f, "{storage:0STORAGE_SIZE_BITS$b}")?;
}
Ok(())
+7 -9
View File
@@ -70,13 +70,11 @@ mod test {
fn print_tree_node(tree: &DebugMessageTree, prefix: &str, is_last: bool, file: &mut std::fs::File) {
// Print the current node
let (branch, child_prefix) = if tree.message_handler_data_fields().is_some() || tree.message_handler_fields().is_some() {
("├── ", format!("{}", prefix))
("├── ", format!("{prefix}"))
} else if is_last {
("└── ", format!("{prefix} "))
} else {
if is_last {
("└── ", format!("{} ", prefix))
} else {
("├── ", format!("{}", prefix))
}
("├── ", format!("{prefix}"))
};
if tree.path().is_empty() {
@@ -101,7 +99,7 @@ mod test {
let is_last_field = i == len - 1;
let branch = if is_last_field { "└── " } else { "├── " };
file.write_all(format!("{}{}{}\n", child_prefix, branch, field).as_bytes()).unwrap();
file.write_all(format!("{child_prefix}{branch}{field}\n").as_bytes()).unwrap();
}
}
@@ -109,9 +107,9 @@ mod test {
if let Some(data) = tree.message_handler_fields() {
let len = data.fields().len();
let (branch, child_prefix) = if tree.message_handler_data_fields().is_some() {
("├── ", format!("{}", prefix))
("├── ", format!("{prefix}"))
} else {
("└── ", format!("{} ", prefix))
("└── ", format!("{prefix} "))
};
const FRONTEND_MESSAGE_STR: &str = "FrontendMessage";
@@ -1782,7 +1782,8 @@ 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(|_| {
.or_else(|e| {
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 {
@@ -91,6 +91,32 @@ pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 {
if let Some(&TaggedValue::DVec2(pivot)) = inputs[5].as_value() { pivot } else { DVec2::splat(0.5) }
}
/// Expand a bounds to avoid div zero errors
fn clamp_bounds(bounds_min: DVec2, mut bounds_max: DVec2) -> [DVec2; 2] {
let bounds_size = bounds_max - bounds_min;
if bounds_size.x < 1e-10 {
bounds_max.x = bounds_min.x + 1.;
}
if bounds_size.y < 1e-10 {
bounds_max.y = bounds_min.y + 1.;
}
[bounds_min, bounds_max]
}
/// Returns corners of all subpaths
fn subpath_bounds(subpaths: &[Subpath<PointId>]) -> [DVec2; 2] {
subpaths
.iter()
.filter_map(|subpath| subpath.bounding_box())
.reduce(|b1, b2| [b1[0].min(b2[0]), b1[1].max(b2[1])])
.unwrap_or_default()
}
/// Returns corners of all subpaths (but expanded to avoid division-by-zero errors)
pub fn nonzero_subpath_bounds(subpaths: &[Subpath<PointId>]) -> [DVec2; 2] {
let [bounds_min, bounds_max] = subpath_bounds(subpaths);
clamp_bounds(bounds_min, bounds_max)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -138,29 +164,3 @@ mod tests {
}
}
}
/// Expand a bounds to avoid div zero errors
fn clamp_bounds(bounds_min: DVec2, mut bounds_max: DVec2) -> [DVec2; 2] {
let bounds_size = bounds_max - bounds_min;
if bounds_size.x < 1e-10 {
bounds_max.x = bounds_min.x + 1.;
}
if bounds_size.y < 1e-10 {
bounds_max.y = bounds_min.y + 1.;
}
[bounds_min, bounds_max]
}
/// Returns corners of all subpaths
fn subpath_bounds(subpaths: &[Subpath<PointId>]) -> [DVec2; 2] {
subpaths
.iter()
.filter_map(|subpath| subpath.bounding_box())
.reduce(|b1, b2| [b1[0].min(b2[0]), b1[1].max(b2[1])])
.unwrap_or_default()
}
/// Returns corners of all subpaths (but expanded to avoid division-by-zero errors)
pub fn nonzero_subpath_bounds(subpaths: &[Subpath<PointId>]) -> [DVec2; 2] {
let [bounds_min, bounds_max] = subpath_bounds(subpaths);
clamp_bounds(bounds_min, bounds_max)
}
@@ -46,7 +46,7 @@ impl NodePropertiesContext<'_> {
return None;
};
widget_override_lambda(*node_id, index, self)
.map_err(|error| log::error!("Error in widget override lambda: {}", error))
.map_err(|error| log::error!("Error in widget override lambda: {error}"))
.ok()
} else {
None
@@ -111,7 +111,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::None, true)],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
@@ -151,19 +151,19 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -233,33 +233,33 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_graphic::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// Secondary (left) input type coercion
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 1)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::wrap_graphic::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// Store the ID of the parent node (which encapsulates this sub-network) in each row we are extending the table with.
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::source_node_id::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// The monitor node is used to display a thumbnail in the UI
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(3), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
..Default::default()
@@ -349,7 +349,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: [
// Ensure this ID is kept in sync with the ID in set_alias so that the name input is kept in sync with the alias
DocumentNode {
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(artboard::create_artboard::IDENTIFIER),
inputs: vec![
NodeInput::network(concrete!(TaggedValue), 1),
@@ -365,7 +365,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)],
implementation: DocumentNodeImplementation::ProtoNode(graphic::source_node_id::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
// The monitor node is used to display a thumbnail in the UI.
@@ -373,12 +373,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![
NodeInput::network(graphene_std::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(Table<Artboard>))), 0),
NodeInput::node(NodeId(2), 0),
@@ -495,13 +495,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: [
DocumentNode {
inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::scope("editor-api"), NodeInput::network(concrete!(String), 1)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::load_resource::IDENTIFIER),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::decode_image::IDENTIFIER),
..Default::default()
},
@@ -568,7 +568,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
..Default::default()
@@ -630,20 +630,20 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::scope("editor-api")],
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
skip_deduplication: true,
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0), NodeInput::network(concrete!(Footprint), 1), NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::rasterize::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
]
@@ -716,7 +716,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
category: "Raster: Pattern",
node_template: NodeTemplate {
document_node: DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::std_nodes::noise_pattern::IDENTIFIER),
inputs: vec![
NodeInput::value(TaggedValue::None, false),
@@ -783,7 +783,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Red), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -792,7 +792,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Green), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -801,7 +801,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Blue), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -810,7 +810,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::value(TaggedValue::RedGreenBlueAlpha(RedGreenBlueAlpha::Alpha), false),
],
implementation: DocumentNodeImplementation::ProtoNode(raster_nodes::adjustments::extract_channel::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -888,13 +888,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Raster<CPU>>), 0), NodeInput::value(TaggedValue::XY(XY::X), false)],
implementation: DocumentNodeImplementation::ProtoNode(extract_xy::extract_xy::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Raster<CPU>>), 0), NodeInput::value(TaggedValue::XY(XY::Y), false)],
implementation: DocumentNodeImplementation::ProtoNode(extract_xy::extract_xy::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -962,7 +962,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(Vec<brush::brush_stroke::BrushStroke>), 1),
NodeInput::network(concrete!(BrushCache), 2),
],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(brush::brush::brush::IDENTIFIER),
..Default::default()
}]
@@ -1013,7 +1013,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
@@ -1032,7 +1032,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
@@ -1054,13 +1054,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
exports: vec![NodeInput::node(NodeId(1), 0)],
nodes: [
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![NodeInput::scope("editor-api")],
implementation: DocumentNodeImplementation::ProtoNode(wgpu_executor::create_gpu_surface::IDENTIFIER),
..Default::default()
},
DocumentNode {
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
..Default::default()
@@ -1126,12 +1126,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Raster<CPU>>), 0), NodeInput::node(NodeId(0), 0)],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(wgpu_executor::texture_upload::upload_texture::IDENTIFIER),
..Default::default()
},
DocumentNode {
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
..Default::default()
@@ -1247,7 +1247,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
@@ -1257,7 +1257,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(graphene_std::vector::VectorModification), 1),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(vector::path_modify::IDENTIFIER),
..Default::default()
},
@@ -1317,7 +1317,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(text::text::IDENTIFIER),
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
inputs: vec![
NodeInput::scope("editor-api"),
NodeInput::value(TaggedValue::String("Lorem ipsum".to_string()), false),
@@ -1427,7 +1427,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(generic!(T), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
skip_deduplication: true,
..Default::default()
},
@@ -1439,7 +1439,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(DVec2), 3),
NodeInput::network(concrete!(DVec2), 4),
],
manual_composition: Some(concrete!(Context)),
call_argument: concrete!(Context),
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::transform::IDENTIFIER),
..Default::default()
},
@@ -1524,25 +1524,25 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0), NodeInput::network(concrete!(vector::style::Fill), 1)],
implementation: DocumentNodeImplementation::ProtoNode(path_bool::boolean_operation::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -1622,7 +1622,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
inputs: vec![NodeInput::network(concrete!(Table<Vector>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(vector::subpath_segment_lengths::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
@@ -1637,25 +1637,25 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::node(NodeId(0), 0),
],
implementation: DocumentNodeImplementation::ProtoNode(vector::sample_polyline::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(3), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -1791,26 +1791,26 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
NodeInput::network(concrete!(f64), 1),
NodeInput::network(concrete!(u32), 2),
],
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
implementation: DocumentNodeImplementation::ProtoNode(vector::poisson_disk_points::IDENTIFIER),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::freeze_real_time::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(2), 0)],
implementation: DocumentNodeImplementation::ProtoNode(transform_nodes::boundless_footprint::IDENTIFIER),
manual_composition: Some(generic!(T)),
call_argument: generic!(T),
..Default::default()
},
]
@@ -1944,10 +1944,10 @@ fn static_input_properties() -> InputProperties {
"string".to_string(),
Box::new(|node_id, index, context| {
let Some(value) = context.network_interface.input_data(&node_id, index, "string_properties", context.selection_network_path) else {
return Err(format!("Could not get string properties for node {}", node_id));
return Err(format!("Could not get string properties for node {node_id}"));
};
let Some(string) = value.as_str() else {
return Err(format!("Could not downcast string properties for node {}", node_id));
return Err(format!("Could not downcast string properties for node {node_id}"));
};
Ok(node_properties::string_properties(string))
}),
@@ -59,8 +59,8 @@ pub(super) fn post_process_nodes(mut custom: Vec<DocumentNodeDefinition>) -> Vec
node_template: NodeTemplate {
document_node: DocumentNode {
inputs,
manual_composition: Some(input_type.clone()),
implementation: DocumentNodeImplementation::ProtoNode(id.clone().into()),
call_argument: (input_type.clone()),
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
visible: true,
skip_deduplication: false,
..Default::default()
@@ -1516,7 +1516,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
let mut nodes = Vec::new();
for node_id in &self.frontend_nodes {
let Some(node_bbox) = network_interface.node_bounding_box(node_id, breadcrumb_network_path) else {
log::error!("Could not get bbox for node: {:?}", node_id);
log::error!("Could not get bbox for node: {node_id:?}");
continue;
};
@@ -1721,7 +1721,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}
NodeGraphMessage::ToggleLocked { node_id } => {
let Some(node_metadata) = network_interface.document_network_metadata().persistent_metadata.node_metadata.get(&node_id) else {
log::error!("Cannot get node {:?} in NodeGraphMessage::ToggleLocked", node_id);
log::error!("Cannot get node {node_id:?} in NodeGraphMessage::ToggleLocked");
return;
};
@@ -2486,7 +2486,7 @@ impl NodeGraphMessageHandler {
data_type: frontend_data_type,
name: "Output 1".to_string(),
description: String::new(),
resolved_type: format!("{:?}", output_type),
resolved_type: format!("{output_type:?}"),
connected_to,
})
} else {
@@ -2518,7 +2518,7 @@ impl NodeGraphMessageHandler {
data_type,
name: output_name,
description: String::new(),
resolved_type: format!("{:?}", output_type),
resolved_type: format!("{output_type:?}"),
connected_to,
});
}
@@ -1156,7 +1156,7 @@ impl OverlayContextInternal {
let move_to = last_point != Some(start_id);
last_point = Some(end_id);
self.bezier_to_path(bezier, row.transform.clone(), move_to, &mut path);
self.bezier_to_path(bezier, *row.transform, move_to, &mut path);
}
// Render the path
@@ -25,6 +25,7 @@ use kurbo::BezPath;
use serde_json::{Value, json};
use std::collections::{HashMap, HashSet, VecDeque};
use std::hash::{DefaultHasher, Hash, Hasher};
use std::ops::Deref;
/// All network modifications should be done through this API, so the fields cannot be public. However, all fields within this struct can be public since it it not possible to have a public mutable reference.
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
@@ -73,10 +74,8 @@ impl NodeNetworkInterface {
fix_network(network);
}
if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation {
if protonode.name.contains("PathModifyNode") {
if node.inputs.len() < 3 {
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath));
}
if protonode.name.contains("PathModifyNode") && node.inputs.len() < 3 {
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath));
}
}
}
@@ -460,13 +459,9 @@ impl NodeNetworkInterface {
/// If the node is not in the hashmap then a default input is found based on the compiled network, using the node_id passed as a parameter
pub fn map_ids(&mut self, mut node_template: NodeTemplate, node_id: &NodeId, new_ids: &HashMap<NodeId, NodeId>, network_path: &[NodeId]) -> NodeTemplate {
for (input_index, input) in node_template.document_node.inputs.iter_mut().enumerate() {
if let &mut NodeInput::Node { node_id: id, output_index, lambda } = input {
if let &mut NodeInput::Node { node_id: id, output_index } = input {
if let Some(&new_id) = new_ids.get(&id) {
*input = NodeInput::Node {
node_id: new_id,
output_index,
lambda,
};
*input = NodeInput::Node { node_id: new_id, output_index };
} else {
// Disconnect node input if it is not connected to another node in new_ids
let tagged_value = TaggedValue::from_type_or_none(&self.input_type(&InputConnector::node(*node_id, input_index), network_path).0);
@@ -547,12 +542,11 @@ impl NodeNetworkInterface {
}
}
DocumentNodeImplementation::ProtoNode(_) => {
// If a node has manual composition, then offset the input index by 1 since the proto node also includes the type of the input passed through manual composition.
let manual_composition_offset = if node.manual_composition.is_some() { 1 } else { 0 };
// Offset the input index by 1 since the proto node also includes the type of the input passed as a call argument.
self.resolved_types
.types
.get(node_id_path.as_slice())
.and_then(|node_types| node_types.inputs.get(input_index + manual_composition_offset).cloned())
.and_then(|node_types| node_types.inputs.get(input_index + 1).cloned())
.map(|node_types| (node_types, TypeSource::Compiled))
}
DocumentNodeImplementation::Extract => None,
@@ -581,7 +575,7 @@ impl NodeNetworkInterface {
return (concrete!(()), TypeSource::Error("could not resolve protonode"));
};
let skip_footprint = if node.manual_composition.is_some() { 1 } else { 0 };
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");
@@ -821,7 +815,7 @@ impl NodeNetworkInterface {
data_type,
name,
description,
resolved_type: format!("{:?}", input_type),
resolved_type: format!("{input_type:?}"),
connected_to,
},
click_target,
@@ -1069,7 +1063,7 @@ impl NodeNetworkInterface {
pub fn reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option<&Option<String>> {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get reference for node: {:?}", node_id);
log::error!("Could not get reference for node: {node_id:?}");
return None;
};
Some(&node_metadata.persistent_metadata.reference)
@@ -1287,7 +1281,7 @@ impl NodeNetworkInterface {
let artboard = self.document_node(&artboard_node_identifier.to_node(), &[]);
let clip_input = artboard.unwrap().inputs.get(5).unwrap();
if let NodeInput::Value { tagged_value, .. } = clip_input {
if tagged_value.clone().into_inner() == TaggedValue::Bool(true) {
if tagged_value.clone().deref() == &TaggedValue::Bool(true) {
return Some(Quad::clip(
self.document_metadata.bounding_box_document(layer).unwrap_or_default(),
self.document_metadata.bounding_box_document(artboard_node_identifier).unwrap_or_default(),
@@ -1499,7 +1493,7 @@ impl NodeNetworkInterface {
let mut node_metadata = DocumentNodeMetadata::default();
node.inputs = old_node.inputs;
node.manual_composition = old_node.manual_composition;
node.call_argument = old_node.manual_composition.unwrap();
node.visible = old_node.visible;
node.skip_deduplication = old_node.skip_deduplication;
node.original_location = old_node.original_location;
@@ -2522,7 +2516,7 @@ impl NodeNetworkInterface {
InputConnector::Node { node_id, input_index } => {
let input_metadata = self.transient_input_metadata(node_id, *input_index, network_path)?;
let TransientMetadata::Loaded(wire) = &input_metadata.wire else {
log::error!("Could not load wire for input: {:?}", input);
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
@@ -2530,7 +2524,7 @@ impl NodeNetworkInterface {
InputConnector::Export(export_index) => {
let network_metadata = self.network_metadata(network_path)?;
let Some(TransientMetadata::Loaded(wire)) = network_metadata.transient_metadata.wires.get(*export_index) else {
log::error!("Could not load wire for input: {:?}", input);
log::error!("Could not load wire for input: {input:?}");
return None;
};
wire.clone()
@@ -2701,12 +2695,12 @@ impl NodeNetworkInterface {
return None;
}
let Some(input_position) = self.get_input_center(&input, network_path) else {
log::error!("Could not get dom rect for wire end in root node: {:?}", input);
log::error!("Could not get dom rect for wire end in root node: {input:?}");
return None;
};
let upstream_output = OutputConnector::node(root_node.node_id, root_node.output_index);
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get dom rect for wire start in root node: {:?}", upstream_output);
log::error!("Could not get dom rect for wire start in root node: {upstream_output:?}");
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
@@ -2733,7 +2727,7 @@ impl NodeNetworkInterface {
/// Returns the vector subpath and a boolean of whether the wire should be thick.
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, bool)> {
let Some(input_position) = self.get_input_center(input, network_path) else {
log::error!("Could not get dom rect for wire end: {:?}", input);
log::error!("Could not get dom rect for wire end: {input:?}");
return None;
};
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
@@ -2741,7 +2735,7 @@ impl NodeNetworkInterface {
return Some((BezPath::new(), false));
};
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
log::error!("Could not get dom rect for wire start: {:?}", upstream_output);
log::error!("Could not get dom rect for wire start: {upstream_output:?}");
return None;
};
let vertical_end = input.node_id().is_some_and(|node_id| self.is_layer(&node_id, network_path) && input.input_index() == 0);
@@ -3357,7 +3351,7 @@ impl NodeNetworkInterface {
self.selected_nodes()
.0
.iter()
.filter(|node| self.is_layer(&node, &[]))
.filter(|node| self.is_layer(node, &[]))
.filter_map(|layer| self.document_metadata.bounding_box_viewport(LayerNodeIdentifier::new(*layer, self)))
.reduce(Quad::combine_bounds)
}
@@ -3366,7 +3360,7 @@ impl NodeNetworkInterface {
self.selected_nodes()
.0
.iter()
.filter(|node| self.is_layer(&node, &[]) && !self.is_locked(&node, &[]))
.filter(|node| self.is_layer(node, &[]) && !self.is_locked(node, &[]))
.filter_map(|layer| self.document_metadata.bounding_box_viewport(LayerNodeIdentifier::new(*layer, self)))
.reduce(Quad::combine_bounds)
}
@@ -4138,7 +4132,7 @@ impl NodeNetworkInterface {
if let DocumentNodeImplementation::Network(network) = &node.implementation {
let number_of_exports = network.exports.len();
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get metadata for node: {:?}", node_id);
log::error!("Could not get metadata for node: {node_id:?}");
return;
};
metadata.persistent_metadata.output_names.resize(number_of_exports, "".to_string());
@@ -4155,7 +4149,7 @@ impl NodeNetworkInterface {
}
/// Keep metadata in sync with the new implementation if this is used by anything other than the upgrade scripts
pub fn set_manual_compostion(&mut self, node_id: &NodeId, network_path: &[NodeId], manual_composition: Option<Type>) {
pub fn set_call_argument(&mut self, node_id: &NodeId, network_path: &[NodeId], call_argument: Type) {
let Some(network) = self.network_mut(network_path) else {
log::error!("Could not get nested network in set_implementation");
return;
@@ -4164,7 +4158,7 @@ impl NodeNetworkInterface {
log::error!("Could not get node in set_implementation");
return;
};
node.manual_composition = manual_composition;
node.call_argument = call_argument;
}
pub fn set_input(&mut self, input_connector: &InputConnector, new_input: NodeInput, network_path: &[NodeId]) {
@@ -20,6 +20,7 @@ use std::collections::HashMap;
const TEXT_REPLACEMENTS: &[(&str, &str)] = &[
("graphene_core::vector::vector_nodes::SamplePointsNode", "graphene_core::vector::SamplePolylineNode"),
("graphene_core::vector::vector_nodes::SubpathSegmentLengthsNode", "graphene_core::vector::SubpathSegmentLengthsNode"),
("\"manual_composition\":null", "\"manual_composition\":{\"Generic\":\"T\"}"),
];
pub struct NodeReplacement<'a> {
@@ -551,7 +552,7 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
let mut default_template = NodeTemplate::default();
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.clone());
document.network_interface.replace_implementation(node_id, &network_path, &mut default_template);
document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
document.network_interface.set_call_argument(node_id, &network_path, graph_craft::Type::Generic("T".into()));
}
}
}
@@ -576,11 +577,11 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
}
}
// Upgrade old nodes to use `Context` instead of `()` or `Footprint` for manual composition
if node.manual_composition == Some(graph_craft::concrete!(())) || node.manual_composition == Some(graph_craft::concrete!(graphene_std::transform::Footprint)) {
// Upgrade old nodes to use `Context` instead of `()` or `Footprint` as their call argument
if node.call_argument == graph_craft::concrete!(()) || node.call_argument == graph_craft::concrete!(graphene_std::transform::Footprint) {
document
.network_interface
.set_manual_compostion(node_id, network_path, graph_craft::concrete!(graphene_std::Context).into());
.set_call_argument(node_id, network_path, graph_craft::concrete!(graphene_std::Context).into());
}
// Only nodes that have not been modified and still refer to a definition can be updated
@@ -1083,8 +1084,8 @@ mod tests {
*hashmap.entry(node.node.clone()).or_default() += 1;
});
let duplicates = hashmap.iter().filter(|(_, count)| **count > 1).map(|(node, _)| &node.name).collect::<Vec<_>>();
if duplicates.len() > 0 {
panic!("Duplicate entries in `NODE_REPLACEMENTS`: {:?}", duplicates);
if !duplicates.is_empty() {
panic!("Duplicate entries in `NODE_REPLACEMENTS`: {duplicates:?}");
}
}
}
@@ -101,14 +101,9 @@ impl Circle {
};
let dimensions = (start - end).abs();
let radius: f64;
// We keep the smaller dimension's scale at 1 and scale the other dimension accordingly
if dimensions.x > dimensions.y {
radius = dimensions.y / 2.;
} else {
radius = dimensions.x / 2.;
}
let radius: f64 = if dimensions.x > dimensions.y { dimensions.y / 2. } else { dimensions.x / 2. };
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 1),
@@ -210,18 +210,18 @@ mod test_line_tool {
async fn get_line_node_inputs(editor: &mut EditorTestUtils) -> Option<(DVec2, DVec2)> {
let document = editor.active_document();
let network_interface = &document.network_interface;
let node_id = network_interface
network_interface
.selected_nodes()
.selected_visible_and_unlocked_layers(network_interface)
.filter_map(|layer| {
let node_inputs = NodeGraphLayer::new(layer, &network_interface).find_node_inputs("Line")?;
let node_inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Line")?;
let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else {
return None;
};
Some((start, end))
})
.next();
node_id
.next()
}
#[tokio::test]
@@ -245,11 +245,7 @@ mod test_line_tool {
editor.new_document().await;
editor.handle_message(NavigationMessage::CanvasZoomSet { zoom_factor: 2. }).await;
editor.handle_message(NavigationMessage::CanvasPan { delta: DVec2::new(100., 50.) }).await;
editor
.handle_message(NavigationMessage::CanvasTiltSet {
angle_radians: (30. as f64).to_radians(),
})
.await;
editor.handle_message(NavigationMessage::CanvasTiltSet { angle_radians: 30_f64.to_radians() }).await;
editor.drag_tool(ToolType::Line, 0., 0., 100., 100., ModifierKeys::empty()).await;
if let Some((start_input, end_input)) = get_line_node_inputs(&mut editor).await {
let document = editor.active_document();
@@ -261,15 +257,11 @@ mod test_line_tool {
assert!(
(start_input - expected_start).length() < 1.,
"Start point should match expected document coordinates. Got {:?}, expected {:?}",
start_input,
expected_start
"Start point should match expected document coordinates. Got {start_input:?}, expected {expected_start:?}"
);
assert!(
(end_input - expected_end).length() < 1.,
"End point should match expected document coordinates. Got {:?}, expected {:?}",
end_input,
expected_end
"End point should match expected document coordinates. Got {end_input:?}, expected {expected_end:?}"
);
} else {
panic!("Line was not created successfully with transformed viewport");
@@ -282,27 +274,19 @@ mod test_line_tool {
editor.new_document().await;
editor.drag_tool(ToolType::Line, 0., 0., 100., 100., ModifierKeys::CONTROL).await;
if let Some((start_input, end_input)) = get_line_node_inputs(&mut editor).await {
match (start_input, end_input) {
(start_input, end_input) => {
let line_vec = end_input - start_input;
let original_angle = line_vec.angle_to(DVec2::X);
editor.drag_tool(ToolType::Line, 0., 0., 200., 50., ModifierKeys::CONTROL).await;
if let Some((updated_start, updated_end)) = get_line_node_inputs(&mut editor).await {
match (updated_start, updated_end) {
(updated_start, updated_end) => {
let updated_line_vec = updated_end - updated_start;
let updated_angle = updated_line_vec.angle_to(DVec2::X);
print!("{:?}", original_angle);
print!("{:?}", updated_angle);
assert!(
line_vec.normalize().dot(updated_line_vec.normalize()).abs() - 1. < 1e-6,
"Line angle should be locked when Ctrl is kept pressed"
);
assert!((updated_start - updated_end).length() > 1., "Line should be able to change length when Ctrl is kept pressed");
}
}
}
}
let line_vec = end_input - start_input;
let original_angle = line_vec.angle_to(DVec2::X);
editor.drag_tool(ToolType::Line, 0., 0., 200., 50., ModifierKeys::CONTROL).await;
if let Some((updated_start, updated_end)) = get_line_node_inputs(&mut editor).await {
let updated_line_vec = updated_end - updated_start;
let updated_angle = updated_line_vec.angle_to(DVec2::X);
print!("{original_angle:?}");
print!("{updated_angle:?}");
assert!(
line_vec.normalize().dot(updated_line_vec.normalize()).abs() - 1. < 1e-6,
"Line angle should be locked when Ctrl is kept pressed"
);
assert!((updated_start - updated_end).length() > 1., "Line should be able to change length when Ctrl is kept pressed");
}
}
}
@@ -313,14 +297,10 @@ mod test_line_tool {
editor.new_document().await;
editor.drag_tool(ToolType::Line, 100., 100., 200., 100., ModifierKeys::ALT).await;
if let Some((start_input, end_input)) = get_line_node_inputs(&mut editor).await {
match (start_input, end_input) {
(start_input, end_input) => {
let expected_start = DVec2::new(0., 100.);
let expected_end = DVec2::new(200., 100.);
assert!((start_input - expected_start).length() < 1., "Start point should be near (0, 100)");
assert!((end_input - expected_end).length() < 1., "End point should be near (200, 100)");
}
}
let expected_start = DVec2::new(0., 100.);
let expected_end = DVec2::new(200., 100.);
assert!((start_input - expected_start).length() < 1., "Start point should be near (0, 100)");
assert!((end_input - expected_end).length() < 1., "End point should be near (200, 100)");
}
}
@@ -569,7 +569,7 @@ mod test_artboard {
async fn get_artboards(editor: &mut EditorTestUtils) -> Table<graphene_std::Artboard> {
let instrumented = match editor.eval_graph().await {
Ok(instrumented) => instrumented,
Err(e) => panic!("Failed to evaluate graph: {}", e),
Err(e) => panic!("Failed to evaluate graph: {e}"),
};
instrumented
.grab_all_input::<graphene_std::graphic::extend::NewInput<graphene_std::Artboard>>(&editor.runtime)
@@ -416,7 +416,10 @@ mod test_freehand {
fn verify_path_points(vector_and_transform_list: &[(Vector, DAffine2)], expected_captured_points: &[DVec2], tolerance: f64) -> Result<(), String> {
assert_eq!(vector_and_transform_list.len(), 1, "There should be one row of Vector geometry");
let (vector, transform) = vector_and_transform_list.iter().find(|(data, _)| data.point_domain.ids().len() > 0).ok_or("Could not find path data")?;
let (vector, transform) = vector_and_transform_list
.iter()
.find(|(data, _)| !data.point_domain.ids().is_empty())
.ok_or("Could not find path data")?;
let point_count = vector.point_domain.ids().len();
let segment_count = vector.segment_domain.ids().len();
@@ -424,7 +427,7 @@ mod test_freehand {
let actual_positions: Vec<DVec2> = vector.point_domain.positions().iter().map(|&position| transform.transform_point2(position)).collect();
if segment_count != point_count - 1 {
return Err(format!("Expected segments to be one less than points, got {} segments for {} points", segment_count, point_count));
return Err(format!("Expected segments to be one less than points, got {segment_count} segments for {point_count} points"));
}
if point_count != expected_captured_points.len() {
@@ -434,7 +437,7 @@ mod test_freehand {
for (i, (&expected, &actual)) in expected_captured_points.iter().zip(actual_positions.iter()).enumerate() {
let distance = (expected - actual).length();
if distance >= tolerance {
return Err(format!("Point {} position mismatch: expected {:?}, got {:?} (distance: {})", i, expected, actual, distance));
return Err(format!("Point {i} position mismatch: expected {expected:?}, got {actual:?} (distance: {distance})"));
}
}
@@ -508,7 +511,7 @@ mod test_freehand {
let initial_point_count = initial_vector.point_domain.ids().len();
let initial_segment_count = initial_vector.segment_domain.ids().len();
assert!(initial_point_count >= 2, "Expected at least 2 points in initial path, found {}", initial_point_count);
assert!(initial_point_count >= 2, "Expected at least 2 points in initial path, found {initial_point_count}");
assert_eq!(
initial_segment_count,
initial_point_count - 1,
@@ -569,17 +572,13 @@ mod test_freehand {
assert!(
extended_point_count > initial_point_count,
"Expected more points after extension, initial: {}, after extension: {}",
initial_point_count,
extended_point_count
"Expected more points after extension, initial: {initial_point_count}, after extension: {extended_point_count}"
);
assert_eq!(
extended_segment_count,
extended_point_count - 1,
"Expected segments to be one less than points, points: {}, segments: {}",
extended_point_count,
extended_segment_count
"Expected segments to be one less than points, points: {extended_point_count}, segments: {extended_segment_count}"
);
let layer_count = {
@@ -627,8 +626,8 @@ mod test_freehand {
let existing_layer_id = {
let document = editor.active_document();
let layer = document.metadata().all_layers().next().unwrap();
layer
document.metadata().all_layers().next().unwrap()
};
editor
@@ -685,9 +684,7 @@ mod test_freehand {
assert!(
final_point_count > initial_point_count,
"Expected more points after appending to layer, initial: {}, after append: {}",
initial_point_count,
final_point_count
"Expected more points after appending to layer, initial: {initial_point_count}, after append: {final_point_count}"
);
let expected_new_points = second_path_points.len();
@@ -552,7 +552,7 @@ mod test_gradient {
async fn get_fills(editor: &mut EditorTestUtils) -> Vec<(Fill, DAffine2)> {
let instrumented = match editor.eval_graph().await {
Ok(instrumented) => instrumented,
Err(e) => panic!("Failed to evaluate graph: {}", e),
Err(e) => panic!("Failed to evaluate graph: {e}"),
};
let document = editor.active_document();
@@ -573,7 +573,7 @@ mod test_gradient {
let (fill, transform) = fills.first().unwrap();
let gradient = fill.as_gradient().expect("Expected gradient fill type");
(gradient.clone(), transform.clone())
(gradient.clone(), *transform)
}
fn assert_stops_at_positions(actual_positions: &[f64], expected_positions: &[f64], tolerance: f64) {
@@ -586,7 +586,7 @@ mod test_gradient {
);
for (i, (actual, expected)) in actual_positions.iter().zip(expected_positions.iter()).enumerate() {
assert!((actual - expected).abs() < tolerance, "Stop {}: Expected position near {}, got {}", i, expected, actual);
assert!((actual - expected).abs() < tolerance, "Stop {i}: Expected position near {expected}, got {actual}");
}
}
@@ -713,8 +713,7 @@ mod test_gradient {
let positions: Vec<f64> = updated_gradient.stops.iter().map(|(pos, _)| *pos).collect();
assert!(
positions.iter().any(|pos| (pos - 0.5).abs() < 0.1),
"Expected to find a stop near position 0.5, but found: {:?}",
positions
"Expected to find a stop near position 0.5, but found: {positions:?}"
);
}
@@ -782,7 +781,7 @@ mod test_gradient {
// Verify the end point has been updated to the new position
let updated_end = transform.transform_point2(updated_gradient.end);
assert!(updated_end.abs_diff_eq(DVec2::new(100., 50.), 1e-10), "Expected end point at (100, 50), got {:?}", updated_end);
assert!(updated_end.abs_diff_eq(DVec2::new(100., 50.), 1e-10), "Expected end point at (100, 50), got {updated_end:?}");
}
#[tokio::test]
@@ -202,11 +202,11 @@ impl SelectTool {
let list = <BooleanOperation as graphene_std::choice_type::ChoiceTypeStatic>::list();
list.iter().flat_map(|i| i.iter()).map(move |(operation, info)| {
let mut tooltip = info.label.to_string();
if let Some(doc) = info.docstring.as_deref() {
if let Some(doc) = info.docstring {
tooltip.push_str("\n\n");
tooltip.push_str(doc);
}
IconButton::new(info.icon.as_deref().unwrap(), 24)
IconButton::new(info.icon.unwrap(), 24)
.tooltip(tooltip)
.disabled(selected_count == 0)
.on_update(move |_| {
@@ -852,7 +852,13 @@ impl Fsm for SelectToolFsmState {
if let Some(pivot) = pivot {
let offset = tool_data
.pivot_gizmo_start
.map(|offset| tool_data.pivot_gizmo.pivot_disconnected().then_some(tool_data.drag_current - offset).unwrap_or_default())
.map(|offset| {
if tool_data.pivot_gizmo.pivot_disconnected() {
tool_data.drag_current - offset
} else {
Default::default()
}
})
.unwrap_or_default();
let shift = tool_data.pivot_gizmo_shift.unwrap_or_default();
overlay_context.pivot(pivot + offset + shift, angle);
@@ -895,7 +901,7 @@ impl Fsm for SelectToolFsmState {
color
} else {
let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
&format!("#{}", color_string)
&format!("#{color_string}")
};
let line_center = tool_data.line_center;
overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color), None);
@@ -1455,7 +1461,11 @@ impl Fsm for SelectToolFsmState {
tool_data.select_single_layer = None;
if let Some(start) = tool_data.pivot_gizmo_start {
let offset = tool_data.pivot_gizmo.pivot_disconnected().then_some(tool_data.drag_current - start).unwrap_or_default();
let offset = if tool_data.pivot_gizmo.pivot_disconnected() {
tool_data.drag_current - start
} else {
Default::default()
};
if let Some(v) = tool_data.pivot_gizmo.pivot.pivot.as_mut() {
*v += offset;
}
@@ -1649,7 +1659,9 @@ impl Fsm for SelectToolFsmState {
}
(_, SelectToolMessage::PivotShift { offset, flush }) => {
if flush {
tool_data.pivot_gizmo.pivot.pivot.as_mut().map(|v| *v += tool_data.pivot_gizmo_shift.take().unwrap_or_default());
if let Some(v) = tool_data.pivot_gizmo.pivot.pivot.as_mut() {
*v += tool_data.pivot_gizmo_shift.take().unwrap_or_default();
}
let pivot_gizmo = tool_data.pivot_gizmo();
responses.add(TransformLayerMessage::SetPivotGizmo { pivot_gizmo });
return self;
@@ -606,11 +606,7 @@ mod test_spline_tool {
assert!(
distance < epsilon,
"Point {} position mismatch: expected {:?}, got {:?} (distance: {})",
i,
expected_point,
actual_point,
distance
"Point {i} position mismatch: expected {expected_point:?}, got {actual_point:?} (distance: {distance})"
);
}
}
@@ -644,8 +640,8 @@ mod test_spline_tool {
// Verify initial spline has correct number of points and segments
let initial_point_count = first_vector.point_domain.ids().len();
let initial_segment_count = first_vector.segment_domain.ids().len();
assert_eq!(initial_point_count, 3, "Expected 3 points in initial spline, found {}", initial_point_count);
assert_eq!(initial_segment_count, 2, "Expected 2 segments in initial spline, found {}", initial_segment_count);
assert_eq!(initial_point_count, 3, "Expected 3 points in initial spline, found {initial_point_count}");
assert_eq!(initial_segment_count, 2, "Expected 2 segments in initial spline, found {initial_segment_count}");
let layer_to_viewport = document.metadata().transform_to_viewport(spline_layer);
@@ -679,8 +675,8 @@ mod test_spline_tool {
let extended_point_count = extended_vector.point_domain.ids().len();
let extended_segment_count = extended_vector.segment_domain.ids().len();
assert_eq!(extended_point_count, 5, "Expected 5 points in extended spline, found {}", extended_point_count);
assert_eq!(extended_segment_count, 4, "Expected 4 segments in extended spline, found {}", extended_segment_count);
assert_eq!(extended_point_count, 5, "Expected 5 points in extended spline, found {extended_point_count}");
assert_eq!(extended_segment_count, 4, "Expected 4 segments in extended spline, found {extended_segment_count}");
// Verify the spline node is still the same
let extended_spline_node = find_spline(document, spline_layer).expect("Spline node not found after extension");
@@ -715,7 +711,7 @@ mod test_spline_tool {
// Evaluate the graph to ensure everything is processed
if let Err(e) = editor.eval_graph().await {
panic!("Graph evaluation failed: {}", e);
panic!("Graph evaluation failed: {e}");
}
// Get the layer and vector data
@@ -755,7 +751,7 @@ mod test_spline_tool {
// Evaluating the graph to ensure everything is processed
if let Err(e) = editor.eval_graph().await {
panic!("Graph evaluation failed: {}", e);
panic!("Graph evaluation failed: {e}");
}
// Get the layer and vector data
@@ -793,7 +789,7 @@ mod test_spline_tool {
// Evaluating the graph to ensure everything is processed
if let Err(e) = editor.eval_graph().await {
panic!("Graph evaluation failed: {}", e);
panic!("Graph evaluation failed: {e}");
}
// Get the layer and vector data
@@ -832,7 +828,7 @@ mod test_spline_tool {
editor.handle_message(SplineToolMessage::Confirm).await;
if let Err(e) = editor.eval_graph().await {
panic!("Graph evaluation failed: {}", e);
panic!("Graph evaluation failed: {e}");
}
// Get the layer and vector data
@@ -889,8 +885,8 @@ mod test_spline_tool {
let point_count = vector.point_domain.ids().len();
let segment_count = vector.segment_domain.ids().len();
assert_eq!(point_count, 3, "Expected 3 points in the spline, found {}", point_count);
assert_eq!(segment_count, 2, "Expected 2 segments in the spline, found {}", segment_count);
assert_eq!(point_count, 3, "Expected 3 points in the spline, found {point_count}");
assert_eq!(segment_count, 2, "Expected 2 segments in the spline, found {segment_count}");
let layer_to_viewport = document.metadata().transform_to_viewport(spline_layer);
@@ -189,7 +189,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
let format_rounded = |value: f64, precision: usize| {
if self.typing.digits.is_empty() || !self.transform_operation.can_begin_typing() {
format!("{:.*}", precision, value).trim_end_matches('0').trim_end_matches('.').to_string()
format!("{value:.precision$}").trim_end_matches('0').trim_end_matches('.').to_string()
} else {
self.typing.string.clone()
}
@@ -892,7 +892,7 @@ mod test_transform_layer {
let final_transform = get_layer_transform(&mut editor, layer).await.unwrap();
let translation_diff = (final_transform.translation - original_transform.translation).length();
assert!(translation_diff > 10., "Transform should have changed after applying transformation. Diff: {}", translation_diff);
assert!(translation_diff > 10., "Transform should have changed after applying transformation. Diff: {translation_diff}");
}
#[tokio::test]
@@ -927,9 +927,7 @@ mod test_transform_layer {
// Verify transform is either restored to original OR reset to identity
assert!(
(final_translation - original_translation).length() < 5. || final_translation.length() < 0.001,
"Transform neither restored to original nor reset to identity. Original: {:?}, Final: {:?}",
original_translation,
final_translation
"Transform neither restored to original nor reset to identity. Original: {original_translation:?}, Final: {final_translation:?}"
);
}
@@ -958,11 +956,11 @@ mod test_transform_layer {
editor.handle_message(TransformLayerMessage::ApplyTransformOperation { final_transform: true }).await;
let final_transform = get_layer_transform(&mut editor, layer).await.unwrap();
println!("Final transform: {:?}", final_transform);
println!("Final transform: {final_transform:?}");
// Check matrix components have changed (rotation affects matrix2)
let matrix_diff = (final_transform.matrix2.x_axis - original_transform.matrix2.x_axis).length();
assert!(matrix_diff > 0.1, "Rotation should have changed the transform matrix. Diff: {}", matrix_diff);
assert!(matrix_diff > 0.1, "Rotation should have changed the transform matrix. Diff: {matrix_diff}");
}
#[tokio::test]
@@ -984,7 +982,7 @@ mod test_transform_layer {
assert!(!after_cancel.translation.y.is_nan(), "Transform is NaN after cancel");
let translation_diff = (after_cancel.translation - original_transform.translation).length();
assert!(translation_diff < 1., "Translation component changed too much: {}", translation_diff);
assert!(translation_diff < 1., "Translation component changed too much: {translation_diff}");
}
#[tokio::test]
@@ -1019,9 +1017,7 @@ mod test_transform_layer {
assert!(
scale_diff_x > 0.1 || scale_diff_y > 0.1,
"Scaling should have changed the transform matrix. Diffs: x={}, y={}",
scale_diff_x,
scale_diff_y
"Scaling should have changed the transform matrix. Diffs: x={scale_diff_x}, y={scale_diff_y}"
);
}
@@ -1050,7 +1046,7 @@ mod test_transform_layer {
// Also check translation component is similar
let translation_diff = (after_cancel.translation - original_transform.translation).length();
assert!(translation_diff < 1., "Translation component changed too much: {}", translation_diff);
assert!(translation_diff < 1., "Translation component changed too much: {translation_diff}");
}
#[tokio::test]
@@ -1077,9 +1073,7 @@ mod test_transform_layer {
let actual_translation = after_grab_transform.translation - original_transform.translation;
assert!(
(actual_translation - expected_translation).length() < 1e-5,
"Expected translation of {:?}, got {:?}",
expected_translation,
actual_translation
"Expected translation of {expected_translation:?}, got {actual_translation:?}"
);
// 2. Chain to rotation - from current position to create ~45 degree rotation
@@ -1115,9 +1109,7 @@ mod test_transform_layer {
let after_scale_det = after_scale_transform.matrix2.determinant();
assert!(
after_scale_det >= 2. * before_scale_det,
"Scale should increase the determinant of the matrix (before: {}, after: {})",
before_scale_det,
after_scale_det
"Scale should increase the determinant of the matrix (before: {before_scale_det}, after: {after_scale_det})"
);
editor.handle_message(TransformLayerMessage::ApplyTransformOperation { final_transform: true }).await;
@@ -1149,8 +1141,8 @@ mod test_transform_layer {
let scale_x = final_transform.matrix2.x_axis.length() / original_transform.matrix2.x_axis.length();
let scale_y = final_transform.matrix2.y_axis.length() / original_transform.matrix2.y_axis.length();
assert!((scale_x - 2.).abs() < 0.1, "Expected scale factor X of 2, got: {}", scale_x);
assert!((scale_y - 2.).abs() < 0.1, "Expected scale factor Y of 2, got: {}", scale_y);
assert!((scale_x - 2.).abs() < 0.1, "Expected scale factor X of 2, got: {scale_x}");
assert!((scale_y - 2.).abs() < 0.1, "Expected scale factor Y of 2, got: {scale_y}");
}
#[tokio::test]
@@ -1175,8 +1167,8 @@ mod test_transform_layer {
let scale_x = final_transform.matrix2.x_axis.length() / original_transform.matrix2.x_axis.length();
let scale_y = final_transform.matrix2.y_axis.length() / original_transform.matrix2.y_axis.length();
assert!((scale_x - 2.).abs() < 0.1, "Expected scale factor X of 2, got: {}", scale_x);
assert!((scale_y - 2.).abs() < 0.1, "Expected scale factor Y of 2, got: {}", scale_y);
assert!((scale_x - 2.).abs() < 0.1, "Expected scale factor X of 2, got: {scale_x}");
assert!((scale_y - 2.).abs() < 0.1, "Expected scale factor Y of 2, got: {scale_y}");
}
#[tokio::test]
@@ -1191,11 +1183,7 @@ mod test_transform_layer {
// Rotate the document view (45 degrees)
editor.handle_message(NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu: false }).await;
editor
.handle_message(NavigationMessage::CanvasTiltSet {
angle_radians: (45. as f64).to_radians(),
})
.await;
editor.handle_message(NavigationMessage::CanvasTiltSet { angle_radians: 45_f64.to_radians() }).await;
editor.handle_message(TransformLayerMessage::BeginRotate).await;
editor.handle_message(TransformLayerMessage::TypeDigit { digit: 9 }).await;
@@ -1210,7 +1198,7 @@ mod test_transform_layer {
// Normalize angle between 0 and 360
let angle_change = ((angle_change % 360.) + 360.) % 360.;
assert!((angle_change - 90.).abs() < 0.1, "Expected rotation of 90 degrees, got: {}", angle_change);
assert!((angle_change - 90.).abs() < 0.1, "Expected rotation of 90 degrees, got: {angle_change}");
}
#[tokio::test]
@@ -1265,8 +1253,8 @@ mod test_transform_layer {
// Verify scale is near zero.
let scale_x = near_zero_transform.matrix2.x_axis.length();
let scale_y = near_zero_transform.matrix2.y_axis.length();
assert!(scale_x < 0.001, "Scale factor X should be near zero, got: {}", scale_x);
assert!(scale_y < 0.001, "Scale factor Y should be near zero, got: {}", scale_y);
assert!(scale_x < 0.001, "Scale factor X should be near zero, got: {scale_x}");
assert!(scale_y < 0.001, "Scale factor Y should be near zero, got: {scale_y}");
assert!(scale_x > 0., "Scale factor X should not be exactly zero");
assert!(scale_y > 0., "Scale factor Y should not be exactly zero");
+1 -1
View File
@@ -431,7 +431,7 @@ mod test {
let monitor_node = DocumentNode {
inputs: vec![input],
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::memo::monitor::IDENTIFIER),
manual_composition: Some(graph_craft::generic!(T)),
call_argument: graph_craft::generic!(T),
skip_deduplication: true,
..Default::default()
};
+2 -2
View File
@@ -432,7 +432,7 @@ pub struct InspectResult {
impl InspectResult {
pub fn take_data(&mut self) -> Option<Arc<dyn std::any::Any + Send + Sync + 'static>> {
return self.introspected_data.clone();
self.introspected_data.clone()
}
}
@@ -462,7 +462,7 @@ impl InspectState {
let monitor_node = DocumentNode {
inputs: vec![NodeInput::node(inspect_node, 0)], // Connect to the primary output of the inspect node
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::memo::monitor::IDENTIFIER),
manual_composition: Some(graph_craft::generic!(T)),
call_argument: graph_craft::generic!(T),
skip_deduplication: true,
..Default::default()
};