I have draw one polygon when I click on close the polygon at that I am filling with mesh using shape but sometimes my shape is not covering full of the polygon so how can i fix it?


const createLine = (
point1: any,
point2: any,
closedLoop = false,
points?: any,
polygonIndex?: any,
) => {
const pointsPositions: any = points;

  console.log(pointsPositions, " aaaaaaaaaaaaaaa");
  const distanceLabel = document.createElement("div");
  distanceLabel.className = "measurementLabel";
  distanceLabel.style.fontWeight = "bold";
  distanceLabel.style.color = "#fff";
  distanceLabel.style.borderRadius = "10px";
  distanceLabel.style.padding = "3px 7px";
  distanceLabel.style.background = "rgba(0, 0, 0, 0.60)";
  distanceLabel.style.zIndex = "999999";
  const geometry = new THREE.BufferGeometry();
  const positions = new Float32Array(pointsPositions.length * 3);

  for (let i = 0; i < pointsPositions.length; i++) {
    positions[i * 3] = pointsPositions[i].x;
    positions[i * 3 + 1] = pointsPositions[i].y;
    positions[i * 3 + 2] = pointsPositions[i].z;
  }

  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  geometry.setFromPoints(pointsPositions);

  const indexArray = [];

  if (closedLoop && pointsPositions.length > 2) {
    // Close the loop by connecting the last point to the first point
    for (let i = 0; i < pointsPositions.length; i++) {
      indexArray.push(i, (i + 1) % pointsPositions.length);
    }
    geometry.setIndex(indexArray);

    if (!polygonLabelsArray[polygonIndex]) {
      createLabels(polygonIndex, pointsPositions);
    } else {
      // Update labels during editing
      const labels = polygonLabelsArray[polygonIndex];
      labels.areaLabel.innerText = `${t("Area")}: ${calculatePolygonArea(
        pointsPositions,
      ).toFixed(2)} m²`;
      labels.perimeterLabel.innerText = `${t("Perimeter")}: ${calculatePolygonPerimeter(
        pointsPositions,
      ).toFixed(2)} m`;
    }

    const removedShapeMesh = shapeMeshes.splice(polygonIndex, 1)[0];
    scene.remove(removedShapeMesh);
    console.log(pointsPositions, "pointPosition");
    **const shapePositions = new THREE.Shape(pointsPositions);**

** shapePositions.autoClose = true;**

** const geomShape = new THREE.ShapeGeometry(shapePositions);**

** const pos = geomShape.attributes.position;**

** for (let i = 0; i < pos.count; i++) {**
** pos.setZ(i, pointsPositions[i].z);**
** }**
** pos.needsUpdate = true;**
** geomShape.computeVertexNormals();**

** const matShape = new THREE.MeshBasicMaterial({**
** color: 0x22d94a,**
** side: THREE.DoubleSide,**
** transparent: true,**
** opacity: 0.2,**
** depthTest: false,**
** });**
** const shape = new THREE.Mesh(geomShape, matShape);**

** scene.add(shape);**

    shapeMeshes.push(shape);
    shapeMeshes.splice(polygonIndex, 0, shape);
  } else {
    scene.remove(shapeMeshes[polygonIndex]);
    scene.remove(distanceObjects[polygonIndex]);
    scene.remove(polygonLabelsArray[polygonIndex]);

    shapeMeshes.splice(polygonIndex, 1);
    distanceObjects.splice(polygonIndex, 1);
    polygonLabelsArray.splice(polygonIndex, 1);
  }

  const material = new THREE.LineBasicMaterial({
    color: 0x22d94a,
    linewidth: 3,
    depthTest: false,
    transparent: false,
    opacity: 1,
  });

  const line = new THREE.Line(geometry, material);
  line.frustumCulled = false;

  line.userData = { point1, point2 };
  linesGroupRef.current.add(line);
  lines.push(line);

  const distance = point1.distanceTo(point2);
  distanceLabel.innerText = `${distance.toFixed(2)}m`;

  const distanceLabelObject = new CSS2DObject(distanceLabel);
  // Set the position of the label to the midpoint between point1 and point2
  const midPoint = new THREE.Vector3().copy(point1).lerp(point2, 0.25);
  distanceLabelObject.position.copy(midPoint);

  linesGroupRef.current.add(distanceLabelObject);

  render();
};

I have marked the shape please anyone help to resolve above problem

Hard to tell whats going on in your code. I would perhaps suggest stripping the problem down to a simple case in a glitch or a codepen, if nobody else has any ideas.

Okay I will share the code sandbox link here

https://codesandbox.io/p/sandbox/charming-tdd-nkf64x?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522cls9v0l5m0006356map1fkgfh%2522%252C%2522sizes%2522%253A%255B100%252C0%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522cls9v0l5m0002356m06sg3gy6%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522cls9v0l5m0003356m3il2pgut%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522cls9v0l5m0005356m3p0pskeg%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B53.5866534769407%252C46.4133465230593%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522cls9v0l5m0002356m06sg3gy6%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9v0l5l0001356mf94p7pwa%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fpublic%252Findex.html%2522%257D%255D%252C%2522id%2522%253A%2522cls9v0l5m0002356m06sg3gy6%2522%252C%2522activeTabId%2522%253A%2522cls9v0l5l0001356mf94p7pwa%2522%257D%252C%2522cls9v0l5m0005356m3p0pskeg%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9v0l5m0004356myp0rrrwc%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A0%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522cls9v0l5m0005356m3p0pskeg%2522%252C%2522activeTabId%2522%253A%2522cls9v0l5m0004356myp0rrrwc%2522%257D%252C%2522cls9v0l5m0003356m3il2pgut%2522%253A%257B%2522tabs%2522%253A%255B%255D%252C%2522id%2522%253A%2522cls9v0l5m0003356m3il2pgut%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Afalse%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D

above is the public link to the code

and for the closing loop just double click on or you can use escape button as well

Hard to tell… but pretty sure there is a logic bug in how you are collecting the points… It looks like you’re not adding the final click point to the path, and also there is some weird projection happening to the points? (or maybe its clipping inside the object?)

The app looks really cool though!!.. i see where you’re going with it…

yes, it is clipping inside the object file how can I fix it for the surface of the object file?
To close the loop how can I add a closing point if I add then it will create a new point right?

yes, the app has a cool concept for adding measurements will we add the solar panels for the measured area first we have to fix this issue then we are able proceed further

Ok so… check the console… you’re trying to add some arrays of points to the scene:

scene.add(pointLight);
scene.add(polygons);  <--- bad
scene.add(camera);
scene.add(points);  <--- bad
scene.add(outLines); <--- bad


scene.add(textMeshes.current);    <--- Bad
scene.add(dragControls.current); <--- Bad
scene.add(dragRef.current); <--- Bad

Secondly:

the THREE.Shape constructor takes an array of Vector2 not Vector3d points.

I fixed it up a bit… but drawing arbitrary shapes on slopes isn’t really supported… you’ll have to do something clever to determine heights…
The loop works if the points are flat… but on other shapes… they don’t match up to the path equivalent points after shape generation:

You may have to search the list of path points and compare their distances to the original points or something to map them to a height value.

https://codesandbox.io/p/sandbox/charming-tdd-nkf64x?file=%2Fsrc%2FobjViewer.tsx%3A467%2C74&layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522cls9v0l5m0006356map1fkgfh%2522%252C%2522sizes%2522%253A%255B100%252C0%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522cls9v0l5m0002356m06sg3gy6%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522cls9v0l5m0003356m3il2pgut%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522cls9v0l5m0005356m3p0pskeg%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B53.5866534769407%252C46.4133465230593%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522cls9v0l5m0002356m06sg3gy6%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9v0l5l0001356mf94p7pwa%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fpublic%252Findex.html%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522id%2522%253A%2522cls9y18en003x356fn5277paa%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522initialSelections%2522%253A%255B%257B%2522startLineNumber%2522%253A467%252C%2522startColumn%2522%253A74%252C%2522endLineNumber%2522%253A467%252C%2522endColumn%2522%253A74%257D%255D%252C%2522filepath%2522%253A%2522%252Fsrc%252FobjViewer.tsx%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522cls9v0l5m0002356m06sg3gy6%2522%252C%2522activeTabId%2522%253A%2522cls9y18en003x356fn5277paa%2522%257D%252C%2522cls9v0l5m0005356m3p0pskeg%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9v0l5m0004356myp0rrrwc%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A0%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522cls9v0l5m0005356m3p0pskeg%2522%252C%2522activeTabId%2522%253A%2522cls9v0l5m0004356myp0rrrwc%2522%257D%252C%2522cls9v0l5m0003356m3il2pgut%2522%253A%257B%2522tabs%2522%253A%255B%255D%252C%2522id%2522%253A%2522cls9v0l5m0003356m3il2pgut%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Afalse%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D

what you have changed can you let me know because I am not able to figure it out

yes I have removed the above arrays from the scene

1 Like

This is as good as I can get it. I made a lookup table to map the 3d vertices back to nearest 2 vertex, to get the height value. It’s not perfect… but good enough for government work.

https://codesandbox.io/p/sandbox/charming-tdd-nkf64x?file=%2Fsrc%2FobjViewer.tsx%3A467%2C74

I marked my changes with THRAX:

okay thanks let me check

Erg… codepen kinda sux… try this link:

https://codesandbox.io/p/sandbox/charming-tdd-nkf64x?file=%2Fsrc%2FobjViewer.tsx%3A467%2C74&layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522cls9v0l5m0006356map1fkgfh%2522%252C%2522sizes%2522%253A%255B100%252C0%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522cls9v0l5m0002356m06sg3gy6%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522cls9v0l5m0003356m3il2pgut%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522cls9v0l5m0005356m3p0pskeg%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B53.5866534769407%252C46.4133465230593%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522cls9v0l5m0002356m06sg3gy6%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9v0l5l0001356mf94p7pwa%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fpublic%252Findex.html%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522id%2522%253A%2522cls9y18en003x356fn5277paa%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522initialSelections%2522%253A%255B%257B%2522startLineNumber%2522%253A467%252C%2522startColumn%2522%253A74%252C%2522endLineNumber%2522%253A467%252C%2522endColumn%2522%253A74%257D%255D%252C%2522filepath%2522%253A%2522%252Fsrc%252FobjViewer.tsx%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522cls9v0l5m0002356m06sg3gy6%2522%252C%2522activeTabId%2522%253A%2522cls9y18en003x356fn5277paa%2522%257D%252C%2522cls9v0l5m0005356m3p0pskeg%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cls9v0l5m0004356myp0rrrwc%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A0%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522cls9v0l5m0005356m3p0pskeg%2522%252C%2522activeTabId%2522%253A%2522cls9v0l5m0004356myp0rrrwc%2522%257D%252C%2522cls9v0l5m0003356m3il2pgut%2522%253A%257B%2522tabs%2522%253A%255B%255D%252C%2522id%2522%253A%2522cls9v0l5m0003356m3il2pgut%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Afalse%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D


not able to find any changes of yours

Codesandbox is being lame.

paste this into your objViewer.ts:

/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/restrict-plus-operands */
import { MutableRefObject } from "react";
import * as THREE from "three";
import { DragControls } from "three/examples/jsm/controls/DragControls";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import {
  CSS2DObject,
  CSS2DRenderer,
} from "three/examples/jsm/renderers/CSS2DRenderer";

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type]
export function NewObjViewer(
  el: any,
  objUrl: any,
  mtlUrl: any,
  linesGroupRef: MutableRefObject<THREE.Group<THREE.Object3DEventMap>>,
  textMeshes: { current: any[] },
  dragRef: any,
  dragControls: any,
  element?: any,
  projectPoints?: any,
  popupRef?: any,
  t?: any,
  fetchData?: any,
  BackendApis?: any
) {
  let scene: any, renderer: any;
  let controls: any;
  let drawingLine = false;
  let maxWidthMode = false;
  const measurementLabels: any = {};
  const lineId = 0;
  let line: any;
  init();

  function init() {
    let camera: any;
    let object: any;
    const raycaster = new THREE.Raycaster();
    const labelRenderer = new CSS2DRenderer();
    let isShiftPressed = false;
    const editModeRadio = document.createElement("input");
    const createModeRadio = document.createElement("input");

    const popupContainer = document.createElement("div");
    popupContainer.className = "popup-container";
    popupContainer.style.position = "absolute";
    popupContainer.style.bottom = "5px";
    popupContainer.style.right = "10px";
    popupContainer.style.backgroundColor = "#fff";
    popupContainer.style.border = "1px solid #ccc";
    popupContainer.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.5)";
    popupContainer.style.borderRadius = "10px";
    popupContainer.style.width = "300px"; // Set a fixed width for better aesthetics
    popupContainer.style.display = "flex";
    popupContainer.style.flexDirection = "column";
    popupContainer.style.alignItems = "center"; // Center content horizontally
    popupContainer.style.justifyContent = "center";
    popupContainer.style.gap = "5px";
    popupContainer.style.zIndex = "999";
    popupContainer.style.maxHeight = "300px";
    popupContainer.style.overflowY = "auto";

    // Create the popup content
    const popupContent = document.createElement("div");
    popupContent.className = "popup-content";
    popupContent.style.display = "flex";
    popupContent.style.flexDirection = "column";
    popupContent.style.alignItems = "center";
    popupContent.style.padding = "0 0 10px 0";

    popupContent.style.gap = "10px";
    popupContent.style.width = "100%";
    popupContent.style.position = "relative";

    // Create the heading

    const dragButton = document.createElement("img");
    dragButton.src = "DragIcon";
    dragButton.alt = "DragIcon";
    dragButton.id = "DragIcon";
    dragButton.style.cssText =
      "height: 25px; width: 25px; margin: 5px; cursor: pointer; position: relative;";

    const heading = document.createElement("div");
    heading.style.display = "flex";
    heading.style.alignItems = "center";
    heading.style.gap = "10px";
    heading.style.position = "relative";

    const header = document.createElement("h2");
    header.innerText = "Measurement Details";
    header.style.color = "#333"; // header color
    header.style.fontSize = "16px";
    header.style.fontWeight = "bold";

    const closeButton = document.createElement("img");
    closeButton.src = "CloseIcon";
    closeButton.alt = "CloseIcon";
    closeButton.id = "CloseIcon";
    closeButton.style.cssText =
      "height: 25px; width: 25px; margin: 5px; cursor: pointer; position: absolute; right:5px; top:10px;";

    popupContainer.appendChild(dragButton);
    popupContainer.appendChild(header);
    popupContainer.appendChild(closeButton);

    closeButton.addEventListener("click", () => {
      el.removeChild(popupContainer);
    });

    let points: any = [];
    let outLines: any = [];
    let lines: any = [];
    const distanceObjects: any = [];

    const polygons: any = [];

    // eslint-disable-next-line prefer-const
    camera = new THREE.PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      0.1,
      20
    );
    // camera.position.set(0, 0, 1.5);
    // eslint-disable-next-line prefer-const
    let mode = "";

    scene = new THREE.Scene();

    const ambientLight = new THREE.AmbientLight(0xffffff);
    scene.add(ambientLight);

    const pointLight = new THREE.PointLight(0xffffff, 15);
    pointLight.position.z = 3;
    scene.add(pointLight);
    //scene.add(polygons); //THRAX:
    scene.add(camera);
    //scene.add(points); //THRAX:
    //scene.add(outLines); //THRAX:
    scene.add(linesGroupRef.current);
    //scene.add(textMeshes.current); //THRAX:
    //scene.add(dragControls.current); //THRAX:
    //scene.add(dragRef.current); //THRAX:

    const handleDragStart: any = () => {
      controls.enabled = false; // Disable OrbitControls when drag starts
    };

    const handleDragEnd = () => {
      controls.enabled = true; // Enable OrbitControls when drag ends
      updateLineGeometry();
      calculateAndShowPopup(polygons);
    };

    const calculateDistance = (
      point1: { distanceTo: (arg0: any) => any },
      point2: any
    ) => {
      return point1.distanceTo(point2);
    };

    const calculatePolygonArea = (points: any) => {
      // Calculate the area of the polygon formed by the points using the shoelace formula
      let area = 0;
      const n = points.length;

      for (let i = 0; i < n; i++) {
        const j = (i + 1) % n;
        area += points[i].x * points[j].y;
        area -= points[j].x * points[i].y;
      }

      area = Math.abs(area) / 2.0;
      return area;
    };

    const calculatePolygonPerimeter = (points: any) => {
      // Calculate the perimeter of the polygon formed by the points
      let perimeter = 0;
      const n = points.length;

      for (let i = 0; i < n; i++) {
        const j = (i + 1) % n;

        perimeter += calculateDistance(points[i], points[j]);
      }

      return perimeter;
    };

    const onDocumentMouseMove = (event: any) => {
      event.preventDefault();

      if (drawingLine) {
        const intersects: any = getIntersections(event);

        if (intersects.length > 0) {
          const positions = line.geometry.attributes.position.array;
          const v0 = new THREE.Vector3(
            positions[0],
            positions[1],
            positions[2]
          );
          const v1 = new THREE.Vector3(
            intersects[0].point.x,
            intersects[0].point.y,
            intersects[0].point.z
          );
          positions[3] = intersects[0].point.x;
          positions[4] = intersects[0].point.y;
          positions[5] = intersects[0].point.z;
          line.geometry.attributes.position.needsUpdate = true;
          const distance = v0.distanceTo(v1);
          measurementLabels[lineId].element.innerText =
            distance.toFixed(2) + "m";
          measurementLabels[lineId].position.lerpVectors(v0, v1, 0.5);
        }
        return;
      }

      if (mode === "Edit mode" && dragControls.current) {
        dragControls.current.addEventListener("dragstart", handleDragStart);
        dragControls.current.addEventListener("dragend", handleDragEnd);
        dragControls.current.addEventListener("drag", updateLineGeometry);
      }
    };

    const onDocumentMouseUp = () => {
      if (mode === "Edit mode" && dragControls.current) {
        dragControls.current.removeEventListener("dragstart", handleDragStart);
        dragControls.current.removeEventListener("dragend", handleDragEnd);
        dragControls.current.removeEventListener("drag", updateLineGeometry);
      }
    };

    function getIntersections(event: any) {
      const rect = el.getBoundingClientRect();

      // Calculate normalized device coordinates (NDC)
      const x = ((event.clientX - rect.left) / el.clientWidth) * 2 - 1;
      const y = -((event.clientY - rect.top) / el.clientHeight) * 2 + 1;

      // Check if the mouse coordinates are within the bounds of the 3D viewer

      const mouse = new THREE.Vector2(x, y);

      raycaster.setFromCamera(mouse, camera);
      // Find intersections with the mesh
      const intersects = raycaster.intersectObjects(scene.children, true);

      return intersects;
    }

    const updateLineGeometry = () => {
      linesGroupRef.current.clear();

      lines.splice(0, lines.length);
      lines = [];
      if (polygons.length > 0) {
        polygons.forEach((el: any, index: number) => {
          const points1 = el.map((point: { position: any }) => point.position);

          if (points1.length > 1) {
            for (let i = 0; i < points1.length; i++) {
              createLine(
                points1[i],
                points1[(i + 1) % points1.length],
                true,
                points1,
                index
              );
            }
          }
        });
      }
      if (points.length) {
        const points1 = points.map(
          (point: { position: any }) => point.position
        );

        if (points1.length > 1) {
          for (let i = 0; i < points1.length; i++) {
            createLine(
              points1[i],
              points1[(i + 1) % points1.length],
              false,
              points1,
              polygons.length
            );
          }
        }
      }

      render();
    };

    const handleUndo = () => {
      drawingLine = false;
      scene.remove(measurementLabels[lineId]);
      if (line) {
        scene.remove(line);
      }
      if (points.length > 0) {
        const removedPoint = points.pop(); // Remove the last point from the points array
        scene.remove(removedPoint); // Remove the point from the scene
        updateLineGeometry(); // Update the line geometry after removing the point
      }
    };

    const saveFile = async (dataUrl: any) => {
      console.log(dataUrl, "dataUrl");
      const formData = new FormData();
      formData.append("type", "savedProject");

      // Decode Base64 to binary

      // eslint-disable-next-line @typescript-eslint/no-floating-promises
      fetch(dataUrl)
        .then(async (res) => await res.blob())
        .then(async (blob) => {
          const file = new File([blob], "model.png ", { type: "image/png" });
          formData.append("file_upload", file);

          try {
            await fetchData({
              ...BackendApis.uploadImages,
              data: formData,
              headers: {
                "Content-Type": "multipart/form-data;",
              },
            });
          } catch (error) {
            console.log(error);
          }
        });
    };
    const strDownloadMime = "image/octet-stream";

    function handleScreenShot() {
      let imgData;

      try {
        const strMime = "image/png";
        imgData = renderer.domElement.toDataURL(strMime);

        saveFile(imgData);
      } catch (e) {
        console.log(e);
      }
    }

    const findMiddlePoint = (points: any[]) => {
      if (points.length === 0) {
        console.error("No points provided.");
        return null;
      }

      // Calculate the average x, y, and z coordinates
      const sum = points.reduce(
        (acc, point) => ({
          x: acc.x + point.x,
          y: acc.y + point.y,
          z: acc.z + point.z,
        }),
        { x: 0, y: 0, z: 0 }
      );

      const average = {
        x: sum.x / points.length,
        y: sum.y / points.length,
        z: sum.z / points.length,
      };

      return average;
    };

    const shapeMeshes: any[] = [];
    const polygonLabelsArray: any = []; // Array to store labels for each polygon

    const createLabels = (polygonIndex: number, pointsPositions: any) => {
      const distanceLabel = document.createElement("div");
      distanceLabel.className = "measurementLabel";
      distanceLabel.style.fontWeight = "bold";
      distanceLabel.style.color = "#fff";
      distanceLabel.style.borderRadius = "10px";
      distanceLabel.style.padding = "3px 7px";
      distanceLabel.style.background = "rgba(0, 0, 0, 0.60)";
      distanceLabel.style.zIndex = "999999";

      const areaLabel = document.createElement("div");
      areaLabel.className = "measurementLabel";
      areaLabel.innerText = `${"Area"}: ${calculatePolygonArea(
        pointsPositions
      ).toFixed(2)} m²`;

      const perimeterLabel = document.createElement("div");
      perimeterLabel.className = "measurementLabel";
      perimeterLabel.style.fontWeight = "bold";
      perimeterLabel.innerText = `${"Perimeter"}: ${calculatePolygonPerimeter(
        pointsPositions
      ).toFixed(2)} m`;

      distanceLabel.appendChild(areaLabel);
      distanceLabel.appendChild(perimeterLabel);

      const middlePoint: any = findMiddlePoint(pointsPositions);
      const distanceLabelObject = new CSS2DObject(distanceLabel);
      distanceLabelObject.position.copy(middlePoint);
      distanceObjects.push(distanceLabelObject);
      // scene.add(distanceLabelObject);

      // Ensure there's an entry for the polygon in the array
      if (!polygonLabelsArray[polygonIndex]) {
        polygonLabelsArray[polygonIndex] = {};
      }

      // Store labels for the polygon in the array
      polygonLabelsArray[polygonIndex] = {
        areaLabel,
        perimeterLabel,
        distanceLabelObject,
      };
    };

    const createLine = (
      point1: any,
      point2: any,
      closedLoop = false,
      points?: any,
      polygonIndex?: any
    ) => {
      const pointsPositions: any = points;

      console.log(pointsPositions, " aaaaaaaaaaaaaaa");
      const distanceLabel = document.createElement("div");
      distanceLabel.className = "measurementLabel";
      distanceLabel.style.fontWeight = "bold";
      distanceLabel.style.color = "#fff";
      distanceLabel.style.borderRadius = "10px";
      distanceLabel.style.padding = "3px 7px";
      distanceLabel.style.background = "rgba(0, 0, 0, 0.60)";
      distanceLabel.style.zIndex = "999999";
      const geometry = new THREE.BufferGeometry();
      const positions = new Float32Array(pointsPositions.length * 3);

      for (let i = 0; i < pointsPositions.length; i++) {
        positions[i * 3] = pointsPositions[i].x;
        positions[i * 3 + 1] = pointsPositions[i].y;
        positions[i * 3 + 2] = pointsPositions[i].z;
      }

      geometry.setAttribute(
        "position",
        new THREE.BufferAttribute(positions, 3)
      );
      geometry.setFromPoints(pointsPositions);

      const indexArray: any = [];

      if (closedLoop && pointsPositions.length > 2) {
        // Close the loop by connecting the last point to the first point
        for (let i = 0; i < pointsPositions.length; i++) {
          indexArray.push(i, (i + 1) % pointsPositions.length);
        }
        geometry.setIndex(indexArray);

        if (!polygonLabelsArray[polygonIndex]) {
          createLabels(polygonIndex, pointsPositions);
        } else {
          // Update labels during editing
          const labels = polygonLabelsArray[polygonIndex];
          labels.areaLabel.innerText = `"Area": ${calculatePolygonArea(
            pointsPositions
          ).toFixed(2)} m²`;
          labels.perimeterLabel.innerText = `
            "Perimeter"
         : ${calculatePolygonPerimeter(pointsPositions).toFixed(2)} m`;
        }

        const removedShapeMesh = shapeMeshes.splice(polygonIndex, 1)[0];
        scene.remove(removedShapeMesh);
        console.log(pointsPositions, "pointPosition");

        let points2d = pointsPositions.map(
          (v3) => new THREE.Vector2(v3.x, v3.z)
        );

        const shapePositions = new THREE.Shape(
          //THRAX: Map these to 2d points for the shape generator....
          points2d
        );

        shapePositions.autoClose = true;

        const geomShape = new THREE.ShapeGeometry(shapePositions);

        const pos = geomShape.attributes.position;

        //Figure out which points in the path map to the points in the geometry...
        let pointMap = [];
        for (let i = 0; i < pos.count; i++) {
          let p = new THREE.Vector2(pos.getX(i), pos.getY(i));
          let nearestDist = Infinity;
          let nearestIndex;
          for (let j = 0; j < points2d.length; j++) {
            //pos.setZ(i, -pointsPositions[pos.count - 1 - i].y);
            let d = points2d[j].distanceTo(p);
            if (d < nearestDist) {
              nearestIndex = j;
              nearestDist = d;
            }
          }
          pointMap.push(nearestIndex);
        }
        console.log(pointMap);
        //THRAX: this hack wont work because these points dont neccesarily have a relationship to pointsPositions anymore..
        // but it works for the flat case
        console.log(pos.array, pointsPositions);
        for (let i = 0; i < pos.count; i++)
          pos.setZ(i, -pointsPositions[pos.count - 1 - i].y);

        //THRAX: Instead use the pointMap we created to find the original height...
        for (let i = 0; i < pos.count; i++)
          pos.setZ(i, -pointsPositions[pointMap[i]].y);

        //THRAX:Instead we probably need to match them back to the original point somehow..

        pos.needsUpdate = true;
        geomShape.computeVertexNormals();

        const matShape = new THREE.MeshBasicMaterial({
          color: 0x22d94a,
          side: THREE.DoubleSide,
          transparent: true,
          opacity: 0.2,
          depthTest: false,
        });
        const shape = new THREE.Mesh(geomShape, matShape);

        //Set the y position of the shape mesh to the height of the first point..
        //shape.position.y = pointsPositions[0].y;

        //Rotate the shape so it lies on the X/Z plane...
        shape.rotation.x = Math.PI * 0.5;

        scene.add(shape);

        shapeMeshes.push(shape);
        shapeMeshes.splice(polygonIndex, 0, shape);
      } else {
        scene.remove(shapeMeshes[polygonIndex]);
        scene.remove(distanceObjects[polygonIndex]);
        scene.remove(polygonLabelsArray[polygonIndex]);

        shapeMeshes.splice(polygonIndex, 1);
        distanceObjects.splice(polygonIndex, 1);
        polygonLabelsArray.splice(polygonIndex, 1);
      }

      const material = new THREE.LineBasicMaterial({
        color: 0x22d94a,
        linewidth: 3,
        depthTest: false,
        transparent: false,
        opacity: 1,
      });

      const line = new THREE.Line(geometry, material);
      line.frustumCulled = false;

      line.userData = { point1, point2 };
      linesGroupRef.current.add(line);
      lines.push(line);

      const distance = point1.distanceTo(point2);
      distanceLabel.innerText = `${distance.toFixed(2)}m`;

      const distanceLabelObject = new CSS2DObject(distanceLabel);
      // Set the position of the label to the midpoint between point1 and point2
      const midPoint = new THREE.Vector3().copy(point1).lerp(point2, 0.25);
      distanceLabelObject.position.copy(midPoint);

      linesGroupRef.current.add(distanceLabelObject);

      render();
    };
    let outlineSphere: any;
    const pointMaterial = new THREE.MeshBasicMaterial({
      color: 0xffffff,
      depthTest: false,
      transparent: true,
      opacity: 1,
    });

    let clickTimer: any;
    const DOUBLE_CLICK_THRESHOLD = 300;

    el.addEventListener("click", onClick);

    function onClick(event: any) {
      clearTimeout(clickTimer);

      clickTimer = setTimeout(() => {
        // Single click logic
        handleMouseDownDocument(event);
      }, DOUBLE_CLICK_THRESHOLD);
    }
    const sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32);
    const suggestedPoint = new THREE.Mesh(sphereGeometry, pointMaterial);

    suggestedPoint.visible = false;

    const handleHover = (event: any) => {
      const rect = el.getBoundingClientRect();
      const x = ((event.clientX - rect.left) / el.clientWidth) * 2 - 1;
      const y = -((event.clientY - rect.top) / el.clientHeight) * 2 + 1;

      const mouse = new THREE.Vector2(x, y);

      raycaster.setFromCamera(mouse, camera);
      const points = polygons.flat(2);
      const intersects = raycaster.intersectObjects(points, true);

      if (intersects.length > 0) {
        if (outlineSphere) {
          scene.remove(outlineSphere);
          outlineSphere = null; // Reset the variable
        }
        const currentHoveredPoint: any = intersects[0].object;
        const outlineMaterial = new THREE.LineBasicMaterial({
          color: 0xffa500,
          linewidth: 2,
          depthTest: false,
        });
        // Create the outline outlineSphere
        const outlineGeometry = new THREE.EdgesGeometry(
          new THREE.SphereGeometry(0.1, 32, 32)
        );
        outlineSphere = new THREE.LineSegments(
          outlineGeometry,
          outlineMaterial
        );
        outlineSphere.position.copy(currentHoveredPoint.position);
        currentHoveredPoint.scale.set(1.4, 1.4, 1.4);
        scene.add(outlineSphere);
        outlineSphere.scale.set(1.7, 1.7, 1.7);
      } else {
        if (outlineSphere) {
          outlineSphere.scale.set(1, 1, 1);
          scene.remove(outlineSphere);
          outlineSphere = null; // Reset the variable
        }
        points.forEach((point: any) => {
          point.scale.set(1, 1, 1); // Reset the size
        });
      }
      const intersectsWithLines = raycaster.intersectObjects(lines, true);

      if (intersectsWithLines?.length > 0) {
        const line: any = intersectsWithLines[0]?.index;
        const actualLine: any = intersectsWithLines[line / 2]?.object;

        if (actualLine?.userData?.point1 && actualLine?.userData?.point2) {
          const center = new THREE.Vector3()
            .addVectors(actualLine.userData.point1, actualLine.userData.point2)
            .multiplyScalar(0.5);

          if (center) {
            suggestedPoint.position.copy(center);

            suggestedPoint.visible = true;
            scene.add(suggestedPoint);
          }
        }
        return;
      }
      scene.remove(suggestedPoint);
    };

    function onDoubleClick() {
      clearTimeout(clickTimer);
      drawingLine = false;
      scene.remove(measurementLabels[lineId]);
      if (line) {
        scene.remove(line);
      }
      if (points.length > 2) {
        polygons.push(points);
        clearPoints();
        updateLineGeometry();
        calculateAndShowPopup(polygons);
        handleModeChange("Edit mode");
        createModeRadio.checked = false;
        editModeRadio.checked = true;
      }
    }
    el.addEventListener("dblclick", onDoubleClick);
    el.addEventListener("mouseout", onMouseOut);

    // Define the mouseout event handler
    function onMouseOut() {
      // Hide the suggested point
      suggestedPoint.visible = false;
    }

    const handleRemoveClick = (clickedPoint: any) => {
      const index = points.indexOf(clickedPoint);
      scene.remove(clickedPoint);
      if (index !== -1) {
        points.splice(index, 1);
      } else {
        polygons.forEach((array: any[]) => {
          const pointIndex = array.indexOf(clickedPoint);
          if (pointIndex !== -1) {
            array.splice(pointIndex, 1);
          }
        });
      }
      updateLineGeometry();
      hidePopup();
    };

    const showPopup = (clientX: any, clientY: any, clickedPoint: any) => {
      if (document.querySelector(".manage_point_popup")) {
        document.body.removeChild(popupRef.current);
      }
      const popup = document.createElement("div");
      popup.style.position = "absolute";
      popup.className = "manage_point_popup";
      popup.style.top = `${clientY}px`;
      popup.style.left = `${clientX}px`;
      popup.style.background = "#FFF";
      popup.style.padding = "10px";
      popup.style.border = "1px solid #ccc";
      popup.style.borderRadius = "5px";
      popup.style.boxShadow = "0px 4px 20px 0px rgba(0, 0, 0, 0.40)";
      popupRef.current = popup;

      const title = document.createElement("span");
      title.textContent = "Point Options";
      popup.appendChild(title);

      const deleteIcon = document.createElement("img");
      deleteIcon.src = "DeleteIcon";
      deleteIcon.alt = "DeleteIcon";
      deleteIcon.id = "DeleteIcon";

      popup.appendChild(deleteIcon);
      deleteIcon.addEventListener("click", () =>
        handleRemoveClick(clickedPoint)
      );

      document.body.appendChild(popup);
    };

    const hidePopup = () => {
      if (popupRef.current) {
        document.body.removeChild(popupRef.current);
        popupRef.current = null;
      }
    };

    const clearPoints = () => {
      points = [];
      outLines = [];
    };

    const handleMouseDownDocument = (event: {
      clientX: number;
      clientY: number;
    }) => {
      drawingLine = false;
      scene.remove(measurementLabels[lineId]);
      if (line) {
        scene.remove(line);
      }

      // const intersect = raycaster.intersectObjects(shapeMeshes, true);

      // if (intersect.length > 0) {
      //   if (document.querySelector(".manage_point_popup")) {
      //     el.removeChild(popupRef.current);
      //   }
      //   const popup = document.createElement("div");
      //   popup.style.position = "absolute";
      //   popup.className = "manage_point_popup";

      //   // Calculate center coordinates of the screen
      //   const centerX = el.clientWidth / 2;
      //   const centerY = el.clientHeight / 2;

      //   // Set the popup position to the center of the screen
      //   popup.style.top = `${centerY}px`;
      //   popup.style.left = `${centerX}px`;

      //   popup.style.background = "#FFF";
      //   popup.style.padding = "10px";
      //   popup.style.border = "1px solid #ccc";
      //   popup.style.borderRadius = "5px";
      //   popup.style.boxShadow = "0px 4px 20px 0px rgba(0, 0, 0, 0.40)";
      //   popup.style.zIndex = "9999";
      //   popup.style.display = "flex";
      //   popup.style.gap = "15px";
      //   popupRef.current = popup;

      //   // Set the second element to be horizontally aligned to the right
      //   const solarPanelContainer = document.createElement("div");
      //   solarPanelContainer.style.display = "flex";
      //   solarPanelContainer.style.flexDirection = "column";
      //   solarPanelContainer.style.justifyContent = "center";
      //   solarPanelContainer.style.alignItems = "center";

      //   const solarPanel = document.createElement("img");
      //   solarPanel.src = BlueSolarPanel;
      //   solarPanel.alt = "SolarPanel";
      //   solarPanel.id = "SolarPanel";
      //   solarPanel.style.marginBottom = "10px";
      //   solarPanel.style.height = "24px";
      //   solarPanel.style.width = "23px";

      //   const solarpanelTitle = document.createElement("div");
      //   solarpanelTitle.textContent = "Add panel";
      //   solarpanelTitle.style.fontFamily = "Nunito";
      //   solarpanelTitle.style.fontSize = "16px";
      //   solarpanelTitle.style.fontWeight = "700";
      //   solarpanelTitle.style.color = "#2D4764";

      //   solarPanelContainer.appendChild(solarPanel);
      //   solarPanelContainer.appendChild(solarpanelTitle);

      //   // Set the second element to be horizontally aligned to the right
      //   const deleteIconContainer = document.createElement("div");
      //   deleteIconContainer.style.marginLeft = "auto";
      //   deleteIconContainer.style.display = "flex";
      //   deleteIconContainer.style.flexDirection = "column";
      //   deleteIconContainer.style.justifyContent = "center";
      //   deleteIconContainer.style.alignItems = "center";

      //   const deleteIcon1 = document.createElement("img");
      //   deleteIcon1.src = DeleteIcon;
      //   deleteIcon1.alt = "DeleteIcon";
      //   deleteIcon1.id = "DeleteIcon";
      //   deleteIcon1.style.marginBottom = "10px";
      //   deleteIcon1.style.height = "24px";
      //   deleteIcon1.style.width = "23px";

      //   const solarpanelDeleteTitle2 = document.createElement("div");
      //   solarpanelDeleteTitle2.textContent = "Remove";
      //   solarpanelDeleteTitle2.style.fontFamily = "Nunito";
      //   solarpanelDeleteTitle2.style.fontSize = "16px";
      //   solarpanelDeleteTitle2.style.fontWeight = "700";
      //   solarpanelDeleteTitle2.style.color = "#2D4764";

      //   deleteIconContainer.appendChild(deleteIcon1);
      //   deleteIconContainer.appendChild(solarpanelDeleteTitle2);

      //   popup.appendChild(solarPanelContainer);
      //   popup.appendChild(deleteIconContainer);

      //   el.appendChild(popup);

      //   // solarPanel.addEventListener("click", addSolarPanel);
      // }

      if (mode === "") return;
      if (mode === "Edit mode") {
        // Enable DragControls when clicking on a point in Edit mode

        const dragablePoints = [...polygons.flat(2), ...points];
        dragControls.current = new DragControls(
          dragablePoints,
          camera,
          renderer.domElement
        );
        return;
      } else {
        dragControls.current = null;
      }

      const intersects: any = getIntersections(event);

      if (intersects?.length > 0) {
        const clickPoint = intersects[0].point;

        const intersect = raycaster.intersectObjects(points, true);

        if (intersect.length > 0) {
          const clickedPoint = intersect[0].object;
          const clickedPointIndex = points.indexOf(clickedPoint);
          const pointsPosition = points.map(
            (point: { position: any }) => point.position
          );
          if (clickedPointIndex === 0 && pointsPosition.length > 2) {
            polygons.push(points);
            clearPoints();

            updateLineGeometry();
            calculateAndShowPopup(polygons);

            return;
          }

          return;
        }

        const intersectsWithLines = raycaster.intersectObjects(lines, true);

        if (intersectsWithLines?.length > 0 && intersect.length === 0) {
          const line: any = intersectsWithLines[0]?.index;
          const actualLine: any = intersectsWithLines[line / 2]?.object;

          if (actualLine?.userData?.point1 && actualLine?.userData?.point2) {
            const center = new THREE.Vector3()
              .addVectors(
                actualLine.userData.point1,
                actualLine.userData.point2
              )
              .multiplyScalar(0.5);

            const allPoints = [...polygons.flat(2), ...points];
            const pointAlreadyExists = allPoints.some((point: any) =>
              point.position.equals(center)
            );

            if (!pointAlreadyExists) {
              if (center) {
                const newPointMaterial = new THREE.MeshBasicMaterial({
                  color: 0xffffff,
                  depthTest: false,
                  transparent: true,
                  opacity: 1,
                });

                const newPointGeometry = new THREE.SphereGeometry(0.1, 32, 32);
                const newPoint = new THREE.Mesh(
                  newPointGeometry,
                  newPointMaterial
                );
                newPoint.position.copy(center);
                scene.add(newPoint);
                const pointPositions = points.map((ele: any) => ele.position);
                if (
                  pointPositions.includes(actualLine?.userData?.point1) &&
                  pointPositions.includes(actualLine?.userData?.point2)
                ) {
                  const indexToInsertAfter = pointPositions.indexOf(
                    actualLine?.userData?.point1
                  ); // Find the index of the element after which you want to insert
                  if (indexToInsertAfter !== -1) {
                    points.splice(indexToInsertAfter + 1, 0, newPoint);
                  }
                } else {
                  polygons.forEach((array: any[]) => {
                    const pointPositions = array.map(
                      (ele: any) => ele.position
                    );

                    const pointIndex = pointPositions.indexOf(
                      actualLine?.userData?.point1
                    );

                    if (pointIndex !== -1) {
                      array.splice(pointIndex + 1, 0, newPoint);
                    }
                  });
                }

                updateLineGeometry();
                calculateAndShowPopup(polygons);

                render();
              }
            }
          }

          return;
        }

        createSpherePointWithStroke(clickPoint, 0.1, 0xffffff);
        if (!drawingLine) {
          // start the line
          const points: any = [];
          points.push(intersects[0].point);
          points.push(intersects[0].point.clone());
          const geometry = new THREE.BufferGeometry().setFromPoints(points);
          line = new THREE.Line(
            geometry,
            new THREE.LineBasicMaterial({
              color: 0x22d94a,
              linewidth: 3,
              depthTest: false,
              transparent: false,
              opacity: 1,
            })
          );
          line.frustumCulled = false;
          scene.add(line);
          const distanceLabel = document.createElement("div");
          distanceLabel.className = "measurementLabel";
          distanceLabel.style.fontWeight = "bold";
          distanceLabel.style.color = "#fff";
          distanceLabel.style.borderRadius = "10px";
          distanceLabel.style.padding = "3px 7px";
          distanceLabel.style.background = "rgba(0, 0, 0, 0.60)";
          distanceLabel.style.zIndex = "999999";
          distanceLabel.innerText = "0.0m";

          const measurementLabel = new CSS2DObject(distanceLabel);
          measurementLabel.position.copy(intersects[0].point);
          measurementLabels[lineId] = measurementLabel;
          scene.add(measurementLabels[lineId]);
          drawingLine = true;
        }
      }
    };

    const handleDeletePoints = (event: {
      clientX: number;
      clientY: number;
    }) => {
      const dragablePoints = [...polygons.flat(2), ...points];

      if (mode === "Create mode" || mode === "Edit mode") return;

      const intersect = raycaster.intersectObjects(dragablePoints, true);

      if (intersect.length > 0) {
        const clickedPoint = intersect[0].object;
        showPopup(event.clientX, event.clientY, clickedPoint);
      }
    };

    el.addEventListener("pointermove", handleHover, false);
    el.addEventListener("click", handleDeletePoints);

    function createSpherePointWithStroke(
      position: THREE.Vector3,
      size: number | undefined,
      mainColor: number
    ) {
      const pointMaterial = new THREE.MeshBasicMaterial({
        color: mainColor,
        depthTest: false,
        transparent: true,
        opacity: 1,
      });

      const sphereGeometry = new THREE.SphereGeometry(size, 32, 32);
      const mainSphere = new THREE.Mesh(sphereGeometry, pointMaterial);
      mainSphere.position.copy(position);
      points.push(mainSphere);
      updateLineGeometry();

      scene.add(mainSphere);
    }

    const toggleFullScreen = () => {
      const element = renderer.domElement;

      if (document.fullscreenElement) {
        // If already in full-screen mode, exit full-screen
        if (document.exitFullscreen) {
          void document.exitFullscreen();
        }
      } else {
        // Request full-screen for the renderer's DOM element
        if (element.requestFullscreen) {
          el.requestFullscreen();
        }
      }
    };

    const handleModeChange = (newMode: string) => {
      mode = newMode;
    };

    const createRadioContainer = () => {
      const radioContainer = document?.createElement("div");
      radioContainer.className = "radio-container";
      radioContainer.style.cssText =
        "position: absolute; top: 10px; left: 50%; transform: translateX(-50%); color:white; border-radius: 20px; background: rgba(0, 0, 0, 0.60); padding: 10px;";

      createModeRadio.type = "radio";
      createModeRadio.name = "modeRadio";
      createModeRadio.value = "Create mode";
      createModeRadio.defaultChecked = true;
      createModeRadio.style.color = "black";
      createModeRadio.style.position = "relative";
      createModeRadio.style.top = "1px";
      createModeRadio.addEventListener("change", () =>
        handleModeChange("Create mode")
      );

      const createModeLabel = document.createElement("label");
      createModeLabel.innerHTML = "Create mode";
      createModeLabel.style.marginRight = "10px";
      createModeLabel.style.marginLeft = "3px";
      createModeLabel.style.color = "white";
      createModeLabel.addEventListener("click", () => {
        handleModeChange("Create mode");
        createModeRadio.checked = true;
        editModeRadio.checked = false;
      });

      const verticalLine = document.createElement("span");
      verticalLine.style.borderLeft = "1px solid white";
      verticalLine.style.height = "20px";
      verticalLine.style.marginLeft = "10px";
      createModeLabel.appendChild(verticalLine);

      editModeRadio.type = "radio";
      editModeRadio.name = "modeRadio";
      editModeRadio.value = "Edit mode";
      editModeRadio.style.color = "black";
      editModeRadio.style.position = "relative";
      editModeRadio.style.top = "1px";
      editModeRadio.addEventListener("change", () =>
        handleModeChange("Edit mode")
      );

      const editModeLabel = document.createElement("label");
      editModeLabel.innerHTML = "Edit mode";
      editModeLabel.style.marginLeft = "3px";
      editModeLabel.style.color = "white";
      editModeLabel.addEventListener("click", () => {
        handleModeChange("Edit mode");
        editModeRadio.checked = true;
        createModeRadio.checked = false;
      });

      radioContainer.appendChild(createModeRadio);
      radioContainer.appendChild(createModeLabel);
      radioContainer.appendChild(editModeRadio);
      radioContainer.appendChild(editModeLabel);

      el.appendChild(radioContainer);

      handleModeChange("Create mode");
    };

    const changeIconColor = (
      createModeButton: any,
      icon: any,
      isResetIcon: boolean
    ) => {
      if (isResetIcon) {
        createModeButton.src = icon;
        createModeButton.classList.remove("activeIcon");
        return;
      }
      createModeButton.className = "activeIcon";
    };

    const resetIcons = (key: any) => {
      panelIcons.forEach((icon) => {
        const iconElement = document.getElementById(icon.key);
        if (iconElement && icon.key !== key) {
          changeIconColor(
            iconElement,
            icon.key === "planet" ? "Planet" : icon.image,
            true
          ); // Assume White is a constant representing the white color
        }
      });
    };

    const handlePaning = (event: any) => {
      if (isShiftPressed && event.button === 0) {
        controls.enablePan = true;
        controls.update();
      }
    };

    const handleClick: any = (key: string, createModeButton: any, e: any) => {
      // Clear existing mode-related elements

      // e.stopPropagation();
      const existingRadioContainer = element?.querySelector(".radio-container");

      resetIcons(key);
      if (existingRadioContainer) {
        mode = "";
        existingRadioContainer.remove();
      }

      if (key === "home") {
        changeIconColor(createModeButton, "HomeYellow", false);
        fitCameraToCenteredObject(camera, object);
      }

      if (key === "light") {
        changeIconColor(createModeButton, "Light", false);
        controls.mouseButtons.LEFT = THREE.MOUSE.PAN;
        controls.update();
      }

      if (key === "planet") {
        changeIconColor(createModeButton, "PlanetYellow", false);
      }
      if (key === "drone") {
        changeIconColor(createModeButton, "DroneYellow", false);
      }

      if (key === "polygon") {
        changeIconColor(createModeButton, "MesureYellow", false);
        const measurementLabels =
          document.querySelectorAll(".measurementLabels");

        measurementLabels.forEach((label: any) => {
          label.style.display = "block";
        });
        createRadioContainer();
      }

      if (key === "solarpanel") {
        changeIconColor(createModeButton, "SolarPanelYellow", false);
        const measurementLabels =
          document.querySelectorAll(".measurementLabels");

        measurementLabels.forEach((label: any) => {
          label.style.display = "none";
        });
      }

      if (key === "FullScreenIcon") {
        toggleFullScreen();
      }

      if (key === "MaxWidth") {
        maxWidthMode = !maxWidthMode;
      }

      if (key === "popup") {
        changeIconColor(createModeButton, "PopupYellow", false);

        el.appendChild(popupContainer);
        calculateAndShowPopup(polygons);
      }

      if (key === "ZoomIn") {
        camera.position.z -= 0.5;
        camera.updateProjectionMatrix();
      }
      if (key === "ZoomOut") {
        camera.position.z += 0.5;
        camera.updateProjectionMatrix();
      }

      if (key !== "polygon") {
        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
        mode === "";
      }

      if (key !== "light") {
        controls.mouseButtons.LEFT = THREE.MOUSE.ROTATE;
        controls.enableRotate = true;
        controls.update();
      }
    };

    function loadModel() {
      // object.position.y = 0;
      // object.position.z = bakeModelImages ? -2.2 : -1;
      // object.scale.setScalar(0.1);

      // Add the object to the scene
      scene.add(object);
      el.addEventListener("mousedown", onClick);
      document.addEventListener("mousemove", onDocumentMouseMove, false);
      document.addEventListener("mouseup", onDocumentMouseUp, false);
      render();
    }

    const manager = new THREE.LoadingManager(loadModel);

    const panelIcons = [
      {
        key: "home",
        image: "Home",
        msg: "Initial View",
        hoverImage: "HomeYellow",
      },

      // {
      //   key: "drone",
      //   image: Drone,
      //   msg: "Coming Soon",
      //   hoverImage: DroneYellow,
      // },

      {
        key: "polygon",
        image: "Polygon",
        msg: "Coming Soon",
        hoverImage: "MesureYellow",
      },
    ];

    const rightPanelIcons = [];

    function onProgress(xhr: {
      lengthComputable: any;
      loaded: number;
      total: number;
    }) {
      if (xhr.lengthComputable) {
        const percentComplete = (xhr.loaded / xhr.total) * 100;
        if (element) {
          // Check if the loader container already exists, and update its content
          let loaderContainer = element.querySelector(".loader-container");
          if (!loaderContainer) {
            loaderContainer = document.createElement("div");
            loaderContainer.className = "loader-container";
            loaderContainer.style.cssText =
              "position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);";
            element.appendChild(loaderContainer);
          }

          // Update the loader content
          loaderContainer.innerHTML = `
            <img src="${"Loader"}" alt="loader" style="height: 25px; width: 25px; margin: 5px; cursor: pointer;">
          `;
          if (percentComplete === 100) {
            // Remove the loader container
            setTimeout(() => {
              loaderContainer.remove();
              if (el) {
                const modeButtonContainer = document.createElement("div");
                modeButtonContainer.className = "left-icons";
                modeButtonContainer.style.cssText =
                  "position: absolute; top: 10px; transform: translateY(70%); left:10px; background: #2d4764; padding: 10px 5px; border-radius: 20px;";

                panelIcons.map((ele) => {
                  const createModeButton = document.createElement("img");
                  createModeButton.src = ele.image;
                  createModeButton.alt = ele.key;
                  createModeButton.id = ele.key;
                  createModeButton.className =
                    ele.key === "planet" ? "activeIcon" : "";

                  createModeButton.style.cssText =
                    "height: 25px; width: 25px; margin: 5px; cursor: pointer;";
                  createModeButton.addEventListener("click", (e) =>
                    handleClick(ele.key, createModeButton, e)
                  );

                  return modeButtonContainer.appendChild(createModeButton);
                });

                el.appendChild(modeButtonContainer);

                const modeButtonContainer2 = document.createElement("div");
                modeButtonContainer2.className = "right-icons";
                modeButtonContainer2.style.cssText =
                  "position: absolute; top: 10px; right:10px; background: #2d4764; padding: 10px 5px; border-radius: 20px;";

                rightPanelIcons.map((ele: any) => {
                  const createModeButton = document.createElement("img");
                  createModeButton.src = ele.image;
                  createModeButton.alt = ele.key;
                  createModeButton.style.cssText =
                    "height: 25px; width: 25px; margin: 10px; cursor: pointer;";
                  createModeButton.addEventListener("click", (e) =>
                    handleClick(ele.key, createModeButton, e)
                  );

                  return modeButtonContainer2.appendChild(createModeButton);
                });

                const undoButton = document.createElement("img");
                undoButton.src = "Undo";
                undoButton.alt = "Undo";
                undoButton.style.cssText =
                  "height: 20px; width: 20px; margin: 10px; cursor: pointer;";
                undoButton.addEventListener("click", handleUndo);

                // modeButtonContainer2.appendChild(undoButton);

                const screenShotButton = document.createElement("img");
                screenShotButton.className = "screenshot";
                screenShotButton.src = "Capture";
                screenShotButton.alt = "Capture";
                screenShotButton.style.cssText =
                  "position: absolute; height: 65px; width: 65px; cursor: pointer; top: 10px; right:70px;  border-radius: 20px;";

                screenShotButton.addEventListener("click", handleScreenShot);

                // el.appendChild(screenShotButton);

                el.appendChild(modeButtonContainer2);

                labelRenderer.setSize(el.clientWidth, el.clientHeight);
                labelRenderer.domElement.style.position = "absolute";
                labelRenderer.domElement.style.top = "0px";
                labelRenderer.domElement.style.pointerEvents = "none";
                labelRenderer.domElement.style.height = `${el.clientHeight}px`;
                labelRenderer.domElement.className = "measurementLabels";

                el.appendChild(labelRenderer.domElement);
              }
            }, 3000);
          }
        }
      }
    }

    function onError() {}

    const calculateAndShowPopup = (polygons: any) => {
      const element = document.querySelector(".popup-content");
      if (element) {
        element.innerHTML = "";
      }
      polygons.map((ele: any, index: any) => {
        const points: any = ele.map(
          (point: { position: any }) => point.position
        );

        const area = calculatePolygonArea(points);
        const perimeter = calculatePolygonPerimeter(points);

        // Create the popup container

        const heading = document.createElement("div");
        heading.style.display = "flex";
        heading.style.alignItems = "center";
        heading.style.gap = "10px";
        heading.style.position = "relative";

        const header = document.createElement("h2");
        header.innerText = `${"Measurement"} ${index + 1}`;
        header.style.color = "#333"; // header color
        header.style.fontSize = "16px";
        header.style.fontWeight = "bold";

        heading.appendChild(header);

        const downIcon = document.createElement("img");
        downIcon.src = "DownIcon";
        downIcon.alt = "DownIcon";
        downIcon.id = "DownIcon";
        downIcon.style.cssText =
          "height: 25px; width: 25px; margin: 5px; cursor: pointer;";

        const upIcon = document.createElement("img");
        upIcon.src = "UpIcon";
        upIcon.alt = "UpIcon";
        upIcon.id = "UpIcon";
        upIcon.style.cssText =
          "height: 25px; width: 25px; margin: 5px; cursor: pointer;";

        const deleteIcon = document.createElement("img");
        deleteIcon.src = "DeleteIcon";
        deleteIcon.alt = "DeleteIcon";
        deleteIcon.id = "DeleteIcon";
        deleteIcon.style.cssText =
          "height: 20px; width: 20px; margin: 5px; cursor: pointer; margin-left:10px";

        heading.appendChild(downIcon);
        heading.appendChild(deleteIcon);

        // Display distances between consecutive points
        const distancesParagraph = document.createElement("p");
        distancesParagraph.innerHTML = `<strong>${"Distances"}:</strong>`;
        distancesParagraph.id = `${index + 1}`;
        points.forEach((point: any, index: any) => {
          const nextIndex = (index + 1) % points.length; // Use modulo to connect last point to the first point
          const distance = calculateDistance(point, points[nextIndex]);
          distancesParagraph.innerHTML += `<br>${"Point"} ${
            index + 1
          } ${"to Point"} ${nextIndex + 1}: ${distance.toFixed(2)} ${"meters"}`;
        });

        const areaParagraph = document.createElement("p");
        areaParagraph.innerHTML = `<strong>${"Area"}:</strong> ${area.toFixed(
          2
        )} ${"square meters"}`;

        const perimeterParagraph = document.createElement("p");
        perimeterParagraph.innerHTML = `<strong>${"Perimeter"}:</strong> ${perimeter.toFixed(
          2
        )} ${"meters"}`;

        // Create the close button
        let measurementsVisible = true;

        const measurementsContainer = document.createElement("div");
        measurementsContainer.style.display = "flex";
        measurementsContainer.style.flexDirection = "column";

        // Append distances, area, and perimeter to the measurements container
        measurementsContainer.appendChild(distancesParagraph);
        measurementsContainer.appendChild(areaParagraph);
        measurementsContainer.appendChild(perimeterParagraph);

        popupContent.appendChild(heading);
        popupContent.appendChild(measurementsContainer);

        popupContainer.appendChild(popupContent);

        deleteIcon.addEventListener("click", () => {
          scene.remove(shapeMeshes[index]);
          scene.remove(distanceObjects[index]);
          scene.remove(polygonLabelsArray[index]);

          polygons[index].forEach((e: any) => {
            scene.remove(e);
          });
          shapeMeshes.splice(index, 1);
          distanceObjects.splice(index, 1);
          polygonLabelsArray.splice(index, 1);
          polygons.splice(index, 1);
          updateLineGeometry();
          calculateAndShowPopup(polygons);
        });

        downIcon.addEventListener("click", (event) => {
          event.stopPropagation();

          // Toggle visibility state
          measurementsVisible = !measurementsVisible;

          // Toggle the display style of the measurements container
          heading.removeChild(downIcon);
          heading.removeChild(deleteIcon);
          heading.appendChild(measurementsVisible ? upIcon : downIcon);
          heading.appendChild(deleteIcon);
          measurementsContainer.style.display = measurementsVisible
            ? "flex"
            : "none";
        });
      });
    };

    // el.appendChild(popupContainer);

    const fitCameraToCenteredObject = (camera: any, object: any) => {
      const boundingBox = new THREE.Box3();

      boundingBox.setFromObject(object);

      const center = boundingBox.getCenter(new THREE.Vector3());
      const size = boundingBox.getSize(new THREE.Vector3());

      const maxSize = Math.max(size.x, size.y, size.z);

      const fov = camera.fov * (Math.PI / 180);
      const cameraZ = Math.abs(maxSize / 2 / Math.tan(fov / 2));

      camera.position.copy(center);
      camera.position.z += cameraZ;

      camera.near = maxSize / 100;
      camera.far = maxSize * 3;
      camera.updateProjectionMatrix();
    };

    const mtlLoader = new MTLLoader(manager);
    mtlLoader.load(mtlUrl, (materials: { preload: () => void }) => {
      materials.preload();
      const loader = new OBJLoader(manager);
      loader.setMaterials(materials as any).load(
        objUrl,
        function (obj: any) {
          obj.traverse(function (child: any) {
            if (child instanceof THREE.Mesh) {
              child.material.wireframe = false;
            }
          });
          object = obj;
          fitCameraToCenteredObject(camera, obj);
        },
        onProgress,
        onError
      );
    });

    const animate = () => {
      requestAnimationFrame(animate);
      controls.update();
      render();
    };

    renderer = new THREE.WebGLRenderer({
      antialias: true,
      preserveDrawingBuffer: true,
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);

    renderer.setClearColor(0x000000);
    el.appendChild(renderer.domElement);

    controls = new OrbitControls(camera, renderer.domElement);
    controls.enablePan = false;
    controls.autoRotate = false;
    controls.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
    controls.mouseButtons.LEFT = THREE.MOUSE.ROTATE;

    controls.addEventListener("change", render);

    // Event listener for keydown to detect the shift key
    document.addEventListener("keydown", (event: any) => {
      console.log(event.key, "keydown");
      if (event.key === "Shift") {
        isShiftPressed = true;
      }

      if (event.key === "Escape") {
        void onDoubleClick();
      }
    });

    // Event listener for keyup to detect when the shift key is released
    document.addEventListener("keyup", (event: any) => {
      if (event.key === "Shift") {
        isShiftPressed = false;
      }
    });

    document.addEventListener("pointerdown", handlePaning);

    animate();
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();

      renderer.setSize(window.innerWidth, window.innerHeight);
      labelRenderer.setSize(el.clientWidth, el.clientHeight);
    }

    function render() {
      labelRenderer.render(scene, camera);
      renderer.render(scene, camera);
    }

    const setDefaultValue = () => {
      if (projectPoints.length > 0) {
        projectPoints?.forEach((points: any) => {
          if (points?.length > 0) {
            const singlePolygonPoints = points?.map((point: any) => {
              const geometry1 = new THREE.SphereGeometry(0.1, 32, 32);
              const material1 = new THREE.MeshBasicMaterial({
                color: 0xffffff,
                depthTest: false,
                transparent: true,
                opacity: 1,
              });
              const sphere1 = new THREE.Mesh(geometry1, material1);
              sphere1.position.copy(
                new THREE.Vector3(point.x, point.y, point.z)
              );
              scene.add(sphere1);
              return sphere1;
            });
            polygons.push(singlePolygonPoints);
            updateLineGeometry();
          }
        });
      }
    };

    setDefaultValue();

    window.addEventListener("resize", onWindowResize, false);

    return () => {
      if (element) {
        element.remove();
      }

      el.removeChild(renderer.domElement);
      el.removeEventListener("mousedown", onClick);
      document.removeEventListener("mousemove", onDocumentMouseMove);
      document.removeEventListener("mouseup", onDocumentMouseUp);
      if (mode === "Edit mode" && dragControls.current) {
        dragControls.current.dispose();
      }
    };
  }
}

yes it working fine but while edit the point position in some minor case it is not filling but it is working 80 to 85% case

Thanks if you find any 100% solution then let me know but as of now that will be great help for me…

Yeah i saw that but not sure why its happening, but I think you can probably fix it up the rest of the way. :slight_smile: cheers.

2 Likes