12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- 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, IFrameData } from '../nodeLocationInfo';
- import { FrameNodePort } from './frameNodePort';
- import { Button } from 'babylonjs-gui/2D/controls/button';
- import { Engine } from 'babylonjs/Engines/engine';
- import { Scene } from 'babylonjs/scene';
- import { Container, Rectangle } from 'babylonjs-gui';
- import { GuiNode } from './guiNode';
- 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 _selectedGuiNodes: GuiNode[] = [];
- 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 selectedGuiNodes() {
- return this._selectedGuiNodes;
- }
- 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){
- 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) {
- return;
- }
- 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;
- }
- let linksToNotifyForDispose: Nullable<NodeLink[]> = null;
- if (pointB.isConnected) {
- let links = nodeB.getLinksForConnectionPoint(pointB);
- linksToNotifyForDispose = links.slice();
- links.forEach(link => {
- link.dispose(false);
- });
- }
- if (pointB.ownerBlock.inputsAreExclusive) { // Disconnect all inputs if block has exclusive inputs
- pointB.ownerBlock.inputs.forEach(i => {
- let links = nodeB.getLinksForConnectionPoint(i);
- if (!linksToNotifyForDispose) {
- linksToNotifyForDispose = links.slice();
- } else {
- linksToNotifyForDispose.push(...links.slice());
- }
- links.forEach(link => {
- link.dispose(false);
- });
- })
- }
- pointA.connectTo(pointB);
- this.connectPorts(pointA, pointB);
- if (pointB.innerType === NodeMaterialBlockConnectionPointTypes.AutoDetect) {
- // need to potentially propagate the type of pointA to other ports of blocks connected to owner of pointB
- const refreshNode = (node: GraphNode) => {
- node.refresh();
- const links = node.links;
- // refresh first the nodes so that the right types are assigned to the auto-detect ports
- links.forEach((link) => {
- const nodeA = link.nodeA, nodeB = link.nodeB;
- if (!visitedNodes.has(nodeA)) {
- visitedNodes.add(nodeA);
- refreshNode(nodeA);
- }
- if (nodeB && !visitedNodes.has(nodeB)) {
- visitedNodes.add(nodeB);
- refreshNode(nodeB);
- }
- });
- // then refresh the links to display the right color between ports
- links.forEach((link) => {
- if (!visitedLinks.has(link)) {
- visitedLinks.add(link);
- link.update();
- }
- });
- };
- const visitedNodes = new Set<GraphNode>([nodeA]);
- const visitedLinks = new Set<NodeLink>([nodeB.links[nodeB.links.length - 1]]);
- refreshNode(nodeB);
- } else {
- nodeB.refresh();
- }
- linksToNotifyForDispose?.forEach((link) => {
- link.onDisposedObservable.notifyObservers(link);
- link.onDisposedObservable.clear();
- });
- this.props.globalState.onRebuildRequiredObservable.notifyObservers();
- }
- processEditorData(editorData: IEditorData) {
- 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) {
- for (var frameData of editorData.frames) {
- var frame = GraphFrame.Parse(frameData, this, editorData.map);
- this._frames.push(frame);
- }
- }
- }
- addFrame(frameData: IFrameData) {
- const frame = GraphFrame.Parse(frameData, this, this.props.globalState.nodeMaterial.editorData.map);
- this._frames.push(frame);
- this.globalState.onSelectionChangedObservable.notifyObservers(frame);
- }
- createGUICanvas()
- {
- // Get the canvas element from the DOM.
- const canvas = document.getElementById("graph-canvas") as HTMLCanvasElement;
- // Associate a Babylon Engine to it.
- const engine = new BABYLON.Engine(canvas);
-
- // Create our first scene.
- var scene = new BABYLON.Scene(engine);
- // This creates and positions a free camera (non-mesh)
- var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
-
- // This targets the camera to scene origin
- camera.setTarget(BABYLON.Vector3.Zero());
-
- // This attaches the camera to the canvas
- camera.attachControl(true);
-
- // GUI
- this._advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
-
- engine.runRenderLoop(() => {this.updateGUIs(); scene.render()});
- }
-
- public addNewButton()
- {
- if(!this._advancedTexture)
- {
- this.createGUICanvas();
- }
- var button1 = BABYLON.GUI.Button.CreateSimpleButton("but1", "Click Me");
- button1.width = "150px"
- button1.height = "40px";
- button1.color = "white";
- button1.cornerRadius = 20;
- button1.background = "green";
- button1.onPointerUpObservable.add(function() {
- });
- this._guis.push(button1);
- this._advancedTexture.addControl(button1);
- }
- private _guis: BABYLON.GUI.Container[] = [];
- private _advancedTexture: BABYLON.GUI.AdvancedDynamicTexture;
- updateGUIs()
- {
- this._guis.forEach(element => {
- element.paddingLeft == 1;
- //should create a new component type that has the contrainer ias a property.
- //with then have the update information.
-
- });
- }
-
- render() {
- //var canv = new HTMLCanvasElement;
- var canv = <canvas 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>
- </canvas>
- return (
- canv
- );
- }
- }
|