// Plan coordinates in metres: px garden -> road; py west -> east. // Areas are the printed PDF values; names (except zádveří) follow the drawn fixtures. export const rooms = [ {id:'living',name:'Obývací pokoj + kuchyň',short:'Obývák + kuchyň',area:49.88,polygon:[[.295,.295],[7.955,.295],[7.955,6.791],[.295,6.791]],label:[4.0,3.7],spawn:[3.7,3.7],look:[1.2,1.8],floor:'wood'}, {id:'spare',name:'Místnost bez určení',short:'Místnost',area:7.05,polygon:[[8.1,.295],[11.255,.295],[11.255,2.531],[8.1,2.531]],label:[9.7,1.4],spawn:[9.7,1.4],look:[8.5,2.3],floor:'tile'}, {id:'wardrobe',name:'Šatna',short:'Šatna',area:6.15,polygon:[[8.1,2.656],[11.255,2.656],[11.255,4.605],[8.1,4.605]],label:[9.7,3.63],spawn:[8.8,3.65],look:[10.5,3.65],floor:'wood'}, {id:'entry',name:'Zádveří',short:'Zádveří',area:8.58,polygon:[[8.1,4.75],[11.255,4.75],[11.255,7.432],[8.1,7.432]],label:[9.8,6.1],spawn:[10.3,6.1],look:[8.3,5.8],floor:'tile'}, {id:'hall',name:'Chodba',short:'Chodba',area:7.59,polygon:[[8.1,7.557],[9.421,7.557],[9.421,13.406],[8.1,13.406]],label:[8.76,10.5],spawn:[8.76,8.15],look:[8.76,12.6],floor:'wood'}, {id:'technical',name:'Technická místnost',short:'Technická',area:7.10,polygon:[[9.546,7.557],[11.38,7.557],[11.38,6.821],[12.755,6.821],[12.755,9.456],[9.546,9.456]],label:[11.15,8.35],spawn:[10.5,8.4],look:[12.3,8.1],floor:'tile'}, {id:'bath',name:'Koupelna',short:'Koupelna',area:8.66,polygon:[[9.546,9.581],[12.755,9.581],[12.755,12.282],[9.546,12.282]],label:[10.6,10.9],spawn:[10.25,10.95],look:[12.15,11.0],floor:'tile'}, {id:'front',name:'Pokoj u příjezdu',short:'Pokoj',area:15.08,polygon:[[9.546,12.407],[12.755,12.407],[12.755,17.105],[9.546,17.105]],label:[11.0,14.6],spawn:[10.9,14.3],look:[12.0,16.0],floor:'wood'}, {id:'garden',name:'Pokoj u zahrady',short:'Pokoj',area:15.62,polygon:[[4.045,7.015],[7.955,7.015],[7.955,10.981],[4.045,10.981]],label:[5.7,8.8],spawn:[5.4,8.7],look:[7.0,7.5],floor:'wood'}, {id:'master',name:'Ložnice se šatnou',short:'Ložnice + šatna',area:22.35,polygon:[[4.045,11.106],[7.955,11.106],[7.955,13.425],[5.344,13.425],[5.344,13.55],[7.673,13.55],[7.673,17.105],[4.045,17.105]],label:[4.65,14.2],spawn:[4.6,14.5],look:[5.95,16.2],floor:'wood'}, {id:'ensuite',name:'Koupelna u ložnice',short:'Koupelna',area:5.68,polygon:[[7.798,13.55],[9.421,13.55],[9.421,17.105],[7.798,17.105]],label:[8.6,15.2],spawn:[8.35,14.55],look:[8.6,16.65],floor:'tile'} ]; // axis=px: fixed px, runs along py; axis=py: fixed py, runs along px. export const partitions = [ ['px',8.0275,.295,13.425,.145,[[5.19,6.09],[9.257,10.156],[11.805,12.708]]], ['py',2.5935,8.1,11.255,.125,[[8.349,9.248]]], ['py',4.6775,8.1,11.255,.145,[[8.349,9.248]]], ['py',7.4945,8.1,11.255,.125,[[8.349,9.248]]], ['py',6.903,3.775,7.955,.224,[]], ['py',11.0435,4.045,7.955,.125,[]], ['px',9.4835,7.557,13.406,.125,[[8.006,8.905],[10.656,11.457],[12.457,13.357]]], ['py',9.5185,9.546,12.755,.125,[]], ['py',12.3445,9.546,12.755,.125,[]], ['py',13.4785,8.1,9.421,.145,[]], ['py',13.4875,5.344,7.955,.125,[]], ['px',7.7355,13.55,17.105,.125,[[14.152,14.953]]], ['px',9.4835,13.406,17.105,.125,[]], ['py',10.63,11.255,12.755,.10,[]] ]; export function buildInterior({THREE,houseGroup,planPoint,box,mesh,group}) { const root=group(houseGroup,'Interiér podle půdorysu'); const floors=group(root,'Podlahy místností'),fullWalls=group(root,'Příčky – plná výška'),cutWalls=group(root,'Příčky – řez 1,12 m'); const ceiling=group(root,'Podhled +2,600 m'),furniture=group(root,'Orientační vybavení'); const height=2.6,cutHeight=1.12,doorHead=2.1; const wallMaterial=new THREE.MeshStandardMaterial({color:0xeeeae1,roughness:.92}); const wood=new THREE.MeshStandardMaterial({color:0xc7ac87,roughness:.85}); const tile=new THREE.MeshStandardMaterial({color:0xc8c6be,roughness:.83}); const trim=new THREE.MeshStandardMaterial({color:0xe4d5bb,roughness:.78}); const grout=new THREE.LineBasicMaterial({color:0x8c8373,transparent:true,opacity:.16}); const wallColliders=[]; function horizontal(parent,name,polygon,y,material,down=false) { const points=polygon.map(p=>planPoint(...p)); const triangles=THREE.ShapeUtils.triangulateShape(points.map(([x,z])=>new THREE.Vector2(x,z)),[]); const geom=new THREE.BufferGeometry(); geom.setAttribute('position',new THREE.Float32BufferAttribute(points.flatMap(([x,z])=>[x,y,z]),3)); const indices=triangles.flatMap(([a,b,c])=>{ const A=points[a],B=points[b],C=points[c]; const normalY=(B[1]-A[1])*(C[0]-A[0])-(B[0]-A[0])*(C[1]-A[1]); return (normalY>0)!==down?[a,b,c]:[c,b,a]; }); geom.setIndex(indices);geom.computeVertexNormals(); const m=mesh(geom,material,parent,name);m.castShadow=false;return m; } // A continuous surface also fills thresholds, so doors have no visible cracks. const outline=[[0,0],[11.55,0],[11.55,6.525],[13.05,6.525],[13.05,17.4],[3.75,17.4],[3.75,7.125],[0,7.125]]; horizontal(floors,'Podlaha a prahy',outline,.004,tile); horizontal(ceiling,'Souvislý rovný podhled',outline,height,new THREE.MeshStandardMaterial({color:0xf5f3eb,roughness:1,side:THREE.DoubleSide}),true); for(const room of rooms) { const m=horizontal(floors,`${room.name} · ${room.area.toFixed(2)} m²`,room.polygon,.01,room.floor==='wood'?wood:tile); m.userData.roomId=room.id; // Thin joints: plank rhythm in dry rooms, large tiles in service rooms. const loX=Math.min(...room.polygon.map(p=>p[0])),hiX=Math.max(...room.polygon.map(p=>p[0])); const loY=Math.min(...room.polygon.map(p=>p[1])),hiY=Math.max(...room.polygon.map(p=>p[1])); const pts=[]; const inside=(px,py)=>pointInPolygon(room.polygon,px,py); function joint(a,b) { const [x,z]=planPoint(...a),[xx,zz]=planPoint(...b);pts.push(x,.012,z,xx,.012,zz); } for(let py=Math.ceil(loY/(room.floor==='wood'?.20:.6))*(room.floor==='wood'?.20:.6);py{ if(b-a<.001)return; const m=wallBox(fullWalls,axis,fixed,a,b,thickness,0,height,wallMaterial,'Vnitřní příčka'); wallBox(cutWalls,axis,fixed,a,b,thickness,0,cutHeight,wallMaterial,'Vnitřní příčka v řezu'); const sx=axis==='px'?b-a:thickness,sz=axis==='px'?thickness:b-a; wallColliders.push({minX:m.position.x-sx/2,maxX:m.position.x+sx/2,minZ:m.position.z-sz/2,maxZ:m.position.z+sz/2}); for(const parent of [fullWalls,cutWalls])wallBox(parent,axis,fixed,a,b,thickness+.016,.015,.09,trim,'Soklová lišta'); }; for(const [a,b] of holes){ solid(cursor,a); wallBox(fullWalls,axis,fixed,a,b,thickness,doorHead,height,wallMaterial,'Nadpraží vnitřních dveří'); for(const parent of [fullWalls,cutWalls])for(const edge of [a,b])wallBox(parent,axis,fixed,edge-.018,edge+.018,thickness+.035,.01,parent===fullWalls?doorHead:cutHeight,trim,'Obložková zárubeň'); wallBox(fullWalls,axis,fixed,a-.018,b+.018,thickness+.035,doorHead-.04,doorHead,trim,'Horní zárubeň'); cursor=b; } solid(cursor,end); } // Attic access as drawn in vestibule; a flush hatch, not a freestanding object. const [hx,hz]=planPoint(8.9,6.3); box(ceiling,'Půdní výlez 1000 × 700',hx,2.586,hz,.70,.018,1.0,trim); fullWalls.visible=false;ceiling.visible=false;root.visible=false; return {root,floors,fullWalls,cutWalls,ceiling,furniture,rooms,wallColliders,wallMaterial,cutHeight,height}; } export function pointInPolygon(polygon,x,z) { let inside=false; for(let i=0,j=polygon.length-1;iz)!==(b[1]>z)&&x<(b[0]-a[0])*(z-a[1])/(b[1]-a[1])+a[0])inside=!inside; } return inside; }