# Modern house: a white ground floor and a timber-clad upper floor cantilevered over a deck;
# windows cut into the walls with glass and frames, a pool cut into the site, trees and a hedge.
# Shown like an architect's model, on its plinth.
from math import *
units("cm")

plaster, timber, frame_c, roof_c, deep = "#efebe3", "#9c6b45", "#3a3d42", "#55585e", "#2c3036"

# ---------- the site: a plinth with the lawn on top, the pool cut into it ----------
site = box(3200, 2300, 160, at=(-900, -1100, -160), name="site", color="#d9d2c5")
pool_x, pool_y, pool_w, pool_d = 1120, -620, 720, 380
site = site - box(pool_w, pool_d, 140, at=(pool_x, pool_y, -140))
site.paint("#d9d2c5")
site.top.paint("#86b463")
water = box(pool_w, pool_d, 125, at=(pool_x, pool_y, -140), name="water", color="#4f9fcf")
coping = box(pool_w + 60, pool_d + 60, 4, at=(pool_x - 30, pool_y - 30, 0), color="stone")
coping = coping - box(pool_w, pool_d, 4, at=(pool_x, pool_y, 0))
coping.paint("stone")
coping.name = "coping"

# ---------- the ground floor ----------
ground = box(1000, 700, 300, name="ground floor", color=plaster)
# a wall of glass to the garden, a door at the side, a window at the back
ground.front.rect(110, 20, 780, 260).push(22)
ground.left.rect(300, 0, 120, 240).push(22)
ground.back.rect(150, 110, 300, 130).push(22)
ground.paint(plaster)
for p in [(500, 22, 150), (22, 360, 120), (300, 678, 175)]:
    ground.face(near=p).paint(deep)          # the room seen through the glass: dark

# ---------- the upper floor, cantilevered 3.5 m over the deck ----------
upper = box(1100, 600, 300, at=(250, 100, 300), name="upper floor", color=timber)
upper.front.rect(80, 70, 940, 150).push(22)
upper.right.rect(120, 30, 360, 240).push(22)
upper.paint(timber)
for p in [(800, 122, 445), (1328, 400, 450)]:
    upper.face(near=p).paint(deep)
roof = box(1120, 620, 24, at=(240, 90, 600), name="roof", color=roof_c)
roof.chamfer(2, edges="top")
canopy = box(220, 160, 14, at=(-160, 280, 255), name="canopy", color=roof_c)

# ---------- glass and frames in every opening ----------
glass, frames = [], []
def glazing(x0, y0, z0, w, h, along, panes, depth=12):
    # a pane in the opening's plane, mullions between the panes, a frame round them
    if along == "x":
        glass.append(box(w, 2, h, at=(x0, y0 + depth, z0), color="glass"))
        for k in range(panes + 1):
            frames.append(box(6, 8, h, at=(x0 + (w - 6) * k / panes, y0 + depth - 3, z0), color=frame_c))
        for z in (z0, z0 + h - 6):
            frames.append(box(w, 8, 6, at=(x0, y0 + depth - 3, z), color=frame_c))
    else:
        glass.append(box(2, w, h, at=(x0 + depth, y0, z0), color="glass"))
        for k in range(panes + 1):
            frames.append(box(8, 6, h, at=(x0 + depth - 3, y0 + (w - 6) * k / panes, z0), color=frame_c))
        for z in (z0, z0 + h - 6):
            frames.append(box(8, w, 6, at=(x0 + depth - 3, y0, z), color=frame_c))
glazing(110, 0, 20, 780, 260, "x", 4)
glazing(250 + 80, 100, 370, 940, 150, "x", 5)
glazing(1328, 220, 330, 360, 240, "y", 2, 10)
glazing(150, 678, 110, 300, 130, "x", 2, 10)
door = box(6, 112, 236, at=(14, 304, 0), name="door", color="#6b4a3a")
handle = box(3, 3, 60, at=(9, 318, 90), name="handle", color="steel")

# ---------- the deck under the cantilever, and a few steps down to the lawn ----------
boards = []
for k in range(48):
    boards.append(box(700, 13.5, 5, at=(1000, 1 + k * 14.6, 0), color="#b07d52"))
deck = group(boards, name="deck")

# ---------- trees, a hedge, stepping stones ----------
def tree(x, y, s, name):
    trunk = cone(14 * s, 9 * s, 260 * s, at=(x, y, 0), name="trunk", color="#6b4a3a", sides=16)
    crown = []
    for dx, dy, dz, r, c in [(0, 0, 300, 120, "#4f7d3a"), (60, 30, 360, 95, "#5f8f44"), (-55, -25, 380, 90, "#457235"), (10, -40, 440, 75, "#6a9a4c")]:
        crown.append(sphere(r * s, at=(x + dx * s, y + dy * s, dz * s), name="crown", color=c, sides=18))
    return group([trunk] + crown, name=name)
tree(-450, 650, 1.1, "tree 1")
tree(1900, 600, 1.25, "tree 2")
tree(-300, -650, 0.9, "tree 3")
hedge = box(1500, 90, 120, at=(-200, 950, 0), name="hedge", color="#3f6b35")
hedge.fillet(40, edges="top")
# stepping stones from the front of the site round to the door
stones = []
for k in range(9):
    t = k / 8
    st = box(100, 52, 4, at=(-150 - 200 * sin(pi * t) * 0.6 - 50, -1000 + 1360 * t, 0), color="stone")
    st.rotate(10 * cos(pi * t))
    st.fillet(10, edges="vertical")
    stones.append(st)
group(stones, name="path")

# two loungers by the pool
loungers = []
for k in range(2):
    x = 1930 + k * 110
    frame = box(70, 190, 26, at=(x, -640, 0), color="#e9e4da")
    frame.fillet(4, edges="y")
    pad = box(64, 130, 8, at=(x + 3, -637, 26), color="#d9cdb8")
    pad.fillet(3, edges="top")
    back = box(64, 60, 8, at=(x + 3, -510, 26), color="#d9cdb8")
    back.fillet(3, edges="top")
    back.rotate(38, axis="x", about=(x, -507, 30))
    loungers.append(group([frame, pad, back], name=f"lounger {k + 1}"))
group(loungers, name="loungers")

# a dark coping round the ground floor's roof, where the upper floor doesn't stand
edge = [box(1004, 12, 14, at=(-2, -2, 300)), box(12, 704, 14, at=(-2, -2, 300)), box(12, 102, 14, at=(990, -2, 300)), box(252, 12, 14, at=(-2, 690, 300))]
for e in edge:
    e.paint(roof_c)
    e.name = "coping"
group([ground, upper, roof, canopy, door, handle] + edge + glass + frames, name="house")
group([site, water, coping], name="site")
dim((0, -20, 0), (1350, -20, 0), offset=(0, -60, 0))
label("cantilever 3.5 m", at=(1700, -150, 820), to=(1260, 100, 300))
