aurea-eden 1.25.11 → 1.26.0

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.
@@ -40288,569 +40288,6 @@ class RenderOutputNode extends TempNode {
40288
40288
  }
40289
40289
  const renderOutput = (color2, toneMapping2 = null, outputColorSpace = null) => nodeObject(new RenderOutputNode(nodeObject(color2), toneMapping2, outputColorSpace));
40290
40290
  addMethodChaining("renderOutput", renderOutput);
40291
- class Diagram {
40292
- /**
40293
- * Creates a new Diagram instance.
40294
- * @param {HTMLElement} container - The HTML container element for the diagram.
40295
- */
40296
- constructor(container) {
40297
- this.elements = [];
40298
- this.connectors = [];
40299
- this.mode = "VIEW";
40300
- this.helpers = false;
40301
- this.container = container;
40302
- this.initScene();
40303
- this.initCamera();
40304
- this.setHelpers();
40305
- this.initRenderer();
40306
- this.initLighting();
40307
- this.initControls();
40308
- this.addEventListeners();
40309
- this.animate();
40310
- console.log("THREE", THREE);
40311
- console.log(this);
40312
- }
40313
- /**
40314
- * Initializes the THREE.js scene.
40315
- */
40316
- initScene() {
40317
- this.scene = new Scene();
40318
- this.scene.background = new Color$1(15790320);
40319
- }
40320
- /**
40321
- * Initializes the camera with a perspective projection.
40322
- */
40323
- initCamera() {
40324
- const aspectRatio = window.innerWidth / window.innerHeight;
40325
- this.camera = new PerspectiveCamera(75, aspectRatio, 0.1, 2e3);
40326
- this.camera.position.set(0, 0, 500);
40327
- this.camera.updateProjectionMatrix();
40328
- }
40329
- // initCamera() { // Orthographic
40330
- // // const aspectRatio = this.container.clientWidth / window.innerHeight;
40331
- // const aspectRatio = window.innerWidth / window.innerHeight;
40332
- // const frustumSize = 100;
40333
- // this.camera = new THREE.OrthographicCamera(
40334
- // frustumSize * aspectRatio / -2,
40335
- // frustumSize * aspectRatio / 2,
40336
- // frustumSize / 2,
40337
- // frustumSize / -2,
40338
- // 0.1,
40339
- // 1000
40340
- // );
40341
- // this.camera.position.set(0, 0, 50);
40342
- // }
40343
- /**
40344
- * Sets up helpers (axes, grid, etc.) for the scene.
40345
- */
40346
- setHelpers() {
40347
- this.axesHelper = new AxesHelper(100);
40348
- this.cameraHelper = new CameraHelper(this.camera);
40349
- const size = 400;
40350
- const divisions = 50;
40351
- this.gridHelper = new GridHelper(size, divisions);
40352
- this.cameraDirection = new Vector3$1();
40353
- this.camPositionSpan = document.querySelector("#position");
40354
- this.camLookAtSpan = document.querySelector("#lookingAt");
40355
- this.helpers = false;
40356
- }
40357
- /**
40358
- * Shows the helpers in the scene.
40359
- */
40360
- showHelpers() {
40361
- if (!this.helpers) {
40362
- this.scene.add(this.axesHelper);
40363
- this.scene.add(this.cameraHelper);
40364
- this.scene.add(this.gridHelper);
40365
- this.helpers = true;
40366
- }
40367
- }
40368
- /**
40369
- * Hides the helpers in the scene.
40370
- */
40371
- hideHelpers() {
40372
- if (this.helpers) {
40373
- this.scene.remove(this.axesHelper);
40374
- this.scene.remove(this.cameraHelper);
40375
- this.scene.remove(this.gridHelper);
40376
- this.helpers = false;
40377
- }
40378
- }
40379
- /**
40380
- * Initializes the renderer and attaches it to the container.
40381
- */
40382
- initRenderer() {
40383
- this.renderer = new WebGLRenderer({ antialias: true });
40384
- this.renderer.setSize(window.innerWidth, window.innerHeight);
40385
- document.body.appendChild(this.renderer.domElement);
40386
- console.log("initRenderer", this.container, this.renderer.domElement);
40387
- }
40388
- /**
40389
- * Initializes the lighting for the scene.
40390
- */
40391
- initLighting() {
40392
- let mainLightColor = 16777215;
40393
- let mainLightIntensity = 4;
40394
- let mainLightDistance = 0;
40395
- let mainLightDecay = 0;
40396
- const mainLight = new PointLight(mainLightColor, mainLightIntensity, mainLightDistance, mainLightDecay);
40397
- let mainLightPosX = -1 * (580 / 2) + 1 / 3 * 580;
40398
- let mainLightPosY = -1 * 209 * 4;
40399
- let mainLightPosZ = Math.abs(mainLightPosY);
40400
- mainLight.position.set(mainLightPosX, mainLightPosY, mainLightPosZ);
40401
- this.scene.add(mainLight);
40402
- this.spotLight = new PointLight(16777215, 4, 0, 0);
40403
- this.spotLightPosX = 0;
40404
- this.spotLightPosY = -1 * 209 - 300;
40405
- this.spotLightPosZ = 70;
40406
- this.scene.add(this.spotLight);
40407
- this.spotLightDirection = 1;
40408
- }
40409
- /**
40410
- * Initializes the controls for the camera.
40411
- */
40412
- initControls() {
40413
- this.controls = new MapControls(this.camera, this.renderer.domElement);
40414
- this.controls.enableDamping = true;
40415
- this.controls.dampingFactor = 0.1;
40416
- this.controls.screenSpacePanning = true;
40417
- this.controls.zoomToCursor = true;
40418
- this.controls.saveState();
40419
- }
40420
- /**
40421
- * Adds event listeners for window resize and other interactions.
40422
- */
40423
- addEventListeners() {
40424
- window.addEventListener("resize", this.onWindowResize.bind(this), false);
40425
- }
40426
- // onDocumentMouseDown(event) {
40427
- // this.isDragging = true;
40428
- // this.previousMousePosition = { x: event.offsetX, y: event.offsetY };
40429
- // }
40430
- // onDocumentMouseMove(event) {
40431
- // if (this.isDragging && this.mugGroup) {
40432
- // const deltaMove = {
40433
- // x: event.offsetX - this.previousMousePosition.x,
40434
- // y: event.offsetY - this.previousMousePosition.y
40435
- // };
40436
- // let rotateAngleX = this.toRadians(deltaMove.y * 1);
40437
- // let rotateAngleY = this.toRadians(deltaMove.x * 1);
40438
- // this.currentRotation = this.currentRotation || { x: 0, y: 0 };
40439
- // this.currentRotation.x += rotateAngleX;
40440
- // this.currentRotation.y += rotateAngleY;
40441
- // const maxRotation = Math.PI / 2;
40442
- // this.currentRotation.x = Math.min(Math.max(this.currentRotation.x, -maxRotation), maxRotation);
40443
- // this.pivotGroup.rotation.x = this.currentRotation.x;
40444
- // this.pivotGroup.rotation.y = this.currentRotation.y;
40445
- // this.previousMousePosition = { x: event.offsetX, y: event.offsetY };
40446
- // }
40447
- // }
40448
- // onDocumentMouseUp() {
40449
- // this.isDragging = false;
40450
- // }
40451
- // toRadians(angle) {
40452
- // return angle * (Math.PI / 180);
40453
- // }
40454
- /**
40455
- * Handles window resize events to update the camera and renderer.
40456
- */
40457
- onWindowResize() {
40458
- this.camera.aspect = window.innerWidth / window.innerHeight;
40459
- this.camera.updateProjectionMatrix();
40460
- this.renderer.setSize(window.innerWidth, window.innerHeight);
40461
- }
40462
- /**
40463
- * Animates the scene and updates controls.
40464
- */
40465
- animate() {
40466
- if (this.tween) this.tween.update();
40467
- requestAnimationFrame(this.animate.bind(this));
40468
- this.controls.update();
40469
- this.renderer.render(this.scene, this.camera);
40470
- if (this.spotLightPosX > 500) {
40471
- this.spotLightDirection = -1;
40472
- this.spotLightPosX = 500;
40473
- }
40474
- if (this.spotLightPosX < -500) {
40475
- this.spotLightDirection = 1;
40476
- this.spotLightPosX = -500;
40477
- }
40478
- this.spotLightPosX += 10 * this.spotLightDirection;
40479
- this.spotLight.position.set(this.spotLightPosX, this.spotLightPosY, this.spotLightPosZ);
40480
- }
40481
- reset() {
40482
- this.hideHelpers();
40483
- this.controls.reset();
40484
- this.setMode("VIEW");
40485
- this.fitScreen();
40486
- }
40487
- // ================================================================
40488
- // Diagram arrangement
40489
- // ================================================================
40490
- arrange() {
40491
- if (this.scene.children.length === 0) {
40492
- console.warn("Scene is empty. Cannot calculate center.");
40493
- return;
40494
- }
40495
- const box = new Box3().setFromObject(this.scene);
40496
- const center = box.getCenter(new Vector3$1());
40497
- const translation = new Vector3$1(-center.x, -center.y, -center.z);
40498
- this.scene.children.forEach((child) => {
40499
- if (child instanceof Object3D) {
40500
- child.position.add(translation);
40501
- }
40502
- });
40503
- }
40504
- /**
40505
- * Calculates the optimal zoom distance for the camera to ensure the entire scene is visible.
40506
- *
40507
- * @returns {number} The optimal distance for the camera to fit the scene within the viewport.
40508
- */
40509
- calculateOptimalZoom() {
40510
- const box = new Box3().setFromObject(this.scene);
40511
- const size = box.getSize(new Vector3$1());
40512
- const aspect2 = window.innerWidth / window.innerHeight;
40513
- const fovRad = MathUtils$1.degToRad(this.camera.fov);
40514
- const distanceForWidth = size.x / 2 / (Math.tan(fovRad / 2) * aspect2);
40515
- const distanceForHeight = size.y / 2 / Math.tan(fovRad / 2);
40516
- return Math.max(distanceForWidth, distanceForHeight);
40517
- }
40518
- /**
40519
- * Adjusts the camera to fit the entire scene within the screen.
40520
- *
40521
- * This method calculates the optimal zoom level and positions the camera
40522
- * at a distance that ensures the entire scene is visible, with a small margin.
40523
- * It also updates the camera's orientation to look at the center of the scene
40524
- * and saves the current camera state for later restoration.
40525
- */
40526
- fitScreen() {
40527
- const minZDistance = this.calculateOptimalZoom();
40528
- const margin = 1.05;
40529
- const cameraZ = minZDistance * margin;
40530
- this.camera.position.set(0, 0, cameraZ);
40531
- this.camera.lookAt(0, 0, 0);
40532
- this.camera.updateProjectionMatrix();
40533
- this.controls.saveState();
40534
- this.initialCameraPosition = this.camera.position.clone();
40535
- this.initialTarget = this.controls.target.clone();
40536
- }
40537
- /**
40538
- * Centers the diagram by moving the camera to its initial position and target.
40539
- * This method uses the Tween.js library to animate the camera movement.
40540
- *
40541
- * Preconditions:
40542
- * - `this.initialCameraPosition` and `this.initialTarget` must be defined.
40543
- *
40544
- * Behavior:
40545
- * - If the initial camera position or target is not defined, a warning is logged, and the method exits.
40546
- * - Animates the camera's position and the controls' target to their initial states over 1200 milliseconds.
40547
- * - Uses a Quartic easing function for smooth animation.
40548
- *
40549
- * Dependencies:
40550
- * - Tween.js library for animation.
40551
- *
40552
- * @returns {void}
40553
- */
40554
- center() {
40555
- if (!this.initialCameraPosition || !this.initialTarget) {
40556
- console.warn("Initial camera position or target is not defined.");
40557
- return;
40558
- }
40559
- const from = {
40560
- cameraPositionX: this.camera.position.x,
40561
- cameraPositionY: this.camera.position.y,
40562
- cameraPositionZ: this.camera.position.z,
40563
- controlsTargetX: this.controls.target.x,
40564
- controlsTargetY: this.controls.target.y,
40565
- controlsTargetZ: this.controls.target.z
40566
- };
40567
- const to = {
40568
- cameraPositionX: this.initialCameraPosition.x,
40569
- cameraPositionY: this.initialCameraPosition.y,
40570
- cameraPositionZ: this.initialCameraPosition.z,
40571
- controlsTargetX: this.initialTarget.x,
40572
- controlsTargetY: this.initialTarget.y,
40573
- controlsTargetZ: this.initialTarget.z
40574
- };
40575
- const camera = this.camera;
40576
- const controls = this.controls;
40577
- this.tween = new Tween$1(from).to(to, 1200).easing(Easing.Quartic.Out).onUpdate(function() {
40578
- camera.position.set(
40579
- from.cameraPositionX,
40580
- from.cameraPositionY,
40581
- from.cameraPositionZ
40582
- );
40583
- controls.target.set(
40584
- from.controlsTargetX,
40585
- from.controlsTargetY,
40586
- from.controlsTargetZ
40587
- );
40588
- }).onComplete(function() {
40589
- }).start();
40590
- }
40591
- /**
40592
- * Rotates the diagram around the Y axis by a specified angle in degrees.
40593
- * The method ensures the diagram is centered and calculates the new camera
40594
- * and target positions based on the provided angle.
40595
- *
40596
- * @param {number} targetAngle - The angle in degrees to rotate the diagram (e.g., 60).
40597
- * @returns {void} - Does not return a value.
40598
- *
40599
- * @throws {Error} Logs a warning if the initial camera position or target is not defined.
40600
- *
40601
- * @example
40602
- * // Rotate the diagram by 60 degrees
40603
- * diagram.rotate(60);
40604
- */
40605
- rotate(targetAngle) {
40606
- if (!this.initialCameraPosition || !this.initialTarget) {
40607
- console.warn("Initial camera position or target is not defined.");
40608
- return;
40609
- }
40610
- const radius = Math.sqrt(
40611
- this.initialCameraPosition.y * this.initialCameraPosition.y + this.initialCameraPosition.z * this.initialCameraPosition.z
40612
- );
40613
- const from = {
40614
- cameraPositionX: this.camera.position.x,
40615
- cameraPositionY: this.camera.position.y,
40616
- cameraPositionZ: this.camera.position.z,
40617
- controlsTargetX: this.controls.target.x,
40618
- controlsTargetY: this.controls.target.y,
40619
- controlsTargetZ: this.controls.target.z
40620
- };
40621
- const targetAngleRad = MathUtils$1.degToRad(targetAngle);
40622
- const to = {
40623
- cameraPositionX: this.initialCameraPosition.x,
40624
- cameraPositionY: radius * Math.sin(targetAngleRad),
40625
- cameraPositionZ: radius * Math.cos(targetAngleRad),
40626
- controlsTargetX: this.initialTarget.x,
40627
- controlsTargetY: this.initialTarget.y,
40628
- controlsTargetZ: this.initialTarget.z
40629
- };
40630
- console.log("rotate() -> from:", from);
40631
- console.log("rotate() -> to:", to);
40632
- const camera = this.camera;
40633
- const controls = this.controls;
40634
- this.tween = new Tween$1(from).to(to, 1200).easing(Easing.Quartic.Out).onUpdate(function() {
40635
- camera.position.set(
40636
- from.cameraPositionX,
40637
- from.cameraPositionY,
40638
- from.cameraPositionZ
40639
- );
40640
- controls.target.set(
40641
- from.controlsTargetX,
40642
- from.controlsTargetY,
40643
- from.controlsTargetZ
40644
- );
40645
- }).start();
40646
- }
40647
- // ================================================================
40648
- // Diagram modes
40649
- // ================================================================
40650
- /**
40651
- * Removes all elements of type 'ValueBarShape' from the diagram.
40652
- * Iterates through the `elements` array in reverse order to safely remove
40653
- * elements without affecting the iteration process. For each matching element,
40654
- * it removes the element from its parent (and thus from the scene) and also
40655
- * removes it from the `elements` array.
40656
- */
40657
- removeValueBars() {
40658
- for (let i = this.elements.length - 1; i >= 0; i--) {
40659
- const element2 = this.elements[i];
40660
- if (element2.type === "ValueBarShape") {
40661
- if (element2.parent) {
40662
- element2.parent.remove(element2);
40663
- }
40664
- this.scene.remove(element2);
40665
- this.elements.splice(i, 1);
40666
- }
40667
- }
40668
- }
40669
- /**
40670
- * Adds value bars to the diagram to visualize the elements' parameters.
40671
- *
40672
- * This method processes the elements in the diagram, calculates the height
40673
- * of the bars based on their parameter values, and assigns a color to each
40674
- * bar based on a normalized value. The bars are then added to the scene.
40675
- *
40676
- * @method
40677
- * @memberof Diagram
40678
- * @description
40679
- * - Filters elements to include only those with a defined `parameters.value`.
40680
- * - Calculates the range of parameter values to normalize them.
40681
- * - Assigns a color to each bar using an HSL color scale (green to red).
40682
- * - Calls the `valueBar` method on each element to set the bar's height and color.
40683
- *
40684
- * @example
40685
- * // Assuming `diagram` is an instance of Diagram with elements having parameters:
40686
- * diagram.addValueBars();
40687
- *
40688
- * @throws {Error} If no elements with `parameters.value` are found.
40689
- */
40690
- addValueBars() {
40691
- const elements = this.elements.filter((el) => el.parameters && el.parameters.value !== void 0);
40692
- if (elements.length === 0) {
40693
- throw new Error("No elements with `parameters.value` found.");
40694
- }
40695
- const max2 = Math.max(...elements.map((el) => el.parameters.value));
40696
- const min2 = 0;
40697
- const range = max2 - min2;
40698
- elements.forEach((element2, i) => {
40699
- const value = element2.parameters.value;
40700
- const normalizedValue = (value - min2) / range;
40701
- const color2 = new Color$1(`hsl(${(normalizedValue * 120).toString(10)}, 100%, 50%)`);
40702
- element2.addValueBar(normalizedValue * 100, color2);
40703
- });
40704
- }
40705
- /**
40706
- * Sets the mode of the diagram and adjusts its state accordingly.
40707
- *
40708
- * @param {string} mode - The mode to set. Possible values are:
40709
- * - 'EDIT': Sets the diagram to edit mode and resets rotation.
40710
- * - 'VIEW': Sets the diagram to view mode and resets rotation.
40711
- * - 'ANALYZE': Sets the diagram to analyze mode, rotates it to -60 degrees,
40712
- * and adds value bars.
40713
- * - Any other value will log a warning about an unknown mode.
40714
- */
40715
- setMode(mode) {
40716
- this.removeValueBars();
40717
- this.mode = mode;
40718
- switch (mode) {
40719
- case "EDIT":
40720
- case "VIEW":
40721
- this.rotate(0);
40722
- break;
40723
- case "ANALYZE":
40724
- this.rotate(-65);
40725
- this.addValueBars();
40726
- break;
40727
- default:
40728
- console.warn(`Unknown mode: ${mode}`);
40729
- }
40730
- }
40731
- // ================================================================
40732
- // Diagram elements
40733
- // ================================================================
40734
- /**
40735
- * Adds an element to the diagram.
40736
- * @param {Object3D} element - The element to add.
40737
- * @param {Vector3} [position] - The position to place the element.
40738
- * @returns {Object3D} The added element.
40739
- */
40740
- addElement(element2, position) {
40741
- this.elements.push(element2);
40742
- this.scene.add(element2);
40743
- if (position) element2.position.set(position.x, position.y, 0);
40744
- element2.setDiagram(this);
40745
- return element2;
40746
- }
40747
- /**
40748
- * Removes an element from the diagram by its ID.
40749
- * @param {string} elementId - The ID of the element to remove.
40750
- */
40751
- removeElement(elementId) {
40752
- const element2 = this.elements.find((el) => el.id === elementId);
40753
- if (element2) {
40754
- this.scene.remove(element2);
40755
- this.elements = this.elements.filter((el) => el.id !== elementId);
40756
- }
40757
- }
40758
- /**
40759
- * Retrieves an element from the `elements` array by its unique `elementId`.
40760
- *
40761
- * @param {string} elementId - The unique identifier of the element to find.
40762
- * @returns {Object|undefined} The element with the matching `elementId`, or `undefined` if not found.
40763
- */
40764
- getElementById(elementId) {
40765
- return this.elements.find((el) => el.elementId === elementId);
40766
- }
40767
- /**
40768
- * Retrieves the elements of the diagram.
40769
- *
40770
- * @returns {Array} The array of elements in the diagram.
40771
- */
40772
- getElements() {
40773
- return this.elements;
40774
- }
40775
- // ================================================================
40776
- // Diagram Connectors
40777
- // ================================================================
40778
- /**
40779
- * Adds a connector to the diagram, registers it with the diagram,
40780
- * and adds it to the scene for rendering.
40781
- *
40782
- * @param {Object} connector - The connector object to be added to the diagram.
40783
- * @returns {Object} The connector that was added.
40784
- */
40785
- addConnector(connector) {
40786
- this.connectors.push(connector);
40787
- this.scene.add(connector);
40788
- connector.setDiagram(this);
40789
- return connector;
40790
- }
40791
- // ================================================================
40792
- // Clear diagram
40793
- // ================================================================
40794
- /**
40795
- * Clears all elements and connectors from the diagram.
40796
- */
40797
- clear() {
40798
- this.elements = [];
40799
- this.connectors = [];
40800
- this.scene.children = this.scene.children.filter((child) => child instanceof AmbientLight);
40801
- }
40802
- // ================================================================
40803
- // Diagram JSON
40804
- // ================================================================
40805
- toJSON() {
40806
- return JSON.stringify(this.elements);
40807
- }
40808
- fromJSON(json) {
40809
- this.elements = JSON.parse(json);
40810
- }
40811
- // ================================================================
40812
- // Diagram export and import to/from file
40813
- // ================================================================
40814
- /**
40815
- * Exports the diagram to a JSON file.
40816
- */
40817
- export() {
40818
- const data = JSON.stringify(this.scene.toJSON());
40819
- const blob = new Blob([data], { type: "application/json" });
40820
- const url = URL.createObjectURL(blob);
40821
- const a = document.createElement("a");
40822
- a.href = url;
40823
- a.download = "diagram.json";
40824
- a.click();
40825
- URL.revokeObjectURL(url);
40826
- }
40827
- /**
40828
- * Placeholder for importing a diagram from a file.
40829
- * This method should be implemented by subclasses.
40830
- * @param {File} file - The file to import.
40831
- * @throws {Error} If the method is not implemented.
40832
- * @returns {Promise<void>}
40833
- */
40834
- import(file) {
40835
- console.error("Import method should be implemented by subclasses.");
40836
- }
40837
- }
40838
- const DiagramDimensions = {
40839
- DISTANCE_BETWEEN_ELEMENTS: 48
40840
- };
40841
- const ExtrusionParameters$1 = {
40842
- steps: 2,
40843
- depth: 0.6,
40844
- bevelEnabled: true,
40845
- bevelThickness: 0.2,
40846
- bevelSize: 0.4,
40847
- bevelOffset: 0,
40848
- bevelSegments: 4
40849
- };
40850
- const Colors = {
40851
- ELEMENT_STROKE: 26265,
40852
- ELEMENT_TEXT: 26265
40853
- };
40854
40291
  class Shape2 {
40855
40292
  constructor(geometry, material) {
40856
40293
  this.geometry = geometry;
@@ -40882,6 +40319,22 @@ class BoxShape extends Shape2 {
40882
40319
  super(new BoxGeometry(1, 1, 1), new DiagramEditMaterial(color2));
40883
40320
  }
40884
40321
  }
40322
+ const DiagramDimensions = {
40323
+ DISTANCE_BETWEEN_ELEMENTS: 48
40324
+ };
40325
+ const ExtrusionParameters$1 = {
40326
+ steps: 2,
40327
+ depth: 0.6,
40328
+ bevelEnabled: true,
40329
+ bevelThickness: 0.2,
40330
+ bevelSize: 0.4,
40331
+ bevelOffset: 0,
40332
+ bevelSegments: 4
40333
+ };
40334
+ const Colors = {
40335
+ ELEMENT_STROKE: 26265,
40336
+ ELEMENT_TEXT: 26265
40337
+ };
40885
40338
  class FontLoader extends Loader {
40886
40339
  constructor(manager) {
40887
40340
  super(manager);
@@ -43120,60 +42573,29 @@ const ExtrusionParameters = {
43120
42573
  bevelOffset: 0,
43121
42574
  bevelSegments: 1
43122
42575
  };
43123
- const ColorPalette = [
43124
- new Color$1(16007990),
43125
- // red
43126
- new Color$1(15277667),
43127
- // pink
43128
- new Color$1(10233776),
43129
- // purple
43130
- new Color$1(6765239),
43131
- // deep purple
43132
- new Color$1(4149685),
43133
- // indigo
43134
- new Color$1(2201331),
43135
- // blue
43136
- new Color$1(240116),
43137
- // light blue
43138
- new Color$1(48340),
43139
- // cyan
43140
- new Color$1(38536),
43141
- // teal
43142
- new Color$1(5025616),
43143
- // green
43144
- new Color$1(9159498),
43145
- // light green
43146
- new Color$1(13491257),
43147
- // lime
43148
- new Color$1(16771899),
43149
- // yellow
43150
- new Color$1(16761095),
43151
- // amber
43152
- new Color$1(16750592),
43153
- // orange
43154
- new Color$1(16733986),
43155
- // deep orange
43156
- new Color$1(7951688),
43157
- // brown
43158
- new Color$1(10395294),
43159
- // grey
43160
- new Color$1(6323595)
43161
- // blue grey
43162
- ];
43163
42576
  class ValueBarShape extends Shape2 {
42577
+ /**
42578
+ * Creates a new ValueBarShape.
42579
+ * This is a simple component that receives a pre-calculated height and color.
42580
+ * @param {THREE.Shape} shape The 2D base shape of the bar.
42581
+ * @param {number} height The final, normalized height of the bar.
42582
+ * @param {THREE.Color} color The final, pre-calculated color for the bar.
42583
+ */
43164
42584
  constructor(shape, height, color2) {
43165
42585
  if (!(shape instanceof Shape$1)) {
43166
42586
  throw new TypeError("shape must be an instance of THREE.Shape");
43167
42587
  }
43168
- const barColor = color2 !== void 0 ? color2 : ColorPalette[Math.floor(Math.random() * ColorPalette.length)];
43169
- const barHeight = height !== void 0 ? height : Math.floor(Math.random() * 10) + 1;
43170
- const extrusionParameters = { ...ExtrusionParameters, depth: barHeight };
43171
- var barGeometry = new ExtrudeGeometry(shape, extrusionParameters);
43172
- super(barGeometry, new BarMaterial(barColor));
42588
+ if (typeof height !== "number") {
42589
+ throw new TypeError("height must be a number.");
42590
+ }
42591
+ if (!(color2 instanceof Color$1)) {
42592
+ throw new TypeError("color must be an instance of THREE.Color.");
42593
+ }
42594
+ const extrusionParameters = { ...ExtrusionParameters, depth: height };
42595
+ const barGeometry = new ExtrudeGeometry(shape, extrusionParameters);
42596
+ super(barGeometry, new BarMaterial(color2));
42597
+ this.type = "ValueBarShape";
43173
42598
  }
43174
- // draw(ctx, x, y, width) {
43175
- // ctx.fillRect(x, y - this.barHeight, width, this.barHeight);
43176
- // }
43177
42599
  }
43178
42600
  const ConnectorDimensions = {
43179
42601
  CONNECTOR_LINE_WIDTH: 1,
@@ -43547,766 +42969,1338 @@ class Element extends Mesh {
43547
42969
  return size;
43548
42970
  }
43549
42971
  /**
43550
- * Positions the element and its decorations at the specified coordinates.
43551
- * @param {Object} position - The position coordinates
43552
- * @param {number} position.x - X coordinate
43553
- * @param {number} position.y - Y coordinate
43554
- * @param {number} [position.z] - Optional Z coordinate
42972
+ * Positions the element and its decorations at the specified coordinates.
42973
+ * @param {Object} position - The position coordinates
42974
+ * @param {number} position.x - X coordinate
42975
+ * @param {number} position.y - Y coordinate
42976
+ * @param {number} [position.z] - Optional Z coordinate
42977
+ * @returns {Element} This element for method chaining
42978
+ */
42979
+ positionAt(position) {
42980
+ if (position.z !== void 0) {
42981
+ this.position.set(position.x, position.y, position.z);
42982
+ } else {
42983
+ this.position.set(position.x, position.y, 0);
42984
+ }
42985
+ this.texts.forEach((text) => {
42986
+ const offset = text.positionOffset;
42987
+ if (position.z !== void 0) {
42988
+ text.element.position.set(position.x + offset.x, position.y + offset.y, position.z + offset.z);
42989
+ } else {
42990
+ text.element.position.set(position.x + offset.x, position.y + offset.y, offset.z);
42991
+ }
42992
+ });
42993
+ this.icons.forEach((icon) => {
42994
+ const offset = icon.positionOffset;
42995
+ if (position.z !== void 0) {
42996
+ icon.element.position.set(position.x + offset.x, position.y + offset.y, position.z + offset.z);
42997
+ } else {
42998
+ icon.element.position.set(position.x + offset.x, position.y + offset.y, offset.z);
42999
+ }
43000
+ });
43001
+ this.valueBars.forEach((bar) => {
43002
+ const offset = bar.positionOffset;
43003
+ if (position.z !== void 0) {
43004
+ bar.element.position.set(position.x + offset.x, position.y + offset.y, position.z + offset.z);
43005
+ } else {
43006
+ bar.element.position.set(position.x + offset.x, position.y + offset.y, offset.z);
43007
+ }
43008
+ });
43009
+ return this;
43010
+ }
43011
+ /**
43012
+ * Sets the position of the element using individual coordinates.
43013
+ * @param {number} x - X coordinate
43014
+ * @param {number} y - Y coordinate
43015
+ * @param {number} z - Z coordinate
43016
+ * @returns {Element} This element for method chaining
43017
+ */
43018
+ setPosition(x, y, z) {
43019
+ const pos = new Vector3$1(x, y, z);
43020
+ this.positionAt(pos);
43021
+ return this;
43022
+ }
43023
+ /**
43024
+ * Gets the current position of the element.
43025
+ * @returns {THREE.Vector3} The position vector
43026
+ */
43027
+ getPosition() {
43028
+ return this.position;
43029
+ }
43030
+ /**
43031
+ * Positions this element to the right of another element.
43032
+ * @param {string} elementId - ID of the reference element
43033
+ * @returns {Element} This element for method chaining
43034
+ */
43035
+ positionRightOf(elementId) {
43036
+ const element2 = this.diagram.getElementById(elementId);
43037
+ const elementWidth = element2.getSize().x;
43038
+ const thisWidth = this.getSize().x;
43039
+ this.setPosition(
43040
+ element2.position.x + elementWidth / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisWidth / 2,
43041
+ // x
43042
+ element2.position.y,
43043
+ // y
43044
+ 0
43045
+ );
43046
+ return this;
43047
+ }
43048
+ /**
43049
+ * Positions this element to the left of another element.
43050
+ * @param {string} elementId - ID of the reference element
43051
+ * @returns {Element} This element for method chaining
43052
+ */
43053
+ positionLeftOf(elementId) {
43054
+ const element2 = this.diagram.getElementById(elementId);
43055
+ const elementWidth = element2.getSize().x;
43056
+ const thisWidth = this.getSize().x;
43057
+ this.setPosition(
43058
+ element2.position.x - elementWidth / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisWidth / 2,
43059
+ // x
43060
+ element2.position.y,
43061
+ // y
43062
+ 0
43063
+ );
43064
+ return this;
43065
+ }
43066
+ /**
43067
+ * Positions this element above another element.
43068
+ * @param {string} elementId - ID of the reference element
43069
+ * @returns {Element} This element for method chaining
43070
+ */
43071
+ positionUpOf(elementId) {
43072
+ const element2 = this.diagram.getElementById(elementId);
43073
+ const elementHeight = element2.getSize().y;
43074
+ const thisHeight = this.getSize().y;
43075
+ this.setPosition(
43076
+ element2.position.x,
43077
+ // x
43078
+ element2.position.y + elementHeight / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisHeight / 2,
43079
+ // y
43080
+ 0
43081
+ );
43082
+ return this;
43083
+ }
43084
+ /**
43085
+ * Positions this element below another element.
43086
+ * @param {string} elementId - ID of the reference element
43087
+ * @returns {Element} This element for method chaining
43088
+ */
43089
+ positionDownOf(elementId) {
43090
+ const element2 = this.diagram.getElementById(elementId);
43091
+ const elementHeight = element2.getSize().y;
43092
+ const thisHeight = this.getSize().y;
43093
+ this.setPosition(
43094
+ element2.position.x,
43095
+ // x
43096
+ element2.position.y - elementHeight / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisHeight / 2,
43097
+ // y
43098
+ 0
43099
+ );
43100
+ return this;
43101
+ }
43102
+ /**
43103
+ * Positions this element above and to the left of another element.
43104
+ * @param {string} elementId - ID of the reference element
43555
43105
  * @returns {Element} This element for method chaining
43556
43106
  */
43557
- positionAt(position) {
43558
- if (position.z !== void 0) {
43559
- this.position.set(position.x, position.y, position.z);
43560
- } else {
43561
- this.position.set(position.x, position.y, 0);
43562
- }
43563
- this.texts.forEach((text) => {
43564
- const offset = text.positionOffset;
43565
- if (position.z !== void 0) {
43566
- text.element.position.set(position.x + offset.x, position.y + offset.y, position.z + offset.z);
43567
- } else {
43568
- text.element.position.set(position.x + offset.x, position.y + offset.y, offset.z);
43569
- }
43570
- });
43571
- this.icons.forEach((icon) => {
43572
- const offset = icon.positionOffset;
43573
- if (position.z !== void 0) {
43574
- icon.element.position.set(position.x + offset.x, position.y + offset.y, position.z + offset.z);
43575
- } else {
43576
- icon.element.position.set(position.x + offset.x, position.y + offset.y, offset.z);
43577
- }
43578
- });
43579
- this.valueBars.forEach((bar) => {
43580
- const offset = bar.positionOffset;
43581
- if (position.z !== void 0) {
43582
- bar.element.position.set(position.x + offset.x, position.y + offset.y, position.z + offset.z);
43583
- } else {
43584
- bar.element.position.set(position.x + offset.x, position.y + offset.y, offset.z);
43585
- }
43586
- });
43107
+ positionUpLeftOf(elementId) {
43108
+ const element2 = this.diagram.getElementById(elementId);
43109
+ const elementWidth = element2.getSize().x;
43110
+ const elementHeight = element2.getSize().y;
43111
+ const thisWidth = this.getSize().x;
43112
+ const thisHeight = this.getSize().y;
43113
+ this.setPosition(
43114
+ element2.position.x - elementWidth / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisWidth / 2,
43115
+ // x
43116
+ element2.position.y + elementHeight / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisHeight / 2,
43117
+ // y
43118
+ 0
43119
+ );
43587
43120
  return this;
43588
43121
  }
43589
43122
  /**
43590
- * Sets the position of the element using individual coordinates.
43591
- * @param {number} x - X coordinate
43592
- * @param {number} y - Y coordinate
43593
- * @param {number} z - Z coordinate
43123
+ * Positions this element below and to the left of another element.
43124
+ * @param {string} elementId - ID of the reference element
43594
43125
  * @returns {Element} This element for method chaining
43595
43126
  */
43596
- setPosition(x, y, z) {
43597
- const pos = new Vector3$1(x, y, z);
43598
- this.positionAt(pos);
43127
+ positionDownLeftOf(elementId) {
43128
+ const element2 = this.diagram.getElementById(elementId);
43129
+ const elementWidth = element2.getSize().x;
43130
+ const elementHeight = element2.getSize().y;
43131
+ const thisWidth = this.getSize().x;
43132
+ const thisHeight = this.getSize().y;
43133
+ this.setPosition(
43134
+ element2.position.x - elementWidth / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisWidth / 2,
43135
+ // x
43136
+ element2.position.y - elementHeight / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisHeight / 2,
43137
+ // y
43138
+ 0
43139
+ );
43599
43140
  return this;
43600
43141
  }
43601
43142
  /**
43602
- * Gets the current position of the element.
43603
- * @returns {THREE.Vector3} The position vector
43604
- */
43605
- getPosition() {
43606
- return this.position;
43607
- }
43608
- /**
43609
- * Positions this element to the right of another element.
43143
+ * Positions this element above and to the right of another element.
43610
43144
  * @param {string} elementId - ID of the reference element
43611
43145
  * @returns {Element} This element for method chaining
43612
43146
  */
43613
- positionRightOf(elementId) {
43147
+ positionUpRightOf(elementId) {
43614
43148
  const element2 = this.diagram.getElementById(elementId);
43615
43149
  const elementWidth = element2.getSize().x;
43150
+ const elementHeight = element2.getSize().y;
43616
43151
  const thisWidth = this.getSize().x;
43152
+ const thisHeight = this.getSize().y;
43617
43153
  this.setPosition(
43618
43154
  element2.position.x + elementWidth / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisWidth / 2,
43619
43155
  // x
43620
- element2.position.y,
43156
+ element2.position.y + elementHeight / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisHeight / 2,
43621
43157
  // y
43622
43158
  0
43623
43159
  );
43624
43160
  return this;
43625
43161
  }
43626
43162
  /**
43627
- * Positions this element to the left of another element.
43163
+ * Positions this element below and to the right of another element.
43628
43164
  * @param {string} elementId - ID of the reference element
43629
43165
  * @returns {Element} This element for method chaining
43630
43166
  */
43631
- positionLeftOf(elementId) {
43167
+ positionDownRightOf(elementId) {
43632
43168
  const element2 = this.diagram.getElementById(elementId);
43633
43169
  const elementWidth = element2.getSize().x;
43170
+ const elementHeight = element2.getSize().y;
43634
43171
  const thisWidth = this.getSize().x;
43172
+ const thisHeight = this.getSize().y;
43635
43173
  this.setPosition(
43636
- element2.position.x - elementWidth / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisWidth / 2,
43174
+ element2.position.x + elementWidth / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisWidth / 2,
43637
43175
  // x
43638
- element2.position.y,
43176
+ element2.position.y - elementHeight / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisHeight / 2,
43639
43177
  // y
43640
43178
  0
43641
43179
  );
43642
- return this;
43180
+ return this;
43181
+ }
43182
+ // =================================================================
43183
+ // Determine connecting points positions in geographical manner
43184
+ // (N, S, E, W, NNE, NNW, NEE, NWW, SEE, SWW, SSE, SSW)
43185
+ // for connectors to connect to
43186
+ // ================================================================
43187
+ /**
43188
+ * Gets the north connection point of the element.
43189
+ * The point is positioned at the middle of the element's top edge.
43190
+ * @returns {THREE.Vector2} A 2D vector representing the north point coordinates
43191
+ * where x is the element's center x-coordinate
43192
+ * and y is the element's top edge y-coordinate
43193
+ */
43194
+ getNorthPoint() {
43195
+ return new Vector2$1(
43196
+ this.position.x,
43197
+ this.position.y + this.getSize().y / 2
43198
+ );
43199
+ }
43200
+ /**
43201
+ * Gets the south connection point of the element.
43202
+ * The point is positioned at the middle of the element's bottom edge.
43203
+ * @returns {THREE.Vector2} A 2D vector representing the south point coordinates
43204
+ * where x is the element's center x-coordinate
43205
+ * and y is the element's bottom edge y-coordinate
43206
+ */
43207
+ getSouthPoint() {
43208
+ return new Vector2$1(
43209
+ this.position.x,
43210
+ this.position.y - this.getSize().y / 2
43211
+ );
43212
+ }
43213
+ /**
43214
+ * Gets the east connection point of the element.
43215
+ * The point is positioned at the middle of the element's right edge.
43216
+ * @returns {THREE.Vector2} A 2D vector representing the east point coordinates
43217
+ * where x is the element's right edge x-coordinate
43218
+ * and y is the element's center y-coordinate
43219
+ */
43220
+ getEastPoint() {
43221
+ return new Vector2$1(
43222
+ this.position.x + this.getSize().x / 2,
43223
+ this.position.y
43224
+ );
43225
+ }
43226
+ /**
43227
+ * Gets the west connection point of the element.
43228
+ * The point is positioned at the middle of the element's left edge.
43229
+ * @returns {THREE.Vector2} A 2D vector representing the west point coordinates
43230
+ * where x is the element's left edge x-coordinate
43231
+ * and y is the element's center y-coordinate
43232
+ */
43233
+ getWestPoint() {
43234
+ return new Vector2$1(
43235
+ this.position.x - this.getSize().x / 2,
43236
+ this.position.y
43237
+ );
43238
+ }
43239
+ /**
43240
+ * Gets the north-east connection point of the element.
43241
+ * The point is positioned at the top-right corner of the element.
43242
+ * @returns {THREE.Vector2} A 2D vector representing the north-east point coordinates
43243
+ * where x is the element's right edge x-coordinate
43244
+ * and y is the element's top edge y-coordinate
43245
+ */
43246
+ getNorthEastPoint() {
43247
+ return new Vector2$1(
43248
+ this.position.x + this.getSize().x / 2,
43249
+ this.position.y + this.getSize().y / 2
43250
+ );
43251
+ }
43252
+ /**
43253
+ * Gets the north-west connection point of the element.
43254
+ * The point is positioned at the top-left corner of the element.
43255
+ * @returns {THREE.Vector2} A 2D vector representing the north-west point coordinates
43256
+ * where x is the element's left edge x-coordinate
43257
+ * and y is the element's top edge y-coordinate
43258
+ */
43259
+ getNorthWestPoint() {
43260
+ return new Vector2$1(
43261
+ this.position.x - this.getSize().x / 2,
43262
+ this.position.y + this.getSize().y / 2
43263
+ );
43264
+ }
43265
+ /**
43266
+ * Gets the south-east connection point of the element.
43267
+ * The point is positioned at the bottom-right corner of the element.
43268
+ * @returns {THREE.Vector2} A 2D vector representing the south-east point coordinates
43269
+ * where x is the element's right edge x-coordinate
43270
+ * and y is the element's bottom edge y-coordinate
43271
+ */
43272
+ getSouthEastPoint() {
43273
+ return new Vector2$1(
43274
+ this.position.x + this.getSize().x / 2,
43275
+ this.position.y - this.getSize().y / 2
43276
+ );
43277
+ }
43278
+ /**
43279
+ * Gets the south-west connection point of the element.
43280
+ * The point is positioned at the bottom-left corner of the element.
43281
+ * @returns {THREE.Vector2} A 2D vector representing the south-west point coordinates
43282
+ * where x is the element's left edge x-coordinate
43283
+ * and y is the element's bottom edge y-coordinate
43284
+ */
43285
+ getSouthWestPoint() {
43286
+ return new Vector2$1(
43287
+ this.position.x - this.getSize().x / 2,
43288
+ this.position.y - this.getSize().y / 2
43289
+ );
43290
+ }
43291
+ /**
43292
+ * Gets the north-north-east connection point of the element.
43293
+ * The point is positioned on the top edge, one-quarter of the width from the center towards the right.
43294
+ * @returns {THREE.Vector2} A 2D vector representing the north-north-east point coordinates
43295
+ * where x is the element's center x-coordinate plus one-quarter of the width
43296
+ * and y is the element's top edge y-coordinate
43297
+ */
43298
+ getNorthNorthEastPoint() {
43299
+ return new Vector2$1(
43300
+ this.position.x + this.getSize().x / 4,
43301
+ this.position.y + this.getSize().y / 2
43302
+ );
43303
+ }
43304
+ /**
43305
+ * Gets the north-north-west connection point of the element.
43306
+ * The point is positioned on the top edge, one-quarter of the width from the center towards the left.
43307
+ * @returns {THREE.Vector2} A 2D vector representing the north-north-west point coordinates
43308
+ * where x is the element's center x-coordinate minus one-quarter of the width
43309
+ * and y is the element's top edge y-coordinate
43310
+ */
43311
+ getNorthNorthWestPoint() {
43312
+ return new Vector2$1(
43313
+ this.position.x - this.getSize().x / 4,
43314
+ this.position.y + this.getSize().y / 2
43315
+ );
43316
+ }
43317
+ /**
43318
+ * Gets the north-east-east connection point of the element.
43319
+ * The point is positioned on the right edge, one-quarter of the height from the top.
43320
+ * @returns {THREE.Vector2} A 2D vector representing the north-east-east point coordinates
43321
+ * where x is the element's right edge x-coordinate
43322
+ * and y is the element's top edge y-coordinate plus one-quarter of the height
43323
+ */
43324
+ getNorthEastEastPoint() {
43325
+ return new Vector2$1(
43326
+ this.position.x + this.getSize().x / 2,
43327
+ this.position.y + this.getSize().y / 4
43328
+ );
43329
+ }
43330
+ /**
43331
+ * Gets the north-west-west connection point of the element.
43332
+ * The point is positioned on the left edge, one-quarter of the height from the top.
43333
+ * @returns {THREE.Vector2} A 2D vector representing the north-west-west point coordinates
43334
+ * where x is the element's left edge x-coordinate
43335
+ * and y is the element's top edge y-coordinate plus one-quarter of the height
43336
+ */
43337
+ getNorthWestWestPoint() {
43338
+ return new Vector2$1(
43339
+ this.position.x - this.getSize().x / 2,
43340
+ this.position.y + this.getSize().y / 4
43341
+ );
43643
43342
  }
43644
43343
  /**
43645
- * Positions this element above another element.
43646
- * @param {string} elementId - ID of the reference element
43647
- * @returns {Element} This element for method chaining
43344
+ * Gets the south-south-east connection point of the element.
43345
+ * The point is positioned on the bottom edge, one-quarter of the width from the center towards the right.
43346
+ * @returns {THREE.Vector2} A 2D vector representing the south-south-east point coordinates
43347
+ * where x is the element's center x-coordinate plus one-quarter of the width
43348
+ * and y is the element's bottom edge y-coordinate
43648
43349
  */
43649
- positionUpOf(elementId) {
43650
- const element2 = this.diagram.getElementById(elementId);
43651
- const elementHeight = element2.getSize().y;
43652
- const thisHeight = this.getSize().y;
43653
- this.setPosition(
43654
- element2.position.x,
43655
- // x
43656
- element2.position.y + elementHeight / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisHeight / 2,
43657
- // y
43658
- 0
43350
+ getSouthSouthEastPoint() {
43351
+ return new Vector2$1(
43352
+ this.position.x + this.getSize().x / 4,
43353
+ this.position.y - this.getSize().y / 2
43659
43354
  );
43660
- return this;
43661
43355
  }
43662
43356
  /**
43663
- * Positions this element below another element.
43664
- * @param {string} elementId - ID of the reference element
43665
- * @returns {Element} This element for method chaining
43357
+ * Gets the south-south-west connection point of the element.
43358
+ * The point is positioned on the bottom edge, one-quarter of the width from the center towards the left.
43359
+ * @returns {THREE.Vector2} A 2D vector representing the south-south-west point coordinates
43360
+ * where x is the element's center x-coordinate minus one-quarter of the width
43361
+ * and y is the element's bottom edge y-coordinate
43666
43362
  */
43667
- positionDownOf(elementId) {
43668
- const element2 = this.diagram.getElementById(elementId);
43669
- const elementHeight = element2.getSize().y;
43670
- const thisHeight = this.getSize().y;
43671
- this.setPosition(
43672
- element2.position.x,
43673
- // x
43674
- element2.position.y - elementHeight / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisHeight / 2,
43675
- // y
43676
- 0
43363
+ getSouthSouthWestPoint() {
43364
+ return new Vector2$1(
43365
+ this.position.x - this.getSize().x / 4,
43366
+ this.position.y - this.getSize().y / 2
43677
43367
  );
43678
- return this;
43679
43368
  }
43680
43369
  /**
43681
- * Positions this element above and to the left of another element.
43682
- * @param {string} elementId - ID of the reference element
43683
- * @returns {Element} This element for method chaining
43370
+ * Gets the south-east-east connection point of the element.
43371
+ * The point is positioned on the right edge, one-quarter of the height from the bottom.
43372
+ * @returns {THREE.Vector2} A 2D vector representing the south-east-east point coordinates
43373
+ * where x is the element's right edge x-coordinate
43374
+ * and y is the element's bottom edge y-coordinate plus one-quarter of the height
43684
43375
  */
43685
- positionUpLeftOf(elementId) {
43686
- const element2 = this.diagram.getElementById(elementId);
43687
- const elementWidth = element2.getSize().x;
43688
- const elementHeight = element2.getSize().y;
43689
- const thisWidth = this.getSize().x;
43690
- const thisHeight = this.getSize().y;
43691
- this.setPosition(
43692
- element2.position.x - elementWidth / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisWidth / 2,
43693
- // x
43694
- element2.position.y + elementHeight / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisHeight / 2,
43695
- // y
43696
- 0
43376
+ getSouthEastEastPoint() {
43377
+ return new Vector2$1(
43378
+ this.position.x + this.getSize().x / 2,
43379
+ this.position.y - this.getSize().y / 4
43697
43380
  );
43698
- return this;
43699
43381
  }
43700
43382
  /**
43701
- * Positions this element below and to the left of another element.
43702
- * @param {string} elementId - ID of the reference element
43703
- * @returns {Element} This element for method chaining
43383
+ * Gets the south-west-west connection point of the element.
43384
+ * The point is positioned on the left edge, one-quarter of the height from the bottom.
43385
+ * @returns {THREE.Vector2} A 2D vector representing the south-west-west point coordinates
43386
+ * where x is the element's left edge x-coordinate
43387
+ * and y is the element's bottom edge y-coordinate plus one-quarter of the height
43704
43388
  */
43705
- positionDownLeftOf(elementId) {
43706
- const element2 = this.diagram.getElementById(elementId);
43707
- const elementWidth = element2.getSize().x;
43708
- const elementHeight = element2.getSize().y;
43709
- const thisWidth = this.getSize().x;
43710
- const thisHeight = this.getSize().y;
43711
- this.setPosition(
43712
- element2.position.x - elementWidth / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisWidth / 2,
43713
- // x
43714
- element2.position.y - elementHeight / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisHeight / 2,
43715
- // y
43716
- 0
43389
+ getSouthWestWestPoint() {
43390
+ return new Vector2$1(
43391
+ this.position.x - this.getSize().x / 2,
43392
+ this.position.y - this.getSize().y / 4
43717
43393
  );
43718
- return this;
43719
43394
  }
43720
43395
  /**
43721
- * Positions this element above and to the right of another element.
43722
- * @param {string} elementId - ID of the reference element
43396
+ * Gets the position of a connection point on the element.
43397
+ * @param {string} position - Position identifier (N, S, E, W, NE, NW, etc.)
43398
+ * @returns {THREE.Vector2} The connection point coordinates
43399
+ * @throws {Error} If the position identifier is invalid
43400
+ */
43401
+ getPointPosition(position) {
43402
+ switch (position) {
43403
+ case "N":
43404
+ case "north":
43405
+ return this.getNorthPoint();
43406
+ case "S":
43407
+ case "south":
43408
+ return this.getSouthPoint();
43409
+ case "E":
43410
+ case "east":
43411
+ return this.getEastPoint();
43412
+ case "W":
43413
+ case "west":
43414
+ return this.getWestPoint();
43415
+ case "NE":
43416
+ case "northeast":
43417
+ return this.getNorthEastPoint();
43418
+ case "NW":
43419
+ case "northwest":
43420
+ return this.getNorthWestPoint();
43421
+ case "SE":
43422
+ case "southeast":
43423
+ return this.getSouthEastPoint();
43424
+ case "SW":
43425
+ case "southwest":
43426
+ return this.getSouthWestPoint();
43427
+ case "NNE":
43428
+ case "northnortheast":
43429
+ return this.getNorthNorthEastPoint();
43430
+ case "NNW":
43431
+ case "northnorthwest":
43432
+ return this.getNorthNorthWestPoint();
43433
+ case "NEE":
43434
+ case "northeast":
43435
+ return this.getNorthEastEastPoint();
43436
+ case "NWW":
43437
+ case "northwest":
43438
+ return this.getNorthWestWestPoint();
43439
+ case "SSE":
43440
+ case "southsoutheast":
43441
+ return this.getSouthSouthEastPoint();
43442
+ case "SSW":
43443
+ case "southsouthwest":
43444
+ return this.getSouthSouthWestPoint();
43445
+ case "SEE":
43446
+ case "southeast":
43447
+ return this.getSouthEastEastPoint();
43448
+ case "SWW":
43449
+ case "southwest":
43450
+ return this.getSouthWestWestPoint();
43451
+ default:
43452
+ throw new Error(`Unknown position: ${position}`);
43453
+ }
43454
+ }
43455
+ // ================================================================
43456
+ // Add Text methods
43457
+ // ================================================================
43458
+ /**
43459
+ * Adds text to the element.
43460
+ * @param {string} text - The text to add
43723
43461
  * @returns {Element} This element for method chaining
43724
43462
  */
43725
- positionUpRightOf(elementId) {
43726
- const element2 = this.diagram.getElementById(elementId);
43727
- const elementWidth = element2.getSize().x;
43728
- const elementHeight = element2.getSize().y;
43729
- const thisWidth = this.getSize().x;
43730
- const thisHeight = this.getSize().y;
43731
- this.setPosition(
43732
- element2.position.x + elementWidth / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisWidth / 2,
43733
- // x
43734
- element2.position.y + elementHeight / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisHeight / 2,
43735
- // y
43736
- 0
43737
- );
43463
+ addText(text) {
43464
+ const textElement = new Element(this.elementId + "_text", new TextShape(text));
43465
+ this.diagram.addElement(textElement).positionAt(this.position);
43466
+ this.texts.push({ element: textElement, positionOffset: new Vector3$1(0, 0, 0) });
43738
43467
  return this;
43739
43468
  }
43740
43469
  /**
43741
- * Positions this element below and to the right of another element.
43742
- * @param {string} elementId - ID of the reference element
43470
+ * Adds wrapped text that fits within the element's bounds.
43471
+ * @param {string} text - The text to add and wrap
43472
+ * @param {THREE.Vector3} [offset=new THREE.Vector3(0, 0, 3)] - Optional offset for the text position
43743
43473
  * @returns {Element} This element for method chaining
43744
43474
  */
43745
- positionDownRightOf(elementId) {
43746
- const element2 = this.diagram.getElementById(elementId);
43747
- const elementWidth = element2.getSize().x;
43748
- const elementHeight = element2.getSize().y;
43749
- const thisWidth = this.getSize().x;
43750
- const thisHeight = this.getSize().y;
43751
- this.setPosition(
43752
- element2.position.x + elementWidth / 2 + DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS + thisWidth / 2,
43753
- // x
43754
- element2.position.y - elementHeight / 2 - DiagramDimensions.DISTANCE_BETWEEN_ELEMENTS - thisHeight / 2,
43755
- // y
43756
- 0
43757
- );
43475
+ addWrappedText(text, offset = new Vector3$1(0, 0, 3)) {
43476
+ if (!text) {
43477
+ console.warn("addWrappedText: text is null or undefined");
43478
+ return this;
43479
+ }
43480
+ let wrappedText = text.replace(/\s+/g, "\n");
43481
+ let wrappedTextElement = new Element(this.elementId + "_text", new TextShape(wrappedText));
43482
+ let candidateTextElement;
43483
+ while (true) {
43484
+ let words = wrappedText.split("\n");
43485
+ let minLength = Infinity;
43486
+ let minIndex = -1;
43487
+ for (let i = 0; i < words.length - 1; i++) {
43488
+ let combinedLength = words[i].length + words[i + 1].length;
43489
+ if (combinedLength < minLength) {
43490
+ minLength = combinedLength;
43491
+ minIndex = i;
43492
+ }
43493
+ }
43494
+ if (minIndex === -1) break;
43495
+ words[minIndex] = words[minIndex] + " " + words[minIndex + 1];
43496
+ words.splice(minIndex + 1, 1);
43497
+ wrappedText = words.join("\n");
43498
+ candidateTextElement = new Element(this.elementId + "_text", new TextShape(wrappedText));
43499
+ if (candidateTextElement.getSize().x <= this.getSize().x * 0.9) {
43500
+ wrappedTextElement = candidateTextElement;
43501
+ } else {
43502
+ break;
43503
+ }
43504
+ }
43505
+ this.diagram.addElement(wrappedTextElement).positionAt({
43506
+ x: this.position.x + offset.x,
43507
+ y: this.position.y + offset.y,
43508
+ z: this.position.z + offset.z
43509
+ });
43510
+ this.texts.push({ element: wrappedTextElement, positionOffset: offset });
43758
43511
  return this;
43759
43512
  }
43760
- // =================================================================
43761
- // Determine connecting points positions in geographical manner
43762
- // (N, S, E, W, NNE, NNW, NEE, NWW, SEE, SWW, SSE, SSW)
43763
- // for connectors to connect to
43513
+ // ================================================================
43514
+ // Add icons methods
43764
43515
  // ================================================================
43765
43516
  /**
43766
- * Gets the north connection point of the element.
43767
- * The point is positioned at the middle of the element's top edge.
43768
- * @returns {THREE.Vector2} A 2D vector representing the north point coordinates
43517
+ * Gets the placeholder position for an icon at the top of the element.
43518
+ * The position is calculated by taking the element's top edge and offsetting it inward
43519
+ * by half the icon size plus padding.
43520
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43769
43521
  * where x is the element's center x-coordinate
43770
- * and y is the element's top edge y-coordinate
43522
+ * and y is the element's top edge y-coordinate minus (icon size/2 + padding)
43771
43523
  */
43772
- getNorthPoint() {
43524
+ getTopIconPlaceholder() {
43773
43525
  return new Vector2$1(
43774
43526
  this.position.x,
43775
- this.position.y + this.getSize().y / 2
43527
+ this.position.y + this.getSize().y / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING
43776
43528
  );
43777
43529
  }
43778
43530
  /**
43779
- * Gets the south connection point of the element.
43780
- * The point is positioned at the middle of the element's bottom edge.
43781
- * @returns {THREE.Vector2} A 2D vector representing the south point coordinates
43531
+ * Gets the placeholder position for an icon at the bottom of the element.
43532
+ * The position is calculated by taking the element's bottom edge and offsetting it inward
43533
+ * by half the icon size plus padding.
43534
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43782
43535
  * where x is the element's center x-coordinate
43783
- * and y is the element's bottom edge y-coordinate
43536
+ * and y is the element's bottom edge y-coordinate plus (icon size/2 + padding)
43784
43537
  */
43785
- getSouthPoint() {
43538
+ getBottomIconPlaceholder() {
43786
43539
  return new Vector2$1(
43787
43540
  this.position.x,
43788
- this.position.y - this.getSize().y / 2
43541
+ this.position.y - this.getSize().y / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING
43789
43542
  );
43790
43543
  }
43791
43544
  /**
43792
- * Gets the east connection point of the element.
43793
- * The point is positioned at the middle of the element's right edge.
43794
- * @returns {THREE.Vector2} A 2D vector representing the east point coordinates
43795
- * where x is the element's right edge x-coordinate
43545
+ * Gets the placeholder position for an icon at the left of the element.
43546
+ * The position is calculated by taking the element's left edge and offsetting it inward
43547
+ * by half the icon size plus padding.
43548
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43549
+ * where x is the element's left edge x-coordinate plus (icon size/2 + padding)
43796
43550
  * and y is the element's center y-coordinate
43797
43551
  */
43798
- getEastPoint() {
43552
+ getLeftIconPlaceholder() {
43799
43553
  return new Vector2$1(
43800
- this.position.x + this.getSize().x / 2,
43554
+ this.position.x - this.getSize().x / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING,
43801
43555
  this.position.y
43802
43556
  );
43803
43557
  }
43804
43558
  /**
43805
- * Gets the west connection point of the element.
43806
- * The point is positioned at the middle of the element's left edge.
43807
- * @returns {THREE.Vector2} A 2D vector representing the west point coordinates
43808
- * where x is the element's left edge x-coordinate
43559
+ * Gets the placeholder position for an icon at the right of the element.
43560
+ * The position is calculated by taking the element's right edge and offsetting it inward
43561
+ * by half the icon size plus padding.
43562
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43563
+ * where x is the element's right edge x-coordinate minus (icon size/2 + padding)
43809
43564
  * and y is the element's center y-coordinate
43810
43565
  */
43811
- getWestPoint() {
43566
+ getRightIconPlaceholder() {
43812
43567
  return new Vector2$1(
43813
- this.position.x - this.getSize().x / 2,
43568
+ this.position.x + this.getSize().x / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING,
43814
43569
  this.position.y
43815
43570
  );
43816
43571
  }
43817
43572
  /**
43818
- * Gets the north-east connection point of the element.
43819
- * The point is positioned at the top-right corner of the element.
43820
- * @returns {THREE.Vector2} A 2D vector representing the north-east point coordinates
43821
- * where x is the element's right edge x-coordinate
43822
- * and y is the element's top edge y-coordinate
43573
+ * Gets the placeholder position for an icon at the top-left corner of the element.
43574
+ * The position is calculated by taking the element's top-left corner and offsetting it inward
43575
+ * by half the icon size plus padding along both axes.
43576
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43577
+ * where x is the element's left edge x-coordinate plus (icon size/2 + padding)
43578
+ * and y is the element's top edge y-coordinate minus (icon size/2 + padding)
43823
43579
  */
43824
- getNorthEastPoint() {
43580
+ getTopLeftIconPlaceholder() {
43825
43581
  return new Vector2$1(
43826
- this.position.x + this.getSize().x / 2,
43827
- this.position.y + this.getSize().y / 2
43582
+ this.position.x - this.getSize().x / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING,
43583
+ this.position.y + this.getSize().y / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING
43828
43584
  );
43829
43585
  }
43830
43586
  /**
43831
- * Gets the north-west connection point of the element.
43832
- * The point is positioned at the top-left corner of the element.
43833
- * @returns {THREE.Vector2} A 2D vector representing the north-west point coordinates
43834
- * where x is the element's left edge x-coordinate
43835
- * and y is the element's top edge y-coordinate
43587
+ * Gets the placeholder position for an icon at the top-right corner of the element.
43588
+ * The position is calculated by taking the element's top-right corner and offsetting it inward
43589
+ * by half the icon size plus padding along both axes.
43590
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43591
+ * where x is the element's right edge x-coordinate minus (icon size/2 + padding)
43592
+ * and y is the element's top edge y-coordinate minus (icon size/2 + padding)
43836
43593
  */
43837
- getNorthWestPoint() {
43594
+ getTopRightIconPlaceholder() {
43838
43595
  return new Vector2$1(
43839
- this.position.x - this.getSize().x / 2,
43840
- this.position.y + this.getSize().y / 2
43596
+ this.position.x + this.getSize().x / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING,
43597
+ this.position.y + this.getSize().y / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING
43841
43598
  );
43842
43599
  }
43843
43600
  /**
43844
- * Gets the south-east connection point of the element.
43845
- * The point is positioned at the bottom-right corner of the element.
43846
- * @returns {THREE.Vector2} A 2D vector representing the south-east point coordinates
43847
- * where x is the element's right edge x-coordinate
43848
- * and y is the element's bottom edge y-coordinate
43601
+ * Gets the placeholder position for an icon at the bottom-left corner of the element.
43602
+ * The position is calculated by taking the element's bottom-left corner and offsetting it inward
43603
+ * by half the icon size plus padding along both axes.
43604
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43605
+ * where x is the element's left edge x-coordinate plus (icon size/2 + padding)
43606
+ * and y is the element's bottom edge y-coordinate plus (icon size/2 + padding)
43849
43607
  */
43850
- getSouthEastPoint() {
43608
+ getBottomLeftIconPlaceholder() {
43851
43609
  return new Vector2$1(
43852
- this.position.x + this.getSize().x / 2,
43853
- this.position.y - this.getSize().y / 2
43610
+ this.position.x - this.getSize().x / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING,
43611
+ this.position.y - this.getSize().y / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING
43854
43612
  );
43855
43613
  }
43856
43614
  /**
43857
- * Gets the south-west connection point of the element.
43858
- * The point is positioned at the bottom-left corner of the element.
43859
- * @returns {THREE.Vector2} A 2D vector representing the south-west point coordinates
43860
- * where x is the element's left edge x-coordinate
43861
- * and y is the element's bottom edge y-coordinate
43615
+ * Gets the placeholder position for an icon at the bottom-right corner of the element.
43616
+ * The position is calculated by taking the element's bottom-right corner and offsetting it inward
43617
+ * by half the icon size plus padding along both axes.
43618
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43619
+ * where x is the element's right edge x-coordinate minus (icon size/2 + padding)
43620
+ * and y is the element's bottom edge y-coordinate plus (icon size/2 + padding)
43862
43621
  */
43863
- getSouthWestPoint() {
43622
+ getBottomRightIconPlaceholder() {
43864
43623
  return new Vector2$1(
43865
- this.position.x - this.getSize().x / 2,
43866
- this.position.y - this.getSize().y / 2
43624
+ this.position.x + this.getSize().x / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING,
43625
+ this.position.y - this.getSize().y / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING
43867
43626
  );
43868
43627
  }
43869
43628
  /**
43870
- * Gets the north-north-east connection point of the element.
43871
- * The point is positioned on the top edge, one-quarter of the width from the center towards the right.
43872
- * @returns {THREE.Vector2} A 2D vector representing the north-north-east point coordinates
43873
- * where x is the element's center x-coordinate plus one-quarter of the width
43874
- * and y is the element's top edge y-coordinate
43629
+ * Gets the placeholder position for an icon at the center of the element.
43630
+ * The position is at the element's center.
43631
+ * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
43632
+ * where x is the element's center x-coordinate
43633
+ * and y is the element's center y-coordinate
43634
+ */
43635
+ getCenterIconPlaceholder() {
43636
+ return new Vector2$1(this.position.x, this.position.y);
43637
+ }
43638
+ /**
43639
+ * Gets the position for an icon placeholder.
43640
+ * @param {string} position - Position identifier (top, bottom, left, right, etc.)
43641
+ * @returns {THREE.Vector2} The placeholder position
43642
+ * @throws {Error} If the position identifier is invalid
43643
+ */
43644
+ getIconPlaceholder(position) {
43645
+ switch (position) {
43646
+ case "top":
43647
+ return this.getTopIconPlaceholder();
43648
+ case "bottom":
43649
+ return this.getBottomIconPlaceholder();
43650
+ case "left":
43651
+ return this.getLeftIconPlaceholder();
43652
+ case "right":
43653
+ return this.getRightIconPlaceholder();
43654
+ case "top-left":
43655
+ return this.getTopLeftIconPlaceholder();
43656
+ case "top-right":
43657
+ return this.getTopRightIconPlaceholder();
43658
+ case "bottom-left":
43659
+ return this.getBottomLeftIconPlaceholder();
43660
+ case "bottom-right":
43661
+ return this.getBottomRightIconPlaceholder();
43662
+ case "center":
43663
+ return this.getCenterIconPlaceholder();
43664
+ default:
43665
+ throw new Error(`Unknown icon position: ${position}`);
43666
+ }
43667
+ }
43668
+ /**
43669
+ * Adds an icon to the element.
43670
+ * @param {string} icon - The icon identifier
43671
+ * @param {string} [placeholder='center'] - Position of the icon
43672
+ * @param {number} [size=IconDimensions.ICON_SIZE_MEDIUM] - Size of the icon
43673
+ * @returns {Element} This element for method chaining
43674
+ */
43675
+ addIcon(icon, placeholder = "center", size = IconDimensions.ICON_SIZE_MEDIUM) {
43676
+ let position;
43677
+ if (typeof placeholder === "string") {
43678
+ position = this.getIconPlaceholder(placeholder);
43679
+ }
43680
+ const iconElement = new Element(this.elementId + "_icon_placeholder", new IconShape(icon, size));
43681
+ this.diagram.addElement(iconElement).positionAt(position);
43682
+ this.icons.push({ element: iconElement, positionOffset: new Vector3$1(position.x - this.position.x, position.y - this.position.y, 0) });
43683
+ return this;
43684
+ }
43685
+ // ================================================================
43686
+ // Add Analysis methods
43687
+ // ================================================================
43688
+ /**
43689
+ * Adds a value bar for analysis mode.
43690
+ * @param {number} value - The value to represent (must be positive)
43691
+ * @returns {Element} This element for method chaining
43692
+ */
43693
+ addValueBar(value) {
43694
+ this.parameters["value"] = value;
43695
+ return this;
43696
+ }
43697
+ // ================================================================
43698
+ // Add connecting methods
43699
+ // ================================================================
43700
+ /**
43701
+ * Creates a connector from another element to this element.
43702
+ * @param {string} sourceElementId - ID of the source element
43703
+ * @param {string} sourcePosition - Connection point on the source element
43704
+ * @param {string} targetPosition - Connection point on this element
43705
+ * @returns {Element} This element for method chaining
43706
+ * @throws {Error} If the diagram is not set or source element is not found
43707
+ */
43708
+ connectFrom(sourceElementId, sourcePosition, targetPosition) {
43709
+ if (!this.diagram) {
43710
+ throw new Error("Diagram is not set for this element.");
43711
+ }
43712
+ const targetElement = this;
43713
+ const sourceElement = this.diagram.getElementById(sourceElementId);
43714
+ if (!sourceElement) {
43715
+ throw new Error(`Element with ID ${sourceElementId} not found.`);
43716
+ }
43717
+ const sourcePoint = sourceElement.getPointPosition(sourcePosition);
43718
+ const targetPoint = targetElement.getPointPosition(targetPosition);
43719
+ const points = Connector.determinePoints(sourcePoint, targetPoint, sourcePosition, targetPosition);
43720
+ this.diagram.addConnector(new Connector(
43721
+ `connector-${sourceElement.elementId}-${targetElement.elementId}`,
43722
+ new RoundedCornerOrthogonalConnectorShape(points)
43723
+ ));
43724
+ return this;
43725
+ }
43726
+ }
43727
+ function getColorForValue(value, min2, max2) {
43728
+ console.log(`Calculating color for: value=${value}, min=${min2}, max=${max2}`);
43729
+ const color2 = new Color$1();
43730
+ if (min2 === max2) {
43731
+ color2.setHSL(0.25, 1, 0.5);
43732
+ return color2;
43733
+ }
43734
+ let ratio = (value - min2) / (max2 - min2);
43735
+ ratio = Math.max(0, Math.min(ratio, 1));
43736
+ console.log(`Calculated Ratio: ${ratio}`);
43737
+ const hue = ratio * (120 / 360);
43738
+ color2.setHSL(hue, 1, 0.5);
43739
+ return color2;
43740
+ }
43741
+ class Diagram {
43742
+ /**
43743
+ * Creates a new Diagram instance.
43744
+ * @param {HTMLElement} container - The HTML container element for the diagram.
43745
+ */
43746
+ constructor(container) {
43747
+ this.elements = [];
43748
+ this.connectors = [];
43749
+ this.mode = "VIEW";
43750
+ this.helpers = false;
43751
+ this.container = container;
43752
+ this.initScene();
43753
+ this.initCamera();
43754
+ this.setHelpers();
43755
+ this.initRenderer();
43756
+ this.initLighting();
43757
+ this.initControls();
43758
+ this.addEventListeners();
43759
+ this.animate();
43760
+ console.log("THREE", THREE);
43761
+ console.log(this);
43762
+ }
43763
+ /**
43764
+ * Initializes the THREE.js scene.
43765
+ */
43766
+ initScene() {
43767
+ this.scene = new Scene();
43768
+ this.scene.background = new Color$1(15790320);
43769
+ }
43770
+ /**
43771
+ * Initializes the camera with a perspective projection.
43772
+ */
43773
+ initCamera() {
43774
+ const aspectRatio = this.container.clientWidth / this.container.clientHeight;
43775
+ this.camera = new PerspectiveCamera(75, aspectRatio, 0.1, 2e3);
43776
+ this.camera.position.set(0, 0, 500);
43777
+ this.camera.updateProjectionMatrix();
43778
+ }
43779
+ // initCamera() { // Orthographic
43780
+ // // const aspectRatio = this.container.clientWidth / window.innerHeight;
43781
+ // const aspectRatio = window.innerWidth / window.innerHeight;
43782
+ // const frustumSize = 100;
43783
+ // this.camera = new THREE.OrthographicCamera(
43784
+ // frustumSize * aspectRatio / -2,
43785
+ // frustumSize * aspectRatio / 2,
43786
+ // frustumSize / 2,
43787
+ // frustumSize / -2,
43788
+ // 0.1,
43789
+ // 1000
43790
+ // );
43791
+ // this.camera.position.set(0, 0, 50);
43792
+ // }
43793
+ /**
43794
+ * Sets up helpers (axes, grid, etc.) for the scene.
43875
43795
  */
43876
- getNorthNorthEastPoint() {
43877
- return new Vector2$1(
43878
- this.position.x + this.getSize().x / 4,
43879
- this.position.y + this.getSize().y / 2
43880
- );
43796
+ setHelpers() {
43797
+ this.axesHelper = new AxesHelper(100);
43798
+ this.cameraHelper = new CameraHelper(this.camera);
43799
+ const size = 400;
43800
+ const divisions = 50;
43801
+ this.gridHelper = new GridHelper(size, divisions);
43802
+ this.cameraDirection = new Vector3$1();
43803
+ this.camPositionSpan = document.querySelector("#position");
43804
+ this.camLookAtSpan = document.querySelector("#lookingAt");
43805
+ this.helpers = false;
43881
43806
  }
43882
43807
  /**
43883
- * Gets the north-north-west connection point of the element.
43884
- * The point is positioned on the top edge, one-quarter of the width from the center towards the left.
43885
- * @returns {THREE.Vector2} A 2D vector representing the north-north-west point coordinates
43886
- * where x is the element's center x-coordinate minus one-quarter of the width
43887
- * and y is the element's top edge y-coordinate
43808
+ * Shows the helpers in the scene.
43888
43809
  */
43889
- getNorthNorthWestPoint() {
43890
- return new Vector2$1(
43891
- this.position.x - this.getSize().x / 4,
43892
- this.position.y + this.getSize().y / 2
43893
- );
43810
+ showHelpers() {
43811
+ if (!this.helpers) {
43812
+ this.scene.add(this.axesHelper);
43813
+ this.scene.add(this.cameraHelper);
43814
+ this.scene.add(this.gridHelper);
43815
+ this.helpers = true;
43816
+ }
43894
43817
  }
43895
43818
  /**
43896
- * Gets the north-east-east connection point of the element.
43897
- * The point is positioned on the right edge, one-quarter of the height from the top.
43898
- * @returns {THREE.Vector2} A 2D vector representing the north-east-east point coordinates
43899
- * where x is the element's right edge x-coordinate
43900
- * and y is the element's top edge y-coordinate plus one-quarter of the height
43819
+ * Hides the helpers in the scene.
43901
43820
  */
43902
- getNorthEastEastPoint() {
43903
- return new Vector2$1(
43904
- this.position.x + this.getSize().x / 2,
43905
- this.position.y + this.getSize().y / 4
43906
- );
43821
+ hideHelpers() {
43822
+ if (this.helpers) {
43823
+ this.scene.remove(this.axesHelper);
43824
+ this.scene.remove(this.cameraHelper);
43825
+ this.scene.remove(this.gridHelper);
43826
+ this.helpers = false;
43827
+ }
43907
43828
  }
43908
43829
  /**
43909
- * Gets the north-west-west connection point of the element.
43910
- * The point is positioned on the left edge, one-quarter of the height from the top.
43911
- * @returns {THREE.Vector2} A 2D vector representing the north-west-west point coordinates
43912
- * where x is the element's left edge x-coordinate
43913
- * and y is the element's top edge y-coordinate plus one-quarter of the height
43830
+ * Initializes the renderer and attaches it to the container.
43914
43831
  */
43915
- getNorthWestWestPoint() {
43916
- return new Vector2$1(
43917
- this.position.x - this.getSize().x / 2,
43918
- this.position.y + this.getSize().y / 4
43919
- );
43832
+ initRenderer() {
43833
+ this.renderer = new WebGLRenderer({ antialias: true });
43834
+ this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
43835
+ this.container.appendChild(this.renderer.domElement);
43836
+ console.log("initRenderer", this.container, this.renderer.domElement);
43920
43837
  }
43921
43838
  /**
43922
- * Gets the south-south-east connection point of the element.
43923
- * The point is positioned on the bottom edge, one-quarter of the width from the center towards the right.
43924
- * @returns {THREE.Vector2} A 2D vector representing the south-south-east point coordinates
43925
- * where x is the element's center x-coordinate plus one-quarter of the width
43926
- * and y is the element's bottom edge y-coordinate
43839
+ * Initializes the lighting for the scene.
43927
43840
  */
43928
- getSouthSouthEastPoint() {
43929
- return new Vector2$1(
43930
- this.position.x + this.getSize().x / 4,
43931
- this.position.y - this.getSize().y / 2
43932
- );
43841
+ initLighting() {
43842
+ let mainLightColor = 16777215;
43843
+ let mainLightIntensity = 4;
43844
+ let mainLightDistance = 0;
43845
+ let mainLightDecay = 0;
43846
+ const mainLight = new PointLight(mainLightColor, mainLightIntensity, mainLightDistance, mainLightDecay);
43847
+ let mainLightPosX = -1 * (580 / 2) + 1 / 3 * 580;
43848
+ let mainLightPosY = -1 * 209 * 4;
43849
+ let mainLightPosZ = Math.abs(mainLightPosY);
43850
+ mainLight.position.set(mainLightPosX, mainLightPosY, mainLightPosZ);
43851
+ this.scene.add(mainLight);
43852
+ this.spotLight = new PointLight(16777215, 4, 0, 0);
43853
+ this.spotLightPosX = 0;
43854
+ this.spotLightPosY = -1 * 209 - 300;
43855
+ this.spotLightPosZ = 70;
43856
+ this.scene.add(this.spotLight);
43857
+ this.spotLightDirection = 1;
43933
43858
  }
43934
43859
  /**
43935
- * Gets the south-south-west connection point of the element.
43936
- * The point is positioned on the bottom edge, one-quarter of the width from the center towards the left.
43937
- * @returns {THREE.Vector2} A 2D vector representing the south-south-west point coordinates
43938
- * where x is the element's center x-coordinate minus one-quarter of the width
43939
- * and y is the element's bottom edge y-coordinate
43860
+ * Initializes the controls for the camera.
43940
43861
  */
43941
- getSouthSouthWestPoint() {
43942
- return new Vector2$1(
43943
- this.position.x - this.getSize().x / 4,
43944
- this.position.y - this.getSize().y / 2
43945
- );
43862
+ initControls() {
43863
+ this.controls = new MapControls(this.camera, this.renderer.domElement);
43864
+ this.controls.enableDamping = true;
43865
+ this.controls.dampingFactor = 0.1;
43866
+ this.controls.screenSpacePanning = true;
43867
+ this.controls.zoomToCursor = true;
43868
+ this.controls.saveState();
43946
43869
  }
43947
43870
  /**
43948
- * Gets the south-east-east connection point of the element.
43949
- * The point is positioned on the right edge, one-quarter of the height from the bottom.
43950
- * @returns {THREE.Vector2} A 2D vector representing the south-east-east point coordinates
43951
- * where x is the element's right edge x-coordinate
43952
- * and y is the element's bottom edge y-coordinate plus one-quarter of the height
43871
+ * Adds event listeners for window resize and other interactions.
43953
43872
  */
43954
- getSouthEastEastPoint() {
43955
- return new Vector2$1(
43956
- this.position.x + this.getSize().x / 2,
43957
- this.position.y - this.getSize().y / 4
43958
- );
43873
+ addEventListeners() {
43874
+ window.addEventListener("resize", this.onWindowResize.bind(this), false);
43959
43875
  }
43876
+ // onDocumentMouseDown(event) {
43877
+ // this.isDragging = true;
43878
+ // this.previousMousePosition = { x: event.offsetX, y: event.offsetY };
43879
+ // }
43880
+ // onDocumentMouseMove(event) {
43881
+ // if (this.isDragging && this.mugGroup) {
43882
+ // const deltaMove = {
43883
+ // x: event.offsetX - this.previousMousePosition.x,
43884
+ // y: event.offsetY - this.previousMousePosition.y
43885
+ // };
43886
+ // let rotateAngleX = this.toRadians(deltaMove.y * 1);
43887
+ // let rotateAngleY = this.toRadians(deltaMove.x * 1);
43888
+ // this.currentRotation = this.currentRotation || { x: 0, y: 0 };
43889
+ // this.currentRotation.x += rotateAngleX;
43890
+ // this.currentRotation.y += rotateAngleY;
43891
+ // const maxRotation = Math.PI / 2;
43892
+ // this.currentRotation.x = Math.min(Math.max(this.currentRotation.x, -maxRotation), maxRotation);
43893
+ // this.pivotGroup.rotation.x = this.currentRotation.x;
43894
+ // this.pivotGroup.rotation.y = this.currentRotation.y;
43895
+ // this.previousMousePosition = { x: event.offsetX, y: event.offsetY };
43896
+ // }
43897
+ // }
43898
+ // onDocumentMouseUp() {
43899
+ // this.isDragging = false;
43900
+ // }
43901
+ // toRadians(angle) {
43902
+ // return angle * (Math.PI / 180);
43903
+ // }
43960
43904
  /**
43961
- * Gets the south-west-west connection point of the element.
43962
- * The point is positioned on the left edge, one-quarter of the height from the bottom.
43963
- * @returns {THREE.Vector2} A 2D vector representing the south-west-west point coordinates
43964
- * where x is the element's left edge x-coordinate
43965
- * and y is the element's bottom edge y-coordinate plus one-quarter of the height
43905
+ * Handles window resize events to update the camera and renderer.
43966
43906
  */
43967
- getSouthWestWestPoint() {
43968
- return new Vector2$1(
43969
- this.position.x - this.getSize().x / 2,
43970
- this.position.y - this.getSize().y / 4
43971
- );
43907
+ onWindowResize() {
43908
+ const width = this.container.clientWidth;
43909
+ const height = this.container.clientHeight;
43910
+ this.camera.aspect = width / height;
43911
+ this.camera.updateProjectionMatrix();
43912
+ this.renderer.setSize(width, height);
43972
43913
  }
43973
43914
  /**
43974
- * Gets the position of a connection point on the element.
43975
- * @param {string} position - Position identifier (N, S, E, W, NE, NW, etc.)
43976
- * @returns {THREE.Vector2} The connection point coordinates
43977
- * @throws {Error} If the position identifier is invalid
43915
+ * Animates the scene and updates controls.
43978
43916
  */
43979
- getPointPosition(position) {
43980
- switch (position) {
43981
- case "N":
43982
- case "north":
43983
- return this.getNorthPoint();
43984
- case "S":
43985
- case "south":
43986
- return this.getSouthPoint();
43987
- case "E":
43988
- case "east":
43989
- return this.getEastPoint();
43990
- case "W":
43991
- case "west":
43992
- return this.getWestPoint();
43993
- case "NE":
43994
- case "northeast":
43995
- return this.getNorthEastPoint();
43996
- case "NW":
43997
- case "northwest":
43998
- return this.getNorthWestPoint();
43999
- case "SE":
44000
- case "southeast":
44001
- return this.getSouthEastPoint();
44002
- case "SW":
44003
- case "southwest":
44004
- return this.getSouthWestPoint();
44005
- case "NNE":
44006
- case "northnortheast":
44007
- return this.getNorthNorthEastPoint();
44008
- case "NNW":
44009
- case "northnorthwest":
44010
- return this.getNorthNorthWestPoint();
44011
- case "NEE":
44012
- case "northeast":
44013
- return this.getNorthEastEastPoint();
44014
- case "NWW":
44015
- case "northwest":
44016
- return this.getNorthWestWestPoint();
44017
- case "SSE":
44018
- case "southsoutheast":
44019
- return this.getSouthSouthEastPoint();
44020
- case "SSW":
44021
- case "southsouthwest":
44022
- return this.getSouthSouthWestPoint();
44023
- case "SEE":
44024
- case "southeast":
44025
- return this.getSouthEastEastPoint();
44026
- case "SWW":
44027
- case "southwest":
44028
- return this.getSouthWestWestPoint();
44029
- default:
44030
- throw new Error(`Unknown position: ${position}`);
43917
+ animate() {
43918
+ if (this.tween) this.tween.update();
43919
+ requestAnimationFrame(this.animate.bind(this));
43920
+ this.controls.update();
43921
+ this.renderer.render(this.scene, this.camera);
43922
+ if (this.spotLightPosX > 500) {
43923
+ this.spotLightDirection = -1;
43924
+ this.spotLightPosX = 500;
43925
+ }
43926
+ if (this.spotLightPosX < -500) {
43927
+ this.spotLightDirection = 1;
43928
+ this.spotLightPosX = -500;
43929
+ }
43930
+ this.spotLightPosX += 10 * this.spotLightDirection;
43931
+ this.spotLight.position.set(this.spotLightPosX, this.spotLightPosY, this.spotLightPosZ);
43932
+ }
43933
+ reset() {
43934
+ this.hideHelpers();
43935
+ this.controls.reset();
43936
+ this.setMode("VIEW");
43937
+ this.fitScreen();
43938
+ }
43939
+ // ================================================================
43940
+ // Diagram arrangement
43941
+ // ================================================================
43942
+ arrange() {
43943
+ if (this.scene.children.length === 0) {
43944
+ console.warn("Scene is empty. Cannot calculate center.");
43945
+ return;
44031
43946
  }
43947
+ const box = new Box3().setFromObject(this.scene);
43948
+ const center = box.getCenter(new Vector3$1());
43949
+ const translation = new Vector3$1(-center.x, -center.y, -center.z);
43950
+ this.scene.children.forEach((child) => {
43951
+ if (child instanceof Object3D) {
43952
+ child.position.add(translation);
43953
+ }
43954
+ });
44032
43955
  }
44033
- // ================================================================
44034
- // Add Text methods
44035
- // ================================================================
44036
43956
  /**
44037
- * Adds text to the element.
44038
- * @param {string} text - The text to add
44039
- * @returns {Element} This element for method chaining
43957
+ * Calculates the optimal zoom distance for the camera to ensure the entire scene is visible.
43958
+ *
43959
+ * @returns {number} The optimal distance for the camera to fit the scene within the viewport.
44040
43960
  */
44041
- addText(text) {
44042
- const textElement = new Element(this.elementId + "_text", new TextShape(text));
44043
- this.diagram.addElement(textElement).positionAt(this.position);
44044
- this.texts.push({ element: textElement, positionOffset: new Vector3$1(0, 0, 0) });
44045
- return this;
43961
+ calculateOptimalZoom() {
43962
+ const box = new Box3().setFromObject(this.scene);
43963
+ const size = box.getSize(new Vector3$1());
43964
+ const aspect2 = this.container.clientWidth / this.container.clientHeight;
43965
+ const fovRad = MathUtils$1.degToRad(this.camera.fov);
43966
+ const distanceForWidth = size.x / 2 / (Math.tan(fovRad / 2) * aspect2);
43967
+ const distanceForHeight = size.y / 2 / Math.tan(fovRad / 2);
43968
+ return Math.max(distanceForWidth, distanceForHeight);
44046
43969
  }
44047
43970
  /**
44048
- * Adds wrapped text that fits within the element's bounds.
44049
- * @param {string} text - The text to add and wrap
44050
- * @returns {Element} This element for method chaining
43971
+ * Adjusts the camera to fit the entire scene within the screen.
43972
+ *
43973
+ * This method calculates the optimal zoom level and positions the camera
43974
+ * at a distance that ensures the entire scene is visible, with a small margin.
43975
+ * It also updates the camera's orientation to look at the center of the scene
43976
+ * and saves the current camera state for later restoration.
44051
43977
  */
44052
- addWrappedText(text) {
44053
- if (!text) {
44054
- console.warn("addWrappedText: text is null or undefined");
44055
- return this;
44056
- }
44057
- let wrappedText = text.replace(/\s+/g, "\n");
44058
- let wrappedTextElement = new Element(this.elementId + "_text", new TextShape(wrappedText));
44059
- let candidateTextElement;
44060
- while (true) {
44061
- let words = wrappedText.split("\n");
44062
- let minLength = Infinity;
44063
- let minIndex = -1;
44064
- for (let i = 0; i < words.length - 1; i++) {
44065
- let combinedLength = words[i].length + words[i + 1].length;
44066
- if (combinedLength < minLength) {
44067
- minLength = combinedLength;
44068
- minIndex = i;
44069
- }
44070
- }
44071
- if (minIndex === -1) break;
44072
- words[minIndex] = words[minIndex] + " " + words[minIndex + 1];
44073
- words.splice(minIndex + 1, 1);
44074
- wrappedText = words.join("\n");
44075
- candidateTextElement = new Element(this.elementId + "_text", new TextShape(wrappedText));
44076
- if (candidateTextElement.getSize().x <= this.getSize().x * 0.9) {
44077
- wrappedTextElement = candidateTextElement;
44078
- } else {
44079
- break;
44080
- }
44081
- }
44082
- this.diagram.addElement(wrappedTextElement).positionAt({ x: this.position.x, y: this.position.y, z: 3 });
44083
- this.texts.push({ element: wrappedTextElement, positionOffset: new Vector3$1(0, 0, 3) });
44084
- return this;
43978
+ fitScreen() {
43979
+ const minZDistance = this.calculateOptimalZoom();
43980
+ const margin = 1.05;
43981
+ const cameraZ = minZDistance * margin;
43982
+ this.camera.position.set(0, 0, cameraZ);
43983
+ this.camera.lookAt(0, 0, 0);
43984
+ this.camera.updateProjectionMatrix();
43985
+ this.controls.saveState();
43986
+ this.initialCameraPosition = this.camera.position.clone();
43987
+ this.initialTarget = this.controls.target.clone();
44085
43988
  }
44086
- // ================================================================
44087
- // Add icons methods
44088
- // ================================================================
44089
43989
  /**
44090
- * Gets the placeholder position for an icon at the top of the element.
44091
- * The position is calculated by taking the element's top edge and offsetting it inward
44092
- * by half the icon size plus padding.
44093
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44094
- * where x is the element's center x-coordinate
44095
- * and y is the element's top edge y-coordinate minus (icon size/2 + padding)
43990
+ * Centers the diagram by moving the camera to its initial position and target.
43991
+ * This method uses the Tween.js library to animate the camera movement.
43992
+ *
43993
+ * Preconditions:
43994
+ * - `this.initialCameraPosition` and `this.initialTarget` must be defined.
43995
+ *
43996
+ * Behavior:
43997
+ * - If the initial camera position or target is not defined, a warning is logged, and the method exits.
43998
+ * - Animates the camera's position and the controls' target to their initial states over 1200 milliseconds.
43999
+ * - Uses a Quartic easing function for smooth animation.
44000
+ *
44001
+ * Dependencies:
44002
+ * - Tween.js library for animation.
44003
+ *
44004
+ * @returns {void}
44096
44005
  */
44097
- getTopIconPlaceholder() {
44098
- return new Vector2$1(
44099
- this.position.x,
44100
- this.position.y + this.getSize().y / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING
44101
- );
44006
+ center() {
44007
+ if (!this.initialCameraPosition || !this.initialTarget) {
44008
+ console.warn("Initial camera position or target is not defined.");
44009
+ return;
44010
+ }
44011
+ const from = {
44012
+ cameraPositionX: this.camera.position.x,
44013
+ cameraPositionY: this.camera.position.y,
44014
+ cameraPositionZ: this.camera.position.z,
44015
+ controlsTargetX: this.controls.target.x,
44016
+ controlsTargetY: this.controls.target.y,
44017
+ controlsTargetZ: this.controls.target.z
44018
+ };
44019
+ const to = {
44020
+ cameraPositionX: this.initialCameraPosition.x,
44021
+ cameraPositionY: this.initialCameraPosition.y,
44022
+ cameraPositionZ: this.initialCameraPosition.z,
44023
+ controlsTargetX: this.initialTarget.x,
44024
+ controlsTargetY: this.initialTarget.y,
44025
+ controlsTargetZ: this.initialTarget.z
44026
+ };
44027
+ const camera = this.camera;
44028
+ const controls = this.controls;
44029
+ this.tween = new Tween$1(from).to(to, 1200).easing(Easing.Quartic.Out).onUpdate(function() {
44030
+ camera.position.set(
44031
+ from.cameraPositionX,
44032
+ from.cameraPositionY,
44033
+ from.cameraPositionZ
44034
+ );
44035
+ controls.target.set(
44036
+ from.controlsTargetX,
44037
+ from.controlsTargetY,
44038
+ from.controlsTargetZ
44039
+ );
44040
+ }).onComplete(function() {
44041
+ }).start();
44102
44042
  }
44103
44043
  /**
44104
- * Gets the placeholder position for an icon at the bottom of the element.
44105
- * The position is calculated by taking the element's bottom edge and offsetting it inward
44106
- * by half the icon size plus padding.
44107
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44108
- * where x is the element's center x-coordinate
44109
- * and y is the element's bottom edge y-coordinate plus (icon size/2 + padding)
44044
+ * Rotates the diagram around the Y axis by a specified angle in degrees.
44045
+ * The method ensures the diagram is centered and calculates the new camera
44046
+ * and target positions based on the provided angle.
44047
+ *
44048
+ * @param {number} targetAngle - The angle in degrees to rotate the diagram (e.g., 60).
44049
+ * @returns {void} - Does not return a value.
44050
+ *
44051
+ * @throws {Error} Logs a warning if the initial camera position or target is not defined.
44052
+ *
44053
+ * @example
44054
+ * // Rotate the diagram by 60 degrees
44055
+ * diagram.rotate(60);
44110
44056
  */
44111
- getBottomIconPlaceholder() {
44112
- return new Vector2$1(
44113
- this.position.x,
44114
- this.position.y - this.getSize().y / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING
44057
+ rotate(targetAngle) {
44058
+ if (!this.initialCameraPosition || !this.initialTarget) {
44059
+ console.warn("Initial camera position or target is not defined.");
44060
+ return;
44061
+ }
44062
+ const radius = Math.sqrt(
44063
+ this.initialCameraPosition.y * this.initialCameraPosition.y + this.initialCameraPosition.z * this.initialCameraPosition.z
44115
44064
  );
44065
+ const from = {
44066
+ cameraPositionX: this.camera.position.x,
44067
+ cameraPositionY: this.camera.position.y,
44068
+ cameraPositionZ: this.camera.position.z,
44069
+ controlsTargetX: this.controls.target.x,
44070
+ controlsTargetY: this.controls.target.y,
44071
+ controlsTargetZ: this.controls.target.z
44072
+ };
44073
+ const targetAngleRad = MathUtils$1.degToRad(targetAngle);
44074
+ const to = {
44075
+ cameraPositionX: this.initialCameraPosition.x,
44076
+ cameraPositionY: radius * Math.sin(targetAngleRad),
44077
+ cameraPositionZ: radius * Math.cos(targetAngleRad),
44078
+ controlsTargetX: this.initialTarget.x,
44079
+ controlsTargetY: this.initialTarget.y,
44080
+ controlsTargetZ: this.initialTarget.z
44081
+ };
44082
+ console.log("rotate() -> from:", from);
44083
+ console.log("rotate() -> to:", to);
44084
+ const camera = this.camera;
44085
+ const controls = this.controls;
44086
+ this.tween = new Tween$1(from).to(to, 1200).easing(Easing.Quartic.Out).onUpdate(function() {
44087
+ camera.position.set(
44088
+ from.cameraPositionX,
44089
+ from.cameraPositionY,
44090
+ from.cameraPositionZ
44091
+ );
44092
+ controls.target.set(
44093
+ from.controlsTargetX,
44094
+ from.controlsTargetY,
44095
+ from.controlsTargetZ
44096
+ );
44097
+ }).start();
44116
44098
  }
44099
+ // ================================================================
44100
+ // Diagram modes
44101
+ // ================================================================
44117
44102
  /**
44118
- * Gets the placeholder position for an icon at the left of the element.
44119
- * The position is calculated by taking the element's left edge and offsetting it inward
44120
- * by half the icon size plus padding.
44121
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44122
- * where x is the element's left edge x-coordinate plus (icon size/2 + padding)
44123
- * and y is the element's center y-coordinate
44103
+ * Removes all elements of type 'ValueBarShape' from the diagram.
44104
+ * Iterates through the `elements` array in reverse order to safely remove
44105
+ * elements without affecting the iteration process. For each matching element,
44106
+ * it removes the element from its parent (and thus from the scene) and also
44107
+ * removes it from the `elements` array.
44124
44108
  */
44125
- getLeftIconPlaceholder() {
44126
- return new Vector2$1(
44127
- this.position.x - this.getSize().x / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING,
44128
- this.position.y
44129
- );
44109
+ removeValueBars() {
44110
+ for (let i = this.elements.length - 1; i >= 0; i--) {
44111
+ const element2 = this.elements[i];
44112
+ if (element2.type === "ValueBarShape") {
44113
+ if (element2.parent) {
44114
+ element2.parent.remove(element2);
44115
+ }
44116
+ this.scene.remove(element2);
44117
+ this.elements.splice(i, 1);
44118
+ }
44119
+ }
44130
44120
  }
44131
44121
  /**
44132
- * Gets the placeholder position for an icon at the right of the element.
44133
- * The position is calculated by taking the element's right edge and offsetting it inward
44134
- * by half the icon size plus padding.
44135
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44136
- * where x is the element's right edge x-coordinate minus (icon size/2 + padding)
44137
- * and y is the element's center y-coordinate
44122
+ * Adds value bars to the diagram to visualize the elements' parameters.
44123
+ *
44124
+ * This method processes the elements in the diagram, calculates the height
44125
+ * of the bars based on their parameter values, and assigns a color to each
44126
+ * bar based on a normalized value. The bars are then added to the scene.
44127
+ *
44128
+ * @method
44129
+ * @memberof Diagram
44130
+ * @description
44131
+ * - Filters elements to include only those with a defined `parameters.value`.
44132
+ * - Calculates the range of parameter values to normalize them.
44133
+ * - Assigns a color to each bar using an HSL color scale (green to red).
44134
+ * - Calls the `valueBar` method on each element to set the bar's height and color.
44135
+ *
44136
+ * @example
44137
+ * // Assuming `diagram` is an instance of Diagram with elements having parameters:
44138
+ * diagram.addValueBars();
44139
+ *
44140
+ * @throws {Error} If no elements with `parameters.value` are found.
44138
44141
  */
44139
- getRightIconPlaceholder() {
44140
- return new Vector2$1(
44141
- this.position.x + this.getSize().x / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING,
44142
- this.position.y
44143
- );
44142
+ addValueBars() {
44143
+ const elements = this.elements.filter((el) => el.parameters && el.parameters.value !== void 0);
44144
+ if (elements.length === 0) {
44145
+ throw new Error("No elements with `parameters.value` found.");
44146
+ }
44147
+ const max2 = Math.max(...elements.map((el) => el.parameters.value));
44148
+ const min2 = 0;
44149
+ const range = max2 - min2;
44150
+ elements.forEach((element2, i) => {
44151
+ const value = element2.parameters.value;
44152
+ const normalizedValue = (value - min2) / range;
44153
+ const color2 = new Color$1(`hsl(${(normalizedValue * 120).toString(10)}, 100%, 50%)`);
44154
+ element2.addValueBar(normalizedValue * 100, color2);
44155
+ });
44144
44156
  }
44145
44157
  /**
44146
- * Gets the placeholder position for an icon at the top-left corner of the element.
44147
- * The position is calculated by taking the element's top-left corner and offsetting it inward
44148
- * by half the icon size plus padding along both axes.
44149
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44150
- * where x is the element's left edge x-coordinate plus (icon size/2 + padding)
44151
- * and y is the element's top edge y-coordinate minus (icon size/2 + padding)
44158
+ * Sets the mode of the diagram and adjusts its state accordingly.
44159
+ *
44160
+ * @param {string} mode - The mode to set. Possible values are:
44161
+ * - 'EDIT': Sets the diagram to edit mode and resets rotation.
44162
+ * - 'VIEW': Sets the diagram to view mode and resets rotation.
44163
+ * - 'ANALYZE': Sets the diagram to analyze mode, rotates it to -60 degrees,
44164
+ * and adds value bars.
44165
+ * - Any other value will log a warning about an unknown mode.
44152
44166
  */
44153
- getTopLeftIconPlaceholder() {
44154
- return new Vector2$1(
44155
- this.position.x - this.getSize().x / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING,
44156
- this.position.y + this.getSize().y / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING
44157
- );
44167
+ setMode(mode) {
44168
+ this.removeValueBars();
44169
+ this.mode = mode;
44170
+ switch (mode) {
44171
+ case "EDIT":
44172
+ case "VIEW":
44173
+ this.rotate(0);
44174
+ break;
44175
+ case "ANALYZE":
44176
+ this.rotate(-65);
44177
+ const elementsWithValue = this.elements.filter((el) => el.parameters && el.parameters.value !== void 0);
44178
+ if (elementsWithValue.length === 0) {
44179
+ break;
44180
+ }
44181
+ const values = elementsWithValue.map((el) => el.parameters.value);
44182
+ const dataMax = Math.max(...values);
44183
+ const dataMin = Math.min(...values);
44184
+ console.log(`[Diagram.js] Coloring Range | Min: ${dataMin}, Max: ${dataMax}`);
44185
+ elementsWithValue.forEach((element2) => {
44186
+ const originalValue = element2.parameters.value;
44187
+ const normalizedHeight = dataMax === 0 ? 0 : originalValue / dataMax * 100;
44188
+ const color2 = getColorForValue(originalValue, dataMin, dataMax);
44189
+ const barShape = new ValueBarShape(element2.shape.getOuterShape(), normalizedHeight, color2);
44190
+ const barElement = new Element(element2.elementId + "_bar", barShape);
44191
+ this.addElement(barElement).positionAt(element2.getPosition());
44192
+ element2.valueBars.push({ element: barElement, positionOffset: new Vector3$1(0, 0, 0) });
44193
+ });
44194
+ break;
44195
+ default:
44196
+ console.warn(`Unknown mode: ${mode}`);
44197
+ }
44158
44198
  }
44199
+ // ================================================================
44200
+ // Diagram elements
44201
+ // ================================================================
44159
44202
  /**
44160
- * Gets the placeholder position for an icon at the top-right corner of the element.
44161
- * The position is calculated by taking the element's top-right corner and offsetting it inward
44162
- * by half the icon size plus padding along both axes.
44163
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44164
- * where x is the element's right edge x-coordinate minus (icon size/2 + padding)
44165
- * and y is the element's top edge y-coordinate minus (icon size/2 + padding)
44203
+ * Adds an element to the diagram.
44204
+ * @param {Object3D} element - The element to add.
44205
+ * @param {Vector3} [position] - The position to place the element.
44206
+ * @returns {Object3D} The added element.
44166
44207
  */
44167
- getTopRightIconPlaceholder() {
44168
- return new Vector2$1(
44169
- this.position.x + this.getSize().x / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING,
44170
- this.position.y + this.getSize().y / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING
44171
- );
44208
+ addElement(element2, position) {
44209
+ this.elements.push(element2);
44210
+ this.scene.add(element2);
44211
+ if (position) element2.position.set(position.x, position.y, 0);
44212
+ element2.setDiagram(this);
44213
+ return element2;
44172
44214
  }
44173
44215
  /**
44174
- * Gets the placeholder position for an icon at the bottom-left corner of the element.
44175
- * The position is calculated by taking the element's bottom-left corner and offsetting it inward
44176
- * by half the icon size plus padding along both axes.
44177
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44178
- * where x is the element's left edge x-coordinate plus (icon size/2 + padding)
44179
- * and y is the element's bottom edge y-coordinate plus (icon size/2 + padding)
44216
+ * Removes an element from the diagram by its ID.
44217
+ * @param {string} elementId - The ID of the element to remove.
44180
44218
  */
44181
- getBottomLeftIconPlaceholder() {
44182
- return new Vector2$1(
44183
- this.position.x - this.getSize().x / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING,
44184
- this.position.y - this.getSize().y / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING
44185
- );
44219
+ removeElement(elementId) {
44220
+ const element2 = this.elements.find((el) => el.id === elementId);
44221
+ if (element2) {
44222
+ this.scene.remove(element2);
44223
+ this.elements = this.elements.filter((el) => el.id !== elementId);
44224
+ }
44186
44225
  }
44187
44226
  /**
44188
- * Gets the placeholder position for an icon at the bottom-right corner of the element.
44189
- * The position is calculated by taking the element's bottom-right corner and offsetting it inward
44190
- * by half the icon size plus padding along both axes.
44191
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44192
- * where x is the element's right edge x-coordinate minus (icon size/2 + padding)
44193
- * and y is the element's bottom edge y-coordinate plus (icon size/2 + padding)
44227
+ * Retrieves an element from the `elements` array by its unique `elementId`.
44228
+ *
44229
+ * @param {string} elementId - The unique identifier of the element to find.
44230
+ * @returns {Object|undefined} The element with the matching `elementId`, or `undefined` if not found.
44194
44231
  */
44195
- getBottomRightIconPlaceholder() {
44196
- return new Vector2$1(
44197
- this.position.x + this.getSize().x / 2 - IconDimensions.ICON_SIZE_SMALL / 2 - IconDimensions.ICON_PADDING,
44198
- this.position.y - this.getSize().y / 2 + IconDimensions.ICON_SIZE_SMALL / 2 + IconDimensions.ICON_PADDING
44199
- );
44232
+ getElementById(elementId) {
44233
+ return this.elements.find((el) => el.elementId === elementId);
44200
44234
  }
44201
44235
  /**
44202
- * Gets the placeholder position for an icon at the center of the element.
44203
- * The position is at the element's center.
44204
- * @returns {THREE.Vector2} A 2D vector representing the placeholder coordinates
44205
- * where x is the element's center x-coordinate
44206
- * and y is the element's center y-coordinate
44236
+ * Retrieves the elements of the diagram.
44237
+ *
44238
+ * @returns {Array} The array of elements in the diagram.
44207
44239
  */
44208
- getCenterIconPlaceholder() {
44209
- return new Vector2$1(this.position.x, this.position.y);
44240
+ getElements() {
44241
+ return this.elements;
44210
44242
  }
44243
+ // ================================================================
44244
+ // Diagram Connectors
44245
+ // ================================================================
44211
44246
  /**
44212
- * Gets the position for an icon placeholder.
44213
- * @param {string} position - Position identifier (top, bottom, left, right, etc.)
44214
- * @returns {THREE.Vector2} The placeholder position
44215
- * @throws {Error} If the position identifier is invalid
44247
+ * Adds a connector to the diagram, registers it with the diagram,
44248
+ * and adds it to the scene for rendering.
44249
+ *
44250
+ * @param {Object} connector - The connector object to be added to the diagram.
44251
+ * @returns {Object} The connector that was added.
44216
44252
  */
44217
- getIconPlaceholder(position) {
44218
- switch (position) {
44219
- case "top":
44220
- return this.getTopIconPlaceholder();
44221
- case "bottom":
44222
- return this.getBottomIconPlaceholder();
44223
- case "left":
44224
- return this.getLeftIconPlaceholder();
44225
- case "right":
44226
- return this.getRightIconPlaceholder();
44227
- case "top-left":
44228
- return this.getTopLeftIconPlaceholder();
44229
- case "top-right":
44230
- return this.getTopRightIconPlaceholder();
44231
- case "bottom-left":
44232
- return this.getBottomLeftIconPlaceholder();
44233
- case "bottom-right":
44234
- return this.getBottomRightIconPlaceholder();
44235
- case "center":
44236
- return this.getCenterIconPlaceholder();
44237
- default:
44238
- throw new Error(`Unknown icon position: ${position}`);
44239
- }
44253
+ addConnector(connector) {
44254
+ this.connectors.push(connector);
44255
+ this.scene.add(connector);
44256
+ connector.setDiagram(this);
44257
+ return connector;
44240
44258
  }
44259
+ // ================================================================
44260
+ // Clear diagram
44261
+ // ================================================================
44241
44262
  /**
44242
- * Adds an icon to the element.
44243
- * @param {string} icon - The icon identifier
44244
- * @param {string} [placeholder='center'] - Position of the icon
44245
- * @param {number} [size=IconDimensions.ICON_SIZE_MEDIUM] - Size of the icon
44246
- * @returns {Element} This element for method chaining
44263
+ * Clears all elements and connectors from the diagram.
44247
44264
  */
44248
- addIcon(icon, placeholder = "center", size = IconDimensions.ICON_SIZE_MEDIUM) {
44249
- let position;
44250
- if (typeof placeholder === "string") {
44251
- position = this.getIconPlaceholder(placeholder);
44252
- }
44253
- const iconElement = new Element(this.elementId + "_icon_placeholder", new IconShape(icon, size));
44254
- this.diagram.addElement(iconElement).positionAt(position);
44255
- this.icons.push({ element: iconElement, positionOffset: new Vector3$1(position.x - this.position.x, position.y - this.position.y, 0) });
44256
- return this;
44265
+ clear() {
44266
+ this.elements = [];
44267
+ this.connectors = [];
44268
+ this.scene.children = this.scene.children.filter((child) => child instanceof AmbientLight);
44257
44269
  }
44258
44270
  // ================================================================
44259
- // Add Analysis methods
44271
+ // Diagram JSON
44260
44272
  // ================================================================
44261
- /**
44262
- * Adds a value bar for analysis mode.
44263
- * @param {number} value - The value to represent (must be positive)
44264
- * @returns {Element} This element for method chaining
44265
- */
44266
- addValueBar(value) {
44267
- if (value < 0) {
44268
- console.warn("valueBar: Value must be positive");
44269
- return this;
44270
- }
44271
- this.parameters["value"] = value;
44272
- console.log("valueBar added:", this);
44273
- if (this.diagram.mode !== "ANALYZE") {
44274
- console.warn("valueBar: Diagram mode is not ANALYZE");
44275
- return this;
44276
- }
44277
- const barElement = new Element(this.elementId + "_bar", new ValueBarShape(this.shape.getOuterShape(), value));
44278
- this.diagram.addElement(barElement).positionAt(this.position);
44279
- this.valueBars.push({ element: barElement, positionOffset: new Vector3$1(0, 0, 0) });
44280
- return this;
44273
+ toJSON() {
44274
+ return JSON.stringify(this.elements);
44275
+ }
44276
+ fromJSON(json) {
44277
+ this.elements = JSON.parse(json);
44281
44278
  }
44282
44279
  // ================================================================
44283
- // Add connecting methods
44280
+ // Diagram export and import to/from file
44284
44281
  // ================================================================
44285
44282
  /**
44286
- * Creates a connector from another element to this element.
44287
- * @param {string} sourceElementId - ID of the source element
44288
- * @param {string} sourcePosition - Connection point on the source element
44289
- * @param {string} targetPosition - Connection point on this element
44290
- * @returns {Element} This element for method chaining
44291
- * @throws {Error} If the diagram is not set or source element is not found
44283
+ * Exports the diagram to a JSON file.
44292
44284
  */
44293
- connectFrom(sourceElementId, sourcePosition, targetPosition) {
44294
- if (!this.diagram) {
44295
- throw new Error("Diagram is not set for this element.");
44296
- }
44297
- const targetElement = this;
44298
- const sourceElement = this.diagram.getElementById(sourceElementId);
44299
- if (!sourceElement) {
44300
- throw new Error(`Element with ID ${sourceElementId} not found.`);
44301
- }
44302
- const sourcePoint = sourceElement.getPointPosition(sourcePosition);
44303
- const targetPoint = targetElement.getPointPosition(targetPosition);
44304
- const points = Connector.determinePoints(sourcePoint, targetPoint, sourcePosition, targetPosition);
44305
- this.diagram.addConnector(new Connector(
44306
- `connector-${sourceElement.elementId}-${targetElement.elementId}`,
44307
- new RoundedCornerOrthogonalConnectorShape(points)
44308
- ));
44309
- return this;
44285
+ export() {
44286
+ const data = JSON.stringify(this.scene.toJSON());
44287
+ const blob = new Blob([data], { type: "application/json" });
44288
+ const url = URL.createObjectURL(blob);
44289
+ const a = document.createElement("a");
44290
+ a.href = url;
44291
+ a.download = "diagram.json";
44292
+ a.click();
44293
+ URL.revokeObjectURL(url);
44294
+ }
44295
+ /**
44296
+ * Placeholder for importing a diagram from a file.
44297
+ * This method should be implemented by subclasses.
44298
+ * @param {File} file - The file to import.
44299
+ * @throws {Error} If the method is not implemented.
44300
+ * @returns {Promise<void>}
44301
+ */
44302
+ import(file) {
44303
+ console.error("Import method should be implemented by subclasses.");
44310
44304
  }
44311
44305
  }
44312
44306
  const RoundedRectangleDimensions = {