@elicdavis/node-flow 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.html CHANGED
@@ -312,10 +312,40 @@ node.addOutput({ name: "sum", type: "float32" })
312
312
  ],
313
313
  });
314
314
 
315
+ const manyPortsNode = new FlowNode({
316
+ position: {
317
+ x: 1200,
318
+ y: 450
319
+ },
320
+ title: "Many Ports",
321
+ subTitle: "Zoom stress test",
322
+ info: "Node with lots of ports — zoom in/out to check height stability",
323
+ inputs: [
324
+ { name: "in 0", type: "float32" },
325
+ { name: "in 1", type: "float32" },
326
+ { name: "in 2", type: "float32" },
327
+ { name: "in 3", type: "float32" },
328
+ { name: "in 4", type: "float32" },
329
+ { name: "in 5", type: "float32" },
330
+ { name: "in 6", type: "float32" },
331
+ { name: "in 7", type: "float32" },
332
+ { name: "in 8", type: "float32" },
333
+ { name: "in 9", type: "float32" },
334
+ ],
335
+ outputs: [
336
+ { name: "out 0", type: "float32" },
337
+ { name: "out 1", type: "float32" },
338
+ { name: "out 2", type: "float32" },
339
+ { name: "out 3", type: "float32" },
340
+ { name: "out 4", type: "float32" },
341
+ ],
342
+ });
343
+
315
344
  graph.addNode(sumNode)
316
345
  graph.addNode(aNode)
317
346
  graph.addNode(bNode)
318
347
  graph.addNode(arrNode)
348
+ graph.addNode(manyPortsNode)
319
349
 
320
350
  graph.connectNodes(aNode, 0, sumNode, 0)
321
351
  graph.connectNodes(bNode, 0, sumNode, 1)
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@elicdavis/node-flow",
3
3
  "description": "build node graphs",
4
4
  "author": "Eli C Davis",
5
- "version": "0.1.13",
5
+ "version": "0.1.15",
6
6
  "license": "MIT",
7
7
  "keywords": [
8
8
  "nodes",
package/src/camera.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { Vector2, Zero } from "./types/vector2";
2
2
 
3
+ /** Serializable camera orientation for save/restore. */
4
+ export interface CameraOrientation {
5
+ position: Vector2;
6
+ zoom: number;
7
+ }
8
+
3
9
  export class Camera {
4
10
 
5
11
  zoom: number;
@@ -22,9 +28,25 @@ export class Camera {
22
28
  out.y = this.position.y + (graphPosition.y * this.zoom);
23
29
  }
24
30
 
31
+ getOrientation(): CameraOrientation {
32
+ return {
33
+ position: {
34
+ x: this.position.x,
35
+ y: this.position.y,
36
+ },
37
+ zoom: this.zoom,
38
+ };
39
+ }
40
+
41
+ setOrientation(orientation: CameraOrientation): void {
42
+ this.position.x = orientation.position.x;
43
+ this.position.y = orientation.position.y;
44
+ this.zoom = orientation.zoom;
45
+ }
46
+
25
47
  reset(): void {
26
48
  this.zoom = 1;
27
49
  this.position.x = 0;
28
50
  this.position.y = 0;
29
51
  }
30
- }
52
+ }
package/src/graph.ts CHANGED
@@ -14,7 +14,8 @@ import { ConnectionRendererConfiguration, NodeAddedCallback, NodeRemovedCallback
14
14
  import { Connection } from './connection';
15
15
  import { Publisher } from './nodes/publisher';
16
16
  import { VectorPool } from './types/pool';
17
- import { Camera } from './camera';
17
+ import { Camera, CameraOrientation } from './camera';
18
+ export { CameraOrientation };
18
19
  import { PassSubsystem } from './pass/subsystem';
19
20
  import { QuickMenu } from './quickMenu';
20
21
 
@@ -388,6 +389,27 @@ export class NodeFlowGraph {
388
389
  return this.#mainNodeSubsystem.getSelectedNodes();
389
390
  }
390
391
 
392
+ /**
393
+ * Returns a copy of the current camera orientation (screen-space pan + zoom).
394
+ */
395
+ getCamera(): CameraOrientation {
396
+ return this.#camera.getOrientation();
397
+ }
398
+
399
+ /**
400
+ * Restores a previously saved camera orientation.
401
+ */
402
+ setCamera(orientation: CameraOrientation): void {
403
+ this.#camera.setOrientation(orientation);
404
+ }
405
+
406
+ /**
407
+ * Resets the camera to the default orientation (origin, zoom 1).
408
+ */
409
+ resetCamera(): void {
410
+ this.#camera.reset();
411
+ }
412
+
391
413
  /**
392
414
  * Adjusts camera position and zoom so all nodes fit in view (~10% padding).
393
415
  * Falls back to camera.reset() when there are no nodes.
package/src/node.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Port, PortConfig, PortType } from "./port";
2
2
  import { FontWeight, TextStyle, TextStyleConfig, TextStyleFallback } from "./styles/text";
3
3
  import { Box, InBox } from "./types/box";
4
- import { CopyVector2, Distance, Vector2, Zero } from "./types/vector2";
4
+ import { CopyVector2, Distance, ScaleVector, Vector2, Zero } from "./types/vector2";
5
5
  import { Widget } from './widgets/widget';
6
6
  import { List } from './types/list';
7
7
  import { BoxStyle, BoxStyleConfig, BoxStyleWithFallback } from "./styles/box";
@@ -784,56 +784,55 @@ export class FlowNode {
784
784
 
785
785
  public calculateBounds(ctx: CanvasRenderingContext2D, camera: Camera): Box {
786
786
  const tempMeasurement = Zero();
787
- const zoom = camera.zoom;
788
- const scaledPadding = this.#padding * zoom;
789
- const doubleScaledPadding = scaledPadding * 2;
790
- const scaledElementSpacing = this.#elementSpacing * zoom;
787
+
788
+ const doublePadding = this.#padding * 2;
791
789
 
792
790
  const screenSpacePosition = Zero();
793
791
  camera.graphSpaceToScreenSpace(this.#position, screenSpacePosition);
794
792
 
795
793
  const size = Zero();
796
- this.#title.size(ctx, zoom, size);
794
+ this.#title.size(ctx, 1, size);
797
795
 
798
796
  const subtitleSize = Zero();
799
- this.#subTitle.size(ctx, zoom, subtitleSize);
797
+ this.#subTitle.size(ctx, 1, subtitleSize);
800
798
  size.x = Math.max(size.x, subtitleSize.x);
801
799
 
802
800
  if (this.#infoText !== "") {
803
801
  size.x += (size.y * 4);
804
802
  }
805
803
 
806
- size.x += doubleScaledPadding;
807
- size.y += doubleScaledPadding + (scaledElementSpacing * this.#input.length);
804
+ size.x += doublePadding;
805
+ size.y += doublePadding + (this.#elementSpacing * this.#input.length);
808
806
 
809
807
  for (let i = 0; i < this.#input.length; i++) {
810
808
  const port = this.#input[i];
811
- this.#portTextStyle.measure(ctx, zoom, port.getDisplayName(), tempMeasurement);
809
+ this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
812
810
  size.y += tempMeasurement.y;
813
- size.x = Math.max(size.x, tempMeasurement.x + doubleScaledPadding)
811
+ size.x = Math.max(size.x, tempMeasurement.x + doublePadding)
814
812
  }
815
813
 
816
- size.y += (scaledElementSpacing * this.#output.length);
814
+ size.y += (this.#elementSpacing * this.#output.length);
817
815
 
818
816
  for (let i = 0; i < this.#output.length; i++) {
819
817
  const port = this.#output[i];
820
- this.#portTextStyle.measure(ctx, zoom, port.getDisplayName(), tempMeasurement);
818
+ this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
821
819
  size.y += tempMeasurement.y;
822
- size.x = Math.max(size.x, tempMeasurement.x + doubleScaledPadding)
820
+ size.x = Math.max(size.x, tempMeasurement.x + doublePadding)
823
821
  }
824
822
 
825
- size.y += (scaledElementSpacing * this.#widgets.length);
823
+ size.y += (this.#elementSpacing * this.#widgets.length);
826
824
  for (let i = 0; i < this.#widgets.length; i++) {
827
825
  const element = this.#widgets[i];
828
826
  const eleSize = element.Size();
829
- size.y += eleSize.y * zoom;
830
- size.x = Math.max(size.x, (eleSize.x * zoom) + doubleScaledPadding)
827
+ size.y += eleSize.y
828
+ size.x = Math.max(size.x, (eleSize.x) + doublePadding)
831
829
  }
832
830
 
833
831
  // Add some padding at the end!
834
- size.y += scaledElementSpacing
832
+ size.y += this.#elementSpacing
835
833
 
836
- size.x = Math.max(size.x, MINIMUM_NODE_WIDTH * zoom)
834
+ size.x = Math.max(size.x, MINIMUM_NODE_WIDTH)
835
+ ScaleVector(size, camera.zoom);
837
836
 
838
837
  return {
839
838
  Position: screenSpacePosition,
@@ -1161,11 +1160,12 @@ export class FlowNode {
1161
1160
  for (let i = 0; i < this.#input.length; i++) {
1162
1161
  ctx.textAlign = TextAlign.Left;
1163
1162
  const port = this.#input[i];
1164
- this.#portTextStyle.measure(ctx, camera.zoom, port.getDisplayName(), tempMeasurement);
1163
+ this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
1164
+ const portHeight = tempMeasurement.y * camera.zoom;
1165
1165
  const position = VectorPool.get();
1166
1166
 
1167
1167
  position.x = nodeBounds.Position.x;
1168
- position.y = startY + (tempMeasurement.y / 2);
1168
+ position.y = startY + (portHeight / 2);
1169
1169
 
1170
1170
  // Text
1171
1171
  this.#portTextStyle.setupStyle(ctx, camera.zoom);
@@ -1174,7 +1174,7 @@ export class FlowNode {
1174
1174
  // Port
1175
1175
  this.#inputPortPositions.Push(port.render(ctx, position, camera, mousePosition, postProcess));
1176
1176
 
1177
- startY += tempMeasurement.y + scaledElementSpacing;
1177
+ startY += portHeight + scaledElementSpacing;
1178
1178
  }
1179
1179
 
1180
1180
  // Output Ports
@@ -1182,10 +1182,11 @@ export class FlowNode {
1182
1182
  ctx.textAlign = TextAlign.Right;
1183
1183
  for (let i = 0; i < this.#output.length; i++) {
1184
1184
  const port = this.#output[i];
1185
- this.#portTextStyle.measure(ctx, camera.zoom, port.getDisplayName(), tempMeasurement);
1185
+ this.#portTextStyle.measure(ctx, 1, port.getDisplayName(), tempMeasurement);
1186
+ const portHeight = tempMeasurement.y * camera.zoom;
1186
1187
  const position = VectorPool.get();
1187
1188
  position.x = rightSide;
1188
- position.y = startY + (tempMeasurement.y / 2);
1189
+ position.y = startY + (portHeight / 2);
1189
1190
 
1190
1191
  // Text
1191
1192
  this.#portTextStyle.setupStyle(ctx, camera.zoom);
@@ -1194,7 +1195,7 @@ export class FlowNode {
1194
1195
  // Port
1195
1196
  this.#outputPortPositions.Push(port.render(ctx, position, camera, mousePosition, postProcess));
1196
1197
 
1197
- startY += tempMeasurement.y + scaledElementSpacing;
1198
+ startY += portHeight + scaledElementSpacing;
1198
1199
  }
1199
1200
 
1200
1201
  // Widgets