@webspatial/core-sdk 1.7.0 → 2.0.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 +61 -0
- package/README.md +10 -0
- package/dist/JSBCommand.d.ts +398 -0
- package/dist/JSBCommand.js +668 -0
- package/dist/JSBCommand.js.map +1 -0
- package/dist/Spatial.d.ts +63 -0
- package/dist/Spatial.js +60 -0
- package/dist/Spatial.js.map +1 -0
- package/dist/SpatialObject.d.ts +19 -0
- package/dist/SpatialObject.js +44 -0
- package/dist/SpatialObject.js.map +1 -0
- package/dist/SpatialScene.d.ts +51 -0
- package/dist/SpatialScene.js +74 -0
- package/dist/SpatialScene.js.map +1 -0
- package/dist/SpatialSession.d.ts +143 -0
- package/dist/SpatialSession.js +173 -0
- package/dist/SpatialSession.js.map +1 -0
- package/dist/SpatialWebEvent.d.ts +8 -0
- package/dist/SpatialWebEvent.js +18 -0
- package/dist/SpatialWebEvent.js.map +1 -0
- package/dist/SpatialWebEventCreator.d.ts +11 -0
- package/dist/SpatialWebEventCreator.js +11 -0
- package/dist/SpatialWebEventCreator.js.map +1 -0
- package/dist/Spatialized2DElement.d.ts +42 -0
- package/dist/Spatialized2DElement.js +47 -0
- package/dist/Spatialized2DElement.js.map +1 -0
- package/dist/SpatializedDynamic3DElement.d.ts +7 -0
- package/dist/SpatializedDynamic3DElement.js +43 -0
- package/dist/SpatializedDynamic3DElement.js.map +1 -0
- package/dist/SpatializedElement.d.ts +7 -0
- package/dist/SpatializedElement.js +206 -0
- package/dist/SpatializedElement.js.map +1 -0
- package/dist/SpatializedElementCreator.d.ts +15 -0
- package/dist/SpatializedElementCreator.js +47 -0
- package/dist/SpatializedElementCreator.js.map +1 -0
- package/dist/SpatializedStatic3DElement.d.ts +195 -0
- package/dist/SpatializedStatic3DElement.js +327 -0
- package/dist/SpatializedStatic3DElement.js.map +1 -0
- package/dist/WebMsgCommand.d.ts +7 -0
- package/dist/WebMsgCommand.js +23 -0
- package/dist/WebMsgCommand.js.map +1 -0
- package/dist/index.d.ts +35 -1251
- package/dist/index.js +21 -3650
- package/dist/index.js.map +1 -1
- package/dist/install-polyfills.d.ts +2 -0
- package/dist/install-polyfills.js +13 -0
- package/dist/install-polyfills.js.map +1 -0
- package/dist/isSSREnv.d.ts +3 -0
- package/dist/isSSREnv.js +6 -0
- package/dist/isSSREnv.js.map +1 -0
- package/dist/motion/AnimationObject.d.ts +7 -0
- package/dist/motion/AnimationObject.js +140 -0
- package/dist/motion/AnimationObject.js.map +1 -0
- package/dist/motion/compute/index.d.ts +10 -0
- package/dist/motion/compute/index.js +19 -0
- package/dist/motion/compute/index.js.map +1 -0
- package/dist/motion/compute/motionPropertyValues.d.ts +19 -0
- package/dist/motion/compute/motionPropertyValues.js +50 -0
- package/dist/motion/compute/motionPropertyValues.js.map +1 -0
- package/dist/motion/compute/normalize.d.ts +31 -0
- package/dist/motion/compute/normalize.js +201 -0
- package/dist/motion/compute/normalize.js.map +1 -0
- package/dist/motion/compute/validate.d.ts +22 -0
- package/dist/motion/compute/validate.js +103 -0
- package/dist/motion/compute/validate.js.map +1 -0
- package/dist/motion/index.d.ts +8 -0
- package/dist/motion/index.js +6 -0
- package/dist/motion/index.js.map +1 -0
- package/dist/motion/native/serializeMotionTimeline.d.ts +12 -0
- package/dist/motion/native/serializeMotionTimeline.js +21 -0
- package/dist/motion/native/serializeMotionTimeline.js.map +1 -0
- package/dist/physicalMetrics-W5uZ5xKb.d.ts +48 -0
- package/dist/physicalMetrics.d.ts +1 -0
- package/dist/physicalMetrics.js +58 -0
- package/dist/physicalMetrics.js.map +1 -0
- package/dist/platform-adapter/CommandResultUtils.d.ts +12 -0
- package/dist/platform-adapter/CommandResultUtils.js +21 -0
- package/dist/platform-adapter/CommandResultUtils.js.map +1 -0
- package/dist/platform-adapter/createPlatformSync.d.ts +16 -0
- package/dist/platform-adapter/createPlatformSync.js +30 -0
- package/dist/platform-adapter/createPlatformSync.js.map +1 -0
- package/dist/platform-adapter/index.d.ts +12 -0
- package/dist/platform-adapter/index.js +10 -0
- package/dist/platform-adapter/index.js.map +1 -0
- package/dist/platform-adapter/interface.d.ts +7 -0
- package/dist/platform-adapter/interface.js +1 -0
- package/dist/platform-adapter/interface.js.map +1 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.d.ts +25 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.js +168 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.js.map +1 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.d.ts +66 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js +372 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js.map +1 -0
- package/dist/platform-adapter/spatialRequestMetadata.d.ts +6 -0
- package/dist/platform-adapter/spatialRequestMetadata.js +39 -0
- package/dist/platform-adapter/spatialRequestMetadata.js.map +1 -0
- package/dist/platform-adapter/spatialSceneQuery.d.ts +12 -0
- package/dist/platform-adapter/spatialSceneQuery.js +12 -0
- package/dist/platform-adapter/spatialSceneQuery.js.map +1 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.d.ts +19 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.js +77 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.js.map +1 -0
- package/dist/platform-runtime.d.ts +12 -0
- package/dist/platform-runtime.js +14 -0
- package/dist/platform-runtime.js.map +1 -0
- package/dist/reality/Attachment.d.ts +19 -0
- package/dist/reality/Attachment.js +39 -0
- package/dist/reality/Attachment.js.map +1 -0
- package/dist/reality/component/ModelComponent.d.ts +14 -0
- package/dist/reality/component/ModelComponent.js +11 -0
- package/dist/reality/component/ModelComponent.js.map +1 -0
- package/dist/reality/component/SpatialComponent.d.ts +8 -0
- package/dist/reality/component/SpatialComponent.js +19 -0
- package/dist/reality/component/SpatialComponent.js.map +1 -0
- package/dist/reality/component/index.d.ts +8 -0
- package/dist/reality/component/index.js +3 -0
- package/dist/reality/component/index.js.map +1 -0
- package/dist/reality/entity/SpatialEntity.d.ts +7 -0
- package/dist/reality/entity/SpatialEntity.js +291 -0
- package/dist/reality/entity/SpatialEntity.js.map +1 -0
- package/dist/reality/entity/SpatialModelEntity.d.ts +17 -0
- package/dist/reality/entity/SpatialModelEntity.js +17 -0
- package/dist/reality/entity/SpatialModelEntity.js.map +1 -0
- package/dist/reality/entity/index.d.ts +8 -0
- package/dist/reality/entity/index.js +3 -0
- package/dist/reality/entity/index.js.map +1 -0
- package/dist/reality/geometry/SpatialBoxGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialBoxGeometry.js +13 -0
- package/dist/reality/geometry/SpatialBoxGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialConeGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialConeGeometry.js +13 -0
- package/dist/reality/geometry/SpatialConeGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.js +13 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialGeometry.d.ts +7 -0
- package/dist/reality/geometry/SpatialGeometry.js +13 -0
- package/dist/reality/geometry/SpatialGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.js +13 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialSphereGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialSphereGeometry.js +13 -0
- package/dist/reality/geometry/SpatialSphereGeometry.js.map +1 -0
- package/dist/reality/geometry/index.d.ts +12 -0
- package/dist/reality/geometry/index.js +7 -0
- package/dist/reality/geometry/index.js.map +1 -0
- package/dist/reality/index.d.ts +18 -0
- package/dist/reality/index.js +7 -0
- package/dist/reality/index.js.map +1 -0
- package/dist/reality/material/SpatialMaterial.d.ts +7 -0
- package/dist/reality/material/SpatialMaterial.js +13 -0
- package/dist/reality/material/SpatialMaterial.js.map +1 -0
- package/dist/reality/material/SpatialUnlitMaterial.d.ts +16 -0
- package/dist/reality/material/SpatialUnlitMaterial.js +16 -0
- package/dist/reality/material/SpatialUnlitMaterial.js.map +1 -0
- package/dist/reality/material/index.d.ts +8 -0
- package/dist/reality/material/index.js +3 -0
- package/dist/reality/material/index.js.map +1 -0
- package/dist/reality/realityCreator.d.ts +22 -0
- package/dist/reality/realityCreator.js +89 -0
- package/dist/reality/realityCreator.js.map +1 -0
- package/dist/reality/resource/SpatialModelAsset.d.ts +15 -0
- package/dist/reality/resource/SpatialModelAsset.js +12 -0
- package/dist/reality/resource/SpatialModelAsset.js.map +1 -0
- package/dist/reality/resource/SpatialTextureResource.d.ts +16 -0
- package/dist/reality/resource/SpatialTextureResource.js +16 -0
- package/dist/reality/resource/SpatialTextureResource.js.map +1 -0
- package/dist/reality/resource/index.d.ts +9 -0
- package/dist/reality/resource/index.js +3 -0
- package/dist/reality/resource/index.js.map +1 -0
- package/dist/runtime/WebSpatialRuntimeError.d.ts +10 -0
- package/dist/runtime/WebSpatialRuntimeError.js +14 -0
- package/dist/runtime/WebSpatialRuntimeError.js.map +1 -0
- package/dist/runtime/capability-data.d.ts +12 -0
- package/dist/runtime/capability-data.js +134 -0
- package/dist/runtime/capability-data.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +27 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/jsbAdapterPlatform.d.ts +9 -0
- package/dist/runtime/jsbAdapterPlatform.js +38 -0
- package/dist/runtime/jsbAdapterPlatform.js.map +1 -0
- package/dist/runtime/keys.d.ts +34 -0
- package/dist/runtime/keys.js +122 -0
- package/dist/runtime/keys.js.map +1 -0
- package/dist/runtime/semver.d.ts +8 -0
- package/dist/runtime/semver.js +32 -0
- package/dist/runtime/semver.js.map +1 -0
- package/dist/runtime/supports.d.ts +22 -0
- package/dist/runtime/supports.js +78 -0
- package/dist/runtime/supports.js.map +1 -0
- package/dist/runtime/types.d.ts +12 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime/userAgent.d.ts +20 -0
- package/dist/runtime/userAgent.js +41 -0
- package/dist/runtime/userAgent.js.map +1 -0
- package/dist/scene-polyfill.d.ts +18 -0
- package/dist/scene-polyfill.js +480 -0
- package/dist/scene-polyfill.js.map +1 -0
- package/dist/spatial-host.d.ts +13 -0
- package/dist/spatial-host.js +18 -0
- package/dist/spatial-host.js.map +1 -0
- package/dist/spatial-window-polyfill.d.ts +3 -0
- package/dist/spatial-window-polyfill.js +155 -0
- package/dist/spatial-window-polyfill.js.map +1 -0
- package/dist/spatializedElementMotion-_0XWPQCE.d.ts +1009 -0
- package/dist/types/animation.d.ts +7 -0
- package/dist/types/animation.js +1 -0
- package/dist/types/animation.js.map +1 -0
- package/dist/types/global.d.ts +38 -0
- package/dist/types/global.js +1 -0
- package/dist/types/global.js.map +1 -0
- package/dist/types/internal.d.ts +7 -0
- package/dist/types/internal.js +12 -0
- package/dist/types/internal.js.map +1 -0
- package/dist/types/motion/index.d.ts +7 -0
- package/dist/types/motion/index.js +3 -0
- package/dist/types/motion/index.js.map +1 -0
- package/dist/types/motion/spatializedElementMotion.d.ts +7 -0
- package/dist/types/motion/spatializedElementMotion.js +1 -0
- package/dist/types/motion/spatializedElementMotion.js.map +1 -0
- package/dist/types/motion/spatializedMotion.d.ts +7 -0
- package/dist/types/motion/spatializedMotion.js +16 -0
- package/dist/types/motion/spatializedMotion.js.map +1 -0
- package/dist/types/motion/spatializedPlayback.d.ts +11 -0
- package/dist/types/motion/spatializedPlayback.js +1 -0
- package/dist/types/motion/spatializedPlayback.js.map +1 -0
- package/dist/types/motion/spatializedVisual.d.ts +35 -0
- package/dist/types/motion/spatializedVisual.js +1 -0
- package/dist/types/motion/spatializedVisual.js.map +1 -0
- package/dist/types/types.d.ts +7 -0
- package/dist/types/types.js +117 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils.d.ts +44 -0
- package/dist/utils.js +86 -0
- package/dist/utils.js.map +1 -0
- package/package.json +20 -4
- package/src/JSBCommand.ts +132 -5
- package/src/SpatialObject.ts +10 -0
- package/src/Spatialized2DElement.ts +5 -0
- package/src/SpatializedDynamic3DElement.ts +5 -0
- package/src/SpatializedElement.ts +29 -0
- package/src/SpatializedStatic3DElement.ts +42 -3
- package/src/WebMsgCommand.ts +40 -0
- package/src/coverage-boost.test.ts +239 -216
- package/src/index.ts +25 -11
- package/src/install-polyfills.ts +38 -0
- package/src/jsbcommand.coverage.test.ts +23 -0
- package/src/motion/AnimationObject.test.ts +421 -0
- package/src/motion/AnimationObject.ts +208 -0
- package/src/motion/compute/index.ts +10 -0
- package/src/motion/compute/motionPropertyValues.test.ts +55 -0
- package/src/motion/compute/motionPropertyValues.ts +70 -0
- package/src/motion/compute/normalize.test.ts +99 -0
- package/src/motion/compute/normalize.ts +289 -0
- package/src/motion/compute/validate.test.ts +229 -0
- package/src/motion/compute/validate.ts +156 -0
- package/src/motion/index.ts +2 -0
- package/src/motion/native/serializeMotionTimeline.test.ts +61 -0
- package/src/motion/native/serializeMotionTimeline.ts +27 -0
- package/src/platform-adapter/createPlatformSync.ts +5 -3
- package/src/platform-adapter/pico-os/PicoOSPlatform.ts +76 -11
- package/src/platform-adapter/spatialRequestMetadata.test.ts +175 -0
- package/src/platform-adapter/spatialRequestMetadata.ts +48 -0
- package/src/platform-adapter/vision-os/VisionOSPlatform.ts +6 -1
- package/src/reality/entity/SpatialEntity.ts +127 -0
- package/src/runtime/capability-data.ts +104 -4
- package/src/runtime/index.ts +0 -11
- package/src/runtime/keys.ts +12 -1
- package/src/runtime/supports.test.ts +143 -4
- package/src/runtime/supports.ts +3 -1
- package/src/runtime/useAnimation.capability.test.ts +275 -0
- package/src/scene-polyfill.manifest.test.ts +306 -5
- package/src/scene-polyfill.request-metadata.test.ts +39 -0
- package/src/scene-polyfill.test.ts +33 -94
- package/src/scene-polyfill.ts +117 -87
- package/src/spatial-window-polyfill.ts +18 -2
- package/src/transform-roundtrip.test.ts +90 -0
- package/src/types/animation.ts +224 -0
- package/src/types/global.ts +1 -8
- package/src/types/motion/index.ts +10 -0
- package/src/types/motion/spatializedElementMotion.ts +49 -0
- package/src/types/motion/spatializedMotion.ts +147 -0
- package/src/types/motion/spatializedPlayback.ts +17 -0
- package/src/types/motion/spatializedVisual.ts +24 -0
- package/src/types/types.ts +3 -0
- package/src/utils.ts +54 -0
- package/tsup.config.ts +61 -36
- package/vitest.config.ts +5 -1
- package/dist/iife/index.d.ts +0 -1251
- package/dist/iife/index.global.js +0 -75
- package/dist/iife/index.global.js.map +0 -1
- package/src/platform-adapter/ssr/SSRPlatform.ts +0 -52
- /package/src/{ssr-polyfill.ts → isSSREnv.ts} +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import type { SpatializedMotionProperty } from '../../types/motion/spatializedMotion'
|
|
3
|
+
import { setMotionPropertyValue } from './motionPropertyValues'
|
|
4
|
+
|
|
5
|
+
describe('setMotionPropertyValue', () => {
|
|
6
|
+
test('writes opacity', () => {
|
|
7
|
+
const values = {}
|
|
8
|
+
|
|
9
|
+
setMotionPropertyValue(values, 'opacity', 0.5)
|
|
10
|
+
|
|
11
|
+
expect(values).toEqual({ opacity: 0.5 })
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test.each([
|
|
15
|
+
['transform.translate.x', { transform: { translate: { x: 1 } } }],
|
|
16
|
+
['transform.translate.y', { transform: { translate: { y: 2 } } }],
|
|
17
|
+
['transform.translate.z', { transform: { translate: { z: 3 } } }],
|
|
18
|
+
['transform.rotate.x', { transform: { rotate: { x: 4 } } }],
|
|
19
|
+
['transform.rotate.y', { transform: { rotate: { y: 5 } } }],
|
|
20
|
+
['transform.rotate.z', { transform: { rotate: { z: 6 } } }],
|
|
21
|
+
['transform.scale.x', { transform: { scale: { x: 7 } } }],
|
|
22
|
+
['transform.scale.y', { transform: { scale: { y: 8 } } }],
|
|
23
|
+
['transform.scale.z', { transform: { scale: { z: 9 } } }],
|
|
24
|
+
] as const)('writes %s', (motionProperty, expected) => {
|
|
25
|
+
const values = {}
|
|
26
|
+
const sampledValue = Object.values(
|
|
27
|
+
Object.values(expected.transform ?? {})[0] ?? {},
|
|
28
|
+
)[0] as number
|
|
29
|
+
|
|
30
|
+
setMotionPropertyValue(
|
|
31
|
+
values,
|
|
32
|
+
motionProperty as SpatializedMotionProperty,
|
|
33
|
+
sampledValue,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
expect(values).toEqual(expected)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('preserves existing transform groups while writing another property', () => {
|
|
40
|
+
const values = {
|
|
41
|
+
transform: {
|
|
42
|
+
translate: { x: 10 },
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setMotionPropertyValue(values, 'transform.rotate.y', 20)
|
|
47
|
+
|
|
48
|
+
expect(values).toEqual({
|
|
49
|
+
transform: {
|
|
50
|
+
translate: { x: 10 },
|
|
51
|
+
rotate: { y: 20 },
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { SpatializedVisualValues } from '../../types/motion/spatializedVisual'
|
|
2
|
+
import type { SpatializedMotionProperty } from '../../types/motion/spatializedMotion'
|
|
3
|
+
|
|
4
|
+
function getTranslate(
|
|
5
|
+
visualValues: SpatializedVisualValues,
|
|
6
|
+
): NonNullable<NonNullable<SpatializedVisualValues['transform']>['translate']> {
|
|
7
|
+
const transform = (visualValues.transform ??= {})
|
|
8
|
+
return (transform.translate ??= {})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getRotate(
|
|
12
|
+
visualValues: SpatializedVisualValues,
|
|
13
|
+
): NonNullable<NonNullable<SpatializedVisualValues['transform']>['rotate']> {
|
|
14
|
+
const transform = (visualValues.transform ??= {})
|
|
15
|
+
return (transform.rotate ??= {})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getScale(
|
|
19
|
+
visualValues: SpatializedVisualValues,
|
|
20
|
+
): NonNullable<NonNullable<SpatializedVisualValues['transform']>['scale']> {
|
|
21
|
+
const transform = (visualValues.transform ??= {})
|
|
22
|
+
return (transform.scale ??= {})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Writes a sampled numeric track value into the corresponding
|
|
27
|
+
* SpatializedVisualValues field.
|
|
28
|
+
*
|
|
29
|
+
* @param visualValues Visual value object being populated for the sampled time.
|
|
30
|
+
* @param motionProperty Motion property path for the sampled track.
|
|
31
|
+
* @param sampledValue Numeric value sampled from the track at the current time.
|
|
32
|
+
*/
|
|
33
|
+
export function setMotionPropertyValue(
|
|
34
|
+
visualValues: SpatializedVisualValues,
|
|
35
|
+
motionProperty: SpatializedMotionProperty,
|
|
36
|
+
sampledValue: number,
|
|
37
|
+
): void {
|
|
38
|
+
switch (motionProperty) {
|
|
39
|
+
case 'opacity':
|
|
40
|
+
visualValues.opacity = sampledValue
|
|
41
|
+
return
|
|
42
|
+
case 'transform.translate.x':
|
|
43
|
+
getTranslate(visualValues).x = sampledValue
|
|
44
|
+
return
|
|
45
|
+
case 'transform.translate.y':
|
|
46
|
+
getTranslate(visualValues).y = sampledValue
|
|
47
|
+
return
|
|
48
|
+
case 'transform.translate.z':
|
|
49
|
+
getTranslate(visualValues).z = sampledValue
|
|
50
|
+
return
|
|
51
|
+
case 'transform.rotate.x':
|
|
52
|
+
getRotate(visualValues).x = sampledValue
|
|
53
|
+
return
|
|
54
|
+
case 'transform.rotate.y':
|
|
55
|
+
getRotate(visualValues).y = sampledValue
|
|
56
|
+
return
|
|
57
|
+
case 'transform.rotate.z':
|
|
58
|
+
getRotate(visualValues).z = sampledValue
|
|
59
|
+
return
|
|
60
|
+
case 'transform.scale.x':
|
|
61
|
+
getScale(visualValues).x = sampledValue
|
|
62
|
+
return
|
|
63
|
+
case 'transform.scale.y':
|
|
64
|
+
getScale(visualValues).y = sampledValue
|
|
65
|
+
return
|
|
66
|
+
case 'transform.scale.z':
|
|
67
|
+
getScale(visualValues).z = sampledValue
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest'
|
|
2
|
+
import { normalizeMotionConfig } from './normalize'
|
|
3
|
+
|
|
4
|
+
describe('normalizeMotionConfig', () => {
|
|
5
|
+
test('normalizes mixed timeline entries into numeric tracks', () => {
|
|
6
|
+
expect(
|
|
7
|
+
normalizeMotionConfig({
|
|
8
|
+
duration: 2,
|
|
9
|
+
timeline: {
|
|
10
|
+
from: { opacity: 0, transform: { translate: { x: 0 } } },
|
|
11
|
+
'50%': { opacity: 0.5 },
|
|
12
|
+
to: { opacity: 1, transform: { translate: { x: 10 } } },
|
|
13
|
+
},
|
|
14
|
+
}),
|
|
15
|
+
).toMatchObject({
|
|
16
|
+
duration: 2,
|
|
17
|
+
tracks: [
|
|
18
|
+
{
|
|
19
|
+
property: 'opacity',
|
|
20
|
+
keyframes: [
|
|
21
|
+
{ at: 0, value: 0 },
|
|
22
|
+
{ at: 1, value: 0.5 },
|
|
23
|
+
{ at: 2, value: 1 },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
property: 'transform.translate.x',
|
|
28
|
+
keyframes: [
|
|
29
|
+
{ at: 0, value: 0 },
|
|
30
|
+
{ at: 2, value: 10 },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('uses only timeline values when top-level boundaries are present', () => {
|
|
38
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
39
|
+
expect(
|
|
40
|
+
normalizeMotionConfig({
|
|
41
|
+
from: { opacity: 0.25 },
|
|
42
|
+
to: { opacity: 0.75 },
|
|
43
|
+
timeline: {
|
|
44
|
+
from: { opacity: 0 },
|
|
45
|
+
to: { opacity: 1 },
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
).toMatchObject({
|
|
49
|
+
duration: 0.3,
|
|
50
|
+
tracks: [
|
|
51
|
+
{
|
|
52
|
+
property: 'opacity',
|
|
53
|
+
keyframes: [
|
|
54
|
+
{ at: 0, value: 0 },
|
|
55
|
+
{ at: 0.3, value: 1 },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
})
|
|
60
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
61
|
+
expect.stringContaining('top-level from/to are ignored'),
|
|
62
|
+
)
|
|
63
|
+
warnSpy.mockRestore()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('preserves sparse property keyframe ranges', () => {
|
|
67
|
+
expect(
|
|
68
|
+
normalizeMotionConfig({
|
|
69
|
+
duration: 2,
|
|
70
|
+
timeline: {
|
|
71
|
+
'0%': { transform: { rotate: { z: -20 } } },
|
|
72
|
+
'50%': {
|
|
73
|
+
opacity: 0,
|
|
74
|
+
transform: { rotate: { z: 80 } },
|
|
75
|
+
},
|
|
76
|
+
'100%': { opacity: 1 },
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
).toMatchObject({
|
|
80
|
+
duration: 2,
|
|
81
|
+
tracks: [
|
|
82
|
+
{
|
|
83
|
+
property: 'transform.rotate.z',
|
|
84
|
+
keyframes: [
|
|
85
|
+
{ at: 0, value: -20 },
|
|
86
|
+
{ at: 1, value: 80 },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
property: 'opacity',
|
|
91
|
+
keyframes: [
|
|
92
|
+
{ at: 1, value: 0 },
|
|
93
|
+
{ at: 2, value: 1 },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import type { SpatializedVisualValues } from '../../types/motion/spatializedVisual'
|
|
2
|
+
import type { TimingFunction } from '../../types/animation'
|
|
3
|
+
import type {
|
|
4
|
+
NormalizedSpatializedMotionConfig,
|
|
5
|
+
SpatializedMotionConfig,
|
|
6
|
+
SpatializedMotionProperty,
|
|
7
|
+
SpatializedMotionSegmentConfig,
|
|
8
|
+
SpatializedMotionTrack,
|
|
9
|
+
SpatializedMotionTimelineConfig,
|
|
10
|
+
} from '../../types/motion/spatializedMotion'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Flattens visual values into motion-property/value pairs.
|
|
14
|
+
*
|
|
15
|
+
* @param values Visual values to inspect.
|
|
16
|
+
* @param out Collector for discovered motion property samples.
|
|
17
|
+
*/
|
|
18
|
+
function collectScalars(
|
|
19
|
+
values: SpatializedVisualValues,
|
|
20
|
+
out: Array<{ property: SpatializedMotionProperty; value: number }>,
|
|
21
|
+
): void {
|
|
22
|
+
if (values.opacity !== undefined) {
|
|
23
|
+
out.push({ property: 'opacity', value: values.opacity })
|
|
24
|
+
}
|
|
25
|
+
const tr = values.transform
|
|
26
|
+
if (!tr) return
|
|
27
|
+
for (const group of ['translate', 'rotate', 'scale'] as const) {
|
|
28
|
+
const part = tr[group]
|
|
29
|
+
if (!part) continue
|
|
30
|
+
for (const axis of ['x', 'y', 'z'] as const) {
|
|
31
|
+
const v = part[axis]
|
|
32
|
+
if (v !== undefined) {
|
|
33
|
+
out.push({
|
|
34
|
+
property: `transform.${group}.${axis}` as SpatializedMotionProperty,
|
|
35
|
+
value: v,
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Converts `0%`-style timeline keys into [0, 1] ratios.
|
|
44
|
+
*
|
|
45
|
+
* @param key Percentage key from the timeline config.
|
|
46
|
+
* @returns The normalized ratio represented by the key.
|
|
47
|
+
*/
|
|
48
|
+
function parsePercentageKey(key: string): number {
|
|
49
|
+
if (!/^\d+(\.\d+)?%$/.test(key)) {
|
|
50
|
+
throw new Error(`[SpatializedMotion] invalid timeline key "${key}"`)
|
|
51
|
+
}
|
|
52
|
+
const ratio = Number(key.slice(0, -1))
|
|
53
|
+
if (!Number.isFinite(ratio) || ratio < 0 || ratio > 100) {
|
|
54
|
+
throw new Error(`[SpatializedMotion] invalid timeline key "${key}"`)
|
|
55
|
+
}
|
|
56
|
+
return ratio / 100
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Desugars shorthand from/to motion config into canonical tracks.
|
|
61
|
+
*
|
|
62
|
+
* @param simple Shorthand segment config.
|
|
63
|
+
* @returns Canonical track-based motion config.
|
|
64
|
+
*/
|
|
65
|
+
export function segmentConfigToMotionConfig(
|
|
66
|
+
simple: SpatializedMotionSegmentConfig,
|
|
67
|
+
): NormalizedSpatializedMotionConfig {
|
|
68
|
+
if (!simple.from || !simple.to) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'[SpatializedMotion] top-level from and to are both required',
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
const duration = simple.duration ?? 0.3
|
|
74
|
+
const fromScalars: Array<{
|
|
75
|
+
property: SpatializedMotionProperty
|
|
76
|
+
value: number
|
|
77
|
+
}> = []
|
|
78
|
+
const toScalars: Array<{
|
|
79
|
+
property: SpatializedMotionProperty
|
|
80
|
+
value: number
|
|
81
|
+
}> = []
|
|
82
|
+
|
|
83
|
+
collectScalars(simple.from, fromScalars)
|
|
84
|
+
collectScalars(simple.to, toScalars)
|
|
85
|
+
|
|
86
|
+
const trackMap = new Map<SpatializedMotionProperty, SpatializedMotionTrack>()
|
|
87
|
+
const properties = new Set([
|
|
88
|
+
...fromScalars.map(entry => entry.property),
|
|
89
|
+
...toScalars.map(entry => entry.property),
|
|
90
|
+
])
|
|
91
|
+
|
|
92
|
+
for (const property of properties) {
|
|
93
|
+
const fromEntry = fromScalars.find(f => f.property === property)
|
|
94
|
+
const toEntry = toScalars.find(f => f.property === property)
|
|
95
|
+
if (!fromEntry) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`[SpatializedMotion] missing start boundary for "${property}"`,
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
if (!toEntry) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`[SpatializedMotion] missing end boundary for "${property}"`,
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
trackMap.set(property, {
|
|
106
|
+
property,
|
|
107
|
+
timingFunction: simple.timingFunction,
|
|
108
|
+
keyframes: [
|
|
109
|
+
{ at: 0, value: fromEntry.value },
|
|
110
|
+
{ at: duration, value: toEntry.value },
|
|
111
|
+
],
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const tracks = [...trackMap.values()]
|
|
116
|
+
if (tracks.length === 0) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
'[SpatializedMotion.simple] to must include at least one animatable field',
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
duration,
|
|
124
|
+
tracks,
|
|
125
|
+
timingFunction: simple.timingFunction,
|
|
126
|
+
delay: simple.delay,
|
|
127
|
+
autoStart: simple.autoStart,
|
|
128
|
+
loop: simple.loop,
|
|
129
|
+
playbackRate: simple.playbackRate,
|
|
130
|
+
onStart: simple.onStart,
|
|
131
|
+
onComplete: simple.onComplete,
|
|
132
|
+
onStop: simple.onStop,
|
|
133
|
+
onReset: simple.onReset,
|
|
134
|
+
onError: simple.onError,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Desugars percentage-key timeline config into canonical tracks.
|
|
140
|
+
*
|
|
141
|
+
* @param config Timeline config using percentage keys.
|
|
142
|
+
* @returns Canonical track-based motion config.
|
|
143
|
+
*/
|
|
144
|
+
export function desugarTimelineConfig(
|
|
145
|
+
config: SpatializedMotionTimelineConfig,
|
|
146
|
+
): NormalizedSpatializedMotionConfig {
|
|
147
|
+
if (Array.isArray(config.timeline) || !config.timeline) {
|
|
148
|
+
throw new Error('[SpatializedMotion] timeline must be an object')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const entries = Object.entries(config.timeline)
|
|
152
|
+
let hasPercentageKey = false
|
|
153
|
+
const frames = entries
|
|
154
|
+
.map(([key, values]) => {
|
|
155
|
+
if (!values || typeof values !== 'object' || Array.isArray(values)) {
|
|
156
|
+
throw new Error(`[SpatializedMotion] invalid timeline frame "${key}"`)
|
|
157
|
+
}
|
|
158
|
+
if (key === 'from') return { key, atRatio: 0, values }
|
|
159
|
+
if (key === 'to') return { key, atRatio: 1, values }
|
|
160
|
+
hasPercentageKey = true
|
|
161
|
+
return { key, atRatio: parsePercentageKey(key), values }
|
|
162
|
+
})
|
|
163
|
+
.sort((a, b) => a.atRatio - b.atRatio)
|
|
164
|
+
|
|
165
|
+
// Dev warn: top-level from/to are ignored once a timeline is present.
|
|
166
|
+
if (config.from !== undefined || config.to !== undefined) {
|
|
167
|
+
console.warn(
|
|
168
|
+
'[SpatializedMotion] top-level from/to are ignored because timeline is present',
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Start/end boundary frames are required; this change does not fill missing
|
|
173
|
+
// boundaries from underlying element style.
|
|
174
|
+
const hasStartFrame = frames.some(frame => frame.atRatio === 0)
|
|
175
|
+
const hasEndFrame = frames.some(frame => frame.atRatio === 1)
|
|
176
|
+
if (!hasStartFrame) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
'[SpatializedMotion] timeline must define a start frame (from or 0%)',
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
if (!hasEndFrame) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
'[SpatializedMotion] timeline must define an end frame (to or 100%)',
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (hasPercentageKey && config.duration === undefined) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
'[SpatializedMotion] duration is required when timeline uses percentage keys',
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
const duration = config.duration ?? 0.3
|
|
193
|
+
|
|
194
|
+
const trackMap = new Map<
|
|
195
|
+
SpatializedMotionProperty,
|
|
196
|
+
Array<{
|
|
197
|
+
at: number
|
|
198
|
+
value: number
|
|
199
|
+
timingFunction?: TimingFunction
|
|
200
|
+
}>
|
|
201
|
+
>()
|
|
202
|
+
const fromProperties = new Set<SpatializedMotionProperty>()
|
|
203
|
+
const zeroProperties = new Set<SpatializedMotionProperty>()
|
|
204
|
+
const toProperties = new Set<SpatializedMotionProperty>()
|
|
205
|
+
const hundredProperties = new Set<SpatializedMotionProperty>()
|
|
206
|
+
|
|
207
|
+
for (const frame of frames) {
|
|
208
|
+
const at = frame.atRatio * duration
|
|
209
|
+
const scalars: Array<{
|
|
210
|
+
property: SpatializedMotionProperty
|
|
211
|
+
value: number
|
|
212
|
+
}> = []
|
|
213
|
+
collectScalars(frame.values, scalars)
|
|
214
|
+
for (const scalar of scalars) {
|
|
215
|
+
if (frame.key === 'from') fromProperties.add(scalar.property)
|
|
216
|
+
if (frame.key !== 'from' && frame.atRatio === 0) {
|
|
217
|
+
zeroProperties.add(scalar.property)
|
|
218
|
+
}
|
|
219
|
+
if (frame.key === 'to') toProperties.add(scalar.property)
|
|
220
|
+
if (frame.key !== 'to' && frame.atRatio === 1) {
|
|
221
|
+
hundredProperties.add(scalar.property)
|
|
222
|
+
}
|
|
223
|
+
const keyframes = trackMap.get(scalar.property) ?? []
|
|
224
|
+
const keyframe = {
|
|
225
|
+
at,
|
|
226
|
+
value: scalar.value,
|
|
227
|
+
...(frame.values.timingFunction !== undefined
|
|
228
|
+
? { timingFunction: frame.values.timingFunction }
|
|
229
|
+
: {}),
|
|
230
|
+
}
|
|
231
|
+
keyframes.push(keyframe)
|
|
232
|
+
trackMap.set(scalar.property, keyframes)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const tracks = [...trackMap.entries()].map(([property, keyframes]) => {
|
|
237
|
+
const startCount =
|
|
238
|
+
Number(fromProperties.has(property)) +
|
|
239
|
+
Number(zeroProperties.has(property))
|
|
240
|
+
const endCount =
|
|
241
|
+
Number(toProperties.has(property)) +
|
|
242
|
+
Number(hundredProperties.has(property))
|
|
243
|
+
if (startCount > 1) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`[SpatializedMotion] duplicate start boundary for "${property}"`,
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
if (endCount > 1) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`[SpatializedMotion] duplicate end boundary for "${property}"`,
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
property,
|
|
255
|
+
timingFunction: config.timingFunction,
|
|
256
|
+
keyframes,
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
duration,
|
|
262
|
+
tracks,
|
|
263
|
+
timingFunction: config.timingFunction,
|
|
264
|
+
delay: config.delay,
|
|
265
|
+
autoStart: config.autoStart,
|
|
266
|
+
loop: config.loop,
|
|
267
|
+
playbackRate: config.playbackRate,
|
|
268
|
+
onStart: config.onStart,
|
|
269
|
+
onComplete: config.onComplete,
|
|
270
|
+
onStop: config.onStop,
|
|
271
|
+
onReset: config.onReset,
|
|
272
|
+
onError: config.onError,
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Normalizes any supported motion config shape into canonical tracks.
|
|
278
|
+
*
|
|
279
|
+
* @param config Motion config in shorthand, timeline, or canonical form.
|
|
280
|
+
* @returns Canonical track-based motion config.
|
|
281
|
+
*/
|
|
282
|
+
export function normalizeMotionConfig(
|
|
283
|
+
config: SpatializedMotionConfig,
|
|
284
|
+
): NormalizedSpatializedMotionConfig {
|
|
285
|
+
if (config.timeline !== undefined) {
|
|
286
|
+
return desugarTimelineConfig(config as SpatializedMotionTimelineConfig)
|
|
287
|
+
}
|
|
288
|
+
return segmentConfigToMotionConfig(config as SpatializedMotionSegmentConfig)
|
|
289
|
+
}
|