dazscript-framework 1.0.16 → 1.0.17
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/package.json
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
vi.mock('./numeric-property-helper', () => ({ adjust: vi.fn() }))
|
|
4
|
+
vi.mock('@dsf/core/global', () => ({
|
|
5
|
+
sceneHelper: {
|
|
6
|
+
getInternalName: vi.fn(),
|
|
7
|
+
getNode: vi.fn(),
|
|
8
|
+
getUniqueMorphName: vi.fn(),
|
|
9
|
+
setInternalName: vi.fn(),
|
|
10
|
+
setPropertyPath: vi.fn()
|
|
11
|
+
}
|
|
12
|
+
}))
|
|
13
|
+
|
|
14
|
+
const { adjust } = await import('./numeric-property-helper')
|
|
15
|
+
const { bakeRotations, bakeScale, bakeTranslations, bakeTransforms, collectTransformBakePlan } = await import('./transform-bake-helper')
|
|
16
|
+
|
|
17
|
+
type TestProperty = {
|
|
18
|
+
name: string
|
|
19
|
+
path: string
|
|
20
|
+
value: number
|
|
21
|
+
outputs: TestProperty[]
|
|
22
|
+
getName: () => string
|
|
23
|
+
getPath: () => string
|
|
24
|
+
getValue: () => number
|
|
25
|
+
getNumSlaveControllers: () => number
|
|
26
|
+
getSlaveController: (index: number) => { getOwner: () => TestProperty }
|
|
27
|
+
getNumControllers: () => number
|
|
28
|
+
getController: () => null
|
|
29
|
+
inherits: (type: string) => boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const property = (name: string, path = '/Pose Controls'): TestProperty => {
|
|
33
|
+
const testProperty: TestProperty = {
|
|
34
|
+
name,
|
|
35
|
+
path,
|
|
36
|
+
value: 7,
|
|
37
|
+
outputs: [],
|
|
38
|
+
getName: () => testProperty.name,
|
|
39
|
+
getPath: () => testProperty.path,
|
|
40
|
+
getValue: () => testProperty.value,
|
|
41
|
+
getNumSlaveControllers: () => testProperty.outputs.length,
|
|
42
|
+
getSlaveController: (index: number) => ({ getOwner: () => testProperty.outputs[index] }),
|
|
43
|
+
getNumControllers: () => 0,
|
|
44
|
+
getController: () => null,
|
|
45
|
+
inherits: (type: string) => type === 'DzNumericProperty' || type === 'DzFloatProperty'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return testProperty
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const link = (driver: TestProperty, output: TestProperty): void => {
|
|
52
|
+
driver.outputs.push(output)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
vi.clearAllMocks()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('transform bake helper', () => {
|
|
60
|
+
it('plans recursive output reset and selected rotation capture', () => {
|
|
61
|
+
const master = property('body_ctrl_rHandGrasp')
|
|
62
|
+
const middle = property('body_ctrl_rFingersGrasp')
|
|
63
|
+
const rotation = property('ZRotate', '/General/Transforms/Rotation')
|
|
64
|
+
const corrective = property('Value', '/Hidden/Base Correctives/Hands')
|
|
65
|
+
link(master, middle)
|
|
66
|
+
link(middle, rotation)
|
|
67
|
+
link(rotation, corrective)
|
|
68
|
+
|
|
69
|
+
const plan = collectTransformBakePlan(master, { rotations: true })
|
|
70
|
+
|
|
71
|
+
expect(plan.resetProperties).toEqual([master, middle, rotation, corrective])
|
|
72
|
+
expect(plan.capturedProperties).toEqual([rotation])
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('bakes selected transforms by capturing final values, zeroing chain, then restoring captures', () => {
|
|
76
|
+
const master = property('master')
|
|
77
|
+
const rotation = property('XRotate', '/General/Transforms/Rotation')
|
|
78
|
+
rotation.value = 45
|
|
79
|
+
link(master, rotation)
|
|
80
|
+
|
|
81
|
+
bakeTransforms(master, { rotations: true })
|
|
82
|
+
|
|
83
|
+
expect(adjust).toHaveBeenNthCalledWith(1, master, 0)
|
|
84
|
+
expect(adjust).toHaveBeenNthCalledWith(2, rotation, 0)
|
|
85
|
+
expect(adjust).toHaveBeenNthCalledWith(3, rotation, 45)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('filters translations and scale independently', () => {
|
|
89
|
+
const master = property('master')
|
|
90
|
+
const translation = property('XTranslate', '/General/Transforms/Translation')
|
|
91
|
+
const rotation = property('XRotate', '/General/Transforms/Rotation')
|
|
92
|
+
const scale = property('XScale', '/General/Transforms/Scale')
|
|
93
|
+
link(master, translation)
|
|
94
|
+
link(master, rotation)
|
|
95
|
+
link(master, scale)
|
|
96
|
+
|
|
97
|
+
expect(collectTransformBakePlan(master, { translations: true }).capturedProperties).toEqual([translation])
|
|
98
|
+
expect(collectTransformBakePlan(master, { rotations: true }).capturedProperties).toEqual([rotation])
|
|
99
|
+
expect(collectTransformBakePlan(master, { scale: true }).capturedProperties).toEqual([scale])
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('exposes focused bake helpers', () => {
|
|
103
|
+
const master = property('master')
|
|
104
|
+
const translation = property('YTranslate', '/General/Transforms/Translation')
|
|
105
|
+
const rotation = property('YRotate', '/General/Transforms/Rotation')
|
|
106
|
+
const scale = property('YScale', '/General/Transforms/Scale')
|
|
107
|
+
link(master, translation)
|
|
108
|
+
link(master, rotation)
|
|
109
|
+
link(master, scale)
|
|
110
|
+
|
|
111
|
+
bakeRotations(master)
|
|
112
|
+
expect(adjust).toHaveBeenLastCalledWith(rotation, 7)
|
|
113
|
+
|
|
114
|
+
vi.clearAllMocks()
|
|
115
|
+
bakeTranslations(master)
|
|
116
|
+
expect(adjust).toHaveBeenLastCalledWith(translation, 7)
|
|
117
|
+
|
|
118
|
+
vi.clearAllMocks()
|
|
119
|
+
bakeScale(master)
|
|
120
|
+
expect(adjust).toHaveBeenLastCalledWith(scale, 7)
|
|
121
|
+
})
|
|
122
|
+
})
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { adjust } from './numeric-property-helper'
|
|
2
|
+
import { getPropertyOutputs } from './property-helper'
|
|
3
|
+
|
|
4
|
+
export type TransformBakeOptions = {
|
|
5
|
+
rotations?: boolean
|
|
6
|
+
translations?: boolean
|
|
7
|
+
scale?: boolean
|
|
8
|
+
maxDepth?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type TransformBakePlan = {
|
|
12
|
+
resetProperties: DzProperty[]
|
|
13
|
+
capturedProperties: TransformBakeProperty[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type TransformBakeProperty = DzFloatProperty | DzIntProperty | DzBoolProperty
|
|
17
|
+
|
|
18
|
+
type CapturedPropertyValue = {
|
|
19
|
+
property: TransformBakeProperty
|
|
20
|
+
value: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DEFAULT_MAX_DEPTH = 10
|
|
24
|
+
|
|
25
|
+
const hasProperty = (properties: DzProperty[], property: DzProperty): boolean => {
|
|
26
|
+
for (let index = 0; index < properties.length; index++) {
|
|
27
|
+
if (properties[index] === property) return true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const isTransformBakeProperty = (property: DzProperty): property is TransformBakeProperty => {
|
|
34
|
+
return property.inherits('DzFloatProperty') || property.inherits('DzIntProperty') || property.inherits('DzBoolProperty')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getPropertyName = (property: DzProperty): string => {
|
|
38
|
+
const getName = (property as any).getName
|
|
39
|
+
if (typeof getName === 'function') return String(getName.call(property))
|
|
40
|
+
|
|
41
|
+
return String((property as any).name ?? '')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const getPropertyPath = (property: DzProperty): string => {
|
|
45
|
+
const getPath = (property as any).getPath
|
|
46
|
+
if (typeof getPath === 'function') return String(getPath.call(property))
|
|
47
|
+
|
|
48
|
+
return ''
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isRotationProperty = (property: DzProperty): boolean => {
|
|
52
|
+
const name = getPropertyName(property)
|
|
53
|
+
return getPropertyPath(property) === '/General/Transforms/Rotation'
|
|
54
|
+
|| name === 'XRotate'
|
|
55
|
+
|| name === 'YRotate'
|
|
56
|
+
|| name === 'ZRotate'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const isTranslationProperty = (property: DzProperty): boolean => {
|
|
60
|
+
const name = getPropertyName(property)
|
|
61
|
+
return getPropertyPath(property) === '/General/Transforms/Translation'
|
|
62
|
+
|| name === 'XTranslate'
|
|
63
|
+
|| name === 'YTranslate'
|
|
64
|
+
|| name === 'ZTranslate'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const isScaleProperty = (property: DzProperty): boolean => {
|
|
68
|
+
const name = getPropertyName(property)
|
|
69
|
+
return getPropertyPath(property) === '/General/Transforms/Scale'
|
|
70
|
+
|| name === 'Scale'
|
|
71
|
+
|| name === 'XScale'
|
|
72
|
+
|| name === 'YScale'
|
|
73
|
+
|| name === 'ZScale'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const shouldCaptureProperty = (property: DzProperty, options: TransformBakeOptions): boolean => {
|
|
77
|
+
return (options.rotations === true && isRotationProperty(property))
|
|
78
|
+
|| (options.translations === true && isTranslationProperty(property))
|
|
79
|
+
|| (options.scale === true && isScaleProperty(property))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const collectRecursiveOutputProperties = (property: DzProperty, maxDepth: number): DzProperty[] => {
|
|
83
|
+
const outputs: DzProperty[] = []
|
|
84
|
+
const queue: DzProperty[] = [property]
|
|
85
|
+
const depths: number[] = [0]
|
|
86
|
+
|
|
87
|
+
for (let queueIndex = 0; queueIndex < queue.length; queueIndex++) {
|
|
88
|
+
const driver = queue[queueIndex]
|
|
89
|
+
const depth = depths[queueIndex]
|
|
90
|
+
if (depth >= maxDepth) continue
|
|
91
|
+
|
|
92
|
+
const relations = getPropertyOutputs(driver)
|
|
93
|
+
for (let relationIndex = 0; relationIndex < relations.length; relationIndex++) {
|
|
94
|
+
const output = relations[relationIndex].property
|
|
95
|
+
if (!output) continue
|
|
96
|
+
if (hasProperty(outputs, output)) continue
|
|
97
|
+
|
|
98
|
+
outputs.push(output)
|
|
99
|
+
if (!hasProperty(queue, output)) {
|
|
100
|
+
queue.push(output)
|
|
101
|
+
depths.push(depth + 1)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return outputs
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const collectTransformBakePlan = (property: DzProperty, options: TransformBakeOptions): TransformBakePlan => {
|
|
110
|
+
const outputs = collectRecursiveOutputProperties(property, options.maxDepth ?? DEFAULT_MAX_DEPTH)
|
|
111
|
+
const capturedProperties: TransformBakeProperty[] = []
|
|
112
|
+
|
|
113
|
+
for (let index = 0; index < outputs.length; index++) {
|
|
114
|
+
const output = outputs[index]
|
|
115
|
+
if (!isTransformBakeProperty(output)) continue
|
|
116
|
+
if (!shouldCaptureProperty(output, options)) continue
|
|
117
|
+
|
|
118
|
+
capturedProperties.push(output)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
resetProperties: [property].concat(outputs),
|
|
123
|
+
capturedProperties
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const captureValues = (properties: TransformBakeProperty[]): CapturedPropertyValue[] => {
|
|
128
|
+
const values: CapturedPropertyValue[] = []
|
|
129
|
+
|
|
130
|
+
for (let index = 0; index < properties.length; index++) {
|
|
131
|
+
const property = properties[index]
|
|
132
|
+
values.push({ property, value: (property as any).getValue() })
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return values
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const resetProperties = (properties: DzProperty[]): void => {
|
|
139
|
+
for (let index = 0; index < properties.length; index++) {
|
|
140
|
+
const property = properties[index]
|
|
141
|
+
if (!isTransformBakeProperty(property)) continue
|
|
142
|
+
|
|
143
|
+
adjust(property, 0)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const restoreValues = (values: CapturedPropertyValue[]): void => {
|
|
148
|
+
for (let index = 0; index < values.length; index++) {
|
|
149
|
+
const captured = values[index]
|
|
150
|
+
adjust(captured.property, captured.value)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const bakeTransforms = (property: DzProperty, options: TransformBakeOptions): void => {
|
|
155
|
+
const plan = collectTransformBakePlan(property, options)
|
|
156
|
+
const capturedValues = captureValues(plan.capturedProperties)
|
|
157
|
+
|
|
158
|
+
resetProperties(plan.resetProperties)
|
|
159
|
+
restoreValues(capturedValues)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const bakeRotations = (property: DzProperty): void => {
|
|
163
|
+
bakeTransforms(property, { rotations: true })
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const bakeTranslations = (property: DzProperty): void => {
|
|
167
|
+
bakeTransforms(property, { translations: true })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const bakeScale = (property: DzProperty): void => {
|
|
171
|
+
bakeTransforms(property, { scale: true })
|
|
172
|
+
}
|