@deepblend/dsh-blender-host 0.10.0 → 0.12.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/lib/assembly-changes.js +21 -0
- package/lib/index.js +16 -4
- package/package.json +2 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Describe saved object-origin moves and changed generators, not surface clearances. */
|
|
2
|
+
import { canonicalStringify } from '@deepblend/dsh-blender-contracts'
|
|
3
|
+
export function assemblyPlacementChanges(before, after, fromRevision, toRevision) {
|
|
4
|
+
const previous = new Map(before.entities.map(entity => [entity.id, entity]))
|
|
5
|
+
const groups = new Map()
|
|
6
|
+
const generatorChangedEntityIds = []
|
|
7
|
+
for (const entity of after.entities) {
|
|
8
|
+
const original = previous.get(entity.id)
|
|
9
|
+
if (!original) continue
|
|
10
|
+
if (canonicalStringify(entity.generator) !== canonicalStringify(original.generator)) generatorChangedEntityIds.push(entity.id)
|
|
11
|
+
const from = original.transform?.location ?? [0, 0, 0]
|
|
12
|
+
const to = entity.transform?.location ?? [0, 0, 0]
|
|
13
|
+
const deltaMm = to.map((value, axis) => Number(((value - from[axis]) * 1000).toFixed(9)))
|
|
14
|
+
if (deltaMm.every(value => value === 0)) continue
|
|
15
|
+
const key = JSON.stringify(deltaMm)
|
|
16
|
+
if (!groups.has(key)) groups.set(key, { deltaMm, entityIds: [] })
|
|
17
|
+
groups.get(key).entityIds.push(entity.id)
|
|
18
|
+
}
|
|
19
|
+
return { source: 'saved-scene-spec', coordinateField: 'entity.transform.location',
|
|
20
|
+
fromRevision, toRevision, groups: [...groups.values()], generatorChangedEntityIds }
|
|
21
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
import { assertRuntimeSceneFeatures } from './runtime-scene-features.js'
|
|
29
29
|
import { readRecipeContext } from './recipe-state.js'
|
|
30
|
+
import { assemblyPlacementChanges } from './assembly-changes.js'
|
|
30
31
|
import { instantiateRecipeLock, planRecipeGeometryEdit, recipeGeometryParameters } from '@deepblend/dsh-blender-contracts'
|
|
31
32
|
import { Service } from '@deepseek-ai/cordis'
|
|
32
33
|
import z from '@deepseek-ai/schemastery'
|
|
@@ -671,7 +672,7 @@ export default class BlenderStudio extends Service {
|
|
|
671
672
|
const digest = sceneSpecDigest(spec)
|
|
672
673
|
return {
|
|
673
674
|
...toCanonicalProjectSummary({
|
|
674
|
-
record: { ...record, goal: spec.project.goal ?? null },
|
|
675
|
+
record: { ...record, projectsRoot: this.projectsRoot, goal: spec.project.goal ?? null },
|
|
675
676
|
revisionCount: record.revisionCount ?? this.store.listRevisions(projectId).length,
|
|
676
677
|
sceneSummary: summarizeSceneSpec(spec, {
|
|
677
678
|
revision,
|
|
@@ -679,6 +680,7 @@ export default class BlenderStudio extends Service {
|
|
|
679
680
|
digest,
|
|
680
681
|
}),
|
|
681
682
|
}),
|
|
683
|
+
projectDirectory: this.store.projectDirectory(projectId),
|
|
682
684
|
revisions: this.store.listRevisions(projectId).map(id => {
|
|
683
685
|
const manifest = this.store.readRevisionManifest(projectId, id)
|
|
684
686
|
return {
|
|
@@ -792,8 +794,14 @@ export default class BlenderStudio extends Service {
|
|
|
792
794
|
const locked = context.state ? { ...context.lock, values: context.state.values,
|
|
793
795
|
valuesSha256: context.state.valuesSha256 } : context.lock
|
|
794
796
|
const resolved = instantiateRecipeLock(locked)
|
|
797
|
+
const currentSpec = this.store.readRevisionSpec(projectId, revision)
|
|
798
|
+
const animatedBindings = definitions.flatMap(parameter => parameter.bindings).filter(binding => binding.offsetAnimation === true)
|
|
799
|
+
const linkedAnimationTracks = (currentSpec.animationTracks ?? []).filter(track => (track.targetKind ?? 'entity') === 'entity'
|
|
800
|
+
&& animatedBindings.some(binding => binding.entityId === track.targetEntityId && track.property === `location.${binding.axis}`))
|
|
801
|
+
.map(track => ({ id: track.id, targetEntityId: track.targetEntityId, property: track.property,
|
|
802
|
+
keyframes: structuredClone(track.keyframes) }))
|
|
795
803
|
let conflicts = []
|
|
796
|
-
try { planRecipeGeometryEdit(context.lock,
|
|
804
|
+
try { planRecipeGeometryEdit(context.lock, currentSpec, context.state ?? undefined, {}) }
|
|
797
805
|
catch (error) {
|
|
798
806
|
if (error.code !== 'RECIPE_PARAMETER_CONFLICT') throw error
|
|
799
807
|
conflicts = error.details.conflicts
|
|
@@ -802,7 +810,7 @@ export default class BlenderStudio extends Service {
|
|
|
802
810
|
recipe: { id: context.lock.id, version: context.lock.version }, valuesSha256: resolved.recipe.valuesSha256,
|
|
803
811
|
parameters: definitions.map(parameter => ({ id: parameter.id, title: parameter.title, type: parameter.type,
|
|
804
812
|
minimum: parameter.minimum, maximum: parameter.maximum, default: parameter.default, value: resolved.values[parameter.id] })),
|
|
805
|
-
conflicts }
|
|
813
|
+
conflicts, linkedAnimationTracks, animationSource: 'saved-scene-spec' }
|
|
806
814
|
}
|
|
807
815
|
|
|
808
816
|
async updateRecipeParameters(request) {
|
|
@@ -818,9 +826,13 @@ export default class BlenderStudio extends Service {
|
|
|
818
826
|
throw new BlenderError('REVISION_CONFLICT', 'The active project store changed; read the selected work again')
|
|
819
827
|
}
|
|
820
828
|
const outcome = await this.transactions.applyRecipeParameters(input, { signal })
|
|
829
|
+
const placementChanges = assemblyPlacementChanges(
|
|
830
|
+
this.store.readRevisionSpec(input.projectId, input.baseRevision),
|
|
831
|
+
this.store.readRevisionSpec(input.projectId, outcome.revision.revision),
|
|
832
|
+
input.baseRevision, outcome.revision.revision)
|
|
821
833
|
return { ...toCanonicalRevisionSummary({ manifest: outcome.revision, checkpointPath: outcome.revision.checkpoint }),
|
|
822
834
|
unchanged: outcome.unchanged === true, idempotentReplay: outcome.idempotentReplay === true,
|
|
823
|
-
parameterState: outcome.parameterState, preservedCameraIds: outcome.preservedCameraIds,
|
|
835
|
+
parameterState: outcome.parameterState, preservedCameraIds: outcome.preservedCameraIds, placementChanges,
|
|
824
836
|
parameters: await this.getRecipeParameters(input.projectId, { revision: outcome.revision.revision }),
|
|
825
837
|
...(outcome.job ? { job: toCanonicalJobRecord(outcome.job) } : {}) }
|
|
826
838
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepblend/dsh-blender-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DeepBlend Host service: the blenderStudio business facade over the BlenderRuntime seam.",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"./package.json": "./package.json"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@deepblend/dsh-blender-contracts": "0.
|
|
23
|
+
"@deepblend/dsh-blender-contracts": "0.12.0",
|
|
24
24
|
"sharp": "0.35.5"
|
|
25
25
|
},
|
|
26
26
|
"license": "MIT",
|