dazscript-framework 1.0.16 → 1.0.18

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dazscript-framework",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "author": "Freddy Diaz",
5
5
  "license": "MPL-2.0",
6
6
  "description": "",
@@ -461,9 +461,11 @@ const build = <TItem, TData>(context: ListViewBuilderContext<TItem, TData>): DzL
461
461
  })
462
462
  }
463
463
 
464
- listView.contextMenuRequested.scriptConnect((item: DzListViewItem, pos: Point) => {
465
- context.contextMenu?.(listView, item, pos).exec(pos.cursorPos(), 0)
466
- })
464
+ if (context.contextMenu) {
465
+ listView.contextMenuRequested.scriptConnect((item: DzListViewItem, pos: Point) => {
466
+ context.contextMenu(listView, item, pos).exec(pos.cursorPos(), 0)
467
+ })
468
+ }
467
469
 
468
470
  return listView
469
471
  }
@@ -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
+ }