@snaptrude/plugin-core 1.1.0-pbr-20261102.2 → 1.1.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/CHANGELOG.md +19 -0
- package/dist/api/core/camera/index.d.ts +16 -6
- package/dist/api/core/camera/index.d.ts.map +1 -1
- package/dist/api/core/comment/index.d.ts +7 -3
- package/dist/api/core/comment/index.d.ts.map +1 -1
- package/dist/api/core/geom/create/index.d.ts +69 -21
- package/dist/api/core/geom/create/index.d.ts.map +1 -1
- package/dist/api/core/geom/query/circle.d.ts +31 -11
- package/dist/api/core/geom/query/circle.d.ts.map +1 -1
- package/dist/api/core/geom/query/contour.d.ts +19 -7
- package/dist/api/core/geom/query/contour.d.ts.map +1 -1
- package/dist/api/core/geom/query/curve.d.ts +72 -24
- package/dist/api/core/geom/query/curve.d.ts.map +1 -1
- package/dist/api/core/geom/query/profile.d.ts +19 -7
- package/dist/api/core/geom/query/profile.d.ts.map +1 -1
- package/dist/api/core/geom/update/contour.d.ts +36 -12
- package/dist/api/core/geom/update/contour.d.ts.map +1 -1
- package/dist/api/core/geom/update/curve.d.ts +13 -5
- package/dist/api/core/geom/update/curve.d.ts.map +1 -1
- package/dist/api/core/geom/update/profile.d.ts +36 -12
- package/dist/api/core/geom/update/profile.d.ts.map +1 -1
- package/dist/api/core/io/import/index.d.ts +7 -3
- package/dist/api/core/io/import/index.d.ts.map +1 -1
- package/dist/api/core/math/quat.d.ts +2 -2
- package/dist/api/core/math/quat.d.ts.map +1 -1
- package/dist/api/core/math/vec3.d.ts +15 -15
- package/dist/api/core/math/vec3.d.ts.map +1 -1
- package/dist/api/core/storeys/index.d.ts +5 -0
- package/dist/api/core/storeys/index.d.ts.map +1 -1
- package/dist/api/design/boolean/index.d.ts +20 -4
- package/dist/api/design/boolean/index.d.ts.map +1 -1
- package/dist/api/design/create/bulk-items.d.ts +84 -16
- package/dist/api/design/create/bulk-items.d.ts.map +1 -1
- package/dist/api/design/create/index.d.ts +146 -59
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/create/opening-fields.d.ts +18 -6
- package/dist/api/design/create/opening-fields.d.ts.map +1 -1
- package/dist/api/design/delete/index.d.ts +14 -7
- package/dist/api/design/delete/index.d.ts.map +1 -1
- package/dist/api/design/dimensions.d.ts +12 -4
- package/dist/api/design/dimensions.d.ts.map +1 -1
- package/dist/api/design/doors/index.d.ts +25 -0
- package/dist/api/design/doors/index.d.ts.map +1 -1
- package/dist/api/design/edit/index.d.ts +7 -3
- package/dist/api/design/edit/index.d.ts.map +1 -1
- package/dist/api/design/family.d.ts +5 -1
- package/dist/api/design/family.d.ts.map +1 -1
- package/dist/api/design/index.d.ts +1 -0
- package/dist/api/design/index.d.ts.map +1 -1
- package/dist/api/design/lock.d.ts +3 -0
- package/dist/api/design/lock.d.ts.map +1 -1
- package/dist/api/design/opening-placement.d.ts +135 -0
- package/dist/api/design/opening-placement.d.ts.map +1 -0
- package/dist/api/design/transform/index.d.ts +28 -8
- package/dist/api/design/transform/index.d.ts.map +1 -1
- package/dist/api/design/windows/index.d.ts +23 -0
- package/dist/api/design/windows/index.d.ts.map +1 -1
- package/dist/api/entity/space.d.ts +30 -6
- package/dist/api/entity/space.d.ts.map +1 -1
- package/dist/api/entity/story.d.ts +4 -0
- package/dist/api/entity/story.d.ts.map +1 -1
- package/dist/api/presentation/drawings.d.ts +1 -1
- package/dist/api/presentation/lighting.d.ts +28 -5
- package/dist/api/presentation/lighting.d.ts.map +1 -1
- package/dist/api/presentation/views.d.ts +15 -3
- package/dist/api/presentation/views.d.ts.map +1 -1
- package/dist/handles.d.ts +8 -0
- package/dist/handles.d.ts.map +1 -1
- package/dist/index.cjs +2526 -2435
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2521 -2435
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/core/camera/index.ts +9 -7
- package/src/api/core/comment/index.ts +3 -3
- package/src/api/core/geom/create/index.ts +25 -25
- package/src/api/core/geom/query/circle.ts +11 -11
- package/src/api/core/geom/query/contour.ts +7 -6
- package/src/api/core/geom/query/curve.ts +25 -25
- package/src/api/core/geom/query/profile.ts +7 -6
- package/src/api/core/geom/update/contour.ts +12 -11
- package/src/api/core/geom/update/curve.ts +5 -5
- package/src/api/core/geom/update/profile.ts +12 -11
- package/src/api/core/io/import/index.ts +3 -2
- package/src/api/core/math/quat.ts +2 -2
- package/src/api/core/math/vec3.ts +16 -16
- package/src/api/core/storeys/index.ts +5 -0
- package/src/api/design/boolean/index.ts +2 -2
- package/src/api/design/create/bulk-items.ts +7 -2
- package/src/api/design/create/index.ts +46 -46
- package/src/api/design/create/opening-fields.ts +7 -7
- package/src/api/design/delete/index.ts +14 -7
- package/src/api/design/dimensions.ts +5 -4
- package/src/api/design/doors/index.ts +31 -0
- package/src/api/design/edit/index.ts +3 -3
- package/src/api/design/family.ts +2 -1
- package/src/api/design/index.ts +1 -0
- package/src/api/design/lock.ts +6 -0
- package/src/api/design/opening-placement.ts +79 -0
- package/src/api/design/transform/index.ts +15 -7
- package/src/api/design/windows/index.ts +29 -0
- package/src/api/entity/space.ts +5 -4
- package/src/api/entity/story.ts +4 -0
- package/src/api/presentation/lighting.ts +29 -5
- package/src/api/presentation/views.ts +17 -4
- package/src/handles.ts +11 -0
- package/test/handles.test.mjs +36 -0
- package/api-manifest.json +0 -10774
|
@@ -4,6 +4,10 @@ import { ComponentHandle, MaterialHandle } from "../../../handles"
|
|
|
4
4
|
import { PluginDesignChangeResult } from "../lock"
|
|
5
5
|
import { PluginObjectCatalogGroup, PluginObjectCatalogItem } from "../doors"
|
|
6
6
|
import { PluginMaterialSpec } from "../materials"
|
|
7
|
+
import type {
|
|
8
|
+
PluginOpeningPlacementArgs,
|
|
9
|
+
PluginOpeningPlacementResult,
|
|
10
|
+
} from "../opening-placement"
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* `snaptrude.design.windows` — window-specific reads. Creation lives at
|
|
@@ -50,6 +54,31 @@ export abstract class PluginDesignWindowsApi {
|
|
|
50
54
|
material: MaterialHandle | PluginMaterialSpec,
|
|
51
55
|
): PluginApiReturn<PluginDesignChangeResult>
|
|
52
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Trial the native host fit and geometry rebuild, then restore the model without publishing commands.
|
|
59
|
+
* The first result uses the creation options; subsequent results use candidateWidths in input order.
|
|
60
|
+
* Widths are authored parameters, not physical envelopes or a guaranteed maximum width.
|
|
61
|
+
* No catalog save, history entry or model persistence occurs. Rerun after model edits.
|
|
62
|
+
* A failed fit leaves cut unverified. Family-host cuts are also unverified: their rebuild
|
|
63
|
+
* cannot supply a reversible, authoritative trial result. Material clearance checks evaluated
|
|
64
|
+
* parts against physical neighbours across hosts. The intended host uses native fit/cut checks.
|
|
65
|
+
* Creation repeats clearance immediately before placement; this report grants no later approval.
|
|
66
|
+
* Editing permission is required; locked hosts and an active opening tool reject.
|
|
67
|
+
* A requested width/height on a family sized by its Type (Custom Sizes off) picks the Type
|
|
68
|
+
* within 25 mm, else turns Custom Sizes on; each result's `sizing` says which. Creation
|
|
69
|
+
* (`design.create.window(s)`) applies the same rule, so the size you pass is the size built.
|
|
70
|
+
* @param options Window creation options and up to 12 alternative widths; numeric lengths use SU.
|
|
71
|
+
* @returns Separate fit/cut/clearance evidence, evaluated bounds and explicit limitations.
|
|
72
|
+
* @examplePrompt Check whether this window fits and its host can be cut
|
|
73
|
+
* @example
|
|
74
|
+
* const result = await snaptrude.design.windows.checkPlacement({
|
|
75
|
+
* familyId, version, hostWall, position, width: 4, sillHeight: 3,
|
|
76
|
+
* })
|
|
77
|
+
*/
|
|
78
|
+
public abstract checkPlacement(
|
|
79
|
+
options: PluginOpeningPlacementArgs,
|
|
80
|
+
): PluginApiReturn<PluginOpeningPlacementResult>
|
|
81
|
+
|
|
53
82
|
/**
|
|
54
83
|
* Get the type name of a window — the label that identifies which kind of
|
|
55
84
|
* window the instance is, as shown in its properties.
|
package/src/api/entity/space.ts
CHANGED
|
@@ -6,6 +6,7 @@ import * as z from "zod"
|
|
|
6
6
|
import {
|
|
7
7
|
ProfileHandle,
|
|
8
8
|
Vec3Handle,
|
|
9
|
+
Vec3Input,
|
|
9
10
|
Vec3Components,
|
|
10
11
|
QuatComponents,
|
|
11
12
|
CurveHandle,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
* | `dimensions` | `{ width, height, depth }` | Box dimensions in Babylon units |
|
|
22
23
|
*/
|
|
23
24
|
export const PluginSpaceCreateRectangularArgs = z.object({
|
|
24
|
-
position:
|
|
25
|
+
position: Vec3Input,
|
|
25
26
|
dimensions: z.object({
|
|
26
27
|
width: z.number(),
|
|
27
28
|
height: z.number(),
|
|
@@ -62,7 +63,7 @@ export const PluginSpaceCreateFromProfileArgs = z.object({
|
|
|
62
63
|
profile: ProfileHandle,
|
|
63
64
|
innerProfiles: z.array(ProfileHandle).optional(),
|
|
64
65
|
extrudeHeight: z.number(),
|
|
65
|
-
position:
|
|
66
|
+
position: Vec3Input,
|
|
66
67
|
})
|
|
67
68
|
|
|
68
69
|
export type PluginSpaceCreateFromProfileArgs = z.infer<
|
|
@@ -466,7 +467,7 @@ export type PluginSpaceUpdateResult = z.infer<typeof PluginSpaceUpdateResult>
|
|
|
466
467
|
export const PluginSpaceBulkCreateItem = z.discriminatedUnion("kind", [
|
|
467
468
|
z.object({
|
|
468
469
|
kind: z.literal("rectangular"),
|
|
469
|
-
position:
|
|
470
|
+
position: Vec3Input,
|
|
470
471
|
dimensions: z.object({
|
|
471
472
|
width: z.number(),
|
|
472
473
|
height: z.number(),
|
|
@@ -482,7 +483,7 @@ export const PluginSpaceBulkCreateItem = z.discriminatedUnion("kind", [
|
|
|
482
483
|
profile: ProfileHandle,
|
|
483
484
|
innerProfiles: z.array(ProfileHandle).optional(),
|
|
484
485
|
extrudeHeight: z.number(),
|
|
485
|
-
position:
|
|
486
|
+
position: Vec3Input,
|
|
486
487
|
room_type: z.string().optional(),
|
|
487
488
|
spaceType: PluginSpaceType.optional(),
|
|
488
489
|
massType: PluginMassType.optional(),
|
package/src/api/entity/story.ts
CHANGED
|
@@ -88,6 +88,10 @@ export abstract class PluginStoryApi {
|
|
|
88
88
|
* @returns A {@linkcode PluginStoryCreateResult} with `storyId` and `storyValue`
|
|
89
89
|
* @throws If a story with the given value already exists or creation fails
|
|
90
90
|
*
|
|
91
|
+
* Storeys are added at the ends of the stack only: a value above the top also creates
|
|
92
|
+
* every storey in between (returning only the requested one), and a value inside a gap
|
|
93
|
+
* is refused.
|
|
94
|
+
*
|
|
91
95
|
* @examplePrompt Add a third floor to the building
|
|
92
96
|
* @examplePrompt Create a new storey above the second floor
|
|
93
97
|
* @examplePrompt Add a basement level to this project
|
|
@@ -22,7 +22,8 @@ import { BBoxComponents, Vec3Components, Vec3Handle } from "../../handles"
|
|
|
22
22
|
*
|
|
23
23
|
* Realistic rendering is rolling out behind a feature flag and can be suspended
|
|
24
24
|
* in a browser whose GPU it lost. `get().realisticAvailable` says whether
|
|
25
|
-
* {@linkcode PluginPresentationLightingApi.enableRealistic} can succeed
|
|
25
|
+
* {@linkcode PluginPresentationLightingApi.enableRealistic} can succeed, and
|
|
26
|
+
* `get().realisticUnavailableReason` says why not. The sky,
|
|
26
27
|
* the cloud shapes and the PBR light are seen only while realistic rendering
|
|
27
28
|
* draws: in 3D, with it on.
|
|
28
29
|
*
|
|
@@ -62,6 +63,7 @@ export abstract class PluginPresentationLightingApi {
|
|
|
62
63
|
*
|
|
63
64
|
* @examplePrompt Is realistic rendering on?
|
|
64
65
|
* @examplePrompt Can I turn on the realistic view?
|
|
66
|
+
* @examplePrompt Why can't realistic mode be turned on?
|
|
65
67
|
* @examplePrompt Which lighting preset is active?
|
|
66
68
|
* @examplePrompt Are clouds showing in the sky?
|
|
67
69
|
*
|
|
@@ -176,10 +178,13 @@ export abstract class PluginPresentationLightingApi {
|
|
|
176
178
|
*
|
|
177
179
|
* # Example
|
|
178
180
|
* ```ts
|
|
179
|
-
* const {
|
|
180
|
-
*
|
|
181
|
+
* const { realisticUnavailableReason } =
|
|
182
|
+
* await snaptrude.presentation.lighting.get()
|
|
183
|
+
* if (realisticUnavailableReason === null) {
|
|
181
184
|
* await snaptrude.presentation.lighting.enableRealistic()
|
|
182
185
|
* await snaptrude.presentation.lighting.applyPreset("golden")
|
|
186
|
+
* } else if (realisticUnavailableReason === "REALISTIC_SUSPENDED") {
|
|
187
|
+
* // Ask the user to turn Realistic on in View settings; do not retry.
|
|
183
188
|
* }
|
|
184
189
|
* ```
|
|
185
190
|
*/
|
|
@@ -392,14 +397,31 @@ export type PluginPresentationLightingApplyPresetOptions = z.infer<
|
|
|
392
397
|
typeof PluginPresentationLightingApplyPresetOptions
|
|
393
398
|
>
|
|
394
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Why realistic rendering cannot be turned on: the `details.reason` of the
|
|
402
|
+
* `PRECONDITION_FAILED` {@linkcode PluginPresentationLightingApi.enableRealistic}
|
|
403
|
+
* throws. `"REALISTIC_ROLLOUT_OFF"`: not enabled for this user, or the rollout
|
|
404
|
+
* has not been evaluated yet (wait or keep the Design look).
|
|
405
|
+
* `"REALISTIC_SUSPENDED"`: suspended in this browser after the GPU was lost or
|
|
406
|
+
* ran over its frame budget; only the user can lift it, from View settings.
|
|
407
|
+
*/
|
|
408
|
+
export const PluginPresentationLightingRealisticUnavailableReason = z.enum([
|
|
409
|
+
"REALISTIC_ROLLOUT_OFF",
|
|
410
|
+
"REALISTIC_SUSPENDED"
|
|
411
|
+
])
|
|
412
|
+
export type PluginPresentationLightingRealisticUnavailableReason = z.infer<
|
|
413
|
+
typeof PluginPresentationLightingRealisticUnavailableReason
|
|
414
|
+
>
|
|
415
|
+
|
|
395
416
|
/**
|
|
396
417
|
* The lighting, returned by every {@linkcode PluginPresentationLightingApi}
|
|
397
418
|
* method but `listPresets`.
|
|
398
419
|
*
|
|
399
420
|
* | Property | Type | Description |
|
|
400
421
|
* |---|---|---|
|
|
401
|
-
* | `realistic` | `boolean` |
|
|
402
|
-
* | `realisticAvailable` | `boolean` | `enableRealistic` can succeed:
|
|
422
|
+
* | `realistic` | `boolean` | Realistic rendering is on (drawn in 3D only); `false` while it is suspended in this browser |
|
|
423
|
+
* | `realisticAvailable` | `boolean` | `enableRealistic` can succeed: `realisticUnavailableReason` is `null` |
|
|
424
|
+
* | `realisticUnavailableReason` | {@linkcode PluginPresentationLightingRealisticUnavailableReason}` \| null` | Why `enableRealistic` would throw: the `details.reason` it throws with; `null` when it can succeed |
|
|
403
425
|
* | `preset` | {@linkcode PluginPresentationLightingPresetId} | The active preset |
|
|
404
426
|
* | `sunSource` | `"preset" \| "analysis"` | Who aims the sun |
|
|
405
427
|
* | `sunDirection` | `{ x, y, z }` | From the sun toward the scene, world space (+Y up) |
|
|
@@ -412,6 +434,8 @@ export type PluginPresentationLightingApplyPresetOptions = z.infer<
|
|
|
412
434
|
export const PluginPresentationLightingState = z.object({
|
|
413
435
|
realistic: z.boolean(),
|
|
414
436
|
realisticAvailable: z.boolean(),
|
|
437
|
+
realisticUnavailableReason:
|
|
438
|
+
PluginPresentationLightingRealisticUnavailableReason.nullable(),
|
|
415
439
|
preset: PluginPresentationLightingPresetId,
|
|
416
440
|
sunSource: z.enum(["preset", "analysis"]),
|
|
417
441
|
sunDirection: Vec3Components,
|
|
@@ -90,17 +90,23 @@ export abstract class PluginPresentationViewsApi {
|
|
|
90
90
|
* @param options - All optional: `viewId` (capture this saved view; omit for the
|
|
91
91
|
* current viewport), `format` (default `"png"`), `resolution` (e.g. `"1280x720"`).
|
|
92
92
|
* @returns A {@linkcode PluginPresentationViewsCaptureResult} with the base64
|
|
93
|
-
* `image` data URL
|
|
93
|
+
* `image` data URL, and `realisticPending: true` when realistic rendering is
|
|
94
|
+
* on but was still compiling its shaders after the capture's 20 s wait, so
|
|
95
|
+
* the image shows the Design look instead (capture again once it is ready).
|
|
94
96
|
* @throws If a given `viewId` does not exist.
|
|
95
97
|
*
|
|
96
98
|
* @examplePrompt Take a screenshot of the current view
|
|
97
99
|
* @examplePrompt Capture this view as a PNG
|
|
98
100
|
* @examplePrompt Render the Site Plan view to an image
|
|
99
101
|
* @examplePrompt Export a high-res image of what I'm looking at
|
|
102
|
+
* @examplePrompt Did the realistic render finish before the screenshot?
|
|
100
103
|
*
|
|
101
104
|
* # Example
|
|
102
105
|
* ```ts
|
|
103
|
-
* const
|
|
106
|
+
* const shot = await snaptrude.presentation.views.capture({ format: "png" })
|
|
107
|
+
* if (shot.realisticPending) {
|
|
108
|
+
* // Drawn with the Design look: realistic rendering was still compiling.
|
|
109
|
+
* }
|
|
104
110
|
* ```
|
|
105
111
|
*/
|
|
106
112
|
public abstract capture(options?: {
|
|
@@ -474,9 +480,11 @@ export type PluginPresentationViewsCaptureArgs = z.infer<
|
|
|
474
480
|
* | Property | Type | Description |
|
|
475
481
|
* |---|---|---|
|
|
476
482
|
* | `image` | `string` | The captured image as a base64 data URL |
|
|
483
|
+
* | `realisticPending` | `boolean \| undefined` | `true` when realistic rendering was on but still compiling after the capture's wait, so the image was drawn with the Design look; `false` or absent otherwise (absent from hosts before 1.1) |
|
|
477
484
|
*/
|
|
478
485
|
export const PluginPresentationViewsCaptureResult = z.object({
|
|
479
486
|
image: z.string(),
|
|
487
|
+
realisticPending: z.boolean().optional(),
|
|
480
488
|
})
|
|
481
489
|
export type PluginPresentationViewsCaptureResult = z.infer<
|
|
482
490
|
typeof PluginPresentationViewsCaptureResult
|
|
@@ -522,8 +530,13 @@ export type PluginPresentationViewsCreateArgs = z.infer<
|
|
|
522
530
|
typeof PluginPresentationViewsCreateArgs
|
|
523
531
|
>
|
|
524
532
|
|
|
525
|
-
/**
|
|
526
|
-
|
|
533
|
+
/**
|
|
534
|
+
* Result of {@linkcode PluginPresentationViewsApi.create} — the saved view, plus `framed`:
|
|
535
|
+
* whether the view was framed on the given elements or bounds (always `false` without `frame`).
|
|
536
|
+
*/
|
|
537
|
+
export const PluginPresentationViewsCreateResult = PluginPresentationView.extend({
|
|
538
|
+
framed: z.boolean().optional(),
|
|
539
|
+
})
|
|
527
540
|
export type PluginPresentationViewsCreateResult = z.infer<
|
|
528
541
|
typeof PluginPresentationViewsCreateResult
|
|
529
542
|
>
|
package/src/handles.ts
CHANGED
|
@@ -288,6 +288,17 @@ export const EdgeHandle = handleSchema("edge")
|
|
|
288
288
|
export const HalfEdgeHandle = handleSchema("halfedge")
|
|
289
289
|
export const VertexHandle = handleSchema("vertex")
|
|
290
290
|
export const Vec3Components = z.object({ x: z.number(), y: z.number(), z: z.number() })
|
|
291
|
+
/**
|
|
292
|
+
* A vec3 ARGUMENT: a {@linkcode Vec3Handle} in any inbound form, or a plain finite
|
|
293
|
+
* `{ x, y, z }` point. Geometry reads return plain points, and callers pass them straight
|
|
294
|
+
* back where a vec3 is expected; the host resolves the point as a vector. Returns stay
|
|
295
|
+
* {@linkcode Vec3Handle}. Explicitly typed to keep handles.d.ts inside its size budget.
|
|
296
|
+
*/
|
|
297
|
+
export type Vec3Input = Handle<"vec3"> | Vec3Components
|
|
298
|
+
export const Vec3Input: z.ZodUnion<readonly [typeof Vec3Handle, typeof Vec3Components]> = z.union(
|
|
299
|
+
[Vec3Handle, Vec3Components],
|
|
300
|
+
{ error: 'expected a "vec3" handle or a plain { x, y, z } point' }
|
|
301
|
+
)
|
|
291
302
|
export const QuatComponents = z.object({
|
|
292
303
|
x: z.number(),
|
|
293
304
|
y: z.number(),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Node built-in test runner suite for the handle schemas.
|
|
2
|
+
// Imports the BUILT package (dist) — run `pnpm build` first.
|
|
3
|
+
import test from "node:test"
|
|
4
|
+
import assert from "node:assert/strict"
|
|
5
|
+
import { Vec3Handle, Vec3Input, Handle, PluginDesignMoveArgs } from "../dist/index.js"
|
|
6
|
+
|
|
7
|
+
test("a vec3 handle parses in every inbound form, and only as a handle", () => {
|
|
8
|
+
for (const v of ["vec3_abc", new Handle("vec3_abc"), { __h: "vec3_abc" }, { id: "vec3_abc" }]) {
|
|
9
|
+
const out = Vec3Handle.parse(v)
|
|
10
|
+
assert.ok(out instanceof Handle)
|
|
11
|
+
assert.equal(out.id, "vec3_abc")
|
|
12
|
+
}
|
|
13
|
+
assert.equal(Vec3Handle.safeParse({ x: 1, y: 2, z: 3 }).success, false)
|
|
14
|
+
const bad = Vec3Handle.safeParse("profile_1")
|
|
15
|
+
assert.equal(bad.success, false)
|
|
16
|
+
assert.match(bad.error.issues[0].message, /expected a "vec3" handle/)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test("Vec3Input takes a handle or a plain { x, y, z } point", () => {
|
|
20
|
+
// Geometry reads return plain points; agents pass them back where a vec3 belongs.
|
|
21
|
+
const point = { x: 1, y: 2.5, z: -3 }
|
|
22
|
+
assert.deepEqual(Vec3Input.parse(point), point)
|
|
23
|
+
assert.ok(Vec3Input.parse("vec3_abc") instanceof Handle)
|
|
24
|
+
const args = PluginDesignMoveArgs.parse({ components: ["wl_1"], displacement: { x: 0, y: 0, z: 10 } })
|
|
25
|
+
assert.deepEqual(args.displacement, { x: 0, y: 0, z: 10 })
|
|
26
|
+
const handled = PluginDesignMoveArgs.parse({ components: ["wl_1"], displacement: "vec3_d" })
|
|
27
|
+
assert.equal(handled.displacement.id, "vec3_d")
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("Vec3Input still rejects non-points and wrong handles, with one message", () => {
|
|
31
|
+
for (const v of [{ x: 1, y: 2 }, { x: 1, y: Number.NaN, z: 3 }, { x: "1", y: 2, z: 3 }, "profile_1", null, 42]) {
|
|
32
|
+
const r = Vec3Input.safeParse(v)
|
|
33
|
+
assert.equal(r.success, false, JSON.stringify(v))
|
|
34
|
+
assert.equal(r.error.issues[0].message, 'expected a "vec3" handle or a plain { x, y, z } point')
|
|
35
|
+
}
|
|
36
|
+
})
|