# Villa with a garden, built from its brief and site plan (docs/villa): a family house on a
# 34 x 30 m plot, the street to the north, the garden to the south. Cm; north is +y.
# House: a white ground floor, a timber upper floor running on over a carport on two columns, a roof
# terrace with a door onto it and a glass railing on every open edge. Garden: a deck level with the
# floor, a pergola with a swing and a table for six, a stone pool terrace with loungers in pairs
# (a parasol and a side table each), a shower, a pool with a ladder and steps, a fire pit with
# benches facing it, vegetable beds, paths that go round the pool, trees kept clear of house and water.
# Most pieces are from the library, so their sizes can still be changed.
from math import *
units("cm")

# Things made of many small parts (a fence's slats, a path's slabs, a pot's flowers) are kept as one
# solid each: they look the same, and the drawing stays light to turn round and to work in.
def one(parts, name):
    # (joined in pairs, then pairs of pairs: far quicker than one after another into a growing whole)
    l = list(parts)
    while len(l) > 1:
        nl = [l[i] + l[i + 1] for i in range(0, len(l) - 1, 2)]
        if len(l) % 2:
            nl.append(l[-1])
        l = nl
    l[0].name = name
    return l[0]

white, timber, dark, steel = "#f1eee8", "#4a3a2e", "#2b2d31", "#9aa0a8"
stone, lawn_c, gravel, tile, water = "#dcd6c8", "#86a86a", "#cfc6b4", "#5fb0d2", "glass"   # water: see-through over the tiles
wood, cushion = "#a57b52", "#2f6f8f"
SX, SY = 3400, 3000

# ---------- the land: the plot, the meadow round it, the street to the north ----------
PX, PY, PW, PD, PDEP = 1050, 450, 1000, 400, 150       # the pool
plot = box(SX, SY, 60, at=(0, 0, -60), name="lawn", color=lawn_c) - box(PW, PD, PDEP, at=(PX, PY, -PDEP))
plot.paint(lawn_c)
MX, MY = 9000, 7000
meadow = box(SX + 2 * MX, SY + 2 * MY, 58, at=(-MX, -MY, -61), name="meadow", color="#a9bf8c") - box(SX, SY, 58, at=(0, 0, -61))
meadow.paint("#a9bf8c")
pavement = box(SX + 2 * MX, 240, 12, at=(-MX, SY, -3), name="pavement", color="#cfcac0")
kerb = box(SX + 2 * MX, 12, 14, at=(-MX, SY + 240, -3), name="kerb", color="#b9b4aa")
street = box(SX + 2 * MX, 700, 4, at=(-MX, SY + 252, -3), name="street", color="#7b7f86")
one([box(300, 15, 1, at=(x, SY + 595, 1), color="#f4f2ec") for x in range(-MX + 100, SX + MX - 300, 600)], "road marking")
for x, y, h, w, crown in [(-600, 1600, 1100, 600, "round"), (-500, 300, 900, 450, "cone"), (4100, 900, 1000, 650, "round"),
                          (4000, 2300, 900, 400, "column"), (1200, -700, 1100, 700, "round"), (2600, -650, 1200, 500, "cone"),
                          (-400, 4300, 900, 600, "round"), (1500, 4350, 1000, 700, "round"), (3100, 4300, 1100, 450, "cone")]:
    tree(h=h, w=w, crown=crown, at=(x - w / 2, y - w / 2, -3))

# ---------- the house ----------
FL, H1, T = 20, 300, 30                     # floor level, storey height, wall thickness
GX0, GY0, GX1, GY1 = 800, 1600, 2300, 2500  # ground floor 15 x 9 m
UX0, UY0, UX1, UY1 = 1200, 1650, 2950, 2450 # upper floor 17.5 x 8 m (6.5 m of it over the carport)
SL = 25                                     # slabs
Z1 = FL + H1                                # the ground floor's top: 320
Z2 = Z1 + SL                                # the upper floor: 345
plinth = box(GX1 - GX0, GY1 - GY0, FL, at=(GX0, GY0, 0), name="plinth", color="#bdb6a8")
gf = box(GX1 - GX0, GY1 - GY0, H1, at=(GX0, GY0, FL), name="ground floor walls", color=white) - box(GX1 - GX0 - 2 * T, GY1 - GY0 - 2 * T, H1, at=(GX0 + T, GY0 + T, FL))
gf.paint(white)
slab_a = box(GX1 - GX0, GY1 - GY0, SL, at=(GX0, GY0, Z1), name="ground floor roof", color=white)
slab_b = box(UX1 - GX1, UY1 - UY0, SL, at=(GX1, UY0, Z1), name="slab over the carport", color=timber)
uf = box(UX1 - UX0, UY1 - UY0, H1, at=(UX0, UY0, Z2), name="upper floor walls", color=timber) - box(UX1 - UX0 - 2 * T, UY1 - UY0 - 2 * T, H1, at=(UX0 + T, UY0 + T, Z2))
uf.paint(timber)
roof = box(UX1 - UX0 + 30, UY1 - UY0 + 30, SL, at=(UX0 - 15, UY0 - 15, Z2 + H1), name="roof", color="#5a5e65")
# the carport: its floor, two steel columns under the slab's free end
carport = box(UX1 - GX1, UY1 - UY0, 4, at=(GX1, UY0, 0), name="carport floor", color="#c4bdb0")
for y in (1688, 2388):
    column(h=Z1 - 4, d=25, style="square", at=(2888, y, 4), color=dark)

# ground floor, south: two sliding glass walls onto the deck, the kitchen door and window
for x in (1000, 1440):
    window(w=340, h=240, panes=3, at=(x, GY0, FL), wall=gf, color=dark)
door(w=100, h=220, at=(1900, GY0, FL), wall=gf, color="#6b5444", frame=dark)
window(w=200, h=120, panes=2, at=(2060, GY0, FL + 95), wall=gf, color=dark)
# ground floor, north: the main door under its canopy, windows either side
door(w=110, h=230, at=(1830, GY1 - 10, FL), turn=180, wall=gf, color="#6b5444", frame=dark)
canopy = box(300, 150, 15, at=(1730, GY1, FL + 255), name="entrance canopy", color=dark)
for x, w in ((1000, 200), (1350, 300), (2060, 200)):
    window(w=w, h=150, panes=2, at=(x, GY1 - 8, FL + 90), turn=180, wall=gf, color=dark)
# ground floor, west and east: a window; the side door from the carport
window(w=200, h=150, panes=2, at=(GX0, 1900, FL + 90), turn=270, wall=gf, color=dark)
door(w=90, h=220, at=(GX1 - 10, 2150, FL), turn=90, wall=gf, color="#6b5444", frame=dark)
# upper floor: bedrooms' windows, the door onto the roof terrace (west wall)
for x in (1300, 1700, 2100, 2500):
    window(w=300, h=160, panes=2, at=(x, UY0, Z2 + 90), wall=uf, color=dark)
for x in (1400, 2100, 2550):
    window(w=240, h=140, panes=2, at=(x, UY1 - 8, Z2 + 100), turn=180, wall=uf, color=dark)
window(w=200, h=150, panes=2, at=(UX1 - 8, 1950, Z2 + 90), turn=90, wall=uf, color=dark)
window(w=200, h=150, panes=2, at=(UX0, 1750, Z2 + 90), turn=270, wall=uf, color=dark)
# solar panels on the roof, tilted to the south
RT = Z2 + H1 + SL                          # the roof's top
L, A = 165, radians(12)
for i in range(6):
    for j in range(2):
        x, y0 = UX0 + 200 + i * 115, UY0 + 170 + j * 230
        y1, h1 = y0 + L * cos(A), L * sin(A)
        prism([(y0, RT), (y1, RT), (y1, RT + 5 + h1), (y0, RT + 5)], 100, plane="yz", offset=x, name="panel frame", color=steel)
        prism([(y0, RT + 5), (y1, RT + 5 + h1), (y1, RT + 9 + h1), (y0, RT + 9)], 100, plane="yz", offset=x, name="solar panel", color="#25344d")

# ---------- the roof terrace on the ground floor roof (west of the upper floor) ----------
RZ = Z1 + SL
terrace_floor = box(UX0 - GX0, GY1 - GY0, 3, at=(GX0, GY0, RZ), name="roof terrace tiles", color="#cfc8ba")
RZ += 3
# (the ones along x run the terrace's full width; the ones along y fit between them, corners meeting)
railing(w=UX0 - GX0, h=105, style="glass", at=(GX0, GY1 - 6, RZ))
railing(w=UX0 - GX0, h=105, style="glass", at=(GX0, GY0, RZ))
railing(w=GY1 - GY0 - 12, h=105, style="glass", at=(GX0, GY0 + 6, RZ), turn=90)
railing(w=UY0 - GY0 - 6, h=105, style="glass", at=(UX0 - 6, GY0 + 6, RZ), turn=90)
railing(w=GY1 - 6 - UY1, h=105, style="glass", at=(UX0 - 6, UY1, RZ), turn=90)
# the door onto it from the upper floor, its sill on the tiles
door(w=90, h=220, at=(UX0, 2005, RZ), turn=270, wall=uf, color="#6b5444", frame=dark)

# ---------- things to sit and lie on ----------
def lounger(x, y, z):
    # head to the north (+y): lie looking south; 70 x 195
    parts = []
    for lx in (x, x + 66):
        for ly in (y, y + 191):
            parts.append(box(4, 4, 22, at=(lx, ly, z), name="lounger leg", color=steel))
    parts.append(box(70, 195, 6, at=(x, y, z + 22), name="lounger frame", color="#e9e4da"))
    parts.append(box(64, 138, 7, at=(x + 3, y + 3, z + 28), name="cushion", color=cushion))
    # the back rest, raised at the head end
    parts.append(prism([(y + 141, z + 28), (y + 150, z + 28), (y + 194, z + 72), (y + 186, z + 79)], 64, plane="yz", offset=x + 3, name="back rest", color=cushion))
    return one(parts, "lounger")

def side_table(cx, cy, z):
    leg = cylinder(3, 42, at=(cx, cy, z + 2), name="side table", color=steel, sides=16)
    foot = cylinder(16, 2, at=(cx, cy, z), name="side table", color=steel, sides=24)
    top = cylinder(22, 3, at=(cx, cy, z + 44), name="side table", color="#e9e4da", sides=32)
    return group([foot, leg, top], name="side table")

def parasol(cx, cy, z):
    base = box(45, 45, 8, at=(cx - 22.5, cy - 22.5, z), name="parasol base", color="#555a60")
    pole = cylinder(2.5, 222, at=(cx, cy, z + 8), name="parasol pole", color="#e9e4da", sides=12)
    canopy_ = cone(150, 6, 38, at=(cx, cy, z + 230), name="parasol", color="#d8302a")
    return group([base, pole, canopy_], name="parasol")

def table(x, y, z, w, d):
    parts = [box(w, d, 4, at=(x, y, z + 72), name="table top", color=wood)]
    for lx in (x + 8, x + w - 14):
        for ly in (y + 8, y + d - 14):
            parts.append(box(6, 6, 72, at=(lx, ly, z), name="table leg", color=dark))
    return group(parts, name="table")

# two loungers and a side table on the roof terrace, clear of the door
for x in (860, 1000):
    lounger(x, 1700, RZ)
side_table(965, 1850, RZ)

# ---------- the car in the carport, nose to the garden ----------
def car(x, y, color, z=4):
    # a hatchback, its nose to -y: a lower body with the bonnet, a glasshouse with raked screens
    body = box(180, 440, 62, at=(x, y, z + 28), name="car body", color=color)
    body.fillet(16, edges="vertical")
    body.fillet(9, edges="top")
    for wy in (y + 85, y + 355):                 # the wheel arches
        body = body - cylinder(37, 184, at=(x - 2, wy, z + 33), axis="x", sides=24)
    body.paint(color)
    body.name = "car body"
    # the glasshouse in side view (y, z): the windscreen raked back from the bonnet, the roof, the tailgate
    zb = z + 90
    glass_ = prism([(y + 140, zb), (y + 430, zb), (y + 420, zb + 46), (y + 214, zb + 50)], 166, plane="yz", offset=x + 7, name="car windows", color="#2a3440")
    shell = prism([(y + 214, zb + 50), (y + 420, zb + 46), (y + 418, zb + 54), (y + 216, zb + 58)], 170, plane="yz", offset=x + 5, name="car roof", color=color)
    parts = [body, shell, glass_]
    for wy in (y + 85, y + 355):
        for wx in (x - 2, x + 158):
            parts.append(cylinder(33, 24, at=(wx, wy, z + 33), axis="x", name="wheel", color="#202124", sides=24))
    for lx in (x + 18, x + 132):                 # headlamps
        parts.append(box(30, 2, 10, at=(lx, y - 2, z + 66), name="headlamp", color="#f4f2ec"))
    return group(parts, name="car")
car(2470, 1900, "#c8ccd2")

# ---------- the deck, the pergola with its swing and table, the BBQ ----------
one(deck(w=GX1 - GX0, d=400, h=FL, at=(GX0, 1200, 0), color=wood).solids, "deck")
PG = (850, 1230, 700, 350, 260)
pergola(w=PG[2], d=PG[3], h=PG[4], at=(PG[0], PG[1], FL), color="#3d3a36")
# a swing hung from two rafters at the west end, facing the garden
RAF = (PG[2] - 5) / (round(PG[2] / 40))        # the rafters' spacing
sx0, sx1 = PG[0] + RAF + 2.5, PG[0] + 5 * RAF + 2.5   # the 2nd and the 6th rafter's middles
rafter_z = FL + PG[4] - 16
seat_z = FL + 45
swing = [box(sx1 - sx0, 55, 5, at=(sx0, 1380, seat_z), name="swing seat", color=wood),
         box(sx1 - sx0, 5, 45, at=(sx0, 1430, seat_z + 5), name="swing back", color=wood)]
for x in (sx0, sx1):
    for y in (1385, 1430):
        swing.append(cylinder(0.8, rafter_z - seat_z - 5, at=(x, y, seat_z + 5), name="chain", color=steel, sides=8))
one(swing, "swing")                        # (the chains hold the seat and its back: one piece)
table(1200, 1300, FL, 220, 95)
for cx in (1215, 1290, 1365):
    chair(at=(cx, 1400, FL))                     # north side, facing the table
    chair(at=(cx, 1245, FL), turn=180)           # south side
bbq(w=60, at=(2150, 1270, FL))

# ---------- the pool, its terrace, loungers, parasols, the shower ----------
basin = box(PW, PD, PDEP, at=(PX, PY, -PDEP), name="pool", color=tile)
basin.shell(12, open="top")
basin.paint(tile)
# steps down at the west end, the ladder on the terrace side
for i, top in enumerate((-35, -70, -105)):
    box(30, 200, top + PDEP - 12, at=(PX + 12 + 30 * i, PY + PD - 12 - 200, -PDEP + 12), name="pool step", color=tile)
pool_water = box(PW - 24, PD - 24, 5, at=(PX + 12, PY + 12, -20), name="water", color=water)
for lx in (1930, 1978):
    pipe([(lx, 878, 4), (lx, 878, 70), (lx, 866, 86), (lx, 846, 72), (lx, 836, 30), (lx, 836, -110)], 2.4, smooth=True, name="ladder rail", color="#dfe2e6", sides=8)
for z in (-30, -60, -90):
    box(44, 10, 3, at=(1932, 826, z), name="ladder step", color="#dfe2e6")
for x0, y0, w, d in [(PX - 40, PY - 40, PW + 80, 40), (PX - 40, PY + PD, PW + 80, 40), (PX - 40, PY, 40, PD), (PX + PW, PY, 40, PD)]:
    box(w, d, 4, at=(x0, y0, 0), name="coping", color="#e6e1d6")
box(1200, 310, 4, at=(950, 890, 0), name="pool terrace", color=stone)
for x in (1090, 1260, 1730, 1900):
    lounger(x, 940, 4)
# between each pair: a parasol in the middle, a side table at the heads (clear of the pergola)
for cx in (1210, 1850):
    parasol(cx, 1020, 4)
    side_table(cx, 1110, 4)
# the shower by the steps: a tray, a post, a head
tray = box(85, 85, 5, at=(965, 960, 4), name="shower tray", color="#8d8a84")
post = cylinder(3, 225, at=(1040, 1035, 9), color="#dfe2e6", sides=12)
arm = box(38, 4, 4, at=(1005, 1033, 230), color="#dfe2e6")
head = cylinder(10, 3, at=(1005, 1035, 227), color="#dfe2e6", sides=24)    # (hung under the arm)
group([tray, one([post, arm, head], "shower")], name="shower")

# ---------- paths, the fire pit, the vegetable beds ----------
one(paving_path(w=500, d=100, slab=50, at=(GX1, 1300, 0)).solids, "path")                 # deck -> east
one(paving_path(w=1050, d=100, slab=50, at=(2700, 250, 0), turn=90).solids, "path")       # down to the beds
one(paving_path(w=350, d=100, slab=50, at=(450, 1300, 0)).solids, "path")                 # deck -> west
one(paving_path(w=500, d=100, slab=50, at=(450, 800, 0), turn=90).solids, "path")         # down to the fire pit
one(paving_path(w=SY - 10 - GY1, d=120, slab=60, at=(1820, GY1, 0), turn=90).solids, "path")   # the gate -> the main door, under the canopy
box(350, 550, 4, at=(2420, GY1 - 50, 0), name="drive", color="#c4bdb0")
gravel_disc = cylinder(300, 3, at=(500, 500, 0), name="gravel", color=gravel, sides=64)
fire_pit(w=110, at=(445, 445, 3))
bench(w=180, d=55, h=80, at=(262, 410, 3), turn=90)     # west, facing the fire
bench(w=180, d=55, h=80, at=(683, 410, 3), turn=270)    # east
bench(w=180, d=55, h=80, at=(410, 262, 3), turn=180)    # south
for y in (300, 600, 900):
    raised_bed(w=400, d=120, h=45, at=(2880, y, 0))

# ---------- planting ----------
for x, y, h, w, crown in [(420, 1650, 800, 450, "round"), (420, 2300, 900, 450, "round"), (650, 2780, 1000, 400, "cone"),
                          (3170, 2760, 900, 350, "column"), (3150, 1350, 750, 450, "round")]:
    tree(h=h, w=w, crown=crown, at=(x - w / 2, y - w / 2, 0))
hedge(w=1700, d=80, h=160, at=(900, 60, 0))
hedge(w=1500, d=80, h=160, at=(60, 900, 0), turn=90)
for x in (880, 1000, 1120, 1250, 1400, 1550, 2080, 2200):
    shrub(w=70, h=60, at=(x, GY1 + 20, 0), color="#7a9a63")

# ---------- flowers in pots: by the main door, on the roof terrace, where the paths meet, hanging from the pergola ----------
TERRA = "#b5653d"
def mound(x, y, z, w, h, color, name, sides=16):
    # a low rounded mass (as shrub() makes one, smaller than it allows): leaves, a flower
    R = w / 2
    m = sphere(R, at=(x + R, y + R, z + R), sides=sides)
    m.scale((1, 1, h / w))
    m.name = name
    m.paint(color)
    return m
LEAF, LEAF2 = "#5b7a46", "#6f8f55"

def rosette(cx, cy, z, R, lobes, turn, color):
    # a ring of leaves as one flat piece: rounded lobes out to R (one solid, light to draw)
    pts = []
    for k in range(lobes):
        a0 = radians(turn + k * 360 / lobes)
        da = radians(360 / lobes)
        for f, rr in ((0.0, 0.28), (0.18, 0.7), (0.32, 0.95), (0.5, 1.0), (0.68, 0.95), (0.82, 0.7)):
            a = a0 + f * da
            pts.append((cx + rr * R * cos(a), cy + rr * R * sin(a)))
    return prism(pts, 1.6, offset=z, name="leaves", color=color)

def flowering(cx, cy, zb, R, color, stems=7, tall=30):
    # two rings of leaves (the outer one over the rim), stems with a round flower head each
    parts = [rosette(cx, cy, zb + 3, R * 1.25, 8, 0, LEAF), rosette(cx, cy, zb + 6, R * 0.85, 6, 30, LEAF2)]
    for k in range(stems):
        a = radians(k * 360 / stems + 20)
        rr = R * (0.12 + 0.36 * (k % 3) / 2)
        x, y = cx + rr * cos(a), cy + rr * sin(a)
        ht = tall * (0.78 + 0.22 * ((k * 5) % 3) / 2)
        parts.append(cylinder(0.6, ht - 7.6, at=(x, y, zb + 7.6), name="stem", color=LEAF2, sides=8))
        parts.append(sphere(R * 0.17 + 1.2, at=(x, y, zb + ht + 2), name="flowers", color=color, sides=8))
    return parts

def pot(cx, cy, z, r=24, h=46, plant_c="#c94f6a", leaf=False, plant=None):
    body = cone(r * 0.78, r, h, at=(cx, cy, z), sides=24) + cylinder(r + 2, 5, at=(cx, cy, z + h - 5), sides=24)
    body.paint(TERRA)
    body.name = "flower pot"
    soil = cylinder(r * 0.92, 1, at=(cx, cy, z + h), name="soil", color="#4a3828", sides=24)
    parts = [body, soil]
    plant_c = plant or plant_c
    if leaf:                                     # a clipped box ball on a short stem
        w = r * 2.0
        parts.append(cylinder(2, 10, at=(cx, cy, z + h + 1), name="stem", color="#6b4f3a", sides=8))
        parts.append(mound(cx - w / 2, cy - w / 2, z + h + 8, w, w, "#4f6e3c", "box ball"))
        return one(parts, "topiary pot")
    # a flowering plant: leaves spilling over the rim, stems with flower heads
    parts += flowering(cx, cy, z + h + 1, r, plant_c)
    return one(parts, "flower pot")

def basket(cx, beam_y, beam_z, plant):
    # a basket on chains from a bracket off the beam's face, its bottom 1.95 m over the deck:
    # a flowering plant in it, and stems trailing over the rim with flowers on them
    cy, zb = beam_y - 30, FL + 196
    b = cone(9, 19, 14, at=(cx, cy, zb), name="hanging basket", color="#6b5444", sides=20)
    soil = cylinder(17, 1, at=(cx, cy, zb + 14), name="soil", color="#4a3828", sides=20)
    parts = [b, soil] + flowering(cx, cy, zb + 15, 17, plant, stems=4, tall=14)
    # flowers spilling over the rim, and short trailing stems with flowers along them (a trailing petunia)
    for k in range(8):
        a = radians(k * 45 + 22)
        parts.append(sphere(3.4, at=(cx + 19.5 * cos(a), cy + 19.5 * sin(a), zb + 15.5), name="flowers", color=plant, sides=8))
    for k in range(6):
        a = k * 60 + 5
        ux, uy = cos(radians(a)), sin(radians(a))
        drop = 10 + 5 * (k % 3)
        pts = [(cx + 20 * ux, cy + 20 * uy, zb + 15), (cx + 24 * ux, cy + 24 * uy, zb + 8), (cx + 25 * ux, cy + 25 * uy, zb - drop)]
        parts.append(pipe(pts, 0.7, smooth=False, name="trailing stem", color=LEAF2, sides=6))
        parts.append(sphere(2.8, at=(cx + 24.6 * ux, cy + 24.6 * uy, zb + 2), name="flowers", color=plant, sides=8))
        parts.append(sphere(3.0, at=(cx + 25 * ux, cy + 25 * uy, zb - drop - 2.5), name="flowers", color=plant, sides=8))
    bracket = box(4, 32, 4, at=(cx - 2, beam_y - 32, beam_z + 12), name="bracket", color=dark)
    parts.append(bracket)
    hook = (cx, cy, beam_z + 12)
    for a in (90, 210, 330):
        parts.append(pipe([(cx + 18 * cos(radians(a)), cy + 18 * sin(radians(a)), zb + 14), hook], 0.5, name="chain", color=steel, sides=8))
    return one(parts, "hanging basket")

for x, c in ((1100, "#d9576f"), (1300, "#f2f0ea"), (1480, "#9b6cc0")):
    basket(x, 1233, FL + 224, c)                         # the pergola's front beam
pot(1770, 2540, 0, r=28, h=55, plant="#5f7d4e", leaf=True)   # either side of the main door
pot(2000, 2540, 0, r=28, h=55, plant="#5f7d4e", leaf=True)
for x, y, c in ((840, 1640, "#d9576f"), (840, 2445, "#f2a7b5"), (1150, 2400, "#e8c43a")):
    pot(x, y, RZ, r=22, h=42, plant=c)                   # the roof terrace's corners, clear of the door
for x, y, c in ((755, 1445, "#d9576f"), (2335, 1445, "#9b6cc0"),      # by the deck where the paths start
                (985, 1165, "#f2a7b5"), (2115, 1165, "#d9576f"),      # the pool terrace's corners
                (405, 845, "#e8c43a"), (595, 845, "#f2f0ea")):        # where the path reaches the fire pit
    pot(x, y, 4 if 950 <= x <= 2150 and 890 <= y <= 1200 else 0, plant=c)

# ---------- lights by the paths ----------
for x, y in [(1770, 2750), (1770, 2900), (1990, 2750), (1990, 2900), (2550, 1450), (2850, 1100), (2850, 700), (620, 1250), (600, 1000)]:
    cylinder(4, 70, at=(x, y, 0), name="lamp post", color=dark, sides=12)
    box(16, 16, 14, at=(x - 8, y - 8, 70), name="lamp", color="#fff6d8")

# ---------- the fence: 1 m to the street with a gate and the drive's opening, 1.8 m elsewhere ----------
FC = "#6d5a48"
one(fence(w=1820, h=100, style="slats", at=(0, SY - 10, 0), color=FC).solids, "fence")
one(fence(w=480, h=100, style="slats", at=(1940, SY - 10, 0), color=FC).solids, "fence")
# the gate (closed), and the drive's sliding gate run back along the inside of the fence, open
gate = one(fence(w=120, h=100, style="boards", at=(1820, SY - 10, 0), color="#5a4a3c").solids, "gate")
box(4, 12, 14, at=(1928, SY - 22, 85), name="gate latch", color=steel)
slide = one(fence(w=350, h=100, style="boards", at=(2785, SY - 22, 4), color="#5a4a3c").solids, "sliding gate")
box(360, 6, 4, at=(2780, SY - 22, 0), name="gate rail", color=steel)
one(fence(w=SX - 2770, h=100, style="slats", at=(2770, SY - 10, 0), color=FC).solids, "fence")
one(fence(w=SY - 10, h=180, style="slats", at=(0, 0, 0), turn=90, color=FC).solids, "fence")
one(fence(w=SY - 10, h=180, style="slats", at=(SX - 9, 0, 0), turn=270, color=FC).solids, "fence")
one(fence(w=(SX - 18) / 2, h=180, style="slats", at=(9, 0, 0), color=FC).solids, "fence")
one(fence(w=(SX - 18) / 2, h=180, style="slats", at=(SX / 2, 0, 0), color=FC).solids, "fence")
