Examples › ObjectDB › Object collection

Object collection

BAM's object library as a catalogue home, the way McMaster-Carr and a Shopify collection lay one out, over its public sample (366 objects): a big title per material with its count, a second heading only where the data has one (Plastics, Glass and Paper split into Medical and Food; Metals has no second layer), and inside each a tile per type with its count. The left rail filters to one group and gives every group back when clicked again; the switcher regroups the same rows by size (bins of the bounding-box volume), shape, type or source, and the rail follows it.

ObjectDBcardstable2 views of one objects.json

The .base file and its data

Every tab above is one entry of this file; the views read these rows. ⋯ in the toolbar copies the current view as YAML.

# BAM's object library as a catalogue home: the grouped gallery (docs/CONTRACT.md §20). Big section titles, a second
# heading level only where the data has one, a tile per type, a left rail that filters to one group, and a live
# grouping switcher. ObjectDB's home page consumes the same keys. The data is ObjectDB's public sample (366 objects:
# open-licence scans and BAM's generated primitives; source/snapshot.py).
# Check it: node bin/data-ui.mjs check <this file>
data: objects.json
facets:
  - { as: nav, follow: groupBy, clear: true }
properties:
  name: { displayName: Name }
  family: { displayName: Material }
  area: { displayName: Area }
  type_name: { displayName: Type }
  grade: { displayName: Grade }
  size_class: { displayName: Size class }
  bbox_volume_ml: { displayName: Size }
  mass_g: { displayName: Mass }
  geometry_class: { displayName: Shape }
  source: { displayName: Source }
  geometry: { displayName: Geometry }
views:
  - type: cards
    name: Home
    order: [name, type_name, size_class, mass_g]
    groupBy: { property: family }
    subGroupBy: { property: area }
    cardSize: 180
    sections: true
    tiles: { property: type_name, all: true, limit: 12 }
    grouping: material
    groupings:
      - id: material
        label: Material
        groupBy: { property: family }
        subGroupBy: { property: area }
        tiles: { property: type_name, all: true, limit: 12 }
      - id: size
        label: Size
        groupBy: { property: bbox_volume_ml, bins: [250, 1000, 5000], log: true, unit: mL }
        tiles: { property: type_name, all: true, limit: 12 }
      - id: shape
        label: Shape
        groupBy: { property: geometry_class }
        tiles: { property: type_name, all: true, limit: 12 }
      - id: type
        label: Type
        groupBy: { property: type_name }
      - id: source
        label: Source
        groupBy: { property: source }
        tiles: { property: type_name, all: true, limit: 12 }
    switcher: segmented
  - type: table
    name: Objects
    order: [name, family, type_name, grade, size_class, bbox_volume_ml, mass_g, geometry_class, source, geometry]
    groupBy: { property: family }
    fold: closed
// objects.json: 366 rows, 102 KB; the first 3
{
 "id": "object-collection",
 "title": "BAM object library (public sample)",
 "titleProperty": "name",
 "source": "ObjectDB's public sample (lane/object-db-public 809e9b2: open-licence scans and BAM's generated primitives; 366 rows, 12 columns)",
 "properties": {
  "id": {
   "type": "text",
   "hidden": true
  },
  "name": {
   "type": "text"
  },
  "family": {
   "type": "text",
   "labels": {
    "plastic": "Plastics",
    "textile": "Textiles",
    "paper": "Paper",
    "composite": "Composites",
    "other": "Other",
    "metal": "Metals",
    "glass": "Glass",
    "electronic": "Electronics",
    "ceramic": "Ceramics",
    "organic": "Organics",
    "rubber": "Rubber",
    "wood": "Wood"
   }
  },
  "area": {
   "type": "text",
   "derived": "Medical / Food for a plastic, glass or paper object of that grade; else none"
  },
  "type_name": {
   "type": "text"
  },
  "grade": {
   "type": "text"
  },
  "size_class": {
   "type": "text"
  },
  "bbox_volume_ml": {
   "type": "number",
   "unitText": "mL",
   "decimals": 0
  },
  "mass_g": {
   "type": "number",
   "unitText": "g",
   "decimals": 1
  },
  "source": {
   "type": "text",
   "labels": {
    "google-scanned-objects": "Google Scanned Objects",
    "egad": "EGAD",
    "synthetic": "Generated (synthetic)",
    "generated": "Generated"
   }
  },
  "geometry": {
   "type": "text"
  },
  "geometry_class": {
   "type": "text",
   "labels": {
    "long": "Long",
    "irregular": "Irregular",
    "flat": "Flat",
    "cubic": "Cubic"
   }
  }
 },
 "rows": [
  {
   "id": "gen_foam-piece_00",
   "name": "Foam piece (generated 00)",
   "family": "plastic",
   "type_name": "Foam piece",
   "grade": "non-food",
   "size_class": "1l",
   "bbox_volume_ml": 878.915,
   "mass_g": 9.38,
   "source": "generated",
   "geometry": "primitive (box)",
   "geometry_class": "irregular",
   "area": null
  },
  {
   "id": "gen_foam-piece_01",
   "name": "Foam piece (generated 01)",
   "family": "plastic",
   "type_name": "Foam piece",
   "grade": "non-food",
   "size_class": "750ml",
   "bbox_volume_ml": 812.108,
   "mass_g": 8.66,
   "source": "generated",
   "geometry": "primitive (box)",
   "geometry_class": "irregular",
   "area": null
  },
  {
   "id": "gen_foam-piece_02",
   "name": "Foam piece (generated 02)",
   "family": "plastic",
   "type_name": "Foam piece",
   "grade": "non-food",
   "size_class": "750ml",
   "bbox_volume_ml": 719.107,
   "mass_g": 7.67,
   "source": "generated",
   "geometry": "primitive (box)",
   "geometry_class": "irregular",
   "area": null
  }
 ]
}
README.md
# Object collection

BAM's object library as a catalogue home, the way McMaster-Carr and a Shopify collection lay one out, over its public
sample (366 objects): a big title per material with its count, a second heading only where the data has one (Plastics,
Glass and Paper split into Medical and Food; Metals has no second layer), and inside each a tile per type with its
count. The left rail filters to one group and gives every group back when clicked again; the switcher regroups the
same rows by size (bins of the bounding-box volume), shape, type or source, and the rail follows it.

- **App:** ObjectDB
- **Replaces:** ObjectDB's home page (object-database `site/`), which draws its own category sections and links; the
  grouped gallery is the cards view with headers and sub-headers, so ObjectDB's home becomes one `.base` view.
- **Shows:** `type: cards` with `tiles: {property, all, limit}` (one tile per sub-category, "All …" first, "Every
  Type (N)" when there are more), `subGroupBy` drawn as a sub-header only where its value exists, `groupings:` with
  `grouping:` and the built-in `switcher: segmented`, a numeric group level with `bins:` (`< 250 mL`, `250 mL – 1 L`,
  `1 L – 5 L`, `≥ 5 L`: 78 / 135 / 113 / 40 objects), and the rail's `as: nav` facet following the view's grouping
  (`?family=metal`, `?bbox_volume_ml=250..<1000`).
- **Data:** `objects.json`, written by `source/snapshot.py`: ObjectDB's public sample only, the 366 objects
  lane/object-db-public (809e9b2) vetted (`source/public_ids.txt`): 150 Google Scanned Objects (CC BY 4.0), 30 EGAD
  shapes (BSD-3-Clause) and 186 of BAM's generated primitives. Columns: id, name, family, type, grade, size class,
  bounding-box volume, mass, source, geometry and shape, plus a derived `area` (Medical / Food for plastic, glass and
  paper; none elsewhere). No customers, mixes, streams, tags, supplier research or labellers; the rebuild fails closed
  on any id or source outside the sample. No thumbnails: each tile draws a monogram.

The original UI it replaces

ObjectDB's home page (object-database site/), which draws its own category sections and links; the grouped gallery is the cards view with headers and sub-headers, so ObjectDB's home becomes one .base view.

What it shows

type: cards with tiles: {property, all, limit} (one tile per sub-category, "All …" first, "Every Type (N)" when there are more), subGroupBy drawn as a sub-header only where its value exists, groupings: with grouping: and the built-in switcher: segmented, a numeric group level with bins: (< 250 mL, 250 mL – 1 L, 1 L – 5 L, ≥ 5 L: 78 / 135 / 113 / 40 objects), and the rail's as: nav facet following the view's grouping (?family=metal, ?bbox_volume_ml=250..<1000).

Data

objects.json, written by source/snapshot.py: ObjectDB's public sample only, the 366 objects lane/object-db-public (809e9b2) vetted (source/public_ids.txt): 150 Google Scanned Objects (CC BY 4.0), 30 EGAD shapes (BSD-3-Clause) and 186 of BAM's generated primitives. Columns: id, name, family, type, grade, size class, bounding-box volume, mass, source, geometry and shape, plus a derived area (Medical / Food for plastic, glass and paper; none elsewhere). No customers, mixes, streams, tags, supplier research or labellers; the rebuild fails closed on any id or source outside the sample. No thumbnails: each tile draws a monogram.