fix(load3d): reapply up-direction after fitToViewer() transform reset

fitToViewer() resets model.rotation to (0,0,0) but did not reapply
currentUpDirection afterward, causing a state/view mismatch when the
user had previously selected a non-default up-axis.

Fixes https://github.com/Comfy-Org/ComfyUI_frontend/issues/11347

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
bymyself
2026-05-02 03:16:23 +00:00
parent 96575fcec9
commit d4d66f0987
2 changed files with 75 additions and 0 deletions

View File

@@ -760,4 +760,74 @@ describe('SceneModelManager', () => {
expect(mainModels).toHaveLength(1)
})
})
describe('fitToViewer', () => {
it('does nothing when there is no current model', () => {
const { manager, setupCamera, setupGizmo } = createManager()
manager.fitToViewer()
expect(setupCamera).not.toHaveBeenCalled()
expect(setupGizmo).not.toHaveBeenCalled()
})
it('scales and centers the model', async () => {
const { manager } = createManager()
const model = createMeshModel()
await manager.setupModel(model)
model.scale.set(0.1, 0.1, 0.1)
manager.fitToViewer()
expect(model.scale.x).toBeGreaterThan(0.1)
expect(model.scale.y).toBeGreaterThan(0.1)
expect(model.scale.z).toBeGreaterThan(0.1)
})
it('reapplies non-original up-direction after fitting', async () => {
const { manager, eventManager } = createManager()
const model = createMeshModel()
await manager.setupModel(model)
manager.setUpDirection('+z')
vi.mocked(eventManager.emitEvent).mockClear()
manager.fitToViewer()
expect(manager.currentUpDirection).toBe('+z')
expect(model.rotation.x).toBeCloseTo(-Math.PI / 2)
expect(eventManager.emitEvent).toHaveBeenCalledWith(
'upDirectionChange',
'+z'
)
})
it('does not call setUpDirection when up-direction is original', async () => {
const { manager, eventManager } = createManager()
const model = createMeshModel()
await manager.setupModel(model)
vi.mocked(eventManager.emitEvent).mockClear()
manager.fitToViewer()
expect(eventManager.emitEvent).not.toHaveBeenCalledWith(
'upDirectionChange',
expect.anything()
)
})
it('resets originalRotation so up-direction is not compounded after fit', async () => {
const { manager } = createManager()
const model = createMeshModel()
await manager.setupModel(model)
manager.setUpDirection('+x')
const rotationAfterFirstSet = model.rotation.z
manager.fitToViewer()
manager.setUpDirection('+x')
expect(model.rotation.z).toBeCloseTo(rotationAfterFirstSet)
})
})
})

View File

@@ -491,6 +491,11 @@ export class SceneModelManager implements ModelManagerInterface {
model.position.set(-center.x, -scaledBox.min.y, -center.z)
if (this.currentUpDirection !== 'original') {
this.originalRotation = null
this.setUpDirection(this.currentUpDirection)
}
const newBox = this.computeWorldBounds(model)
const newSize = newBox.getSize(new THREE.Vector3())
const newCenter = newBox.getCenter(new THREE.Vector3())