{
  "bldg_catalog": "0.1",
  "prefix": "acme",
  "name": "ACME special furniture (example plugin)",
  "description": "Example declarative bldg catalog plugin: one fixed item and four parametric families. Every length is in millimetres (a plan in inches converts at the boundary). Local frame: origin at the body centre, +x along width, +y along depth, front (the side a person uses) facing -y. Numeric fields of a family are numbers or expressions over its parameters (W, D, H = the body; 'let' adds derived names; a switch is 0 / 1, an option its index). A component type names a family or an item as its model; presets are named sizes.",
  "author": "ACME example",
  "license": "CC0-1.0",
  "items": [
    {
      "id": "acme:piano_upright_1500",
      "category": "furniture",
      "groups": ["living"],
      "names": {"en": "Upright piano", "zh": "立式钢琴"},
      "width": 1500,
      "depth": 600,
      "height": 1250,
      "symbol": [
        {
          "shape": "polyline",
          "closed": true,
          "points": [[-750, 300], [-750, -150], [-700, -150], [-700, -300], [700, -300], [700, -150], [750, -150], [750, 300]]
        },
        {"shape": "rect", "x0": -680, "y0": -290, "x1": 680, "y1": -160, "style": "detail"},
        {"shape": "line", "x0": -750, "y0": 250, "x1": 750, "y1": 250, "style": "detail"}
      ],
      "solids": [
        {"shape": "box", "x0": -750, "y0": -150, "x1": 750, "y1": 300, "z0": 0, "z1": 1250, "part": "case"},
        {"shape": "box", "x0": -700, "y0": -300, "x1": 700, "y1": -150, "z0": 650, "z1": 750, "part": "keyboard"}
      ],
      "clearances": [{"side": "front", "depth": 900, "note": "bench + pianist"}]
    }
  ],
  "families": [
    {
      "id": "acme:piano_grand",
      "category": "furniture",
      "groups": ["living"],
      "names": {"en": "Grand piano", "zh": "三角钢琴"},
      "parameters": [
        {
          "name": "L",
          "default": 1800,
          "min": 1500,
          "max": 2800,
          "step": 50,
          "doc": "length, keyboard to tail (baby 1500, parlour 1800, concert 2750)"
        }
      ],
      "width": 1500,
      "depth": "L",
      "height": 1000,
      "let": {"hd": "D/2", "y0": "-hd + 250", "c": "L - 260"},
      "symbol": [
        {
          "shape": "polyline",
          "closed": true,
          "points": [
            [-750, "y0"],
            [750, "y0"],
            [750, "y0 + 0.2273*c"],
            [700, "y0 + 0.3896*c"],
            [560, "y0 + 0.539*c"],
            [380, "y0 + 0.6948*c"],
            [180, "y0 + 0.8377*c"],
            [-40, "y0 + 0.9416*c"],
            [-300, "y0 + c"],
            [-560, "y0 + c"],
            [-720, "y0 + 0.9416*c"],
            [-750, "y0 + 0.8442*c"]
          ]
        },
        {"shape": "rect", "x0": -720, "y0": "-hd", "x1": 720, "y1": "y0"},
        {"shape": "rect", "x0": -700, "y0": "-hd + 20", "x1": 700, "y1": "y0 - 50", "style": "detail"},
        {"shape": "rect", "x0": -500, "y0": "y0 + 50", "x1": 500, "y1": "y0 + 110", "style": "detail"},
        {"shape": "line", "x0": -700, "y0": "y0 + 50", "x1": -700, "y1": "y0 + 0.9416*c - 20", "style": "detail"},
        {"shape": "circle", "cx": -650, "cy": "y0 + 90", "r": 40, "style": "detail"},
        {"shape": "circle", "cx": 650, "cy": "y0 + 90", "r": 40, "style": "detail"},
        {"shape": "circle", "cx": -500, "cy": "y0 + 0.9156*c", "r": 40, "style": "detail"}
      ],
      "solids": [
        {"shape": "box", "x0": -720, "y0": "-hd", "x1": 720, "y1": "y0", "z0": 600, "z1": 750, "part": "keyboard"},
        {
          "shape": "box",
          "x0": -750,
          "y0": "y0",
          "x1": 750,
          "y1": "y0 + 0.552*c",
          "z0": 600,
          "z1": "H",
          "part": "case"
        },
        {
          "shape": "box",
          "x0": -750,
          "y0": "y0 + 0.552*c",
          "x1": 350,
          "y1": "y0 + c",
          "z0": 600,
          "z1": "H",
          "part": "case"
        },
        {"shape": "cylinder", "cx": -650, "cy": "y0 + 90", "r": 40, "z0": 0, "z1": 600, "part": "leg"},
        {"shape": "cylinder", "cx": 650, "cy": "y0 + 90", "r": 40, "z0": 0, "z1": 600, "part": "leg"},
        {"shape": "cylinder", "cx": -500, "cy": "y0 + 0.9156*c", "r": 40, "z0": 0, "z1": 600, "part": "leg"}
      ],
      "clearances": [{"side": "front", "depth": 900, "note": "bench + pianist"}],
      "presets": [{"name": "Baby grand piano", "params": {"L": 1500}}, {"name": "Concert grand piano", "params": {"L": 2750}}],
      "rules": "case outline scales with the length; keyboard 250 deep",
      "size_source": "typical grands: 1500 wide, 1500-2750 long"
    },
    {
      "id": "acme:pool_table",
      "category": "furniture",
      "groups": ["living"],
      "names": {"en": "Pool table", "zh": "台球桌"},
      "parameters": [
        {
          "name": "L",
          "default": 2240,
          "min": 1800,
          "max": 2600,
          "step": 10,
          "standard": [1980, 2240, 2540],
          "doc": "playing-field length (7 ft 1980, 8 ft 2240, 9 ft 2540); the field is L x L/2"
        }
      ],
      "width": "L + 260",
      "depth": "L/2 + 280",
      "height": 800,
      "let": {"hw": "W/2", "hd": "D/2", "px": "L/2", "py": "L/4"},
      "symbol": [
        {"shape": "rect", "x0": "-hw", "y0": "-hd", "x1": "hw", "y1": "hd", "corner_radius": 40},
        {"shape": "rect", "x0": "-px - 10", "y0": "-py - 20", "x1": "px + 10", "y1": "py + 20", "style": "detail"},
        {
          "op": "array",
          "mode": "repeat",
          "count": 4,
          "var": "k",
          "items": [
            {
              "shape": "circle",
              "cx": "if(k % 2, 1, -1) * (px + 30)",
              "cy": "if(k >= 2, 1, -1) * (py + 40)",
              "r": 55,
              "style": "detail"
            }
          ]
        },
        {"shape": "circle", "cx": 0, "cy": "-py - 55", "r": 50, "style": "detail"},
        {"shape": "circle", "cx": 0, "cy": "py + 55", "r": 50, "style": "detail"},
        {"shape": "line", "x0": "-L/4", "y0": "-py - 20", "x1": "-L/4", "y1": "py + 20", "style": "detail"},
        {"shape": "circle", "cx": "L/4", "cy": 0, "r": 15, "style": "detail"}
      ],
      "solids": [
        {"shape": "box", "x0": "-hw", "y0": "-hd", "x1": "hw", "y1": "hd", "z0": 700, "z1": "H", "part": "top"},
        {
          "shape": "box",
          "x0": "-px + 220",
          "y0": "-py + 160",
          "x1": "px - 220",
          "y1": "py - 160",
          "z0": 0,
          "z1": 700,
          "part": "base"
        }
      ],
      "clearances": [
        {"side": "front", "depth": 1400, "note": "cue swing ~1400-1500"},
        {"side": "back", "depth": 1400, "note": "cue swing ~1400-1500"},
        {"side": "left", "depth": 1400, "note": "cue swing ~1400-1500"},
        {"side": "right", "depth": 1400, "note": "cue swing ~1400-1500"}
      ],
      "presets": [
        {"name": "Pool table 7 ft", "params": {"L": 1980}},
        {"name": "Pool table 8 ft", "params": {"L": 2240}},
        {"name": "Pool table 9 ft", "params": {"L": 2540}}
      ],
      "rules": "footprint = field + 260 x field/2 + 280 (rails); 6 pockets",
      "size_source": "playing field L x L/2: 7 ft 1980, 8 ft 2240, 9 ft 2540"
    },
    {
      "id": "acme:bench",
      "category": "furniture",
      "groups": ["living"],
      "names": {"en": "Bench", "zh": "长凳"},
      "parameters": [
        {"name": "L", "default": 1500, "min": 900, "max": 2400, "step": 50, "doc": "length"},
        {
          "name": "seats",
          "dim": "count",
          "default": "floor(L/450)",
          "min": 1,
          "max": "floor(L/400)",
          "tag": "s",
          "doc": "seat cushions (derived from the length)"
        },
        {"name": "back", "dim": "bool", "default": false, "tag": "b", "doc": "a backrest (a switch: true / false)"}
      ],
      "width": "L",
      "depth": 400,
      "height": "if(back, 800, 450)",
      "let": {"hw": "W/2", "hd": "D/2", "sw": "W/seats", "legs": "1 + ceil(L/1200)"},
      "symbol": [
        {"shape": "rect", "x0": "-hw", "y0": "-hd", "x1": "hw", "y1": "hd", "corner_radius": 20},
        {
          "op": "array",
          "mode": "linear",
          "count": "seats",
          "dx": "sw",
          "items": [
            {
              "shape": "rect",
              "x0": "-hw + 15",
              "y0": "-hd + 15",
              "x1": "-hw + sw - 15",
              "y1": "hd - 15",
              "corner_radius": 30,
              "style": "detail"
            }
          ]
        },
        {
          "shape": "line",
          "x0": "-hw",
          "y0": "hd - 40",
          "x1": "hw",
          "y1": "hd - 40",
          "style": "detail",
          "when": "back"
        }
      ],
      "solids": [
        {"shape": "box", "x0": "-hw", "y0": "-hd", "x1": "hw", "y1": "hd", "z0": 400, "z1": "H", "part": "seat"},
        {
          "op": "array",
          "mode": "linear",
          "count": "legs",
          "dx": "(W - 160)/(legs - 1)",
          "items": [
            {
              "shape": "box",
              "x0": "-hw + 60",
              "y0": "-hd + 20",
              "x1": "-hw + 100",
              "y1": "hd - 20",
              "z0": 0,
              "z1": 400,
              "part": "leg"
            }
          ]
        },
        {
          "shape": "box",
          "x0": "-hw",
          "y0": "hd - 40",
          "x1": "hw",
          "y1": "hd",
          "z0": 450,
          "z1": "H",
          "part": "back",
          "when": "back"
        }
      ],
      "clearances": [{"side": "front", "depth": 450, "note": "leg room"}],
      "checks": [{"if": "L/seats < 450", "severity": "warning", "message": "{seats} seats on {L} mm: less than 450 mm each"}],
      "rules": "seats floor(L/450); a middle leg every 1200 mm; back: a backrest 800 high"
    },
    {
      "id": "acme:cafe_table",
      "category": "furniture",
      "groups": ["dining"],
      "names": {"en": "Cafe table with stools", "zh": "咖啡桌(含凳)"},
      "parameters": [
        {"name": "dia", "default": 700, "min": 600, "max": 1000, "step": 50, "doc": "table diameter"},
        {
          "name": "stools",
          "dim": "count",
          "default": "floor(2*pi*(dia/2 + 200)/700)",
          "min": 0,
          "max": "floor(2*pi*(dia/2 + 200)/400)",
          "tag": "s"
        }
      ],
      "width": "dia + if(stools > 0, 800, 0)",
      "depth": "dia + if(stools > 0, 800, 0)",
      "height": 750,
      "size_params": {"width": "dia", "depth": "dia"},
      "let": {"R": "dia/2", "rr": "R + 200"},
      "symbol": [
        {"shape": "circle", "cx": 0, "cy": 0, "r": "R"},
        {"shape": "circle", "cx": 0, "cy": 0, "r": "min(200, R - 100)", "style": "detail"},
        {
          "op": "array",
          "mode": "polar",
          "count": "stools",
          "cx": 0,
          "cy": 0,
          "items": [
            {"shape": "circle", "cx": 0, "cy": "-rr", "r": 175, "style": "detail"},
            {"shape": "circle", "cx": 0, "cy": "-rr", "r": 120, "style": "detail"}
          ]
        }
      ],
      "solids": [
        {"shape": "cylinder", "cx": 0, "cy": 0, "r": "R", "z0": 720, "z1": "H", "part": "top"},
        {"shape": "cylinder", "cx": 0, "cy": 0, "r": 40, "z0": 20, "z1": 720, "part": "pedestal"},
        {"shape": "cylinder", "cx": 0, "cy": 0, "r": "min(250, R - 50)", "z0": 0, "z1": 20, "part": "foot"},
        {
          "op": "array",
          "mode": "polar",
          "count": "stools",
          "items": [{"shape": "cylinder", "cx": 0, "cy": "-rr", "r": 175, "z0": 0, "z1": 450, "part": "stool"}]
        }
      ],
      "clearances": [
        {"side": "front", "depth": 450, "note": "pass behind a stool"},
        {"side": "back", "depth": 450, "note": "pass behind a stool"}
      ],
      "rules": "stools evenly around (polar array), ~700 mm of ring per stool"
    }
  ]
}
