Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d91416004 |
@@ -80,8 +80,8 @@ assets/ logo, texturen, skybox
|
|||||||
sandwichpanelen rondom, 600 mm de grond in.
|
sandwichpanelen rondom, 600 mm de grond in.
|
||||||
- **Verlichting:** boeideel-spots of gevellampen (per gevel verdeeld,
|
- **Verlichting:** boeideel-spots of gevellampen (per gevel verdeeld,
|
||||||
override per wand) en binnenspots in een raster per unit.
|
override per wand) en binnenspots in een raster per unit.
|
||||||
- **3D:** weergavefilters, omgeving Ontwerp/Dag/Nacht, Render met lampen als
|
- **3D:** weergavefilters (o.a. Vloerplan: wanden afgesneden op 500 mm),
|
||||||
echte lichtbronnen.
|
omgeving Ontwerp/Dag/Nacht, Render met lampen als echte lichtbronnen.
|
||||||
- **Uitvoer:** JSON opslaan/openen en een samenhangende PDF (plattegrond,
|
- **Uitvoer:** JSON opslaan/openen en een samenhangende PDF (plattegrond,
|
||||||
vloeren, wandstukken, binnenwanden, totalen).
|
vloeren, wandstukken, binnenwanden, totalen).
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
||||||
* Zie LICENTIE.md.
|
* Zie LICENTIE.md.
|
||||||
*/
|
*/
|
||||||
const APP_VERSION = '0.4.13';
|
const APP_VERSION = '0.4.14';
|
||||||
// 0.0.X per kleine iteratie, 0.X.0 per mijlpaal, X.0.0 bij live-gang.
|
// 0.0.X per kleine iteratie, 0.X.0 per mijlpaal, X.0.0 bij live-gang.
|
||||||
|
|
||||||
let tabs = [];
|
let tabs = [];
|
||||||
|
|||||||
@@ -813,7 +813,13 @@ window.HouseModel = (() => {
|
|||||||
try { UnitModel.validateState(u.unit || {}); } catch (err) { throw new Error(`unit ${u.id}: ${err.message}`); }
|
try { UnitModel.validateState(u.unit || {}); } catch (err) { throw new Error(`unit ${u.id}: ${err.message}`); }
|
||||||
});
|
});
|
||||||
(design.interiorWalls || []).forEach((iw, i) => {
|
(design.interiorWalls || []).forEach((iw, i) => {
|
||||||
['x1', 'z1', 'x2', 'z2'].forEach(f => { if (typeof iw[f] !== 'number') throw new Error(`binnenwand #${i + 1}: ${f} ontbreekt`); });
|
if (iw.seam) {
|
||||||
|
// Naadwand: ligt op de naad tussen twee units, geen eigen coördinaten.
|
||||||
|
if (!ids.has(iw.seam.a) || !ids.has(iw.seam.b)) throw new Error(`naadwand #${i + 1}: unit ${iw.seam.a} of ${iw.seam.b} bestaat niet`);
|
||||||
|
if (iw.seam.in !== undefined && !['a', 'b'].includes(iw.seam.in)) throw new Error(`naadwand #${i + 1}: ongeldige plaats`);
|
||||||
|
} else {
|
||||||
|
['x1', 'z1', 'x2', 'z2'].forEach(f => { if (typeof iw[f] !== 'number') throw new Error(`binnenwand #${i + 1}: ${f} ontbreekt`); });
|
||||||
|
}
|
||||||
try { WallModel.validateState(iw.wall || {}); } catch (err) { throw new Error(`binnenwand #${i + 1}: ${err.message}`); }
|
try { WallModel.validateState(iw.wall || {}); } catch (err) { throw new Error(`binnenwand #${i + 1}: ${err.message}`); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-4
@@ -322,6 +322,12 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
// Opgeslagen camerastand gaat voor; anders eenmalig passend maken.
|
// Opgeslagen camerastand gaat voor; anders eenmalig passend maken.
|
||||||
let cameraFitted = CameraMemory.restore(overviewCamKey, camera, controls);
|
let cameraFitted = CameraMemory.restore(overviewCamKey, camera, controls);
|
||||||
let beplatingVisible = true, lampenVisible = true;
|
let beplatingVisible = true, lampenVisible = true;
|
||||||
|
// Vloerplan: alle wanden afgesneden op VLOERPLAN_CUT_MM boven het loopvlak
|
||||||
|
// (clipping-vlak, doorsnede blijft open); dak en lampen uit.
|
||||||
|
const VLOERPLAN_CUT_MM = 500;
|
||||||
|
let vloerplan = false;
|
||||||
|
const cutPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0);
|
||||||
|
renderer.localClippingEnabled = true;
|
||||||
let fullRender = false;
|
let fullRender = false;
|
||||||
let skyMode = Skybox.getMode();
|
let skyMode = Skybox.getMode();
|
||||||
const skyEnv = Skybox.createEnvironment(scene);
|
const skyEnv = Skybox.createEnvironment(scene);
|
||||||
@@ -331,11 +337,13 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
createViewFilter(document.getElementById('ho-toolbar-3d'), [
|
createViewFilter(document.getElementById('ho-toolbar-3d'), [
|
||||||
{ key: 'beplating', label: 'Beplating' },
|
{ key: 'beplating', label: 'Beplating' },
|
||||||
{ key: 'dak', label: 'Dak' },
|
{ key: 'dak', label: 'Dak' },
|
||||||
{ key: 'lampen', label: 'Lampen' }
|
{ key: 'lampen', label: 'Lampen' },
|
||||||
|
{ key: 'vloerplan', label: 'Vloerplan', checked: false }
|
||||||
], (v) => {
|
], (v) => {
|
||||||
beplatingVisible = v.beplating;
|
beplatingVisible = v.beplating;
|
||||||
dakVisible = v.dak;
|
dakVisible = v.dak;
|
||||||
lampenVisible = v.lampen;
|
lampenVisible = v.lampen;
|
||||||
|
vloerplan = v.vloerplan;
|
||||||
refresh({ keepProps: true });
|
refresh({ keepProps: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -354,7 +362,7 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
if (lightsGroup) { scene.remove(lightsGroup); lightsGroup = null; }
|
if (lightsGroup) { scene.remove(lightsGroup); lightsGroup = null; }
|
||||||
// De nep-lichtbundels (kegels) alleen zonder echte lampen.
|
// De nep-lichtbundels (kegels) alleen zonder echte lampen.
|
||||||
if (parts && parts.lightingGroup) parts.lightingGroup.traverse(o => { if (o.userData.beam) o.visible = !lampsOn; });
|
if (parts && parts.lightingGroup) parts.lightingGroup.traverse(o => { if (o.userData.beam) o.visible = !lampsOn; });
|
||||||
if (lampsOn && parts && lampenVisible) {
|
if (lampsOn && parts && lampenVisible && !vloerplan) {
|
||||||
const edge = dakVisible ? parts.roofEdge : null;
|
const edge = dakVisible ? parts.roofEdge : null;
|
||||||
lightsGroup = LightingModel.buildLights3D(parts.lighting.lamps, edge ? edge.spots : [], edge ? edge.top - edge.fasciaHeight : 0, parts.lighting.indoor, {
|
lightsGroup = LightingModel.buildLights3D(parts.lighting.lamps, edge ? edge.spots : [], edge ? edge.top - edge.fasciaHeight : 0, parts.lighting.indoor, {
|
||||||
outdoorFactor: skyMode === 'night' ? 1 : LightingModel.DAY_OUTDOOR_FACTOR
|
outdoorFactor: skyMode === 'night' ? 1 : LightingModel.DAY_OUTDOOR_FACTOR
|
||||||
@@ -919,12 +927,24 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
u.floorGroup.position.set(u.rect.cx * toM, 0, u.rect.cz * toM);
|
u.floorGroup.position.set(u.rect.cx * toM, 0, u.rect.cz * toM);
|
||||||
houseGroup.add(u.floorGroup);
|
houseGroup.add(u.floorGroup);
|
||||||
});
|
});
|
||||||
|
// Vloerplan: snijvlak op het (hoogste) loopvlak + VLOERPLAN_CUT_MM. De
|
||||||
|
// wandmaterialen worden gedeeld, dus altijd zetten (ook weer uit).
|
||||||
|
cutPlane.constant = (Math.max(...parts.units.map(u => u.layout.floorTop)) + VLOERPLAN_CUT_MM) * toM;
|
||||||
|
const clip = vloerplan ? [cutPlane] : [];
|
||||||
HouseModel.allWallPieces(parts).forEach(p => {
|
HouseModel.allWallPieces(parts).forEach(p => {
|
||||||
p.group.rotation.y = p.place.rotY;
|
p.group.rotation.y = p.place.rotY;
|
||||||
p.group.position.set(p.place.cx * toM, p.place.floorTop * toM, p.place.cz * toM);
|
p.group.position.set(p.place.cx * toM, p.place.floorTop * toM, p.place.cz * toM);
|
||||||
|
p.group.traverse(o => {
|
||||||
|
if (!o.material) return;
|
||||||
|
(Array.isArray(o.material) ? o.material : [o.material]).forEach(m => {
|
||||||
|
if (m.clippingPlanes && m.clippingPlanes.length === clip.length) return;
|
||||||
|
m.clippingPlanes = clip;
|
||||||
|
m.needsUpdate = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
houseGroup.add(p.group);
|
houseGroup.add(p.group);
|
||||||
});
|
});
|
||||||
if (dakVisible) {
|
if (dakVisible && !vloerplan) {
|
||||||
parts.units.forEach(u => {
|
parts.units.forEach(u => {
|
||||||
u.roof.group.position.set(u.roof.rect.cx * toM, u.roof.base * toM, u.roof.rect.cz * toM);
|
u.roof.group.position.set(u.roof.rect.cx * toM, u.roof.base * toM, u.roof.rect.cz * toM);
|
||||||
houseGroup.add(u.roof.group);
|
houseGroup.add(u.roof.group);
|
||||||
@@ -932,7 +952,7 @@ function mountHouseEditor(container, callbacks) {
|
|||||||
if (parts.roofEdgeGroup) houseGroup.add(parts.roofEdgeGroup);
|
if (parts.roofEdgeGroup) houseGroup.add(parts.roofEdgeGroup);
|
||||||
}
|
}
|
||||||
if (parts.sandwichGroup) houseGroup.add(parts.sandwichGroup);
|
if (parts.sandwichGroup) houseGroup.add(parts.sandwichGroup);
|
||||||
if (parts.lightingGroup) houseGroup.add(parts.lightingGroup);
|
if (parts.lightingGroup && !vloerplan) houseGroup.add(parts.lightingGroup);
|
||||||
scene.add(houseGroup);
|
scene.add(houseGroup);
|
||||||
|
|
||||||
const bx0 = Math.min(...parts.rects.map(r => r.x0)), bx1 = Math.max(...parts.rects.map(r => r.x1));
|
const bx0 = Math.min(...parts.rects.map(r => r.x0)), bx1 = Math.max(...parts.rects.map(r => r.x1));
|
||||||
|
|||||||
+21
-5
@@ -247,17 +247,33 @@ window.RoofModel = (() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3D van het dak van één unit (meters, gecentreerd op de dakrechthoek,
|
// 3D van het dak van één unit (meters, gecentreerd op de dakrechthoek,
|
||||||
// Y = 0 op het loopvlak): per dakvlak build3D, gekanteld om de onderkant
|
// Y = 0 op het loopvlak): per dakvlak build3D, zo vervormd dat de
|
||||||
// van het plafond zodat die precies op de daklijn van de wanden ligt.
|
// onderkant van het plafond precies op de daklijn van de wanden ligt.
|
||||||
|
// Geen kanteling maar een afschuiving (lokaal +Z = oplopende as):
|
||||||
|
// plan = z·cos θ, Y = y/cos θ + z·sin θ
|
||||||
|
// De onderkant ligt dan op de helling, de dikte haaks op de helling blijft
|
||||||
|
// gelijk (verticaal = dikte/cos θ, zoals vThick), de lengte langs de
|
||||||
|
// helling ook (balklengtes), en de kopse kanten staan loodrecht (zoals een
|
||||||
|
// loodrechte zaagsnede aan sporen). Bij kantelen zouden de kopse kanten
|
||||||
|
// haaks op de helling staan en ligt de bovenkant aan de hoge kant
|
||||||
|
// dikte·sin θ terug van de dakrand.
|
||||||
function buildUnit3D(unitRoof, opts) {
|
function buildUnit3D(unitRoof, opts) {
|
||||||
const toM = 0.001;
|
const toM = 0.001;
|
||||||
const group = new THREE.Group();
|
const group = new THREE.Group();
|
||||||
unitRoof.planes.forEach(pl => {
|
unitRoof.planes.forEach(pl => {
|
||||||
const g = new THREE.Group();
|
const g = new THREE.Group();
|
||||||
g.add(build3D(pl.data.calcData, pl.data.resolvedOpenings, opts));
|
|
||||||
g.position.set(pl.cx * toM, pl.y * toM, pl.cz * toM);
|
g.position.set(pl.cx * toM, pl.y * toM, pl.cz * toM);
|
||||||
// Eerst kantelen om lokaal X (lokaal +Z = oplopende as), dan draaien.
|
g.rotation.set(0, pl.yaw, 0);
|
||||||
g.rotation.set(-pl.theta, pl.yaw, 0, 'YXZ');
|
const shear = new THREE.Group();
|
||||||
|
const c = Math.cos(pl.theta), sn = Math.sin(pl.theta);
|
||||||
|
shear.matrixAutoUpdate = false;
|
||||||
|
shear.matrix.set(
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, 1 / c, sn, 0,
|
||||||
|
0, 0, c, 0,
|
||||||
|
0, 0, 0, 1);
|
||||||
|
shear.add(build3D(pl.data.calcData, pl.data.resolvedOpenings, opts));
|
||||||
|
g.add(shear);
|
||||||
group.add(g);
|
group.add(g);
|
||||||
});
|
});
|
||||||
return group;
|
return group;
|
||||||
|
|||||||
Reference in New Issue
Block a user