123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893 |
- import * as React from "react";
- import { GlobalState } from '../globalState';
- import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
- import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes';
- import { GraphNode } from './graphNode';
- import * as dagre from 'dagre';
- import { Nullable } from 'babylonjs/types';
- import { NodeLink } from './nodeLink';
- import { NodePort } from './nodePort';
- import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection, NodeMaterialConnectionPointCompatibilityStates } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
- import { Vector2 } from 'babylonjs/Maths/math.vector';
- import { FragmentOutputBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/fragmentOutputBlock';
- import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
- import { DataStorage } from 'babylonjs/Misc/dataStorage';
- import { GraphFrame } from './graphFrame';
- import { IEditorData } from '../nodeLocationInfo';
- import { FrameNodePort } from './frameNodePort';
- require("./graphCanvas.scss");
- export interface IGraphCanvasComponentProps {
- globalState: GlobalState
- }
- export type FramePortData = {
- frame: GraphFrame,
- port: FrameNodePort
- }
- export const isFramePortData = (variableToCheck: any): variableToCheck is FramePortData => {
- if (variableToCheck) {
- return (variableToCheck as FramePortData).port !== undefined;
- }
- else return false;
- }
- export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentProps> {
- private readonly MinZoom = 0.1;
- private readonly MaxZoom = 4;
- private _hostCanvas: HTMLDivElement;
- private _graphCanvas: HTMLDivElement;
- private _selectionContainer: HTMLDivElement;
- private _frameContainer: HTMLDivElement;
- private _svgCanvas: HTMLElement;
- private _rootContainer: HTMLDivElement;
- private _nodes: GraphNode[] = [];
- private _links: NodeLink[] = [];
- private _mouseStartPointX: Nullable<number> = null;
- private _mouseStartPointY: Nullable<number> = null
- private _dropPointX = 0;
- private _dropPointY = 0;
- private _selectionStartX = 0;
- private _selectionStartY = 0;
- private _candidateLinkedHasMoved = false;
- private _x = 0;
- private _y = 0;
- private _zoom = 1;
- private _selectedNodes: GraphNode[] = [];
- private _selectedLink: Nullable<NodeLink> = null;
- private _selectedPort: Nullable<NodePort> = null;
- private _candidateLink: Nullable<NodeLink> = null;
- private _candidatePort: Nullable<NodePort | FrameNodePort> = null;
- private _gridSize = 20;
- private _selectionBox: Nullable<HTMLDivElement> = null;
- private _selectedFrame: Nullable<GraphFrame> = null;
- private _frameCandidate: Nullable<HTMLDivElement> = null;
- private _frames: GraphFrame[] = [];
- private _altKeyIsPressed = false;
- private _ctrlKeyIsPressed = false;
- private _oldY = -1;
- public _frameIsMoving = false;
- public _isLoading = false;
- public get gridSize() {
- return this._gridSize;
- }
- public set gridSize(value: number) {
- this._gridSize = value;
-
- this.updateTransform();
- }
- public get globalState(){
- return this.props.globalState;
- }
- public get nodes() {
- return this._nodes;
- }
- public get links() {
- return this._links;
- }
- public get frames() {
- return this._frames;
- }
- public get zoom() {
- return this._zoom;
- }
- public set zoom(value: number) {
- if (this._zoom === value) {
- return;
- }
- this._zoom = value;
-
- this.updateTransform();
- }
- public get x() {
- return this._x;
- }
- public set x(value: number) {
- this._x = value;
-
- this.updateTransform();
- }
- public get y() {
- return this._y;
- }
- public set y(value: number) {
- this._y = value;
-
- this.updateTransform();
- }
- public get selectedNodes() {
- return this._selectedNodes;
- }
- public get selectedLink() {
- return this._selectedLink;
- }
- public get selectedFrame() {
- return this._selectedFrame;
- }
- public get selectedPort() {
- return this._selectedPort;
- }
- public get canvasContainer() {
- return this._graphCanvas;
- }
- public get hostCanvas() {
- return this._hostCanvas;
- }
- public get svgCanvas() {
- return this._svgCanvas;
- }
- public get selectionContainer() {
- return this._selectionContainer;
- }
- public get frameContainer() {
- return this._frameContainer;
- }
-
- constructor(props: IGraphCanvasComponentProps) {
- super(props);
- props.globalState.onSelectionChangedObservable.add(selection => {
- if (!selection) {
- this._selectedNodes = [];
- this._selectedLink = null;
- this._selectedFrame = null;
- this._selectedPort = null;
- } else {
- if (selection instanceof NodeLink) {
- this._selectedNodes = [];
- this._selectedFrame = null;
- this._selectedLink = selection;
- this._selectedPort = null;
- } else if (selection instanceof GraphFrame) {
- this._selectedNodes = [];
- this._selectedFrame = selection;
- this._selectedLink = null;
- this._selectedPort = null;
- } else if (selection instanceof GraphNode){
- if (this._ctrlKeyIsPressed) {
- if (this._selectedNodes.indexOf(selection) === -1) {
- this._selectedNodes.push(selection);
- }
- } else {
- this._selectedNodes = [selection];
- }
- } else if(selection instanceof NodePort && !selection.hasLabel()){ // if node port is uneditable, select graphNode instead
- props.globalState.onSelectionChangedObservable.notifyObservers(selection.node)
- } else if(selection instanceof NodePort){
- this._selectedNodes = [];
- this._selectedFrame = null;
- this._selectedLink = null;
- this._selectedPort = selection;
- } else {
- this._selectedNodes = [];
- this._selectedFrame = null;
- this._selectedLink = null;
- this._selectedPort = selection.port;
- }
- }
- });
- props.globalState.onCandidatePortSelectedObservable.add(port => {
- this._candidatePort = port;
- });
- props.globalState.onGridSizeChanged.add(() => {
- this.gridSize = DataStorage.ReadNumber("GridSize", 20);
- });
- this.props.globalState.hostDocument!.addEventListener("keyup", () => this.onKeyUp(), false);
- this.props.globalState.hostDocument!.addEventListener("keydown", evt => {
- this._altKeyIsPressed = evt.altKey;
- this._ctrlKeyIsPressed = evt.ctrlKey;
- }, false);
- this.props.globalState.hostDocument!.defaultView!.addEventListener("blur", () => {
- this._altKeyIsPressed = false;
- this._ctrlKeyIsPressed = false;
- }, false);
- // Store additional data to serialization object
- this.props.globalState.storeEditorData = (editorData, graphFrame) => {
- editorData.frames = [];
- if (graphFrame) {
- editorData.frames.push(graphFrame!.serialize());
- } else {
- editorData.x = this.x;
- editorData.y = this.y;
- editorData.zoom = this.zoom;
- for (var frame of this._frames) {
- editorData.frames.push(frame.serialize());
- }
- }
- }
- }
- public getGridPosition(position: number, useCeil = false) {
- let gridSize = this.gridSize;
- if (gridSize === 0) {
- return position;
- }
- if (useCeil) {
- return gridSize * Math.ceil(position / gridSize);
- }
- return gridSize * Math.floor(position / gridSize);
- }
-
- public getGridPositionCeil(position: number) {
- let gridSize = this.gridSize;
- if (gridSize === 0) {
- return position;
- }
- return gridSize * Math.ceil(position / gridSize);
- }
- updateTransform() {
- this._rootContainer.style.transform = `translate(${this._x}px, ${this._y}px) scale(${this._zoom})`;
- if (DataStorage.ReadBoolean("ShowGrid", true)) {
- this._hostCanvas.style.backgroundSize = `${this._gridSize * this._zoom}px ${this._gridSize * this._zoom}px`;
- this._hostCanvas.style.backgroundPosition = `${this._x}px ${this._y}px`;
- } else {
- this._hostCanvas.style.backgroundSize = `0`;
- }
- }
- onKeyUp() {
- this._altKeyIsPressed = false;
- this._ctrlKeyIsPressed = false;
- this._oldY = -1;
- }
- findNodeFromBlock(block: NodeMaterialBlock) {
- return this.nodes.filter(n => n.block === block)[0];
- }
- reset() {
- for (var node of this._nodes) {
- node.dispose();
- }
-
- const frames = this._frames.splice(0);
- for (var frame of frames) {
- frame.dispose();
- }
- this._nodes = [];
- this._frames = [];
- this._links = [];
- this._graphCanvas.innerHTML = "";
- this._svgCanvas.innerHTML = "";
- }
- connectPorts(pointA: NodeMaterialConnectionPoint, pointB: NodeMaterialConnectionPoint) {
- var blockA = pointA.ownerBlock;
- var blockB = pointB.ownerBlock;
- var nodeA = this.findNodeFromBlock(blockA);
- var nodeB = this.findNodeFromBlock(blockB);
- if (!nodeA || !nodeB) {
- return;
- }
- var portA = nodeA.getPortForConnectionPoint(pointA);
- var portB = nodeB.getPortForConnectionPoint(pointB);
- if (!portA || !portB) {
- return;
- }
- for (var currentLink of this._links) {
- if (currentLink.portA === portA && currentLink.portB === portB) {
- return;
- }
- if (currentLink.portA === portB && currentLink.portB === portA) {
- return;
- }
- }
- const link = new NodeLink(this, portA, nodeA, portB, nodeB);
- this._links.push(link);
- nodeA.links.push(link);
- nodeB.links.push(link);
- }
- removeLink(link: NodeLink) {
- let index = this._links.indexOf(link);
- if (index > -1) {
- this._links.splice(index, 1);
- }
- link.dispose();
- }
- appendBlock(block: NodeMaterialBlock) {
- let newNode = new GraphNode(block, this.props.globalState);
- newNode.appendVisual(this._graphCanvas, this);
- this._nodes.push(newNode);
- return newNode;
- }
- distributeGraph() {
- this.x = 0;
- this.y = 0;
- this.zoom = 1;
- let graph = new dagre.graphlib.Graph();
- graph.setGraph({});
- graph.setDefaultEdgeLabel(() => ({}));
- graph.graph().rankdir = "LR";
- // Build dagre graph
- this._nodes.forEach(node => {
- if (this._frames.some(f => f.nodes.indexOf(node) !== -1)) {
- return;
- }
- graph.setNode(node.id.toString(), {
- id: node.id,
- type: "node",
- width: node.width,
- height: node.height
- });
- });
- this._frames.forEach(frame => {
- graph.setNode(frame.id.toString(), {
- id: frame.id,
- type: "frame",
- width: frame.element.clientWidth,
- height: frame.element.clientHeight
- });
- })
- this._nodes.forEach(node => {
- node.block.outputs.forEach(output => {
- if (!output.hasEndpoints) {
- return;
- }
- output.endpoints.forEach(endpoint => {
- let sourceFrames = this._frames.filter(f => f.nodes.indexOf(node) !== -1);
- let targetFrames = this._frames.filter(f => f.nodes.some(n => n.block === endpoint.ownerBlock));
- let sourceId = sourceFrames.length > 0 ? sourceFrames[0].id : node.id;
- let targetId = targetFrames.length > 0 ? targetFrames[0].id : endpoint.ownerBlock.uniqueId;
- graph.setEdge(sourceId.toString(), targetId.toString());
- });
- });
- });
- // Distribute
- dagre.layout(graph);
- // Update graph
- let dagreNodes = graph.nodes().map(node => graph.node(node));
- dagreNodes.forEach((dagreNode: any) => {
- if (dagreNode.type === "node") {
- for (var node of this._nodes) {
- if (node.id === dagreNode.id) {
- node.x = dagreNode.x - dagreNode.width / 2;
- node.y = dagreNode.y - dagreNode.height / 2;
- node.cleanAccumulation();
- return;
- }
- }
- return;
- }
- for (var frame of this._frames) {
- if (frame.id === dagreNode.id) {
- this._frameIsMoving = true;
- frame.move(dagreNode.x - dagreNode.width / 2, dagreNode.y - dagreNode.height / 2, false);
- frame.cleanAccumulation();
- this._frameIsMoving = false;
- return;
- }
- }
- });
- }
- componentDidMount() {
- this._hostCanvas = this.props.globalState.hostDocument.getElementById("graph-canvas") as HTMLDivElement;
- this._rootContainer = this.props.globalState.hostDocument.getElementById("graph-container") as HTMLDivElement;
- this._graphCanvas = this.props.globalState.hostDocument.getElementById("graph-canvas-container") as HTMLDivElement;
- this._svgCanvas = this.props.globalState.hostDocument.getElementById("graph-svg-container") as HTMLElement;
- this._selectionContainer = this.props.globalState.hostDocument.getElementById("selection-container") as HTMLDivElement;
- this._frameContainer = this.props.globalState.hostDocument.getElementById("frame-container") as HTMLDivElement;
-
- this.gridSize = DataStorage.ReadNumber("GridSize", 20);
- this.updateTransform();
- }
- onMove(evt: React.PointerEvent) {
- // Selection box
- if (this._selectionBox) {
- const rootRect = this.canvasContainer.getBoundingClientRect();
- const localX = evt.pageX - rootRect.left;
- const localY = evt.pageY - rootRect.top;
- if (localX > this._selectionStartX) {
- this._selectionBox.style.left = `${this._selectionStartX / this.zoom}px`;
- this._selectionBox.style.width = `${(localX - this._selectionStartX) / this.zoom}px`;
- } else {
- this._selectionBox.style.left = `${localX / this.zoom}px`;
- this._selectionBox.style.width = `${(this._selectionStartX - localX) / this.zoom}px`;
- }
- if (localY > this._selectionStartY) {
- this._selectionBox.style.top = `${this._selectionStartY / this.zoom}px`;
- this._selectionBox.style.height = `${(localY - this._selectionStartY) / this.zoom}px`;
- } else {
- this._selectionBox.style.top = `${localY / this.zoom}px`;
- this._selectionBox.style.height = `${(this._selectionStartY - localY) / this.zoom}px`;
- }
-
- this.props.globalState.onSelectionBoxMoved.notifyObservers(this._selectionBox.getBoundingClientRect());
- return;
- }
- // Candidate frame box
- if (this._frameCandidate) {
- const rootRect = this.canvasContainer.getBoundingClientRect();
- const localX = evt.pageX - rootRect.left;
- const localY = evt.pageY - rootRect.top;
- if (localX > this._selectionStartX) {
- this._frameCandidate.style.left = `${this._selectionStartX / this.zoom}px`;
- this._frameCandidate.style.width = `${(localX - this._selectionStartX) / this.zoom}px`;
- } else {
- this._frameCandidate.style.left = `${localX / this.zoom}px`;
- this._frameCandidate.style.width = `${(this._selectionStartX - localX) / this.zoom}px`;
- }
- if (localY > this._selectionStartY) {
- this._frameCandidate.style.top = `${this._selectionStartY / this.zoom}px`;
- this._frameCandidate.style.height = `${(localY - this._selectionStartY) / this.zoom}px`;
- } else {
- this._frameCandidate.style.top = `${localY / this.zoom}px`;
- this._frameCandidate.style.height = `${(this._selectionStartY - localY) / this.zoom}px`;
- }
- return;
- }
- // Candidate link
- if (this._candidateLink) {
- const rootRect = this.canvasContainer.getBoundingClientRect();
- this._candidatePort = null;
- this.props.globalState.onCandidateLinkMoved.notifyObservers(new Vector2(evt.pageX, evt.pageY));
- this._dropPointX = (evt.pageX - rootRect.left) / this.zoom;
- this._dropPointY = (evt.pageY - rootRect.top) / this.zoom;
- this._candidateLink.update(this._dropPointX, this._dropPointY, true);
- this._candidateLinkedHasMoved = true;
-
- return;
- }
- // Zoom with mouse + alt
- if (this._altKeyIsPressed && evt.buttons === 1) {
- if (this._oldY < 0) {
- this._oldY = evt.pageY;
- }
- let zoomDelta = (evt.pageY - this._oldY) / 10;
- if (Math.abs(zoomDelta) > 5) {
- const oldZoom = this.zoom;
- this.zoom = Math.max(Math.min(this.MaxZoom, this.zoom + zoomDelta / 100), this.MinZoom);
- const boundingRect = evt.currentTarget.getBoundingClientRect();
- const clientWidth = boundingRect.width;
- const widthDiff = clientWidth * this.zoom - clientWidth * oldZoom;
- const clientX = evt.clientX - boundingRect.left;
-
- const xFactor = (clientX - this.x) / oldZoom / clientWidth;
-
- this.x = this.x - widthDiff * xFactor;
- this._oldY = evt.pageY;
- }
- return;
- }
- // Move canvas
- this._rootContainer.style.cursor = "move";
- if (this._mouseStartPointX === null || this._mouseStartPointY === null) {
- return;
- }
- this.x += evt.clientX - this._mouseStartPointX;
- this.y += evt.clientY - this._mouseStartPointY;
- this._mouseStartPointX = evt.clientX;
- this._mouseStartPointY = evt.clientY;
- }
- onDown(evt: React.PointerEvent<HTMLElement>) {
- this._rootContainer.setPointerCapture(evt.pointerId);
- // Selection?
- if (evt.currentTarget === this._hostCanvas && evt.ctrlKey) {
- this._selectionBox = this.props.globalState.hostDocument.createElement("div");
- this._selectionBox.classList.add("selection-box");
- this._selectionContainer.appendChild(this._selectionBox);
- const rootRect = this.canvasContainer.getBoundingClientRect();
- this._selectionStartX = (evt.pageX - rootRect.left);
- this._selectionStartY = (evt.pageY - rootRect.top);
- this._selectionBox.style.left = `${this._selectionStartX / this.zoom}px`;
- this._selectionBox.style.top = `${this._selectionStartY / this.zoom}px`;
- this._selectionBox.style.width = "0px";
- this._selectionBox.style.height = "0px";
- return;
- }
- // Frame?
- if (evt.currentTarget === this._hostCanvas && evt.shiftKey) {
- this._frameCandidate = this.props.globalState.hostDocument.createElement("div");
- this._frameCandidate.classList.add("frame-box");
- this._frameContainer.appendChild(this._frameCandidate);
- const rootRect = this.canvasContainer.getBoundingClientRect();
- this._selectionStartX = (evt.pageX - rootRect.left);
- this._selectionStartY = (evt.pageY - rootRect.top);
- this._frameCandidate.style.left = `${this._selectionStartX / this.zoom}px`;
- this._frameCandidate.style.top = `${this._selectionStartY / this.zoom}px`;
- this._frameCandidate.style.width = "0px";
- this._frameCandidate.style.height = "0px";
- return;
- }
- // Port dragging
- if (evt.nativeEvent.srcElement && (evt.nativeEvent.srcElement as HTMLElement).nodeName === "IMG") {
- if (!this._candidateLink) {
- let portElement = ((evt.nativeEvent.srcElement as HTMLElement).parentElement as any).port as NodePort;
- this._candidateLink = new NodeLink(this, portElement, portElement.node);
- this._candidateLinkedHasMoved = false;
- }
- return;
- }
- this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
- this._mouseStartPointX = evt.clientX;
- this._mouseStartPointY = evt.clientY;
- }
- onUp(evt: React.PointerEvent) {
- this._mouseStartPointX = null;
- this._mouseStartPointY = null;
- this._rootContainer.releasePointerCapture(evt.pointerId);
- this._oldY = -1;
- if (this._candidateLink) {
- if (this._candidateLinkedHasMoved) {
- this.processCandidatePort();
- this.props.globalState.onCandidateLinkMoved.notifyObservers(null);
- } else { // is a click event on NodePort
- if(this._candidateLink.portA instanceof FrameNodePort) { //only on Frame Node Ports
- const port = this._candidateLink.portA;
- const frame = this.frames.find((frame: GraphFrame) => frame.id === port.parentFrameId);
- if (frame) {
- const data: FramePortData = {
- frame,
- port
- }
- this.props.globalState.onSelectionChangedObservable.notifyObservers(data);
- }
- } else if(this._candidateLink.portA instanceof NodePort){
- this.props.globalState.onSelectionChangedObservable.notifyObservers(this._candidateLink.portA );
- }
- }
- this._candidateLink.dispose();
- this._candidateLink = null;
- this._candidatePort = null;
- }
- if (this._selectionBox) {
- this._selectionBox.parentElement!.removeChild(this._selectionBox);
- this._selectionBox = null;
- }
- if (this._frameCandidate) {
- let newFrame = new GraphFrame(this._frameCandidate, this);
- this._frames.push(newFrame);
- this._frameCandidate.parentElement!.removeChild(this._frameCandidate);
- this._frameCandidate = null;
- this.props.globalState.onSelectionChangedObservable.notifyObservers(newFrame);
- }
- }
- onWheel(evt: React.WheelEvent) {
- let delta = evt.deltaY < 0 ? 0.1 : -0.1;
- let oldZoom = this.zoom;
- this.zoom = Math.min(Math.max(this.MinZoom, this.zoom + delta * this.zoom), this.MaxZoom);
- const boundingRect = evt.currentTarget.getBoundingClientRect();
- const clientWidth = boundingRect.width;
- const clientHeight = boundingRect.height;
- const widthDiff = clientWidth * this.zoom - clientWidth * oldZoom;
- const heightDiff = clientHeight * this.zoom - clientHeight * oldZoom;
- const clientX = evt.clientX - boundingRect.left;
- const clientY = evt.clientY - boundingRect.top;
- const xFactor = (clientX - this.x) / oldZoom / clientWidth;
- const yFactor = (clientY - this.y) / oldZoom / clientHeight;
- this.x = this.x - widthDiff * xFactor;
- this.y = this.y - heightDiff * yFactor;
- evt.stopPropagation();
- }
- zoomToFit() {
- // Get negative offset
- let minX = 0;
- let minY = 0;
- this._nodes.forEach(node => {
- if (this._frames.some(f => f.nodes.indexOf(node) !== -1)) {
- return;
- }
- if (node.x < minX) {
- minX = node.x;
- }
- if (node.y < minY) {
- minY = node.y;
- }
- });
- this._frames.forEach(frame => {
- if (frame.x < minX) {
- minX = frame.x;
- }
- if (frame.y < minY) {
- minY = frame.y;
- }
- });
- // Restore to 0
- this._frames.forEach(frame => {
- frame.x += -minX;
- frame.y += -minY;
- frame.cleanAccumulation();
- });
- this._nodes.forEach(node => {
- node.x += -minX;
- node.y += -minY;
- node.cleanAccumulation();
- });
- // Get correct zoom
- const xFactor = this._rootContainer.clientWidth / this._rootContainer.scrollWidth;
- const yFactor = this._rootContainer.clientHeight / this._rootContainer.scrollHeight;
- const zoomFactor = xFactor < yFactor ? xFactor : yFactor;
-
- this.zoom = zoomFactor;
- this.x = 0;
- this.y = 0;
- }
- processCandidatePort() {
- let pointB = this._candidateLink!.portA.connectionPoint;
- let nodeB = this._candidateLink!.portA.node;
- let pointA: NodeMaterialConnectionPoint;
- let nodeA: GraphNode;
- if (this._candidatePort) {
- pointA = this._candidatePort.connectionPoint;
- nodeA = this._candidatePort.node;
- } else {
- if (pointB.direction === NodeMaterialConnectionPointDirection.Output) {
- return;
- }
- // No destination so let's spin a new input block
- let pointName = "output", inputBlock;
- let customInputBlock = this._candidateLink!.portA.connectionPoint.createCustomInputBlock();
- if (!customInputBlock) {
- inputBlock = new InputBlock(NodeMaterialBlockConnectionPointTypes[this._candidateLink!.portA.connectionPoint.type], undefined, this._candidateLink!.portA.connectionPoint.type);
- } else {
- [inputBlock, pointName] = customInputBlock;
- }
- this.props.globalState.nodeMaterial.attachedBlocks.push(inputBlock);
- pointA = (inputBlock as any)[pointName];
- nodeA = this.appendBlock(inputBlock);
-
- nodeA.x = this._dropPointX - 200;
- nodeA.y = this._dropPointY - 50;
- }
- if (pointA.direction === NodeMaterialConnectionPointDirection.Input) {
- let temp = pointB;
- pointB = pointA;
- pointA = temp;
- let tempNode = nodeA;
- nodeA = nodeB;
- nodeB = tempNode;
- }
- if (pointB.connectedPoint === pointA) {
- return;
- }
- if (pointB === pointA) {
- return;
- }
- if (pointB.direction === pointA.direction) {
- return;
- }
- if (pointB.ownerBlock === pointA.ownerBlock) {
- return;
- }
- // Check compatibility
- let isFragmentOutput = pointB.ownerBlock.getClassName() === "FragmentOutputBlock";
- let compatibilityState = pointA.checkCompatibilityState(pointB);
- if ((pointA.needDualDirectionValidation || pointB.needDualDirectionValidation) && compatibilityState === NodeMaterialConnectionPointCompatibilityStates.Compatible && !(pointA instanceof InputBlock)) {
- compatibilityState = pointB.checkCompatibilityState(pointA);
- }
- if (compatibilityState === NodeMaterialConnectionPointCompatibilityStates.Compatible) {
- if (isFragmentOutput) {
- let fragmentBlock = pointB.ownerBlock as FragmentOutputBlock;
- if (pointB.name === "rgb" && fragmentBlock.rgba.isConnected) {
- nodeB.getLinksForConnectionPoint(fragmentBlock.rgba)[0].dispose();
- } else if (pointB.name === "rgba" && fragmentBlock.rgb.isConnected) {
- nodeB.getLinksForConnectionPoint(fragmentBlock.rgb)[0].dispose();
- }
- }
- } else {
- let message = "";
- switch (compatibilityState) {
- case NodeMaterialConnectionPointCompatibilityStates.TypeIncompatible:
- message = "Cannot connect two different connection types";
- break;
- case NodeMaterialConnectionPointCompatibilityStates.TargetIncompatible:
- message = "Source block can only work in fragment shader whereas destination block is currently aimed for the vertex shader";
- break;
- }
- this.props.globalState.onErrorMessageDialogRequiredObservable.notifyObservers(message);
- return;
- }
- if (pointB.isConnected) {
- let links = nodeB.getLinksForConnectionPoint(pointB);
- links.forEach(link => {
- link.dispose();
- });
- }
- if (pointB.ownerBlock.inputsAreExclusive) { // Disconnect all inputs if block has exclusive inputs
- pointB.ownerBlock.inputs.forEach(i => {
- let links = nodeB.getLinksForConnectionPoint(i);
- links.forEach(link => {
- link.dispose();
- });
- })
- }
- pointA.connectTo(pointB);
- this.connectPorts(pointA, pointB);
- nodeB.refresh();
- this.props.globalState.onRebuildRequiredObservable.notifyObservers();
- }
- processEditorData(editorData: IEditorData, isImportingAFrame = false) {
- if (!isImportingAFrame) {
- const frames = this._frames.splice(0);
- for (var frame of frames) {
- frame.dispose();
- }
- this._frames = [];
- this.x = editorData.x || 0;
- this.y = editorData.y || 0;
- this.zoom = editorData.zoom || 1;
- }
- // Frames
- if (editorData.frames) {
- if (isImportingAFrame) {
- var frame = GraphFrame.Parse(editorData.frames[editorData.frames.length - 1], this, editorData.map);
- this._frames.push(frame);
- this.globalState.onSelectionChangedObservable.notifyObservers(frame);
- } else {
- for (var frameData of editorData.frames) {
- var frame = GraphFrame.Parse(frameData, this, editorData.map);
- this._frames.push(frame);
- }
- }
- }
- }
-
- render() {
- return (
- <div id="graph-canvas"
- onWheel={evt => this.onWheel(evt)}
- onPointerMove={evt => this.onMove(evt)}
- onPointerDown={evt => this.onDown(evt)}
- onPointerUp={evt => this.onUp(evt)}
- >
- <div id="graph-container">
- <div id="graph-canvas-container">
- </div>
- <div id="frame-container">
- </div>
- <svg id="graph-svg-container">
- </svg>
- <div id="selection-container">
- </div>
- </div>
- </div>
- );
- }
- }
|