@oeave/bakery3 0.1.0 → 0.3.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.
Files changed (77) hide show
  1. package/README.md +10 -10
  2. package/dist/bake/index.d.ts +3 -3
  3. package/dist/bake/index.js +4 -4
  4. package/dist/{bake-DaOlenyx.d.ts → bake-DZ-CJR6f.d.ts} +8 -1
  5. package/dist/catalog/index.d.ts +5 -5
  6. package/dist/catalog/index.js +3 -3
  7. package/dist/{chunk-TGFNQBKC.js → chunk-3T3FJOSD.js} +5 -5
  8. package/dist/chunk-3T3FJOSD.js.map +1 -0
  9. package/dist/{chunk-V3QINEEG.js → chunk-62M6XXNX.js} +58 -3
  10. package/dist/chunk-62M6XXNX.js.map +1 -0
  11. package/dist/{chunk-ARMTTZXZ.js → chunk-6FAMQZXB.js} +8 -8
  12. package/dist/chunk-6FAMQZXB.js.map +1 -0
  13. package/dist/{chunk-54WJDN5R.js → chunk-CSSJRP5W.js} +9 -6
  14. package/dist/chunk-CSSJRP5W.js.map +1 -0
  15. package/dist/{chunk-W6WQ5UP7.js → chunk-EN4ROBYQ.js} +32 -5
  16. package/dist/chunk-EN4ROBYQ.js.map +1 -0
  17. package/dist/chunk-KZLVTSBI.js +191 -0
  18. package/dist/chunk-KZLVTSBI.js.map +1 -0
  19. package/dist/{chunk-MD2U6S4N.js → chunk-LAKXC4WR.js} +218 -142
  20. package/dist/chunk-LAKXC4WR.js.map +1 -0
  21. package/dist/{chunk-HQTUFVMM.js → chunk-NEK5TBEH.js} +3 -3
  22. package/dist/chunk-NEK5TBEH.js.map +1 -0
  23. package/dist/chunk-OPM2L63B.js +253 -0
  24. package/dist/chunk-OPM2L63B.js.map +1 -0
  25. package/dist/{chunk-APBOWWSV.js → chunk-R5IUPOWI.js} +3 -3
  26. package/dist/chunk-R5IUPOWI.js.map +1 -0
  27. package/dist/{chunk-6YVQANMW.js → chunk-STRK4SZN.js} +262 -16
  28. package/dist/chunk-STRK4SZN.js.map +1 -0
  29. package/dist/{chunk-CX4WTVGH.js → chunk-WLEFNUOH.js} +411 -371
  30. package/dist/chunk-WLEFNUOH.js.map +1 -0
  31. package/dist/{chunk-25ZXSVQ3.js → chunk-X6GW43EX.js} +3 -3
  32. package/dist/{chunk-25ZXSVQ3.js.map → chunk-X6GW43EX.js.map} +1 -1
  33. package/dist/{chunk-WBIAFIHV.js → chunk-XK35ANPJ.js} +3 -3
  34. package/dist/{chunk-WBIAFIHV.js.map → chunk-XK35ANPJ.js.map} +1 -1
  35. package/dist/devtools/index.d.ts +3 -3
  36. package/dist/devtools/index.js +10 -9
  37. package/dist/hotspots/index.d.ts +237 -31
  38. package/dist/hotspots/index.js +1013 -118
  39. package/dist/hotspots/index.js.map +1 -1
  40. package/dist/index.d.ts +39 -8
  41. package/dist/index.js +11 -10
  42. package/dist/node/index.cjs +266 -141
  43. package/dist/node/index.cjs.map +1 -1
  44. package/dist/node/index.d.cts +152 -87
  45. package/dist/node/index.d.ts +27 -6
  46. package/dist/node/index.js +52 -3
  47. package/dist/node/index.js.map +1 -1
  48. package/dist/{prepare-C5qVvaw4.d.ts → prepare-BtjY4G3q.d.ts} +1 -1
  49. package/dist/presets/index.d.ts +37 -1
  50. package/dist/presets/index.js +4 -4
  51. package/dist/presets/index.js.map +1 -1
  52. package/dist/r3f/index.d.ts +4 -4
  53. package/dist/r3f/index.js +13 -12
  54. package/dist/r3f/index.js.map +1 -1
  55. package/dist/{room-S5TLCK4F.js → room-BM7Z7NKG.js} +6 -6
  56. package/dist/{room-S5TLCK4F.js.map → room-BM7Z7NKG.js.map} +1 -1
  57. package/dist/{rooms.gen-vhj6sFW5.d.ts → rooms.gen-DItzBR9k.d.ts} +112 -84
  58. package/dist/session-NPVBESWA.js +9 -0
  59. package/dist/{session-YCQS4MRH.js.map → session-NPVBESWA.js.map} +1 -1
  60. package/dist/{testRun-BIPXaVPb.d.ts → testRun-20OARnQr.d.ts} +12 -3
  61. package/dist/tsl/index.d.ts +14 -1
  62. package/dist/tsl/index.js +8 -2
  63. package/dist/tsl/index.js.map +1 -1
  64. package/package.json +1 -1
  65. package/dist/chunk-54WJDN5R.js.map +0 -1
  66. package/dist/chunk-6YVQANMW.js.map +0 -1
  67. package/dist/chunk-AF5BIELI.js +0 -70
  68. package/dist/chunk-AF5BIELI.js.map +0 -1
  69. package/dist/chunk-APBOWWSV.js.map +0 -1
  70. package/dist/chunk-ARMTTZXZ.js.map +0 -1
  71. package/dist/chunk-CX4WTVGH.js.map +0 -1
  72. package/dist/chunk-HQTUFVMM.js.map +0 -1
  73. package/dist/chunk-MD2U6S4N.js.map +0 -1
  74. package/dist/chunk-TGFNQBKC.js.map +0 -1
  75. package/dist/chunk-V3QINEEG.js.map +0 -1
  76. package/dist/chunk-W6WQ5UP7.js.map +0 -1
  77. package/dist/session-YCQS4MRH.js +0 -9
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/variant.ts","../src/renderSet.ts"],"names":[],"mappings":"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","file":"chunk-WBIAFIHV.js","sourcesContent":["/**\n * One capture, many pictures: the pure pieces.\n *\n * A capture is one GLB with every finish inside it and a few KB of manifest.\n * A picture of it is that manifest with another camera and another hidden\n * list, and nothing else. `render({ cameras })`, `renderVariants()` and the\n * catalog recorder all build their items from these, and they are exported\n * from `@oeave/bakery3/catalog` so a recorder that writes `manifests.jsonl`\n * instead of submitting builds exactly the same ones.\n */\n\nimport { Matrix4, Quaternion, Vector3, type Object3D } from 'three';\nimport type {\n CameraSpec,\n OutputSpec,\n Quat,\n RuntimeState,\n SceneManifest,\n Vec3,\n} from './protocol/index.js';\nimport { Bakery3Error } from './errors.js';\n\nexport type CatalogCamera = {\n id: string;\n position: Vec3;\n target: Vec3;\n /** Vertical field of view in degrees, as `PerspectiveCamera.fov`. */\n fov: number;\n};\n\nexport type CatalogFinish = {\n id: string;\n /** Object names this finish shows. Anything another finish claims and this\n * one does not is hidden; anything no finish claims stays as it is. */\n show: string[];\n};\n\n/**\n * Which object ids this finish hides: everything any finish claims that this\n * finish does not show. What no finish claims is not touched: the frame a\n * chair's finishes share stays visible, and so does whatever the developer\n * had hidden themselves.\n */\nexport function variantHiddenIds(\n objects: Record<string, string>,\n finishes: CatalogFinish[],\n finish: CatalogFinish,\n): string[] {\n const shown = new Set(finish.show);\n const hidden = new Set<string>();\n for (const other of finishes) {\n for (const name of other.show) {\n if (shown.has(name)) continue;\n const id = objects[name];\n if (id === undefined) throw unknownObject(name, other.id, objects);\n hidden.add(id);\n }\n }\n // A shown name that names nothing is the same typo from the other side:\n // the finish would render silently without the part it says it has.\n for (const name of finish.show) {\n if (objects[name] === undefined)\n throw unknownObject(name, finish.id, objects);\n }\n return [...hidden];\n}\n\n/**\n * A `CameraSpec` for a catalog camera: perspective, at `position`, looking\n * at `target` with +Y up. `output` sets the aspect so the render frames what\n * the declaration meant, not what some viewport happened to be.\n */\nexport function cameraSpec(\n camera: CatalogCamera,\n output?: Pick<OutputSpec, 'width' | 'height'>,\n): CameraSpec {\n const position = new Vector3(...camera.position);\n const target = new Vector3(...camera.target);\n const distance = position.distanceTo(target);\n if (!(distance > 0)) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Camera \"${camera.id}\" has its position on its target.`,\n why: 'A camera needs a direction to look in, and position === target gives it none.',\n fix: 'Move `position` or `target` so they differ.',\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n const up =\n Math.abs(position.x - target.x) < 1e-9 &&\n Math.abs(position.z - target.z) < 1e-9\n ? // Straight down (or up): +Y up is degenerate, so face -Z as three does.\n new Vector3(0, 0, -1)\n : new Vector3(0, 1, 0);\n // three's own convention: a camera looks down its local -Z.\n const rotation = new Matrix4().lookAt(position, target, up);\n const q = new Quaternion().setFromRotationMatrix(rotation);\n const quaternion: Quat = [q.x, q.y, q.z, q.w];\n\n return {\n type: 'perspective',\n fov: camera.fov,\n ...(output ? { aspect: output.width / output.height } : {}),\n // Clip planes scaled to the shot: a watch a hand-span away and a sofa\n // across a room both keep their subject well inside them.\n near: Math.max(0.01, distance / 100),\n far: Math.max(100, distance * 100),\n position: [position.x, position.y, position.z],\n quaternion,\n name: camera.id,\n };\n}\n\n/** One capture, one variant: a new camera and a new hidden list, nothing else. */\nexport function variantManifest(\n manifest: SceneManifest,\n camera: CameraSpec,\n hiddenObjectIds: string[],\n): SceneManifest {\n const runtime: RuntimeState = { ...manifest.runtime };\n if (hiddenObjectIds.length) runtime.hiddenObjectIds = hiddenObjectIds;\n else delete runtime.hiddenObjectIds;\n return {\n ...manifest,\n camera,\n ...(Object.keys(runtime).length ? { runtime } : {}),\n };\n}\n\n/**\n * Release everything a product holds on the GPU: geometries, materials and\n * every texture a material references. The tree is detached from the scene\n * by the caller; this is what lets the browser actually free it.\n */\nexport function disposeTree(root: Object3D): void {\n const textures = new Set<{ dispose(): void }>();\n const materials = new Set<{ dispose(): void }>();\n root.traverse((object) => {\n const mesh = object as Object3D & {\n geometry?: { dispose(): void };\n material?: unknown;\n };\n mesh.geometry?.dispose();\n const list = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n for (const material of list) {\n if (!material || typeof material !== 'object') continue;\n materials.add(material as { dispose(): void });\n for (const value of Object.values(material as Record<string, unknown>)) {\n if (\n value &&\n typeof value === 'object' &&\n (value as { isTexture?: boolean }).isTexture\n ) {\n textures.add(value as { dispose(): void });\n }\n }\n }\n });\n for (const texture of textures) texture.dispose();\n for (const material of materials) material.dispose();\n}\n\n/** Every id any finish of the product claims. */\nexport function claimedIds(\n objects: Record<string, string>,\n finishes: CatalogFinish[],\n): Set<string> {\n const ids = new Set<string>();\n for (const finish of finishes) {\n for (const name of finish.show) {\n const id = objects[name];\n if (id !== undefined) ids.add(id);\n }\n }\n return ids;\n}\n\nfunction unknownObject(\n name: string,\n finish: string,\n objects: Record<string, string>,\n): Bakery3Error {\n const known = Object.keys(objects);\n const sample = known.slice(0, 8).join(', ') + (known.length > 8 ? ', …' : '');\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Finish \"${finish}\" shows \"${name}\", but no object in the product has that name.`,\n why: 'The finish would render without the part it says it has, and the missing part would only be noticed in the catalog.',\n fix: `Match \\`show\\` to the object names in the model${known.length ? ` (it has: ${sample})` : ''}.`,\n path: name,\n docs: 'https://bakery3.com/docs/api',\n });\n}\n","/**\n * Many pictures from one call.\n *\n * // every camera of the scene you have\n * const shots = await bakery3.render({ cameras: ['hero', 'front', 'detail'] });\n * const images = await shots.result();\n *\n * // every variant, from every camera\n * const run = await bakery3.renderVariants({\n * key: 'fall-2026',\n * variants: products,\n * apply: (product) => configurator.show(product),\n * cameras: ['hero', 'front', 'detail'],\n * maxCost: 600,\n * });\n * await run.wait();\n * for await (const image of run.images()) save(image.variant, image.camera, image.url);\n *\n * Both record into a batch and hand back a `RenderSet`, so a set of three\n * and a catalog of six thousand are the same object with the same\n * guarantees: one ceiling for all of it, one poll for all of it, and a\n * re-run that adopts what already rendered.\n *\n * What this file is careful about is the customer's machine. A catalog is\n * recorded in a browser tab, and a tab that holds two hundred products, or\n * two hundred exported GLBs, or six thousand result records, is a tab that\n * dies at product 140 with nothing to show for it. So:\n *\n * - One variant resident. A variant is applied, captured, uploaded, turned\n * into items, flushed and let go of before the next is touched. The\n * exported GLB is the largest thing a run holds, and it holds one.\n * - The list need not exist. `variants` is any iterable or async iterable: a\n * generator, a database cursor, a paged fetch. It is pulled one at a time\n * and never collected.\n * - One capture, many pictures. Cameras and finishes multiply items, not\n * exports: each is the same few-KB manifest with another camera and\n * another hidden list (`variant.ts`).\n * - Nothing uploads twice. Assets are content addressed; a variant that\n * changed no geometry uploads nothing, and one whose hashes this run has\n * already sent does not even ask.\n * - Results are walked, not held. `images()` and `failures()` read the\n * ledger a page at a time by cursor. `result()` collects, and says so: it\n * is for a handful of cameras.\n */\n\nimport { PerspectiveCamera, type Camera, type Scene } from 'three';\nimport type {\n BatchItemOutcome,\n BatchItemRequest,\n CameraSpec,\n OutputSpec,\n RenderError,\n RenderResult,\n RenderSpec,\n SceneManifest,\n} from './protocol/index.js';\nimport type { Batch, BatchProgress, WaitOptions } from './batch.js';\nimport type { Capture } from './index.js';\nimport type { UploadProgress } from './upload.js';\nimport { captureCamera } from './capture/camera.js';\nimport { Bakery3Error } from './errors.js';\nimport { emptyRecorded, who, type Recorded } from './set.js';\nimport {\n cameraSpec,\n claimedIds,\n variantHiddenIds,\n variantManifest,\n type CatalogCamera,\n type CatalogFinish,\n} from './variant.js';\n\n// ---------------------------------------------------------------------------\n// What the developer types\n// ---------------------------------------------------------------------------\n\n/**\n * A camera to photograph from:\n *\n * - a **name**: a camera in the scene with that `name`. Looked up again for\n * every variant, after `apply`, so a camera that ships inside a product's\n * own GLB, or rides a rig the variant moves, is read where it then stands.\n * - a **Camera**: read as it stands at capture. Its `name` is its id.\n * - a **declaration**: `{ id, position, target, fov }`, for a shot no camera\n * object exists for. Its aspect is the output's.\n */\nexport type ViewCamera = string | Camera | CatalogCamera;\n\n/** What `apply` may hand back: how to undo it. Called before the next variant, and when a run stops. */\nexport type VariantCleanup = () => void | Promise<void>;\n\nexport type VariantProgress = BatchProgress & {\n /** The variant just recorded. */\n variant: string;\n /** Variants recorded so far, this one included. */\n variants: number;\n /** Pictures recorded so far: variants × finishes × cameras. */\n images: number;\n /** What this variant's capture uploaded. Usually nothing after the first. */\n upload?: UploadProgress;\n};\n\nexport { RenderSet, RenderSetRefused } from './set.js';\nexport type {\n Recorded,\n RenderSetFailure,\n RenderSetImage,\n RenderSetRefusal,\n VariantsQuote,\n} from './set.js';\n\nconst REFUSALS_KEPT = 50;\nconst KEY_LIMIT = 200;\nconst DOCS = 'https://bakery3.com/docs/batches';\n\n// ---------------------------------------------------------------------------\n// The recorder\n// ---------------------------------------------------------------------------\n\n/** The slice of the SDK a recording drives. `Bakery3` satisfies it; so does a fake. */\nexport type RecorderHost = {\n capture(options: { scene: Scene; camera: Camera }): Promise<Capture>;\n uploadAssets(\n capture: Pick<Capture, 'assets'>,\n onProgress?: (progress: UploadProgress) => void,\n ): Promise<UploadProgress>;\n};\n\nexport type Recording<V> = {\n host: RecorderHost;\n batch: Batch;\n scene: Scene;\n variants: Iterable<V> | AsyncIterable<V>;\n idOf: (variant: V, index: number) => string;\n apply?: (\n variant: V,\n ) => void | VariantCleanup | Promise<void | VariantCleanup>;\n finishesOf?: (variant: V) => CatalogFinish[] | undefined;\n cameras: ViewCamera[];\n output: OutputSpec;\n /** The manifest of one picture, wrapped in everything else its render needs. */\n spec: (manifest: SceneManifest) => RenderSpec;\n metadataOf: (\n variant: V,\n ids: { variant: string; finish?: string; camera: string },\n ) => Record<string, unknown>;\n /** Appended to every item key as `@version`. */\n version?: string;\n /** Prefixed to every item key, for a set whose variant ids are not unique in the project on their own. */\n prefix?: string;\n /** A ceiling for each picture, under the set's. */\n maxCostPerImage?: number;\n /** Price, admit nothing, upload nothing. */\n dryRun?: boolean;\n signal?: AbortSignal;\n onVariant?: (progress: VariantProgress) => void;\n /** Collect the item keys, in order. Only for a set small enough to want one. */\n keys?: string[];\n};\n\nexport type RecordingResult = Recorded & { estimated: number; maximum: number };\n\n/**\n * Walk the variants, one resident at a time.\n *\n * Every `await` in the loop is a point where the previous variant's capture\n * is already unreachable: it lives in a `const` inside the iteration, the\n * items built from it hold its manifest (a few KB) and never its blobs, and\n * the flush at the end of the iteration hands those to the server.\n */\nexport async function record<V>(plan: Recording<V>): Promise<RecordingResult> {\n validateCameras(plan.cameras);\n const tally: RecordingResult = {\n ...emptyRecorded(),\n estimated: 0,\n maximum: 0,\n };\n /** Asset hashes this run has already put in storage. Strings, not bytes. */\n const sent = new Set<string>();\n const seen = new Set<string>();\n let index = 0;\n\n for await (const variant of plan.variants) {\n if (plan.signal?.aborted) throw plan.signal.reason ?? abortError();\n\n const id = plan.idOf(variant, index++);\n if (seen.has(id)) throw duplicateVariant(id);\n seen.add(id);\n\n const cleanup = await plan.apply?.(variant);\n try {\n const views = resolveCameras(plan.cameras, plan.scene, plan.output);\n const capture = await plan.host.capture({\n scene: plan.scene,\n camera:\n views.find((view) => view.object)?.object ?? new PerspectiveCamera(),\n });\n\n let upload: UploadProgress | undefined;\n if (\n !plan.dryRun &&\n capture.assets.some((asset) => !sent.has(asset.hash))\n ) {\n upload = await plan.host.uploadAssets(capture);\n for (const asset of capture.assets) sent.add(asset.hash);\n }\n\n const items = itemsOf(plan, variant, id, capture, views);\n if (plan.keys) plan.keys.push(...items.map((item) => item.key));\n\n let outcomes: BatchItemOutcome[];\n if (plan.dryRun) {\n outcomes = await plan.batch.quote(items);\n } else {\n for (const item of items) plan.batch.add(item);\n // A refusal here is honest and touches nothing already admitted: the\n // page stays in the batch's buffer and the error says what to do.\n outcomes = await plan.batch.flush();\n }\n\n tally.variants++;\n tally.images += items.length;\n const metadata = new Map(items.map((item) => [item.key, item.metadata]));\n for (const outcome of outcomes) {\n if (outcome.error) {\n tally.refused++;\n if (tally.refusals.length < REFUSALS_KEPT) {\n // Named the way the set's images are: by variant, finish and\n // camera, read from the same metadata.\n const { kind: _kind, ...ids } = who(metadata.get(outcome.key));\n tally.refusals.push({\n key: outcome.key,\n ...ids,\n error: outcome.error,\n });\n }\n } else if (outcome.adopted) {\n tally.adopted++;\n } else {\n tally.estimated += outcome.estimatedCost;\n tally.maximum += outcome.maximumCost;\n }\n }\n\n plan.onVariant?.({\n ...plan.batch.progress(),\n variant: id,\n variants: tally.variants,\n images: tally.images,\n ...(upload ? { upload } : {}),\n });\n } finally {\n // Even when the capture or the flush threw: the caller's scene must not\n // be left holding a variant it did not ask to keep.\n await cleanup?.();\n }\n }\n\n // Sums of cents, kept as cents: 0.1 + 0.2 is not a number to show anybody.\n tally.estimated = Math.round(tally.estimated * 1e4) / 1e4;\n tally.maximum = Math.round(tally.maximum * 1e4) / 1e4;\n return tally;\n}\n\ntype ResolvedView = { id: string; spec: CameraSpec; object?: Camera };\n\nfunction itemsOf<V>(\n plan: Recording<V>,\n variant: V,\n id: string,\n capture: Capture,\n views: ResolvedView[],\n): BatchItemRequest[] {\n const finishes = plan.finishesOf?.(variant);\n const items: BatchItemRequest[] = [];\n\n const push = (\n finish: CatalogFinish | undefined,\n hidden: string[] | undefined,\n ) => {\n for (const view of views) {\n const parts = [plan.prefix, id, finish?.id, view.id].filter(\n (part): part is string => Boolean(part),\n );\n const key = parts.join('/') + (plan.version ? `@${plan.version}` : '');\n if (key.length > KEY_LIMIT) throw keyTooLong(key);\n const manifest = hidden\n ? variantManifest(capture.manifest, view.spec, hidden)\n : { ...capture.manifest, camera: view.spec };\n items.push({\n key,\n spec: plan.spec(manifest),\n metadata: plan.metadataOf(variant, {\n variant: id,\n ...(finish ? { finish: finish.id } : {}),\n camera: view.id,\n }),\n ...(plan.maxCostPerImage !== undefined\n ? { maxCost: plan.maxCostPerImage }\n : {}),\n });\n }\n };\n\n if (!finishes?.length) {\n push(undefined, undefined);\n return items;\n }\n\n const finishIds = new Set<string>();\n // What the scene itself hides stays hidden, unless a finish claims it:\n // a loader that starts on oak has not decided anything about walnut.\n const claimed = claimedIds(capture.objects, finishes);\n const sceneHidden = (capture.manifest.runtime?.hiddenObjectIds ?? []).filter(\n (hiddenId) => !claimed.has(hiddenId),\n );\n for (const finish of finishes) {\n if (finishIds.has(finish.id)) throw duplicateFinish(id, finish.id);\n finishIds.add(finish.id);\n push(finish, [\n ...sceneHidden,\n ...variantHiddenIds(capture.objects, finishes, finish),\n ]);\n }\n return items;\n}\n\n// ---------------------------------------------------------------------------\n// Cameras\n// ---------------------------------------------------------------------------\n\nfunction idOfCamera(camera: ViewCamera, index: number): string {\n if (typeof camera === 'string') return camera;\n if (isCameraObject(camera)) return camera.name || `camera-${index + 1}`;\n return camera.id;\n}\n\nfunction isCameraObject(camera: ViewCamera): camera is Camera {\n return typeof camera === 'object' && (camera as Camera).isCamera === true;\n}\n\nfunction validateCameras(cameras: ViewCamera[]): void {\n if (!cameras.length) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: '`cameras` is empty.',\n why: 'Every picture is taken from a camera, and the camera is part of its key.',\n fix: \"Name the cameras in your scene (`cameras: ['hero', 'front']`), pass Camera objects, or declare `{ id, position, target, fov }`. For the one live camera, leave `cameras` out.\",\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n const ids = new Set<string>();\n cameras.forEach((camera, index) => {\n const id = idOfCamera(camera, index);\n if (ids.has(id)) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Two cameras are called \"${id}\".`,\n why: 'The camera id ends every picture’s key, so two cameras under one id would be one picture.',\n fix: 'Give each camera its own `name` (or `id`).',\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n ids.add(id);\n });\n}\n\n/** Read every camera as it stands now: after `apply`, before the capture. */\nfunction resolveCameras(\n cameras: ViewCamera[],\n scene: Scene,\n output: OutputSpec,\n): ResolvedView[] {\n return cameras.map((camera, index) => {\n const id = idOfCamera(camera, index);\n if (typeof camera === 'string') {\n const object = findCamera(scene, camera);\n return { id, spec: captureCamera(object), object };\n }\n if (isCameraObject(camera))\n return {\n id,\n spec: { ...captureCamera(camera), name: id },\n object: camera,\n };\n return { id, spec: cameraSpec(camera, output) };\n });\n}\n\nfunction findCamera(scene: Scene, name: string): Camera {\n let found: Camera | undefined;\n const cameras: string[] = [];\n scene.traverse((object) => {\n if (!(object as Camera).isCamera) return;\n if (object.name) cameras.push(object.name);\n if (!found && object.name === name) found = object as Camera;\n });\n if (found) return found;\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `No camera named \"${name}\" in the scene.`,\n why: 'A camera given by name is looked up in the scene for every variant, so a rig the variant moves is read where it stands.',\n fix: cameras.length\n ? `The scene's cameras are: ${cameras.slice(0, 8).join(', ')}${cameras.length > 8 ? ', …' : ''}. Match one, or pass the Camera object itself.`\n : 'The scene has no named cameras. Add the camera to the scene and set its `name`, pass the Camera object, or declare `{ id, position, target, fov }`.',\n path: name,\n docs: 'https://bakery3.com/docs/cameras',\n });\n}\n\n// ---------------------------------------------------------------------------\n\n/** `variant.id`, `.sku` or `.key`, or the variant itself when it is a string. */\nexport function defaultVariantId(variant: unknown, index: number): string {\n if (typeof variant === 'string' && variant) return variant;\n if (variant && typeof variant === 'object') {\n for (const field of ['id', 'sku', 'key'] as const) {\n const value = (variant as Record<string, unknown>)[field];\n if (typeof value === 'string' && value) return value;\n if (typeof value === 'number') return String(value);\n }\n }\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Variant ${index + 1} has no id.`,\n why: 'The id starts every picture’s key. It is what lets a re-run adopt the pictures that already rendered, and what a failure is reported under. A position in a list is not stable enough for either.',\n fix: 'Give each variant an `id` (or `sku`), or pass `id: (variant) => …`.',\n docs: DOCS,\n });\n}\n\nfunction duplicateVariant(id: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Two variants are called \"${id}\".`,\n why: 'The second would be adopted as the first: same key, same picture, and the variant it was meant to show never rendered.',\n fix: 'Make the ids unique. When two variants share a SKU, put what differs in the id: `${sku}-${color}`.',\n path: id,\n docs: DOCS,\n });\n}\n\nfunction duplicateFinish(variant: string, finish: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Finish \"${finish}\" of \"${variant}\" is declared twice.`,\n why: 'Finish ids are part of the item key.',\n fix: 'Make them unique per variant.',\n path: `${variant}/${finish}`,\n docs: DOCS,\n });\n}\n\nfunction keyTooLong(key: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `The key \"${key.slice(0, 60)}…\" is ${key.length} characters; a key holds at most ${KEY_LIMIT}.`,\n why: 'A picture’s key is its variant, finish and camera ids joined with \"/\".',\n fix: 'Use shorter ids: a SKU rather than a product title.',\n docs: DOCS,\n });\n}\n\nfunction abortError(): Error {\n const error = new Error('The recording was aborted.');\n error.name = 'AbortError';\n return error;\n}\n"]}
1
+ {"version":3,"sources":["../src/variant.ts","../src/renderSet.ts"],"names":[],"mappings":"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","file":"chunk-XK35ANPJ.js","sourcesContent":["/**\n * One capture, many pictures: the pure pieces.\n *\n * A capture is one GLB with every finish inside it and a few KB of manifest.\n * A picture of it is that manifest with another camera and another hidden\n * list, and nothing else. `render({ cameras })`, `renderVariants()` and the\n * catalog recorder all build their items from these, and they are exported\n * from `@oeave/bakery3/catalog` so a recorder that writes `manifests.jsonl`\n * instead of submitting builds exactly the same ones.\n */\n\nimport { Matrix4, Quaternion, Vector3, type Object3D } from 'three';\nimport type {\n CameraSpec,\n OutputSpec,\n Quat,\n RuntimeState,\n SceneManifest,\n Vec3,\n} from './protocol/index.js';\nimport { Bakery3Error } from './errors.js';\n\nexport type CatalogCamera = {\n id: string;\n position: Vec3;\n target: Vec3;\n /** Vertical field of view in degrees, as `PerspectiveCamera.fov`. */\n fov: number;\n};\n\nexport type CatalogFinish = {\n id: string;\n /** Object names this finish shows. Anything another finish claims and this\n * one does not is hidden; anything no finish claims stays as it is. */\n show: string[];\n};\n\n/**\n * Which object ids this finish hides: everything any finish claims that this\n * finish does not show. What no finish claims is not touched: the frame a\n * chair's finishes share stays visible, and so does whatever the developer\n * had hidden themselves.\n */\nexport function variantHiddenIds(\n objects: Record<string, string>,\n finishes: CatalogFinish[],\n finish: CatalogFinish,\n): string[] {\n const shown = new Set(finish.show);\n const hidden = new Set<string>();\n for (const other of finishes) {\n for (const name of other.show) {\n if (shown.has(name)) continue;\n const id = objects[name];\n if (id === undefined) throw unknownObject(name, other.id, objects);\n hidden.add(id);\n }\n }\n // A shown name that names nothing is the same typo from the other side:\n // the finish would render silently without the part it says it has.\n for (const name of finish.show) {\n if (objects[name] === undefined)\n throw unknownObject(name, finish.id, objects);\n }\n return [...hidden];\n}\n\n/**\n * A `CameraSpec` for a catalog camera: perspective, at `position`, looking\n * at `target` with +Y up. `output` sets the aspect so the render frames what\n * the declaration meant, not what some viewport happened to be.\n */\nexport function cameraSpec(\n camera: CatalogCamera,\n output?: Pick<OutputSpec, 'width' | 'height'>,\n): CameraSpec {\n const position = new Vector3(...camera.position);\n const target = new Vector3(...camera.target);\n const distance = position.distanceTo(target);\n if (!(distance > 0)) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Camera \"${camera.id}\" has its position on its target.`,\n why: 'A camera needs a direction to look in, and position === target gives it none.',\n fix: 'Move `position` or `target` so they differ.',\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n const up =\n Math.abs(position.x - target.x) < 1e-9 &&\n Math.abs(position.z - target.z) < 1e-9\n ? // Straight down (or up): +Y up is degenerate, so face -Z as three does.\n new Vector3(0, 0, -1)\n : new Vector3(0, 1, 0);\n // three's own convention: a camera looks down its local -Z.\n const rotation = new Matrix4().lookAt(position, target, up);\n const q = new Quaternion().setFromRotationMatrix(rotation);\n const quaternion: Quat = [q.x, q.y, q.z, q.w];\n\n return {\n type: 'perspective',\n fov: camera.fov,\n ...(output ? { aspect: output.width / output.height } : {}),\n // Clip planes scaled to the shot: a watch a hand-span away and a sofa\n // across a room both keep their subject well inside them.\n near: Math.max(0.01, distance / 100),\n far: Math.max(100, distance * 100),\n position: [position.x, position.y, position.z],\n quaternion,\n name: camera.id,\n };\n}\n\n/** One capture, one variant: a new camera and a new hidden list, nothing else. */\nexport function variantManifest(\n manifest: SceneManifest,\n camera: CameraSpec,\n hiddenObjectIds: string[],\n): SceneManifest {\n const runtime: RuntimeState = { ...manifest.runtime };\n if (hiddenObjectIds.length) runtime.hiddenObjectIds = hiddenObjectIds;\n else delete runtime.hiddenObjectIds;\n return {\n ...manifest,\n camera,\n ...(Object.keys(runtime).length ? { runtime } : {}),\n };\n}\n\n/**\n * Release everything a product holds on the GPU: geometries, materials and\n * every texture a material references. The tree is detached from the scene\n * by the caller; this is what lets the browser actually free it.\n */\nexport function disposeTree(root: Object3D): void {\n const textures = new Set<{ dispose(): void }>();\n const materials = new Set<{ dispose(): void }>();\n root.traverse((object) => {\n const mesh = object as Object3D & {\n geometry?: { dispose(): void };\n material?: unknown;\n };\n mesh.geometry?.dispose();\n const list = Array.isArray(mesh.material) ? mesh.material : [mesh.material];\n for (const material of list) {\n if (!material || typeof material !== 'object') continue;\n materials.add(material as { dispose(): void });\n for (const value of Object.values(material as Record<string, unknown>)) {\n if (\n value &&\n typeof value === 'object' &&\n (value as { isTexture?: boolean }).isTexture\n ) {\n textures.add(value as { dispose(): void });\n }\n }\n }\n });\n for (const texture of textures) texture.dispose();\n for (const material of materials) material.dispose();\n}\n\n/** Every id any finish of the product claims. */\nexport function claimedIds(\n objects: Record<string, string>,\n finishes: CatalogFinish[],\n): Set<string> {\n const ids = new Set<string>();\n for (const finish of finishes) {\n for (const name of finish.show) {\n const id = objects[name];\n if (id !== undefined) ids.add(id);\n }\n }\n return ids;\n}\n\nfunction unknownObject(\n name: string,\n finish: string,\n objects: Record<string, string>,\n): Bakery3Error {\n const known = Object.keys(objects);\n const sample = known.slice(0, 8).join(', ') + (known.length > 8 ? ', …' : '');\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Finish \"${finish}\" shows \"${name}\", but no object in the product has that name.`,\n why: 'The finish would render without the part it says it has, and the missing part would only be noticed in the catalog.',\n fix: `Match \\`show\\` to the object names in the model${known.length ? ` (it has: ${sample})` : ''}.`,\n path: name,\n docs: 'https://bakery3.com/docs/api',\n });\n}\n","/**\n * Many pictures from one call.\n *\n * // every camera of the scene you have\n * const shots = await bakery3.render({ cameras: ['hero', 'front', 'detail'] });\n * const images = await shots.result();\n *\n * // every variant, from every camera\n * const run = await bakery3.renderVariants({\n * key: 'fall-2026',\n * variants: products,\n * apply: (product) => configurator.show(product),\n * cameras: ['hero', 'front', 'detail'],\n * maxCost: 600,\n * });\n * await run.wait();\n * for await (const image of run.images()) save(image.variant, image.camera, image.url);\n *\n * Both record into a batch and hand back a `RenderSet`, so a set of three\n * and a catalog of six thousand are the same object with the same\n * guarantees: one ceiling for all of it, one poll for all of it, and a\n * re-run that adopts what already rendered.\n *\n * What this file is careful about is the customer's machine. A catalog is\n * recorded in a browser tab, and a tab that holds two hundred products, or\n * two hundred exported GLBs, or six thousand result records, is a tab that\n * dies at product 140 with nothing to show for it. So:\n *\n * - One variant resident. A variant is applied, captured, uploaded, turned\n * into items, flushed and let go of before the next is touched. The\n * exported GLB is the largest thing a run holds, and it holds one.\n * - The list need not exist. `variants` is any iterable or async iterable: a\n * generator, a database cursor, a paged fetch. It is pulled one at a time\n * and never collected.\n * - One capture, many pictures. Cameras and finishes multiply items, not\n * exports: each is the same few-KB manifest with another camera and\n * another hidden list (`variant.ts`).\n * - Nothing uploads twice. Assets are content addressed; a variant that\n * changed no geometry uploads nothing, and one whose hashes this run has\n * already sent does not even ask.\n * - Results are walked, not held. `images()` and `failures()` read the\n * ledger a page at a time by cursor. `result()` collects, and says so: it\n * is for a handful of cameras.\n */\n\nimport { PerspectiveCamera, type Camera, type Scene } from 'three';\nimport type {\n BatchItemOutcome,\n BatchItemRequest,\n CameraSpec,\n OutputSpec,\n RenderError,\n RenderResult,\n RenderSpec,\n SceneManifest,\n} from './protocol/index.js';\nimport type { Batch, BatchProgress, WaitOptions } from './batch.js';\nimport type { Capture } from './index.js';\nimport type { UploadProgress } from './upload.js';\nimport { captureCamera } from './capture/camera.js';\nimport { Bakery3Error } from './errors.js';\nimport { emptyRecorded, who, type Recorded } from './set.js';\nimport {\n cameraSpec,\n claimedIds,\n variantHiddenIds,\n variantManifest,\n type CatalogCamera,\n type CatalogFinish,\n} from './variant.js';\n\n// ---------------------------------------------------------------------------\n// What the developer types\n// ---------------------------------------------------------------------------\n\n/**\n * A camera to photograph from:\n *\n * - a **name**: a camera in the scene with that `name`. Looked up again for\n * every variant, after `apply`, so a camera that ships inside a product's\n * own GLB, or rides a rig the variant moves, is read where it then stands.\n * - a **Camera**: read as it stands at capture. Its `name` is its id.\n * - a **declaration**: `{ id, position, target, fov }`, for a shot no camera\n * object exists for. Its aspect is the output's.\n */\nexport type ViewCamera = string | Camera | CatalogCamera;\n\n/** What `apply` may hand back: how to undo it. Called before the next variant, and when a run stops. */\nexport type VariantCleanup = () => void | Promise<void>;\n\nexport type VariantProgress = BatchProgress & {\n /** The variant just recorded. */\n variant: string;\n /** Variants recorded so far, this one included. */\n variants: number;\n /** Pictures recorded so far: variants × finishes × cameras. */\n images: number;\n /** What this variant's capture uploaded. Usually nothing after the first. */\n upload?: UploadProgress;\n};\n\nexport { RenderSet, RenderSetRefused } from './set.js';\nexport type {\n Recorded,\n RenderSetFailure,\n RenderSetImage,\n RenderSetRefusal,\n VariantsQuote,\n} from './set.js';\n\nconst REFUSALS_KEPT = 50;\nconst KEY_LIMIT = 200;\nconst DOCS = 'https://bakery3.com/docs/batches';\n\n// ---------------------------------------------------------------------------\n// The recorder\n// ---------------------------------------------------------------------------\n\n/** The slice of the SDK a recording drives. `Bakery3` satisfies it; so does a fake. */\nexport type RecorderHost = {\n capture(options: { scene: Scene; camera: Camera }): Promise<Capture>;\n uploadAssets(\n capture: Pick<Capture, 'assets'>,\n onProgress?: (progress: UploadProgress) => void,\n ): Promise<UploadProgress>;\n};\n\nexport type Recording<V> = {\n host: RecorderHost;\n batch: Batch;\n scene: Scene;\n variants: Iterable<V> | AsyncIterable<V>;\n idOf: (variant: V, index: number) => string;\n apply?: (\n variant: V,\n ) => void | VariantCleanup | Promise<void | VariantCleanup>;\n finishesOf?: (variant: V) => CatalogFinish[] | undefined;\n cameras: ViewCamera[];\n output: OutputSpec;\n /** The manifest of one picture, wrapped in everything else its render needs. */\n spec: (manifest: SceneManifest) => RenderSpec;\n metadataOf: (\n variant: V,\n ids: { variant: string; finish?: string; camera: string },\n ) => Record<string, unknown>;\n /** Appended to every item key as `@version`. */\n version?: string;\n /** Prefixed to every item key, for a set whose variant ids are not unique in the project on their own. */\n prefix?: string;\n /** A ceiling for each picture, under the set's. */\n maxCostPerImage?: number;\n /** Price, admit nothing, upload nothing. */\n dryRun?: boolean;\n signal?: AbortSignal;\n onVariant?: (progress: VariantProgress) => void;\n /** Collect the item keys, in order. Only for a set small enough to want one. */\n keys?: string[];\n};\n\nexport type RecordingResult = Recorded & { estimated: number; maximum: number };\n\n/**\n * Walk the variants, one resident at a time.\n *\n * Every `await` in the loop is a point where the previous variant's capture\n * is already unreachable: it lives in a `const` inside the iteration, the\n * items built from it hold its manifest (a few KB) and never its blobs, and\n * the flush at the end of the iteration hands those to the server.\n */\nexport async function record<V>(plan: Recording<V>): Promise<RecordingResult> {\n validateCameras(plan.cameras);\n const tally: RecordingResult = {\n ...emptyRecorded(),\n estimated: 0,\n maximum: 0,\n };\n /** Asset hashes this run has already put in storage. Strings, not bytes. */\n const sent = new Set<string>();\n const seen = new Set<string>();\n let index = 0;\n\n for await (const variant of plan.variants) {\n if (plan.signal?.aborted) throw plan.signal.reason ?? abortError();\n\n const id = plan.idOf(variant, index++);\n if (seen.has(id)) throw duplicateVariant(id);\n seen.add(id);\n\n const cleanup = await plan.apply?.(variant);\n try {\n const views = resolveCameras(plan.cameras, plan.scene, plan.output);\n const capture = await plan.host.capture({\n scene: plan.scene,\n camera:\n views.find((view) => view.object)?.object ?? new PerspectiveCamera(),\n });\n\n let upload: UploadProgress | undefined;\n if (\n !plan.dryRun &&\n capture.assets.some((asset) => !sent.has(asset.hash))\n ) {\n upload = await plan.host.uploadAssets(capture);\n for (const asset of capture.assets) sent.add(asset.hash);\n }\n\n const items = itemsOf(plan, variant, id, capture, views);\n if (plan.keys) plan.keys.push(...items.map((item) => item.key));\n\n let outcomes: BatchItemOutcome[];\n if (plan.dryRun) {\n outcomes = await plan.batch.quote(items);\n } else {\n for (const item of items) plan.batch.add(item);\n // A refusal here is honest and touches nothing already admitted: the\n // page stays in the batch's buffer and the error says what to do.\n outcomes = await plan.batch.flush();\n }\n\n tally.variants++;\n tally.images += items.length;\n const metadata = new Map(items.map((item) => [item.key, item.metadata]));\n for (const outcome of outcomes) {\n if (outcome.error) {\n tally.refused++;\n if (tally.refusals.length < REFUSALS_KEPT) {\n // Named the way the set's images are: by variant, finish and\n // camera, read from the same metadata.\n const { kind: _kind, ...ids } = who(metadata.get(outcome.key));\n tally.refusals.push({\n key: outcome.key,\n ...ids,\n error: outcome.error,\n });\n }\n } else if (outcome.adopted) {\n tally.adopted++;\n } else {\n tally.estimated += outcome.estimatedCost;\n tally.maximum += outcome.maximumCost;\n }\n }\n\n plan.onVariant?.({\n ...plan.batch.progress(),\n variant: id,\n variants: tally.variants,\n images: tally.images,\n ...(upload ? { upload } : {}),\n });\n } finally {\n // Even when the capture or the flush threw: the caller's scene must not\n // be left holding a variant it did not ask to keep.\n await cleanup?.();\n }\n }\n\n // Sums of cents, kept as cents: 0.1 + 0.2 is not a number to show anybody.\n tally.estimated = Math.round(tally.estimated * 1e4) / 1e4;\n tally.maximum = Math.round(tally.maximum * 1e4) / 1e4;\n return tally;\n}\n\ntype ResolvedView = { id: string; spec: CameraSpec; object?: Camera };\n\nfunction itemsOf<V>(\n plan: Recording<V>,\n variant: V,\n id: string,\n capture: Capture,\n views: ResolvedView[],\n): BatchItemRequest[] {\n const finishes = plan.finishesOf?.(variant);\n const items: BatchItemRequest[] = [];\n\n const push = (\n finish: CatalogFinish | undefined,\n hidden: string[] | undefined,\n ) => {\n for (const view of views) {\n const parts = [plan.prefix, id, finish?.id, view.id].filter(\n (part): part is string => Boolean(part),\n );\n const key = parts.join('/') + (plan.version ? `@${plan.version}` : '');\n if (key.length > KEY_LIMIT) throw keyTooLong(key);\n const manifest = hidden\n ? variantManifest(capture.manifest, view.spec, hidden)\n : { ...capture.manifest, camera: view.spec };\n items.push({\n key,\n spec: plan.spec(manifest),\n metadata: plan.metadataOf(variant, {\n variant: id,\n ...(finish ? { finish: finish.id } : {}),\n camera: view.id,\n }),\n ...(plan.maxCostPerImage !== undefined\n ? { maxCost: plan.maxCostPerImage }\n : {}),\n });\n }\n };\n\n if (!finishes?.length) {\n push(undefined, undefined);\n return items;\n }\n\n const finishIds = new Set<string>();\n // What the scene itself hides stays hidden, unless a finish claims it:\n // a loader that starts on oak has not decided anything about walnut.\n const claimed = claimedIds(capture.objects, finishes);\n const sceneHidden = (capture.manifest.runtime?.hiddenObjectIds ?? []).filter(\n (hiddenId) => !claimed.has(hiddenId),\n );\n for (const finish of finishes) {\n if (finishIds.has(finish.id)) throw duplicateFinish(id, finish.id);\n finishIds.add(finish.id);\n push(finish, [\n ...sceneHidden,\n ...variantHiddenIds(capture.objects, finishes, finish),\n ]);\n }\n return items;\n}\n\n// ---------------------------------------------------------------------------\n// Cameras\n// ---------------------------------------------------------------------------\n\nfunction idOfCamera(camera: ViewCamera, index: number): string {\n if (typeof camera === 'string') return camera;\n if (isCameraObject(camera)) return camera.name || `camera-${index + 1}`;\n return camera.id;\n}\n\nfunction isCameraObject(camera: ViewCamera): camera is Camera {\n return typeof camera === 'object' && (camera as Camera).isCamera === true;\n}\n\nfunction validateCameras(cameras: ViewCamera[]): void {\n if (!cameras.length) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: '`cameras` is empty.',\n why: 'Every picture is taken from a camera, and the camera is part of its key.',\n fix: \"Name the cameras in your scene (`cameras: ['hero', 'front']`), pass Camera objects, or declare `{ id, position, target, fov }`. For the one live camera, leave `cameras` out.\",\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n const ids = new Set<string>();\n cameras.forEach((camera, index) => {\n const id = idOfCamera(camera, index);\n if (ids.has(id)) {\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Two cameras are called \"${id}\".`,\n why: 'The camera id ends every picture’s key, so two cameras under one id would be one picture.',\n fix: 'Give each camera its own `name` (or `id`).',\n docs: 'https://bakery3.com/docs/cameras',\n });\n }\n ids.add(id);\n });\n}\n\n/** Read every camera as it stands now: after `apply`, before the capture. */\nfunction resolveCameras(\n cameras: ViewCamera[],\n scene: Scene,\n output: OutputSpec,\n): ResolvedView[] {\n return cameras.map((camera, index) => {\n const id = idOfCamera(camera, index);\n if (typeof camera === 'string') {\n const object = findCamera(scene, camera);\n return { id, spec: captureCamera(object), object };\n }\n if (isCameraObject(camera))\n return {\n id,\n spec: { ...captureCamera(camera), name: id },\n object: camera,\n };\n return { id, spec: cameraSpec(camera, output) };\n });\n}\n\nfunction findCamera(scene: Scene, name: string): Camera {\n let found: Camera | undefined;\n const cameras: string[] = [];\n scene.traverse((object) => {\n if (!(object as Camera).isCamera) return;\n if (object.name) cameras.push(object.name);\n if (!found && object.name === name) found = object as Camera;\n });\n if (found) return found;\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `No camera named \"${name}\" in the scene.`,\n why: 'A camera given by name is looked up in the scene for every variant, so a rig the variant moves is read where it stands.',\n fix: cameras.length\n ? `The scene's cameras are: ${cameras.slice(0, 8).join(', ')}${cameras.length > 8 ? ', …' : ''}. Match one, or pass the Camera object itself.`\n : 'The scene has no named cameras. Add the camera to the scene and set its `name`, pass the Camera object, or declare `{ id, position, target, fov }`.',\n path: name,\n docs: 'https://bakery3.com/docs/cameras',\n });\n}\n\n// ---------------------------------------------------------------------------\n\n/** `variant.id`, `.sku` or `.key`, or the variant itself when it is a string. */\nexport function defaultVariantId(variant: unknown, index: number): string {\n if (typeof variant === 'string' && variant) return variant;\n if (variant && typeof variant === 'object') {\n for (const field of ['id', 'sku', 'key'] as const) {\n const value = (variant as Record<string, unknown>)[field];\n if (typeof value === 'string' && value) return value;\n if (typeof value === 'number') return String(value);\n }\n }\n throw new Bakery3Error({\n code: 'InvalidRequest',\n message: `Variant ${index + 1} has no id.`,\n why: 'The id starts every picture’s key. It is what lets a re-run adopt the pictures that already rendered, and what a failure is reported under. A position in a list is not stable enough for either.',\n fix: 'Give each variant an `id` (or `sku`), or pass `id: (variant) => …`.',\n docs: DOCS,\n });\n}\n\nfunction duplicateVariant(id: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Two variants are called \"${id}\".`,\n why: 'The second would be adopted as the first: same key, same picture, and the variant it was meant to show never rendered.',\n fix: 'Make the ids unique. When two variants share a SKU, put what differs in the id: `${sku}-${color}`.',\n path: id,\n docs: DOCS,\n });\n}\n\nfunction duplicateFinish(variant: string, finish: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `Finish \"${finish}\" of \"${variant}\" is declared twice.`,\n why: 'Finish ids are part of the item key.',\n fix: 'Make them unique per variant.',\n path: `${variant}/${finish}`,\n docs: DOCS,\n });\n}\n\nfunction keyTooLong(key: string): Bakery3Error {\n return new Bakery3Error({\n code: 'InvalidRequest',\n message: `The key \"${key.slice(0, 60)}…\" is ${key.length} characters; a key holds at most ${KEY_LIMIT}.`,\n why: 'A picture’s key is its variant, finish and camera ids joined with \"/\".',\n fix: 'Use shorter ids: a SKU rather than a product title.',\n docs: DOCS,\n });\n}\n\nfunction abortError(): Error {\n const error = new Error('The recording was aborted.');\n error.name = 'AbortError';\n return error;\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  import { Object3D, Scene, Camera } from 'three';
2
- import { a as BakeQuality, B as BakeTextureSize, e as BakeSpec, j as BakeBundle, Q as QualityPreset } from '../bake-DaOlenyx.js';
2
+ import { a as BakeQuality, B as BakeTextureSize, e as BakeSpec, j as BakeBundle, Q as QualityPreset } from '../bake-DZ-CJR6f.js';
3
3
  import { CapturedScene, Bakery3, RenderOptions, RenderJob, CaptureOptions } from '../index.js';
4
4
  import { T as Timeline } from '../timeline-ChwgD7bT.js';
5
5
  import { BakeSession } from '../bake/index.js';
6
6
  import '../animation-B1h0Ryvj.js';
7
- import '../testRun-BIPXaVPb.js';
8
- import '../prepare-C5qVvaw4.js';
7
+ import '../testRun-20OARnQr.js';
8
+ import '../prepare-BtjY4G3q.js';
9
9
 
10
10
  /**
11
11
  * The timeline panel: a floating strip, not an editor.
@@ -1,15 +1,16 @@
1
- export { mountBakery3Devtools, mountDevPanel, mountTimelinePanel } from '../chunk-54WJDN5R.js';
2
- import '../chunk-CX4WTVGH.js';
3
- import '../chunk-V3QINEEG.js';
4
- import '../chunk-APBOWWSV.js';
1
+ export { mountBakery3Devtools, mountDevPanel, mountTimelinePanel } from '../chunk-CSSJRP5W.js';
2
+ import '../chunk-WLEFNUOH.js';
3
+ import '../chunk-62M6XXNX.js';
4
+ import '../chunk-R5IUPOWI.js';
5
5
  import '../chunk-S4AJTLLN.js';
6
- import '../chunk-AF5BIELI.js';
7
- import '../chunk-TGFNQBKC.js';
8
- import '../chunk-HQTUFVMM.js';
6
+ import '../chunk-KZLVTSBI.js';
7
+ import '../chunk-3T3FJOSD.js';
8
+ import '../chunk-NEK5TBEH.js';
9
9
  import '../chunk-4E5VV4QY.js';
10
- import '../chunk-W6WQ5UP7.js';
10
+ import '../chunk-EN4ROBYQ.js';
11
11
  import '../chunk-6NYR73Y7.js';
12
- import '../chunk-MD2U6S4N.js';
12
+ import '../chunk-LAKXC4WR.js';
13
13
  import '../chunk-3G5QL4F4.js';
14
+ import '../chunk-OPM2L63B.js';
14
15
  //# sourceMappingURL=index.js.map
15
16
  //# sourceMappingURL=index.js.map
@@ -1,32 +1,117 @@
1
- import { Group, Camera, Vector3, Object3D } from 'three';
1
+ import { Group, Vector3, Camera, Material, Color, Object3D, Intersection } from 'three';
2
+ import { T as Timeline } from '../timeline-ChwgD7bT.js';
3
+ import '../animation-B1h0Ryvj.js';
2
4
 
3
5
  /**
4
6
  * The implementation behind `@oeave/bakery3/hotspots`. `index.ts` lists what
5
- * is public; the orb's parts and the brand color are here for the SDK's own
6
- * pointer handling and the monorepo's tools (`internal.ts`).
7
+ * is public; the parts, the pose and the brand color are here for the SDK's
8
+ * own pointer handling and the monorepo's tools (`internal.ts`).
9
+ *
10
+ * One function decides how a hotspot looks: `poseOf` turns its state (how far
11
+ * it has appeared, how far it is open, the hover, the label, the pulse) into
12
+ * numbers on its parts. The live hotspot applies them every frame;
13
+ * `animateHotspots` samples them at every frame of a timeline and writes them
14
+ * as tracks, which is how the render plays the same motion the viewport
15
+ * showed. There is no second animation system to drift from the first.
7
16
  */
8
17
 
9
18
  /** `[x, y, z]`, in meters: the protocol's own vector shape. */
10
19
  type Vec3 = [number, number, number];
20
+ /**
21
+ * Where a hotspot is headed. `hidden` is not there at all, `closed` is the
22
+ * resting dot, `open` is the dot as it looks while its panel is showing.
23
+ */
24
+ type HotspotState = 'hidden' | 'closed' | 'open';
25
+ /** Where a hotspot is now, including the moves between states. */
26
+ type HotspotPhase = 'hidden' | 'entering' | 'closed' | 'opening' | 'open' | 'closing' | 'leaving';
27
+ /**
28
+ * The built-in looks.
29
+ *
30
+ * - `orb`: a glowing core in a soft halo that breathes.
31
+ * - `ring`: a small glowing core inside a ring.
32
+ * - `glass`: a clear glass bead with a glowing core inside.
33
+ * - `solid`: a glossy colored bead that barely glows.
34
+ */
35
+ type HotspotPreset = 'orb' | 'ring' | 'glass' | 'solid';
36
+ /** The meshes a hotspot can be made of; which exist depends on the preset. */
37
+ type HotspotPartName = 'core' | 'halo' | 'ring' | 'shell' | 'tether' | 'dot';
38
+ /**
39
+ * When the label beside a hotspot shows.
40
+ *
41
+ * - `never` (default): the hotspot draws no text.
42
+ * - `always`: whenever the hotspot is there.
43
+ * - `open`: while it is open.
44
+ * - `hover`: while the pointer is over it, and while it is open. A timeline
45
+ * hovers it with a `hover` event.
46
+ */
47
+ type HotspotLabelShow = 'never' | 'always' | 'open' | 'hover';
48
+ /** Which side of the orb the label is on. `auto` moves it off a screen edge. */
49
+ type HotspotLabelSide = 'auto' | 'left' | 'right';
50
+ /**
51
+ * A material of your own for a part, or a function asked once per part that
52
+ * returns one (or `undefined` to keep the preset's). A node material with an
53
+ * `emissiveNode` should multiply it by `bakeryEmissiveIntensity()` from
54
+ * `@oeave/bakery3/tsl`, so the glow the state animates reaches it in the
55
+ * viewport as it does in the render.
56
+ */
57
+ type HotspotMaterial = Material | ((part: {
58
+ name: HotspotPartName;
59
+ color: Color;
60
+ }) => Material | undefined);
11
61
  type HotspotOptions = {
12
62
  /** Stable identity: it names the group and travels with `hotspotSpecOf`. */
13
63
  id: string;
14
- /** A caption for the app to show; the orb itself draws no text. */
64
+ /** The hotspot's caption: the text of its label, and yours to show in your app. */
15
65
  label?: string;
16
- /** Where the orb is, in the parent's space (world space without a parent). */
17
- position: Vec3 | Vector3;
18
66
  /**
19
- * A surface point on the model, in the same space as `position`. When given
20
- * the hotspot is anchored: a thin line is drawn from here to the orb.
67
+ * Where the orb is, in the parent's space (world space without a parent).
68
+ * With an `anchor`, you can give `direction` and `distance` instead.
69
+ */
70
+ position?: Vec3 | Vector3;
71
+ /**
72
+ * A point on the model's surface, in the same space as `position`. When
73
+ * given, the hotspot is tied to it: a thin line runs from a small dot on
74
+ * the surface out to the orb.
21
75
  */
22
76
  anchor?: Vec3 | Vector3;
77
+ /**
78
+ * Which way the orb stands off the anchor, in the parent's space. Default:
79
+ * toward `position`, or straight up.
80
+ */
81
+ direction?: Vec3 | Vector3;
82
+ /** How far the orb stands off the anchor, in meters. Default: to `position`, or 0.1. */
83
+ distance?: number;
23
84
  /**
24
85
  * Attach to this object so the hotspot follows it. Without one the caller
25
86
  * adds `hotspot.object` to the scene themselves.
26
87
  */
27
88
  parent?: Object3D;
28
- /** CSS color of the orb, halo and line. Default the brand cyan, `#33d1ff`. */
89
+ /**
90
+ * Whether the hotspot shows up in renders. Default false: a hotspot is
91
+ * viewport UI and the capture walks past it. With true it is part of the
92
+ * scene, its glow lights the model in the render, and `animateHotspots`
93
+ * can play its states in a video.
94
+ */
95
+ render?: boolean;
96
+ /** The look. Default `orb`. */
97
+ style?: HotspotPreset;
98
+ /** Any CSS color. Default the brand cyan, `#33d1ff`. */
29
99
  color?: string;
100
+ /** The color it turns while open. Default: it keeps `color`. */
101
+ openColor?: string;
102
+ /**
103
+ * How bright the glowing parts are (emission strength). Default 3 for
104
+ * `orb` and `ring`, 6 for `glass`, 0.2 for `solid`; 1 for a hotspot that
105
+ * does not render, so the color on screen is exactly `color`.
106
+ */
107
+ intensity?: number;
108
+ /**
109
+ * How much the hotspot lights the model around it while open: a point light
110
+ * at the orb, in candela. A closed hotspot gives a third of it. Default 0.
111
+ */
112
+ glow?: number;
113
+ /** Your own material for some or all parts, a TSL node material included. */
114
+ material?: HotspotMaterial;
30
115
  /**
31
116
  * How the orb is sized. `screen` (default) keeps it the same size on screen
32
117
  * wherever it is: `size` is then a fraction of the viewport height, and
@@ -34,33 +119,86 @@ type HotspotOptions = {
34
119
  * `world` is a fixed radius in meters, which shrinks with distance.
35
120
  */
36
121
  sizing?: 'screen' | 'world';
37
- /** Orb radius: a fraction of viewport height (`screen`, default 0.012) or meters (`world`, default 0.03). */
122
+ /** Orb radius: a fraction of viewport height (`screen`, default 0.015) or meters (`world`, default 0.03). */
38
123
  size?: number;
39
- /** Whether the halo breathes. Default true. */
40
- pulse?: boolean;
124
+ /** Whether the closed hotspot breathes, or how fast, in cycles a second. Default true (0.7). */
125
+ pulse?: boolean | number;
126
+ /** Draw the line to the anchor. Default true when anchored. */
127
+ tether?: boolean;
128
+ /** Draw the small dot on the anchor. Default true when anchored. */
129
+ dot?: boolean;
130
+ /** When the label beside the orb shows. Default `never`. */
131
+ showLabel?: HotspotLabelShow;
132
+ /** Which side the label is on. Default `auto`: right, or left near the right edge. */
133
+ labelSide?: HotspotLabelSide;
134
+ /**
135
+ * The label's CSS font. Its pixel size is how sharp the label is, not how
136
+ * big: the label is sized to the orb. Default the platform's UI face at 40px.
137
+ */
138
+ labelFont?: string;
139
+ /** The label's text color. Default white. */
140
+ labelColor?: string;
141
+ /** The label's background color. Default near black. */
142
+ labelBackground?: string;
143
+ /** How opaque the shown label is. Default 0.9. */
144
+ labelOpacity?: number;
145
+ /** The label's height, as a multiple of its default. Default 1. */
146
+ labelSize?: number;
147
+ /** The state it starts in, already there. Default `closed`. */
148
+ state?: HotspotState;
149
+ /** Seconds each move takes. Default 0.8 to appear, 0.45 to open, 0.3 for the label. */
150
+ timing?: {
151
+ appear?: number;
152
+ open?: number;
153
+ label?: number;
154
+ };
41
155
  };
42
156
  type Hotspot = {
43
157
  readonly id: string;
44
158
  readonly label: string | undefined;
45
159
  /**
46
- * The group holding everything (the orb, and the tether when anchored),
47
- * placed at the orb; excluded from capture, named `bakery3-hotspot-<id>`.
160
+ * The group holding everything, placed at the anchor (at the orb when
161
+ * there is none), named `bakery3-hotspot-<id>`.
48
162
  */
49
163
  readonly object: Group;
50
- /** Whether `setSelected(true)` is in effect. */
164
+ /** The state it is headed for. */
165
+ readonly state: HotspotState;
166
+ /** Where it is now, moves included. */
167
+ readonly phase: HotspotPhase;
168
+ /** Whether it is open, or opening. */
51
169
  readonly selected: boolean;
52
- /** Whether the cursor is over it; set by `attachHotspots`. */
170
+ /** Whether it is hovered, by the pointer or by `setHovered()`. */
53
171
  readonly hovered: boolean;
172
+ /** Meters from the anchor to the orb. */
173
+ readonly distance: number;
174
+ /** The side the label is on, or headed for. */
175
+ readonly labelSide: 'left' | 'right';
176
+ /** True while `animateHotspots` drives it: `update()` then leaves it to the timeline. */
177
+ readonly driven: boolean;
178
+ /** Move to a state, animated unless `instant`. */
179
+ setState(state: HotspotState, options?: {
180
+ instant?: boolean;
181
+ }): void;
182
+ /** `setState('open')` or `setState('closed')`. */
54
183
  setSelected(on: boolean): void;
55
- /** Advance the pulse; `seconds` is any monotonic clock. */
184
+ /** Hover it, or stop: what `attachHotspots` does as the pointer moves. */
185
+ setHovered(on: boolean): void;
186
+ /** Change the label's text. */
187
+ setLabel(text: string): void;
188
+ /** Stand the orb this far off the anchor, in meters. */
189
+ setDistance(meters: number): void;
190
+ /** Point the orb this way from the anchor (parent space). */
191
+ setDirection(direction: Vec3 | Vector3): void;
192
+ /** Advance the moves and the pulse; `seconds` is any monotonic clock. */
56
193
  update(seconds: number): void;
57
194
  /**
58
- * Scale the orb so it projects to its `size` for this camera. Called by
59
- * `attachHotspots.update()`; a host without pointer handling calls it
60
- * itself once per frame. No-op for `sizing: 'world'`.
195
+ * Turn the orb and its label to face this camera, put the label on the
196
+ * side with room, and, for a screen-sized orb, scale it to its `size`.
197
+ * Called by `attachHotspots.update()`; a host without pointer handling
198
+ * calls it itself once per frame, before `update()`.
61
199
  */
62
200
  fit(camera: Camera): void;
63
- /** Detach from its parent and free every geometry and material. */
201
+ /** Detach from its parent and free every geometry, material and texture. */
64
202
  dispose(): void;
65
203
  };
66
204
  /** A hotspot as data, in world space: what to keep with a capture. */
@@ -71,29 +209,97 @@ type HotspotSpec = {
71
209
  anchor?: Vec3;
72
210
  };
73
211
  declare function createHotspot(options: HotspotOptions): Hotspot;
212
+ /**
213
+ * Where to put a hotspot for a click on a model: the surface point, and the
214
+ * way out of the surface there.
215
+ *
216
+ * `along: 'normal'` (default) stands the orb off the face that was hit;
217
+ * `'center'` stands it away from `center` (the model's middle) through the
218
+ * point, which keeps orbs on a curved or noisy surface from pointing every
219
+ * which way. Both are returned in `space`'s coordinates (world without one),
220
+ * ready to pass as `anchor` and `direction` with `parent: space`.
221
+ */
222
+ declare function hotspotFromHit(hit: Intersection, options?: {
223
+ along?: 'normal' | 'center';
224
+ center?: Vector3;
225
+ space?: Object3D;
226
+ }): {
227
+ anchor: Vector3;
228
+ direction: Vector3;
229
+ };
74
230
  /**
75
231
  * A hotspot as plain data, in world space, ready for `productState`.
76
232
  *
77
233
  * Reads through the parents so a hotspot riding on a product that has been
78
- * moved reports where it actually is, not where it was authored.
234
+ * moved reports where it actually is, not where it was authored. The
235
+ * position is where the orb stands once it is out, whatever it is doing.
79
236
  */
80
237
  declare function hotspotSpecOf(hotspot: Hotspot): HotspotSpec;
238
+ /** One change, at a time on the timeline: a state, a hover, or both. */
239
+ type HotspotEvent = {
240
+ /** The hotspot, or its id. */
241
+ hotspot: Hotspot | string;
242
+ /** Seconds from the start of the timeline. */
243
+ at: number;
244
+ state?: HotspotState;
245
+ /**
246
+ * The pointer moves onto it (true) or off (false): the hover look, and the
247
+ * label of a hotspot that shows it on hover.
248
+ */
249
+ hover?: boolean;
250
+ };
251
+ type AnimateHotspotsOptions = {
252
+ /** The changes, in any order. */
253
+ events?: HotspotEvent[];
254
+ /**
255
+ * Where each hotspot is at time zero, already there: one state for all, or
256
+ * one per id. Default `hidden`, so a clip opens on the model alone.
257
+ */
258
+ initial?: HotspotState | Record<string, HotspotState>;
259
+ /**
260
+ * Round each pulse to whole breaths over the timeline, so a clip that loops
261
+ * does not hitch at the seam. Default true.
262
+ */
263
+ loop?: boolean;
264
+ };
265
+ type AnimatedHotspots = {
266
+ /** How many tracks were written. */
267
+ readonly tracks: number;
268
+ /** Remove the tracks and hand the hotspots back to their own `update()`. */
269
+ release(): void;
270
+ };
271
+ /**
272
+ * Play hotspot states on a timeline, so a video renders them.
273
+ *
274
+ * Every frame of the timeline is posed by the same function the live hotspot
275
+ * uses, and written as ordinary tracks (position, rotation, scale, emission,
276
+ * opacity, the glow light's intensity), so `seek()` previews exactly what the
277
+ * render traces. The camera at each frame decides what a live camera would:
278
+ * the size of a screen-sized orb, which way the rig faces, and which side the
279
+ * label is on. Author the camera first. Calling it again on the same timeline
280
+ * replaces what it wrote. Hotspots must be made with `render: true`.
281
+ *
282
+ * While animated, a hotspot's `update()` and `fit()` stand aside.
283
+ */
284
+ declare function animateHotspots(timeline: Timeline, hotspots: Hotspot[], options?: AnimateHotspotsOptions): AnimatedHotspots;
285
+ /** Undo `animateHotspots` on a timeline: its tracks go, the hotspots are live again. */
286
+ declare function releaseHotspots(timeline: Timeline): void;
81
287
  type AttachHotspotsOptions = {
82
288
  hotspots: Hotspot[];
83
289
  camera: Camera;
84
290
  /** The renderer's canvas, or whatever element receives the pointer. */
85
291
  domElement: HTMLElement;
86
- /** Selection changed: the hotspot now selected, or null when cleared. */
292
+ /** Selection changed: the hotspot now open, or null when cleared. */
87
293
  onSelect?(hotspot: Hotspot | null): void;
88
294
  /** The pointer moved onto (or off) a hotspot. */
89
295
  onHover?(hotspot: Hotspot | null): void;
90
296
  };
91
297
  type AttachedHotspots = {
92
- /** Advance every hotspot's pulse. Call once per frame. */
298
+ /** Fit and advance every hotspot. Call once per frame. */
93
299
  update(seconds: number): void;
94
- /** The hotspot currently selected, if any. */
300
+ /** The hotspot currently open, if any. */
95
301
  readonly selected: Hotspot | null;
96
- /** Select (or clear) programmatically; fires `onSelect` like a click would. */
302
+ /** Open one (or close all) programmatically; fires `onSelect` like a click would. */
97
303
  select(hotspot: Hotspot | null): void;
98
304
  /** Remove the listeners. Does not dispose the hotspots. */
99
305
  dispose(): void;
@@ -101,11 +307,11 @@ type AttachedHotspots = {
101
307
  /**
102
308
  * Wire hover and click for a set of hotspots on one canvas.
103
309
  *
104
- * Raycasts the orbs (core and halo, so the target is a little generous) from
105
- * the pointer through `camera`; sets `cursor: pointer` over one; toggles
106
- * selection on click, one at a time. Nothing here is a render loop: call
107
- * `update()` from yours.
310
+ * Raycasts the heads (core and halo, ring or glass, and the label, so the
311
+ * target is a little generous) from the pointer through `camera`; sets
312
+ * `cursor: pointer` over one; opens it on click and closes the one that was
313
+ * open. Nothing here is a render loop: call `update()` from yours.
108
314
  */
109
315
  declare function attachHotspots(options: AttachHotspotsOptions): AttachedHotspots;
110
316
 
111
- export { type AttachHotspotsOptions, type AttachedHotspots, type Hotspot, type HotspotOptions, type HotspotSpec, type Vec3, attachHotspots, createHotspot, hotspotSpecOf };
317
+ export { type AnimateHotspotsOptions, type AnimatedHotspots, type AttachHotspotsOptions, type AttachedHotspots, type Hotspot, type HotspotEvent, type HotspotLabelShow, type HotspotLabelSide, type HotspotMaterial, type HotspotOptions, type HotspotPartName, type HotspotPhase, type HotspotPreset, type HotspotSpec, type HotspotState, type Vec3, animateHotspots, attachHotspots, createHotspot, hotspotFromHit, hotspotSpecOf, releaseHotspots };