Tectum v0.4.0 — mijlpaal gevelopbouw
Configurator voor Van Emous Mantelzorgwoningen: wanden, vloeren, units, woningen (L-vorm), plat dak met dakrand, buitenverlichting, latwerk op vochtdoek, trasraam, sandwichpanelen en PDF-tekenbladen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vendored
+131
@@ -0,0 +1,131 @@
|
||||
// ============================================================
|
||||
// js/vendor/OrbitControls.js — eigen, compacte vervanging van de officiële
|
||||
// three.js OrbitControls-addon. Geschreven omdat we geen netwerktoegang
|
||||
// hebben om het origineel te vendoren; dekt precies de subset die wall.js
|
||||
// gebruikt (`new THREE.OrbitControls(camera, domElement)`,
|
||||
// `controls.target.set(...)`, `controls.update()`), plus interactie:
|
||||
// links-slepen = roteren, scrollwiel = zoomen, rechts-slepen = pannen.
|
||||
//
|
||||
// Moet NA three.min.js geladen worden (leunt op window.THREE).
|
||||
// ============================================================
|
||||
(function () {
|
||||
if (typeof THREE === 'undefined') {
|
||||
console.error('OrbitControls.js (eigen versie): THREE is niet geladen — controleer de scriptvolgorde.');
|
||||
return;
|
||||
}
|
||||
|
||||
function OrbitControls(camera, domElement) {
|
||||
const scope = this;
|
||||
this.object = camera;
|
||||
this.domElement = domElement || document;
|
||||
this.target = new THREE.Vector3();
|
||||
|
||||
this.minDistance = 0.1;
|
||||
this.maxDistance = 1000;
|
||||
this.minPolarAngle = 0.001;
|
||||
this.maxPolarAngle = Math.PI - 0.001;
|
||||
this.rotateSpeed = 1.0;
|
||||
this.zoomSpeed = 1.0;
|
||||
this.panSpeed = 1.0;
|
||||
this.enableRotate = true;
|
||||
this.enableZoom = true;
|
||||
this.enablePan = true;
|
||||
|
||||
const offset = new THREE.Vector3();
|
||||
const spherical = new THREE.Spherical();
|
||||
let isRotating = false;
|
||||
let isPanning = false;
|
||||
let lastX = 0, lastY = 0;
|
||||
|
||||
function getZoomScale() { return Math.pow(0.95, scope.zoomSpeed); }
|
||||
|
||||
this.update = function () {
|
||||
offset.copy(scope.object.position).sub(scope.target);
|
||||
spherical.setFromVector3(offset);
|
||||
offset.setFromSpherical(spherical);
|
||||
scope.object.position.copy(scope.target).add(offset);
|
||||
scope.object.lookAt(scope.target);
|
||||
return true;
|
||||
};
|
||||
|
||||
function onPointerDown(event) {
|
||||
if (event.button === 2) {
|
||||
if (!scope.enablePan) return;
|
||||
isPanning = true;
|
||||
} else {
|
||||
if (!scope.enableRotate) return;
|
||||
isRotating = true;
|
||||
}
|
||||
lastX = event.clientX;
|
||||
lastY = event.clientY;
|
||||
}
|
||||
|
||||
function onPointerMove(event) {
|
||||
if (isRotating) {
|
||||
const dx = event.clientX - lastX;
|
||||
const dy = event.clientY - lastY;
|
||||
lastX = event.clientX;
|
||||
lastY = event.clientY;
|
||||
|
||||
const w = scope.domElement.clientWidth || 1;
|
||||
const h = scope.domElement.clientHeight || 1;
|
||||
|
||||
offset.copy(scope.object.position).sub(scope.target);
|
||||
spherical.setFromVector3(offset);
|
||||
spherical.theta -= (dx / w) * Math.PI * scope.rotateSpeed;
|
||||
spherical.phi -= (dy / h) * Math.PI * scope.rotateSpeed;
|
||||
spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi));
|
||||
spherical.makeSafe();
|
||||
offset.setFromSpherical(spherical);
|
||||
scope.object.position.copy(scope.target).add(offset);
|
||||
scope.object.lookAt(scope.target);
|
||||
} else if (isPanning) {
|
||||
const dx = event.clientX - lastX;
|
||||
const dy = event.clientY - lastY;
|
||||
lastX = event.clientX;
|
||||
lastY = event.clientY;
|
||||
|
||||
const distance = scope.object.position.distanceTo(scope.target);
|
||||
const fov = (scope.object.fov || 50) * Math.PI / 180;
|
||||
const h = scope.domElement.clientHeight || 1;
|
||||
const factor = 2 * Math.tan(fov / 2) * distance / h;
|
||||
|
||||
const te = scope.object.matrix.elements;
|
||||
const xAxis = new THREE.Vector3(te[0], te[1], te[2]).multiplyScalar(-dx * factor * scope.panSpeed);
|
||||
const yAxis = new THREE.Vector3(te[4], te[5], te[6]).multiplyScalar(dy * factor * scope.panSpeed);
|
||||
scope.target.add(xAxis).add(yAxis);
|
||||
scope.object.position.add(xAxis).add(yAxis);
|
||||
}
|
||||
}
|
||||
|
||||
function onPointerUp() { isRotating = false; isPanning = false; }
|
||||
|
||||
function onWheel(event) {
|
||||
if (!scope.enableZoom) return;
|
||||
event.preventDefault();
|
||||
const scale = event.deltaY > 0 ? 1 / getZoomScale() : getZoomScale();
|
||||
offset.copy(scope.object.position).sub(scope.target);
|
||||
const distance = Math.max(scope.minDistance, Math.min(scope.maxDistance, offset.length() * scale));
|
||||
offset.setLength(distance);
|
||||
scope.object.position.copy(scope.target).add(offset);
|
||||
}
|
||||
|
||||
function onContextMenu(event) { event.preventDefault(); }
|
||||
|
||||
this.domElement.addEventListener('mousedown', onPointerDown);
|
||||
window.addEventListener('mousemove', onPointerMove);
|
||||
window.addEventListener('mouseup', onPointerUp);
|
||||
this.domElement.addEventListener('wheel', onWheel, { passive: false });
|
||||
this.domElement.addEventListener('contextmenu', onContextMenu);
|
||||
|
||||
this.dispose = function () {
|
||||
scope.domElement.removeEventListener('mousedown', onPointerDown);
|
||||
window.removeEventListener('mousemove', onPointerMove);
|
||||
window.removeEventListener('mouseup', onPointerUp);
|
||||
scope.domElement.removeEventListener('wheel', onWheel);
|
||||
scope.domElement.removeEventListener('contextmenu', onContextMenu);
|
||||
};
|
||||
}
|
||||
|
||||
THREE.OrbitControls = OrbitControls;
|
||||
})();
|
||||
Reference in New Issue
Block a user