mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-18 07:48:02 +08:00
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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user