import {
Vector2,
Vector3,
Box3,
Mesh,
PerspectiveCamera,
MeshBasicMaterial,
Scene,
Color,
DoubleSide,
WebGLRenderer,
BufferGeometry
} from "https://esm.sh/three";
import {
OrbitControls,
TextGeometry,
FontLoader
} 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 = 48;
const height = 10;
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;
};
const positions: Vector3[] = [];
for (const polygon of skeletonPostProcessingResult.polygons) {
const polygonVertices: number[] = [];
const edgeLine = [
new Vector2(polygon.edgeStart.x, polygon.edgeStart.y),
new Vector2(polygon.edgeEnd.x, polygon.edgeEnd.y)
];
for (const vertex of polygon.vertices) {
polygonVertices.push(vertex.x, vertex.y);
}
const triangles = earcut(polygonVertices).reverse();
for (let i = 0; i < triangles.length; i++) {
const index = triangles[i];
const x = polygonVertices[index * 2];
const y = polygonVertices[index * 2 + 1];
const vertex = new Vector2(x, y);
// distance maximale entre le point "vertex" et le vecteur [sommet debut, sommet fin] "edgeLine"
const dst = _signedDistanceToLine(vertex, edgeLine);
// Interpolation de l'altitude du point "vertex"
const vertexZ = minHeight + (height * dst) / Hmax;
positions.push(new Vector3(x, y, vertexZ));
}
}
////// THREE.JS SCENE
let camera, scene, renderer, controls;
const loader = new FontLoader();
////// Roof polygons
const material = new MeshBasicMaterial({
color: "#ffb6e9",
side: DoubleSide,
wireframe: false
});
const roofGeometry = new BufferGeometry();
roofGeometry.setFromPoints(positions);
const roofMesh = new Mesh(roofGeometry, material);
const center = new Vector3(
skeletonBox.minX + (skeletonBox.maxX - skeletonBox.minX) / 2,
skeletonBox.minY + (skeletonBox.maxY - skeletonBox.minY) / 2,
minHeight + height / 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(
30,
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.minY, minHeight * 2);
camera.lookAt(center);
controls.target.copy(center);
controls.update();
scene.add(roofMesh);
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();
loader.load(
"https://esm.sh/three@0.172.0/examples/fonts/helvetiker_regular.typeface.json",
function (font) {
for (let index = 0; index < positions.length; index++) {
const roofVertice = positions[index];
const labelMesh = getPointLabelMesh(
font,
Math.ceil(roofVertice.z) + " m"
);
labelMesh.position.set(roofVertice.x, roofVertice.y, roofVertice.z);
scene.add(labelMesh);
//console.log(labelMesh, roofVertice);
}
}
);
const getPointLabelMesh = (font, label) => {
const geometry = new TextGeometry(label, {
font: font,
size: 2,
depth: 0.1
});
const labelMesh = new Mesh(
geometry,
new MeshBasicMaterial({
color: "black",
side: DoubleSide
})
);
labelMesh.rotation.y = Math.PI / 2;
labelMesh.rotation.z = Math.PI / 2;
return labelMesh;
};
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.