David Catuhe 5 anni fa
parent
commit
59e6bc2129

+ 11 - 0
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -11,6 +11,7 @@ import { SerializationTools } from '../../serializationTools';
 import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
 import { DataStorage } from '../../dataStorage';
 import { GraphNode } from '../../diagram/graphNode';
+import { SliderLineComponent } from '../../sharedComponents/sliderLineComponent';
 require("./propertyTab.scss");
 
 interface IPropertyTabComponentProps {
@@ -71,6 +72,7 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                 </div>
             );
         }
+        let gridSize = DataStorage.ReadNumber("GridSize", 20);
 
         return (
             <div id="propertyTab">
@@ -102,6 +104,15 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                                 DataStorage.StoreBoolean("EmbedTextures", value);
                             }}
                         />
+                        <SliderLineComponent label="Grid size" minimum={0} maximum={50} step={5} 
+                            decimalCount={0} 
+                            directValue={gridSize}
+                            onChange={value => {
+                                DataStorage.StoreNumber("GridSize", value);                                
+                                this.props.globalState.onGridSizeChanged.notifyObservers();
+                                this.forceUpdate();
+                            }}
+                        />
                     </LineContainerComponent>
                     <LineContainerComponent title="FILE">                        
                         <FileButtonLineComponent label="Load" onClick={(file) => this.load(file)} accept=".json" />

+ 13 - 3
nodeEditor/src/diagram/graphCanvas.scss

@@ -7,6 +7,10 @@
     user-select: none;
     overflow: hidden;
 
+    background-image:
+        linear-gradient(to right, grey 1px, transparent 1px),
+        linear-gradient(to bottom, grey 1px, transparent 1px);  
+
     #graph-container {
         width: 100%;
         height: 100%;
@@ -14,7 +18,7 @@
         top: 0;
         transform-origin: left top;
         display: grid;
-        grid-template: 100% 100%;
+        grid-template: 100% 100%;        
 
         #graph-svg-container {
             grid-row: 1;
@@ -25,7 +29,11 @@
             overflow: visible; 
             
             .link {
-                stroke-width: 4px;                       
+                stroke-width: 4px;    
+                &.selected {                    
+                    stroke: white !important;
+                    stroke-dasharray: 10, 2;
+                }                   
             }
 
             .selection-link {
@@ -43,7 +51,7 @@
             grid-column: 1;
             position: relative;
             width: 100%;
-            height: 100%;
+            height: 100%;                  
 
             .visual {
                 z-index: 1;
@@ -72,6 +80,8 @@
                     font-size: 16px;
                     text-align: center;
                     margin-top: -1px;
+                    margin-left: -1px;
+                    margin-right: -1px;
                     white-space: nowrap;
                     text-overflow: ellipsis;
                     overflow: hidden;

+ 49 - 9
nodeEditor/src/diagram/graphCanvas.tsx

@@ -10,6 +10,7 @@ import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection, Node
 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 '../dataStorage';
 
 require("./graphCanvas.scss");
 
@@ -18,6 +19,7 @@ export interface IGraphCanvasComponentProps {
 }
 
 export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentProps> {
+    private _hostCanvas: HTMLDivElement;
     private _graphCanvas: HTMLDivElement;
     private _svgCanvas: HTMLElement;
     private _rootContainer: HTMLDivElement;
@@ -34,11 +36,25 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
     private _selectedLink: Nullable<NodeLink> = null;
     private _candidateLink: Nullable<NodeLink> = null;
     private _candidatePort: Nullable<NodePort> = null;
+    private _gridSize = 20;
 
     private _altKeyIsPressed = false;
     private _ctrlKeyIsPressed = false;
     private _oldY = -1;
 
+    public get gridSize() {
+        return this._gridSize;
+    }
+
+    public set gridSize(value: number) {
+        if (this._gridSize === value) {
+            return;
+        }
+        this._gridSize = value;
+
+        this._hostCanvas.style.backgroundSize = `${value}px ${value}px`;
+    }
+
     public get globalState(){
         return this.props.globalState;
     }
@@ -104,6 +120,8 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
     constructor(props: IGraphCanvasComponentProps) {
         super(props);
 
+        this.gridSize = DataStorage.ReadNumber("GridSize", 20);
+
         props.globalState.onSelectionChangedObservable.add(selection => {
             
             if (!selection) {
@@ -128,6 +146,10 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
             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;            
@@ -139,6 +161,14 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
         }, false);     
     }
 
+    public getGridPosition(position: number) {
+        let gridSize = this.gridSize;
+		if (gridSize === 0) {
+			return position;
+		}
+		return gridSize * Math.floor((position + gridSize / 2) / gridSize);
+	}
+
     updateTransform() {
         this._rootContainer.style.transform = `translate(${this._x}px, ${this._y}px) scale(${this._zoom})`;
     }
@@ -255,8 +285,8 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
         dagreNodes.forEach(dagreNode => {
             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.x = this.getGridPosition(dagreNode.x - dagreNode.width / 2);
+                    node.y = this.getGridPosition(dagreNode.y - dagreNode.height / 2);
                     return;
                 }
             }
@@ -264,6 +294,7 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
     }
 
     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;        
@@ -345,15 +376,24 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
     }
 
     onWheel(evt: React.WheelEvent) {
-        let delta = -evt.deltaY / 50;
+        let delta = evt.deltaY < 0 ? 0.1 : -0.1;
 
-        if (evt.ctrlKey && delta % 1 !== 0) {
-            delta /= 3;
-        } else {
-            delta /= 60;
-        }
+        let oldZoom = this.zoom;
+        this.zoom = Math.min(Math.max(0.1, this.zoom + delta), 4);
+
+        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.zoom = Math.min(Math.max(0.4, this.zoom + delta), 4);
+        this.x = this.x - widthDiff * xFactor;
+        this.y = this.y - heightDiff * yFactor;
 
         evt.stopPropagation();
     }

+ 13 - 4
nodeEditor/src/diagram/graphNode.ts

@@ -212,13 +212,22 @@ export class GraphNode {
             return;
         }
 
+        let newX = this._ownerCanvas.getGridPosition(evt.clientX - this._mouseStartPointX);
+        let newY = this._ownerCanvas.getGridPosition(evt.clientY - this._mouseStartPointY);
+
         for (var selectedNode of this._ownerCanvas.selectedNodes) {
-            selectedNode.x += (evt.clientX - this._mouseStartPointX) / this._ownerCanvas.zoom;
-            selectedNode.y += (evt.clientY - this._mouseStartPointY) / this._ownerCanvas.zoom;
+            selectedNode.x += newX / this._ownerCanvas.zoom;
+            selectedNode.y += newY / this._ownerCanvas.zoom;
+        }
+
+        if (Math.abs(newX) > 0) { 
+            this._mouseStartPointX = evt.clientX;
+        }
+
+        if (Math.abs(newY) > 0) {
+            this._mouseStartPointY = evt.clientY;   
         }
 
-        this._mouseStartPointX = evt.clientX;
-        this._mouseStartPointY = evt.clientY;   
         evt.stopPropagation();
     }
 

+ 4 - 2
nodeEditor/src/diagram/nodeLink.ts

@@ -80,9 +80,11 @@ export class NodeLink {
         }
 
         this._onSelectionChangedObserver = this._graphCanvas.globalState.onSelectionChangedObservable.add(selection => {
-            if (selection === this) {
+            if (selection === this) {                
+                this._path.classList.add("selected");
                 this._selectionPath.classList.add("selected");
-            } else {
+            } else {                
+                this._path.classList.remove("selected");
                 this._selectionPath.classList.remove("selected");
             }
         });

+ 1 - 0
nodeEditor/src/globalState.ts

@@ -33,6 +33,7 @@ export class GlobalState {
     onCandidateLinkMoved = new Observable<Nullable<Vector2>>();    
     onCandidatePortSelected = new Observable<Nullable<NodePort>>();
     onGetNodeFromBlock: (block: NodeMaterialBlock) => GraphNode;
+    onGridSizeChanged = new Observable<void>();
     previewMeshType: PreviewMeshType;
     previewMeshFile: File;
     rotatePreview: boolean;

+ 10 - 14
nodeEditor/src/graphEditor.tsx

@@ -133,10 +133,6 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             }
         });
 
-        this.props.globalState.onUpdateRequiredObservable.add(() => {
-            // Do nothing for now
-        });
-
         this.props.globalState.onZoomToFitRequiredObservable.add(() => {
             this.zoomToFit();
         });
@@ -227,8 +223,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                         y = currentY;
                     }
 
-                    newNode.x = x;
-                    newNode.y = y;
+                    newNode.x = this._graphCanvas.getGridPosition(x);
+                    newNode.y = this._graphCanvas.getGridPosition(y);
                 }
             }
 
@@ -301,8 +297,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             for (var location of locations) {
                 for (var node of this._graphCanvas.nodes) {
                     if (node.block && node.block.uniqueId === location.blockId) {
-                        node.x = location.x;
-                        node.y = location.y;
+                        node.x = this._graphCanvas.getGridPosition(location.x);
+                        node.y = this._graphCanvas.getGridPosition(location.y);
                         break;
                     }
                 }
@@ -372,11 +368,11 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
             newNode = this.createNodeFromObject(block);
         };
 
-        let x = (event.clientX - event.currentTarget.offsetLeft - this._graphCanvas.x - this.NodeWidth) / this._graphCanvas.zoom;
-        let y = (event.clientY - event.currentTarget.offsetTop - this._graphCanvas.y - 20) / this._graphCanvas.zoom;
+        let x = this._graphCanvas.getGridPosition(event.clientX - event.currentTarget.offsetLeft - this._graphCanvas.x - this.NodeWidth);
+        let y = this._graphCanvas.getGridPosition(event.clientY - event.currentTarget.offsetTop - this._graphCanvas.y - 20);
         
-        newNode.x = x;
-        newNode.y = y;
+        newNode.x = x / this._graphCanvas.zoom;
+        newNode.y = y / this._graphCanvas.zoom;
 
         this.props.globalState.onSelectionChangedObservable.notifyObservers(newNode);
 
@@ -390,8 +386,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                 let connectedNode = existingNodes[0];
 
                 if (connectedNode.x === 0 && connectedNode.y === 0) {
-                    connectedNode.x = x
-                    connectedNode.y = y;
+                    connectedNode.x = this._graphCanvas.getGridPosition(x) / this._graphCanvas.zoom; 
+                    connectedNode.y = this._graphCanvas.getGridPosition(y) / this._graphCanvas.zoom;
                     y += 80;
                 }
             }