import {
  Vector2,
  Vector3,
  Box3,
  Mesh,
  PerspectiveCamera,
  PointsMaterial,
  MeshBasicMaterial,
  Scene,
  Color,
  DoubleSide,
  WebGLRenderer,
  BufferGeometry,
  Points,
  Float32BufferAttribute
} from "https://esm.sh/three";

import { OrbitControls } from "https://esm.sh/three/addons";

import earcut from "https://esm.sh/earcut";

const skeletonPostProcessingResult = {
  polygons: [
    {
      vertices: [],
      edgeStart: {
        x: 259505.3125,
        y: 6253223.5
      },
      edgeEnd: {
        x: 259521.140625,
        y: 6253168
      }
    },
    {
      vertices: [
        {
          x: 259606.34375,
          y: 6253192.5
        },
        {
          x: 259598.453125,
          y: 6253220.25
        },
        {
          x: 259513.2265625,
          y: 6253195.75
        },
        {
          x: 259521.140625,
          y: 6253168
        }
      ],
      edgeStart: {
        x: 259521.140625,
        y: 6253168
      },
      edgeEnd: {
        x: 259606.34375,
        y: 6253192.5
      }
    },
    {
      vertices: [],
      edgeStart: {
        x: 259606.34375,
        y: 6253192.5
      },
      edgeEnd: {
        x: 259590.5625,
        y: 6253248
      }
    },
    {
      vertices: [
        {
          x: 259505.3125,
          y: 6253223.5
        },
        {
          x: 259513.2265625,
          y: 6253195.75
        },
        {
          x: 259598.453125,
          y: 6253220.25
        },
        {
          x: 259590.5625,
          y: 6253248
        }
      ],
      edgeStart: {
        x: 259590.5625,
        y: 6253248
      },
      edgeEnd: {
        x: 259505.3125,
        y: 6253223.5
      }
    }
  ]
};

const skeletonBox = {
  maxX: 259606.34375,
  maxY: 6253248,
  minX: 259505.3125,
  minY: 6253168 - 20
};
const minHeight = 31;

const roofMaxHeight = 58;
const roofHeight = 10;
const roofMinHeight = 48;

const Hmax = 28.85;

const _signedDistanceToLine = function (point, line) {
  const lineVector = new Vector2().subVectors(line[1], line[0]);
  const pointVector = new Vector2().subVectors(point, line[0]);
  const cross = lineVector.x * pointVector.y - lineVector.y * pointVector.x;
  const lineLength = lineVector.length();

  return cross / lineLength;
};

function createWallTriangles(positions: Array<Vector3>) {
  const postionsResult = positions.slice();

  for (let index = 0; index < positions.length; index++) {
    const A = positions[index];
    const B = positions[index + 1] ? positions[index + 1] : positions[0];
    // Triangle ABA'
    postionsResult.push(A); // A
    postionsResult.push(B); // B
    postionsResult.push(new Vector3(A.x, A.y, minHeight)); // A'

    // Triangle A',B,B'
    postionsResult.push(new Vector3(A.x, A.y, minHeight)); // A'
    postionsResult.push(B); // B
    postionsResult.push(new Vector3(B.x, B.y, minHeight)); // B'
  }

  return postionsResult;
}

const vertices: Vector3[] = [];
for (const polygon of skeletonPostProcessingResult.polygons) {
  const edgeLine = [polygon.edgeStart, polygon.edgeEnd];

  // les polygones avec uniquement 2 sommets
  if (polygon.vertices.length == 0) {
    const middle = new Vector2()
      .addVectors(polygon.edgeStart, polygon.edgeEnd)
      .multiplyScalar(0.5);

    const startDst = _signedDistanceToLine(polygon.edgeStart, edgeLine);
    const endDst = _signedDistanceToLine(polygon.edgeEnd, edgeLine);

    // A
    vertices.push(
      new Vector3(
        polygon.edgeStart.x,
        polygon.edgeStart.y,
        roofMinHeight + (roofHeight * startDst) / Hmax
      )
    );
    // B
    vertices.push(new Vector3(middle.x, middle.y, roofMaxHeight));
    // C
    vertices.push(
      new Vector3(
        polygon.edgeEnd.x,
        polygon.edgeEnd.y,
        roofMinHeight + (roofHeight * endDst) / Hmax
      )
    );
  }
}

const wallTrianglesVertices = createWallTriangles(vertices);
console.log(vertices, wallTrianglesVertices);

////// THREE.JS SCENE
let camera, scene, renderer, controls;

//const material = new PointsMaterial({ color: 0x888888, size: 10 });
const material = new MeshBasicMaterial({
  color: "#ffb6e9",
  side: DoubleSide
});

const wallGeometry = new BufferGeometry();
wallGeometry.setFromPoints(wallTrianglesVertices);

const wallMesh = new Mesh(wallGeometry, material);

const center = new Vector3(
  skeletonBox.minX + (skeletonBox.maxX - skeletonBox.minX) / 2,
  skeletonBox.minY + (skeletonBox.maxY - skeletonBox.minY) / 2,
  minHeight + roofHeight / 2
);

function init() {
  renderer = new WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  scene = new Scene();
  scene.background = new Color("white");

  camera = new PerspectiveCamera(
    70,
    window.innerWidth / window.innerHeight,
    1,
    10000
  );
  // Our up axes here is the Z
  camera.up.set(0, 0, 1);

  controls = new OrbitControls(camera, renderer.domElement);

  camera.position.set(skeletonBox.maxX, skeletonBox.maxY, roofMaxHeight * 2);
  camera.lookAt(center);

  controls.target.copy(center);
  controls.update();

  scene.add(wallMesh);

  window.addEventListener("resize", onWindowResize);
}
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);

  controls.update();

  renderer.render(scene, camera);
}

init();
animate();
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.