Add nondestructive vector editing (#1676)

* Initial vector modify node

* Initial extraction of data from monitor nodes

* Migrate to point id

* Start converting to modify node

* Non destructive spline tool (tout le reste est cassé)

* Fix unconnected modify node

* Fix freehand tool

* Pen tool

* Migrate demo art

* Select points

* Fix the demo artwork

* Fix the X and Y inputs for path tool

* G1 continous toggle

* Delete points

* Fix test

* Insert point

* Improve robustness of handles

* Fix GRS shortcuts on path

* Dragging points

* Fix build

* Preserve opposing handle lengths

* Update demo art and snapping

* Fix polygon tool

* Double click end anchor

* Improve dragging

* Fix text shifting

* Select only connected verts

* Colinear alt

* Cleanup

* Fix imports

* Improve pen tool avoiding handle placement

* Improve disolve

* Remove pivot widget from Transform node properties

* Fix demo art

* Fix bugs

* Re-save demo artwork

* Code review

* Serialize hashmap as tuple vec to enable deserialize_inputs

* Fix migrate

* Add document upgrade function to editor_api.rs

* Finalize document upgrading

* Rename to the Path node

* Remove smoothing from Freehand tool

* Upgrade demo artwork

* Propertly disable raw-rs tests

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
Co-authored-by: Adam <adamgerhant@gmail.com>
Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
James Lindsay
2024-07-05 21:42:40 +01:00
committed by GitHub
parent fd3613018a
commit 1652c713a6
96 changed files with 3343 additions and 2622 deletions

View File

@@ -57,12 +57,14 @@ pub struct NodeRuntime {
thumbnail_renders: HashMap<NodeId, Vec<SvgSegment>>,
/// The current click targets for layer nodes.
click_targets: HashMap<NodeId, Vec<ClickTarget>>,
/// Vector data in Path nodes.
vector_modify: HashMap<NodeId, VectorData>,
/// The current upstream transforms for nodes.
upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
}
/// Messages passed from the editor thread to the node runtime thread.
enum NodeRuntimeMessage {
pub enum NodeRuntimeMessage {
ExecutionRequest(ExecutionRequest),
FontCacheUpdate(FontCache),
ImaginatePreferencesUpdate(ImaginatePreferences),
@@ -78,24 +80,25 @@ pub struct ExportConfig {
pub size: DVec2,
}
pub(crate) struct ExecutionRequest {
pub struct ExecutionRequest {
execution_id: u64,
graph: NodeNetwork,
render_config: RenderConfig,
}
pub(crate) struct ExecutionResponse {
pub struct ExecutionResponse {
execution_id: u64,
result: Result<TaggedValue, String>,
responses: VecDeque<Message>,
new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
new_vector_modify: HashMap<NodeId, VectorData>,
new_upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
resolved_types: ResolvedDocumentNodeTypes,
node_graph_errors: GraphErrors,
transform: DAffine2,
}
enum NodeGraphUpdate {
pub enum NodeGraphUpdate {
ExecutionResponse(ExecutionResponse),
NodeGraphUpdateMessage(NodeGraphUpdateMessage),
}
@@ -119,7 +122,7 @@ thread_local! {
}
impl NodeRuntime {
fn new(receiver: Receiver<NodeRuntimeMessage>, sender: Sender<NodeGraphUpdate>) -> Self {
pub fn new(receiver: Receiver<NodeRuntimeMessage>, sender: Sender<NodeGraphUpdate>) -> Self {
Self {
executor: DynamicExecutor::default(),
receiver,
@@ -136,6 +139,7 @@ impl NodeRuntime {
thumbnail_renders: Default::default(),
click_targets: HashMap::new(),
vector_modify: HashMap::new(),
upstream_transforms: HashMap::new(),
}
}
@@ -166,6 +170,7 @@ impl NodeRuntime {
result,
responses,
new_click_targets: self.click_targets.clone().into_iter().map(|(id, targets)| (LayerNodeIdentifier::new_unchecked(id), targets)).collect(),
new_vector_modify: self.vector_modify.clone(),
new_upstream_transforms: self.upstream_transforms.clone(),
resolved_types: self.resolved_types.clone(),
node_graph_errors: core::mem::take(&mut self.node_graph_errors),
@@ -233,7 +238,7 @@ impl NodeRuntime {
Some(t) if t == concrete!(WasmEditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()),
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
Some(t) => Err(format!("Invalid input type {t:?}")),
_ => Err("No input type".to_string()),
_ => Err(format!("No input type:\n{:?}", self.node_graph_errors)),
};
let result = match result {
Ok(value) => value,
@@ -326,6 +331,9 @@ impl NodeRuntime {
});
*old_thumbnail_svg = new_thumbnail_svg;
}
} else if let Some(record) = introspected_data.downcast_ref::<IORecord<Footprint, VectorData>>() {
// Insert the vector modify if we are dealing with vector data
self.vector_modify.insert(parent_network_node_id, record.output.clone());
}
// If this is `VectorData`, `ImageFrame`, or `GraphicElement` data:
@@ -378,6 +386,13 @@ pub async fn run_node_graph() {
}
}
pub fn replace_node_runtime(runtime: NodeRuntime) -> Option<NodeRuntime> {
NODE_RUNTIME.with(|node_runtime| {
let mut node_runtime = node_runtime.borrow_mut();
node_runtime.replace(runtime)
})
}
#[derive(Debug)]
pub struct NodeGraphExecutor {
sender: Sender<NodeRuntimeMessage>,
@@ -394,9 +409,7 @@ impl Default for NodeGraphExecutor {
fn default() -> Self {
let (request_sender, request_receiver) = std::sync::mpsc::channel();
let (response_sender, response_receiver) = std::sync::mpsc::channel();
NODE_RUNTIME.with(|runtime| {
runtime.borrow_mut().replace(NodeRuntime::new(request_receiver, response_sender));
});
replace_node_runtime(NodeRuntime::new(request_receiver, response_sender));
Self {
futures: Default::default(),
@@ -556,6 +569,7 @@ impl NodeGraphExecutor {
result,
responses: existing_responses,
new_click_targets,
new_vector_modify,
new_upstream_transforms,
resolved_types,
node_graph_errors,
@@ -567,15 +581,18 @@ impl NodeGraphExecutor {
responses.add(NodeGraphMessage::SendGraph);
responses.add(OverlaysMessage::Draw);
let Ok(node_graph_output) = result else {
// Clear the click targets while the graph is in an un-renderable state
document.metadata.update_click_targets(HashMap::new());
let node_graph_output = match result {
Ok(output) => output,
Err(e) => {
// Clear the click targets while the graph is in an un-renderable state
document.metadata.update_from_monitor(HashMap::new(), HashMap::new());
return Err("Node graph evaluation failed".to_string());
return Err(format!("Node graph evaluation failed:\n{e}"));
}
};
document.metadata.update_transforms(new_upstream_transforms);
document.metadata.update_click_targets(new_click_targets);
document.metadata.update_from_monitor(new_click_targets, new_vector_modify);
let execution_context = self.futures.remove(&execution_id).ok_or_else(|| "Invalid generation ID".to_string())?;
if let Some(export_config) = execution_context.export_config {