from pathlib import Path import json,html O=Path(__file__).parent;d=json.loads((O/('container-water-layout.json' if (O/'container-water-layout.json').exists() else 'garden-flow-layout.json')).read_text());p=[] p.append('A home held together by its garden.SAMFORD VALLEY / Aligned pavilions, soft garden curves and room to grow · Concept 004') def pt(x,y):return (600+x*13,500-y*13) def line(pts,color,w,dash=''): q=' '.join(f'{pt(x,y)[0]:.1f},{pt(x,y)[1]:.1f}' for x,y in pts);p.append(f'') def rect(cx,cy,w,h,fill='#d9c5a5',dash=''): x,y=pt(cx-w/2,cy+h/2);p.append(f'') def label(x,y,lines,size=11): X,Y=pt(x,y) for i,t in enumerate(lines):p.append(f'{html.escape(t)}') def ellipse(cx,cy,rx,ry,fill): x,y=pt(cx,cy);p.append(f'') line([(0,-23),(0,28)],'#c1cbbc',1,'stroke-dasharray="4 6"') ellipse(0,13.2,7.8,4.6,'#c7d4b5') for path in d['paths']: color='#c9bda6' if 'VEHICLE' in path['name'] else ('#b59b75' if path['kind']=='roofed walk' else '#d9d2bf') line(path['points'],color,path['width']*13) rect(2,-12.5,12,6,'#c9bda6') for bed in d['beds']: x,y=bed['center'];rx,ry=bed['radii'];ellipse(x,y,rx,ry,'#8fa981') for xx in [-.5,0,.5]:ellipse(x+xx*rx,y,.12,.12,'#d5b6b5') rect(*d['main']);rect(0,4.2,12,2.4,'#c5cfb6');label(0,4.4,['VERANDA'],11) rect(-3.8,.1,2.2,.95,'#f5f2e9');label(1,1,['MAIN HOUSE','Kitchen · dining','Living / reading'],10) rect(*d['parents']);label(-11.8,-4.4,['PARENTS','Bedroom + bath'],11) rect(-7.75,-1.8,3.5,1.2,'#c8bba5') rect(*d['meditation']);label(-11.8,12.5,['MEDITATION','ROOM'],10) for idx,shape in enumerate(d['future']): rect(*shape,'#e7ecdf','stroke-dasharray="5 5"');label(shape[0],shape[1]+.6,['FUTURE',f'PAVILION {idx+1}'],10) label(0,13.5,['SHARED','GARDEN COURT'],11) label(0,28,['ONE CENTRAL AXIS'],11) label(-16.7,18.6,['Curved covered','timber breezeway'],11) line([(-16.7,17),(-17,8.5),(-8.3,7.2)],'#8b9785',1) rect(*d['outdoor_table'],'#d9c49f');rect(10,3.5,3.2,1.03,'#a48663');label(10,9,['TABLE FOR TEN','Tea + matcha'],10) rect(*d['garage'],'#c7c2b3');rect(-1.7,-19.1,1.9,4.35,'#9da99c');label(1.5,-18.5,['GARAGE','Workshop','Bikes'],10) rect(*d['training'],'#c1ceb1');label(-9.4,-17.9,['TRAINING','Bar + rings','Weights + mats'],10) rect(*d['solar'],'#849a9d');label(9.4,-18.7,['GROUND SOLAR'],9) label(0,-25,['Aligned garage, training and energy zone'],11) x,y=d['sauna'];ellipse(x,y,2.2,2.0,'#ba9b75');label(x,y+.15,['SAUNA'],10) rect(-27,6.8,1.3,1.3,'#b0c5c1');label(-29,10,['Outdoor shower'],10) if 'water_container' in d: rect(*d['water_container'][:4],'#6d8563');label(-25,-13.8,['CONTAINER WATER','2 enclosed tanks'],10) else: for x in [-23.95,-26.25]:ellipse(x,-8,1,1,'#6d8563') label(-25,-10.8,['Rainwater tanks'],10) rect(24.2,-7.5,4.3,5,'#c3d7c2');label(24.2,-7.4,['GREENHOUSE'],9) for x in [23.8,27.5]: for y in [-1.7,1.5,4.7]:rect(x,y,2.3,1.3,'#ad9573') label(25.5,8.5,['FOOD GARDEN'],11) rect(29.8,1.5,.78,2.05,'#a78e6b');label(33.5,1.5,['Harvest bench'],10) rect(30,10.9,1.8,.45,'#a78e6b');label(31.5,16,['Quiet bench'],10) label(12,-7,['FLOWER','GARDEN'],10) rect(*d['future_basketball'],'#edf0e6','stroke-dasharray="6 6"');label(13,-35,['FUTURE','BASKETBALL','Unbuilt reserve'],11) label(27,-29,['4 m gravel access'],11) p.extend(['N','10 metres','13 planted garden beds · Smooth pedestrian routes · Future pavilions remain shadows · Table-tennis deck removed','Concept only: illustrative terrain, no surveyed boundary. Plant forms and structural connections require site-specific design development.']) (O/('container-water-plan.svg' if d['version']==5 else 'garden-flow-plan.svg')).write_text(''.join(p).replace('Concept 004','Concept 005' if d['version']==5 else 'Concept 004'))