wadi-mcp 0.1.4 → 0.1.7
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/dist/server.mjs +218 -40
- package/package.json +1 -1
package/dist/server.mjs
CHANGED
|
@@ -387483,6 +387483,12 @@ var Segment = {
|
|
|
387483
387483
|
hip_setback_start: "hip_setback_start",
|
|
387484
387484
|
id: "id",
|
|
387485
387485
|
min_overhang: "min_overhang",
|
|
387486
|
+
overhang_end: "overhang_end",
|
|
387487
|
+
overhang_high: "overhang_high",
|
|
387488
|
+
overhang_left: "overhang_left",
|
|
387489
|
+
overhang_low: "overhang_low",
|
|
387490
|
+
overhang_right: "overhang_right",
|
|
387491
|
+
overhang_start: "overhang_start",
|
|
387486
387492
|
shed_high_side: "shed_high_side",
|
|
387487
387493
|
start_endpoint: "start_endpoint",
|
|
387488
387494
|
sx: "sx",
|
|
@@ -388639,6 +388645,30 @@ var WadiAstReflection = class extends AbstractAstReflection {
|
|
|
388639
388645
|
name: Segment.min_overhang,
|
|
388640
388646
|
optional: true
|
|
388641
388647
|
},
|
|
388648
|
+
overhang_end: {
|
|
388649
|
+
name: Segment.overhang_end,
|
|
388650
|
+
optional: true
|
|
388651
|
+
},
|
|
388652
|
+
overhang_high: {
|
|
388653
|
+
name: Segment.overhang_high,
|
|
388654
|
+
optional: true
|
|
388655
|
+
},
|
|
388656
|
+
overhang_left: {
|
|
388657
|
+
name: Segment.overhang_left,
|
|
388658
|
+
optional: true
|
|
388659
|
+
},
|
|
388660
|
+
overhang_low: {
|
|
388661
|
+
name: Segment.overhang_low,
|
|
388662
|
+
optional: true
|
|
388663
|
+
},
|
|
388664
|
+
overhang_right: {
|
|
388665
|
+
name: Segment.overhang_right,
|
|
388666
|
+
optional: true
|
|
388667
|
+
},
|
|
388668
|
+
overhang_start: {
|
|
388669
|
+
name: Segment.overhang_start,
|
|
388670
|
+
optional: true
|
|
388671
|
+
},
|
|
388642
388672
|
shed_high_side: {
|
|
388643
388673
|
name: Segment.shed_high_side,
|
|
388644
388674
|
optional: true
|
|
@@ -394935,6 +394965,138 @@ var WadiGrammar = () => loadedWadiGrammar ?? (loadedWadiGrammar = loadGrammarFro
|
|
|
394935
394965
|
],
|
|
394936
394966
|
"cardinality": "?"
|
|
394937
394967
|
},
|
|
394968
|
+
{
|
|
394969
|
+
"$type": "Group",
|
|
394970
|
+
"elements": [
|
|
394971
|
+
{
|
|
394972
|
+
"$type": "Keyword",
|
|
394973
|
+
"value": "overhang_start"
|
|
394974
|
+
},
|
|
394975
|
+
{
|
|
394976
|
+
"$type": "Assignment",
|
|
394977
|
+
"feature": "overhang_start",
|
|
394978
|
+
"operator": "=",
|
|
394979
|
+
"terminal": {
|
|
394980
|
+
"$type": "RuleCall",
|
|
394981
|
+
"rule": {
|
|
394982
|
+
"$ref": "#/rules@12"
|
|
394983
|
+
},
|
|
394984
|
+
"arguments": []
|
|
394985
|
+
}
|
|
394986
|
+
}
|
|
394987
|
+
],
|
|
394988
|
+
"cardinality": "?"
|
|
394989
|
+
},
|
|
394990
|
+
{
|
|
394991
|
+
"$type": "Group",
|
|
394992
|
+
"elements": [
|
|
394993
|
+
{
|
|
394994
|
+
"$type": "Keyword",
|
|
394995
|
+
"value": "overhang_end"
|
|
394996
|
+
},
|
|
394997
|
+
{
|
|
394998
|
+
"$type": "Assignment",
|
|
394999
|
+
"feature": "overhang_end",
|
|
395000
|
+
"operator": "=",
|
|
395001
|
+
"terminal": {
|
|
395002
|
+
"$type": "RuleCall",
|
|
395003
|
+
"rule": {
|
|
395004
|
+
"$ref": "#/rules@12"
|
|
395005
|
+
},
|
|
395006
|
+
"arguments": []
|
|
395007
|
+
}
|
|
395008
|
+
}
|
|
395009
|
+
],
|
|
395010
|
+
"cardinality": "?"
|
|
395011
|
+
},
|
|
395012
|
+
{
|
|
395013
|
+
"$type": "Group",
|
|
395014
|
+
"elements": [
|
|
395015
|
+
{
|
|
395016
|
+
"$type": "Keyword",
|
|
395017
|
+
"value": "overhang_low"
|
|
395018
|
+
},
|
|
395019
|
+
{
|
|
395020
|
+
"$type": "Assignment",
|
|
395021
|
+
"feature": "overhang_low",
|
|
395022
|
+
"operator": "=",
|
|
395023
|
+
"terminal": {
|
|
395024
|
+
"$type": "RuleCall",
|
|
395025
|
+
"rule": {
|
|
395026
|
+
"$ref": "#/rules@12"
|
|
395027
|
+
},
|
|
395028
|
+
"arguments": []
|
|
395029
|
+
}
|
|
395030
|
+
}
|
|
395031
|
+
],
|
|
395032
|
+
"cardinality": "?"
|
|
395033
|
+
},
|
|
395034
|
+
{
|
|
395035
|
+
"$type": "Group",
|
|
395036
|
+
"elements": [
|
|
395037
|
+
{
|
|
395038
|
+
"$type": "Keyword",
|
|
395039
|
+
"value": "overhang_high"
|
|
395040
|
+
},
|
|
395041
|
+
{
|
|
395042
|
+
"$type": "Assignment",
|
|
395043
|
+
"feature": "overhang_high",
|
|
395044
|
+
"operator": "=",
|
|
395045
|
+
"terminal": {
|
|
395046
|
+
"$type": "RuleCall",
|
|
395047
|
+
"rule": {
|
|
395048
|
+
"$ref": "#/rules@12"
|
|
395049
|
+
},
|
|
395050
|
+
"arguments": []
|
|
395051
|
+
}
|
|
395052
|
+
}
|
|
395053
|
+
],
|
|
395054
|
+
"cardinality": "?"
|
|
395055
|
+
},
|
|
395056
|
+
{
|
|
395057
|
+
"$type": "Group",
|
|
395058
|
+
"elements": [
|
|
395059
|
+
{
|
|
395060
|
+
"$type": "Keyword",
|
|
395061
|
+
"value": "overhang_left"
|
|
395062
|
+
},
|
|
395063
|
+
{
|
|
395064
|
+
"$type": "Assignment",
|
|
395065
|
+
"feature": "overhang_left",
|
|
395066
|
+
"operator": "=",
|
|
395067
|
+
"terminal": {
|
|
395068
|
+
"$type": "RuleCall",
|
|
395069
|
+
"rule": {
|
|
395070
|
+
"$ref": "#/rules@12"
|
|
395071
|
+
},
|
|
395072
|
+
"arguments": []
|
|
395073
|
+
}
|
|
395074
|
+
}
|
|
395075
|
+
],
|
|
395076
|
+
"cardinality": "?"
|
|
395077
|
+
},
|
|
395078
|
+
{
|
|
395079
|
+
"$type": "Group",
|
|
395080
|
+
"elements": [
|
|
395081
|
+
{
|
|
395082
|
+
"$type": "Keyword",
|
|
395083
|
+
"value": "overhang_right"
|
|
395084
|
+
},
|
|
395085
|
+
{
|
|
395086
|
+
"$type": "Assignment",
|
|
395087
|
+
"feature": "overhang_right",
|
|
395088
|
+
"operator": "=",
|
|
395089
|
+
"terminal": {
|
|
395090
|
+
"$type": "RuleCall",
|
|
395091
|
+
"rule": {
|
|
395092
|
+
"$ref": "#/rules@12"
|
|
395093
|
+
},
|
|
395094
|
+
"arguments": []
|
|
395095
|
+
}
|
|
395096
|
+
}
|
|
395097
|
+
],
|
|
395098
|
+
"cardinality": "?"
|
|
395099
|
+
},
|
|
394938
395100
|
{
|
|
394939
395101
|
"$type": "Group",
|
|
394940
395102
|
"elements": [
|
|
@@ -395526,6 +395688,10 @@ function roofSegment(s) {
|
|
|
395526
395688
|
}
|
|
395527
395689
|
const mo = put("min_overhang", s.min_overhang);
|
|
395528
395690
|
if (mo !== void 0) seg.min_overhang = mo;
|
|
395691
|
+
for (const side2 of ["overhang_start", "overhang_end", "overhang_low", "overhang_high", "overhang_left", "overhang_right"]) {
|
|
395692
|
+
const v = put(side2, s[side2]);
|
|
395693
|
+
if (v !== void 0) seg[side2] = v;
|
|
395694
|
+
}
|
|
395529
395695
|
if (s.tie_beam_count !== void 0) seg.tie_beam_count = Math.round(s.tie_beam_count);
|
|
395530
395696
|
return done(seg, formulas);
|
|
395531
395697
|
}
|
|
@@ -411765,16 +411931,19 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411765
411931
|
const endRes = !endIsLeaf ? "joint" : seg.end_endpoint ?? defaultEndpoint;
|
|
411766
411932
|
const hipSetbackStart = startRes === "closed" ? seg.hip_setback_start ?? crossHalf : 0;
|
|
411767
411933
|
const hipSetbackEnd = endRes === "closed" ? seg.hip_setback_end ?? crossHalf : 0;
|
|
411768
|
-
const gableOverhangStart = startRes === "open" ? seg.gable_overhang_start ?? minOverhang : 0;
|
|
411769
|
-
const gableOverhangEnd = endRes === "open" ? seg.gable_overhang_end ?? minOverhang : 0;
|
|
411934
|
+
const gableOverhangStart = startRes === "open" ? seg.gable_overhang_start ?? seg.overhang_start ?? minOverhang : 0;
|
|
411935
|
+
const gableOverhangEnd = endRes === "open" ? seg.gable_overhang_end ?? seg.overhang_end ?? minOverhang : 0;
|
|
411770
411936
|
const dCrit = globalDCrit;
|
|
411771
411937
|
if (!(dCrit > 0)) {
|
|
411772
411938
|
throw new Error(`pitched segment ${seg.id}: dCrit=${dCrit} \u2014 hip setbacks must be > 0`);
|
|
411773
411939
|
}
|
|
411774
|
-
const eaveDrop = minOverhang * ridgeH / dCrit;
|
|
411775
|
-
const eaveZ = opts.wallTopZ - eaveDrop;
|
|
411776
411940
|
const ridgeZ = opts.wallTopZ + ridgeH;
|
|
411777
|
-
const
|
|
411941
|
+
const ohLeft = seg.overhang_left ?? minOverhang;
|
|
411942
|
+
const ohRight = seg.overhang_right ?? minOverhang;
|
|
411943
|
+
const eaveZLeft = opts.wallTopZ - ohLeft * ridgeH / dCrit;
|
|
411944
|
+
const eaveZRight = opts.wallTopZ - ohRight * ridgeH / dCrit;
|
|
411945
|
+
const oCrossLeft = ohLeft * crossHalf / dCrit;
|
|
411946
|
+
const oCrossRight = ohRight * crossHalf / dCrit;
|
|
411778
411947
|
const oStart = startRes === "closed" ? minOverhang * hipSetbackStart / dCrit : startRes === "open" ? gableOverhangStart : 0;
|
|
411779
411948
|
const oEnd = endRes === "closed" ? minOverhang * hipSetbackEnd / dCrit : endRes === "open" ? gableOverhangEnd : 0;
|
|
411780
411949
|
const unit = segmentUnitVector(seg);
|
|
@@ -411782,10 +411951,10 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411782
411951
|
const rightN = [-leftN[0], -leftN[1]];
|
|
411783
411952
|
const startBase = shift(seg.start, unit, -oStart);
|
|
411784
411953
|
const endBase = shift(seg.end, unit, +oEnd);
|
|
411785
|
-
const backLeft = shift(startBase, leftN, crossHalf +
|
|
411786
|
-
const backRight = shift(startBase, rightN, crossHalf +
|
|
411787
|
-
const frontLeft = shift(endBase, leftN, crossHalf +
|
|
411788
|
-
const frontRight = shift(endBase, rightN, crossHalf +
|
|
411954
|
+
const backLeft = shift(startBase, leftN, crossHalf + oCrossLeft);
|
|
411955
|
+
const backRight = shift(startBase, rightN, crossHalf + oCrossRight);
|
|
411956
|
+
const frontLeft = shift(endBase, leftN, crossHalf + oCrossLeft);
|
|
411957
|
+
const frontRight = shift(endBase, rightN, crossHalf + oCrossRight);
|
|
411789
411958
|
const ridgeStart2D = startRes === "closed" ? shift(seg.start, unit, +hipSetbackStart) : startRes === "open" ? shift(seg.start, unit, -gableOverhangStart) : seg.start;
|
|
411790
411959
|
const ridgeEnd2D = endRes === "closed" ? shift(seg.end, unit, -hipSetbackEnd) : endRes === "open" ? shift(seg.end, unit, +gableOverhangEnd) : seg.end;
|
|
411791
411960
|
const ridgeStart3D = to3D(ridgeStart2D, ridgeZ);
|
|
@@ -411793,10 +411962,10 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411793
411962
|
planes.push({
|
|
411794
411963
|
id: `${seg.id}.slope.left`,
|
|
411795
411964
|
vertices: [
|
|
411796
|
-
to3D(backLeft,
|
|
411965
|
+
to3D(backLeft, eaveZLeft),
|
|
411797
411966
|
ridgeStart3D,
|
|
411798
411967
|
ridgeEnd3D,
|
|
411799
|
-
to3D(frontLeft,
|
|
411968
|
+
to3D(frontLeft, eaveZLeft)
|
|
411800
411969
|
],
|
|
411801
411970
|
role: "slope",
|
|
411802
411971
|
source_segment_id: seg.id,
|
|
@@ -411804,15 +411973,15 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411804
411973
|
rafter_direction: normaliseVec3([
|
|
411805
411974
|
backLeft[0] - ridgeStart2D[0],
|
|
411806
411975
|
backLeft[1] - ridgeStart2D[1],
|
|
411807
|
-
|
|
411976
|
+
eaveZLeft - ridgeZ
|
|
411808
411977
|
]),
|
|
411809
411978
|
purlin_direction: [unit[0], unit[1], 0]
|
|
411810
411979
|
});
|
|
411811
411980
|
planes.push({
|
|
411812
411981
|
id: `${seg.id}.slope.right`,
|
|
411813
411982
|
vertices: [
|
|
411814
|
-
to3D(backRight,
|
|
411815
|
-
to3D(frontRight,
|
|
411983
|
+
to3D(backRight, eaveZRight),
|
|
411984
|
+
to3D(frontRight, eaveZRight),
|
|
411816
411985
|
ridgeEnd3D,
|
|
411817
411986
|
ridgeStart3D
|
|
411818
411987
|
],
|
|
@@ -411822,7 +411991,7 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411822
411991
|
rafter_direction: normaliseVec3([
|
|
411823
411992
|
backRight[0] - ridgeStart2D[0],
|
|
411824
411993
|
backRight[1] - ridgeStart2D[1],
|
|
411825
|
-
|
|
411994
|
+
eaveZRight - ridgeZ
|
|
411826
411995
|
]),
|
|
411827
411996
|
purlin_direction: [unit[0], unit[1], 0]
|
|
411828
411997
|
});
|
|
@@ -411896,8 +412065,8 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411896
412065
|
emitVentExtension(
|
|
411897
412066
|
ridgeMemberStart,
|
|
411898
412067
|
ridgeStart3D,
|
|
411899
|
-
to3D(backLeft,
|
|
411900
|
-
to3D(backRight,
|
|
412068
|
+
to3D(backLeft, eaveZLeft),
|
|
412069
|
+
to3D(backRight, eaveZRight),
|
|
411901
412070
|
ridgeExtStart,
|
|
411902
412071
|
"start"
|
|
411903
412072
|
);
|
|
@@ -411906,8 +412075,8 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411906
412075
|
emitVentExtension(
|
|
411907
412076
|
ridgeMemberEnd,
|
|
411908
412077
|
ridgeEnd3D,
|
|
411909
|
-
to3D(frontLeft,
|
|
411910
|
-
to3D(frontRight,
|
|
412078
|
+
to3D(frontLeft, eaveZLeft),
|
|
412079
|
+
to3D(frontRight, eaveZRight),
|
|
411911
412080
|
ridgeExtEnd,
|
|
411912
412081
|
"end"
|
|
411913
412082
|
);
|
|
@@ -411955,8 +412124,8 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411955
412124
|
planes.push({
|
|
411956
412125
|
id: `${seg.id}.hip_face.start`,
|
|
411957
412126
|
vertices: [
|
|
411958
|
-
to3D(backRight,
|
|
411959
|
-
to3D(backLeft,
|
|
412127
|
+
to3D(backRight, eaveZRight),
|
|
412128
|
+
to3D(backLeft, eaveZLeft),
|
|
411960
412129
|
ridgeStart3D
|
|
411961
412130
|
],
|
|
411962
412131
|
role: "hip_face",
|
|
@@ -411966,14 +412135,14 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
411966
412135
|
members.push({
|
|
411967
412136
|
id: `${seg.id}.hip.start.left`,
|
|
411968
412137
|
start: ridgeStart3D,
|
|
411969
|
-
end: to3D(backLeft,
|
|
412138
|
+
end: to3D(backLeft, eaveZLeft),
|
|
411970
412139
|
role: "hip",
|
|
411971
412140
|
source_segment_id: seg.id
|
|
411972
412141
|
});
|
|
411973
412142
|
members.push({
|
|
411974
412143
|
id: `${seg.id}.hip.start.right`,
|
|
411975
412144
|
start: ridgeStart3D,
|
|
411976
|
-
end: to3D(backRight,
|
|
412145
|
+
end: to3D(backRight, eaveZRight),
|
|
411977
412146
|
role: "hip",
|
|
411978
412147
|
source_segment_id: seg.id
|
|
411979
412148
|
});
|
|
@@ -412016,8 +412185,8 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
412016
412185
|
planes.push({
|
|
412017
412186
|
id: `${seg.id}.hip_face.end`,
|
|
412018
412187
|
vertices: [
|
|
412019
|
-
to3D(frontLeft,
|
|
412020
|
-
to3D(frontRight,
|
|
412188
|
+
to3D(frontLeft, eaveZLeft),
|
|
412189
|
+
to3D(frontRight, eaveZRight),
|
|
412021
412190
|
ridgeEnd3D
|
|
412022
412191
|
],
|
|
412023
412192
|
role: "hip_face",
|
|
@@ -412027,14 +412196,14 @@ function derivePitchedRoof(cfg, opts) {
|
|
|
412027
412196
|
members.push({
|
|
412028
412197
|
id: `${seg.id}.hip.end.left`,
|
|
412029
412198
|
start: ridgeEnd3D,
|
|
412030
|
-
end: to3D(frontLeft,
|
|
412199
|
+
end: to3D(frontLeft, eaveZLeft),
|
|
412031
412200
|
role: "hip",
|
|
412032
412201
|
source_segment_id: seg.id
|
|
412033
412202
|
});
|
|
412034
412203
|
members.push({
|
|
412035
412204
|
id: `${seg.id}.hip.end.right`,
|
|
412036
412205
|
start: ridgeEnd3D,
|
|
412037
|
-
end: to3D(frontRight,
|
|
412206
|
+
end: to3D(frontRight, eaveZRight),
|
|
412038
412207
|
role: "hip",
|
|
412039
412208
|
source_segment_id: seg.id
|
|
412040
412209
|
});
|
|
@@ -412145,13 +412314,13 @@ function resolveRise2(slope, run) {
|
|
|
412145
412314
|
function to3D3(pt, z2) {
|
|
412146
412315
|
return [pt[0], pt[1], z2];
|
|
412147
412316
|
}
|
|
412148
|
-
function extendSegment(seg,
|
|
412149
|
-
if (
|
|
412317
|
+
function extendSegment(seg, byStart, byEnd) {
|
|
412318
|
+
if (byStart === 0 && byEnd === 0) return seg;
|
|
412150
412319
|
const [ux, uy] = segmentUnitVector(seg);
|
|
412151
412320
|
return {
|
|
412152
412321
|
...seg,
|
|
412153
|
-
start: [seg.start[0] - ux *
|
|
412154
|
-
end: [seg.end[0] + ux *
|
|
412322
|
+
start: [seg.start[0] - ux * byStart, seg.start[1] - uy * byStart],
|
|
412323
|
+
end: [seg.end[0] + ux * byEnd, seg.end[1] + uy * byEnd]
|
|
412155
412324
|
};
|
|
412156
412325
|
}
|
|
412157
412326
|
function deriveShedRoof(cfg, opts) {
|
|
@@ -412179,15 +412348,20 @@ function deriveShedRoof(cfg, opts) {
|
|
|
412179
412348
|
throw new Error(`shed segment ${seg.id}: rise must be > 0`);
|
|
412180
412349
|
}
|
|
412181
412350
|
const overhang = seg.min_overhang ?? roofOverhang;
|
|
412182
|
-
const
|
|
412351
|
+
const ohStart = seg.overhang_start ?? overhang;
|
|
412352
|
+
const ohEnd = seg.overhang_end ?? overhang;
|
|
412353
|
+
const ohLow = seg.overhang_low ?? overhang;
|
|
412354
|
+
const ohHigh = seg.overhang_high ?? overhang;
|
|
412355
|
+
const eaveDropLow = ohLow * rise / run;
|
|
412356
|
+
const eaveDropHigh = ohHigh * rise / run;
|
|
412183
412357
|
const highSign = highSide === "left" ? 1 : -1;
|
|
412184
|
-
const highOffset = highSign * (seg.width / 2 +
|
|
412185
|
-
const lowOffset = -highSign * (seg.width / 2 +
|
|
412186
|
-
const extended = extendSegment(seg,
|
|
412358
|
+
const highOffset = highSign * (seg.width / 2 + ohHigh);
|
|
412359
|
+
const lowOffset = -highSign * (seg.width / 2 + ohLow);
|
|
412360
|
+
const extended = extendSegment(seg, ohStart, ohEnd);
|
|
412187
412361
|
const highEdge = offsetLine(extended, highOffset);
|
|
412188
412362
|
const lowEdge = offsetLine(extended, lowOffset);
|
|
412189
|
-
const zHigh = opts.wallTopZ + rise +
|
|
412190
|
-
const zLow = opts.wallTopZ -
|
|
412363
|
+
const zHigh = opts.wallTopZ + rise + eaveDropHigh;
|
|
412364
|
+
const zLow = opts.wallTopZ - eaveDropLow;
|
|
412191
412365
|
planes.push({
|
|
412192
412366
|
id: `${seg.id}.slope`,
|
|
412193
412367
|
vertices: [
|
|
@@ -415368,7 +415542,11 @@ function generateElevationView(houseConfig, viewType, scale = 2, flipX = false,
|
|
|
415368
415542
|
const ph = obj.height ?? floorHeight;
|
|
415369
415543
|
const pTopY = zToY(slabZ + ph);
|
|
415370
415544
|
const pBotY = zToY(slabZ);
|
|
415371
|
-
|
|
415545
|
+
const isSideView = viewType === "left" || viewType === "right";
|
|
415546
|
+
const pObjXWorld = isSideView ? obj.y : obj.x;
|
|
415547
|
+
const pObjW = isSideView ? obj.length : obj.width;
|
|
415548
|
+
const pSvgX = worldToSvgX(pObjXWorld, pObjW);
|
|
415549
|
+
svg += `<rect x="${fFloat(pSvgX)}" y="${fFloat(pTopY)}" width="${fFloat(pObjW)}" height="${fFloat(pBotY - pTopY)}" fill="#A0826D" stroke="#000" stroke-width="1"/>
|
|
415372
415550
|
`;
|
|
415373
415551
|
}
|
|
415374
415552
|
const floorName = floorConfig.name ?? `Floor ${floorNum}`;
|
|
@@ -417195,7 +417373,7 @@ var DOCS = {
|
|
|
417195
417373
|
},
|
|
417196
417374
|
"dsl": {
|
|
417197
417375
|
"title": "The Wadi DSL (.wdl) \u2014 syntax reference",
|
|
417198
|
-
"body": '# The Wadi DSL (`.wdl`) \u2014 authoring reference\n\nYou author houses in the **Wadi DSL** \u2014 a small, formal language (`.wdl`) that\ncompiles to a resolved `.wadi` (`house_config.json`). The DSL is **complete**:\nevery object type in the model has first-class syntax, so you rarely need the\n`raw` escape. Authoring the DSL is more direct and less error-prone than writing\nJSON \u2014 the grammar enforces structure, and `check.sh` reports parse errors with\nline:col.\n\n**This file is the SYNTAX reference.** The *semantics* live in the other\nreferences and apply unchanged \u2014 read them:\n\n- `coordinate-system.md` \u2014 X\u2192right, **Y\u2192DOWN**, Z\u2192up; **10 units = 1 ft**;\n the **centreline** convention. The #1 source of mistakes.\n- `conventions.md` \u2014 the **structural coding conventions** (`check.sh` enforces\n them): plinth-floor height must match the plinth block, rooms must wall every\n exterior side, a no-slab floor must set `slab_thickness 0`.\n- `parametric-conventions.md` \u2014 the grid-first recipe for reusable templates.\n- `roof-v2-guide.md` \u2014 roof segments, hip vs gable, trusses, joints.\n- `data-model.md` \u2014 the underlying `.wadi` schema (what the DSL compiles to; also\n the field reference for the `raw` escape).\n\n## The loop\n\n1. Write / edit `house.wdl` \u2014 the **single shared source** (you and the human\n co-edit it; the app\'s DSL previewer renders it live). You never produce a `.wadi`.\n2. `wadi-skill/architect/scripts/check.sh house.wdl` \u2014 runs the DSL compiler +\n validator (schema + wall/roof geometry) against a **throwaway temp** just for\n feedback; fix any reported error and re-run.\n3. `preview.sh house.wdl` \u2192 read the PNGs (plans / elevations / roof) to check your\n work. (It also compiles to a throwaway temp \u2014 no persistent `.wadi`.)\n\n## Skeleton\n\n```wdl\nhouse MyHouse {\n convention center // ALWAYS use center (wall-centreline coords)\n units feet_inches per_unit 10 // 10 project units = 1 ft\n site { plot (WIDTH, LENGTH) ref (0, 0) }\n defaults { floor_height 120 wall_height 108 slab_thickness 8 wall_thickness 8 }\n\n // parametric core (optional): var, point, grid, configurator\n // component / layer declarations (optional)\n floor 0 "Plinth" { \u2026 } // floors stack in source order (0 = plinth)\n floor 1 "Ground Floor" { \u2026 }\n floor 2 "Loft" { roof \u2026 } // roof lives ALONE on its own top floor\n}\n```\n\nNumbers are **project units** (feet \xD7 10 by default). Names after `house`,\n`room`, `pillar`, `var`, `point`, `grid`, and `use`/`component` are bare\nidentifiers (no spaces); names introduced with the `name` keyword are quoted\nstrings.\n\n## Parametric core (domain-neutral)\n\n```wdl\nvar wallT = 8 // a knob; may reference other vars\nvar pilInset = (pillarW - wallT) / 2\n\npoint House { x = 420, y = 470 } // reference as House.x / House.W / House.L\n // (.W = x, .L = y \u2014 a point doubles as a size)\n\ngrid main { // named wall centrelines; publishes main.x1 / main.yA\n x: 1 @ wallT / 2, 2 @ House.W / 2, 3 @ House.W - wallT / 2\n y: A @ wallT / 2, B @ House.L / 2, C @ House.L - wallT / 2\n}\n// each line may add: \u2026 @ <expr> thick <expr> role structural|planning\n\nconfigurator { // the knobs a downstream user turns\n slider pillarW "Column size" ft [8 .. 14 step 1]\n number ceiling "Ceiling height" ft\n toggle has_loft "Add a loft"\n select roof_style "Roof style" { Flat = 0, Shed = 1, Gable = 2, Hip = 3 }\n}\n```\n\n**Formulas are automatic.** Any geometry number can be a formula \u2014 just write the\nexpression instead of a literal (`at (main.x1, main.yA)`, `size (House.W/2, 200)`).\nOperators: `+ - * /`, unary `-`, parentheses, and the functions\n`min max clamp round floor ceil abs`. References: a `var`, a `point`\n(`House.W`), or a grid line (`main.x3 - main.x1`). No comparison operators \u2014 gate\nthings with the `min/abs` idiom (see `enabled` below).\n\n## Common attribute tail (every object)\n\nAfter an object\'s geometry, in THIS order, any of:\n\n```\n\u2026 z_offset <expr> enabled <expr> layer "id" [material "id"]\n```\n\n- `enabled <expr>` \u2014 the on/off switch. A `0`/`false` value hides the object. To\n gate on a configurator variable, use a 0/1 formula:\n `enabled 1 - min(1, abs(roof_style - 3))` renders the object only when\n `roof_style == 3`. (This is how one template carries several roofs and shows\n only the chosen one.)\n- `z_offset <expr>` \u2014 lift above the floor base (split levels).\n- `material "id"` \u2014 only on plinth / ground / room / wall / staircase / kitchen /\n roof.\n\n## Objects \u2014 structure & envelope\n\n```wdl\nslab [name "N"] at (x,y) size (w,l) [thickness <t>] // floor_slab\nbeam [name "N"] at (x,y) size (w,l) [height <h>]\nplinth [name "N"] at (x,y) size (w,l) height <h> // raised base (Plinth floor)\nground [name "N"] at (x,y) size (w,l) [height <h>] // terrain plane\npillar Name at (x,y) size (w,l) [height <h>] // (x,y) = TOP-LEFT corner\n```\n\n`at (x,y)` is the **TOP-LEFT CORNER** \u2014 **not the centre** \u2014 for every one of these\n(same as rooms/slabs/beams); `size (w,l)` is width \xD7 length. All accept the common tail.\n\n**Pillars catch people out here.** A column reads as "placed at a point," but `at` is\nstill its corner. To **centre a column on a point** `(cx, cy)` \u2014 a grid node, a room\ncorner \u2014 place it at **`at (cx - w/2, cy - l/2)`**, never at `(cx, cy)`. On a grid, the\n`pilInset` idiom (see `parametric-conventions.md`) does exactly this so columns sit flush.\n\n## Objects \u2014 rooms, walls & openings\n\nA room shows exactly the walls you declare. A **bare room (no `wall` lines) is\nenclosed on all four sides.** List plain walls compactly; give a wall its own line\nonly when it carries a door/window; omit a side to leave it open (verandah).\n\n```wdl\nroom Name at (x,y) size (w,l) [height <h>] [material "\u2026"] {\n wall east west north // plain walls \u2014 several in one statement\n wall south { door Main at <offset> size (w,h) [open] } // wall WITH openings: one side\n wall west { window W at <offset> size (w,h) [sill <s>] [open] }\n item asset { \u2026 } anchor center [gap (gx,gy)] // furniture anchored inside the room\n}\n```\n\n- `wall <side>\u2026` sides are `north|south|east|west`. A `wall <side>` line may also\n add `height <h>` / `height_end <h>` (sloped).\n- `door`/`window` `at <offset>` is measured along the wall from its start;\n `size (width, height)`; `window \u2026 sill <s>` sets the sill height; `open` = a bare\n hole (no leaf/glazing).\n\nA **free-standing wall** (not a room side):\n\n```wdl\nwall Name from (x1,y1) to (x2,y2) [height <h>] [height_end <h>] [facing north|\u2026] {\n \u2026 door/window openings \u2026\n}\n```\n\n- `from`/`to` are the wall\'s **centreline** endpoints; the wall is drawn as a rectangle\n `wall_thickness` wide, centred on that line.\n- **Overlap walls at corners \u2014 they do NOT auto-mitre.** Two free-standing walls that\n merely *touch* at a shared endpoint leave an unfilled square notch (\xBD\xB7`wall_thickness`)\n at the corner, because each is just a rectangle capped at its endpoint. To fill the\n corner, **extend the endpoints so the wall bodies OVERLAP** \u2014 run at least one wall\'s\n end **half the wall thickness past** the shared point (overlapping by the full thickness\n is fine and simplest). For an L of thickness 8 meeting at `(160,40)`:\n\n ```wdl\n wall H from (40, 40) to (164, 40) height 108 // ends 4 (\xBD\xB78) PAST the corner\n wall V from (160, 40) to (160, 160) height 108 // butts into H\'s overlapped body\n ```\n\n (Room walls handle their own corners; this only applies to `wall \u2026 from \u2026 to \u2026`.)\n\n## Objects \u2014 circulation & fittings\n\n```wdl\nstaircase [name "N"] at (start_x, start_y) step (rise, tread, width)\n direction north|south|east|west\n [total_height <h>] [max_run <r>] [landing_depth <d>]\n [landing_thickness <t>] [turn clockwise|anticlockwise] [flight_gap <g>]\n\nkitchen [name "N"] path ((x,y), (x,y), \u2026) side left|right\n depth <d> height <h> [base_z <z>] // path points are literal numbers\n```\n\n**Staircases are TOP-anchored \u2014 this is the #1 mistake.** You put a staircase on the\n**UPPER** floor and it **DESCENDS** to the floor below:\n\n- `at (x,y)` is the **TOP** of the stair (where it meets the floor it\'s declared on).\n- `direction` is the **descent** direction (the way it travels going *down*).\n- `total_height` is the **drop** to the floor below (omit \u2192 the floor-below\'s height).\n- `max_run` caps a flight\'s run; exceed it and the stair auto-splits into switchback\n flights with turn landings (`landing_depth`/`turn`/`flight_gap` tune the switchback).\n\nSo a stair connecting the ground floor **up** to the first floor lives on the **First\nFloor**, descending to the ground:\n\n```wdl\nfloor 2 "First Floor" height 116 {\n slab at (\u2026) size (\u2026)\n staircase name "Stair" at (212, 64) step (7, 11, 44) // top = this floor, at the landing\n direction south total_height 116 // descends south to the floor below\n}\n```\n\nPut it on the *lower* floor (thinking of it as "climbing up") and it descends the wrong way\n\u2014 **below ground** \u2014 where it draws in 2D plans but is buried/invisible in 3D. `check.sh`\ncatches that (convention **C5**), but author it top-anchored from the start.\n\nitem [name "N"] asset { id "sofa" src "furniture/sofa.glb" dims (w,h,d) [category "\u2026"] }\n at (x,y) [rotation <deg>] [scale <s>]\n [anchor_to "RoomName" anchor center gap (gx,gy)]\n```\n\nFurniture `dims` are the real-world size in **metres** `(width, height, depth)`;\n`src` is a GLB URL (bundled ids resolve at `furniture/<id>.glb` \u2014 e.g. `sofa`,\n`bed_double`, `dining_table`; an unreachable GLB shows a placeholder box, never a\nblank). `anchor` is one of `top-left top-center top-right center-left center\ncenter-right bottom-left bottom-center bottom-right`.\n\n## Objects \u2014 roof (one object; flat / shed / gable / hip)\n\nThe roof lives ALONE on its own top floor and you never set its Z (see\n`roof-v2-guide.md`). `endpoint`: `closed` = hip triangle, `open` = gable end-wall.\n\n```wdl\nroof [name "N"] pitched|shed|flat\n [endpoint open|closed]\n [slope angle <deg> | slope height <ridge_h>]\n [overhang <o>] [slab_thickness <t>] [parapet <h> x <t>] [gable_wall_thickness <t>] {\n segment "id" from (x,y) to (x,y) width <w>\n [high_side left|right] // shed only\n [start_endpoint open|closed] [end_endpoint open|closed]\n [hip_setback (a,b)] [gable_overhang (a,b)] [hip_ridge_extension (a,b)]\n [overhang <o>] [tie_beams N]\n truss "segId" fink|mono_pitch at (pos, pos, \u2026)\n }\n```\n\nSegment `from`/`to`/`width` and the `hip_setback`/\u2026 values accept formulas, so a\nroof scales with the plot (e.g. `width House.W`, `hip_setback (Verandah.L, Padvi.L)`).\n\n**Roof coordinates are wall centrelines (under `convention center`), same as\nrooms.** `from`/`to` is the segment\'s ridge/axis and `width` its span *centred on\nthat axis*. Author them on the **same centreline grid as the walls** \u2014 a segment\nwhose axis + width match the rooms\' centrelines auto-grows to the **outer wall\nface** on every side (the compiler extends the axis by \xBD\xB7wall_thickness at each end\nand widens by wall_thickness, exactly the grow a room gets). `overhang` then\nextends *beyond* the outer face. So to cover a footprint spanning wall centrelines\n`x1..x2` (E\u2013W) and `yA..yB` (N\u2013S), write `from (x1, (yA+yB)/2) to (x2, (yA+yB)/2)\nwidth (yB - yA)` \u2014 do **not** add \xBD-wall fudge factors; the convention handles it.\n(Before this, a roof drawn on the grid sat half a wall-thickness *inside* the walls.)\n\n## Components & layers\n\n```wdl\ncomponent Bench { // a reusable mini-house in LOCAL coords (origin 0,0)\n param blen = 60 label "Bench length"\n beam name "Top" at (0,0) size (blen, 18) height 6\n}\nuse Bench as "B1" at (x,y) with { blen = 80 } // stamp it onto a floor\n\nlayer "structure" "Structure" [color "#rrggbb"] [group "Frame"] // per-house layer registry\n```\n\n## The `raw` escape (rarely needed)\n\nAnything the first-class syntax doesn\'t cover can be written as literal JSON per\nthe `.wadi` schema (`data-model.md`):\n\n```wdl\nraw "type" { "field": 1, "formulas": { "field": "= expr" } }\n```\n\n## DSL-specific pitfalls\n\n- **`convention center` and `units \u2026 per_unit 10`** belong at the top of every\n `house` \u2014 same as the JSON path. All the `coordinate-system.md` rules (Y-down,\n units, centreline abutment) apply identically; the DSL just writes them shorter.\n- **Formulas are bare expressions**, not `"= \u2026"` strings \u2014 the compiler emits the\n `= \u2026` form for you. Write `at (main.x1, main.yA)`, not `at ("= main.x1", \u2026)`.\n- **`name "\u2026"` is quoted; `room`/`pillar`/`var`/grid-line names are bare** ids\n (no spaces, and not a reserved word like `width`, `height`, `size`, `at`).\n- **Roof alone on the top floor**; segment widths/positions come from the walls\n they sit on. See `roof-v2-guide.md`.\n- **A pillar\'s `at` is its TOP-LEFT corner, not its centre.** To centre a column on\n `(cx,cy)`, author `at (cx - w/2, cy - l/2)`.\n- **Free-standing walls don\'t auto-mitre at corners** \u2014 extend endpoints so the wall\n bodies overlap (\u2265 \xBD\xB7`wall_thickness` past the shared point), or the corner is left\n as a gap.\n- **Staircases are top-anchored** \u2014 put them on the UPPER floor; they descend to the\n floor below (`check.sh` C5 flags one that lands below ground). See the staircase note.\n- **Structural conventions are enforced** \u2014 `check.sh` fails on floating floors\n (plinth-floor `height` \u2260 plinth block height; a no-slab floor with nonzero\n `slab_thickness`) and warns on exterior room sides left open. See\n `conventions.md`; the DSL editor shows the same findings in its status pill.\n- Compile after **every** edit; a parse error means the `.wadi` wasn\'t updated, so\n the live model just won\'t change \u2014 never silently wrong.\n'
|
|
417376
|
+
"body": '# The Wadi DSL (`.wdl`) \u2014 authoring reference\n\nYou author houses in the **Wadi DSL** \u2014 a small, formal language (`.wdl`) that\ncompiles to a resolved `.wadi` (`house_config.json`). The DSL is **complete**:\nevery object type in the model has first-class syntax, so you rarely need the\n`raw` escape. Authoring the DSL is more direct and less error-prone than writing\nJSON \u2014 the grammar enforces structure, and `check.sh` reports parse errors with\nline:col.\n\n**This file is the SYNTAX reference.** The *semantics* live in the other\nreferences and apply unchanged \u2014 read them:\n\n- `coordinate-system.md` \u2014 X\u2192right, **Y\u2192DOWN**, Z\u2192up; **10 units = 1 ft**;\n the **centreline** convention. The #1 source of mistakes.\n- `conventions.md` \u2014 the **structural coding conventions** (`check.sh` enforces\n them): plinth-floor height must match the plinth block, rooms must wall every\n exterior side, a no-slab floor must set `slab_thickness 0`.\n- `parametric-conventions.md` \u2014 the grid-first recipe for reusable templates.\n- `roof-v2-guide.md` \u2014 roof segments, hip vs gable, trusses, joints.\n- `data-model.md` \u2014 the underlying `.wadi` schema (what the DSL compiles to; also\n the field reference for the `raw` escape).\n\n## The loop\n\n1. Write / edit `house.wdl` \u2014 the **single shared source** (you and the human\n co-edit it; the app\'s DSL previewer renders it live). You never produce a `.wadi`.\n2. `wadi-skill/architect/scripts/check.sh house.wdl` \u2014 runs the DSL compiler +\n validator (schema + wall/roof geometry) against a **throwaway temp** just for\n feedback; fix any reported error and re-run.\n3. `preview.sh house.wdl` \u2192 read the PNGs (plans / elevations / roof) to check your\n work. (It also compiles to a throwaway temp \u2014 no persistent `.wadi`.)\n\n## Skeleton\n\n```wdl\nhouse MyHouse {\n convention center // ALWAYS use center (wall-centreline coords)\n units feet_inches per_unit 10 // 10 project units = 1 ft\n site { plot (WIDTH, LENGTH) ref (0, 0) }\n defaults { floor_height 120 wall_height 108 slab_thickness 8 wall_thickness 8 }\n\n // parametric core (optional): var, point, grid, configurator\n // component / layer declarations (optional)\n floor 0 "Plinth" { \u2026 } // floors stack in source order (0 = plinth)\n floor 1 "Ground Floor" { \u2026 }\n floor 2 "Loft" { roof \u2026 } // roof lives ALONE on its own top floor\n}\n```\n\nNumbers are **project units** (feet \xD7 10 by default). Names after `house`,\n`room`, `pillar`, `var`, `point`, `grid`, and `use`/`component` are bare\nidentifiers (no spaces); names introduced with the `name` keyword are quoted\nstrings.\n\n## Parametric core (domain-neutral)\n\n```wdl\nvar wallT = 8 // a knob; may reference other vars\nvar pilInset = (pillarW - wallT) / 2\n\npoint House { x = 420, y = 470 } // reference as House.x / House.W / House.L\n // (.W = x, .L = y \u2014 a point doubles as a size)\n\ngrid main { // named wall centrelines; publishes main.x1 / main.yA\n x: 1 @ wallT / 2, 2 @ House.W / 2, 3 @ House.W - wallT / 2\n y: A @ wallT / 2, B @ House.L / 2, C @ House.L - wallT / 2\n}\n// each line may add: \u2026 @ <expr> thick <expr> role structural|planning\n\nconfigurator { // the knobs a downstream user turns\n slider pillarW "Column size" ft [8 .. 14 step 1]\n number ceiling "Ceiling height" ft\n toggle has_loft "Add a loft"\n select roof_style "Roof style" { Flat = 0, Shed = 1, Gable = 2, Hip = 3 }\n}\n```\n\n**Formulas are automatic.** Any geometry number can be a formula \u2014 just write the\nexpression instead of a literal (`at (main.x1, main.yA)`, `size (House.W/2, 200)`).\nOperators: `+ - * /`, unary `-`, parentheses, and the functions\n`min max clamp round floor ceil abs`. References: a `var`, a `point`\n(`House.W`), or a grid line (`main.x3 - main.x1`). No comparison operators \u2014 gate\nthings with the `min/abs` idiom (see `enabled` below).\n\n## Common attribute tail (every object)\n\nAfter an object\'s geometry, in THIS order, any of:\n\n```\n\u2026 z_offset <expr> enabled <expr> layer "id" [material "id"]\n```\n\n- `enabled <expr>` \u2014 the on/off switch. A `0`/`false` value hides the object. To\n gate on a configurator variable, use a 0/1 formula:\n `enabled 1 - min(1, abs(roof_style - 3))` renders the object only when\n `roof_style == 3`. (This is how one template carries several roofs and shows\n only the chosen one.)\n- `z_offset <expr>` \u2014 lift above the floor base (split levels).\n- `material "id"` \u2014 only on plinth / ground / room / wall / staircase / kitchen /\n roof.\n\n## Objects \u2014 structure & envelope\n\n```wdl\nslab [name "N"] at (x,y) size (w,l) [thickness <t>] // floor_slab\nbeam [name "N"] at (x,y) size (w,l) [height <h>]\nplinth [name "N"] at (x,y) size (w,l) height <h> // raised base (Plinth floor)\nground [name "N"] at (x,y) size (w,l) [height <h>] // terrain plane\npillar Name at (x,y) size (w,l) [height <h>] // (x,y) = TOP-LEFT corner\n```\n\n`at (x,y)` is the **TOP-LEFT CORNER** \u2014 **not the centre** \u2014 for every one of these\n(same as rooms/slabs/beams); `size (w,l)` is width \xD7 length. All accept the common tail.\n\n**Pillars catch people out here.** A column reads as "placed at a point," but `at` is\nstill its corner. To **centre a column on a point** `(cx, cy)` \u2014 a grid node, a room\ncorner \u2014 place it at **`at (cx - w/2, cy - l/2)`**, never at `(cx, cy)`. On a grid, the\n`pilInset` idiom (see `parametric-conventions.md`) does exactly this so columns sit flush.\n\n## Objects \u2014 rooms, walls & openings\n\nA room shows exactly the walls you declare. A **bare room (no `wall` lines) is\nenclosed on all four sides.** List plain walls compactly; give a wall its own line\nonly when it carries a door/window; omit a side to leave it open (verandah).\n\n```wdl\nroom Name at (x,y) size (w,l) [height <h>] [material "\u2026"] {\n wall east west north // plain walls \u2014 several in one statement\n wall south { door Main at <offset> size (w,h) [open] } // wall WITH openings: one side\n wall west { window W at <offset> size (w,h) [sill <s>] [open] }\n item asset { \u2026 } anchor center [gap (gx,gy)] // furniture anchored inside the room\n}\n```\n\n- `wall <side>\u2026` sides are `north|south|east|west`. A `wall <side>` line may also\n add `height <h>` / `height_end <h>` (sloped).\n- `door`/`window` `at <offset>` is measured along the wall from its start;\n `size (width, height)`; `window \u2026 sill <s>` sets the sill height; `open` = a bare\n hole (no leaf/glazing).\n\nA **free-standing wall** (not a room side):\n\n```wdl\nwall Name from (x1,y1) to (x2,y2) [height <h>] [height_end <h>] [facing north|\u2026] {\n \u2026 door/window openings \u2026\n}\n```\n\n- `from`/`to` are the wall\'s **centreline** endpoints; the wall is drawn as a rectangle\n `wall_thickness` wide, centred on that line.\n- **Overlap walls at corners \u2014 they do NOT auto-mitre.** Two free-standing walls that\n merely *touch* at a shared endpoint leave an unfilled square notch (\xBD\xB7`wall_thickness`)\n at the corner, because each is just a rectangle capped at its endpoint. To fill the\n corner, **extend the endpoints so the wall bodies OVERLAP** \u2014 run at least one wall\'s\n end **half the wall thickness past** the shared point (overlapping by the full thickness\n is fine and simplest). For an L of thickness 8 meeting at `(160,40)`:\n\n ```wdl\n wall H from (40, 40) to (164, 40) height 108 // ends 4 (\xBD\xB78) PAST the corner\n wall V from (160, 40) to (160, 160) height 108 // butts into H\'s overlapped body\n ```\n\n (Room walls handle their own corners; this only applies to `wall \u2026 from \u2026 to \u2026`.)\n\n## Objects \u2014 circulation & fittings\n\n```wdl\nstaircase [name "N"] at (start_x, start_y) step (rise, tread, width)\n direction north|south|east|west\n [total_height <h>] [max_run <r>] [landing_depth <d>]\n [landing_thickness <t>] [turn clockwise|anticlockwise] [flight_gap <g>]\n\nkitchen [name "N"] path ((x,y), (x,y), \u2026) side left|right\n depth <d> height <h> [base_z <z>] // path points are literal numbers\n```\n\n**Staircases are TOP-anchored \u2014 this is the #1 mistake.** You put a staircase on the\n**UPPER** floor and it **DESCENDS** to the floor below:\n\n- `at (x,y)` is the **TOP** of the stair (where it meets the floor it\'s declared on).\n- `direction` is the **descent** direction (the way it travels going *down*).\n- `total_height` is the **drop** to the floor below (omit \u2192 the floor-below\'s height).\n- `max_run` caps a flight\'s run; exceed it and the stair auto-splits into switchback\n flights with turn landings (`landing_depth`/`turn`/`flight_gap` tune the switchback).\n\nSo a stair connecting the ground floor **up** to the first floor lives on the **First\nFloor**, descending to the ground:\n\n```wdl\nfloor 2 "First Floor" height 116 {\n slab at (\u2026) size (\u2026)\n staircase name "Stair" at (212, 64) step (7, 11, 44) // top = this floor, at the landing\n direction south total_height 116 // descends south to the floor below\n}\n```\n\nPut it on the *lower* floor (thinking of it as "climbing up") and it descends the wrong way\n\u2014 **below ground** \u2014 where it draws in 2D plans but is buried/invisible in 3D. `check.sh`\ncatches that (convention **C5**), but author it top-anchored from the start.\n\nitem [name "N"] asset { id "sofa" src "furniture/sofa.glb" dims (w,h,d) [category "\u2026"] }\n at (x,y) [rotation <deg>] [scale <s>]\n [anchor_to "RoomName" anchor center gap (gx,gy)]\n```\n\nFurniture `dims` are the real-world size in **metres** `(width, height, depth)`;\n`src` is a GLB URL (bundled ids resolve at `furniture/<id>.glb` \u2014 e.g. `sofa`,\n`bed_double`, `dining_table`; an unreachable GLB shows a placeholder box, never a\nblank). `anchor` is one of `top-left top-center top-right center-left center\ncenter-right bottom-left bottom-center bottom-right`.\n\n## Objects \u2014 roof (one object; flat / shed / gable / hip)\n\nThe roof lives ALONE on its own top floor and you never set its Z (see\n`roof-v2-guide.md`). `endpoint`: `closed` = hip triangle, `open` = gable end-wall.\n\n```wdl\nroof [name "N"] pitched|shed|flat\n [endpoint open|closed]\n [slope angle <deg> | slope height <ridge_h>]\n [overhang <o>] [slab_thickness <t>] [parapet <h> x <t>] [gable_wall_thickness <t>] {\n segment "id" from (x,y) to (x,y) width <w>\n [high_side left|right] // shed only\n [start_endpoint open|closed] [end_endpoint open|closed]\n [hip_setback (a,b)] [gable_overhang (a,b)] [hip_ridge_extension (a,b)]\n [overhang <o>] // uniform eave, all four sides\n [overhang_start <o>] [overhang_end <o>] // per-side along the axis (shed;\n // on a gable end = gable_overhang)\n [overhang_low <o>] [overhang_high <o>] // SHED eaves (down-slope / up-slope)\n [overhang_left <o>] [overhang_right <o>] // PITCHED eaves (left / right of ridge)\n [tie_beams N]\n truss "segId" fink|mono_pitch at (pos, pos, \u2026)\n }\n```\n\nSegment `from`/`to`/`width` and the `hip_setback`/\u2026 values accept formulas, so a\nroof scales with the plot (e.g. `width House.W`, `hip_setback (Verandah.L, Padvi.L)`).\n\n**Per-side overhang (cantilever one edge).** `overhang <o>` sets a uniform eave on\nall four sides. Any sloping roof can override a side independently \u2014 each defaults to\n`overhang`. **Along the axis:** `overhang_start` / `overhang_end` (on a shed, or a\ngable open end \u2014 there they\'re the same as `gable_overhang`; a hip end is geometric,\ntuned via `hip_setback`). **Eaves:** `overhang_low` / `overhang_high` on a **shed**\n(down-slope / up-slope); `overhang_left` / `overhang_right` on a **pitched** roof\n(the two eaves either side of the ridge). A bigger eave overhang also drops that\neave\'s edge along the same pitch, so the slope stays planar. (Per-eave on a *pitched*\nroof is single-segment only \u2014 on a multi-segment roof the eaves share one height so\njoints line up.)\nIdiom: keep the roof FOOTPRINT (its supported edges) on the main room, then cantilever\none eave to cover an entry landing / stair \u2014 end the axis on the room wall and set a big\n`overhang_end`:\n```wdl\n// footprint ends on the main room\'s east wall (x204 centreline \u2192 x208 outer);\n// the east eave reaches 258, covering a landing that sticks out to x256.\nsegment "seg0" from (4,124) to (204,124) width 240 high_side right overhang 25 overhang_end 50\n```\n\n**Roof coordinates are wall centrelines (under `convention center`), same as\nrooms.** `from`/`to` is the segment\'s ridge/axis and `width` its span *centred on\nthat axis*. Author them on the **same centreline grid as the walls** \u2014 a segment\nwhose axis + width match the rooms\' centrelines auto-grows to the **outer wall\nface** on every side (the compiler extends the axis by \xBD\xB7wall_thickness at each end\nand widens by wall_thickness, exactly the grow a room gets). `overhang` then\nextends *beyond* the outer face. So to cover a footprint spanning wall centrelines\n`x1..x2` (E\u2013W) and `yA..yB` (N\u2013S), write `from (x1, (yA+yB)/2) to (x2, (yA+yB)/2)\nwidth (yB - yA)` \u2014 do **not** add \xBD-wall fudge factors; the convention handles it.\n(Before this, a roof drawn on the grid sat half a wall-thickness *inside* the walls.)\n\n## Components & layers\n\n```wdl\ncomponent Bench { // a reusable mini-house in LOCAL coords (origin 0,0)\n param blen = 60 label "Bench length"\n beam name "Top" at (0,0) size (blen, 18) height 6\n}\nuse Bench as "B1" at (x,y) with { blen = 80 } // stamp it onto a floor\n\nlayer "structure" "Structure" [color "#rrggbb"] [group "Frame"] // per-house layer registry\n```\n\n## The `raw` escape (rarely needed)\n\nAnything the first-class syntax doesn\'t cover can be written as literal JSON per\nthe `.wadi` schema (`data-model.md`):\n\n```wdl\nraw "type" { "field": 1, "formulas": { "field": "= expr" } }\n```\n\n## DSL-specific pitfalls\n\n- **`convention center` and `units \u2026 per_unit 10`** belong at the top of every\n `house` \u2014 same as the JSON path. All the `coordinate-system.md` rules (Y-down,\n units, centreline abutment) apply identically; the DSL just writes them shorter.\n- **Formulas are bare expressions**, not `"= \u2026"` strings \u2014 the compiler emits the\n `= \u2026` form for you. Write `at (main.x1, main.yA)`, not `at ("= main.x1", \u2026)`.\n- **`name "\u2026"` is quoted; `room`/`pillar`/`var`/grid-line names are bare** ids\n (no spaces, and not a reserved word like `width`, `height`, `size`, `at`).\n- **Roof alone on the top floor**; segment widths/positions come from the walls\n they sit on. See `roof-v2-guide.md`.\n- **A pillar\'s `at` is its TOP-LEFT corner, not its centre.** To centre a column on\n `(cx,cy)`, author `at (cx - w/2, cy - l/2)`.\n- **Free-standing walls don\'t auto-mitre at corners** \u2014 extend endpoints so the wall\n bodies overlap (\u2265 \xBD\xB7`wall_thickness` past the shared point), or the corner is left\n as a gap.\n- **Staircases are top-anchored** \u2014 put them on the UPPER floor; they descend to the\n floor below (`check.sh` C5 flags one that lands below ground). See the staircase note.\n- **Structural conventions are enforced** \u2014 `check.sh` fails on floating floors\n (plinth-floor `height` \u2260 plinth block height; a no-slab floor with nonzero\n `slab_thickness`) and warns on exterior room sides left open. See\n `conventions.md`; the DSL editor shows the same findings in its status pill.\n- Compile after **every** edit; a parse error means the `.wadi` wasn\'t updated, so\n the live model just won\'t change \u2014 never silently wrong.\n'
|
|
417199
417377
|
},
|
|
417200
417378
|
"conventions": {
|
|
417201
417379
|
"title": "Structural coding conventions (C1/C2/C3\u2026)",
|
|
@@ -417211,7 +417389,7 @@ var DOCS = {
|
|
|
417211
417389
|
},
|
|
417212
417390
|
"roof-v2-guide": {
|
|
417213
417391
|
"title": "The roof object (hip/gable/shed/flat)",
|
|
417214
|
-
"body": '# Roof v2 guide (the unified `roof` object)\n\nThe `roof` object replaces the legacy `hip_roof`/`gable_roof`/`flat_roof`/\n`shed_roof`. It is **segment-based**. The schema is permissive; the real\nvalidation happens in the compute pipeline, so **always run `validate.mjs`**\nafter writing a roof (it runs the exact derivation the app uses).\n\n> **Strategy:** roof geometry is the subtlest part of the config. Don\'t\n> hand-invent it \u2014 **copy the roof from the closest example** (see the shape map\n> below), then adjust its segment coordinates/widths to your plinth, and validate.\n\n## Where the roof lives, and its height (you do NOT set a Z)\n\nRead this first \u2014 it\'s the part that isn\'t obvious and that you must not try to\nreverse-engineer from the geometry.\n\n- **A roof object goes on its OWN top floor** \u2014 a floor whose `floor_number` is\n ABOVE every floor it covers, containing only the roof object(s). Name it\n something like `"Roof"` or `"Loft Floor"`. Example: ground = 0, first = 1 \u2192 the\n roof floor is `floor_number` **2**. The roof then covers the floors below it.\n (All the examples do this: the roof sits alone on the topmost floor.)\n- **You NEVER write a Z or height on the roof.** There is no Z field anywhere in\n the roof object. Its base sits automatically at the top of the walls below it,\n computed by the pipeline as:\n\n ```\n roof base Z = plinth.height + \u03A3 (height of every floor BELOW the roof\'s floor)\n ```\n\n using each floor\'s own `height` (falling back to `defaults.floor_height`).\n **`slab_thickness` does NOT enter this stack** \u2014 `height` already is the full\n floor-to-floor rise.\n- **To raise or lower the roof, change the FLOORS\' `height` (or `plinth.height`),\n not the roof.** If the roof floats too low/high, you put it on the wrong floor\n or a floor below it has the wrong `height`.\n- `slope` / `ridge_h` is the rise **above** that computed wall-top; segment\n `start`/`end` are 2D `[x, y]` only. Don\'t compute a Z \u2014 the pipeline does it\n entirely from the floor stack.\n\n## The core idea\n\nA roof is a set of **segments**. Each segment is a line (`start`\u2192`end`) with a\n**`width`** measured *perpendicular* to it, and **the segment sits at the CENTRE\nof that width** (the roof extends `width/2` to each side of the line). What the\nline means depends on `roof_type`:\n\n- **pitched** \u2014 the segment **is the ridge**; two symmetric slopes rise from the\n eaves up to it. Ends are covered per `default_endpoint`:\n - `"closed"` \u2192 **hip** (a sloped triangular hip face at the end).\n - `"open"` \u2192 **gable** (a vertical gable-end wall triangle).\n- **shed** \u2014 the segment is the **high edge**; the roof slopes down perpendicular\n to the far side. `shed_high_side` names which side is high.\n- **flat** \u2014 the segment\'s width band is a flat slab extruded down.\n\nSegments can run in **any direction** (not just axis-aligned).\n\n**Centreline convention (`coord_convention: "center"`).** A segment\'s\n`start`/`end`/`width` are **wall centrelines**, exactly like a room\'s\n`x/y/width/length`. On expand, each segment grows to the **outer wall face** \u2014\nthe axis is extended by \xBD\xB7`wall_thickness` at each end and `width` grows by\n`wall_thickness` \u2014 then `overhang` extends beyond that. So author segments on the\n**same centreline grid as the walls below** (e.g. `width House.W`, axis endpoints\non grid lines); do **not** hand-add \xBD-wall offsets, or the roof lands half a wall\nthickness inside the walls. (In the default `"outer"` convention the coordinates\nare literal outer-face positions and no grow happens.)\n\n## Object shape\n\n```jsonc\n{\n "type": "roof",\n "roof_type": "pitched" | "shed" | "flat",\n "default_endpoint": "closed" | "open", // pitched only: closed=hip, open=gable\n "min_overhang": 25, // > 0 \u2014 eave overhang beyond the walls\n "slope": { "by": "height", "ridge_h": 50 }, // rise above wall top \u2026\n // or { "by": "angle", "angle_deg": 30 }, // \u2026 or a pitch angle\n "segments": [\n {\n "id": "seg0",\n "start": [x, y], "end": [x, y], // the ridge (pitched) / high edge (shed)\n "width": 200, // perpendicular span, segment centred in it\n "slope": { \u2026 }, // optional per-segment slope override\n "shed_high_side": "\u2026", // shed only\n "hip_setback_start": \u2026, "hip_setback_end": \u2026
|
|
417392
|
+
"body": '# Roof v2 guide (the unified `roof` object)\n\nThe `roof` object replaces the legacy `hip_roof`/`gable_roof`/`flat_roof`/\n`shed_roof`. It is **segment-based**. The schema is permissive; the real\nvalidation happens in the compute pipeline, so **always run `validate.mjs`**\nafter writing a roof (it runs the exact derivation the app uses).\n\n> **Strategy:** roof geometry is the subtlest part of the config. Don\'t\n> hand-invent it \u2014 **copy the roof from the closest example** (see the shape map\n> below), then adjust its segment coordinates/widths to your plinth, and validate.\n\n## Where the roof lives, and its height (you do NOT set a Z)\n\nRead this first \u2014 it\'s the part that isn\'t obvious and that you must not try to\nreverse-engineer from the geometry.\n\n- **A roof object goes on its OWN top floor** \u2014 a floor whose `floor_number` is\n ABOVE every floor it covers, containing only the roof object(s). Name it\n something like `"Roof"` or `"Loft Floor"`. Example: ground = 0, first = 1 \u2192 the\n roof floor is `floor_number` **2**. The roof then covers the floors below it.\n (All the examples do this: the roof sits alone on the topmost floor.)\n- **You NEVER write a Z or height on the roof.** There is no Z field anywhere in\n the roof object. Its base sits automatically at the top of the walls below it,\n computed by the pipeline as:\n\n ```\n roof base Z = plinth.height + \u03A3 (height of every floor BELOW the roof\'s floor)\n ```\n\n using each floor\'s own `height` (falling back to `defaults.floor_height`).\n **`slab_thickness` does NOT enter this stack** \u2014 `height` already is the full\n floor-to-floor rise.\n- **To raise or lower the roof, change the FLOORS\' `height` (or `plinth.height`),\n not the roof.** If the roof floats too low/high, you put it on the wrong floor\n or a floor below it has the wrong `height`.\n- `slope` / `ridge_h` is the rise **above** that computed wall-top; segment\n `start`/`end` are 2D `[x, y]` only. Don\'t compute a Z \u2014 the pipeline does it\n entirely from the floor stack.\n\n## The core idea\n\nA roof is a set of **segments**. Each segment is a line (`start`\u2192`end`) with a\n**`width`** measured *perpendicular* to it, and **the segment sits at the CENTRE\nof that width** (the roof extends `width/2` to each side of the line). What the\nline means depends on `roof_type`:\n\n- **pitched** \u2014 the segment **is the ridge**; two symmetric slopes rise from the\n eaves up to it. Ends are covered per `default_endpoint`:\n - `"closed"` \u2192 **hip** (a sloped triangular hip face at the end).\n - `"open"` \u2192 **gable** (a vertical gable-end wall triangle).\n- **shed** \u2014 the segment is the **high edge**; the roof slopes down perpendicular\n to the far side. `shed_high_side` names which side is high.\n- **flat** \u2014 the segment\'s width band is a flat slab extruded down.\n\nSegments can run in **any direction** (not just axis-aligned).\n\n**Centreline convention (`coord_convention: "center"`).** A segment\'s\n`start`/`end`/`width` are **wall centrelines**, exactly like a room\'s\n`x/y/width/length`. On expand, each segment grows to the **outer wall face** \u2014\nthe axis is extended by \xBD\xB7`wall_thickness` at each end and `width` grows by\n`wall_thickness` \u2014 then `overhang` extends beyond that. So author segments on the\n**same centreline grid as the walls below** (e.g. `width House.W`, axis endpoints\non grid lines); do **not** hand-add \xBD-wall offsets, or the roof lands half a wall\nthickness inside the walls. (In the default `"outer"` convention the coordinates\nare literal outer-face positions and no grow happens.)\n\n## Object shape\n\n```jsonc\n{\n "type": "roof",\n "roof_type": "pitched" | "shed" | "flat",\n "default_endpoint": "closed" | "open", // pitched only: closed=hip, open=gable\n "min_overhang": 25, // > 0 \u2014 eave overhang beyond the walls\n "slope": { "by": "height", "ridge_h": 50 }, // rise above wall top \u2026\n // or { "by": "angle", "angle_deg": 30 }, // \u2026 or a pitch angle\n "segments": [\n {\n "id": "seg0",\n "start": [x, y], "end": [x, y], // the ridge (pitched) / high edge (shed)\n "width": 200, // perpendicular span, segment centred in it\n "slope": { \u2026 }, // optional per-segment slope override\n "shed_high_side": "\u2026", // shed only\n "hip_setback_start": \u2026, "hip_setback_end": \u2026, // optional, tune closed hip faces\n // Per-side overhang overrides (each defaults to min_overhang). Cantilever one\n // edge \u2014 e.g. big overhang_end over an entry landing \u2014 while others stay tight.\n "overhang_start": \u2026, "overhang_end": \u2026, // along the ridge axis (shed; on a gable\n // open end = gable_overhang_*)\n "overhang_low": \u2026, "overhang_high": \u2026, // SHED eaves (down-slope / up-slope)\n "overhang_left": \u2026, "overhang_right": \u2026 // PITCHED eaves (left / right of ridge;\n // single-segment only \u2014 see below)\n }\n ],\n "trusses": [\n { "segment_id": "seg0", "type": "fink", "positions_along": [100, 210, 320] }\n // type: "fink" (pitched) | "mono_pitch" (shed)\n // positions_along: distances along the segment where a truss sits\n ],\n "framing": { \u2026 } // optional overrides \u2014 omit unless asked\n}\n```\n\n`slope` may be given once at the roof level (applies to all segments) or per\nsegment. `by: "height"` \u2192 `ridge_h` is the ridge rise above the wall top; `by:\n"angle"` \u2192 `angle_deg` is the pitch.\n\n## Joints (multi-segment shapes)\n\nWhen two segments share an **exactly coincident endpoint**, the pipeline\nauto-resolves that end as a **joint** (no hip/gable face there) \u2014 this is how L,\nU, and closed-loop (courtyard) roofs are formed. Get the endpoints numerically\nequal or they won\'t join.\n\n## Shape \u2192 which example to copy\n\n| Want | Segments | Copy from |\n|---|---|---|\n| Simple hip (one ridge, hipped ends) | 1, `pitched`, `closed` | `two_story_konkan.json` / `verandah_cottage.json` |\n| Simple gable (one ridge, gable ends) | 1, `pitched`, `open` | same, set `default_endpoint: "open"` |\n| **L-shape** (two wings) | 2, `pitched`, coincident inner endpoints | `l_shape_villa.json` |\n| **Courtyard** (ridge loop) | 4, `pitched`, endpoints chained in a loop | `courtyard_home.json` |\n| **Flat** roof/terrace | 1+, `flat` | `modern_flat.json` |\n| Shed / mono-pitch (lean-to, verandah) | 1, `shed`, `shed_high_side` set | derive from a `pitched` seg; add `shed_high_side` + slope |\n\n## Rules & pitfalls\n\n- The roof **footprint must cover the plinth footprint** \u2014 segment lines +\n widths should span the walls they sit on. Mirror the example\'s relationship to\n its plinth.\n- `min_overhang` must be **> 0**.\n- Every segment needs an `id`; every truss references a real `segment_id`.\n- Shed segments **require** a slope (`ridge_h` or `angle_deg`) and a\n `shed_high_side`, or derivation throws.\n- After any roof edit: run `validate.mjs` \u2014 a zero-length segment, missing slope,\n or non-covering footprint fails there, not in the schema.\n'
|
|
417215
417393
|
},
|
|
417216
417394
|
"data-model": {
|
|
417217
417395
|
"title": "The underlying .wadi schema (generated from Zod)",
|