From c7b5f3645d39bc2b8fa59d1a039eda1c55bdef46 Mon Sep 17 00:00:00 2001 From: min Date: Fri, 5 Jun 2026 02:53:35 +0300 Subject: [PATCH] =?UTF-8?q?fix(studio):=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF?= =?UTF-8?q?=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=BC=D0=B0=D0=BD=D0=B8=D0=BF=D1=83?= =?UTF-8?q?=D0=BB=D1=8F=D1=86=D0=B8=D0=B8=20=D0=BF=D0=B0=D0=BF=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B2=20=D1=80=D0=B5=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=BC=20?= =?UTF-8?q?=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8=20(=D0=BD=D0=B5=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D0=B5=D0=BF=D0=BE=D1=80=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Дельта пивота применялась только в dragEnd → объекты телепортировались в конце. Теперь _onFolderGizmoDrag применяет инкрементальную дельту на каждом тике (setOnDrag) — движение/вращение/масштаб группы видно в процессе, как у одиночных объектов. Co-Authored-By: Claude Opus 4.8 --- src/editor/engine/BabylonScene.js | 64 ++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/editor/engine/BabylonScene.js b/src/editor/engine/BabylonScene.js index 79abf35..7813a55 100644 --- a/src/editor/engine/BabylonScene.js +++ b/src/editor/engine/BabylonScene.js @@ -1367,7 +1367,12 @@ export class BabylonScene { this._gizmo.setOnDragEnd(() => this._onGizmoDragEnd()); // Во время scale-drag — live-обновление тайлинга studs (кружки одного // размера, не растягиваются пока тянешь гизмо). - this._gizmo.setOnDrag((mode) => { if (mode === 'scale') this._onGizmoScaleDrag(); }); + this._gizmo.setOnDrag((mode) => { + if (mode === 'scale') this._onGizmoScaleDrag(); + // Групповая папка — применяем дельту в реальном времени (видно движение). + const sel = this.selection?.getSelection?.(); + if (sel && sel.type === 'folder') this._onFolderGizmoDrag(mode); + }); // Привязка гизмо к выделенному this.selection.setOnSelectionChange((sel) => this._updateGizmoForSelection(sel)); @@ -3756,36 +3761,59 @@ export class BabylonScene { pivot.scaling = new Vector3(1, 1, 1); this._folderPivot = pivot; this._folderPivotId = folderId; - // Запоминаем стартовое состояние пивота для вычисления дельты в dragEnd. - this._folderPivotStart = { - pos: { x: center.x, y: center.y, z: center.z }, + // «Последнее применённое» состояние пивота — для инкрементальной + // дельты в реальном времени (_onFolderGizmoDrag). + this._folderPivotLast = { + x: center.x, y: center.y, z: center.z, + ry: 0, scale: 1, center: { x: center.x, y: center.y, z: center.z }, }; } catch (e) { console.warn('[folderGizmo] attach failed', e); } } - /** Применить трансформацию пивота к объектам папки (вызывается на dragEnd). */ - _applyFolderGizmo(mode) { + /** + * Инкрементально применить движение/поворот/масштаб пивота к объектам папки + * ПРЯМО ВО ВРЕМЯ drag (чтобы было видно перемещение, а не телепорт в конце). + */ + _onFolderGizmoDrag(mode) { const pivot = this._folderPivot; const fid = this._folderPivotId; - const start = this._folderPivotStart; - if (!pivot || fid == null || !start || !this.folderManager) return; + const last = this._folderPivotLast; + if (!pivot || fid == null || !last || !this.folderManager) return; if (mode === 'move') { - const dx = pivot.position.x - start.pos.x; - const dy = pivot.position.y - start.pos.y; - const dz = pivot.position.z - start.pos.z; - if (dx || dy || dz) this.folderManager.moveFolderBy(fid, dx, dy, dz); + const dx = pivot.position.x - last.x; + const dy = pivot.position.y - last.y; + const dz = pivot.position.z - last.z; + if (dx || dy || dz) { + this.folderManager.moveFolderBy(fid, dx, dy, dz); + last.x = pivot.position.x; last.y = pivot.position.y; last.z = pivot.position.z; + last.center.x += dx; last.center.y += dy; last.center.z += dz; + } } else if (mode === 'rotate') { - const ry = pivot.rotation.y; - if (Math.abs(ry) > 0.0001) this.folderManager.rotateFolderY(fid, ry, start.center); + const dRy = pivot.rotation.y - last.ry; + if (Math.abs(dRy) > 0.0001) { + this.folderManager.rotateFolderY(fid, dRy, last.center); + last.ry = pivot.rotation.y; + } } else if (mode === 'scale') { - const f = (pivot.scaling.x + pivot.scaling.y + pivot.scaling.z) / 3; - if (Math.abs(f - 1) > 0.001) this.folderManager.scaleFolder(fid, f, start.center); + const cur = (pivot.scaling.x + pivot.scaling.y + pivot.scaling.z) / 3; + const factor = last.scale !== 0 ? cur / last.scale : 1; + if (Math.abs(factor - 1) > 0.001) { + this.folderManager.scaleFolder(fid, factor, last.center); + last.scale = cur; + } } - // Пересоздаём пивот в новом центре (сброс дельты для следующего drag). + } + + /** dragEnd: дельта уже применена в _onFolderGizmoDrag — пересоздаём пивот. */ + _applyFolderGizmo(mode) { + const fid = this._folderPivotId; + if (fid == null || !this.folderManager) return; + // На всякий случай добираем остаток дельты (если drag был очень коротким). + this._onFolderGizmoDrag(mode); + // Пересоздаём пивот в новом центре (сброс для следующего drag). const g = this.folderManager.getFolderObjects(fid); this._attachFolderGizmo(fid, g.center); - // Переустановить gizmo на новый пивот + обновить selection.center. const sel = this.selection?.getSelection?.(); if (sel && sel.type === 'folder') { sel.center = g.center; } if (this._gizmo && this._folderPivot) {