Files
martinfencl.eu/klabava/js/interior-furniture.js
T
martin.fencl f0a6480c86
continuous-integration/drone/push Build is passing
feat: add BufferGeometryUtils for advanced geometry manipulation
- Implemented computeMikkTSpaceTangents for consistent tangent generation.
- Added mergeGeometries to combine multiple BufferGeometries.
- Introduced mergeAttributes for merging BufferAttributes.
- Created deepCloneAttribute for cloning BufferAttributes.
- Developed interleaveAttributes and deinterleaveAttribute for attribute management.
- Added estimateBytesUsed to calculate memory usage of geometries.
- Implemented mergeVertices to optimize vertex data.
- Created toTrianglesDrawMode for converting geometries to triangle mode.
- Added computeMorphedAttributes for handling morph targets in geometries.
- Implemented mergeGroups to consolidate geometry groups.
- Developed toCreasedNormals for generating smooth normals with crease angles.
2026-11-02 13:38:01 +01:00

146 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Schematic furnishing positions follow the supplied floor plan. Finishes are provisional.
export function furnishInterior({THREE,parent,planPoint,box,mesh,group}) {
const material=(color,roughness=.8)=>new THREE.MeshStandardMaterial({color,roughness});
const m={oak:material(0xb99b70),oakLight:material(0xd2ba95),white:material(0xf0eee5),stone:material(0xe2ded2),sage:material(0x9ca894),cloth:material(0xd3c7b5),linen:material(0xeae5d9),pillow:material(0xb9bca9),dark:material(0x323a38),metal:new THREE.MeshStandardMaterial({color:0x8c9390,metalness:.65,roughness:.25}),water:material(0xc8d8d7),glass:new THREE.MeshStandardMaterial({color:0xb6d4d3,transparent:true,opacity:.23,roughness:.15,side:THREE.DoubleSide}),mirror:new THREE.MeshStandardMaterial({color:0xbdcaca,metalness:.65,roughness:.08})};
const colliders=[];
function block(p,name,px,py,w,d,h,y,mat){const [x,z]=planPoint(px,py);return box(p,name,x,y+h/2,z,d,h,w,mat);}
function collide(px,py,w,d){const [x,z]=planPoint(px,py);colliders.push({minX:x-d/2,maxX:x+d/2,minZ:z-w/2,maxZ:z+w/2});}
function cylinder(p,name,px,py,r,h,y,mat){const [x,z]=planPoint(px,py);const o=mesh(new THREE.CylinderGeometry(r,r,h,24),mat,p,name);o.position.set(x,y+h/2,z);return o;}
function oval(p,name,px,py,w,d,h,y,mat){const [x,z]=planPoint(px,py);const o=mesh(new THREE.SphereGeometry(1,24,12),mat,p,name);o.scale.set(d/2,h/2,w/2);o.position.set(x,y+h/2,z);return o;}
function cabinet(p,name,px,py,w,d,h=2.35,axis='py',mat=m.white){
const g=group(p,name);block(g,'Korpus',px,py,w,d,h,.02,mat);block(g,'Sokl',px,py,w-.06,d-.06,.09,.02,m.dark);
const span=axis==='py'?w:d,n=Math.max(1,Math.round(span/.55));
for(let i=1;i<n;i++){
const a=-span/2+i*span/n;
block(g,'Spára dvířek',px+(axis==='py'?a:0),py+(axis==='px'?a:0),axis==='py'?.008:w+.002,axis==='px'?.008:d+.002,h-.16,.12,m.oakLight);
}
collide(px,py,w,d);return g;
}
function legs(p,px,py,w,d,h){for(const a of [-1,1])for(const b of [-1,1])block(p,'Noha',px+a*(w/2-.08),py+b*(d/2-.08),.045,.045,h,.02,m.oak);}
function table(p,name,px,py,w,d,h=.75){const g=group(p,name);block(g,'Deska',px,py,w,d,.05,h-.05,m.oakLight);legs(g,px,py,w,d,h-.05);collide(px,py,w,d);return g;}
function chair(p,px,py,rotation=0){
const g=group(p,'Židle');const [x,z]=planPoint(px,py);g.position.set(x,0,z);g.rotation.y=rotation;
box(g,'Sedák',0,.47,0,.43,.08,.43,m.cloth);box(g,'Opěradlo',0,.71,.19,.43,.45,.06,m.cloth);
for(const a of [-1,1])for(const b of [-1,1])box(g,'Noha',a*.16,.22,b*.16,.035,.44,.035,m.oak);
}
function bed(p,name,px,py,w,d,head='py'){
const g=group(p,name);block(g,'Rám postele',px,py,w,d,.29,.08,m.oak);block(g,'Matrace',px,py,w-.06,d-.06,.2,.37,m.linen);
const across=head==='py'?w:d,along=head==='py'?d:w;
block(g,'Čelo',px+(head==='px'?w/2-.04:0),py+(head==='py'?d/2-.04:0),head==='px'?.08:w,head==='py'?.08:d,.9,.07,m.oakLight);
block(g,'Přikrývka',px-(head==='px'?.25:0),py-(head==='py'?.25:0),head==='px'?w-.55:w-.03,head==='py'?d-.55:d-.03,.055,.58,m.pillow);
const count=across>1.3?2:1;
for(let i=0;i<count;i++){
const side=(i-(count-1)/2)*.8;
oval(g,'Polštář',px+(head==='px'?along/2-.32:side),py+(head==='py'?along/2-.32:side),head==='py'?.64:.40,head==='py'?.40:.64,.16,.58,m.linen);
}
collide(px,py,w,d);return g;
}
function sink(p,name,px,py,w,d){
const g=group(p,name);block(g,'Umyvadlová skříňka',px,py,w,d,.52,.3,m.oakLight);block(g,'Umyvadlová deska',px,py,w+.03,d+.03,.06,.82,m.white);
const alongPx=w>d;const n=Math.max(w,d)>1.1?2:1;
for(let i=0;i<n;i++){
const off=(i-(n-1)/2)*.62,cx=px+(alongPx?off:0),cy=py+(alongPx?0:off);
oval(g,'Umyvadlo',cx,cy,alongPx?.5:.36,alongPx?.36:.5,.05,.88,m.white);
oval(g,'Dno mísy',cx,cy,alongPx?.37:.25,alongPx?.25:.37,.014,.912,m.water);
cylinder(g,'Baterie',cx+(alongPx?0:.18),cy+(alongPx?.18:0),.018,.20,.86,m.metal);
}
collide(px,py,w,d);return g;
}
function wc(p,px,py,axis='px'){
const g=group(p,'Závěsné WC');const w=axis==='px'?.65:.38,d=axis==='px'?.38:.65;
oval(g,'Keramická mísa',px,py,w,d,.34,.15,m.white);oval(g,'Sedátko',px,py,w,d,.045,.49,m.white);
oval(g,'Vnitřek mísy',px-.035,py, w*.59,d*.57,.016,.526,m.water);collide(px,py,w,d);
}
function shower(p,px,py,w,d){
const g=group(p,'Sprchový kout');block(g,'Vanička v úrovni podlahy',px,py,w,d,.025,.016,m.stone);
block(g,'Odtok',px+w/2-.1,py,.035,d*.55,.005,.043,m.metal);
const [x,z]=planPoint(px+w/2-.08,py);const o=mesh(new THREE.CylinderGeometry(.014,.014,1.35,10),m.metal,g,'Sprchová tyč');o.position.set(x,1.45,z);
cylinder(g,'Sprchová hlavice',px+w/2-.2,py,.11,.018,2.08,m.metal);
// Open entry to the shower; only the side screen is solid.
block(g,'Skleněná zástěna',px,py+d/2,w,.014,2,.03,m.glass);
}
const kitchen=group(parent,'Kuchyň do U s poloostrovem');
// Run at garden wall, then west side and peninsula; clear aisle 1.30 m.
for(let i=0;i<6;i++){
const py=.625+i*.60;cabinet(kitchen,'Spodní kuchyňská skříňka',.605,py,.60,.59,.82,'px',m.sage);
block(kitchen,'Pracovní deska',.605,py,.64,.60,.045,.84,m.stone);
}
cabinet(kitchen,'Dřezová část',1.545,.605,1.30,.60,.82,'py',m.sage);
block(kitchen,'Pracovní deska',1.545,.605,1.30,.64,.045,.84,m.stone);
cabinet(kitchen,'Poloostrov',2.645,1.745,.90,2.30,.82,'px',m.sage);
block(kitchen,'Deska poloostrova',2.645,1.745,.96,2.34,.045,.84,m.oakLight);
block(kitchen,'Dřez',1.45,.61,.60,.44,.012,.89,m.metal);block(kitchen,'Dřez – vnitřek',1.45,.61,.50,.34,.013,.90,m.dark);
cylinder(kitchen,'Dřezová baterie',1.45,.39,.018,.30,.89,m.metal);
block(kitchen,'Varná deska',.61,2.45,.49,.60,.015,.89,m.dark);
for(const a of [-.13,.13])for(const b of [-.16,.16])cylinder(kitchen,'Varná zóna',.61+a,2.45+b,.083,.003,.908,m.metal);
block(kitchen,'Vestavná trouba',.91,3.04,.016,.48,.42,.27,m.dark);block(kitchen,'Madlo trouby',.93,3.04,.025,.36,.025,.65,m.metal);
cabinet(kitchen,'Vysoká potravinová skříň',.62,3.92,.64,.60,2.30,'px',m.sage);
cabinet(kitchen,'Americká lednice',.66,4.78,.72,.92,1.88,'px',m.metal);
block(kitchen,'Lednice – madla',1.035,4.78,.025,.045,.62,.78,m.dark);
const living=group(parent,'Obývací pokoj a jídelna');
block(living,'Koberec',5.55,2.1,2.6,3.1,.012,.016,m.linen);
block(living,'Pohovka – sedací část',5.0,1.86,.95,2.80,.40,.12,m.cloth);
block(living,'Pohovka – lenoška',5.65,.91,1.7,.90,.40,.12,m.cloth);
block(living,'Pohovka – opěradlo',4.51,1.86,.18,2.80,.76,.12,m.cloth);
block(living,'Pohovka – bočnice',5.0,3.23,1.0,.15,.60,.12,m.cloth);
for(let py=.8;py<3;py+=.75)block(living,'Sedací polštář',5.05,py,.78,.68,.12,.51,m.linen);
collide(5.0,1.86,1.1,2.8);collide(5.65,.91,1.7,.9);
table(living,'Konferenční stolek',6.25,2.25,.70,1.15,.40);
cabinet(living,'Nízká TV skříňka',7.65,1.95,.45,2.30,.42,'px',m.oak);
block(living,'Televize',7.89,1.95,.05,1.45,.82,1.05,m.dark);
cylinder(living,'Kamna',7.582,4.763,.24,1.02,.02,m.dark);cylinder(living,'Kouřovod',7.582,4.763,.075,1.56,1.04,m.dark);
collide(7.582,4.763,.52,.52);
table(living,'Jídelní stůl pro šest',3.95,5.30,2.1,.86);
for(const px of [3.25,3.95,4.65]){chair(living,px,4.57,Math.PI/2);chair(living,px,6.03,-Math.PI/2);}
const storage=group(parent,'Šatny a zádveří');
for(const py of [2.956,4.306])cabinet(storage,'Šatní skříň',10.26,py,1.96,.60,2.35,'py');
cabinet(storage,'Šatní skříň v zádveří',10.2,7.13,1.90,.60,2.35,'py');
cabinet(storage,'Botník',10.20,4.98,1.90,.44,.48,'py',m.oakLight);
block(storage,'Zrcadlo v zádveří',10.20,4.76,1.20,.012,1.25,.92,m.mirror);
for(const py of [11.406,13.125])cabinet(storage,'Šatní skříň u ložnice',6.68,py,2.55,.60,2.35,'py');
const bedrooms=group(parent,'Pokoje a ložnice');
bed(bedrooms,'Lůžko pokoje u zahrady',6.96,7.53,1.95,1.0,'px');
cabinet(bedrooms,'Šatní skříň pokoje',6.65,10.65,2.50,.65,2.35,'py');
table(bedrooms,'Pracovní stůl u zahrady',4.7,10.64,1.15,.62);chair(bedrooms,4.7,9.85,Math.PI/2);
bed(bedrooms,'Lůžko pokoje u příjezdu',12.25,16.08,.92,2.0,'py');
cabinet(bedrooms,'Šatní skříň pokoje u příjezdu',9.85,15.8,.60,2.50,2.35,'px');
table(bedrooms,'Pracovní stůl u příjezdu',11.7,12.75,1.45,.65);chair(bedrooms,11.7,13.55,-Math.PI/2);
bed(bedrooms,'Manželská postel',5.95,16.06,1.80,2.05,'py');
for(const px of [4.77,7.13])cabinet(bedrooms,'Noční stolek',px,16.75,.48,.50,.46,'py',m.oakLight);
const bath=group(parent,'Sanita – společná koupelna');
shower(bath,12.005,10.08,1.50,1.0);
// Quarter-round corner tub, bounded by the 1600 x 1600 footprint in the PDF.
const tubShape=new THREE.Shape();tubShape.moveTo(0,0);tubShape.lineTo(1.6,0);tubShape.absarc(0,0,1.6,0,Math.PI/2,false);tubShape.lineTo(0,0);
const tubGeometry=new THREE.ExtrudeGeometry(tubShape,{depth:.53,bevelEnabled:true,bevelSize:.025,bevelThickness:.025,bevelSegments:2,steps:1,curveSegments:24});
tubGeometry.rotateX(-Math.PI/2);
const tub=mesh(tubGeometry,m.white,bath,'Rohová vana 1600');const [tx,tz]=planPoint(12.73,12.26);tub.position.set(tx,.055,tz);
// Local x increases towards room, local -z towards garden; oval basin is schematic.
oval(bath,'Vana – vnitřní mísa',12.05,11.53,1.15,.83,.035,.62,m.water);
collide(12.0,11.5,1.55,1.55);
sink(bath,'Dvojumyvadlo',10.65,12.01,1.25,.50);
block(bath,'Zrcadlo',10.65,12.27,1.28,.012,.82,1.08,m.mirror);wc(bath,10.0,9.91,'py');
const ensuite=group(parent,'Sanita – koupelna u ložnice');
shower(ensuite,8.60,16.60,1.55,1.0);wc(ensuite,9.06,13.95,'px');
sink(ensuite,'Dvojumyvadlo u ložnice',9.15,15.30,.48,1.35);
block(ensuite,'Zrcadlo',9.41,15.30,.012,1.40,.80,1.1,m.mirror);
for(let i=0;i<7;i++)block(ensuite,'Topný žebřík',7.83,15.35,.04,.55,.025,.60+i*.10,m.white);
const technical=group(parent,'Technické vybavení – orientační');
cabinet(technical,'Rekuperace / tepelné čerpadlo',12.10,7.15,1.0,.60,1.9,'py',m.white);
for(let i=0;i<2;i++){
block(technical,i?'Sušička':'Pračka',12.39,9.13,.62,.62,.82,.04+i*.84,m.white);
const o=cylinder(technical,'Kruhová dvířka spotřebiče',12.39,8.805,.20,.025,.44+i*.84,m.dark);o.rotation.x=Math.PI/2;o.position.y=.44+i*.84;
const [x,z]=planPoint(12.39,8.80);o.position.x=x;o.position.z=z;
}
collide(12.39,9.13,.62,.62);
cabinet(technical,'Mrazák',11.65,9.13,.63,.62,1.45,'py',m.white);
block(technical,'Elektroměnič a baterie',10.20,7.72,.65,.30,1.1,.6,m.white);
collide(10.20,7.72,.65,.30);
return {colliders,materials:m};
}