mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Node graph improvements (#855)
* Selecting multiple nodes * Improve logs * Log bad types in dyn any * Add (broken) node links * New topological sort * Fix reorder ids function * Input and output node * Add nodes that operate on images * Fixups * Show node parameters together with layer properties * New nodes don't crash editor * Fix tests * Node positions backend * Generate node graph on value change * Add expose input message * Fix tests Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
bbe98d3fe3
commit
2994afa6b8
23 changed files with 734 additions and 331 deletions
|
@ -35,8 +35,8 @@
|
|||
class="node"
|
||||
:class="{ selected: selected.includes(node.id) }"
|
||||
:style="{
|
||||
'--offset-left': 8 + Number(node.id < 9n ? node.id : node.id - 9n) * 7,
|
||||
'--offset-top': 4 + Number(node.id < 9n ? node.id : node.id - 9n) * 2,
|
||||
'--offset-left': node.position?.x || 0,
|
||||
'--offset-top': node.position?.y || 0,
|
||||
'--data-color': 'var(--color-data-raster)',
|
||||
'--data-color-dim': 'var(--color-data-raster-dim)',
|
||||
}"
|
||||
|
@ -438,11 +438,17 @@ export default defineComponent({
|
|||
const nodeId = node?.getAttribute("data-node") || undefined;
|
||||
if (nodeId) {
|
||||
const id = BigInt(nodeId);
|
||||
this.editor.instance.selectNodes(new BigUint64Array([id]));
|
||||
this.selected = [id];
|
||||
if (e.shiftKey || e.ctrlKey) {
|
||||
if (this.selected.includes(id)) this.selected.splice(this.selected.lastIndexOf(id), 1);
|
||||
else this.selected.push(id);
|
||||
} else {
|
||||
this.selected = [id];
|
||||
}
|
||||
|
||||
this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
||||
} else {
|
||||
this.editor.instance.selectNodes(new BigUint64Array([]));
|
||||
this.selected = [];
|
||||
this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
||||
const graphDiv: HTMLDivElement | undefined = (this.$refs.graph as typeof LayoutCol | undefined)?.$el;
|
||||
graphDiv?.setPointerCapture(e.pointerId);
|
||||
|
||||
|
@ -483,9 +489,9 @@ export default defineComponent({
|
|||
const inputNodeConnectionIndex = inputNodeConnectionIndexSearch > -1 ? inputNodeConnectionIndexSearch : undefined;
|
||||
|
||||
if (inputNodeConnectionIndex !== undefined) {
|
||||
const oneBasedIndex = inputNodeConnectionIndex + 1;
|
||||
// const oneBasedIndex = inputNodeConnectionIndex + 1;
|
||||
|
||||
this.editor.instance.connectNodesByLink(BigInt(outputConnectedNodeID), BigInt(inputConnectedNodeID), oneBasedIndex);
|
||||
this.editor.instance.connectNodesByLink(BigInt(outputConnectedNodeID), BigInt(inputConnectedNodeID), inputNodeConnectionIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,11 +88,11 @@
|
|||
|
||||
.widget-row {
|
||||
&:first-child {
|
||||
margin-top: -1px;
|
||||
margin-top: calc(4px - 1px);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: -1px;
|
||||
margin-bottom: calc(4px - 1px);
|
||||
}
|
||||
|
||||
> .text-label:first-of-type {
|
||||
|
|
|
@ -12,6 +12,11 @@ export class JsMessage {
|
|||
static readonly jsMessageMarker = true;
|
||||
}
|
||||
|
||||
const TupleToVec2 = Transform(({ value }: { value: [number, number] | undefined }) => (value === undefined ? undefined : { x: value[0], y: value[1] }));
|
||||
const BigIntTupleToVec2 = Transform(({ value }: { value: [bigint, bigint] | undefined }) => (value === undefined ? undefined : { x: Number(value[0]), y: Number(value[1]) }));
|
||||
|
||||
export type XY = { x: number; y: number };
|
||||
|
||||
// ============================================================================
|
||||
// Add additional classes below to replicate Rust's `FrontendMessage`s and data structures.
|
||||
//
|
||||
|
@ -64,10 +69,19 @@ export class FrontendDocumentDetails extends DocumentDetails {
|
|||
readonly id!: bigint;
|
||||
}
|
||||
|
||||
export type DataType = "Raster" | "Color" | "Image" | "F32";
|
||||
|
||||
export class FrontendNode {
|
||||
readonly id!: bigint;
|
||||
|
||||
readonly displayName!: string;
|
||||
|
||||
readonly exposedInputs!: DataType[];
|
||||
|
||||
readonly outputs!: DataType[];
|
||||
|
||||
@TupleToVec2
|
||||
readonly position!: XY | undefined;
|
||||
}
|
||||
|
||||
export class FrontendNodeLink {
|
||||
|
@ -403,11 +417,6 @@ export class UpdateDocumentArtboards extends JsMessage {
|
|||
readonly svg!: string;
|
||||
}
|
||||
|
||||
const TupleToVec2 = Transform(({ value }: { value: [number, number] | undefined }) => (value === undefined ? undefined : { x: value[0], y: value[1] }));
|
||||
const BigIntTupleToVec2 = Transform(({ value }: { value: [bigint, bigint] | undefined }) => (value === undefined ? undefined : { x: Number(value[0]), y: Number(value[1]) }));
|
||||
|
||||
export type XY = { x: number; y: number };
|
||||
|
||||
export class UpdateDocumentScrollbars extends JsMessage {
|
||||
@TupleToVec2
|
||||
readonly position!: XY;
|
||||
|
|
|
@ -541,7 +541,7 @@ impl JsEditorHandle {
|
|||
|
||||
/// Notifies the backend that the user connected a node's primary output to one of another node's inputs
|
||||
#[wasm_bindgen(js_name = connectNodesByLink)]
|
||||
pub fn connect_nodes_by_link(&self, output_node: u64, input_node: u64, input_node_connector_index: u32) {
|
||||
pub fn connect_nodes_by_link(&self, output_node: u64, input_node: u64, input_node_connector_index: usize) {
|
||||
let message = NodeGraphMessage::ConnectNodesByLink {
|
||||
output_node,
|
||||
input_node,
|
||||
|
@ -553,9 +553,6 @@ impl JsEditorHandle {
|
|||
/// Creates a new document node in the node graph
|
||||
#[wasm_bindgen(js_name = createNode)]
|
||||
pub fn create_node(&self, node_type: String) {
|
||||
use graph_craft::proto::{NodeIdentifier, Type};
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn generate_node_id() -> u64 {
|
||||
static mut NODE_ID: u64 = 10;
|
||||
unsafe {
|
||||
|
@ -564,24 +561,9 @@ impl JsEditorHandle {
|
|||
}
|
||||
}
|
||||
|
||||
let (ident, args) = match node_type.as_str() {
|
||||
"Identity" => (NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]), 1),
|
||||
"Grayscale Color" => (NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]), 1),
|
||||
"Brighten Color" => (NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]), 1),
|
||||
"Hue Shift Color" => (NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]), 1),
|
||||
"Add" => (
|
||||
NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete(Cow::Borrowed("u32")), Type::Concrete(Cow::Borrowed("u32"))]),
|
||||
2,
|
||||
),
|
||||
"Map Image" => (NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]), 2),
|
||||
_ => panic!("Invalid node type: {}", node_type),
|
||||
};
|
||||
|
||||
let message = NodeGraphMessage::CreateNode {
|
||||
node_id: generate_node_id(),
|
||||
name: node_type,
|
||||
identifier: ident,
|
||||
num_inputs: args,
|
||||
node_type,
|
||||
};
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
@ -593,6 +575,13 @@ impl JsEditorHandle {
|
|||
self.dispatch(message);
|
||||
}
|
||||
|
||||
/// Notifies the backend that the selected nodes have been moved
|
||||
#[wasm_bindgen(js_name = moveSelectedNodes)]
|
||||
pub fn move_selected_nodes(&self, displacement_x: i32, displacement_y: i32) {
|
||||
let message = NodeGraphMessage::MoveSelectedNodes { displacement_x, displacement_y };
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
/// Pastes an image
|
||||
#[wasm_bindgen(js_name = pasteImage)]
|
||||
pub fn paste_image(&self, mime: String, image_data: Vec<u8>, mouse_x: Option<f64>, mouse_y: Option<f64>) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue