@scenography/plugin-artwork 0.2.0
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/data-BsI0PbuK.mjs +87 -0
- package/dist/data-BsI0PbuK.mjs.map +1 -0
- package/dist/data-WcCPqQ_k.cjs +110 -0
- package/dist/data-WcCPqQ_k.cjs.map +1 -0
- package/dist/data.cjs +6 -0
- package/dist/data.d.cts +2 -0
- package/dist/data.d.mts +2 -0
- package/dist/data.mjs +2 -0
- package/dist/index-XUkegdM8.d.cts +170 -0
- package/dist/index-XUkegdM8.d.mts +170 -0
- package/dist/index.cjs +358 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +24 -0
- package/dist/index.d.mts +24 -0
- package/dist/index.mjs +353 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { eachPropIs, existsProp, fail, isFiniteNumber, isIntegerNumber, isMeasure, isNonNegativeNumber, isObject, isOneOf, isPositiveNumber, isString, isVec2, isVec3, propIs, validate, when } from "@scenography/scene";
|
|
2
|
+
import { allEdgeNames } from "@scenography/plugin-wall/data";
|
|
3
|
+
//#region src/data/artwork.ts
|
|
4
|
+
/** Default visibility when a label/card doesn't specify its own `fields`. */
|
|
5
|
+
const DEFAULT_ARTWORK_FIELDS = [
|
|
6
|
+
"title",
|
|
7
|
+
"technique",
|
|
8
|
+
"dimensions",
|
|
9
|
+
"availability"
|
|
10
|
+
];
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/data/validate.ts
|
|
13
|
+
const AVAILABILITY = new Set([
|
|
14
|
+
"for-sale",
|
|
15
|
+
"sold",
|
|
16
|
+
"not-for-sale"
|
|
17
|
+
]);
|
|
18
|
+
const ARTWORK_FIELDS = new Set([
|
|
19
|
+
"title",
|
|
20
|
+
"author",
|
|
21
|
+
"year",
|
|
22
|
+
"technique",
|
|
23
|
+
"dimensions",
|
|
24
|
+
"availability"
|
|
25
|
+
]);
|
|
26
|
+
const LABEL_SIDES = new Set([
|
|
27
|
+
"below",
|
|
28
|
+
"left",
|
|
29
|
+
"right"
|
|
30
|
+
]);
|
|
31
|
+
const LOCATION_TYPES = new Set(["wall", "floating"]);
|
|
32
|
+
const locationOfType = (type) => (artwork) => isObject(artwork.location) && artwork.location.type === type;
|
|
33
|
+
/**
|
|
34
|
+
* Validate one artwork. `edgeNames` is the set of wall-edge names in the scene,
|
|
35
|
+
* used to check `location.edge` references. Returns every problem found, in
|
|
36
|
+
* order, with paths relative to the artwork (the caller scopes them).
|
|
37
|
+
*/
|
|
38
|
+
const validateArtwork = (artwork, edgeNames) => validate([
|
|
39
|
+
existsProp("id", "Artwork is missing an id."),
|
|
40
|
+
existsProp("src", "Artwork is missing a src."),
|
|
41
|
+
existsProp("size", "Artwork is missing a size."),
|
|
42
|
+
propIs("size", isVec2, "size must be [width, height]."),
|
|
43
|
+
propIs("depth", isNonNegativeNumber, "depth must be a non-negative number."),
|
|
44
|
+
propIs("year", isIntegerNumber, "year must be an integer."),
|
|
45
|
+
propIs("href", isString, "href must be a string."),
|
|
46
|
+
propIs("availability", isOneOf(AVAILABILITY), "availability must be 'for-sale', 'sold' or 'not-for-sale'."),
|
|
47
|
+
propIs("infoRadius", isPositiveNumber, "infoRadius must be a positive number."),
|
|
48
|
+
propIs("label.side", isOneOf(LABEL_SIDES), "side must be 'below', 'left' or 'right'."),
|
|
49
|
+
propIs("label.fields", Array.isArray, "fields must be an array."),
|
|
50
|
+
eachPropIs("label.fields", isOneOf(ARTWORK_FIELDS), (field) => `unknown field "${String(field)}".`),
|
|
51
|
+
propIs("label.scale", isPositiveNumber, "scale must be a positive number."),
|
|
52
|
+
propIs("label.offset", isVec2, "offset must be [x, y]."),
|
|
53
|
+
propIs("frame.width", isFiniteNumber, "width must be a finite number."),
|
|
54
|
+
propIs("frame.depth", isFiniteNumber, "depth must be a finite number."),
|
|
55
|
+
when((a) => a.frame?.texture !== void 0, existsProp("frame.texture.src", "frame texture is missing a src.")),
|
|
56
|
+
propIs("frame.passepartout.width", isFiniteNumber, "passepartout width must be a finite number."),
|
|
57
|
+
existsProp("location", "Artwork is missing a location."),
|
|
58
|
+
propIs("location", isObject, "location must be an object."),
|
|
59
|
+
when(locationOfType("wall"), existsProp("location.edge"), propIs("location.edge", (edge) => isString(edge) && edgeNames.has(edge), (edge) => `references unknown edge "${String(edge)}".`), existsProp("location.position"), propIs("location.position", isMeasure, "position must be a number or a percentage string."), propIs("location.height", isMeasure, "height must be a number or a percentage string.")),
|
|
60
|
+
when(locationOfType("floating"), existsProp("location.point"), propIs("location.point", isVec3, "point must be [x, y, z].")),
|
|
61
|
+
when((a) => isObject(a.location) && !isOneOf(LOCATION_TYPES)(a.location.type), fail("location.type", "unknown location type."))
|
|
62
|
+
])(artwork);
|
|
63
|
+
/**
|
|
64
|
+
* Validate an artwork against its scene: resolves the set of valid wall-edge
|
|
65
|
+
* names from the scene's walls (the wall plugin's data half) so `location.edge`
|
|
66
|
+
* references are checked. Floating pieces don't need any wall.
|
|
67
|
+
*/
|
|
68
|
+
const validateArtworkInScene = (artwork, ctx) => {
|
|
69
|
+
const walls = ctx.scene.walls ?? [];
|
|
70
|
+
return validateArtwork(artwork, new Set(allEdgeNames(walls)));
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/data/plugin.ts
|
|
74
|
+
/**
|
|
75
|
+
* The data half of the artwork plugin: owns the `artworks` scene key (a
|
|
76
|
+
* collection) and validates each piece — including that a wall-hung piece
|
|
77
|
+
* references a real wall edge. Pure — no three.js.
|
|
78
|
+
*/
|
|
79
|
+
const artworkData = {
|
|
80
|
+
kind: "artworks",
|
|
81
|
+
cardinality: "many",
|
|
82
|
+
validate: (artwork, ctx) => validateArtworkInScene(artwork, ctx)
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { DEFAULT_ARTWORK_FIELDS as i, validateArtwork as n, validateArtworkInScene as r, artworkData as t };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=data-BsI0PbuK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-BsI0PbuK.mjs","names":[],"sources":["../src/data/artwork.ts","../src/data/validate.ts","../src/data/plugin.ts"],"sourcesContent":["import type { Measure, SurfaceTexture, Vec2, Vec3 } from \"@scenography/scene\";\n\n/**\n * Where an artwork is placed. Extensible via `type`; today a piece either hangs\n * on a named wall edge or floats freely. Future element types (floor, ceiling…)\n * add new variants here.\n */\nexport type ArtworkLocation =\n | {\n type: \"wall\";\n /** Name of the `NamedEdge` to hang on. */\n edge: string;\n /** Position along the edge: a number is units from the edge start, `'50%'` is a fraction. */\n position: Measure;\n /**\n * Vertical placement: a number is an absolute world Y, `'50%'` is a\n * fraction of the wall height. Omitted defaults to the camera's eye height.\n */\n height?: Measure;\n /**\n * Which face of the wall the piece shows on, relative to the edge\n * direction. `'right'` (default) is the right-hand side when walking the\n * edge from its start vertex to its end vertex.\n */\n side?: \"right\" | \"left\";\n }\n | {\n type: \"floating\";\n /** World position `[x, y, z]`. */\n point: Vec3;\n /** Y-axis rotation in radians. Defaults to `0`. */\n yaw?: number;\n };\n\n/**\n * A passepartout (mat) sitting between the frame and the image: a flat border\n * that insets the image. Only meaningful when the artwork has a `frame`.\n */\nexport interface Passepartout {\n /** Mat border width around the image, in world units. Defaults to `6`. */\n width?: number;\n /** Mat color (CSS). Defaults to an off-white. */\n color?: string;\n}\n\n/**\n * A frame around an artwork. By default it's four flat bars rendered with a\n * solid `color` or a `texture` (e.g. wood). For an ornate, sculpted frame, point\n * `model` at an external glTF/GLB instead. A `passepartout` mat can sit between\n * the frame and the image.\n */\nexport interface Frame {\n /** Border width (how wide the frame face is), in world units. Defaults to `5`. */\n width?: number;\n /** How far the frame stands proud of the artwork face, in world units. Defaults to `3`. */\n depth?: number;\n /** Frame color (CSS). Defaults to a dark tone. Ignored when `texture` or `model` is set. */\n color?: string;\n /** Surface texture (e.g. wood); takes visual precedence over `color`. */\n texture?: SurfaceTexture;\n /** External glTF/GLB model for an ornate frame; replaces the generated bars. */\n model?: string;\n /** Passepartout mat between the frame and the image. */\n passepartout?: Passepartout;\n}\n\n/**\n * Commercial status of a piece. Gallery convention: a colored dot signals\n * availability. The dot's color is presentation (decided by the renderer), not\n * data — this is the state it derives from.\n */\nexport type Availability = \"for-sale\" | \"sold\" | \"not-for-sale\";\n\n/**\n * The fields a label/info card can show, identified by name. A `fields` array\n * toggles which are visible; the render order is fixed (it is not the array\n * order): title, then author/year, then technique + dimensions on one line, with\n * availability shown as a corner dot. Shared by the physical label (engine) and\n * the overlay info card.\n */\nexport type ArtworkField = \"title\" | \"author\" | \"year\" | \"technique\" | \"dimensions\" | \"availability\";\n\n/** Default visibility when a label/card doesn't specify its own `fields`. */\nexport const DEFAULT_ARTWORK_FIELDS: readonly ArtworkField[] = [\n \"title\",\n \"technique\",\n \"dimensions\",\n \"availability\",\n];\n\n/**\n * A physical wall label (cartela) materialized next to the piece. Its presence\n * is the opt-in; the engine renders it from the artwork's own data.\n */\nexport interface ArtworkLabel {\n /** Where the label sits relative to the piece. Defaults to `'below'`. */\n side?: \"below\" | \"left\" | \"right\";\n /** Which fields to show. Defaults to `DEFAULT_ARTWORK_FIELDS`. */\n fields?: ArtworkField[];\n /**\n * Size multiplier over the real physical size (24pt title etc). Defaults to\n * `1` (true to life), but a real cartela is only a few cm tall — tiny and hard\n * to read/click at gallery scale — so bump this (e.g. `3`) for legibility.\n */\n scale?: number;\n /**\n * Fine-tune the placement with an `[x, y]` shift in world units, added on top\n * of the `side` position (the frame is already cleared). `x` runs along the\n * wall (right is positive), `y` is vertical (up is positive).\n */\n offset?: Vec2;\n}\n\n/** A framed image placed somewhere in the scene. */\nexport interface Artwork {\n id: string;\n location: ArtworkLocation;\n /** Image URL/path to display. */\n src: string;\n /** `[width, height]` of the artwork, in world units. */\n size: Vec2;\n /**\n * Box depth (volume) in world units: the piece is a box with the image on the\n * front and white sides/back. Defaults to `2`.\n */\n depth?: number;\n /** Optional frame around the artwork. */\n frame?: Frame;\n title?: string;\n technique?: string;\n /** Author / artist name. */\n author?: string;\n /** Year the piece was made. */\n year?: number;\n /**\n * Real-world dimensions for display, e.g. `'92 × 73 cm'`. Distinct from `size`,\n * which is in world units.\n */\n dimensions?: string;\n /** Commercial status; drives the availability dot. */\n availability?: Availability;\n /** Long-form caption / catalog entry, shown in the expanded info panel. */\n description?: string;\n /**\n * External URL for the piece (a catalog page, or a marketplace listing when\n * it's for sale). Surfaced as a link in the expanded info panel.\n */\n href?: string;\n /** When present, a physical wall label is materialized next to the piece. */\n label?: ArtworkLabel;\n /**\n * Max distance (world units) from which the piece can be clicked to open its\n * info panel. A gate so far-away pieces aren't interactive. Absent = no gate.\n */\n infoRadius?: number;\n}\n\ndeclare module \"@scenography/scene\" {\n interface Scene {\n /** Framed images placed in the space — provided by `@scenography/plugin-artwork`. */\n artworks?: Artwork[];\n }\n}\n","import {\n eachPropIs,\n existsProp,\n fail,\n isFiniteNumber,\n isIntegerNumber,\n isMeasure,\n isNonNegativeNumber,\n isObject,\n isOneOf,\n isPositiveNumber,\n isString,\n isVec2,\n isVec3,\n propIs,\n validate,\n when,\n type SceneContext,\n type ValidationResult,\n} from \"@scenography/scene\";\nimport { allEdgeNames, type Wall } from \"@scenography/plugin-wall/data\";\n\nimport type { Artwork, ArtworkField, Availability } from \"./artwork\";\n\nconst AVAILABILITY: ReadonlySet<Availability> = new Set<Availability>([\n \"for-sale\",\n \"sold\",\n \"not-for-sale\",\n]);\nconst ARTWORK_FIELDS: ReadonlySet<ArtworkField> = new Set<ArtworkField>([\n \"title\",\n \"author\",\n \"year\",\n \"technique\",\n \"dimensions\",\n \"availability\",\n]);\nconst LABEL_SIDES: ReadonlySet<string> = new Set([\"below\", \"left\", \"right\"]);\nconst LOCATION_TYPES: ReadonlySet<string> = new Set([\"wall\", \"floating\"]);\n\nconst locationOfType =\n (type: string) =>\n (artwork: Artwork): boolean =>\n isObject(artwork.location) && artwork.location.type === type;\n\n/**\n * Validate one artwork. `edgeNames` is the set of wall-edge names in the scene,\n * used to check `location.edge` references. Returns every problem found, in\n * order, with paths relative to the artwork (the caller scopes them).\n */\nexport const validateArtwork = (artwork: Artwork, edgeNames: Set<string>): ValidationResult =>\n validate<Artwork>([\n existsProp(\"id\", \"Artwork is missing an id.\"),\n existsProp(\"src\", \"Artwork is missing a src.\"),\n existsProp(\"size\", \"Artwork is missing a size.\"),\n propIs(\"size\", isVec2, \"size must be [width, height].\"),\n propIs(\"depth\", isNonNegativeNumber, \"depth must be a non-negative number.\"),\n propIs(\"year\", isIntegerNumber, \"year must be an integer.\"),\n propIs(\"href\", isString, \"href must be a string.\"),\n propIs(\n \"availability\",\n isOneOf(AVAILABILITY),\n \"availability must be 'for-sale', 'sold' or 'not-for-sale'.\",\n ),\n propIs(\"infoRadius\", isPositiveNumber, \"infoRadius must be a positive number.\"),\n propIs(\"label.side\", isOneOf(LABEL_SIDES), \"side must be 'below', 'left' or 'right'.\"),\n propIs(\"label.fields\", Array.isArray, \"fields must be an array.\"),\n eachPropIs(\"label.fields\", isOneOf(ARTWORK_FIELDS), (field) => `unknown field \"${String(field)}\".`),\n propIs(\"label.scale\", isPositiveNumber, \"scale must be a positive number.\"),\n propIs(\"label.offset\", isVec2, \"offset must be [x, y].\"),\n propIs(\"frame.width\", isFiniteNumber, \"width must be a finite number.\"),\n propIs(\"frame.depth\", isFiniteNumber, \"depth must be a finite number.\"),\n when(\n (a: Artwork) => a.frame?.texture !== undefined,\n existsProp(\"frame.texture.src\", \"frame texture is missing a src.\"),\n ),\n propIs(\n \"frame.passepartout.width\",\n isFiniteNumber,\n \"passepartout width must be a finite number.\",\n ),\n existsProp(\"location\", \"Artwork is missing a location.\"),\n propIs(\"location\", isObject, \"location must be an object.\"),\n when(\n locationOfType(\"wall\"),\n existsProp(\"location.edge\"),\n propIs(\n \"location.edge\",\n (edge) => isString(edge) && edgeNames.has(edge),\n (edge) => `references unknown edge \"${String(edge)}\".`,\n ),\n existsProp(\"location.position\"),\n propIs(\"location.position\", isMeasure, \"position must be a number or a percentage string.\"),\n propIs(\"location.height\", isMeasure, \"height must be a number or a percentage string.\"),\n ),\n when(\n locationOfType(\"floating\"),\n existsProp(\"location.point\"),\n propIs(\"location.point\", isVec3, \"point must be [x, y, z].\"),\n ),\n when(\n (a: Artwork) => isObject(a.location) && !isOneOf(LOCATION_TYPES)(a.location.type),\n fail(\"location.type\", \"unknown location type.\"),\n ),\n ])(artwork);\n\n/**\n * Validate an artwork against its scene: resolves the set of valid wall-edge\n * names from the scene's walls (the wall plugin's data half) so `location.edge`\n * references are checked. Floating pieces don't need any wall.\n */\nexport const validateArtworkInScene = (artwork: Artwork, ctx: SceneContext): ValidationResult => {\n const walls = (ctx.scene as { walls?: Wall[] }).walls ?? [];\n return validateArtwork(artwork, new Set(allEdgeNames(walls)));\n};\n","import type { DataPlugin } from \"@scenography/scene\";\n\nimport { validateArtworkInScene } from \"./validate\";\nimport type { Artwork } from \"./artwork\";\n\n/**\n * The data half of the artwork plugin: owns the `artworks` scene key (a\n * collection) and validates each piece — including that a wall-hung piece\n * references a real wall edge. Pure — no three.js.\n */\nexport const artworkData: DataPlugin<Artwork> = {\n kind: \"artworks\",\n cardinality: \"many\",\n validate: (artwork, ctx) => validateArtworkInScene(artwork, ctx),\n};\n"],"mappings":";;;;AAmFA,MAAa,yBAAkD;CAC7D;CACA;CACA;CACA;AACF;;;AChEA,MAAM,eAA0C,IAAI,IAAkB;CACpE;CACA;CACA;AACF,CAAC;AACD,MAAM,iBAA4C,IAAI,IAAkB;CACtE;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,cAAmC,IAAI,IAAI;CAAC;CAAS;CAAQ;AAAO,CAAC;AAC3E,MAAM,iBAAsC,IAAI,IAAI,CAAC,QAAQ,UAAU,CAAC;AAExE,MAAM,kBACH,UACA,YACC,SAAS,QAAQ,QAAQ,KAAK,QAAQ,SAAS,SAAS;;;;;;AAO5D,MAAa,mBAAmB,SAAkB,cAChD,SAAkB;CAChB,WAAW,MAAM,2BAA2B;CAC5C,WAAW,OAAO,2BAA2B;CAC7C,WAAW,QAAQ,4BAA4B;CAC/C,OAAO,QAAQ,QAAQ,+BAA+B;CACtD,OAAO,SAAS,qBAAqB,sCAAsC;CAC3E,OAAO,QAAQ,iBAAiB,0BAA0B;CAC1D,OAAO,QAAQ,UAAU,wBAAwB;CACjD,OACE,gBACA,QAAQ,YAAY,GACpB,4DACF;CACA,OAAO,cAAc,kBAAkB,uCAAuC;CAC9E,OAAO,cAAc,QAAQ,WAAW,GAAG,0CAA0C;CACrF,OAAO,gBAAgB,MAAM,SAAS,0BAA0B;CAChE,WAAW,gBAAgB,QAAQ,cAAc,IAAI,UAAU,kBAAkB,OAAO,KAAK,EAAE,GAAG;CAClG,OAAO,eAAe,kBAAkB,kCAAkC;CAC1E,OAAO,gBAAgB,QAAQ,wBAAwB;CACvD,OAAO,eAAe,gBAAgB,gCAAgC;CACtE,OAAO,eAAe,gBAAgB,gCAAgC;CACtE,MACG,MAAe,EAAE,OAAO,YAAY,KAAA,GACrC,WAAW,qBAAqB,iCAAiC,CACnE;CACA,OACE,4BACA,gBACA,6CACF;CACA,WAAW,YAAY,gCAAgC;CACvD,OAAO,YAAY,UAAU,6BAA6B;CAC1D,KACE,eAAe,MAAM,GACrB,WAAW,eAAe,GAC1B,OACE,kBACC,SAAS,SAAS,IAAI,KAAK,UAAU,IAAI,IAAI,IAC7C,SAAS,4BAA4B,OAAO,IAAI,EAAE,GACrD,GACA,WAAW,mBAAmB,GAC9B,OAAO,qBAAqB,WAAW,mDAAmD,GAC1F,OAAO,mBAAmB,WAAW,iDAAiD,CACxF;CACA,KACE,eAAe,UAAU,GACzB,WAAW,gBAAgB,GAC3B,OAAO,kBAAkB,QAAQ,0BAA0B,CAC7D;CACA,MACG,MAAe,SAAS,EAAE,QAAQ,KAAK,CAAC,QAAQ,cAAc,CAAC,CAAC,EAAE,SAAS,IAAI,GAChF,KAAK,iBAAiB,wBAAwB,CAChD;AACF,CAAC,CAAC,CAAC,OAAO;;;;;;AAOZ,MAAa,0BAA0B,SAAkB,QAAwC;CAC/F,MAAM,QAAS,IAAI,MAA6B,SAAS,CAAC;CAC1D,OAAO,gBAAgB,SAAS,IAAI,IAAI,aAAa,KAAK,CAAC,CAAC;AAC9D;;;;;;;;ACxGA,MAAa,cAAmC;CAC9C,MAAM;CACN,aAAa;CACb,WAAW,SAAS,QAAQ,uBAAuB,SAAS,GAAG;AACjE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
let _scenography_scene = require("@scenography/scene");
|
|
2
|
+
let _scenography_plugin_wall_data = require("@scenography/plugin-wall/data");
|
|
3
|
+
//#region src/data/artwork.ts
|
|
4
|
+
/** Default visibility when a label/card doesn't specify its own `fields`. */
|
|
5
|
+
const DEFAULT_ARTWORK_FIELDS = [
|
|
6
|
+
"title",
|
|
7
|
+
"technique",
|
|
8
|
+
"dimensions",
|
|
9
|
+
"availability"
|
|
10
|
+
];
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/data/validate.ts
|
|
13
|
+
const AVAILABILITY = new Set([
|
|
14
|
+
"for-sale",
|
|
15
|
+
"sold",
|
|
16
|
+
"not-for-sale"
|
|
17
|
+
]);
|
|
18
|
+
const ARTWORK_FIELDS = new Set([
|
|
19
|
+
"title",
|
|
20
|
+
"author",
|
|
21
|
+
"year",
|
|
22
|
+
"technique",
|
|
23
|
+
"dimensions",
|
|
24
|
+
"availability"
|
|
25
|
+
]);
|
|
26
|
+
const LABEL_SIDES = new Set([
|
|
27
|
+
"below",
|
|
28
|
+
"left",
|
|
29
|
+
"right"
|
|
30
|
+
]);
|
|
31
|
+
const LOCATION_TYPES = new Set(["wall", "floating"]);
|
|
32
|
+
const locationOfType = (type) => (artwork) => (0, _scenography_scene.isObject)(artwork.location) && artwork.location.type === type;
|
|
33
|
+
/**
|
|
34
|
+
* Validate one artwork. `edgeNames` is the set of wall-edge names in the scene,
|
|
35
|
+
* used to check `location.edge` references. Returns every problem found, in
|
|
36
|
+
* order, with paths relative to the artwork (the caller scopes them).
|
|
37
|
+
*/
|
|
38
|
+
const validateArtwork = (artwork, edgeNames) => (0, _scenography_scene.validate)([
|
|
39
|
+
(0, _scenography_scene.existsProp)("id", "Artwork is missing an id."),
|
|
40
|
+
(0, _scenography_scene.existsProp)("src", "Artwork is missing a src."),
|
|
41
|
+
(0, _scenography_scene.existsProp)("size", "Artwork is missing a size."),
|
|
42
|
+
(0, _scenography_scene.propIs)("size", _scenography_scene.isVec2, "size must be [width, height]."),
|
|
43
|
+
(0, _scenography_scene.propIs)("depth", _scenography_scene.isNonNegativeNumber, "depth must be a non-negative number."),
|
|
44
|
+
(0, _scenography_scene.propIs)("year", _scenography_scene.isIntegerNumber, "year must be an integer."),
|
|
45
|
+
(0, _scenography_scene.propIs)("href", _scenography_scene.isString, "href must be a string."),
|
|
46
|
+
(0, _scenography_scene.propIs)("availability", (0, _scenography_scene.isOneOf)(AVAILABILITY), "availability must be 'for-sale', 'sold' or 'not-for-sale'."),
|
|
47
|
+
(0, _scenography_scene.propIs)("infoRadius", _scenography_scene.isPositiveNumber, "infoRadius must be a positive number."),
|
|
48
|
+
(0, _scenography_scene.propIs)("label.side", (0, _scenography_scene.isOneOf)(LABEL_SIDES), "side must be 'below', 'left' or 'right'."),
|
|
49
|
+
(0, _scenography_scene.propIs)("label.fields", Array.isArray, "fields must be an array."),
|
|
50
|
+
(0, _scenography_scene.eachPropIs)("label.fields", (0, _scenography_scene.isOneOf)(ARTWORK_FIELDS), (field) => `unknown field "${String(field)}".`),
|
|
51
|
+
(0, _scenography_scene.propIs)("label.scale", _scenography_scene.isPositiveNumber, "scale must be a positive number."),
|
|
52
|
+
(0, _scenography_scene.propIs)("label.offset", _scenography_scene.isVec2, "offset must be [x, y]."),
|
|
53
|
+
(0, _scenography_scene.propIs)("frame.width", _scenography_scene.isFiniteNumber, "width must be a finite number."),
|
|
54
|
+
(0, _scenography_scene.propIs)("frame.depth", _scenography_scene.isFiniteNumber, "depth must be a finite number."),
|
|
55
|
+
(0, _scenography_scene.when)((a) => a.frame?.texture !== void 0, (0, _scenography_scene.existsProp)("frame.texture.src", "frame texture is missing a src.")),
|
|
56
|
+
(0, _scenography_scene.propIs)("frame.passepartout.width", _scenography_scene.isFiniteNumber, "passepartout width must be a finite number."),
|
|
57
|
+
(0, _scenography_scene.existsProp)("location", "Artwork is missing a location."),
|
|
58
|
+
(0, _scenography_scene.propIs)("location", _scenography_scene.isObject, "location must be an object."),
|
|
59
|
+
(0, _scenography_scene.when)(locationOfType("wall"), (0, _scenography_scene.existsProp)("location.edge"), (0, _scenography_scene.propIs)("location.edge", (edge) => (0, _scenography_scene.isString)(edge) && edgeNames.has(edge), (edge) => `references unknown edge "${String(edge)}".`), (0, _scenography_scene.existsProp)("location.position"), (0, _scenography_scene.propIs)("location.position", _scenography_scene.isMeasure, "position must be a number or a percentage string."), (0, _scenography_scene.propIs)("location.height", _scenography_scene.isMeasure, "height must be a number or a percentage string.")),
|
|
60
|
+
(0, _scenography_scene.when)(locationOfType("floating"), (0, _scenography_scene.existsProp)("location.point"), (0, _scenography_scene.propIs)("location.point", _scenography_scene.isVec3, "point must be [x, y, z].")),
|
|
61
|
+
(0, _scenography_scene.when)((a) => (0, _scenography_scene.isObject)(a.location) && !(0, _scenography_scene.isOneOf)(LOCATION_TYPES)(a.location.type), (0, _scenography_scene.fail)("location.type", "unknown location type."))
|
|
62
|
+
])(artwork);
|
|
63
|
+
/**
|
|
64
|
+
* Validate an artwork against its scene: resolves the set of valid wall-edge
|
|
65
|
+
* names from the scene's walls (the wall plugin's data half) so `location.edge`
|
|
66
|
+
* references are checked. Floating pieces don't need any wall.
|
|
67
|
+
*/
|
|
68
|
+
const validateArtworkInScene = (artwork, ctx) => {
|
|
69
|
+
const walls = ctx.scene.walls ?? [];
|
|
70
|
+
return validateArtwork(artwork, new Set((0, _scenography_plugin_wall_data.allEdgeNames)(walls)));
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/data/plugin.ts
|
|
74
|
+
/**
|
|
75
|
+
* The data half of the artwork plugin: owns the `artworks` scene key (a
|
|
76
|
+
* collection) and validates each piece — including that a wall-hung piece
|
|
77
|
+
* references a real wall edge. Pure — no three.js.
|
|
78
|
+
*/
|
|
79
|
+
const artworkData = {
|
|
80
|
+
kind: "artworks",
|
|
81
|
+
cardinality: "many",
|
|
82
|
+
validate: (artwork, ctx) => validateArtworkInScene(artwork, ctx)
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
Object.defineProperty(exports, "DEFAULT_ARTWORK_FIELDS", {
|
|
86
|
+
enumerable: true,
|
|
87
|
+
get: function() {
|
|
88
|
+
return DEFAULT_ARTWORK_FIELDS;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
Object.defineProperty(exports, "artworkData", {
|
|
92
|
+
enumerable: true,
|
|
93
|
+
get: function() {
|
|
94
|
+
return artworkData;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
Object.defineProperty(exports, "validateArtwork", {
|
|
98
|
+
enumerable: true,
|
|
99
|
+
get: function() {
|
|
100
|
+
return validateArtwork;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
Object.defineProperty(exports, "validateArtworkInScene", {
|
|
104
|
+
enumerable: true,
|
|
105
|
+
get: function() {
|
|
106
|
+
return validateArtworkInScene;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=data-WcCPqQ_k.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-WcCPqQ_k.cjs","names":["isVec2","isNonNegativeNumber","isIntegerNumber","isString","isPositiveNumber","isFiniteNumber","isObject","isMeasure","isVec3"],"sources":["../src/data/artwork.ts","../src/data/validate.ts","../src/data/plugin.ts"],"sourcesContent":["import type { Measure, SurfaceTexture, Vec2, Vec3 } from \"@scenography/scene\";\n\n/**\n * Where an artwork is placed. Extensible via `type`; today a piece either hangs\n * on a named wall edge or floats freely. Future element types (floor, ceiling…)\n * add new variants here.\n */\nexport type ArtworkLocation =\n | {\n type: \"wall\";\n /** Name of the `NamedEdge` to hang on. */\n edge: string;\n /** Position along the edge: a number is units from the edge start, `'50%'` is a fraction. */\n position: Measure;\n /**\n * Vertical placement: a number is an absolute world Y, `'50%'` is a\n * fraction of the wall height. Omitted defaults to the camera's eye height.\n */\n height?: Measure;\n /**\n * Which face of the wall the piece shows on, relative to the edge\n * direction. `'right'` (default) is the right-hand side when walking the\n * edge from its start vertex to its end vertex.\n */\n side?: \"right\" | \"left\";\n }\n | {\n type: \"floating\";\n /** World position `[x, y, z]`. */\n point: Vec3;\n /** Y-axis rotation in radians. Defaults to `0`. */\n yaw?: number;\n };\n\n/**\n * A passepartout (mat) sitting between the frame and the image: a flat border\n * that insets the image. Only meaningful when the artwork has a `frame`.\n */\nexport interface Passepartout {\n /** Mat border width around the image, in world units. Defaults to `6`. */\n width?: number;\n /** Mat color (CSS). Defaults to an off-white. */\n color?: string;\n}\n\n/**\n * A frame around an artwork. By default it's four flat bars rendered with a\n * solid `color` or a `texture` (e.g. wood). For an ornate, sculpted frame, point\n * `model` at an external glTF/GLB instead. A `passepartout` mat can sit between\n * the frame and the image.\n */\nexport interface Frame {\n /** Border width (how wide the frame face is), in world units. Defaults to `5`. */\n width?: number;\n /** How far the frame stands proud of the artwork face, in world units. Defaults to `3`. */\n depth?: number;\n /** Frame color (CSS). Defaults to a dark tone. Ignored when `texture` or `model` is set. */\n color?: string;\n /** Surface texture (e.g. wood); takes visual precedence over `color`. */\n texture?: SurfaceTexture;\n /** External glTF/GLB model for an ornate frame; replaces the generated bars. */\n model?: string;\n /** Passepartout mat between the frame and the image. */\n passepartout?: Passepartout;\n}\n\n/**\n * Commercial status of a piece. Gallery convention: a colored dot signals\n * availability. The dot's color is presentation (decided by the renderer), not\n * data — this is the state it derives from.\n */\nexport type Availability = \"for-sale\" | \"sold\" | \"not-for-sale\";\n\n/**\n * The fields a label/info card can show, identified by name. A `fields` array\n * toggles which are visible; the render order is fixed (it is not the array\n * order): title, then author/year, then technique + dimensions on one line, with\n * availability shown as a corner dot. Shared by the physical label (engine) and\n * the overlay info card.\n */\nexport type ArtworkField = \"title\" | \"author\" | \"year\" | \"technique\" | \"dimensions\" | \"availability\";\n\n/** Default visibility when a label/card doesn't specify its own `fields`. */\nexport const DEFAULT_ARTWORK_FIELDS: readonly ArtworkField[] = [\n \"title\",\n \"technique\",\n \"dimensions\",\n \"availability\",\n];\n\n/**\n * A physical wall label (cartela) materialized next to the piece. Its presence\n * is the opt-in; the engine renders it from the artwork's own data.\n */\nexport interface ArtworkLabel {\n /** Where the label sits relative to the piece. Defaults to `'below'`. */\n side?: \"below\" | \"left\" | \"right\";\n /** Which fields to show. Defaults to `DEFAULT_ARTWORK_FIELDS`. */\n fields?: ArtworkField[];\n /**\n * Size multiplier over the real physical size (24pt title etc). Defaults to\n * `1` (true to life), but a real cartela is only a few cm tall — tiny and hard\n * to read/click at gallery scale — so bump this (e.g. `3`) for legibility.\n */\n scale?: number;\n /**\n * Fine-tune the placement with an `[x, y]` shift in world units, added on top\n * of the `side` position (the frame is already cleared). `x` runs along the\n * wall (right is positive), `y` is vertical (up is positive).\n */\n offset?: Vec2;\n}\n\n/** A framed image placed somewhere in the scene. */\nexport interface Artwork {\n id: string;\n location: ArtworkLocation;\n /** Image URL/path to display. */\n src: string;\n /** `[width, height]` of the artwork, in world units. */\n size: Vec2;\n /**\n * Box depth (volume) in world units: the piece is a box with the image on the\n * front and white sides/back. Defaults to `2`.\n */\n depth?: number;\n /** Optional frame around the artwork. */\n frame?: Frame;\n title?: string;\n technique?: string;\n /** Author / artist name. */\n author?: string;\n /** Year the piece was made. */\n year?: number;\n /**\n * Real-world dimensions for display, e.g. `'92 × 73 cm'`. Distinct from `size`,\n * which is in world units.\n */\n dimensions?: string;\n /** Commercial status; drives the availability dot. */\n availability?: Availability;\n /** Long-form caption / catalog entry, shown in the expanded info panel. */\n description?: string;\n /**\n * External URL for the piece (a catalog page, or a marketplace listing when\n * it's for sale). Surfaced as a link in the expanded info panel.\n */\n href?: string;\n /** When present, a physical wall label is materialized next to the piece. */\n label?: ArtworkLabel;\n /**\n * Max distance (world units) from which the piece can be clicked to open its\n * info panel. A gate so far-away pieces aren't interactive. Absent = no gate.\n */\n infoRadius?: number;\n}\n\ndeclare module \"@scenography/scene\" {\n interface Scene {\n /** Framed images placed in the space — provided by `@scenography/plugin-artwork`. */\n artworks?: Artwork[];\n }\n}\n","import {\n eachPropIs,\n existsProp,\n fail,\n isFiniteNumber,\n isIntegerNumber,\n isMeasure,\n isNonNegativeNumber,\n isObject,\n isOneOf,\n isPositiveNumber,\n isString,\n isVec2,\n isVec3,\n propIs,\n validate,\n when,\n type SceneContext,\n type ValidationResult,\n} from \"@scenography/scene\";\nimport { allEdgeNames, type Wall } from \"@scenography/plugin-wall/data\";\n\nimport type { Artwork, ArtworkField, Availability } from \"./artwork\";\n\nconst AVAILABILITY: ReadonlySet<Availability> = new Set<Availability>([\n \"for-sale\",\n \"sold\",\n \"not-for-sale\",\n]);\nconst ARTWORK_FIELDS: ReadonlySet<ArtworkField> = new Set<ArtworkField>([\n \"title\",\n \"author\",\n \"year\",\n \"technique\",\n \"dimensions\",\n \"availability\",\n]);\nconst LABEL_SIDES: ReadonlySet<string> = new Set([\"below\", \"left\", \"right\"]);\nconst LOCATION_TYPES: ReadonlySet<string> = new Set([\"wall\", \"floating\"]);\n\nconst locationOfType =\n (type: string) =>\n (artwork: Artwork): boolean =>\n isObject(artwork.location) && artwork.location.type === type;\n\n/**\n * Validate one artwork. `edgeNames` is the set of wall-edge names in the scene,\n * used to check `location.edge` references. Returns every problem found, in\n * order, with paths relative to the artwork (the caller scopes them).\n */\nexport const validateArtwork = (artwork: Artwork, edgeNames: Set<string>): ValidationResult =>\n validate<Artwork>([\n existsProp(\"id\", \"Artwork is missing an id.\"),\n existsProp(\"src\", \"Artwork is missing a src.\"),\n existsProp(\"size\", \"Artwork is missing a size.\"),\n propIs(\"size\", isVec2, \"size must be [width, height].\"),\n propIs(\"depth\", isNonNegativeNumber, \"depth must be a non-negative number.\"),\n propIs(\"year\", isIntegerNumber, \"year must be an integer.\"),\n propIs(\"href\", isString, \"href must be a string.\"),\n propIs(\n \"availability\",\n isOneOf(AVAILABILITY),\n \"availability must be 'for-sale', 'sold' or 'not-for-sale'.\",\n ),\n propIs(\"infoRadius\", isPositiveNumber, \"infoRadius must be a positive number.\"),\n propIs(\"label.side\", isOneOf(LABEL_SIDES), \"side must be 'below', 'left' or 'right'.\"),\n propIs(\"label.fields\", Array.isArray, \"fields must be an array.\"),\n eachPropIs(\"label.fields\", isOneOf(ARTWORK_FIELDS), (field) => `unknown field \"${String(field)}\".`),\n propIs(\"label.scale\", isPositiveNumber, \"scale must be a positive number.\"),\n propIs(\"label.offset\", isVec2, \"offset must be [x, y].\"),\n propIs(\"frame.width\", isFiniteNumber, \"width must be a finite number.\"),\n propIs(\"frame.depth\", isFiniteNumber, \"depth must be a finite number.\"),\n when(\n (a: Artwork) => a.frame?.texture !== undefined,\n existsProp(\"frame.texture.src\", \"frame texture is missing a src.\"),\n ),\n propIs(\n \"frame.passepartout.width\",\n isFiniteNumber,\n \"passepartout width must be a finite number.\",\n ),\n existsProp(\"location\", \"Artwork is missing a location.\"),\n propIs(\"location\", isObject, \"location must be an object.\"),\n when(\n locationOfType(\"wall\"),\n existsProp(\"location.edge\"),\n propIs(\n \"location.edge\",\n (edge) => isString(edge) && edgeNames.has(edge),\n (edge) => `references unknown edge \"${String(edge)}\".`,\n ),\n existsProp(\"location.position\"),\n propIs(\"location.position\", isMeasure, \"position must be a number or a percentage string.\"),\n propIs(\"location.height\", isMeasure, \"height must be a number or a percentage string.\"),\n ),\n when(\n locationOfType(\"floating\"),\n existsProp(\"location.point\"),\n propIs(\"location.point\", isVec3, \"point must be [x, y, z].\"),\n ),\n when(\n (a: Artwork) => isObject(a.location) && !isOneOf(LOCATION_TYPES)(a.location.type),\n fail(\"location.type\", \"unknown location type.\"),\n ),\n ])(artwork);\n\n/**\n * Validate an artwork against its scene: resolves the set of valid wall-edge\n * names from the scene's walls (the wall plugin's data half) so `location.edge`\n * references are checked. Floating pieces don't need any wall.\n */\nexport const validateArtworkInScene = (artwork: Artwork, ctx: SceneContext): ValidationResult => {\n const walls = (ctx.scene as { walls?: Wall[] }).walls ?? [];\n return validateArtwork(artwork, new Set(allEdgeNames(walls)));\n};\n","import type { DataPlugin } from \"@scenography/scene\";\n\nimport { validateArtworkInScene } from \"./validate\";\nimport type { Artwork } from \"./artwork\";\n\n/**\n * The data half of the artwork plugin: owns the `artworks` scene key (a\n * collection) and validates each piece — including that a wall-hung piece\n * references a real wall edge. Pure — no three.js.\n */\nexport const artworkData: DataPlugin<Artwork> = {\n kind: \"artworks\",\n cardinality: \"many\",\n validate: (artwork, ctx) => validateArtworkInScene(artwork, ctx),\n};\n"],"mappings":";;;;AAmFA,MAAa,yBAAkD;CAC7D;CACA;CACA;CACA;AACF;;;AChEA,MAAM,eAA0C,IAAI,IAAkB;CACpE;CACA;CACA;AACF,CAAC;AACD,MAAM,iBAA4C,IAAI,IAAkB;CACtE;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,cAAmC,IAAI,IAAI;CAAC;CAAS;CAAQ;AAAO,CAAC;AAC3E,MAAM,iBAAsC,IAAI,IAAI,CAAC,QAAQ,UAAU,CAAC;AAExE,MAAM,kBACH,UACA,aAAA,GAAA,mBAAA,SAAA,CACU,QAAQ,QAAQ,KAAK,QAAQ,SAAS,SAAS;;;;;;AAO5D,MAAa,mBAAmB,SAAkB,eAAA,GAAA,mBAAA,SAAA,CAC9B;oCACL,MAAM,2BAA2B;oCACjC,OAAO,2BAA2B;oCAClC,QAAQ,4BAA4B;gCACxC,QAAQA,mBAAAA,QAAQ,+BAA+B;gCAC/C,SAASC,mBAAAA,qBAAqB,sCAAsC;gCACpE,QAAQC,mBAAAA,iBAAiB,0BAA0B;gCACnD,QAAQC,mBAAAA,UAAU,wBAAwB;gCAE/C,iBAAA,GAAA,mBAAA,QAAA,CACQ,YAAY,GACpB,4DACF;gCACO,cAAcC,mBAAAA,kBAAkB,uCAAuC;gCACvE,eAAA,GAAA,mBAAA,QAAA,CAAsB,WAAW,GAAG,0CAA0C;gCAC9E,gBAAgB,MAAM,SAAS,0BAA0B;oCACrD,iBAAA,GAAA,mBAAA,QAAA,CAAwB,cAAc,IAAI,UAAU,kBAAkB,OAAO,KAAK,EAAE,GAAG;gCAC3F,eAAeA,mBAAAA,kBAAkB,kCAAkC;gCACnE,gBAAgBJ,mBAAAA,QAAQ,wBAAwB;gCAChD,eAAeK,mBAAAA,gBAAgB,gCAAgC;gCAC/D,eAAeA,mBAAAA,gBAAgB,gCAAgC;+BAEnE,MAAe,EAAE,OAAO,YAAY,KAAA,IAAA,GAAA,mBAAA,WAAA,CAC1B,qBAAqB,iCAAiC,CACnE;gCAEE,4BACAA,mBAAAA,gBACA,6CACF;oCACW,YAAY,gCAAgC;gCAChD,YAAYC,mBAAAA,UAAU,6BAA6B;8BAExD,eAAe,MAAM,IAAA,GAAA,mBAAA,WAAA,CACV,eAAe,IAAA,GAAA,mBAAA,OAAA,CAExB,kBACC,UAAA,GAAA,mBAAA,SAAA,CAAkB,IAAI,KAAK,UAAU,IAAI,IAAI,IAC7C,SAAS,4BAA4B,OAAO,IAAI,EAAE,GACrD,IAAA,GAAA,mBAAA,WAAA,CACW,mBAAmB,IAAA,GAAA,mBAAA,OAAA,CACvB,qBAAqBC,mBAAAA,WAAW,mDAAmD,IAAA,GAAA,mBAAA,OAAA,CACnF,mBAAmBA,mBAAAA,WAAW,iDAAiD,CACxF;8BAEE,eAAe,UAAU,IAAA,GAAA,mBAAA,WAAA,CACd,gBAAgB,IAAA,GAAA,mBAAA,OAAA,CACpB,kBAAkBC,mBAAAA,QAAQ,0BAA0B,CAC7D;+BAEG,OAAA,GAAA,mBAAA,SAAA,CAAwB,EAAE,QAAQ,KAAK,EAAA,GAAA,mBAAA,QAAA,CAAS,cAAc,CAAC,CAAC,EAAE,SAAS,IAAI,IAAA,GAAA,mBAAA,KAAA,CAC3E,iBAAiB,wBAAwB,CAChD;AACF,CAAC,CAAC,CAAC,OAAO;;;;;;AAOZ,MAAa,0BAA0B,SAAkB,QAAwC;CAC/F,MAAM,QAAS,IAAI,MAA6B,SAAS,CAAC;CAC1D,OAAO,gBAAgB,SAAS,IAAI,KAAA,GAAA,8BAAA,aAAA,CAAiB,KAAK,CAAC,CAAC;AAC9D;;;;;;;;ACxGA,MAAa,cAAmC;CAC9C,MAAM;CACN,aAAa;CACb,WAAW,SAAS,QAAQ,uBAAuB,SAAS,GAAG;AACjE"}
|
package/dist/data.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_data = require("./data-WcCPqQ_k.cjs");
|
|
3
|
+
exports.DEFAULT_ARTWORK_FIELDS = require_data.DEFAULT_ARTWORK_FIELDS;
|
|
4
|
+
exports.artworkData = require_data.artworkData;
|
|
5
|
+
exports.validateArtwork = require_data.validateArtwork;
|
|
6
|
+
exports.validateArtworkInScene = require_data.validateArtworkInScene;
|
package/dist/data.d.cts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ArtworkField, c as Availability, d as Passepartout, i as Artwork, l as DEFAULT_ARTWORK_FIELDS, n as validateArtwork, o as ArtworkLabel, r as validateArtworkInScene, s as ArtworkLocation, t as artworkData, u as Frame } from "./index-XUkegdM8.cjs";
|
|
2
|
+
export { type Artwork, type ArtworkField, type ArtworkLabel, type ArtworkLocation, type Availability, DEFAULT_ARTWORK_FIELDS, type Frame, type Passepartout, artworkData, validateArtwork, validateArtworkInScene };
|
package/dist/data.d.mts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ArtworkField, c as Availability, d as Passepartout, i as Artwork, l as DEFAULT_ARTWORK_FIELDS, n as validateArtwork, o as ArtworkLabel, r as validateArtworkInScene, s as ArtworkLocation, t as artworkData, u as Frame } from "./index-XUkegdM8.mjs";
|
|
2
|
+
export { type Artwork, type ArtworkField, type ArtworkLabel, type ArtworkLocation, type Availability, DEFAULT_ARTWORK_FIELDS, type Frame, type Passepartout, artworkData, validateArtwork, validateArtworkInScene };
|
package/dist/data.mjs
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { DataPlugin, Measure, SceneContext, SurfaceTexture, ValidationResult, Vec2, Vec3 } from "@scenography/scene";
|
|
2
|
+
|
|
3
|
+
//#region src/data/artwork.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Where an artwork is placed. Extensible via `type`; today a piece either hangs
|
|
6
|
+
* on a named wall edge or floats freely. Future element types (floor, ceiling…)
|
|
7
|
+
* add new variants here.
|
|
8
|
+
*/
|
|
9
|
+
type ArtworkLocation = {
|
|
10
|
+
type: "wall"; /** Name of the `NamedEdge` to hang on. */
|
|
11
|
+
edge: string; /** Position along the edge: a number is units from the edge start, `'50%'` is a fraction. */
|
|
12
|
+
position: Measure;
|
|
13
|
+
/**
|
|
14
|
+
* Vertical placement: a number is an absolute world Y, `'50%'` is a
|
|
15
|
+
* fraction of the wall height. Omitted defaults to the camera's eye height.
|
|
16
|
+
*/
|
|
17
|
+
height?: Measure;
|
|
18
|
+
/**
|
|
19
|
+
* Which face of the wall the piece shows on, relative to the edge
|
|
20
|
+
* direction. `'right'` (default) is the right-hand side when walking the
|
|
21
|
+
* edge from its start vertex to its end vertex.
|
|
22
|
+
*/
|
|
23
|
+
side?: "right" | "left";
|
|
24
|
+
} | {
|
|
25
|
+
type: "floating"; /** World position `[x, y, z]`. */
|
|
26
|
+
point: Vec3; /** Y-axis rotation in radians. Defaults to `0`. */
|
|
27
|
+
yaw?: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* A passepartout (mat) sitting between the frame and the image: a flat border
|
|
31
|
+
* that insets the image. Only meaningful when the artwork has a `frame`.
|
|
32
|
+
*/
|
|
33
|
+
interface Passepartout {
|
|
34
|
+
/** Mat border width around the image, in world units. Defaults to `6`. */
|
|
35
|
+
width?: number;
|
|
36
|
+
/** Mat color (CSS). Defaults to an off-white. */
|
|
37
|
+
color?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A frame around an artwork. By default it's four flat bars rendered with a
|
|
41
|
+
* solid `color` or a `texture` (e.g. wood). For an ornate, sculpted frame, point
|
|
42
|
+
* `model` at an external glTF/GLB instead. A `passepartout` mat can sit between
|
|
43
|
+
* the frame and the image.
|
|
44
|
+
*/
|
|
45
|
+
interface Frame {
|
|
46
|
+
/** Border width (how wide the frame face is), in world units. Defaults to `5`. */
|
|
47
|
+
width?: number;
|
|
48
|
+
/** How far the frame stands proud of the artwork face, in world units. Defaults to `3`. */
|
|
49
|
+
depth?: number;
|
|
50
|
+
/** Frame color (CSS). Defaults to a dark tone. Ignored when `texture` or `model` is set. */
|
|
51
|
+
color?: string;
|
|
52
|
+
/** Surface texture (e.g. wood); takes visual precedence over `color`. */
|
|
53
|
+
texture?: SurfaceTexture;
|
|
54
|
+
/** External glTF/GLB model for an ornate frame; replaces the generated bars. */
|
|
55
|
+
model?: string;
|
|
56
|
+
/** Passepartout mat between the frame and the image. */
|
|
57
|
+
passepartout?: Passepartout;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Commercial status of a piece. Gallery convention: a colored dot signals
|
|
61
|
+
* availability. The dot's color is presentation (decided by the renderer), not
|
|
62
|
+
* data — this is the state it derives from.
|
|
63
|
+
*/
|
|
64
|
+
type Availability = "for-sale" | "sold" | "not-for-sale";
|
|
65
|
+
/**
|
|
66
|
+
* The fields a label/info card can show, identified by name. A `fields` array
|
|
67
|
+
* toggles which are visible; the render order is fixed (it is not the array
|
|
68
|
+
* order): title, then author/year, then technique + dimensions on one line, with
|
|
69
|
+
* availability shown as a corner dot. Shared by the physical label (engine) and
|
|
70
|
+
* the overlay info card.
|
|
71
|
+
*/
|
|
72
|
+
type ArtworkField = "title" | "author" | "year" | "technique" | "dimensions" | "availability";
|
|
73
|
+
/** Default visibility when a label/card doesn't specify its own `fields`. */
|
|
74
|
+
declare const DEFAULT_ARTWORK_FIELDS: readonly ArtworkField[];
|
|
75
|
+
/**
|
|
76
|
+
* A physical wall label (cartela) materialized next to the piece. Its presence
|
|
77
|
+
* is the opt-in; the engine renders it from the artwork's own data.
|
|
78
|
+
*/
|
|
79
|
+
interface ArtworkLabel {
|
|
80
|
+
/** Where the label sits relative to the piece. Defaults to `'below'`. */
|
|
81
|
+
side?: "below" | "left" | "right";
|
|
82
|
+
/** Which fields to show. Defaults to `DEFAULT_ARTWORK_FIELDS`. */
|
|
83
|
+
fields?: ArtworkField[];
|
|
84
|
+
/**
|
|
85
|
+
* Size multiplier over the real physical size (24pt title etc). Defaults to
|
|
86
|
+
* `1` (true to life), but a real cartela is only a few cm tall — tiny and hard
|
|
87
|
+
* to read/click at gallery scale — so bump this (e.g. `3`) for legibility.
|
|
88
|
+
*/
|
|
89
|
+
scale?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Fine-tune the placement with an `[x, y]` shift in world units, added on top
|
|
92
|
+
* of the `side` position (the frame is already cleared). `x` runs along the
|
|
93
|
+
* wall (right is positive), `y` is vertical (up is positive).
|
|
94
|
+
*/
|
|
95
|
+
offset?: Vec2;
|
|
96
|
+
}
|
|
97
|
+
/** A framed image placed somewhere in the scene. */
|
|
98
|
+
interface Artwork {
|
|
99
|
+
id: string;
|
|
100
|
+
location: ArtworkLocation;
|
|
101
|
+
/** Image URL/path to display. */
|
|
102
|
+
src: string;
|
|
103
|
+
/** `[width, height]` of the artwork, in world units. */
|
|
104
|
+
size: Vec2;
|
|
105
|
+
/**
|
|
106
|
+
* Box depth (volume) in world units: the piece is a box with the image on the
|
|
107
|
+
* front and white sides/back. Defaults to `2`.
|
|
108
|
+
*/
|
|
109
|
+
depth?: number;
|
|
110
|
+
/** Optional frame around the artwork. */
|
|
111
|
+
frame?: Frame;
|
|
112
|
+
title?: string;
|
|
113
|
+
technique?: string;
|
|
114
|
+
/** Author / artist name. */
|
|
115
|
+
author?: string;
|
|
116
|
+
/** Year the piece was made. */
|
|
117
|
+
year?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Real-world dimensions for display, e.g. `'92 × 73 cm'`. Distinct from `size`,
|
|
120
|
+
* which is in world units.
|
|
121
|
+
*/
|
|
122
|
+
dimensions?: string;
|
|
123
|
+
/** Commercial status; drives the availability dot. */
|
|
124
|
+
availability?: Availability;
|
|
125
|
+
/** Long-form caption / catalog entry, shown in the expanded info panel. */
|
|
126
|
+
description?: string;
|
|
127
|
+
/**
|
|
128
|
+
* External URL for the piece (a catalog page, or a marketplace listing when
|
|
129
|
+
* it's for sale). Surfaced as a link in the expanded info panel.
|
|
130
|
+
*/
|
|
131
|
+
href?: string;
|
|
132
|
+
/** When present, a physical wall label is materialized next to the piece. */
|
|
133
|
+
label?: ArtworkLabel;
|
|
134
|
+
/**
|
|
135
|
+
* Max distance (world units) from which the piece can be clicked to open its
|
|
136
|
+
* info panel. A gate so far-away pieces aren't interactive. Absent = no gate.
|
|
137
|
+
*/
|
|
138
|
+
infoRadius?: number;
|
|
139
|
+
}
|
|
140
|
+
declare module "@scenography/scene" {
|
|
141
|
+
interface Scene {
|
|
142
|
+
/** Framed images placed in the space — provided by `@scenography/plugin-artwork`. */
|
|
143
|
+
artworks?: Artwork[];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/data/validate.d.ts
|
|
148
|
+
/**
|
|
149
|
+
* Validate one artwork. `edgeNames` is the set of wall-edge names in the scene,
|
|
150
|
+
* used to check `location.edge` references. Returns every problem found, in
|
|
151
|
+
* order, with paths relative to the artwork (the caller scopes them).
|
|
152
|
+
*/
|
|
153
|
+
declare const validateArtwork: (artwork: Artwork, edgeNames: Set<string>) => ValidationResult;
|
|
154
|
+
/**
|
|
155
|
+
* Validate an artwork against its scene: resolves the set of valid wall-edge
|
|
156
|
+
* names from the scene's walls (the wall plugin's data half) so `location.edge`
|
|
157
|
+
* references are checked. Floating pieces don't need any wall.
|
|
158
|
+
*/
|
|
159
|
+
declare const validateArtworkInScene: (artwork: Artwork, ctx: SceneContext) => ValidationResult;
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/data/plugin.d.ts
|
|
162
|
+
/**
|
|
163
|
+
* The data half of the artwork plugin: owns the `artworks` scene key (a
|
|
164
|
+
* collection) and validates each piece — including that a wall-hung piece
|
|
165
|
+
* references a real wall edge. Pure — no three.js.
|
|
166
|
+
*/
|
|
167
|
+
declare const artworkData: DataPlugin<Artwork>;
|
|
168
|
+
//#endregion
|
|
169
|
+
export { ArtworkField as a, Availability as c, Passepartout as d, Artwork as i, DEFAULT_ARTWORK_FIELDS as l, validateArtwork as n, ArtworkLabel as o, validateArtworkInScene as r, ArtworkLocation as s, artworkData as t, Frame as u };
|
|
170
|
+
//# sourceMappingURL=index-XUkegdM8.d.cts.map
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { DataPlugin, Measure, SceneContext, SurfaceTexture, ValidationResult, Vec2, Vec3 } from "@scenography/scene";
|
|
2
|
+
|
|
3
|
+
//#region src/data/artwork.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Where an artwork is placed. Extensible via `type`; today a piece either hangs
|
|
6
|
+
* on a named wall edge or floats freely. Future element types (floor, ceiling…)
|
|
7
|
+
* add new variants here.
|
|
8
|
+
*/
|
|
9
|
+
type ArtworkLocation = {
|
|
10
|
+
type: "wall"; /** Name of the `NamedEdge` to hang on. */
|
|
11
|
+
edge: string; /** Position along the edge: a number is units from the edge start, `'50%'` is a fraction. */
|
|
12
|
+
position: Measure;
|
|
13
|
+
/**
|
|
14
|
+
* Vertical placement: a number is an absolute world Y, `'50%'` is a
|
|
15
|
+
* fraction of the wall height. Omitted defaults to the camera's eye height.
|
|
16
|
+
*/
|
|
17
|
+
height?: Measure;
|
|
18
|
+
/**
|
|
19
|
+
* Which face of the wall the piece shows on, relative to the edge
|
|
20
|
+
* direction. `'right'` (default) is the right-hand side when walking the
|
|
21
|
+
* edge from its start vertex to its end vertex.
|
|
22
|
+
*/
|
|
23
|
+
side?: "right" | "left";
|
|
24
|
+
} | {
|
|
25
|
+
type: "floating"; /** World position `[x, y, z]`. */
|
|
26
|
+
point: Vec3; /** Y-axis rotation in radians. Defaults to `0`. */
|
|
27
|
+
yaw?: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* A passepartout (mat) sitting between the frame and the image: a flat border
|
|
31
|
+
* that insets the image. Only meaningful when the artwork has a `frame`.
|
|
32
|
+
*/
|
|
33
|
+
interface Passepartout {
|
|
34
|
+
/** Mat border width around the image, in world units. Defaults to `6`. */
|
|
35
|
+
width?: number;
|
|
36
|
+
/** Mat color (CSS). Defaults to an off-white. */
|
|
37
|
+
color?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A frame around an artwork. By default it's four flat bars rendered with a
|
|
41
|
+
* solid `color` or a `texture` (e.g. wood). For an ornate, sculpted frame, point
|
|
42
|
+
* `model` at an external glTF/GLB instead. A `passepartout` mat can sit between
|
|
43
|
+
* the frame and the image.
|
|
44
|
+
*/
|
|
45
|
+
interface Frame {
|
|
46
|
+
/** Border width (how wide the frame face is), in world units. Defaults to `5`. */
|
|
47
|
+
width?: number;
|
|
48
|
+
/** How far the frame stands proud of the artwork face, in world units. Defaults to `3`. */
|
|
49
|
+
depth?: number;
|
|
50
|
+
/** Frame color (CSS). Defaults to a dark tone. Ignored when `texture` or `model` is set. */
|
|
51
|
+
color?: string;
|
|
52
|
+
/** Surface texture (e.g. wood); takes visual precedence over `color`. */
|
|
53
|
+
texture?: SurfaceTexture;
|
|
54
|
+
/** External glTF/GLB model for an ornate frame; replaces the generated bars. */
|
|
55
|
+
model?: string;
|
|
56
|
+
/** Passepartout mat between the frame and the image. */
|
|
57
|
+
passepartout?: Passepartout;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Commercial status of a piece. Gallery convention: a colored dot signals
|
|
61
|
+
* availability. The dot's color is presentation (decided by the renderer), not
|
|
62
|
+
* data — this is the state it derives from.
|
|
63
|
+
*/
|
|
64
|
+
type Availability = "for-sale" | "sold" | "not-for-sale";
|
|
65
|
+
/**
|
|
66
|
+
* The fields a label/info card can show, identified by name. A `fields` array
|
|
67
|
+
* toggles which are visible; the render order is fixed (it is not the array
|
|
68
|
+
* order): title, then author/year, then technique + dimensions on one line, with
|
|
69
|
+
* availability shown as a corner dot. Shared by the physical label (engine) and
|
|
70
|
+
* the overlay info card.
|
|
71
|
+
*/
|
|
72
|
+
type ArtworkField = "title" | "author" | "year" | "technique" | "dimensions" | "availability";
|
|
73
|
+
/** Default visibility when a label/card doesn't specify its own `fields`. */
|
|
74
|
+
declare const DEFAULT_ARTWORK_FIELDS: readonly ArtworkField[];
|
|
75
|
+
/**
|
|
76
|
+
* A physical wall label (cartela) materialized next to the piece. Its presence
|
|
77
|
+
* is the opt-in; the engine renders it from the artwork's own data.
|
|
78
|
+
*/
|
|
79
|
+
interface ArtworkLabel {
|
|
80
|
+
/** Where the label sits relative to the piece. Defaults to `'below'`. */
|
|
81
|
+
side?: "below" | "left" | "right";
|
|
82
|
+
/** Which fields to show. Defaults to `DEFAULT_ARTWORK_FIELDS`. */
|
|
83
|
+
fields?: ArtworkField[];
|
|
84
|
+
/**
|
|
85
|
+
* Size multiplier over the real physical size (24pt title etc). Defaults to
|
|
86
|
+
* `1` (true to life), but a real cartela is only a few cm tall — tiny and hard
|
|
87
|
+
* to read/click at gallery scale — so bump this (e.g. `3`) for legibility.
|
|
88
|
+
*/
|
|
89
|
+
scale?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Fine-tune the placement with an `[x, y]` shift in world units, added on top
|
|
92
|
+
* of the `side` position (the frame is already cleared). `x` runs along the
|
|
93
|
+
* wall (right is positive), `y` is vertical (up is positive).
|
|
94
|
+
*/
|
|
95
|
+
offset?: Vec2;
|
|
96
|
+
}
|
|
97
|
+
/** A framed image placed somewhere in the scene. */
|
|
98
|
+
interface Artwork {
|
|
99
|
+
id: string;
|
|
100
|
+
location: ArtworkLocation;
|
|
101
|
+
/** Image URL/path to display. */
|
|
102
|
+
src: string;
|
|
103
|
+
/** `[width, height]` of the artwork, in world units. */
|
|
104
|
+
size: Vec2;
|
|
105
|
+
/**
|
|
106
|
+
* Box depth (volume) in world units: the piece is a box with the image on the
|
|
107
|
+
* front and white sides/back. Defaults to `2`.
|
|
108
|
+
*/
|
|
109
|
+
depth?: number;
|
|
110
|
+
/** Optional frame around the artwork. */
|
|
111
|
+
frame?: Frame;
|
|
112
|
+
title?: string;
|
|
113
|
+
technique?: string;
|
|
114
|
+
/** Author / artist name. */
|
|
115
|
+
author?: string;
|
|
116
|
+
/** Year the piece was made. */
|
|
117
|
+
year?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Real-world dimensions for display, e.g. `'92 × 73 cm'`. Distinct from `size`,
|
|
120
|
+
* which is in world units.
|
|
121
|
+
*/
|
|
122
|
+
dimensions?: string;
|
|
123
|
+
/** Commercial status; drives the availability dot. */
|
|
124
|
+
availability?: Availability;
|
|
125
|
+
/** Long-form caption / catalog entry, shown in the expanded info panel. */
|
|
126
|
+
description?: string;
|
|
127
|
+
/**
|
|
128
|
+
* External URL for the piece (a catalog page, or a marketplace listing when
|
|
129
|
+
* it's for sale). Surfaced as a link in the expanded info panel.
|
|
130
|
+
*/
|
|
131
|
+
href?: string;
|
|
132
|
+
/** When present, a physical wall label is materialized next to the piece. */
|
|
133
|
+
label?: ArtworkLabel;
|
|
134
|
+
/**
|
|
135
|
+
* Max distance (world units) from which the piece can be clicked to open its
|
|
136
|
+
* info panel. A gate so far-away pieces aren't interactive. Absent = no gate.
|
|
137
|
+
*/
|
|
138
|
+
infoRadius?: number;
|
|
139
|
+
}
|
|
140
|
+
declare module "@scenography/scene" {
|
|
141
|
+
interface Scene {
|
|
142
|
+
/** Framed images placed in the space — provided by `@scenography/plugin-artwork`. */
|
|
143
|
+
artworks?: Artwork[];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/data/validate.d.ts
|
|
148
|
+
/**
|
|
149
|
+
* Validate one artwork. `edgeNames` is the set of wall-edge names in the scene,
|
|
150
|
+
* used to check `location.edge` references. Returns every problem found, in
|
|
151
|
+
* order, with paths relative to the artwork (the caller scopes them).
|
|
152
|
+
*/
|
|
153
|
+
declare const validateArtwork: (artwork: Artwork, edgeNames: Set<string>) => ValidationResult;
|
|
154
|
+
/**
|
|
155
|
+
* Validate an artwork against its scene: resolves the set of valid wall-edge
|
|
156
|
+
* names from the scene's walls (the wall plugin's data half) so `location.edge`
|
|
157
|
+
* references are checked. Floating pieces don't need any wall.
|
|
158
|
+
*/
|
|
159
|
+
declare const validateArtworkInScene: (artwork: Artwork, ctx: SceneContext) => ValidationResult;
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/data/plugin.d.ts
|
|
162
|
+
/**
|
|
163
|
+
* The data half of the artwork plugin: owns the `artworks` scene key (a
|
|
164
|
+
* collection) and validates each piece — including that a wall-hung piece
|
|
165
|
+
* references a real wall edge. Pure — no three.js.
|
|
166
|
+
*/
|
|
167
|
+
declare const artworkData: DataPlugin<Artwork>;
|
|
168
|
+
//#endregion
|
|
169
|
+
export { ArtworkField as a, Availability as c, Passepartout as d, Artwork as i, DEFAULT_ARTWORK_FIELDS as l, validateArtwork as n, ArtworkLabel as o, validateArtworkInScene as r, ArtworkLocation as s, artworkData as t, Frame as u };
|
|
170
|
+
//# sourceMappingURL=index-XUkegdM8.d.mts.map
|