v0.4.16: PDF-aanzichten (vlakke render) met legenda afwerking en afmetingen, zaaglijst/zaagmachine eigen blad, infopaneel afmetingen, lopen (Shift+T) met camera-menu, zachter licht, naadwanden volgen koppeling, boeideel over de gevel (gevel tot bovenkant dak), thumbnails riet/vloer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-09-30 22:29:57 +02:00
co-authored by Claude Opus 5.5
parent 12c2579471
commit 23bfe153a2
26 changed files with 818 additions and 77 deletions
+102
View File
@@ -0,0 +1,102 @@
/*!
* Tectum
* Copyright © 2026 Machiel Heinen.
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
* Zie LICENTIE.md.
*/
// ============================================================
// core/walk-controls.js — lopen in de 3D-weergave (first person).
// W/S of pijl omhoog/omlaag vooruit/achteruit
// A/D opzij stappen
// pijl links/rechts draaien
// Shift rennen
// slepen met de muis rondkijken
// Botsen (optioneel) en vloerhoogte komen van de aanroeper:
// opts.blocked(x, z) -> true als je daar niet kunt staan (m); standaard nooit
// opts.floorAt(x, z) -> hoogte van de ondergrond (m)
// opts.eye -> ooghoogte boven de ondergrond (m, standaard 1,65)
// Plain script (geen ES-module); gebruikt THREE.
//
// const walk = createWalkControls(camera, domElement, opts);
// walk.start({ x, z, yaw }); walk.update(dt); walk.stop();
// ============================================================
function createWalkControls(camera, domElement, opts) {
const o = Object.assign({ eye: 1.65, speed: 1.68, run: 3.84, turn: Math.PI / 2, blocked: () => false, floorAt: () => 0 }, opts || {});
const keys = new Set();
let active = false;
let x = 0, z = 0, yaw = 0, pitch = 0;
let drag = null;
const inField = () => ['INPUT', 'SELECT', 'TEXTAREA'].includes(document.activeElement && document.activeElement.tagName);
const MOVE_KEYS = ['w', 'a', 's', 'd', 'arrowup', 'arrowdown', 'arrowleft', 'arrowright', 'shift'];
function onKeyDown(e) {
if (!active || inField() || e.ctrlKey || e.metaKey || e.altKey) return;
const k = e.key.toLowerCase();
if (!MOVE_KEYS.includes(k)) return;
keys.add(k);
e.preventDefault();
}
function onKeyUp(e) { keys.delete(e.key.toLowerCase()); }
function onDown(e) { if (active && e.button === 0) drag = { x: e.clientX, y: e.clientY }; }
function onMove(e) {
if (!active || !drag) return;
const w = domElement.clientWidth || 1;
yaw -= (e.clientX - drag.x) / w * Math.PI;
pitch = Math.max(-1.2, Math.min(1.2, pitch - (e.clientY - drag.y) / w * Math.PI));
drag = { x: e.clientX, y: e.clientY };
}
function onUp() { drag = null; }
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
domElement.addEventListener('pointerdown', onDown);
window.addEventListener('pointermove', onMove);
window.addEventListener('pointerup', onUp);
function apply() {
camera.position.set(x, o.floorAt(x, z) + o.eye, z);
// yaw 0 = kijkrichting +Z; pitch > 0 = omhoog.
const dir = new THREE.Vector3(Math.sin(yaw) * Math.cos(pitch), Math.sin(pitch), Math.cos(yaw) * Math.cos(pitch));
camera.lookAt(camera.position.clone().add(dir));
}
// Stap met botsen: eerst de hele stap, anders per as (langs een muur glijden).
function step(dx, dz) {
if (!o.blocked(x + dx, z + dz)) { x += dx; z += dz; return; }
if (!o.blocked(x + dx, z)) x += dx;
else if (!o.blocked(x, z + dz)) z += dz;
}
return {
get active() { return active; },
start(pose) {
x = pose.x; z = pose.z; yaw = pose.yaw || 0; pitch = 0;
keys.clear();
active = true;
apply();
},
stop() { active = false; keys.clear(); drag = null; },
update(dt) {
if (!active) return;
const t = Math.min(dt, 0.1);
if (keys.has('arrowleft')) yaw += o.turn * t;
if (keys.has('arrowright')) yaw -= o.turn * t;
const f = (keys.has('w') || keys.has('arrowup') ? 1 : 0) - (keys.has('s') || keys.has('arrowdown') ? 1 : 0);
const r = (keys.has('d') ? 1 : 0) - (keys.has('a') ? 1 : 0);
if (f || r) {
const v = (keys.has('shift') ? o.run : o.speed) * t / Math.hypot(f, r);
const fx = Math.sin(yaw), fz = Math.cos(yaw); // vooruit
const rx = -fz, rz = fx; // rechts (kijkend langs +Z is rechts -X)
step((fx * f + rx * r) * v, (fz * f + rz * r) * v);
}
apply();
},
dispose() {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
domElement.removeEventListener('pointerdown', onDown);
window.removeEventListener('pointermove', onMove);
window.removeEventListener('pointerup', onUp);
}
};
}