@adatechnology/game-floorplan-three 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/index.d.ts +34 -1
- package/dist/index.global.js +1079 -16
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +70 -5
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.global.js
CHANGED
|
@@ -42,7 +42,9 @@ var GameFloorplanThree = (() => {
|
|
|
42
42
|
ptBR: () => ptBR,
|
|
43
43
|
rectsOf: () => rectsOf,
|
|
44
44
|
specimenSpec: () => specimenSpec,
|
|
45
|
-
tapSpec: () => tapSpec
|
|
45
|
+
tapSpec: () => tapSpec,
|
|
46
|
+
wallPiece: () => wallPiece,
|
|
47
|
+
wallPlan: () => wallPlan
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
// ../game-props3d/dist/index.js
|
|
@@ -3461,7 +3463,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
3461
3463
|
light(M, x, y, z, color2, range2, kind) {
|
|
3462
3464
|
into(M).push({ kind: "point", position: [x, y, z], data: { light: true, color: color2, range: range2, kind } });
|
|
3463
3465
|
},
|
|
3464
|
-
part(id, pivot, ry,
|
|
3466
|
+
part(id, pivot, ry, draw2) {
|
|
3465
3467
|
let children = partChildren.get(id);
|
|
3466
3468
|
if (!children) {
|
|
3467
3469
|
children = [];
|
|
@@ -3472,7 +3474,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
3472
3474
|
const outer = current;
|
|
3473
3475
|
current = children;
|
|
3474
3476
|
try {
|
|
3475
|
-
|
|
3477
|
+
draw2();
|
|
3476
3478
|
} finally {
|
|
3477
3479
|
current = outer;
|
|
3478
3480
|
}
|
|
@@ -4257,7 +4259,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
4257
4259
|
// src/house/walls.ts
|
|
4258
4260
|
function installHouseWalls(K, shared) {
|
|
4259
4261
|
const { EPS: EPS3, Y0, boxC, bx, cursor, effectiveHeight, pieceRecord, structurePiece, wallColor } = K;
|
|
4260
|
-
function
|
|
4262
|
+
function wallBox2(alongU, L, a, b, ya, yb, th, cL, cH, mL, mH) {
|
|
4261
4263
|
if (yb - ya < 1e-4 || b - a < 1e-4) return;
|
|
4262
4264
|
if (alongU) {
|
|
4263
4265
|
bx(mL, a, ya, L - th / 2, b, yb, L, cL);
|
|
@@ -4324,7 +4326,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
4324
4326
|
continue;
|
|
4325
4327
|
}
|
|
4326
4328
|
const seg = (a, b, ya, yb) => {
|
|
4327
|
-
|
|
4329
|
+
wallBox2(alongU, L, a, b, y0 + ya, y0 + yb, th, sideL.color, sideH.color, sideL.mat, sideH.mat);
|
|
4328
4330
|
};
|
|
4329
4331
|
const win = R.windows.filter((j) => j.floor === f && j.axis === w.axis && Math.abs(j.pos - L) < EPS3 && j.at + j.width / 2 > s0 + EPS3 && j.at - j.width / 2 < s1 - EPS3).sort((p, q) => p.at - q.at);
|
|
4330
4332
|
let cur = s0;
|
|
@@ -4356,13 +4358,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
4356
4358
|
bx(e.color.mat, e.pu - e.th / 2, e.y0, e.pv - e.th / 2, e.pu + e.th / 2, e.y0 + e.hh, e.pv + e.th / 2, e.color.color);
|
|
4357
4359
|
}
|
|
4358
4360
|
}
|
|
4359
|
-
Object.assign(K, { wallBox, sideOf, pieceOf, nearestWall, walls });
|
|
4361
|
+
Object.assign(K, { wallBox: wallBox2, sideOf, pieceOf, nearestWall, walls });
|
|
4360
4362
|
}
|
|
4361
4363
|
|
|
4362
4364
|
// src/house/doors.ts
|
|
4363
4365
|
function installHouseDoors(K, shared) {
|
|
4364
4366
|
const seedKey = (id) => K.seedKey(id);
|
|
4365
|
-
const { EPS: EPS3, Y0, boxC, bx, cursor, effectiveHeight, nearestWall, part, pieceOf, pieceRecord, roomAt, sideOf, wallBox } = K;
|
|
4367
|
+
const { EPS: EPS3, Y0, boxC, bx, cursor, effectiveHeight, nearestWall, part, pieceOf, pieceRecord, roomAt, sideOf, wallBox: wallBox2 } = K;
|
|
4366
4368
|
const SEED = (...a) => K.SEED(...a);
|
|
4367
4369
|
const definePiece = (...a) => K.definePiece(...a);
|
|
4368
4370
|
const rngString = (...a) => K.rngString(...a);
|
|
@@ -4387,7 +4389,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
4387
4389
|
if (!cut && wallHeight > dH + 1e-3) {
|
|
4388
4390
|
const wv = nearestWall(f, p.axis, L, p.at);
|
|
4389
4391
|
cursor.pc = wv ? pieceOf(wv) : null;
|
|
4390
|
-
|
|
4392
|
+
wallBox2(alongU, L, a, b, y0 + dH, y0 + effectiveHeight(f, wallHeight), th, lL.color, lH.color, lL.mat, lH.mat);
|
|
4391
4393
|
cursor.pc = null;
|
|
4392
4394
|
}
|
|
4393
4395
|
const fb = (u0, y_0, u1, y_1, tt = th + 0.02, color2 = frame2) => alongU ? bx("wood", u0, y0 + y_0, L - tt / 2, u1, y0 + y_1, L + tt / 2, color2) : bx("wood", L - tt / 2, y0 + y_0, u0, L + tt / 2, y0 + y_1, u1, color2);
|
|
@@ -5335,8 +5337,8 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
5335
5337
|
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
5336
5338
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
5337
5339
|
};
|
|
5338
|
-
const
|
|
5339
|
-
void
|
|
5340
|
+
const draw2 = rnd() < p, light = rnd() < 0.6;
|
|
5341
|
+
void draw2;
|
|
5340
5342
|
const on = true;
|
|
5341
5343
|
return { on, rnd, color: high ? 3816770 : light ? 15527921 : 10857392, bad: (shared.ctx.colors.meta && shared.ctx.colors.meta.state) === "abandoned" || R.state === "abandoned", fallback: false };
|
|
5342
5344
|
}
|
|
@@ -10893,6 +10895,13 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
10893
10895
|
const flame = flameBase * (1 - rules.flameFillInfluence * (1 - k));
|
|
10894
10896
|
return 1 - (1 - Math.min(1, Math.max(0, spark))) * (1 - Math.min(1, Math.max(0, flame)));
|
|
10895
10897
|
};
|
|
10898
|
+
var KIT_ORIGIN_KEY = "kitOrigin";
|
|
10899
|
+
function markKitOrigin(object2, origin) {
|
|
10900
|
+
const node = object2;
|
|
10901
|
+
node.userData = node.userData ?? {};
|
|
10902
|
+
node.userData[KIT_ORIGIN_KEY] = { ...origin };
|
|
10903
|
+
return object2;
|
|
10904
|
+
}
|
|
10896
10905
|
|
|
10897
10906
|
// ../game-destruction/dist/index.js
|
|
10898
10907
|
var FIRE_PRIME_C = 300;
|
|
@@ -16157,6 +16166,9 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16157
16166
|
};
|
|
16158
16167
|
|
|
16159
16168
|
// ../game-floorplan-structure/dist/index.js
|
|
16169
|
+
var __defProp2 = Object.defineProperty;
|
|
16170
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
16171
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
16160
16172
|
var uniform = (joint) => ({ wall: joint, slab: joint, beam: joint, column: joint, truss: joint });
|
|
16161
16173
|
var CONSTRUCTION_SYSTEMS = /* @__PURE__ */ new Map([
|
|
16162
16174
|
["masonry", { walls: "brick_wall_plastered", joints: { wall: "mortar", slab: "rebar", beam: "mortar", column: "rebar", truss: "bolt" }, slabThickness: 0.12, plateThickness: 0.2, timber: false, wallRole: "rules", wallFill: 1 }],
|
|
@@ -16165,10 +16177,59 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16165
16177
|
["improvised", { walls: "sheet_metal_shack", joints: uniform("nail"), slabThickness: 0.2, plateThickness: 0.1, timber: true, wallRole: "partition", wallFill: 1 }],
|
|
16166
16178
|
["canvas", { walls: "", joints: uniform("tie"), slabThickness: 0, plateThickness: 0, timber: false, wallRole: "partition", wallFill: 1 }]
|
|
16167
16179
|
]);
|
|
16180
|
+
var ROOF_COVERINGS = /* @__PURE__ */ new Map([
|
|
16181
|
+
["colonial", { material: "ceramic", joint: "resting" }],
|
|
16182
|
+
["fiber_cement", { material: "concrete", joint: "bolt" }],
|
|
16183
|
+
["slab", { material: "concrete", joint: "rebar" }],
|
|
16184
|
+
["asphalt", { material: "plastic", joint: "nail" }],
|
|
16185
|
+
["zinc", { material: "steel", joint: "nail" }],
|
|
16186
|
+
["wood", { material: "wood_soft", joint: "nail" }],
|
|
16187
|
+
["canvas", { material: "fabric", joint: "tie" }],
|
|
16188
|
+
["thatch", { material: "foliage", joint: "tie" }]
|
|
16189
|
+
]);
|
|
16190
|
+
var WALL_THICKNESS = 0.15;
|
|
16168
16191
|
var round2 = (value) => Math.round(value * 100) / 100;
|
|
16169
16192
|
function touches(a, b, tolerance) {
|
|
16170
16193
|
return a[0] < b[2] + tolerance && a[2] > b[0] - tolerance && a[1] < b[3] + tolerance && a[3] > b[1] - tolerance;
|
|
16171
16194
|
}
|
|
16195
|
+
function distanceBetween(a, b) {
|
|
16196
|
+
return Math.hypot(Math.max(a[0] - b[2], b[0] - a[2], 0), Math.max(a[1] - b[3], b[1] - a[3], 0));
|
|
16197
|
+
}
|
|
16198
|
+
function unionOf(rects) {
|
|
16199
|
+
const out = [1e9, 1e9, -1e9, -1e9];
|
|
16200
|
+
for (const rect of rects) {
|
|
16201
|
+
out[0] = Math.min(out[0], rect[0]);
|
|
16202
|
+
out[1] = Math.min(out[1], rect[1]);
|
|
16203
|
+
out[2] = Math.max(out[2], rect[2]);
|
|
16204
|
+
out[3] = Math.max(out[3], rect[3]);
|
|
16205
|
+
}
|
|
16206
|
+
return out;
|
|
16207
|
+
}
|
|
16208
|
+
function wallBox(wall) {
|
|
16209
|
+
const half = (wall.thickness ?? WALL_THICKNESS) / 2;
|
|
16210
|
+
return [Math.min(wall.u0, wall.u1) - half, Math.min(wall.v0, wall.v1) - half, Math.max(wall.u0, wall.u1) + half, Math.max(wall.v0, wall.v1) + half];
|
|
16211
|
+
}
|
|
16212
|
+
var SLAB_COVERING = "slab";
|
|
16213
|
+
var PLASTERED_FINISHES = ["plaster", "partial_plaster"];
|
|
16214
|
+
function masonryConstruction(source) {
|
|
16215
|
+
const unit = source.floorNumber !== void 0;
|
|
16216
|
+
const covering = unit ? SLAB_COVERING : source.facade.roof ?? "colonial";
|
|
16217
|
+
const finish = source.facade.finish ?? "roughcast";
|
|
16218
|
+
const budget = source.style === "budget";
|
|
16219
|
+
const flat = unit || covering === SLAB_COVERING;
|
|
16220
|
+
return {
|
|
16221
|
+
system: "masonry",
|
|
16222
|
+
walls: { material: "brick", cladding: PLASTERED_FINISHES.includes(finish) ? "plaster" : null, finish, thickness: WALL_THICKNESS, ...finish === "exposed_block" ? { assembly: "concrete_block_wall" } : {} },
|
|
16223
|
+
floor: { material: budget ? "concrete" : "ceramic", finish: budget ? "burnt_cement" : "ceramic_tile" },
|
|
16224
|
+
roof: { material: ROOF_COVERINGS.get(covering)?.material ?? "ceramic", covering, shape: flat ? "slab" : "gable", pitch: flat ? 0 : covering === "colonial" ? 30 : 15, timber: "wood_soft" },
|
|
16225
|
+
frame: { material: "concrete", pillars: "concrete", beams: "concrete", slabs: "concrete", timber: "wood_soft", rebar: "steel" },
|
|
16226
|
+
foundation: { type: unit ? "building" : "strip_footing", material: "concrete", elevation: 0 }
|
|
16227
|
+
};
|
|
16228
|
+
}
|
|
16229
|
+
function resolveConstruction(source) {
|
|
16230
|
+
const extras = source.structureExtras ?? {};
|
|
16231
|
+
return { construction: source.construction ?? masonryConstruction(source), extras };
|
|
16232
|
+
}
|
|
16172
16233
|
var en_default = {
|
|
16173
16234
|
ui: {
|
|
16174
16235
|
structure_layer: "Structure",
|
|
@@ -16239,8 +16300,950 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
16239
16300
|
};
|
|
16240
16301
|
var DEFAULT_DENSITY = 500;
|
|
16241
16302
|
var densityOf = (material) => DENSITY[material] ?? DEFAULT_DENSITY;
|
|
16303
|
+
var StructureBuilder = class {
|
|
16304
|
+
constructor() {
|
|
16305
|
+
__publicField(this, "parts", []);
|
|
16306
|
+
__publicField(this, "supports", []);
|
|
16307
|
+
__publicField(this, "ids", /* @__PURE__ */ new Set());
|
|
16308
|
+
}
|
|
16309
|
+
add(draft, options = {}) {
|
|
16310
|
+
const { id, role, material, aabb, y0, y1 } = draft;
|
|
16311
|
+
if (this.ids.has(id)) throw new Error(`Part repeated in the structure: ${id}`);
|
|
16312
|
+
this.ids.add(id);
|
|
16313
|
+
const { floor, fill: fill2, ...extras } = options;
|
|
16314
|
+
const volume = Math.max(0, (aabb[2] - aabb[0]) * (aabb[3] - aabb[1]) * (y1 - y0)) * (fill2 ?? 1);
|
|
16315
|
+
const part = {
|
|
16316
|
+
id,
|
|
16317
|
+
role,
|
|
16318
|
+
material,
|
|
16319
|
+
aabb: aabb.map(round2),
|
|
16320
|
+
y0: round2(y0),
|
|
16321
|
+
y1: round2(y1),
|
|
16322
|
+
floor: floor ?? 0,
|
|
16323
|
+
mass: Math.max(1, Math.round(volume * densityOf(material)))
|
|
16324
|
+
};
|
|
16325
|
+
for (const [key, value] of Object.entries(extras)) if (value !== void 0) part[key] = value;
|
|
16326
|
+
this.parts.push(part);
|
|
16327
|
+
return part;
|
|
16328
|
+
}
|
|
16329
|
+
rest(part, on, joint) {
|
|
16330
|
+
const id = typeof part === "string" ? part : part.id;
|
|
16331
|
+
const base = typeof on === "string" ? on : on.id;
|
|
16332
|
+
this.supports.push({ part: id, on: base === GROUND ? GROUND : base, joint });
|
|
16333
|
+
}
|
|
16334
|
+
has(id) {
|
|
16335
|
+
return this.ids.has(id);
|
|
16336
|
+
}
|
|
16337
|
+
find(id) {
|
|
16338
|
+
return this.parts.find((part) => part.id === id);
|
|
16339
|
+
}
|
|
16340
|
+
};
|
|
16341
|
+
function hashLegacyKey(key) {
|
|
16342
|
+
let hash = 1779033703 ^ key.length;
|
|
16343
|
+
for (let i = 0; i < key.length; i++) {
|
|
16344
|
+
hash = Math.imul(hash ^ key.charCodeAt(i), 3432918353);
|
|
16345
|
+
hash = hash << 13 | hash >>> 19;
|
|
16346
|
+
}
|
|
16347
|
+
hash = Math.imul(hash ^ hash >>> 16, 2246822507);
|
|
16348
|
+
hash = Math.imul(hash ^ hash >>> 13, 3266489909);
|
|
16349
|
+
return (hash ^ hash >>> 16) >>> 0;
|
|
16350
|
+
}
|
|
16242
16351
|
var OPEN_ROOM_TYPES2 = ["balcony", "terrace", "porch"];
|
|
16352
|
+
var GUARD_HEIGHT = 1.1;
|
|
16353
|
+
function createContext(source, construction, extras, tank, options) {
|
|
16354
|
+
const system = CONSTRUCTION_SYSTEMS.get(construction.system);
|
|
16355
|
+
if (!system) throw new Error(`Unknown construction system "${construction.system}"`);
|
|
16356
|
+
const floorCount2 = source.floors.length;
|
|
16357
|
+
const isUnit = source.floorNumber !== void 0;
|
|
16358
|
+
const elevation = construction.foundation.elevation || 0;
|
|
16359
|
+
const level = (floor) => round2(elevation + (source.floors[floor]?.y0 ?? 0));
|
|
16360
|
+
const ceilingOf = (floor) => source.floors[floor]?.ceiling ?? 0;
|
|
16361
|
+
const topOf = (floor) => floor < floorCount2 - 1 && !isUnit ? level(floor + 1) - system.slabThickness : level(floor) + ceilingOf(floor);
|
|
16362
|
+
const rectsOf2 = (floor, exclude = []) => source.rooms.filter((room) => room.floor === floor && !exclude.includes(room.type)).flatMap((room) => room.parts);
|
|
16363
|
+
const builder = new StructureBuilder();
|
|
16364
|
+
const context = {
|
|
16365
|
+
source,
|
|
16366
|
+
construction,
|
|
16367
|
+
system,
|
|
16368
|
+
extras,
|
|
16369
|
+
tank,
|
|
16370
|
+
builder,
|
|
16371
|
+
porchRoofOnPostsOnly: options.porchRoofOnPostsOnly,
|
|
16372
|
+
rafterRoof: options.rafterRoof,
|
|
16373
|
+
floorCount: floorCount2,
|
|
16374
|
+
isUnit,
|
|
16375
|
+
elevation,
|
|
16376
|
+
isTimber: system.timber,
|
|
16377
|
+
walls: [],
|
|
16378
|
+
wallParts: /* @__PURE__ */ new Map(),
|
|
16379
|
+
carriers: /* @__PURE__ */ new Map(),
|
|
16380
|
+
plates: /* @__PURE__ */ new Map(),
|
|
16381
|
+
pillars: /* @__PURE__ */ new Map(),
|
|
16382
|
+
porches: /* @__PURE__ */ new Map(),
|
|
16383
|
+
base0: GROUND,
|
|
16384
|
+
houseBox: [0, 0, 0, 0],
|
|
16385
|
+
topBox: [0, 0, 0, 0],
|
|
16386
|
+
hasBasement: false,
|
|
16387
|
+
roof: { shape: "slab", covering: "", material: "", pitch: 0, bearingFloor: 0, panes: [], trusses: 0 },
|
|
16388
|
+
level,
|
|
16389
|
+
ceilingOf,
|
|
16390
|
+
topOf,
|
|
16391
|
+
rectsOf: rectsOf2,
|
|
16392
|
+
baseOf: (floor) => floor === 0 ? context.base0 : builder.find(`slab_${floor}`)
|
|
16393
|
+
};
|
|
16394
|
+
const closed = rectsOf2(0, ["balcony", "porch"]);
|
|
16395
|
+
context.houseBox = unionOf(closed.length ? closed : rectsOf2(0));
|
|
16396
|
+
context.walls = classifyWalls(context);
|
|
16397
|
+
return context;
|
|
16398
|
+
}
|
|
16399
|
+
var isBearingOnTop = (wall, walls) => {
|
|
16400
|
+
const along = (w, pick2) => w.axis === "u" ? pick2(w.u0, w.u1) : pick2(w.v0, w.v1);
|
|
16401
|
+
const across = (w) => w.axis === "u" ? w.v0 : w.u0;
|
|
16402
|
+
return walls.some(
|
|
16403
|
+
(other) => other.floor === wall.floor + 1 && other.type === "wall" && other.axis === wall.axis && Math.abs(across(other) - across(wall)) < 0.2 && Math.min(along(wall, Math.max), along(other, Math.max)) - Math.max(along(wall, Math.min), along(other, Math.min)) > 0.3
|
|
16404
|
+
);
|
|
16405
|
+
};
|
|
16406
|
+
function roleOf(context, wall, walls, porchIds) {
|
|
16407
|
+
if (wall.type === "railing") return "partition";
|
|
16408
|
+
const { wallRole } = context.system;
|
|
16409
|
+
if (wallRole === "bearing") return "wall";
|
|
16410
|
+
if (wallRole === "partition") return "partition";
|
|
16411
|
+
const bearingFloor = context.construction.roof.bearingFloor ?? context.floorCount - 1;
|
|
16412
|
+
if (wall.floor > bearingFloor) return "partition";
|
|
16413
|
+
if (wall.exterior || wall.sides.some((id) => porchIds.has(id))) return "wall";
|
|
16414
|
+
return wall.floor < context.floorCount - 1 && isBearingOnTop(wall, walls) ? "wall" : "partition";
|
|
16415
|
+
}
|
|
16416
|
+
function materialOf(context, wall) {
|
|
16417
|
+
const { walls, guard } = context.construction;
|
|
16418
|
+
if (wall.type === "railing") return guard ?? (context.isTimber ? "wood_soft" : "steel");
|
|
16419
|
+
if (walls.mixed?.length) return walls.mixed[hashLegacyKey(wall.legacyKey) % walls.mixed.length];
|
|
16420
|
+
return wall.exterior ? walls.material : walls.interior ?? walls.material;
|
|
16421
|
+
}
|
|
16422
|
+
function classifyWalls(context) {
|
|
16423
|
+
const countByFloor = /* @__PURE__ */ new Map();
|
|
16424
|
+
const all = context.source.walls.map((wall) => {
|
|
16425
|
+
const index = countByFloor.get(wall.floor) ?? 0;
|
|
16426
|
+
countByFloor.set(wall.floor, index + 1);
|
|
16427
|
+
return { ...wall, id: `wall_${wall.floor}_${index}`, legacyKey: `w${wall.floor}_${index}`, role: "wall", material: "", box: wallBox(wall) };
|
|
16428
|
+
});
|
|
16429
|
+
const built = all.filter((wall) => wall.type === "wall" || wall.type === "railing");
|
|
16430
|
+
const porchIds = new Set(context.source.rooms.filter((room) => room.type === "porch").map((room) => room.id));
|
|
16431
|
+
for (const wall of built) {
|
|
16432
|
+
wall.role = roleOf(context, wall, built, porchIds);
|
|
16433
|
+
wall.material = materialOf(context, wall);
|
|
16434
|
+
}
|
|
16435
|
+
return built;
|
|
16436
|
+
}
|
|
16437
|
+
var guardHeightOf = (wall) => wall.h || GUARD_HEIGHT;
|
|
16438
|
+
function addBalconies(context) {
|
|
16439
|
+
const { builder, construction, system } = context;
|
|
16440
|
+
for (const room of context.source.rooms.filter((r) => r.type === "balcony" && r.floor >= 0)) {
|
|
16441
|
+
const floor = room.floor;
|
|
16442
|
+
const box22 = unionOf(room.parts);
|
|
16443
|
+
const base = floor === 0 ? void 0 : context.baseOf(floor);
|
|
16444
|
+
const deck = builder.add({ id: `slab_${floor}_${room.id}`, role: "slab", material: construction.frame.slabs, aabb: box22, y0: context.level(floor) - system.slabThickness, y1: context.level(floor) }, { floor });
|
|
16445
|
+
if (base) {
|
|
16446
|
+
builder.rest(deck, base, system.joints.slab);
|
|
16447
|
+
continue;
|
|
16448
|
+
}
|
|
16449
|
+
const pillar = builder.add({ id: `balcony_column_${room.id}`, role: "column", material: construction.frame.pillars, aabb: [box22[0] - 0.08, box22[1] - 0.08, box22[0] + 0.08, box22[1] + 0.08], y0: 0, y1: context.level(floor) - system.slabThickness }, { floor: 0 });
|
|
16450
|
+
builder.rest(pillar, GROUND, "rebar");
|
|
16451
|
+
builder.rest(deck, pillar, "rebar");
|
|
16452
|
+
}
|
|
16453
|
+
}
|
|
16454
|
+
var GROUNDSHEET = 0.02;
|
|
16455
|
+
function buildCanvasStructure(construction, extras) {
|
|
16456
|
+
const builder = new StructureBuilder();
|
|
16457
|
+
const panes = [];
|
|
16458
|
+
for (const tent of extras.tents ?? []) {
|
|
16459
|
+
const [u0, v0, u1, v1] = tent.aabb;
|
|
16460
|
+
const centreU = (u0 + u1) / 2;
|
|
16461
|
+
const centreV = (v0 + v1) / 2;
|
|
16462
|
+
const sheet = builder.add({ id: `groundsheet_${tent.id}`, role: "slab", material: "plastic", aabb: tent.aabb, y0: 0, y1: GROUNDSHEET }, { floor: 0, tent: tent.id });
|
|
16463
|
+
builder.rest(sheet, GROUND, "resting");
|
|
16464
|
+
const canvas = builder.add({ id: `canvas_${tent.id}`, role: "roof", material: "fabric", aabb: tent.aabb, y0: GROUNDSHEET, y1: tent.height }, { floor: 0, fill: 0.04, tent: tent.id, form: tent.kind });
|
|
16465
|
+
panes.push(canvas.id);
|
|
16466
|
+
const poles = tent.kind === "tunnel" ? [0.25, 0.75].map((fraction, k) => [[u0 + (u1 - u0) * fraction - 0.015, v0 + 0.05, u0 + (u1 - u0) * fraction + 0.015, v1 - 0.05], k]) : [[[u0 + 0.05, centreV - 0.015, u1 - 0.05, centreV + 0.015], 0], [[centreU - 0.015, v0 + 0.05, centreU + 0.015, v1 - 0.05], 1]];
|
|
16467
|
+
for (const [aabb, k] of poles) {
|
|
16468
|
+
const pole = builder.add({ id: `pole_${tent.id}_${k}`, role: "column", material: construction.frame.pillars, aabb, y0: GROUNDSHEET, y1: tent.height - GROUNDSHEET }, { floor: 0, fill: 0.05, tent: tent.id });
|
|
16469
|
+
builder.rest(pole, GROUND, "resting");
|
|
16470
|
+
builder.rest(canvas, pole, "tie");
|
|
16471
|
+
}
|
|
16472
|
+
const corners = [[u0 - 0.2, v0 - 0.2], [u1 + 0.2, v0 - 0.2], [u0 - 0.2, v1 + 0.2], [u1 + 0.2, v1 + 0.2]];
|
|
16473
|
+
corners.forEach(([u, v], k) => {
|
|
16474
|
+
const peg = builder.add({ id: `peg_${tent.id}_${k}`, role: "column", material: "wood_soft", aabb: [u - 0.02, v - 0.02, u + 0.02, v + 0.02], y0: 0, y1: 0.2 }, { floor: 0 });
|
|
16475
|
+
builder.rest(peg, GROUND, "nail");
|
|
16476
|
+
builder.rest(canvas, peg, "tie");
|
|
16477
|
+
});
|
|
16478
|
+
}
|
|
16479
|
+
const roof = { shape: "tent", covering: "canvas", material: "fabric", pitch: 0, bearingFloor: 0, panes, trusses: 0 };
|
|
16480
|
+
return { parts: builder.parts, supports: builder.supports, levels: [0], elevation: 0, roof, construction, framed: false, openings: [] };
|
|
16481
|
+
}
|
|
16482
|
+
var CHIMNEY_CLEARANCE_ABOVE_RIDGE = 0.6;
|
|
16483
|
+
function addChimney(context) {
|
|
16484
|
+
const chimney = context.extras.chimney;
|
|
16485
|
+
if (!chimney) return;
|
|
16486
|
+
const { builder } = context;
|
|
16487
|
+
const top = context.floorCount - 1;
|
|
16488
|
+
const ridge = context.roof.ridge || context.level(top) + context.ceilingOf(top);
|
|
16489
|
+
const height = Math.max(ridge, chimney.top || 0) + CHIMNEY_CLEARANCE_ABOVE_RIDGE;
|
|
16490
|
+
const part = builder.add({ id: "chimney", role: "column", material: chimney.material || "brick", aabb: chimney.aabb, y0: chimney.flue ? context.level(0) : 0, y1: height }, { floor: 0, fill: chimney.flue ? 0.15 : 0.7 });
|
|
16491
|
+
if (chimney.flue) builder.rest(part, context.base0 ?? GROUND, "resting");
|
|
16492
|
+
else builder.rest(part, GROUND, "mortar");
|
|
16493
|
+
const walls = builder.parts.filter((wall) => wall.role === "wall" && wall.floor === 0 && !wall.id.startsWith("basement") && touches(wall.aabb, part.aabb, 0.1));
|
|
16494
|
+
for (const wall of walls) builder.rest(part, wall, "bolt");
|
|
16495
|
+
chimney.height = round2(height);
|
|
16496
|
+
}
|
|
16497
|
+
function addWell(context) {
|
|
16498
|
+
const well = context.extras.well;
|
|
16499
|
+
if (!well) return;
|
|
16500
|
+
const part = context.builder.add({ id: "well", role: "column", material: "stone", aabb: well.aabb, y0: -0.5, y1: well.height || 0.9 }, { floor: 0, fill: 0.5 });
|
|
16501
|
+
context.builder.rest(part, GROUND, "mortar");
|
|
16502
|
+
}
|
|
16503
|
+
var JOIST_PITCH = 0.6;
|
|
16504
|
+
function addJoists(context, options) {
|
|
16505
|
+
const { floor, box: box22, thickness, carriers, material, joint, suffix = "" } = options;
|
|
16506
|
+
const length = box22[2] - box22[0];
|
|
16507
|
+
const depth = box22[3] - box22[1];
|
|
16508
|
+
const alongU = length >= depth;
|
|
16509
|
+
const span = alongU ? length : depth;
|
|
16510
|
+
const count = Math.max(2, Math.round(span / JOIST_PITCH) + 1);
|
|
16511
|
+
const top = context.level(floor) - thickness * 0.15;
|
|
16512
|
+
const out = [];
|
|
16513
|
+
for (let k = 0; k < count; k++) {
|
|
16514
|
+
const centre = (alongU ? box22[0] : box22[1]) + 0.05 + k * (span - 0.1) / (count - 1);
|
|
16515
|
+
const aabb = alongU ? [centre - 0.025, box22[1], centre + 0.025, box22[3]] : [box22[0], centre - 0.025, box22[2], centre + 0.025];
|
|
16516
|
+
const joist = context.builder.add({ id: `joist_${floor}${suffix}_${k}`, role: "beam", material, aabb, y0: context.level(floor) - thickness, y1: top }, { floor, fill: 1, spacing: JOIST_PITCH });
|
|
16517
|
+
out.push(joist);
|
|
16518
|
+
const below = carriers.filter((carrier) => carrier.y0 <= top + 0.05);
|
|
16519
|
+
const touching = below.filter((carrier) => touches(aabb, carrier.aabb, 0.2));
|
|
16520
|
+
const pool = below.length ? below : carriers;
|
|
16521
|
+
const chosen = touching.length ? touching : pool.slice().sort((a, b) => distanceBetween(aabb, a.aabb) - distanceBetween(aabb, b.aabb)).slice(0, 1);
|
|
16522
|
+
for (const carrier of chosen) context.builder.rest(joist, carrier, joint);
|
|
16523
|
+
}
|
|
16524
|
+
return out;
|
|
16525
|
+
}
|
|
16526
|
+
function addDeck(context, id, material, options, joists) {
|
|
16527
|
+
const { floor, box: box22, thickness, joint } = options;
|
|
16528
|
+
const deck = context.builder.add({ id, role: "slab", material, aabb: box22, y0: context.level(floor) - thickness * 0.15, y1: context.level(floor) }, { floor });
|
|
16529
|
+
for (const joist of joists) context.builder.rest(deck, joist, joint);
|
|
16530
|
+
return deck;
|
|
16531
|
+
}
|
|
16532
|
+
var raft = (context, thickness, material) => {
|
|
16533
|
+
const [u0, v0, u1, v1] = context.houseBox;
|
|
16534
|
+
const part = context.builder.add({ id: "foundation_raft", role: "slab", material, aabb: [u0 - 0.1, v0 - 0.1, u1 + 0.1, v1 + 0.1], y0: 0, y1: thickness }, { floor: 0 });
|
|
16535
|
+
context.builder.rest(part, GROUND, "resting");
|
|
16536
|
+
return part;
|
|
16537
|
+
};
|
|
16538
|
+
function addBuildingBase(context) {
|
|
16539
|
+
const { builder, construction, houseBox: box22 } = context;
|
|
16540
|
+
const corners = [[box22[0], box22[1]], [box22[2], box22[1]], [box22[0], box22[3]], [box22[2], box22[3]]];
|
|
16541
|
+
const top = context.level(0) - context.system.slabThickness;
|
|
16542
|
+
const pillars = corners.map(([u, v], k) => {
|
|
16543
|
+
const pillar = builder.add({ id: `building_column_${k}`, role: "column", material: construction.frame.pillars, aabb: [u - 0.15, v - 0.15, u + 0.15, v + 0.15], y0: 0, y1: top }, { floor: 0 });
|
|
16544
|
+
builder.rest(pillar, GROUND, "rebar");
|
|
16545
|
+
return pillar;
|
|
16546
|
+
});
|
|
16547
|
+
const slab = builder.add({ id: "slab_base", role: "slab", material: construction.frame.slabs, aabb: box22, y0: top, y1: context.level(0) }, { floor: 0, footprint: [box22] });
|
|
16548
|
+
for (const pillar of pillars) builder.rest(slab, pillar, "rebar");
|
|
16549
|
+
context.base0 = slab;
|
|
16550
|
+
}
|
|
16551
|
+
function addBasement(context) {
|
|
16552
|
+
const { builder, houseBox: box22, elevation } = context;
|
|
16553
|
+
const depth = context.construction.foundation.depth || 2.4;
|
|
16554
|
+
const floor = builder.add({ id: "basement_slab", role: "slab", material: "concrete", aabb: box22, y0: -depth - 0.15, y1: -depth }, { floor: -1 });
|
|
16555
|
+
builder.rest(floor, GROUND, "resting");
|
|
16556
|
+
const sides = [
|
|
16557
|
+
["front", [box22[0], box22[1] - 0.1, box22[2], box22[1] + 0.1]],
|
|
16558
|
+
["back", [box22[0], box22[3] - 0.1, box22[2], box22[3] + 0.1]],
|
|
16559
|
+
["left", [box22[0] - 0.1, box22[1], box22[0] + 0.1, box22[3]]],
|
|
16560
|
+
["right", [box22[2] - 0.1, box22[1], box22[2] + 0.1, box22[3]]]
|
|
16561
|
+
];
|
|
16562
|
+
const carried = [];
|
|
16563
|
+
for (const [name, aabb] of sides) {
|
|
16564
|
+
const wall = builder.add({ id: `basement_wall_${name}`, role: "wall", material: "concrete", aabb, y0: -depth, y1: elevation }, { floor: -1 });
|
|
16565
|
+
builder.rest(wall, GROUND, "rebar");
|
|
16566
|
+
builder.rest(wall, floor, "rebar");
|
|
16567
|
+
carried.push(wall);
|
|
16568
|
+
}
|
|
16569
|
+
const count = Math.max(1, Math.floor((box22[2] - box22[0]) / 3));
|
|
16570
|
+
for (let k = 0; k < count; k++) {
|
|
16571
|
+
const u = box22[0] + (k + 0.5) * (box22[2] - box22[0]) / count;
|
|
16572
|
+
const v = (box22[1] + box22[3]) / 2;
|
|
16573
|
+
const column = builder.add({ id: `basement_column_${k}`, role: "column", material: "concrete", aabb: [u - 0.15, v - 0.15, u + 0.15, v + 0.15], y0: -depth, y1: elevation - 0.3 }, { floor: -1 });
|
|
16574
|
+
builder.rest(column, floor, "rebar");
|
|
16575
|
+
builder.rest(column, GROUND, "rebar");
|
|
16576
|
+
carried.push(column);
|
|
16577
|
+
}
|
|
16578
|
+
context.hasBasement = true;
|
|
16579
|
+
return carried;
|
|
16580
|
+
}
|
|
16581
|
+
function addPiers(context) {
|
|
16582
|
+
const { builder, houseBox: box22, elevation, construction } = context;
|
|
16583
|
+
const columns = Math.max(2, Math.ceil((box22[2] - box22[0]) / 2) + 1);
|
|
16584
|
+
const rows = Math.max(2, Math.ceil((box22[3] - box22[1]) / 2) + 1);
|
|
16585
|
+
const height = Math.max(0.1, elevation - context.system.slabThickness - 0.15);
|
|
16586
|
+
const piers = [];
|
|
16587
|
+
for (let j = 0; j < rows; j++) {
|
|
16588
|
+
for (let i = 0; i < columns; i++) {
|
|
16589
|
+
const u = box22[0] + i * (box22[2] - box22[0]) / (columns - 1);
|
|
16590
|
+
const v = box22[1] + j * (box22[3] - box22[1]) / (rows - 1);
|
|
16591
|
+
const pier = builder.add({ id: `footing_pier_${j}_${i}`, role: "column", material: construction.foundation.material || "concrete", aabb: [u - 0.12, v - 0.12, u + 0.12, v + 0.12], y0: 0, y1: height }, { floor: 0 });
|
|
16592
|
+
builder.rest(pier, GROUND, "resting");
|
|
16593
|
+
piers.push(pier);
|
|
16594
|
+
}
|
|
16595
|
+
}
|
|
16596
|
+
const alongU = box22[2] - box22[0] >= box22[3] - box22[1];
|
|
16597
|
+
const beams = [];
|
|
16598
|
+
const lines = alongU ? rows : columns;
|
|
16599
|
+
for (let line = 0; line < lines; line++) {
|
|
16600
|
+
const at2 = alongU ? box22[1] + line * (box22[3] - box22[1]) / (rows - 1) : box22[0] + line * (box22[2] - box22[0]) / (columns - 1);
|
|
16601
|
+
const aabb = alongU ? [box22[0], at2 - 0.07, box22[2], at2 + 0.07] : [at2 - 0.07, box22[1], at2 + 0.07, box22[3]];
|
|
16602
|
+
const beam = builder.add({ id: `base_beam_${line}`, role: "beam", material: construction.frame.timber, aabb, y0: height, y1: elevation - context.system.slabThickness }, { floor: 0 });
|
|
16603
|
+
const centre = (q) => alongU ? (q.aabb[1] + q.aabb[3]) / 2 : (q.aabb[0] + q.aabb[2]) / 2;
|
|
16604
|
+
for (const pier of piers.filter((q) => Math.abs(centre(q) - at2) < 0.05)) builder.rest(beam, pier, "bolt");
|
|
16605
|
+
beams.push(beam);
|
|
16606
|
+
}
|
|
16607
|
+
return beams;
|
|
16608
|
+
}
|
|
16609
|
+
function addRaisedFloor(context) {
|
|
16610
|
+
const { construction, system, houseBox: box22 } = context;
|
|
16611
|
+
const carriers = construction.foundation.type === "basement" ? addBasement(context) : addPiers(context);
|
|
16612
|
+
const thickness = system.slabThickness;
|
|
16613
|
+
const joists = addJoists(context, { floor: 0, box: box22, thickness, carriers, material: construction.frame.timber, joint: construction.foundation.type === "basement" ? "bolt" : "nail" });
|
|
16614
|
+
const deckMaterial = context.isTimber ? construction.floor.material === "wood_hard" ? "wood_hard" : "plywood" : construction.frame.slabs;
|
|
16615
|
+
context.base0 = addDeck(context, "slab_0", deckMaterial, { floor: 0, box: box22, thickness, joint: "nail" }, joists);
|
|
16616
|
+
}
|
|
16617
|
+
function addFoundation(context) {
|
|
16618
|
+
const { type, material } = context.construction.foundation;
|
|
16619
|
+
if (context.isUnit) return addBuildingBase(context);
|
|
16620
|
+
if (type === "raft") context.base0 = raft(context, Math.max(0.1, context.elevation), material || "concrete");
|
|
16621
|
+
else if (type === "crawl_space" || type === "piles" || type === "basement") addRaisedFloor(context);
|
|
16622
|
+
else if (context.isTimber && type !== "ground") context.base0 = raft(context, Math.max(0.12, context.elevation), "concrete");
|
|
16623
|
+
}
|
|
16624
|
+
var MAX_GAP_WITHIN_RUN = 3.2;
|
|
16625
|
+
var POST_SPACING = 2.4;
|
|
16626
|
+
var POST_SIDE = 0.05;
|
|
16627
|
+
var alongOf = (wall, pick2) => wall.axis === "u" ? pick2(wall.u0, wall.u1) : pick2(wall.v0, wall.v1);
|
|
16628
|
+
var acrossOf = (wall) => wall.axis === "u" ? wall.v0 : wall.u0;
|
|
16629
|
+
function runsOf(context, walls, floor) {
|
|
16630
|
+
const lines = /* @__PURE__ */ new Map();
|
|
16631
|
+
for (const wall of walls) {
|
|
16632
|
+
if (!wall.exterior || wall.type !== "wall") continue;
|
|
16633
|
+
const top = round2(context.wallParts.get(wall.id).y1);
|
|
16634
|
+
const key = `${floor}|${wall.axis}|${acrossOf(wall).toFixed(2)}|${top.toFixed(2)}`;
|
|
16635
|
+
lines.set(key, [...lines.get(key) ?? [], wall]);
|
|
16636
|
+
}
|
|
16637
|
+
return [...lines.values()].map((line) => {
|
|
16638
|
+
const sorted = line.slice().sort((a, b) => alongOf(a, Math.min) - alongOf(b, Math.min));
|
|
16639
|
+
const runs = [];
|
|
16640
|
+
for (const wall of sorted) {
|
|
16641
|
+
const from = alongOf(wall, Math.min);
|
|
16642
|
+
const to = alongOf(wall, Math.max);
|
|
16643
|
+
const last = runs[runs.length - 1];
|
|
16644
|
+
if (last && from - last.to <= MAX_GAP_WITHIN_RUN) {
|
|
16645
|
+
last.to = Math.max(last.to, to);
|
|
16646
|
+
last.walls.push(wall);
|
|
16647
|
+
} else runs.push({ from, to, walls: [wall] });
|
|
16648
|
+
}
|
|
16649
|
+
return runs;
|
|
16650
|
+
});
|
|
16651
|
+
}
|
|
16652
|
+
function addShackPosts(context, run, plate, floor, index) {
|
|
16653
|
+
const first = run.walls[0];
|
|
16654
|
+
const across = acrossOf(first);
|
|
16655
|
+
const top = context.wallParts.get(first.id).y1;
|
|
16656
|
+
const count = Math.max(1, Math.ceil((run.to - run.from) / POST_SPACING));
|
|
16657
|
+
const posts = [];
|
|
16658
|
+
for (let k = 0; k <= count; k++) {
|
|
16659
|
+
const along = run.from + (run.to - run.from) * k / count;
|
|
16660
|
+
const [u, v] = first.axis === "u" ? [along, across] : [across, along];
|
|
16661
|
+
const half = 0.05 + POST_SIDE / 2;
|
|
16662
|
+
const post = context.builder.add({ id: `post_${floor}_${index}_${k}`, role: "column", material: "wood_soft", aabb: [u - half, v - half, u + half, v + half], y0: context.level(floor), y1: top }, { floor });
|
|
16663
|
+
const base = context.baseOf(floor);
|
|
16664
|
+
if (base === GROUND || !base) context.builder.rest(post, GROUND, "tie");
|
|
16665
|
+
else context.builder.rest(post, base, "nail");
|
|
16666
|
+
context.builder.rest(plate, post, "nail");
|
|
16667
|
+
posts.push(post);
|
|
16668
|
+
}
|
|
16669
|
+
return posts;
|
|
16670
|
+
}
|
|
16671
|
+
function addPlates(context, walls, floor) {
|
|
16672
|
+
const { builder, system, construction } = context;
|
|
16673
|
+
const plates = [];
|
|
16674
|
+
const pillars = [];
|
|
16675
|
+
let counter = 0;
|
|
16676
|
+
for (const runs of runsOf(context, walls, floor)) {
|
|
16677
|
+
for (const run of runs) {
|
|
16678
|
+
const first = run.walls[0];
|
|
16679
|
+
const half = (first.thickness ?? 0.15) / 2;
|
|
16680
|
+
const across = acrossOf(first);
|
|
16681
|
+
const top = context.wallParts.get(first.id).y1;
|
|
16682
|
+
const aabb = first.axis === "u" ? [run.from - half, across - half, run.to + half, across + half] : [across - half, run.from - half, across + half, run.to + half];
|
|
16683
|
+
const material = context.isTimber ? construction.frame.timber : construction.frame.beams;
|
|
16684
|
+
const plate = builder.add({ id: `plate_${floor}_${counter++}`, role: "beam", material, aabb, y0: top, y1: top + system.plateThickness }, { floor, beamKind: context.isTimber ? "plate" : "bond_beam" });
|
|
16685
|
+
if (construction.system === "improvised") pillars.push(...addShackPosts(context, run, plate, floor, counter));
|
|
16686
|
+
else for (const wall of run.walls) builder.rest(plate, context.wallParts.get(wall.id), system.joints.beam);
|
|
16687
|
+
plates.push(plate);
|
|
16688
|
+
}
|
|
16689
|
+
}
|
|
16690
|
+
return { plates, pillars };
|
|
16691
|
+
}
|
|
16692
|
+
function cornerPoints(walls) {
|
|
16693
|
+
const ends = walls.filter((wall) => wall.exterior && wall.type === "wall").flatMap(
|
|
16694
|
+
(wall) => wall.axis === "u" ? [[Math.min(wall.u0, wall.u1), wall.v0, "u"], [Math.max(wall.u0, wall.u1), wall.v0, "u"]] : [[wall.u0, Math.min(wall.v0, wall.v1), "v"], [wall.u0, Math.max(wall.v0, wall.v1), "v"]]
|
|
16695
|
+
);
|
|
16696
|
+
const points = [];
|
|
16697
|
+
for (const a of ends) {
|
|
16698
|
+
const joinsOtherAxis = ends.some((b) => b[2] !== a[2] && Math.hypot(a[0] - b[0], a[1] - b[1]) < 0.12);
|
|
16699
|
+
if (joinsOtherAxis && !points.some((q) => Math.hypot(q[0] - a[0], q[1] - a[1]) < 0.12)) points.push([a[0], a[1]]);
|
|
16700
|
+
}
|
|
16701
|
+
return points;
|
|
16702
|
+
}
|
|
16703
|
+
function addCornerPillars(context, walls, floor, plates) {
|
|
16704
|
+
const { builder } = context;
|
|
16705
|
+
const topOfFloor = context.topOf(floor);
|
|
16706
|
+
return cornerPoints(walls).map(([u, v], k) => {
|
|
16707
|
+
const box22 = [u - 0.1, v - 0.1, u + 0.1, v + 0.1];
|
|
16708
|
+
const top = Math.max(topOfFloor, ...plates.filter((plate) => touches(plate.aabb, box22, 0.05)).map((plate) => plate.y0));
|
|
16709
|
+
const pillar = builder.add({ id: `column_${floor}_${k}`, role: "column", material: context.construction.frame.pillars, aabb: box22, y0: context.level(floor), y1: top }, { floor });
|
|
16710
|
+
const base = context.baseOf(floor);
|
|
16711
|
+
builder.rest(pillar, base === GROUND || !base ? GROUND : base, "rebar");
|
|
16712
|
+
for (const plate of plates.filter((p) => touches(p.aabb, pillar.aabb, 0.05))) builder.rest(plate, pillar, "rebar");
|
|
16713
|
+
return pillar;
|
|
16714
|
+
});
|
|
16715
|
+
}
|
|
16716
|
+
function addPlatesAndPillars(context, floor) {
|
|
16717
|
+
const walls = context.walls.filter((wall) => wall.floor === floor);
|
|
16718
|
+
const { plates, pillars } = addPlates(context, walls, floor);
|
|
16719
|
+
if (context.construction.system === "masonry") pillars.push(...addCornerPillars(context, walls, floor, plates));
|
|
16720
|
+
context.pillars.set(floor, pillars);
|
|
16721
|
+
context.plates.set(floor, plates);
|
|
16722
|
+
const innerBearing = walls.filter((wall) => wall.role === "wall" && !wall.exterior).map((wall) => context.wallParts.get(wall.id));
|
|
16723
|
+
context.carriers.set(floor, [...plates, ...pillars, ...innerBearing]);
|
|
16724
|
+
}
|
|
16725
|
+
var POST_HALF = 0.08;
|
|
16726
|
+
var FOOTING_HALF = 0.12;
|
|
16727
|
+
var BEAM_DEPTH = 0.2;
|
|
16728
|
+
var BEAM_WIDTH = 0.16;
|
|
16729
|
+
function addPosts(context, porchId, rect, deck, floor) {
|
|
16730
|
+
const { builder, construction } = context;
|
|
16731
|
+
const hasUpperFloor = floor < context.floorCount - 1;
|
|
16732
|
+
const ceilingY = hasUpperFloor ? context.level(floor + 1) - context.system.slabThickness : context.level(floor) + context.ceilingOf(floor);
|
|
16733
|
+
const deckDepth = Math.min(0.2, Math.max(0.06, context.elevation * 0.5));
|
|
16734
|
+
const spots = [[rect[0] + 0.1, rect[1] + 0.1], [rect[2] - 0.1, rect[1] + 0.1]];
|
|
16735
|
+
if (rect[2] - rect[0] > 4.5) spots.push([(rect[0] + rect[2]) / 2, rect[1] + 0.1]);
|
|
16736
|
+
const posts = spots.map(([u, v], k) => {
|
|
16737
|
+
const footing = builder.add({ id: `porch_footing_${porchId}_${k}`, role: "column", material: "concrete", aabb: [u - FOOTING_HALF, v - FOOTING_HALF, u + FOOTING_HALF, v + FOOTING_HALF], y0: 0, y1: context.level(floor) - deckDepth }, { floor: 0 });
|
|
16738
|
+
builder.rest(footing, GROUND, "resting");
|
|
16739
|
+
builder.rest(deck, footing, "resting");
|
|
16740
|
+
const material = context.isTimber ? construction.frame.timber : construction.frame.pillars;
|
|
16741
|
+
const post = builder.add({ id: `porch_post_${porchId}_${k}`, role: "column", material, aabb: [u - POST_HALF, v - POST_HALF, u + POST_HALF, v + POST_HALF], y0: context.level(floor), y1: hasUpperFloor ? ceilingY - BEAM_DEPTH : ceilingY }, { floor });
|
|
16742
|
+
builder.rest(post, deck, context.isTimber ? "nail" : "rebar");
|
|
16743
|
+
return post;
|
|
16744
|
+
});
|
|
16745
|
+
context.porches.set(porchId, { posts, ceilingY });
|
|
16746
|
+
return posts;
|
|
16747
|
+
}
|
|
16748
|
+
function addUpperBeams(context, porchId, rect, posts, floor) {
|
|
16749
|
+
const { builder, construction } = context;
|
|
16750
|
+
const material = context.isTimber ? construction.frame.timber : construction.frame.beams;
|
|
16751
|
+
const joint = context.isTimber ? "nail" : "rebar";
|
|
16752
|
+
const ceilingY = context.porches.get(porchId).ceilingY;
|
|
16753
|
+
const y0 = ceilingY - BEAM_DEPTH;
|
|
16754
|
+
const carriers = context.carriers.get(floor);
|
|
16755
|
+
const head = builder.add({ id: `porch_beam_${porchId}`, role: "beam", material, aabb: [rect[0], rect[1], rect[2], rect[1] + BEAM_WIDTH], y0, y1: ceilingY }, { floor });
|
|
16756
|
+
for (const post of posts) builder.rest(head, post, joint);
|
|
16757
|
+
carriers.push(head);
|
|
16758
|
+
const sides = [["e", [rect[0], rect[1], rect[0] + BEAM_WIDTH, rect[3]], posts[0]], ["d", [rect[2] - BEAM_WIDTH, rect[1], rect[2], rect[3]], posts[1]]];
|
|
16759
|
+
for (const [name, aabb, post] of sides) {
|
|
16760
|
+
const side = builder.add({ id: `porch_beam_${porchId}_${name}`, role: "beam", material, aabb, y0, y1: ceilingY }, { floor });
|
|
16761
|
+
builder.rest(side, post, joint);
|
|
16762
|
+
builder.rest(side, head, joint);
|
|
16763
|
+
for (const wall of context.walls.filter((w) => w.floor === floor && w.role === "wall" && touches(w.box, side.aabb, 0.3))) builder.rest(side, context.wallParts.get(wall.id), joint);
|
|
16764
|
+
carriers.push(side);
|
|
16765
|
+
}
|
|
16766
|
+
}
|
|
16767
|
+
function addPorches(context, floor) {
|
|
16768
|
+
const { builder, construction } = context;
|
|
16769
|
+
for (const room of context.source.rooms.filter((r) => r.type === "porch" && r.floor === floor)) {
|
|
16770
|
+
const rect = room.parts[0];
|
|
16771
|
+
const base = context.baseOf(floor);
|
|
16772
|
+
const deckDepth = Math.min(0.2, Math.max(0.06, context.elevation * 0.5));
|
|
16773
|
+
const deck = builder.add({ id: `porch_floor_${room.id}`, role: "slab", material: context.isTimber ? "wood_soft" : construction.frame.slabs, aabb: rect, y0: context.level(floor) - deckDepth, y1: context.level(floor) }, { floor });
|
|
16774
|
+
if (base && base !== GROUND) builder.rest(deck, base, context.isTimber ? "nail" : context.system.joints.slab);
|
|
16775
|
+
const posts = addPosts(context, room.id, rect, deck, floor);
|
|
16776
|
+
if (floor < context.floorCount - 1) addUpperBeams(context, room.id, rect, posts, floor);
|
|
16777
|
+
}
|
|
16778
|
+
}
|
|
16779
|
+
var ROOF_RISE = 0.6;
|
|
16780
|
+
function addPorchRoofs(context) {
|
|
16781
|
+
const { builder, construction } = context;
|
|
16782
|
+
for (const room of context.source.rooms.filter((r) => r.type === "porch")) {
|
|
16783
|
+
const floor = room.floor;
|
|
16784
|
+
const rect = room.parts[0];
|
|
16785
|
+
const posts = context.porches.get(room.id);
|
|
16786
|
+
if (floor !== context.floorCount - 1 || !posts) continue;
|
|
16787
|
+
const material = ROOF_COVERINGS.get(construction.roof.covering)?.material ?? "ceramic";
|
|
16788
|
+
const roof = builder.add({ id: `porch_roof_${room.id}`, role: "roof", material, aabb: [rect[0] - 0.3, rect[1] - 0.5, rect[2] + 0.3, rect[3]], y0: posts.ceilingY, y1: posts.ceilingY + ROOF_RISE }, { floor });
|
|
16789
|
+
for (const post of posts.posts) builder.rest(roof, post, context.isTimber ? "nail" : "bolt");
|
|
16790
|
+
const housewalls = context.walls.filter((w) => w.floor === floor && !w.exterior && w.sides.includes(room.id) && w.type === "wall");
|
|
16791
|
+
for (const wall of housewalls) {
|
|
16792
|
+
if (context.porchRoofOnPostsOnly) builder.supports.push({ part: roof.id, on: wall.id, joint: "nail", bears: false });
|
|
16793
|
+
else builder.rest(roof, context.wallParts.get(wall.id), "nail");
|
|
16794
|
+
}
|
|
16795
|
+
}
|
|
16796
|
+
}
|
|
16797
|
+
var TRUSS_PITCH = 1.2;
|
|
16798
|
+
var DEFAULT_PITCH = 20;
|
|
16799
|
+
var DEFAULT_EAVE = 0.4;
|
|
16243
16800
|
var CLOSED_ONLY = [...OPEN_ROOM_TYPES2, "void"];
|
|
16801
|
+
function roofUnits(context, bearing) {
|
|
16802
|
+
const units = [{ suffix: "", box: context.topBox, floor: bearing, annex: false }];
|
|
16803
|
+
if (context.floorCount <= 1 || context.isUnit || bearing !== context.floorCount - 1) return units;
|
|
16804
|
+
for (let floor = 0; floor < context.floorCount - 1; floor++) {
|
|
16805
|
+
const above = context.source.rooms.filter((room) => room.floor === floor + 1).flatMap((room) => room.parts);
|
|
16806
|
+
const rooms = context.source.rooms.filter((room) => room.floor === floor && !OPEN_ROOM_TYPES2.includes(room.type) && room.type !== "void" && room.type !== "elevator");
|
|
16807
|
+
for (const room of rooms) {
|
|
16808
|
+
const area3 = room.parts.reduce((total, part) => total + (part[2] - part[0]) * (part[3] - part[1]), 0);
|
|
16809
|
+
const covered = room.parts.reduce((total, part) => total + above.reduce((sum, other) => sum + Math.max(0, Math.min(part[2], other[2]) - Math.max(part[0], other[0])) * Math.max(0, Math.min(part[3], other[3]) - Math.max(part[1], other[1])), 0), 0);
|
|
16810
|
+
if (covered < area3 * 0.7) units.push({ suffix: `_${room.id}`, box: unionOf(room.parts), floor, annex: true });
|
|
16811
|
+
}
|
|
16812
|
+
}
|
|
16813
|
+
return units;
|
|
16814
|
+
}
|
|
16815
|
+
function roofCarriers(context, unit) {
|
|
16816
|
+
const platesHere = context.plates.get(unit.floor) ?? [];
|
|
16817
|
+
const allPlates = [...context.plates.values()].flat();
|
|
16818
|
+
const eaveHeight = platesHere.length ? Math.max(...platesHere.map((plate) => plate.y1)) : 0;
|
|
16819
|
+
const plates = allPlates.filter((plate) => Math.abs(plate.y1 - eaveHeight) < 0.4 && touches(plate.aabb, unit.box, 0.3));
|
|
16820
|
+
const pillars = (context.pillars.get(unit.floor) ?? []).filter((pillar) => touches(pillar.aabb, unit.box, 0.3));
|
|
16821
|
+
const walls = context.walls.filter((wall) => wall.floor === unit.floor && wall.role === "wall" && !wall.exterior && touches(wall.box, unit.box, 0.2)).map((wall) => context.wallParts.get(wall.id));
|
|
16822
|
+
return [...plates, ...pillars, ...walls];
|
|
16823
|
+
}
|
|
16824
|
+
function addFlatRoof(context, unit, carriers) {
|
|
16825
|
+
const { builder, construction, system } = context;
|
|
16826
|
+
const top = context.level(unit.floor) + context.ceilingOf(unit.floor);
|
|
16827
|
+
const [u0, v0, u1, v1] = unit.box;
|
|
16828
|
+
const slab = builder.add({ id: `slab_top${unit.suffix}`, role: "slab", material: construction.frame.slabs, aabb: [u0 - 0.15, v0 - 0.15, u1 + 0.15, v1 + 0.15], y0: top, y1: top + 0.12 }, { floor: unit.floor, cover: true, footprint: [unit.box] });
|
|
16829
|
+
for (const carrier of carriers.length ? carriers : context.plates.get(unit.floor) ?? []) builder.rest(slab, carrier, system.joints.slab);
|
|
16830
|
+
if (unit.suffix === "") context.topSlab = slab;
|
|
16831
|
+
return { id: slab.id, aabb: slab.aabb, y0: slab.y0, y1: slab.y1, flat: true };
|
|
16832
|
+
}
|
|
16833
|
+
function addTrusses(context, unit, carriers, geometry) {
|
|
16834
|
+
const { builder, construction, system } = context;
|
|
16835
|
+
const { box: box22 } = unit;
|
|
16836
|
+
const { axis, length, eave, rise, eaveY } = geometry;
|
|
16837
|
+
const count = Math.max(2, Math.ceil(length / TRUSS_PITCH) + 1);
|
|
16838
|
+
const trusses = [];
|
|
16839
|
+
for (let k = 0; k < count; k++) {
|
|
16840
|
+
const at2 = (axis === "u" ? box22[0] : box22[1]) + k * length / (count - 1);
|
|
16841
|
+
const aabb = axis === "u" ? [at2 - 0.04, box22[1] - eave * 0.5, at2 + 0.04, box22[3] + eave * 0.5] : [box22[0] - eave * 0.5, at2 - 0.04, box22[2] + eave * 0.5, at2 + 0.04];
|
|
16842
|
+
const truss = builder.add({ id: `truss${unit.suffix}_${k}`, role: "beam", material: construction.frame.timber, aabb, y0: eaveY, y1: eaveY + rise }, { floor: unit.floor, fill: 0.25 });
|
|
16843
|
+
const touching = carriers.filter((carrier) => touches(aabb, carrier.aabb, 0.2) && carrier.role !== "slab");
|
|
16844
|
+
const chosen = touching.length ? touching : carriers.slice().sort((a, b) => distanceBetween(aabb, a.aabb) - distanceBetween(aabb, b.aabb)).slice(0, 2);
|
|
16845
|
+
for (const carrier of chosen) builder.rest(truss, carrier, system.joints.truss);
|
|
16846
|
+
trusses.push(truss);
|
|
16847
|
+
}
|
|
16848
|
+
return trusses;
|
|
16849
|
+
}
|
|
16850
|
+
function geometryOf(context, unit) {
|
|
16851
|
+
const { box: box22 } = unit;
|
|
16852
|
+
const roof = context.construction.roof;
|
|
16853
|
+
const platesHere = context.plates.get(unit.floor) ?? [];
|
|
16854
|
+
const width = box22[2] - box22[0];
|
|
16855
|
+
const depth = box22[3] - box22[1];
|
|
16856
|
+
const axis = roof.axis ?? (width >= depth ? "u" : "v");
|
|
16857
|
+
const span = axis === "u" ? depth : width;
|
|
16858
|
+
const pitch = unit.annex ? Math.min(roof.pitch || DEFAULT_PITCH, 20) : roof.pitch || DEFAULT_PITCH;
|
|
16859
|
+
const eaveY = platesHere.length ? Math.max(...platesHere.map((plate) => plate.y1)) : context.level(unit.floor) + context.ceilingOf(unit.floor);
|
|
16860
|
+
return { axis, length: axis === "u" ? width : depth, eave: roof.eave ?? DEFAULT_EAVE, rise: round2(span / 2 * Math.tan(pitch * Math.PI / 180)), eaveY };
|
|
16861
|
+
}
|
|
16862
|
+
function addRafters(context, unit, carriers, geometry) {
|
|
16863
|
+
const { builder, construction, system } = context;
|
|
16864
|
+
const { box: box22 } = unit;
|
|
16865
|
+
const { axis, length, eave, rise, eaveY } = geometry;
|
|
16866
|
+
const count = Math.max(2, Math.ceil(length / TRUSS_PITCH) + 1);
|
|
16867
|
+
const middle = axis === "u" ? (box22[1] + box22[3]) / 2 : (box22[0] + box22[2]) / 2;
|
|
16868
|
+
const frames = { all: [], sideA: [], sideB: [] };
|
|
16869
|
+
for (let k = 0; k < count; k++) {
|
|
16870
|
+
const at2 = (axis === "u" ? box22[0] : box22[1]) + k * length / (count - 1);
|
|
16871
|
+
const halves = axis === "u" ? [["a", [at2 - 0.04, box22[1] - eave * 0.5, at2 + 0.04, middle]], ["b", [at2 - 0.04, middle, at2 + 0.04, box22[3] + eave * 0.5]]] : [["a", [box22[0] - eave * 0.5, at2 - 0.04, middle, at2 + 0.04]], ["b", [middle, at2 - 0.04, box22[2] + eave * 0.5, at2 + 0.04]]];
|
|
16872
|
+
for (const [side, aabb] of halves) {
|
|
16873
|
+
const rafter = builder.add({ id: `rafter${unit.suffix}_${side}_${k}`, role: "beam", material: construction.frame.timber, aabb, y0: eaveY, y1: eaveY + rise }, { floor: unit.floor, fill: 0.25 });
|
|
16874
|
+
const touching = carriers.filter((carrier) => touches(aabb, carrier.aabb, 0.2) && carrier.role !== "slab");
|
|
16875
|
+
const chosen = touching.length ? touching : carriers.slice().sort((x, y) => distanceBetween(aabb, x.aabb) - distanceBetween(aabb, y.aabb)).slice(0, 2);
|
|
16876
|
+
for (const carrier of chosen) builder.rest(rafter, carrier, system.joints.truss);
|
|
16877
|
+
frames.all.push(rafter);
|
|
16878
|
+
if (side === "a") frames.sideA.push(rafter);
|
|
16879
|
+
else frames.sideB.push(rafter);
|
|
16880
|
+
}
|
|
16881
|
+
}
|
|
16882
|
+
if (!unit.annex) {
|
|
16883
|
+
const ridgeBox = axis === "u" ? [box22[0] - eave, middle - 0.05, box22[2] + eave, middle + 0.05] : [middle - 0.05, box22[1] - eave, middle + 0.05, box22[3] + eave];
|
|
16884
|
+
const ridge = builder.add({ id: `ridge${unit.suffix}`, role: "beam", material: construction.frame.timber, aabb: ridgeBox, y0: eaveY + rise - 0.12, y1: eaveY + rise }, { floor: unit.floor });
|
|
16885
|
+
for (const rafter of frames.all) builder.rest(ridge, rafter, system.joints.truss);
|
|
16886
|
+
}
|
|
16887
|
+
context.roof.trusses += count;
|
|
16888
|
+
return frames;
|
|
16889
|
+
}
|
|
16890
|
+
function panesOf(unit, geometry) {
|
|
16891
|
+
const [u0, v0, u1, v1] = unit.box;
|
|
16892
|
+
const { eave, axis } = geometry;
|
|
16893
|
+
if (unit.annex) return [[u0 - eave, v0 - eave, u1 + eave, v1 + eave]];
|
|
16894
|
+
const midV = (v0 + v1) / 2;
|
|
16895
|
+
const midU = (u0 + u1) / 2;
|
|
16896
|
+
return axis === "u" ? [[u0 - eave, v0 - eave, u1 + eave, midV], [u0 - eave, midV, u1 + eave, v1 + eave]] : [[u0 - eave, v0 - eave, midU, v1 + eave], [midU, v0 - eave, u1 + eave, v1 + eave]];
|
|
16897
|
+
}
|
|
16898
|
+
function coarseFrames(context, unit, carriers, geometry) {
|
|
16899
|
+
const trusses = addTrusses(context, unit, carriers, geometry);
|
|
16900
|
+
context.roof.trusses += trusses.length;
|
|
16901
|
+
return { all: trusses, sideA: trusses, sideB: trusses };
|
|
16902
|
+
}
|
|
16903
|
+
function addGableRoof(context, unit, carriers, covering) {
|
|
16904
|
+
const { builder, construction } = context;
|
|
16905
|
+
const geometry = geometryOf(context, unit);
|
|
16906
|
+
const frames = context.rafterRoof ? addRafters(context, unit, carriers, geometry) : coarseFrames(context, unit, carriers, geometry);
|
|
16907
|
+
const panes = [];
|
|
16908
|
+
panesOf(unit, geometry).forEach((aabb, k) => {
|
|
16909
|
+
const name = unit.annex ? "annex" : k === 0 ? "a" : "b";
|
|
16910
|
+
const options = { floor: unit.floor, fill: construction.roof.covering === "colonial" ? 0.12 : 0.05, ...construction.roof.pitch === void 0 ? {} : { pitch: construction.roof.pitch } };
|
|
16911
|
+
const pane = builder.add({ id: `roof${unit.suffix}_${name}`, role: "roof", material: construction.roof.material, aabb, y0: geometry.eaveY, y1: geometry.eaveY + geometry.rise + 0.05 }, options);
|
|
16912
|
+
const carried = unit.annex || !context.rafterRoof ? frames.all : k === 0 ? frames.sideA : frames.sideB;
|
|
16913
|
+
for (const frame2 of carried) builder.rest(pane, frame2, covering);
|
|
16914
|
+
panes.push({ id: pane.id, aabb: pane.aabb, y0: pane.y0, y1: pane.y1 });
|
|
16915
|
+
});
|
|
16916
|
+
if (!unit.annex) {
|
|
16917
|
+
const roof = context.roof;
|
|
16918
|
+
roof.axis = geometry.axis;
|
|
16919
|
+
roof.ridge = round2(geometry.eaveY + geometry.rise);
|
|
16920
|
+
roof.eave = geometry.eave;
|
|
16921
|
+
roof.y0 = round2(geometry.eaveY);
|
|
16922
|
+
roof.aabb = [unit.box[0] - geometry.eave, unit.box[1] - geometry.eave, unit.box[2] + geometry.eave, unit.box[3] + geometry.eave].map(round2);
|
|
16923
|
+
}
|
|
16924
|
+
return panes;
|
|
16925
|
+
}
|
|
16926
|
+
function addCeilingJoists(context) {
|
|
16927
|
+
const { builder, construction } = context;
|
|
16928
|
+
const bearing = context.roof.bearingFloor;
|
|
16929
|
+
const rects = context.rectsOf(bearing, CLOSED_ONLY);
|
|
16930
|
+
const box22 = unionOf(rects.length ? rects : [context.topBox]);
|
|
16931
|
+
const carriers = [...context.plates.get(bearing) ?? [], ...context.walls.filter((w) => w.floor === bearing && w.role === "wall" && !w.exterior).map((w) => context.wallParts.get(w.id))];
|
|
16932
|
+
const length = box22[2] - box22[0];
|
|
16933
|
+
const depth = box22[3] - box22[1];
|
|
16934
|
+
const alongU = length >= depth;
|
|
16935
|
+
const span = alongU ? length : depth;
|
|
16936
|
+
const count = Math.max(2, Math.round(span / 0.6) + 1);
|
|
16937
|
+
const beamTops = carriers.filter((carrier) => carrier.role === "beam").map((carrier) => carrier.y0);
|
|
16938
|
+
const y0 = carriers.length ? Math.max(...beamTops, context.level(bearing) + 1) : context.topOf(bearing);
|
|
16939
|
+
const joists = [];
|
|
16940
|
+
for (let k = 0; k < count; k++) {
|
|
16941
|
+
const at2 = (alongU ? box22[0] : box22[1]) + 0.05 + k * (span - 0.1) / (count - 1);
|
|
16942
|
+
const aabb = alongU ? [at2 - 0.025, box22[1], at2 + 0.025, box22[3]] : [box22[0], at2 - 0.025, box22[2], at2 + 0.025];
|
|
16943
|
+
const joist = builder.add({ id: `ceiling_joist_${k}`, role: "beam", material: construction.frame.timber, aabb, y0: y0 - 0.02, y1: y0 + context.system.plateThickness - 0.02 }, { floor: bearing, spacing: 0.6 });
|
|
16944
|
+
const beams = carriers.filter((carrier) => carrier.role === "beam" && touches(aabb, carrier.aabb, 0.2));
|
|
16945
|
+
for (const carrier of beams.length ? beams : carriers.slice(0, 2)) builder.rest(joist, carrier, "nail");
|
|
16946
|
+
joists.push(joist);
|
|
16947
|
+
}
|
|
16948
|
+
context.ceilingJoists = joists;
|
|
16949
|
+
}
|
|
16950
|
+
function addRoof(context) {
|
|
16951
|
+
const { construction } = context;
|
|
16952
|
+
const covering = ROOF_COVERINGS.get(construction.roof.covering);
|
|
16953
|
+
const bearing = construction.roof.bearingFloor ?? context.floorCount - 1;
|
|
16954
|
+
const open = context.rectsOf(context.floorCount - 1, OPEN_ROOM_TYPES2);
|
|
16955
|
+
const topRects = context.rectsOf(context.floorCount - 1, CLOSED_ONLY);
|
|
16956
|
+
const body = unionOf(open.length ? open : topRects);
|
|
16957
|
+
context.topBox = context.floorCount - 1 === bearing ? body : unionOf(context.rectsOf(bearing, OPEN_ROOM_TYPES2));
|
|
16958
|
+
context.roof = { shape: construction.roof.shape, covering: construction.roof.covering, material: construction.roof.material, pitch: construction.roof.pitch || 0, bearingFloor: bearing, panes: [], trusses: 0 };
|
|
16959
|
+
for (const unit of roofUnits(context, bearing)) {
|
|
16960
|
+
const carriers = roofCarriers(context, unit);
|
|
16961
|
+
if (construction.roof.shape === "slab") context.roof.panes.push(addFlatRoof(context, unit, carriers));
|
|
16962
|
+
else context.roof.panes.push(...addGableRoof(context, unit, carriers, covering?.joint ?? "nail"));
|
|
16963
|
+
}
|
|
16964
|
+
if (context.isTimber && construction.roof.shape !== "slab") addCeilingJoists(context);
|
|
16965
|
+
}
|
|
16966
|
+
function flightsOf(items) {
|
|
16967
|
+
const flights = /* @__PURE__ */ new Map();
|
|
16968
|
+
for (const item of items.filter((it) => it.category === "stairs" && it.link)) {
|
|
16969
|
+
const key = `${item.link.from}_${item.link.to}`;
|
|
16970
|
+
if (!flights.has(key) || item.floor === item.link.from) flights.set(key, item);
|
|
16971
|
+
}
|
|
16972
|
+
return flights;
|
|
16973
|
+
}
|
|
16974
|
+
function addStairs(context) {
|
|
16975
|
+
const { builder, construction } = context;
|
|
16976
|
+
const material = context.isTimber ? construction.frame.timber : construction.stairs ?? "concrete";
|
|
16977
|
+
for (const [key, item2] of flightsOf(context.source.items)) {
|
|
16978
|
+
const { from, to } = item2.link;
|
|
16979
|
+
const flight2 = builder.add({ id: `stairs_${key}`, role: "stair", material, aabb: item2.aabb, y0: context.level(from), y1: context.level(to) }, { floor: from, steps: Math.round((context.level(to) - context.level(from)) / 0.18) });
|
|
16980
|
+
const base = context.baseOf(from);
|
|
16981
|
+
builder.rest(flight2, !base || base === GROUND ? GROUND : base, !base || base === GROUND ? "resting" : context.isTimber ? "nail" : "mortar");
|
|
16982
|
+
const upper = context.baseOf(to);
|
|
16983
|
+
if (upper && upper !== GROUND) builder.rest(flight2, upper, context.isTimber ? "nail" : "bolt");
|
|
16984
|
+
}
|
|
16985
|
+
if (!context.hasBasement) return;
|
|
16986
|
+
const item = context.source.items.find((it) => it.type === "basement_stairs");
|
|
16987
|
+
if (!item?.aabb) return;
|
|
16988
|
+
const depth = construction.foundation.depth || 2.4;
|
|
16989
|
+
const flight = builder.add({ id: "basement_stairs", role: "stair", material: "wood_soft", aabb: item.aabb, y0: -depth, y1: context.level(0) }, { floor: -1, steps: Math.round((context.level(0) + depth) / 0.18) });
|
|
16990
|
+
builder.rest(flight, builder.find("basement_slab"), "resting");
|
|
16991
|
+
if (context.base0 && context.base0 !== GROUND) builder.rest(flight, context.base0, "nail");
|
|
16992
|
+
}
|
|
16993
|
+
var TOLERANCE = 0.02;
|
|
16994
|
+
var DOOR_HEIGHT = 2.1;
|
|
16995
|
+
var LINTEL_DEPTH = 0.2;
|
|
16996
|
+
var LINTEL_BEARING = 0.2;
|
|
16997
|
+
var crossOf = (wall) => wall.axis === "u" ? wall.v0 : wall.u0;
|
|
16998
|
+
var alongRange = (wall) => wall.axis === "u" ? [Math.min(wall.u0, wall.u1), Math.max(wall.u0, wall.u1)] : [Math.min(wall.v0, wall.v1), Math.max(wall.v0, wall.v1)];
|
|
16999
|
+
function findOpenings(context) {
|
|
17000
|
+
const openings = [];
|
|
17001
|
+
for (const window of context.source.windows ?? []) {
|
|
17002
|
+
const from = window.at - window.width / 2;
|
|
17003
|
+
const to = window.at + window.width / 2;
|
|
17004
|
+
const wall = context.walls.find((w) => {
|
|
17005
|
+
if (w.floor !== window.floor || w.axis !== window.axis || w.type !== "wall" || Math.abs(crossOf(w) - window.pos) > TOLERANCE) return false;
|
|
17006
|
+
const [low, high] = alongRange(w);
|
|
17007
|
+
return from >= low - TOLERANCE && to <= high + TOLERANCE;
|
|
17008
|
+
});
|
|
17009
|
+
if (!wall) continue;
|
|
17010
|
+
const base = context.level(window.floor);
|
|
17011
|
+
openings.push({ id: window.id, kind: "window", wall: wall.id, from, to, y0: base + window.y0, y1: base + window.y1 });
|
|
17012
|
+
}
|
|
17013
|
+
return openings;
|
|
17014
|
+
}
|
|
17015
|
+
function addDoorLintels(context) {
|
|
17016
|
+
const { builder, construction, system } = context;
|
|
17017
|
+
for (const door of context.source.doors.filter((d) => d.a !== void 0 && d.type !== "zipper")) {
|
|
17018
|
+
const along = door.axis === "u" ? door.u : door.v;
|
|
17019
|
+
const cross = door.axis === "u" ? door.v : door.u;
|
|
17020
|
+
const from = along - door.width / 2;
|
|
17021
|
+
const to = along + door.width / 2;
|
|
17022
|
+
const beside = context.walls.filter((w) => w.floor === door.floor && w.axis === door.axis && w.type === "wall" && Math.abs(crossOf(w) - cross) <= TOLERANCE && (Math.abs(alongRange(w)[1] - from) <= 0.05 || Math.abs(alongRange(w)[0] - to) <= 0.05));
|
|
17023
|
+
if (!beside.length) continue;
|
|
17024
|
+
const half = (beside[0].thickness ?? 0.15) / 2;
|
|
17025
|
+
const lo = from - LINTEL_BEARING;
|
|
17026
|
+
const hi = to + LINTEL_BEARING;
|
|
17027
|
+
const box22 = door.axis === "u" ? [lo, cross - half, hi, cross + half] : [cross - half, lo, cross + half, hi];
|
|
17028
|
+
const y0 = context.level(door.floor) + DOOR_HEIGHT;
|
|
17029
|
+
const lintel = builder.add({ id: `lintel_${door.id}`, role: "beam", material: system.timber ? construction.frame.timber : "concrete", aabb: box22, y0, y1: y0 + LINTEL_DEPTH }, { floor: door.floor, ...system.timber ? {} : { assembly: "reinforced_concrete" } });
|
|
17030
|
+
for (const wall of beside) builder.rest(lintel, context.wallParts.get(wall.id), system.joints.beam);
|
|
17031
|
+
for (const plate of context.plates.get(door.floor) ?? []) if (overlapsInPlan(plate.aabb, box22)) builder.rest(plate, lintel, system.joints.beam);
|
|
17032
|
+
}
|
|
17033
|
+
}
|
|
17034
|
+
var overlapsInPlan = (a, b) => a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1];
|
|
17035
|
+
var RISER = 0.18;
|
|
17036
|
+
var TREAD = 0.28;
|
|
17037
|
+
var stepCount = (elevation) => Math.max(1, Math.ceil(elevation / RISER));
|
|
17038
|
+
function addPorchSteps(context) {
|
|
17039
|
+
const { builder } = context;
|
|
17040
|
+
for (const room of context.source.rooms.filter((r) => r.type === "porch")) {
|
|
17041
|
+
const deck = builder.find(`porch_floor_${room.id}`);
|
|
17042
|
+
for (const door of context.source.doors.filter((d) => d.floor === room.floor && d.b === "outside" && d.a === room.id)) {
|
|
17043
|
+
const count = stepCount(context.elevation);
|
|
17044
|
+
const depth = count * TREAD;
|
|
17045
|
+
const aabb = [door.u - door.width / 2, door.v - depth, door.u + door.width / 2, door.v];
|
|
17046
|
+
const flight = builder.add({ id: `steps_${door.id}`, role: "stair", material: context.isTimber ? "wood_soft" : "concrete", aabb, y0: 0, y1: context.level(room.floor) }, { floor: 0, steps: count, door: door.id });
|
|
17047
|
+
builder.rest(flight, GROUND, "resting");
|
|
17048
|
+
if (deck) builder.rest(flight, deck, context.isTimber ? "nail" : "mortar");
|
|
17049
|
+
}
|
|
17050
|
+
}
|
|
17051
|
+
}
|
|
17052
|
+
function boxOfSteps(door, depth) {
|
|
17053
|
+
const direction = door.axis === "u" ? door.side === "front" ? -1 : 1 : door.side === "left" ? -1 : 1;
|
|
17054
|
+
if (door.axis === "u") return [door.u - door.width / 2, Math.min(door.v, door.v + direction * depth), door.u + door.width / 2, Math.max(door.v, door.v + direction * depth)];
|
|
17055
|
+
return [Math.min(door.u, door.u + direction * depth), door.v - door.width / 2, Math.max(door.u, door.u + direction * depth), door.v + door.width / 2];
|
|
17056
|
+
}
|
|
17057
|
+
function addDoorSteps(context) {
|
|
17058
|
+
if (context.elevation <= 0.1) return;
|
|
17059
|
+
const { builder } = context;
|
|
17060
|
+
const doors = context.source.doors.filter((d) => d.floor === 0 && d.exterior && d.model !== "steel" && d.type !== "steps" && d.type !== "zipper");
|
|
17061
|
+
for (const door of doors) {
|
|
17062
|
+
const count = stepCount(context.elevation);
|
|
17063
|
+
const depth = Math.min(TREAD * count, 0.9);
|
|
17064
|
+
const flight = builder.add({ id: `steps_${door.id}`, role: "stair", material: context.isTimber ? "wood_soft" : "concrete", aabb: boxOfSteps(door, depth), y0: 0, y1: context.level(0) }, { floor: 0, steps: count, door: door.id });
|
|
17065
|
+
builder.rest(flight, GROUND, "resting");
|
|
17066
|
+
const base = context.base0;
|
|
17067
|
+
if (base && base !== GROUND) builder.rest(flight, base, context.isTimber ? "nail" : "mortar");
|
|
17068
|
+
}
|
|
17069
|
+
}
|
|
17070
|
+
function addExteriorSteps(context) {
|
|
17071
|
+
addPorchSteps(context);
|
|
17072
|
+
addDoorSteps(context);
|
|
17073
|
+
}
|
|
17074
|
+
var TANK_FILL = 0.08;
|
|
17075
|
+
var squareAround = (u, v, side) => [u - side / 2, v - side / 2, u + side / 2, v + side / 2];
|
|
17076
|
+
function addTank(context, tank, floor, y0) {
|
|
17077
|
+
return context.builder.add({ id: "water_tank", role: "none", material: tank.material ?? "plastic", aabb: tank.aabb, y0, y1: y0 + tank.h }, { floor, fill: TANK_FILL, liters: tank.capacity });
|
|
17078
|
+
}
|
|
17079
|
+
function ensureCeilingSlab(context, y) {
|
|
17080
|
+
const { builder, construction, system } = context;
|
|
17081
|
+
const existing = builder.find("slab_ceiling");
|
|
17082
|
+
if (existing) return existing;
|
|
17083
|
+
const top = context.floorCount - 1;
|
|
17084
|
+
const box22 = context.topBox;
|
|
17085
|
+
const carriers = builder.parts.filter(
|
|
17086
|
+
(part) => (part.role === "beam" && part.id.startsWith(`plate_${top}_`) || part.role === "column" && part.floor === top || part.role === "wall" && part.floor === top && !part.id.startsWith("basement")) && touches(box22, part.aabb, 0.1)
|
|
17087
|
+
);
|
|
17088
|
+
const slab = builder.add({ id: "slab_ceiling", role: "slab", material: construction.frame.slabs, aabb: box22, y0: y - 0.12, y1: y }, { floor: top, footprint: [box22] });
|
|
17089
|
+
for (const carrier of carriers) builder.rest(slab, carrier, system.joints.slab);
|
|
17090
|
+
return slab;
|
|
17091
|
+
}
|
|
17092
|
+
function addRaisedPlatform(context, tank) {
|
|
17093
|
+
const { builder, construction } = context;
|
|
17094
|
+
const platform = tank.platform;
|
|
17095
|
+
const top = context.floorCount - 1;
|
|
17096
|
+
const ceiling = ensureCeilingSlab(context, platform.baseY);
|
|
17097
|
+
const pillars = platform.pillars.map((q, k) => {
|
|
17098
|
+
const pillar = builder.add({ id: `tank_column_${k}`, role: "column", material: construction.frame.pillars, aabb: squareAround(q.u, q.v, q.side), y0: platform.baseY, y1: platform.topY - platform.thickness }, { floor: top });
|
|
17099
|
+
builder.rest(pillar, ceiling, "rebar");
|
|
17100
|
+
return pillar;
|
|
17101
|
+
});
|
|
17102
|
+
const slab = builder.add({ id: "tank_slab", role: "slab", material: construction.frame.slabs, aabb: platform.aabb, y0: platform.topY - platform.thickness, y1: platform.topY }, { floor: top });
|
|
17103
|
+
for (const pillar of pillars) builder.rest(slab, pillar, "rebar");
|
|
17104
|
+
builder.rest(addTank(context, tank, top, tank.y), slab, "resting");
|
|
17105
|
+
}
|
|
17106
|
+
function addAtticTank(context, tank) {
|
|
17107
|
+
const { builder } = context;
|
|
17108
|
+
const top = context.floorCount - 1;
|
|
17109
|
+
if (!context.isTimber) {
|
|
17110
|
+
const ceiling = ensureCeilingSlab(context, tank.y);
|
|
17111
|
+
builder.rest(addTank(context, tank, top, tank.y), ceiling, "resting");
|
|
17112
|
+
return;
|
|
17113
|
+
}
|
|
17114
|
+
const supports = context.topSlab ? [context.topSlab] : context.ceilingJoists ?? builder.parts.filter((part) => part.role === "beam" && part.id.startsWith(`plate_${top}_`));
|
|
17115
|
+
const y0 = supports[0] ? supports[0].y1 : tank.y - 0.03;
|
|
17116
|
+
const floor = builder.add({ id: "attic_floor", role: "slab", material: "plywood", aabb: context.topBox, y0, y1: y0 + 0.03 }, { floor: top });
|
|
17117
|
+
for (const support of supports) if (touches(floor.aabb, support.aabb, 0.1)) builder.rest(floor, support, "nail");
|
|
17118
|
+
builder.rest(addTank(context, tank, top, tank.y), floor, "resting");
|
|
17119
|
+
}
|
|
17120
|
+
function addSlabTank(context, tank) {
|
|
17121
|
+
const { builder } = context;
|
|
17122
|
+
const supports = builder.parts.filter((part2) => part2.role === "slab" && Math.abs(part2.y1 - tank.y) < 0.3 && touches(part2.aabb, tank.aabb, 0.1));
|
|
17123
|
+
const carrier = supports.sort((a, b) => Math.abs(a.y1 - tank.y) - Math.abs(b.y1 - tank.y))[0] ?? context.topSlab;
|
|
17124
|
+
const part = addTank(context, tank, tank.floor, tank.y);
|
|
17125
|
+
builder.rest(part, carrier ?? GROUND, "resting");
|
|
17126
|
+
}
|
|
17127
|
+
function addTrestleTank(context, tank) {
|
|
17128
|
+
const { builder } = context;
|
|
17129
|
+
const stand = tank.stand;
|
|
17130
|
+
const columns = stand.pillars.map((q, k) => {
|
|
17131
|
+
const column = builder.add({ id: `trestle_column_${k}`, role: "column", material: stand.material, aabb: squareAround(q.u, q.v, q.side), y0: 0, y1: stand.height }, { floor: 0 });
|
|
17132
|
+
builder.rest(column, GROUND, stand.baseJoint ?? "resting");
|
|
17133
|
+
return column;
|
|
17134
|
+
});
|
|
17135
|
+
const [u0, v0, u1, v1] = tank.aabb;
|
|
17136
|
+
const beams = [[u0 - 0.1, v0 - 0.1, u1 + 0.1, v0 + 0.05], [u0 - 0.1, v1 - 0.05, u1 + 0.1, v1 + 0.1]].map((aabb, k) => {
|
|
17137
|
+
const beam = builder.add({ id: `trestle_beam_${k}`, role: "beam", material: stand.material, aabb, y0: stand.height - 0.12, y1: stand.height }, { floor: 0 });
|
|
17138
|
+
for (const column of columns.filter((c) => touches(beam.aabb, c.aabb, 0.15))) builder.rest(beam, column, stand.joint ?? "nail");
|
|
17139
|
+
return beam;
|
|
17140
|
+
});
|
|
17141
|
+
const part = addTank(context, tank, 0, stand.height);
|
|
17142
|
+
for (const beam of beams) builder.rest(part, beam, stand.tie ?? "resting");
|
|
17143
|
+
}
|
|
17144
|
+
function addWaterTank(context) {
|
|
17145
|
+
const tank = context.tank;
|
|
17146
|
+
if (!tank) return;
|
|
17147
|
+
switch (tank.place) {
|
|
17148
|
+
case "slab":
|
|
17149
|
+
return addSlabTank(context, tank);
|
|
17150
|
+
case "raised_slab":
|
|
17151
|
+
return addRaisedPlatform(context, tank);
|
|
17152
|
+
case "attic":
|
|
17153
|
+
return addAtticTank(context, tank);
|
|
17154
|
+
case "trestle":
|
|
17155
|
+
return addTrestleTank(context, tank);
|
|
17156
|
+
}
|
|
17157
|
+
}
|
|
17158
|
+
function addUpperFloor(context, floor) {
|
|
17159
|
+
if (floor >= context.floorCount - 1 || context.isUnit) return;
|
|
17160
|
+
const next = floor + 1;
|
|
17161
|
+
const rects = context.rectsOf(next, ["balcony", "porch", "void"]);
|
|
17162
|
+
const box22 = rects.length ? unionOf(rects) : context.houseBox;
|
|
17163
|
+
const carriers = context.carriers.get(floor);
|
|
17164
|
+
const { system, construction, builder } = context;
|
|
17165
|
+
const thickness = system.slabThickness;
|
|
17166
|
+
if (context.isTimber) {
|
|
17167
|
+
const joists = addJoists(context, { floor: next, box: box22, thickness, carriers, material: construction.frame.timber, joint: system.joints.beam });
|
|
17168
|
+
addDeck(context, `slab_${next}`, construction.frame.slabs, { floor: next, box: box22, thickness, joint: system.joints.slab }, joists);
|
|
17169
|
+
return;
|
|
17170
|
+
}
|
|
17171
|
+
const slab = builder.add({ id: `slab_${next}`, role: "slab", material: construction.frame.slabs, aabb: box22, y0: context.level(next) - thickness, y1: context.level(next) }, { floor: next, footprint: rects.length ? rects : [context.houseBox] });
|
|
17172
|
+
const below = carriers.filter((carrier) => carrier.y0 <= slab.y0 + 0.05);
|
|
17173
|
+
const touching = below.filter((carrier) => touches(slab.aabb, carrier.aabb, 0.2));
|
|
17174
|
+
for (const carrier of touching.length ? touching : below) builder.rest(slab, carrier, system.joints.slab);
|
|
17175
|
+
}
|
|
17176
|
+
function wallCeiling(context, wall) {
|
|
17177
|
+
const rooms = wall.sides.map((id) => context.source.rooms.find((room) => room.id === id && room.floor === wall.floor)).filter((room) => room !== void 0);
|
|
17178
|
+
return Math.max(context.ceilingOf(wall.floor), ...rooms.map((room) => room.ceiling || 0));
|
|
17179
|
+
}
|
|
17180
|
+
function wallTop(context, wall, floor) {
|
|
17181
|
+
const y0 = context.level(floor);
|
|
17182
|
+
if (wall.type === "railing") return y0 + guardHeightOf(wall);
|
|
17183
|
+
const ceiling = wallCeiling(context, wall);
|
|
17184
|
+
const height = wall.structureHeight || wall.h || context.ceilingOf(floor);
|
|
17185
|
+
if (wall.exterior && floor < context.floorCount - 1 && !context.isUnit && ceiling > context.ceilingOf(floor) + 0.01) return y0 + ceiling;
|
|
17186
|
+
if (floor === context.floorCount - 1 || context.isUnit) return y0 + Math.max(height, ceiling);
|
|
17187
|
+
const top = context.topOf(floor);
|
|
17188
|
+
return Math.max(y0 + height, y0 + ceiling > top ? top : y0 + ceiling);
|
|
17189
|
+
}
|
|
17190
|
+
function railingBase(context, wall, floor) {
|
|
17191
|
+
const open = wall.sides.map((id) => context.source.rooms.find((room) => room.id === id && room.floor === floor)).find((room) => room && (room.type === "balcony" || room.type === "porch"));
|
|
17192
|
+
if (!open) return context.baseOf(floor);
|
|
17193
|
+
return open.type === "porch" ? `porch_floor_${open.id}` : `slab_${floor}_${open.id}`;
|
|
17194
|
+
}
|
|
17195
|
+
var assemblyOf = (context) => context.construction.walls.assembly ?? (context.system.walls || void 0);
|
|
17196
|
+
function addWallPart(context, wall, floor) {
|
|
17197
|
+
const { system, builder } = context;
|
|
17198
|
+
const isRailing = wall.type === "railing";
|
|
17199
|
+
const y0 = context.level(floor);
|
|
17200
|
+
const top = wallTop(context, wall, floor);
|
|
17201
|
+
const y1 = isRailing ? top : wall.exterior && !wall.gable ? top - system.plateThickness : top;
|
|
17202
|
+
const framed = system.studs !== void 0 && !isRailing;
|
|
17203
|
+
const part = builder.add(
|
|
17204
|
+
{ id: wall.id, role: wall.role, material: wall.material, aabb: wall.box, y0, y1: Math.max(y1, y0 + 0.2) },
|
|
17205
|
+
{ floor, fill: context.isTimber && wall.role === "wall" ? system.wallFill : 1, ...framed ? { studs: system.studs } : {}, ...isRailing ? {} : { assembly: assemblyOf(context) } }
|
|
17206
|
+
);
|
|
17207
|
+
context.wallParts.set(wall.id, part);
|
|
17208
|
+
const base = isRailing ? railingBase(context, wall, floor) : context.baseOf(floor);
|
|
17209
|
+
const joint = isRailing ? "nail" : system.joints.wall;
|
|
17210
|
+
if (base === GROUND || floor === 0 && !base) builder.rest(part, GROUND, joint);
|
|
17211
|
+
else if (base) builder.rest(part, base, joint);
|
|
17212
|
+
}
|
|
17213
|
+
function addWalls(context, floor) {
|
|
17214
|
+
for (const wall of context.walls.filter((w) => w.floor === floor)) addWallPart(context, wall, floor);
|
|
17215
|
+
}
|
|
17216
|
+
function buildFloors(context) {
|
|
17217
|
+
for (let floor = 0; floor < context.floorCount; floor++) {
|
|
17218
|
+
addWalls(context, floor);
|
|
17219
|
+
addPlatesAndPillars(context, floor);
|
|
17220
|
+
addPorches(context, floor);
|
|
17221
|
+
addUpperFloor(context, floor);
|
|
17222
|
+
}
|
|
17223
|
+
}
|
|
17224
|
+
function buildStructure(source, options = {}) {
|
|
17225
|
+
const refined = options.wallDetail !== "coarse";
|
|
17226
|
+
const built = options.construction ? { construction: options.construction, extras: options.extras ?? {} } : resolveConstruction(source);
|
|
17227
|
+
const construction = built.construction;
|
|
17228
|
+
const extras = options.extras ?? built.extras ?? {};
|
|
17229
|
+
if (construction.system === "canvas") return buildCanvasStructure(construction, extras);
|
|
17230
|
+
const tank = options.tank !== void 0 ? options.tank : source.waterTank ?? null;
|
|
17231
|
+
const context = createContext(source, construction, extras, tank, { porchRoofOnPostsOnly: options.porchRoofOnPostsOnly ?? refined, rafterRoof: options.rafterRoof ?? refined });
|
|
17232
|
+
addFoundation(context);
|
|
17233
|
+
buildFloors(context);
|
|
17234
|
+
addBalconies(context);
|
|
17235
|
+
addPorchRoofs(context);
|
|
17236
|
+
addExteriorSteps(context);
|
|
17237
|
+
addStairs(context);
|
|
17238
|
+
if (options.roof !== false) addRoof(context);
|
|
17239
|
+
if (refined) addDoorLintels(context);
|
|
17240
|
+
addWaterTank(context);
|
|
17241
|
+
addChimney(context);
|
|
17242
|
+
addWell(context);
|
|
17243
|
+
const levels = source.floors.map((_, floor) => context.level(floor));
|
|
17244
|
+
const framed = construction.system === "masonry" && (context.isUnit || source.style === "commercial" || context.floorCount >= 3);
|
|
17245
|
+
return { parts: context.builder.parts, supports: context.builder.supports, levels, elevation: context.elevation, roof: context.roof, construction, framed, openings: findOpenings(context) };
|
|
17246
|
+
}
|
|
16244
17247
|
var entry = (itemId, min, max, group, pickable, weight) => ({ itemId, min, max, group, pickable, weight });
|
|
16245
17248
|
var FRIDGE = [
|
|
16246
17249
|
entry("water_bottle", 1, 3, "water", true, 3),
|
|
@@ -18634,7 +19637,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
18634
19637
|
assembleInteractive(gs);
|
|
18635
19638
|
linkDoorLeaves();
|
|
18636
19639
|
mailboxInteractive();
|
|
18637
|
-
const root = new THREE.Group(), house = new THREE.Group();
|
|
19640
|
+
const root = markKitOrigin(new THREE.Group(), { owner: "game-floorplan-three", kind: "structure", id: r.template }), house = new THREE.Group();
|
|
18638
19641
|
root.name = "house3d";
|
|
18639
19642
|
root.add(house);
|
|
18640
19643
|
shared.mirrors.house = house;
|
|
@@ -20373,10 +21376,10 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
20373
21376
|
}
|
|
20374
21377
|
|
|
20375
21378
|
// ../game-floorplan-residential/dist/index.js
|
|
20376
|
-
var
|
|
21379
|
+
var __defProp3 = Object.defineProperty;
|
|
20377
21380
|
var __export2 = (target, all) => {
|
|
20378
21381
|
for (var name in all)
|
|
20379
|
-
|
|
21382
|
+
__defProp3(target, name, { get: all[name], enumerable: true });
|
|
20380
21383
|
};
|
|
20381
21384
|
var residentialItems = [
|
|
20382
21385
|
{ type: "sofa_3seat", category: "sofa", w: 2.1, d: 0.9, h: 0.85, color: { role: "fabric_main" } },
|
|
@@ -21577,7 +22580,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21577
22580
|
}
|
|
21578
22581
|
function recordSpec(THREE, name, root, run, opts) {
|
|
21579
22582
|
const o = { chairs: opts.chairs };
|
|
21580
|
-
const
|
|
22583
|
+
const materialOf2 = { ...BASE_MATERIAL, ...opts.materials };
|
|
21581
22584
|
const drawn = [];
|
|
21582
22585
|
const counters = /* @__PURE__ */ new Map();
|
|
21583
22586
|
const q = new THREE.Quaternion();
|
|
@@ -21633,7 +22636,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21633
22636
|
if (!drawn.length) throw new Error(`game-floorplan-three: the builder of "${name}" drew no box or cylinder`);
|
|
21634
22637
|
const parts = drawn.map((p) => {
|
|
21635
22638
|
const thin = Math.min(...p.shape.kind === "box" ? p.shape.size : p.shape.kind === "cylinder" ? [p.shape.radius * 2, p.shape.height] : [p.shape.radius * 2]);
|
|
21636
|
-
let material =
|
|
22639
|
+
let material = materialOf2[p.key] ?? "plastic";
|
|
21637
22640
|
if (p.key === "wood" && material === "wood_hard" && thin < WOOD_THIN) material = "wood_soft";
|
|
21638
22641
|
const part = { id: p.id, material, shape: p.shape, position: p.position };
|
|
21639
22642
|
if (p.rotation) part.rotation = p.rotation;
|
|
@@ -21672,7 +22675,7 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21672
22675
|
const spec = { name, parts, joints, tags: ["furniture"] };
|
|
21673
22676
|
const colors = new Map(drawn.map((p) => [p.id, p.color]));
|
|
21674
22677
|
const build = (T3) => {
|
|
21675
|
-
const group = new T3.Group();
|
|
22678
|
+
const group = markKitOrigin(new T3.Group(), { owner: "game-floorplan-three", kind: "furniture", id: name });
|
|
21676
22679
|
group.name = name;
|
|
21677
22680
|
const mats = /* @__PURE__ */ new Map();
|
|
21678
22681
|
for (const p of parts) {
|
|
@@ -21759,6 +22762,66 @@ uniform mat4 uNodes[${nodes.length}]; attribute float aNodeIndex; attribute vec3
|
|
|
21759
22762
|
K.PARTS.length = before;
|
|
21760
22763
|
return { spec: made.spec, build: made.build, spout: [water.pos[0] - root.x, water.pos[1], water.pos[2] - root.z] };
|
|
21761
22764
|
}
|
|
22765
|
+
|
|
22766
|
+
// src/wall.ts
|
|
22767
|
+
var PLASTER = 15262420;
|
|
22768
|
+
var WALL_ID = "wall_0_0";
|
|
22769
|
+
function wallPlan(options) {
|
|
22770
|
+
const { w, h, thickness = 0.15, finish = "plaster", color: color2 = PLASTER } = options;
|
|
22771
|
+
const facade = { finish, roof: "none", floors: 1 };
|
|
22772
|
+
const wall = { id: WALL_ID, floor: 0, axis: "u", u0: -w / 2, v0: 0, u1: w / 2, v1: 0, thickness, sides: ["outside", "outside"], exterior: true, type: "wall", h };
|
|
22773
|
+
const plan = {
|
|
22774
|
+
template: "wall",
|
|
22775
|
+
name: "wall",
|
|
22776
|
+
style: "mid",
|
|
22777
|
+
state: "normal",
|
|
22778
|
+
w,
|
|
22779
|
+
d: thickness,
|
|
22780
|
+
floors: [{ index: 0, y0: 0, ceiling: h }],
|
|
22781
|
+
rooms: [],
|
|
22782
|
+
walls: [wall],
|
|
22783
|
+
doors: [],
|
|
22784
|
+
windows: [],
|
|
22785
|
+
items: [],
|
|
22786
|
+
stateItems: [],
|
|
22787
|
+
openPairs: [],
|
|
22788
|
+
partyWalls: [],
|
|
22789
|
+
missing: [],
|
|
22790
|
+
facade,
|
|
22791
|
+
colors: { facade: color2, wall: color2 },
|
|
22792
|
+
lot: null,
|
|
22793
|
+
building: null,
|
|
22794
|
+
waterTank: null,
|
|
22795
|
+
outbuildings: []
|
|
22796
|
+
};
|
|
22797
|
+
plan.construction = masonryConstruction({ ...plan, facade: { ...facade, roof: "colonial" } });
|
|
22798
|
+
plan.structure = buildStructure(plan, { wallDetail: "coarse", construction: plan.construction, roof: false });
|
|
22799
|
+
return plan;
|
|
22800
|
+
}
|
|
22801
|
+
function draw(THREE, plan, options) {
|
|
22802
|
+
return create3(THREE, { ...options, fleet: null }).build(plan, { quality: "game", terrain: false, noPerimeter: true, pieces: "meshes" });
|
|
22803
|
+
}
|
|
22804
|
+
function label(house) {
|
|
22805
|
+
for (const [id, items] of Object.entries(house.destruction.ranges())) {
|
|
22806
|
+
for (const item of items) {
|
|
22807
|
+
const object2 = item.isObject3D ? item : item.mesh;
|
|
22808
|
+
object2.userData.destructionPart = id;
|
|
22809
|
+
}
|
|
22810
|
+
}
|
|
22811
|
+
return house.group;
|
|
22812
|
+
}
|
|
22813
|
+
function wallPiece(THREE, options) {
|
|
22814
|
+
const plan = wallPlan(options);
|
|
22815
|
+
const houseOptions = options.house ?? {};
|
|
22816
|
+
const probe = draw(THREE, plan, { ...houseOptions, destruction: void 0, destructionRenderer: void 0 });
|
|
22817
|
+
const spec = probe.structure.spec({ wallDetail: "coarse" });
|
|
22818
|
+
probe.dispose();
|
|
22819
|
+
return {
|
|
22820
|
+
plan,
|
|
22821
|
+
spec,
|
|
22822
|
+
build: (T2) => label(draw(T2, plan, houseOptions))
|
|
22823
|
+
};
|
|
22824
|
+
}
|
|
21762
22825
|
return __toCommonJS(index_exports);
|
|
21763
22826
|
})();
|
|
21764
22827
|
//# sourceMappingURL=index.global.js.map
|