architwin 1.14.8 → 1.14.9

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.
@@ -4,6 +4,7 @@ import { SPACE_EVENTS } from "../types";
4
4
  import { getPolygonArea } from "../utils";
5
5
  import { PolygonCalculator, WallCalculator } from "../math";
6
6
  import i18n from "../atwinui/components/toolbar/i18n";
7
+ import log from "loglevel";
7
8
  export class TubeLine {
8
9
  constructor(mpSdk) {
9
10
  this.time = 0;
@@ -34,7 +35,11 @@ export class TubeLine {
34
35
  scrollSpeed: 1.35,
35
36
  collider: true,
36
37
  visible: false,
37
- renderPolygonOnAdd: true
38
+ drawingMode: 'floor',
39
+ targetIndex: undefined,
40
+ polygonData: undefined,
41
+ renderPolygonOnAdd: true,
42
+ targetUUID: undefined,
38
43
  };
39
44
  //@ts-expect-error
40
45
  this.outputs = {
@@ -77,11 +82,21 @@ export class TubeLine {
77
82
  if (this.inputs.path.length > 1) {
78
83
  this.renderTubeLine();
79
84
  console.log("paths ", this.inputs.path);
80
- if (isToolbarFeatureEnabled('roomCreation') && this.inputs.path.length >= 3 && this.inputs.renderPolygonOnAdd) {
85
+ if (isToolbarFeatureEnabled('roomCreation') && this.inputs.path.length >= 3 && this.inputs.drawingMode == 'window') {
86
+ const options = {
87
+ drawingMode: this.inputs.drawingMode,
88
+ targetIndex: this.inputs.targetIndex,
89
+ polygonData: this.inputs.polygonData,
90
+ targetUUID: this.inputs.targetUUID
91
+ };
92
+ renderPolygon(this.inputs.path, options);
93
+ }
94
+ else if (isToolbarFeatureEnabled('roomCreation') && this.inputs.path.length >= 3 && this.inputs.renderPolygonOnAdd) {
81
95
  const wallHeight = getWallBaseHeight();
82
96
  if (wallHeight) {
83
97
  const options = {
84
- wallHeight: wallHeight
98
+ wallHeight: wallHeight,
99
+ drawingMode: this.inputs.drawingMode
85
100
  };
86
101
  renderPolygon(this.inputs.path, options);
87
102
  }
@@ -481,6 +496,8 @@ export class BufferGeometry {
481
496
  floorName: '',
482
497
  floorColor: 0x0000ff,
483
498
  wallColor: 0xffd150,
499
+ // windowColor : 0xEAF2EF,
500
+ windowColor: 0xC09672,
484
501
  floorOpacity: 0.3,
485
502
  wallOpacity: 0.6,
486
503
  visible: true,
@@ -489,7 +506,11 @@ export class BufferGeometry {
489
506
  polygonData: undefined,
490
507
  excludeHiddenWallsFromCalculation: true,
491
508
  wallHeight: 2,
492
- floorLevel: undefined
509
+ floorLevel: undefined,
510
+ drawingMode: 'floor',
511
+ windowData: undefined,
512
+ targetIndex: undefined,
513
+ targetUUID: undefined,
493
514
  };
494
515
  this.emits = {
495
516
  changed: true,
@@ -557,13 +578,13 @@ export class BufferGeometry {
557
578
  const polyGeometry = new THREE.ShapeGeometry(polyShape);
558
579
  polyGeometry.rotateX(Math.PI / 2);
559
580
  const floorMesh = new THREE.Mesh(polyGeometry, new THREE.MeshBasicMaterial({
560
- color: this.inputs.floorColor,
581
+ color: this.inputs.drawingMode == 'floor' ? this.inputs.floorColor : this.inputs.windowColor,
561
582
  side: THREE.DoubleSide,
562
583
  opacity: this.inputs.floorOpacity,
563
584
  transparent: true
564
585
  }));
565
586
  floorMesh.translateY(floorLevel);
566
- floorMesh.name = this.inputs.uuid != '' ? `${this.inputs.uuid}_floor` : 'floor';
587
+ floorMesh.name = this.inputs.uuid != '' ? `${this.inputs.uuid}_${this.inputs.drawingMode}` : `${this.inputs.drawingMode}`;
567
588
  const floorConfig = polyData ? polyData.floor : this.inputs.floorData;
568
589
  const floorDimension = PolygonCalculator.calculatePolygonProperties(this.inputs.path);
569
590
  if (floorConfig && floorConfig.options) {
@@ -574,54 +595,185 @@ export class BufferGeometry {
574
595
  // }
575
596
  }
576
597
  this.groupedMesh.add(floorMesh);
577
- const floorArea = getPolygonArea(this.inputs.path);
578
- let [floors, walls] = this.renderWalls(WALL_HEIGHT);
579
- floorDataArray = floors;
580
- wallDataArray = walls;
581
- //end of wall loop
582
- const floorData = {
583
- options: {
584
- color: this.inputs.floorColor,
585
- opacity: this.inputs.floorOpacity,
586
- is_visible: floorMesh.visible
587
- },
588
- area: floorDimension.area,
589
- perimeter: floorDimension.perimeter,
590
- floor_level: floorLevel,
591
- edges: floorDataArray
592
- };
593
- if (polyData && polyData.floor.uuid) {
594
- floorData.uuid = polyData.floor.uuid;
598
+ try {
599
+ let twiceSignedArea = 0;
600
+ let centroidX = 0;
601
+ let centroidZ = 0;
602
+ for (let i = 0; i < shapeCoords.length; i++) {
603
+ const p1 = shapeCoords[i];
604
+ const p2 = shapeCoords[(i + 1) % shapeCoords.length];
605
+ const cross = p1.x * p2.y - p2.x * p1.y;
606
+ twiceSignedArea += cross;
607
+ centroidX += (p1.x + p2.x) * cross;
608
+ centroidZ += (p1.y + p2.y) * cross;
609
+ }
610
+ let centerX;
611
+ let centerZ;
612
+ if (Math.abs(twiceSignedArea) > 1e-8) {
613
+ const polygonArea = twiceSignedArea * 0.5;
614
+ centerX = centroidX / (6 * polygonArea);
615
+ centerZ = centroidZ / (6 * polygonArea);
616
+ }
617
+ else {
618
+ // Fallback: average of vertices
619
+ const sum = shapeCoords.reduce((acc, p) => ({ x: acc.x + p.x, z: acc.z + p.y }), { x: 0, z: 0 });
620
+ centerX = sum.x / shapeCoords.length;
621
+ centerZ = sum.z / shapeCoords.length;
622
+ }
623
+ const floorMaterial = polyData && polyData.floor.material ? polyData.floor.material : undefined;
624
+ // const floorLabelText = `Floor_Cement: ${floorDimension.area.toFixed(2)}m²`;
625
+ const floorLabelText = `Floor ${floorMaterial ? '_' + floorMaterial : ' (' + i18n.t('Area') + ')'} : ${floorDimension.area.toFixed(2)}m²`;
626
+ // const floorLabelText = `${floorMaterial ? floorMaterial : i18n.t('Area')}: ${floorDimension.area.toFixed(2)}m²`;
627
+ const floorCanvas = this.createLabelCanvas(floorLabelText);
628
+ const labelWidth = Math.max(1, Math.min(5, Math.sqrt(Math.max(0.0001, floorDimension.area))));
629
+ const labelHeight = labelWidth / 4;
630
+ const floorLabelGeometry = new THREE.PlaneGeometry(labelWidth, labelHeight);
631
+ const floorLabelMaterial = new THREE.MeshBasicMaterial({
632
+ map: new THREE.CanvasTexture(floorCanvas),
633
+ transparent: true,
634
+ side: THREE.DoubleSide,
635
+ });
636
+ const floorCenterLabel = new THREE.Mesh(floorLabelGeometry, floorLabelMaterial);
637
+ // Lay flat on the floor (XZ plane) and slightly offset in Y to avoid z-fighting
638
+ floorCenterLabel.rotation.x = -Math.PI / 2;
639
+ floorCenterLabel.position.set(centerX, floorLevel + 0.02, centerZ);
640
+ floorCenterLabel.name = this.inputs.uuid != '' ? `${this.inputs.uuid}_floorCenterLabel` : `floorCenterLabel`;
641
+ if (floorConfig && floorConfig.options) {
642
+ floorCenterLabel.visible = floorConfig.options.is_visible;
643
+ }
644
+ this.floorLabels.push(floorCenterLabel);
645
+ this.groupedMesh.add(floorCenterLabel);
595
646
  }
596
- let totalWallArea = 0;
597
- let totalWallLength = 0;
598
- for (let index = 0; index < wallDataArray.length; index++) {
599
- if (wallDataArray[index].options && wallDataArray[index].options.is_deleted) {
600
- continue;
647
+ catch (e) {
648
+ log.error('Error creating floor center label', e);
649
+ }
650
+ const floorArea = getPolygonArea(this.inputs.path);
651
+ let partitionData = undefined;
652
+ if (this.inputs.drawingMode == 'floor') {
653
+ let [floors, walls] = this.renderWalls(WALL_HEIGHT, polyData);
654
+ floorDataArray = floors;
655
+ wallDataArray = walls;
656
+ //end of wall loop
657
+ const floorData = {
658
+ options: {
659
+ color: this.inputs.floorColor,
660
+ opacity: this.inputs.floorOpacity,
661
+ is_visible: floorMesh.visible
662
+ },
663
+ area: floorDimension.area,
664
+ perimeter: floorDimension.perimeter,
665
+ floor_level: floorLevel,
666
+ edges: floorDataArray,
667
+ material: polyData && polyData.floor.material ? polyData.floor.material : undefined
668
+ };
669
+ if (polyData && polyData.floor.uuid) {
670
+ if (polyData.floor.uuid)
671
+ floorData.uuid = polyData.floor.uuid;
672
+ if (polyData.floor.material)
673
+ floorData.material = polyData.floor.material;
601
674
  }
602
- if (this.inputs.excludeHiddenWallsFromCalculation && wallDataArray[index].options && wallDataArray[index].options.is_visible == false) {
603
- continue;
675
+ let totalWallArea = 0;
676
+ let totalWallLength = 0;
677
+ for (let index = 0; index < wallDataArray.length; index++) {
678
+ if (wallDataArray[index].options && wallDataArray[index].options.is_deleted) {
679
+ continue;
680
+ }
681
+ if (this.inputs.excludeHiddenWallsFromCalculation && wallDataArray[index].options && wallDataArray[index].options.is_visible == false) {
682
+ continue;
683
+ }
684
+ totalWallArea += wallDataArray[index].area;
685
+ totalWallLength += wallDataArray[index].width;
686
+ }
687
+ const totalWallPerimeter = totalWallLength + (WALL_HEIGHT * 2);
688
+ //WINDOW RENDERING
689
+ try {
690
+ if (polyData && polyData.walls && polyData.walls.length) {
691
+ log.info("polyData.walls ", polyData.walls);
692
+ const windowsPolyData = polyData.walls.reduce((acc, wall, index) => {
693
+ if (wall.windows && wall.windows.length > 0) {
694
+ acc = [...acc, ...wall.windows];
695
+ wallDataArray[index].windows = wall.windows;
696
+ }
697
+ return acc;
698
+ }, []);
699
+ this.renderWindows(windowsPolyData);
700
+ }
604
701
  }
605
- totalWallArea += wallDataArray[index].area;
606
- totalWallLength += wallDataArray[index].width;
702
+ catch (error) {
703
+ log.error("Error rendering windows ", error);
704
+ }
705
+ //this.mesh = this.groupedMesh;
706
+ const currentOffsetValue = getPolygonFloorOffset();
707
+ const currentFloor = getCurrentFloor();
708
+ partitionData = {
709
+ path: this.inputs.path,
710
+ floor_offset: currentOffsetValue,
711
+ floor_sequence: currentFloor,
712
+ floor: floorData,
713
+ walls: wallDataArray,
714
+ wall_height: WALL_HEIGHT,
715
+ totalWallArea,
716
+ totalWallPerimeter,
717
+ options: {
718
+ is_visible: this.inputs.visible
719
+ }
720
+ };
721
+ console.log("floor mode partitionData ", partitionData);
607
722
  }
608
- const totalWallPerimeter = totalWallLength + (WALL_HEIGHT * 2);
609
- //this.mesh = this.groupedMesh;
610
- const currentOffsetValue = getPolygonFloorOffset();
611
- const currentFloor = getCurrentFloor();
612
- const partitionData = {
613
- path: this.inputs.path,
614
- floor_offset: currentOffsetValue,
615
- floor_sequence: currentFloor,
616
- floor: floorData,
617
- walls: wallDataArray,
618
- wall_height: WALL_HEIGHT,
619
- totalWallArea,
620
- totalWallPerimeter,
621
- options: {
622
- is_visible: this.inputs.visible
723
+ //IF DRAWING WINDOWS
724
+ else if (this.inputs.drawingMode == 'window' && this.inputs.polygonData && this.inputs.targetIndex != undefined) {
725
+ const polyData = this.inputs.polygonData;
726
+ const targetWallIndex = polyData.walls.findIndex(wall => wall.uuid == this.inputs.targetUUID);
727
+ if (targetWallIndex != undefined) {
728
+ if (!polyData.walls[targetWallIndex].windows)
729
+ polyData.walls[targetWallIndex].windows = [];
730
+ const currentWindowCount = polyData.walls[targetWallIndex].windows ? polyData.walls[targetWallIndex].windows.length + 1 : 0;
731
+ const windowDimension = PolygonCalculator.calculatePolygonProperties(this.inputs.path, true);
732
+ let windowDataArray = [];
733
+ for (let i = 0; i < this.inputs.path.length; i++) {
734
+ const startPoint = this.inputs.path[i];
735
+ const endPoint = this.inputs.path[(i + 1) % this.inputs.path.length];
736
+ const windowLength = new THREE.Vector3(endPoint.x - startPoint.x, endPoint.y - startPoint.y, endPoint.z - startPoint.z).length();
737
+ const windowEdges = {
738
+ start: startPoint,
739
+ end: endPoint,
740
+ length: windowLength
741
+ };
742
+ windowDataArray.push(windowEdges);
743
+ }
744
+ let windowName = `${this.inputs.uuid}_window-${this.inputs.targetIndex + 1}`;
745
+ let windowMaterial = undefined;
746
+ if (polyData.walls[targetWallIndex] && polyData.walls[targetWallIndex].windows[this.inputs.targetIndex]) {
747
+ const currWindow = polyData.walls[targetWallIndex].windows[this.inputs.targetIndex];
748
+ if (currWindow.name)
749
+ windowName = currWindow.name;
750
+ if (currWindow.material)
751
+ windowMaterial = currWindow.material;
752
+ }
753
+ const windowData = {
754
+ path: this.inputs.path,
755
+ name: windowName,
756
+ index: this.inputs.targetIndex,
757
+ options: {
758
+ color: this.inputs.windowColor,
759
+ is_visible: true,
760
+ is_deleted: false
761
+ },
762
+ area: windowDimension.area,
763
+ edges: windowDataArray,
764
+ material: windowMaterial
765
+ };
766
+ try {
767
+ const window = [windowData];
768
+ this.renderWindows(window);
769
+ }
770
+ catch (error) {
771
+ log.error("Error rendering windows ", error);
772
+ }
773
+ polyData.walls[targetWallIndex].windows[this.inputs.targetIndex] = (windowData);
774
+ partitionData = polyData;
623
775
  }
624
- };
776
+ }
625
777
  dispatchSpaceEvent(SPACE_EVENTS.PARTITION_UPDATED, partitionData);
626
778
  console.log("Polygon mesh ", this.groupedMesh, partitionData);
627
779
  console.log("partitionData ", partitionData);
@@ -652,9 +804,6 @@ export class BufferGeometry {
652
804
  this.mesh.geometry.dispose();
653
805
  }
654
806
  ;
655
- renderWall() {
656
- }
657
- ;
658
807
  createLabelCanvas(text) {
659
808
  const canvas = document.createElement('canvas');
660
809
  const wallContext = canvas.getContext('2d');
@@ -668,7 +817,16 @@ export class BufferGeometry {
668
817
  return canvas;
669
818
  }
670
819
  ;
671
- renderWalls(WALL_HEIGHT) {
820
+ renderWindow(WALL_HEIGHT) {
821
+ // const THREE = this.context.three
822
+ // const windowData:WindowPolyData = this.inputs.windowData
823
+ // const windowGeometry = new THREE.BoxGeometry(windowData.width, windowData.height, windowData.depth)
824
+ // const windowMesh = new THREE.Mesh(windowGeometry, new THREE.MeshBasicMaterial({color: windowData.color}))
825
+ // windowMesh.position.set(windowData.position.x, windowData.position.y, windowData.position.z)
826
+ // windowMesh.name = windowData.uuid != '' ? `${windowData.uuid}_window` : 'window'
827
+ // this.groupedMesh.add(windowMesh)
828
+ }
829
+ renderWalls(WALL_HEIGHT, metadata) {
672
830
  const THREE = this.context.three;
673
831
  let floorDataArray = [];
674
832
  let wallDataArray = [];
@@ -708,7 +866,11 @@ export class BufferGeometry {
708
866
  const wallLength = new THREE.Vector3(endPoint.x - startPoint.x, endPoint.y - startPoint.y, endPoint.z - startPoint.z).length();
709
867
  const wallArea = wallLength * WALL_HEIGHT;
710
868
  // Create wall label
711
- const wallCanvas = this.createLabelCanvas(`${i18n.t('Wall')} ${i + 1} (${i18n.t('Area')}): ${wallArea.toFixed(2)}m²`);
869
+ let wallMaterial;
870
+ if (metadata && metadata.walls[i] && metadata.walls[i].material)
871
+ wallMaterial = metadata.walls[i].material;
872
+ const wallCanvas = this.createLabelCanvas(`${i18n.t('Wall')} ${i + 1}${wallMaterial ? '_' + wallMaterial : ' (' + i18n.t('Area') + ')'}: ${wallArea.toFixed(2)}m²`);
873
+ // const wallCanvas = this.createLabelCanvas(`${i18n.t('Wall')} ${i+1}_Wood: ${wallArea.toFixed(2)}m²`)
712
874
  // Create floor edge label
713
875
  const floorCanvas = this.createLabelCanvas(`${i18n.t('Wall')} ${i + 1} (${i18n.t('Edge')}): ${wallLength.toFixed(2)}m`);
714
876
  const midPoint = {
@@ -817,7 +979,8 @@ export class BufferGeometry {
817
979
  width: wallDimensions.width,
818
980
  perimeter: wallDimensions.perimeter,
819
981
  wall_height: WALL_HEIGHT,
820
- edges: wallEdges
982
+ edges: wallEdges,
983
+ material: wallMaterial ? wallMaterial : undefined
821
984
  };
822
985
  if (polyData && polyData.walls[i].uuid) {
823
986
  wallData.uuid = polyData.walls[i].uuid;
@@ -828,6 +991,117 @@ export class BufferGeometry {
828
991
  return [floorDataArray, wallDataArray];
829
992
  }
830
993
  ;
994
+ renderWindows(windows) {
995
+ const THREE = this.context.three;
996
+ log.info("renderWindows ", windows);
997
+ if (windows && windows.length > 0) {
998
+ for (let i = 0; i < windows.length; i++) {
999
+ const path3D = windows[i].path;
1000
+ if (!path3D || path3D.length < 3) {
1001
+ continue;
1002
+ }
1003
+ const p0 = new THREE.Vector3(path3D[0].x, path3D[0].y, path3D[0].z);
1004
+ const p1 = new THREE.Vector3(path3D[1].x, path3D[1].y, path3D[1].z);
1005
+ const p2 = new THREE.Vector3(path3D[2].x, path3D[2].y, path3D[2].z);
1006
+ const v1 = new THREE.Vector3().subVectors(p1, p0);
1007
+ const v2 = new THREE.Vector3().subVectors(p2, p0);
1008
+ const normal = new THREE.Vector3().crossVectors(v1, v2);
1009
+ const normalLen = normal.length();
1010
+ if (normalLen === 0) {
1011
+ continue;
1012
+ }
1013
+ normal.divideScalar(normalLen);
1014
+ const uAxis = v1.clone().normalize();
1015
+ const vAxis = new THREE.Vector3().crossVectors(normal, uAxis).normalize();
1016
+ const shapePoints2D = [];
1017
+ for (let j = 0; j < path3D.length; j++) {
1018
+ const pj = new THREE.Vector3(path3D[j].x, path3D[j].y, path3D[j].z);
1019
+ const rel = new THREE.Vector3().subVectors(pj, p0);
1020
+ const x = rel.dot(uAxis);
1021
+ const y = rel.dot(vAxis);
1022
+ shapePoints2D.push(new THREE.Vector2(x, y));
1023
+ }
1024
+ const windowShape = new THREE.Shape();
1025
+ windowShape.moveTo(shapePoints2D[0].x, shapePoints2D[0].y);
1026
+ for (let j = 1; j < shapePoints2D.length; j++) {
1027
+ windowShape.lineTo(shapePoints2D[j].x, shapePoints2D[j].y);
1028
+ }
1029
+ windowShape.lineTo(shapePoints2D[0].x, shapePoints2D[0].y);
1030
+ const windowGeometry = new THREE.ShapeGeometry(windowShape);
1031
+ const basisMatrix = new THREE.Matrix4().makeBasis(uAxis, vAxis, normal);
1032
+ const translation = new THREE.Matrix4().makeTranslation(p0.x, p0.y, p0.z);
1033
+ const worldMatrix = new THREE.Matrix4().multiplyMatrices(translation, basisMatrix);
1034
+ // Apply a small offset along the wall normal to avoid z-fighting with coplanar planes
1035
+ const offset = -0.05;
1036
+ const offsetMatrix = new THREE.Matrix4().makeTranslation(normal.x * offset, normal.y * offset, normal.z * offset);
1037
+ const finalMatrix = new THREE.Matrix4().multiplyMatrices(offsetMatrix, worldMatrix);
1038
+ windowGeometry.applyMatrix4(finalMatrix);
1039
+ windowGeometry.computeVertexNormals();
1040
+ const windowMesh = new THREE.Mesh(windowGeometry, new THREE.MeshBasicMaterial({
1041
+ color: this.inputs.windowColor,
1042
+ side: THREE.DoubleSide,
1043
+ opacity: 1,
1044
+ transparent: true,
1045
+ depthWrite: false
1046
+ }));
1047
+ windowMesh.name = windows[i].uuid ? `${windows[i].uuid}_window` : `window-${i}`;
1048
+ // Add window center label if material is defined
1049
+ if (windows[i].material) {
1050
+ try {
1051
+ let twiceSignedArea = 0;
1052
+ let centroidU = 0;
1053
+ let centroidV = 0;
1054
+ for (let j = 0; j < shapePoints2D.length; j++) {
1055
+ const p1 = shapePoints2D[j];
1056
+ const p2 = shapePoints2D[(j + 1) % shapePoints2D.length];
1057
+ const cross = p1.x * p2.y - p2.x * p1.y;
1058
+ twiceSignedArea += cross;
1059
+ centroidU += (p1.x + p2.x) * cross;
1060
+ centroidV += (p1.y + p2.y) * cross;
1061
+ }
1062
+ let centerU;
1063
+ let centerV;
1064
+ if (Math.abs(twiceSignedArea) > 1e-8) {
1065
+ const polygonArea = twiceSignedArea * 0.5;
1066
+ centerU = centroidU / (6 * polygonArea);
1067
+ centerV = centroidV / (6 * polygonArea);
1068
+ }
1069
+ else {
1070
+ const sum = shapePoints2D.reduce((acc, p) => ({ x: acc.x + p.x, y: acc.y + p.y }), { x: 0, y: 0 });
1071
+ centerU = sum.x / shapePoints2D.length;
1072
+ centerV = sum.y / shapePoints2D.length;
1073
+ }
1074
+ // Create window label canvas
1075
+ const windowLabel = windows[i].name ? `${windows[i].name}_${windows[i].material}` : `Window ${i}_${windows[i].material}`;
1076
+ const windowCanvas = this.createLabelCanvas(windowLabel);
1077
+ const windowArea = Math.abs(twiceSignedArea * 0.5);
1078
+ const labelWidth = Math.max(0.5, Math.min(2, Math.sqrt(Math.max(0.0001, windowArea))));
1079
+ const labelHeight = labelWidth / 4;
1080
+ const windowLabelGeometry = new THREE.PlaneGeometry(labelWidth, labelHeight);
1081
+ const windowLabelMaterial = new THREE.MeshBasicMaterial({
1082
+ map: new THREE.CanvasTexture(windowCanvas),
1083
+ transparent: true,
1084
+ side: THREE.DoubleSide,
1085
+ });
1086
+ const windowCenterLabel = new THREE.Mesh(windowLabelGeometry, windowLabelMaterial);
1087
+ const center3D = new THREE.Vector3()
1088
+ .addScaledVector(uAxis, centerU)
1089
+ .addScaledVector(vAxis, centerV)
1090
+ .add(p0);
1091
+ const labelOffset = 0.01;
1092
+ windowCenterLabel.position.copy(center3D).addScaledVector(normal.clone().negate(), labelOffset);
1093
+ windowCenterLabel.lookAt(windowCenterLabel.position.clone().add(normal.clone().negate()));
1094
+ windowCenterLabel.name = windows[i].uuid ? `${windows[i].uuid}_windowLabel` : `windowLabel-${i}`;
1095
+ this.groupedMesh.add(windowCenterLabel);
1096
+ }
1097
+ catch (e) {
1098
+ log.error('Error creating window center label', e);
1099
+ }
1100
+ }
1101
+ this.groupedMesh.add(windowMesh);
1102
+ }
1103
+ }
1104
+ }
831
1105
  }
832
1106
  export const bufferGeometryType = 'bufferGeometry';
833
1107
  export const bufferGeometry = function (mpSdk) {
@@ -1,10 +1,11 @@
1
1
  export declare class PolygonCalculator {
2
2
  /**
3
- * Calculates both area and perimeter of a polygon floor from 3D coordinates
3
+ * Calculates both area and perimeter of a polygon from 3D coordinates
4
4
  * @param {Array<{x: number, y: number, z: number}>} coordinates - Array of 3D points
5
+ * @param {boolean} useVerticalPlane - If true, uses XY plane for vertical polygons. If false, uses XZ plane for horizontal polygons
5
6
  * @returns {{area: number, perimeter: number}} Object containing area and perimeter
6
7
  */
7
- static calculatePolygonProperties(coordinates: any): {
8
+ static calculatePolygonProperties(coordinates: any, useVerticalPlane?: boolean): {
8
9
  area: number;
9
10
  perimeter: number;
10
11
  };
@@ -49,11 +49,12 @@
49
49
  // }
50
50
  export class PolygonCalculator {
51
51
  /**
52
- * Calculates both area and perimeter of a polygon floor from 3D coordinates
52
+ * Calculates both area and perimeter of a polygon from 3D coordinates
53
53
  * @param {Array<{x: number, y: number, z: number}>} coordinates - Array of 3D points
54
+ * @param {boolean} useVerticalPlane - If true, uses XY plane for vertical polygons. If false, uses XZ plane for horizontal polygons
54
55
  * @returns {{area: number, perimeter: number}} Object containing area and perimeter
55
56
  */
56
- static calculatePolygonProperties(coordinates) {
57
+ static calculatePolygonProperties(coordinates, useVerticalPlane = false) {
57
58
  if (!Array.isArray(coordinates) || coordinates.length < 3) {
58
59
  throw new Error('At least 3 coordinates are required to form a polygon');
59
60
  }
@@ -62,19 +63,32 @@ export class PolygonCalculator {
62
63
  for (let i = 0; i < coordinates.length; i++) {
63
64
  const point1 = coordinates[i];
64
65
  const point2 = coordinates[(i + 1) % coordinates.length];
65
- // Calculate distance between two points in XZ plane (floor plane)
66
- const distance = Math.sqrt(Math.pow(point2.x - point1.x, 2) +
67
- Math.pow(point2.z - point1.z, 2) // Using Z instead of Y for floor plane
68
- );
66
+ let distance;
67
+ if (useVerticalPlane) {
68
+ // Calculate distance between two points in XY plane (vertical plane for windows)
69
+ distance = Math.sqrt(Math.pow(point2.x - point1.x, 2) +
70
+ Math.pow(point2.y - point1.y, 2));
71
+ }
72
+ else {
73
+ // Calculate distance between two points in XZ plane (floor plane)
74
+ distance = Math.sqrt(Math.pow(point2.x - point1.x, 2) +
75
+ Math.pow(point2.z - point1.z, 2));
76
+ }
69
77
  perimeter += distance;
70
78
  }
71
- // Calculate area using the Shoelace formula on XZ plane
79
+ // Calculate area using the Shoelace formula
72
80
  let area = 0;
73
81
  for (let i = 0; i < coordinates.length; i++) {
74
82
  const point1 = coordinates[i];
75
83
  const point2 = coordinates[(i + 1) % coordinates.length];
76
- // Use X and Z coordinates for area calculation since it's a floor
77
- area += (point1.x * point2.z - point2.x * point1.z);
84
+ if (useVerticalPlane) {
85
+ // Use X and Y coordinates for area calculation (vertical plane for windows)
86
+ area += (point1.x * point2.y - point2.x * point1.y);
87
+ }
88
+ else {
89
+ // Use X and Z coordinates for area calculation (horizontal plane for floors)
90
+ area += (point1.x * point2.z - point2.x * point1.z);
91
+ }
78
92
  }
79
93
  area = Math.abs(area) / 2;
80
94
  return {
package/lib/types.d.ts CHANGED
@@ -261,6 +261,12 @@ export interface IObjectData {
261
261
  node: Scene.INode;
262
262
  type?: string;
263
263
  iframeId?: string;
264
+ children?: {
265
+ component?: Scene.IComponent;
266
+ node?: Scene.INode;
267
+ type?: string;
268
+ index?: number;
269
+ }[];
264
270
  }
265
271
  export interface IObjectFilters {
266
272
  type?: string | undefined;
@@ -452,6 +458,9 @@ export interface ComponentOptions {
452
458
  floorLevel?: number | undefined;
453
459
  lineComponentType?: string | undefined;
454
460
  id?: number | undefined;
461
+ drawingMode?: string | undefined;
462
+ targetIndex?: number | undefined;
463
+ targetUUID?: string | undefined;
455
464
  }
456
465
  export interface VectorCoords {
457
466
  object_position: Vector3;
@@ -725,6 +734,7 @@ export declare enum SPACE_EVENTS {
725
734
  PARTITION_CLICKED = "PARTITION_CLICKED",
726
735
  PARTITION_DISPOSED = "PARTITION_DISPOSED",
727
736
  PARTITION_RENDERED = "PARTITION_RENDERED",
737
+ PARTITION_COLLIDER_CLICKED = "PARTITION_COLLIDER_CLICKED",
728
738
  DRAW_HISTORY = "DRAW_HISTORY",
729
739
  BASEPOINT_UPDATED = "BASEPOINT_UPDATED",
730
740
  MINIMAP_CHANGED = "MINIMAP_CHANGED",
@@ -1084,6 +1094,7 @@ export interface FloorPolyData {
1084
1094
  perimeter: number | undefined;
1085
1095
  floor_level: number;
1086
1096
  edges: Array<EdgePolyData>;
1097
+ material?: string | undefined;
1087
1098
  }
1088
1099
  export interface WallPolyData {
1089
1100
  uuid?: string | undefined;
@@ -1095,11 +1106,29 @@ export interface WallPolyData {
1095
1106
  is_visible?: boolean | undefined;
1096
1107
  is_deleted?: boolean | undefined;
1097
1108
  };
1109
+ material?: string | undefined;
1098
1110
  area: number;
1099
1111
  perimeter: number | undefined;
1100
1112
  width: number | undefined;
1101
1113
  wall_height: number;
1102
1114
  edges: Array<EdgePolyData>;
1115
+ windows?: Array<WindowPolyData>;
1116
+ }
1117
+ export interface WindowPolyData {
1118
+ path: Array<Vector3>;
1119
+ uuid?: string | undefined;
1120
+ name?: string | undefined;
1121
+ index?: number | undefined;
1122
+ options?: {
1123
+ color?: number | undefined;
1124
+ opacity?: number | undefined;
1125
+ is_visible?: boolean | undefined;
1126
+ is_deleted?: boolean | undefined;
1127
+ };
1128
+ area: number;
1129
+ width: number | undefined;
1130
+ edges: Array<EdgePolyData>;
1131
+ material?: string | undefined;
1103
1132
  }
1104
1133
  export interface PolygonData {
1105
1134
  path: Array<Vector3>;
@@ -1113,6 +1142,7 @@ export interface PolygonData {
1113
1142
  wall_height: number;
1114
1143
  totalWallArea?: number;
1115
1144
  totalWallPerimeter?: number;
1145
+ material?: string | undefined;
1116
1146
  }
1117
1147
  export interface PolyWallVisibility {
1118
1148
  index: number;
package/lib/types.js CHANGED
@@ -95,6 +95,7 @@ export var SPACE_EVENTS;
95
95
  SPACE_EVENTS["PARTITION_CLICKED"] = "PARTITION_CLICKED";
96
96
  SPACE_EVENTS["PARTITION_DISPOSED"] = "PARTITION_DISPOSED";
97
97
  SPACE_EVENTS["PARTITION_RENDERED"] = "PARTITION_RENDERED";
98
+ SPACE_EVENTS["PARTITION_COLLIDER_CLICKED"] = "PARTITION_COLLIDER_CLICKED";
98
99
  SPACE_EVENTS["DRAW_HISTORY"] = "DRAW_HISTORY";
99
100
  SPACE_EVENTS["BASEPOINT_UPDATED"] = "BASEPOINT_UPDATED";
100
101
  SPACE_EVENTS["MINIMAP_CHANGED"] = "MINIMAP_CHANGED";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "architwin",
3
- "version": "1.14.8",
3
+ "version": "1.14.9",
4
4
  "description": "ArchiTwin Library for Matterport",
5
5
  "main": "./lib/architwin.js",
6
6
  "types": "./lib/architwin.d.ts",