Deploy web Studio / deploy (push) Successful in 3m27s
House parts (stdlib isokit/house): house_wall drawn along a line, with ends that adapt at corners, T-junctions and straight runs so no two walls overlap; window and door that sit on a wall and cut its opening (door styles panel, glass, double, sliding, opening; swing and hinge by rotation); floor (boards, tiles, carpet, stone); roof (hip, gable, flat, optional solar); stairs. Furniture (stdlib isokit/furniture): chair, sofa / armchair, table with chairs, desk with monitor and chair, TV, bed, cupboard, bookshelf, kitchen run, bathtub, shower, toilet, washbasin, plant, rug, floor lamp. Cut-open levels: on gf / 1f the walls in front of a room are cut low (rooms are found by their floors), the roof becomes an outline on the floor, and storeys above are left out. A part's storey comes from z (storey_h, default 3 m). Scripting: objects_of, objects_at, objects_on, mount_of, mounted, frame, to_world, to_local, local_of. Prefab meta: shape (line / rect), mount, storey. Studio: drag to draw lines and rectangles (walls, roads, fences, hedges, floors, areas, buildings, roofs). The Room tool draws a floor plus walls, without doubling walls that already stand on an edge. Windows and doors snap onto walls with a hover preview, and they move, turn, copy and delete with their wall in one undo step. New parts land on the storey the view shows. Palette categories collapse, and the palette scrolls apart from the object list. The MCP instructions explain house building, and list_prefabs shows sits_on. examples/house is a furnished bungalow; the web Studio gets a House demo button. Tests: wall corners and junctions, zero depth-sort cycles in every view, cut levels, and the house demo in every view; check_studio covers the room / window / door / storey flow, check_web the House demo.
477 lines
20 KiB
Plaintext
477 lines
20 KiB
Plaintext
// isokit/furniture: interior pieces for houses and offices. Every piece
|
|
// faces local -y (its front; the back goes against a wall at +y) and stands
|
|
// on its object's z, so it can sit on any storey (see isokit/house). Pieces
|
|
// on storeys above the one a level cuts open are left out.
|
|
|
|
fn furniture_hidden(o) { house_view(o) == 2 }
|
|
fn wood_choices() { ["wood", "woodd", "furnw", "steel", "kitchenc"] }
|
|
fn fabric_choices() { ["uph1", "uph2", "uph3", "uph4", "sofa", "bed", "cloth"] }
|
|
|
|
/// A chair at (x, y) with its back towards (bx, by) (one of the axes).
|
|
fn chair_at(x, y, bx, by, mat) {
|
|
let h = 0.22;
|
|
for c in [[-1, -1], [1, -1], [1, 1], [-1, 1]] {
|
|
let lx = x + c[0] * 0.19;
|
|
let ly = y + c[1] * 0.19;
|
|
this.box(lx - 0.02, ly - 0.02, lx + 0.02, ly + 0.02, 0.02, 0.42, mat);
|
|
}
|
|
this.box(x - h, y - h, x + h, y + h, 0.42, 0.47, mat);
|
|
if bx != 0 {
|
|
let a = x + bx * 0.18;
|
|
let b = x + bx * 0.22;
|
|
this.box(fmin(a, b), y - h, fmax(a, b), y + h, 0.47, 0.92, mat);
|
|
} else {
|
|
let a = y + by * 0.18;
|
|
let b = y + by * 0.22;
|
|
this.box(x - h, fmin(a, b), x + h, fmax(a, b), 0.47, 0.92, mat);
|
|
}
|
|
this
|
|
}
|
|
|
|
/// An office chair at (x, y) with its back towards -y.
|
|
fn office_chair(x, y, mat) {
|
|
this.disc(x, y, 0.03, 0.27, "keyb");
|
|
this.stem(x, y, 0.03, 0.45, 0.25, "metal");
|
|
this.box(x - 0.24, y - 0.22, x + 0.24, y + 0.24, 0.45, 0.52, mat);
|
|
this.box(x - 0.22, y - 0.26, x + 0.22, y - 0.21, 0.52, 1.0, mat);
|
|
this
|
|
}
|
|
|
|
fn meta_chair() {
|
|
#{ label: "Chair", category: "Furniture", icon: "🪑", storey: true,
|
|
help: "A dining chair; its back is on the +y side.",
|
|
props: [ #{ key: "mat", type: "material", def: "wood", choices: wood_choices(), label: "Colour" } ] }
|
|
}
|
|
fn prefab_chair(o) {
|
|
if furniture_hidden(o) { return; }
|
|
sprite().chair_at(0.0, 0.0, 0, 1, o.p.mat);
|
|
}
|
|
|
|
fn meta_sofa() {
|
|
#{ label: "Sofa", category: "Furniture", icon: "🛋", storey: true,
|
|
help: "Seats face -y; one seat makes an armchair.",
|
|
props: [
|
|
#{ key: "seats", type: "int", def: 3, min: 1, max: 5, step: 1, label: "Seats" },
|
|
#{ key: "mat", type: "material", def: "uph1", choices: fabric_choices(), label: "Fabric" },
|
|
] }
|
|
}
|
|
fn prefab_sofa(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let m = o.p.mat;
|
|
let n = o.p.seats;
|
|
let W = n * 0.7 / 2.0 + 0.18;
|
|
let s = sprite();
|
|
s.box(-W, -0.45, W, 0.45, 0.02, 0.4, m);
|
|
s.seq([
|
|
[0.0, 0.35, |q| q.box(-W, 0.25, W, 0.45, 0.4, 0.85, m)],
|
|
[0.0, -0.1, |q| {
|
|
for i in 0..n {
|
|
let x0 = -W + 0.18 + i * 0.7;
|
|
q.box(x0 + 0.01, -0.44, x0 + 0.69, 0.25, 0.4, 0.5, m);
|
|
}
|
|
}],
|
|
[-W + 0.09, -0.1, |q| q.box(-W, -0.45, -W + 0.18, 0.25, 0.4, 0.64, m)],
|
|
[W - 0.09, -0.1, |q| q.box(W - 0.18, -0.45, W, 0.25, 0.4, 0.64, m)],
|
|
]);
|
|
}
|
|
|
|
fn meta_table() {
|
|
#{ label: "Table", category: "Furniture", icon: "🍽", storey: true,
|
|
help: "A table with chairs pulled out along its long sides (and its ends from six chairs on).",
|
|
props: [
|
|
#{ key: "w", type: "float", def: 1.6, min: 0.6, max: 6.0, step: 0.05, label: "Length (m)" },
|
|
#{ key: "d", type: "float", def: 0.9, min: 0.5, max: 2.0, step: 0.05, label: "Width (m)" },
|
|
#{ key: "h", type: "float", def: 0.75, min: 0.4, max: 1.1, step: 0.01, label: "Height (m)" },
|
|
#{ key: "chairs", type: "int", def: 4, min: 0, max: 16, step: 1, label: "Chairs" },
|
|
#{ key: "mat", type: "material", def: "wood", choices: wood_choices(), label: "Colour" },
|
|
#{ key: "chair_mat", type: "material", def: "woodd", choices: wood_choices(), label: "Chairs" },
|
|
] }
|
|
}
|
|
fn prefab_table(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let p = o.p;
|
|
let w = p.w / 2.0;
|
|
let d = p.d / 2.0;
|
|
let s = sprite();
|
|
for c in [[-1, -1], [1, -1], [1, 1], [-1, 1]] {
|
|
let x = c[0] * (w - 0.07);
|
|
let y = c[1] * (d - 0.07);
|
|
s.box(x - 0.03, y - 0.03, x + 0.03, y + 0.03, 0.02, p.h - 0.04, p.mat);
|
|
}
|
|
s.box(-w, -d, w, d, p.h - 0.04, p.h, p.mat);
|
|
// each chair its own sprite, clear of the table so the depth sort can order them
|
|
let n = p.chairs;
|
|
let ends = if n >= 6 { 2 } else { 0 };
|
|
let front = (n - ends + 1) / 2;
|
|
let back = n - ends - front;
|
|
for i in 0..front { sprite().chair_at(-w + (i + 0.5) * p.w / front, -d - 0.24, 0, -1, p.chair_mat); }
|
|
for i in 0..back { sprite().chair_at(-w + (i + 0.5) * p.w / back, d + 0.24, 0, 1, p.chair_mat); }
|
|
if ends > 0 {
|
|
sprite().chair_at(-w - 0.24, 0.0, -1, 0, p.chair_mat);
|
|
sprite().chair_at(w + 0.24, 0.0, 1, 0, p.chair_mat);
|
|
}
|
|
}
|
|
|
|
fn meta_desk() {
|
|
#{ label: "Desk", category: "Furniture", icon: "🖥", storey: true,
|
|
help: "A desk with a monitor and an office chair on its -y side.",
|
|
props: [
|
|
#{ key: "w", type: "float", def: 1.4, min: 0.8, max: 4.0, step: 0.05, label: "Width (m)" },
|
|
#{ key: "d", type: "float", def: 0.7, min: 0.5, max: 1.2, step: 0.05, label: "Depth (m)" },
|
|
#{ key: "mat", type: "material", def: "furnw", choices: wood_choices(), label: "Colour" },
|
|
#{ key: "monitor", type: "bool", def: true, label: "Monitor" },
|
|
#{ key: "chair", type: "bool", def: true, label: "Chair" },
|
|
#{ key: "chair_mat", type: "material", def: "uph1", choices: fabric_choices(), label: "Chair colour" },
|
|
] }
|
|
}
|
|
fn prefab_desk(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let p = o.p;
|
|
let w = p.w / 2.0;
|
|
let d = p.d / 2.0;
|
|
let h = 0.75;
|
|
let s = sprite();
|
|
s.box(-w, -d, -w + 0.04, d, 0.02, h - 0.03, p.mat);
|
|
s.box(w - 0.04, -d, w, d, 0.02, h - 0.03, p.mat);
|
|
s.box(-w + 0.04, d - 0.04, w - 0.04, d, 0.3, h - 0.03, p.mat);
|
|
s.box(-w, -d, w, d, h - 0.03, h, p.mat);
|
|
if p.monitor {
|
|
s.flat(rect(-0.22, -d + 0.1, 0.22, -d + 0.25), h + 0.004, "keyb");
|
|
s.box(-0.04, d - 0.2, 0.04, d - 0.12, h, h + 0.12, "tvbody");
|
|
s.box(-0.3, d - 0.18, 0.3, d - 0.15, h + 0.1, h + 0.45, "tvbody");
|
|
s.open([-0.3, d - 0.18], [0.3, d - 0.18], [[0.02, 0.58, h + 0.12, h + 0.43]], "tvs on");
|
|
}
|
|
if p.chair { sprite().office_chair(0.0, -d - 0.3, p.chair_mat); }
|
|
}
|
|
|
|
fn meta_tv() {
|
|
#{ label: "TV", category: "Furniture", icon: "📺", storey: true,
|
|
help: "A TV on a low cabinet (or on the wall), screen towards -y.",
|
|
props: [
|
|
#{ key: "size", type: "choice", def: "55", choices: ["32", "43", "55", "65", "75", "85"], label: "Size (inch)" },
|
|
#{ key: "stand", type: "bool", def: true, label: "On a cabinet" },
|
|
#{ key: "on", type: "bool", def: true, label: "Switched on" },
|
|
#{ key: "mat", type: "material", def: "woodd", choices: wood_choices(), label: "Cabinet" },
|
|
] }
|
|
}
|
|
fn prefab_tv(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let p = o.p;
|
|
let sw = parse_float(p.size) * 0.0254 * 0.8716;
|
|
let sh = sw * 9.0 / 16.0;
|
|
let s = sprite();
|
|
let zb = 1.0;
|
|
let y0 = 0.15;
|
|
if p.stand {
|
|
let cw = fmax(1.6, sw + 0.3) / 2.0;
|
|
s.box(-cw, -0.22, cw, 0.22, 0.02, 0.5, p.mat);
|
|
s.open([-cw, -0.22], [cw, -0.22], [[0.04, cw - 0.02, 0.08, 0.44], [cw + 0.02, 2.0 * cw - 0.04, 0.08, 0.44]], "seam");
|
|
s.box(-0.15, -0.08, 0.15, 0.08, 0.5, 0.53, "tvbody");
|
|
s.box(-0.03, 0.0, 0.03, 0.04, 0.53, 0.62, "tvbody");
|
|
zb = 0.6;
|
|
y0 = 0.0;
|
|
}
|
|
s.box(-sw / 2.0, y0, sw / 2.0, y0 + 0.05, zb, zb + sh, "tvbody");
|
|
s.open([-sw / 2.0, y0], [sw / 2.0, y0], [[0.02, sw - 0.02, zb + 0.02, zb + sh - 0.02]], if p.on { "tvs on" } else { "tvs" });
|
|
}
|
|
|
|
fn meta_bed() {
|
|
#{ label: "Bed", category: "Furniture", icon: "🛏", storey: true,
|
|
help: "Head (headboard, pillows) at +y.",
|
|
props: [
|
|
#{ key: "size", type: "choice", def: "double", choices: ["single", "double", "king"], label: "Size" },
|
|
#{ key: "mat", type: "material", def: "uph2", choices: fabric_choices(), label: "Duvet" },
|
|
#{ key: "frame", type: "material", def: "woodd", choices: wood_choices(), label: "Frame" },
|
|
#{ key: "nightstands", type: "bool", def: true, label: "Nightstands" },
|
|
] }
|
|
}
|
|
fn prefab_bed(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let p = o.p;
|
|
let w = (if p.size == "single" { 0.9 } else if p.size == "king" { 1.8 } else { 1.6 }) / 2.0;
|
|
let L = 1.0;
|
|
let fr = p.frame;
|
|
let duvet = p.mat;
|
|
let two = w > 0.6;
|
|
sprite().seq([
|
|
[0.0, L + 0.04, |q| q.box(-w - 0.04, L, w + 0.04, L + 0.08, 0.02, 0.95, fr)],
|
|
[0.0, 0.0, |q| {
|
|
q.box(-w - 0.04, -L, w + 0.04, L, 0.02, 0.3, fr);
|
|
q.box(-w, -L + 0.02, w, L - 0.02, 0.3, 0.5, "cloth");
|
|
if two {
|
|
q.box(-w + 0.06, L - 0.45, -0.03, L - 0.08, 0.5, 0.62, "cloth");
|
|
q.box(0.03, L - 0.45, w - 0.06, L - 0.08, 0.5, 0.62, "cloth");
|
|
} else {
|
|
q.box(-w + 0.06, L - 0.45, w - 0.06, L - 0.08, 0.5, 0.62, "cloth");
|
|
}
|
|
q.box(-w - 0.02, -L, w + 0.02, L - 0.55, 0.5, 0.58, duvet);
|
|
}],
|
|
]);
|
|
if p.nightstands {
|
|
for side in [-1, 1] {
|
|
let x = side * (w + 0.32);
|
|
let n = sprite();
|
|
n.box(x - 0.22, L - 0.42, x + 0.22, L - 0.02, 0.02, 0.5, fr);
|
|
n.cyl(x, L - 0.2, 0.08, 0.5, 0.75, "lshade");
|
|
}
|
|
}
|
|
}
|
|
|
|
fn meta_cupboard() {
|
|
#{ label: "Cupboard", category: "Furniture", icon: "▯", storey: true,
|
|
help: "A wardrobe or cabinet, doors on the -y side.",
|
|
props: [
|
|
#{ key: "w", type: "float", def: 1.2, min: 0.3, max: 6.0, step: 0.05, label: "Width (m)" },
|
|
#{ key: "d", type: "float", def: 0.6, min: 0.3, max: 1.0, step: 0.05, label: "Depth (m)" },
|
|
#{ key: "h", type: "float", def: 2.0, min: 0.4, max: 3.0, step: 0.05, label: "Height (m)" },
|
|
#{ key: "mat", type: "material", def: "wardrobe", choices: ["wardrobe", "wood", "woodd", "furnw", "kitchenc"], label: "Colour" },
|
|
] }
|
|
}
|
|
fn prefab_cupboard(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let p = o.p;
|
|
let w = p.w / 2.0;
|
|
let d = p.d / 2.0;
|
|
let s = sprite();
|
|
s.box(-w, -d, w, d, 0.02, p.h, p.mat);
|
|
let n = fmax(1.0, (p.w / 0.5).round()).to_int();
|
|
let dw = p.w / n;
|
|
let doors = [];
|
|
for i in 0..n { doors.push([i * dw + 0.015, (i + 1) * dw - 0.015, 0.06, p.h - 0.04]); }
|
|
s.open([-w, -d], [w, -d], doors, "seam");
|
|
}
|
|
|
|
fn meta_shelf() {
|
|
#{ label: "Bookshelf", category: "Furniture", icon: "📚", storey: true,
|
|
help: "Open shelves facing -y.",
|
|
props: [
|
|
#{ key: "w", type: "float", def: 1.0, min: 0.3, max: 6.0, step: 0.05, label: "Width (m)" },
|
|
#{ key: "d", type: "float", def: 0.35, min: 0.2, max: 0.8, step: 0.05, label: "Depth (m)" },
|
|
#{ key: "h", type: "float", def: 1.9, min: 0.4, max: 3.0, step: 0.05, label: "Height (m)" },
|
|
#{ key: "books", type: "bool", def: true, label: "Books" },
|
|
#{ key: "mat", type: "material", def: "wood", choices: wood_choices(), label: "Colour" },
|
|
] }
|
|
}
|
|
fn prefab_shelf(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let p = o.p;
|
|
let w = p.w / 2.0;
|
|
let d = p.d / 2.0;
|
|
let s = sprite();
|
|
s.box(-w, -d, w, d, 0.02, p.h, p.mat);
|
|
let n = fmax(1.0, ((p.h - 0.1) / 0.36).floor()).to_int();
|
|
let sh = (p.h - 0.08) / n;
|
|
let comp = [];
|
|
let bks = [[], [], []];
|
|
for i in 0..n {
|
|
let z0 = 0.06 + i * sh;
|
|
let z1 = z0 + sh - 0.03;
|
|
comp.push([0.03, p.w - 0.03, z0, z1]);
|
|
if p.books {
|
|
let u = 0.05;
|
|
let j = 0;
|
|
while u < p.w - 0.12 {
|
|
let bw = 0.04 + 0.08 * hash(i * 13.1 + j * 3.7 + p.w);
|
|
if hash(j * 7.9 + i * 2.3) > 0.88 { u += bw + 0.06; j += 1; continue; }
|
|
let bh = (z1 - z0) * (0.62 + 0.3 * hash(j * 5.3 + i));
|
|
bks[j % 3].push([u, fmin(u + bw, p.w - 0.05), z0, z0 + bh]);
|
|
u += bw + 0.006;
|
|
j += 1;
|
|
}
|
|
}
|
|
}
|
|
let a = [-w, -d];
|
|
let b = [w, -d];
|
|
s.open(a, b, comp, "open");
|
|
for k in 0..3 {
|
|
if bks[k].len() > 0 { s.open(a, b, bks[k], "bk" + (k + 1)); }
|
|
}
|
|
}
|
|
|
|
fn meta_kitchen() {
|
|
#{ label: "Kitchen", category: "Furniture", icon: "🍳", storey: true,
|
|
help: "A kitchen run: base cabinets and worktop with sink and hob, wall cabinets and a fridge at the +x end. Front towards -y.",
|
|
props: [
|
|
#{ key: "w", type: "float", def: 3.0, min: 0.6, max: 10.0, step: 0.05, label: "Length (m)" },
|
|
#{ key: "mat", type: "material", def: "kitchenc", choices: ["kitchenc", "furnw", "wood", "woodd", "doorg"], label: "Cabinets" },
|
|
#{ key: "top", type: "material", def: "worktop", choices: ["worktop", "wood", "steel", "furnw"], label: "Worktop" },
|
|
#{ key: "sink", type: "bool", def: true, label: "Sink" },
|
|
#{ key: "hob", type: "bool", def: true, label: "Hob" },
|
|
#{ key: "upper", type: "bool", def: true, label: "Wall cabinets" },
|
|
#{ key: "fridge", type: "bool", def: true, label: "Fridge" },
|
|
] }
|
|
}
|
|
fn prefab_kitchen(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let p = o.p;
|
|
let w = p.w / 2.0;
|
|
let fr = p.fridge && p.w >= 1.3;
|
|
let x1 = if fr { w - 0.64 } else { w };
|
|
let L = x1 + w;
|
|
let m = p.mat;
|
|
let tp = p.top;
|
|
let items = [[(x1 - w) / 2.0, 0.0, |q| {
|
|
q.box(-w, -0.28, x1, 0.3, 0.02, 0.86, m);
|
|
let n = fmax(1.0, (L / 0.6).round()).to_int();
|
|
let dw = L / n;
|
|
let fronts = [];
|
|
let uppers = [];
|
|
for i in 0..n {
|
|
fronts.push([i * dw + 0.01, (i + 1) * dw - 0.01, 0.1, 0.84]);
|
|
uppers.push([i * dw + 0.01, (i + 1) * dw - 0.01, 1.47, 2.13]);
|
|
}
|
|
q.open([-w, -0.28], [x1, -0.28], fronts, "seam");
|
|
q.box(-w, -0.32, x1, 0.3, 0.86, 0.9, tp);
|
|
if p.sink && L >= 1.0 {
|
|
let sx = -w + fmin(0.5, L * 0.3);
|
|
q.flat(rect(sx - 0.24, -0.2, sx + 0.24, 0.14), 0.904, "sink");
|
|
q.stem(sx, 0.22, 0.9, 1.18, 0.16, "metal");
|
|
}
|
|
if p.hob && L >= 1.8 {
|
|
let hx = x1 - 0.5;
|
|
q.flat(rect(hx - 0.3, -0.24, hx + 0.3, 0.22), 0.904, "hob");
|
|
for c in [[-1, -1], [1, -1], [1, 1], [-1, 1]] { q.disc(hx + c[0] * 0.14, c[1] * 0.11, 0.906, 0.08, "hobr"); }
|
|
}
|
|
if p.upper {
|
|
q.box(-w, -0.05, x1, 0.3, 1.45, 2.15, m);
|
|
q.open([-w, -0.05], [x1, -0.05], uppers, "seam");
|
|
}
|
|
}]];
|
|
if fr {
|
|
items.push([x1 + 0.33, 0.0, |q| {
|
|
q.box(x1 + 0.02, -0.32, w, 0.3, 0.02, 1.9, "fridge");
|
|
q.open([x1 + 0.02, -0.32], [w, -0.32], [[0.03, w - x1 - 0.05, 0.06, 1.12], [0.03, w - x1 - 0.05, 1.16, 1.86]], "seam");
|
|
}]);
|
|
}
|
|
sprite().seq(items);
|
|
}
|
|
|
|
fn meta_bath() {
|
|
#{ label: "Bathtub", category: "Furniture", icon: "🛁", storey: true,
|
|
props: [
|
|
#{ key: "w", type: "float", def: 1.7, min: 1.0, max: 2.4, step: 0.05, label: "Length (m)" },
|
|
#{ key: "d", type: "float", def: 0.75, min: 0.6, max: 1.4, step: 0.05, label: "Width (m)" },
|
|
] }
|
|
}
|
|
fn prefab_bath(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let w = o.p.w / 2.0;
|
|
let d = o.p.d / 2.0;
|
|
let s = sprite();
|
|
s.box(-w, -d, w, d, 0.02, 0.56, "sanit");
|
|
s.flat(rect(-w + 0.08, -d + 0.08, w - 0.08, d - 0.08), 0.562, "poolw t");
|
|
s.lines(rsegs([-w + 0.08, -d + 0.08, w - 0.08, d - 0.08]), 0.563, "seam");
|
|
s.stem(w - 0.1, 0.0, 0.56, 0.76, 0.15, "metal");
|
|
}
|
|
|
|
fn meta_shower() {
|
|
#{ label: "Shower", category: "Furniture", icon: "🚿", storey: true,
|
|
help: "A tray with glass on its -y and +x sides; the riser is in the back left corner.",
|
|
props: [
|
|
#{ key: "w", type: "float", def: 0.9, min: 0.7, max: 2.0, step: 0.05, label: "Width (m)" },
|
|
#{ key: "d", type: "float", def: 0.9, min: 0.7, max: 2.0, step: 0.05, label: "Depth (m)" },
|
|
] }
|
|
}
|
|
fn prefab_shower(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let w = o.p.w / 2.0;
|
|
let d = o.p.d / 2.0;
|
|
let s = sprite();
|
|
s.box(-w, -d, w, d, 0.02, 0.08, "sanit");
|
|
s.flat(rect(-w + 0.05, -d + 0.05, w - 0.05, d - 0.05), 0.082, "shower t");
|
|
s.disc(0.0, 0.0, 0.083, 0.04, "keyb");
|
|
s.stem(-w + 0.06, d - 0.06, 0.08, 2.05, 0.14, "metal");
|
|
s.disc(-w + 0.2, d - 0.2, 2.0, 0.1, "metal");
|
|
s.box(-w, -d, w, -d + 0.02, 0.08, 2.0, "glassp");
|
|
s.box(w - 0.02, -d + 0.02, w, d, 0.08, 2.0, "glassp");
|
|
}
|
|
|
|
fn meta_toilet() {
|
|
#{ label: "Toilet", category: "Furniture", icon: "🚽", storey: true, help: "Cistern at +y.", props: [] }
|
|
}
|
|
fn prefab_toilet(o) {
|
|
if furniture_hidden(o) { return; }
|
|
sprite().seq([
|
|
[0.0, 0.25, |q| q.box(-0.19, 0.17, 0.19, 0.33, 0.02, 0.8, "sanit")],
|
|
[0.0, -0.05, |q| {
|
|
q.frust(0.0, -0.05, 0.13, 0.19, 0.02, 0.42, "sanit", 12);
|
|
q.disc(0.0, -0.05, 0.422, 0.14, "seam");
|
|
}],
|
|
]);
|
|
}
|
|
|
|
fn meta_basin() {
|
|
#{ label: "Washbasin", category: "Furniture", icon: "🚰", storey: true,
|
|
help: "A vanity unit with a basin and a mirror on the wall behind (+y).",
|
|
props: [
|
|
#{ key: "w", type: "float", def: 0.6, min: 0.4, max: 2.4, step: 0.05, label: "Width (m)" },
|
|
#{ key: "mirror", type: "bool", def: true, label: "Mirror" },
|
|
#{ key: "mat", type: "material", def: "furnw", choices: wood_choices(), label: "Cabinet" },
|
|
] }
|
|
}
|
|
fn prefab_basin(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let w = o.p.w / 2.0;
|
|
let s = sprite();
|
|
s.box(-w, -0.22, w, 0.23, 0.02, 0.82, o.p.mat);
|
|
s.open([-w, -0.22], [w, -0.22], [[0.02, w - 0.01, 0.1, 0.8], [w + 0.01, 2.0 * w - 0.02, 0.1, 0.8]], "seam");
|
|
s.box(-w, -0.24, w, 0.23, 0.82, 0.86, "sanit");
|
|
s.flat(rect(-w + 0.08, -0.17, w - 0.08, 0.1), 0.862, "sink");
|
|
s.stem(0.0, 0.17, 0.86, 1.02, 0.14, "metal");
|
|
if o.p.mirror { s.box(-w + 0.04, 0.21, w - 0.04, 0.23, 1.05, 1.75, "glassp"); }
|
|
}
|
|
|
|
fn meta_plant() {
|
|
#{ label: "Plant", category: "Furniture", icon: "🪴", storey: true,
|
|
props: [
|
|
#{ key: "h", type: "float", def: 1.0, min: 0.3, max: 2.5, step: 0.05, label: "Height (m)" },
|
|
#{ key: "pot", type: "material", def: "terra", choices: ["terra", "furnw", "concb", "woodd"], label: "Pot" },
|
|
] }
|
|
}
|
|
fn prefab_plant(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let h = o.p.h;
|
|
let r = 0.1 + h * 0.08;
|
|
let zp = 0.02 + r * 1.5;
|
|
let cr = 0.15 + h * 0.2;
|
|
let s = sprite();
|
|
s.frust(0.0, 0.0, r * 0.75, r, 0.02, zp, o.p.pot, 10);
|
|
s.ext_box(-cr, -cr, cr, cr, zp, zp + h);
|
|
s.stem(0.0, 0.0, zp, zp + h * 0.5, 0.25, "trunk");
|
|
let R = cr * px() * 1.18;
|
|
s.circle(0.0, 0.0, zp + h * 0.62, R, "leaf l");
|
|
s.circle(0.0, 0.0, zp + h * 0.62, R * 0.58, "leaf t", -R * 0.28, -R * 0.3);
|
|
}
|
|
|
|
fn meta_rug() {
|
|
#{ label: "Rug", category: "Furniture", icon: "▭", shape: "rect", storey: true,
|
|
props: [
|
|
#{ key: "w", type: "float", def: 2.0, min: 0.4, max: 10.0, step: 0.05, label: "Width (m)" },
|
|
#{ key: "d", type: "float", def: 1.4, min: 0.4, max: 10.0, step: 0.05, label: "Depth (m)" },
|
|
#{ key: "mat", type: "material", def: "rug1", choices: ["rug1", "rug2", "rug3", "uph1", "uph3", "cloth"], label: "Colour" },
|
|
] }
|
|
}
|
|
fn prefab_rug(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let w = o.p.w / 2.0;
|
|
let d = o.p.d / 2.0;
|
|
if o.z.abs() < 0.25 { layer("floor"); }
|
|
let s = sprite();
|
|
s.flat(rect(-w, -d, w, d), 0.012, o.p.mat + " t");
|
|
let i = fmin(0.1, fmin(w, d) * 0.2);
|
|
s.lines(rsegs([-w + i, -d + i, w - i, d - i]), 0.013, "rugl");
|
|
}
|
|
|
|
fn meta_floor_lamp() {
|
|
#{ label: "Floor lamp", category: "Furniture", icon: "💡", storey: true,
|
|
props: [ #{ key: "on", type: "bool", def: true, label: "Lit" } ] }
|
|
}
|
|
fn prefab_floor_lamp(o) {
|
|
if furniture_hidden(o) { return; }
|
|
let s = sprite();
|
|
s.disc(0.0, 0.0, 0.025, 0.16, "keyb");
|
|
s.stem(0.0, 0.0, 0.02, 1.4, 0.12, "metal");
|
|
s.frust(0.0, 0.0, 0.22, 0.14, 1.32, 1.62, "lshade", 10);
|
|
if o.p.on { s.circle(0.0, 0.0, 1.5, 1.1, "lamp"); }
|
|
}
|