Fix diagram text sync, resize handle positioning, and PouchDB delete handling
- Fix diagramObject text setter to update entity before notifying observers - Improve ResizeGizmo handle positioning directly at corners/faces with constant screen-size scaling - Fix PouchDB sync to handle deleted documents using _id field for compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
960c64984e
commit
8c2b7f9c7d
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "immersive",
|
"name": "immersive",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.8-45",
|
"version": "0.0.8-46",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@ -227,13 +227,24 @@ export class DiagramObject {
|
|||||||
if (this._labelBack) {
|
if (this._labelBack) {
|
||||||
this._labelBack.dispose();
|
this._labelBack.dispose();
|
||||||
}
|
}
|
||||||
if (this._diagramEntity.text != value) {
|
const textChanged = this._diagramEntity.text != value;
|
||||||
|
|
||||||
|
// Update the entity text FIRST (before notifying observers)
|
||||||
|
this._diagramEntity.text = value;
|
||||||
|
|
||||||
|
// Also update mesh metadata to keep in sync with diagramEntity getter
|
||||||
|
if (this._mesh && this._mesh.metadata) {
|
||||||
|
this._mesh.metadata.text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// THEN notify observers with the UPDATED entity
|
||||||
|
if (textChanged) {
|
||||||
this._eventObservable.notifyObservers({
|
this._eventObservable.notifyObservers({
|
||||||
type: DiagramEventType.MODIFY,
|
type: DiagramEventType.MODIFY,
|
||||||
entity: this._diagramEntity
|
entity: this._diagramEntity
|
||||||
}, DiagramEventObserverMask.TO_DB);
|
}, DiagramEventObserverMask.TO_DB);
|
||||||
}
|
}
|
||||||
this._diagramEntity.text = value;
|
|
||||||
this._label = createLabel(value);
|
this._label = createLabel(value);
|
||||||
this._label.parent = this._baseTransform;
|
this._label.parent = this._baseTransform;
|
||||||
this._labelBack = new InstancedMesh('labelBack' + value, (this._label as Mesh));
|
this._labelBack = new InstancedMesh('labelBack' + value, (this._label as Mesh));
|
||||||
|
|||||||
@ -108,6 +108,10 @@ export class ResizeGizmo {
|
|||||||
* Create corner and face handles
|
* Create corner and face handles
|
||||||
*/
|
*/
|
||||||
private createHandles(): void {
|
private createHandles(): void {
|
||||||
|
// Ensure world matrix and bounding info are current
|
||||||
|
this._targetMesh.computeWorldMatrix(true);
|
||||||
|
this._targetMesh.refreshBoundingInfo();
|
||||||
|
|
||||||
// Get bounding box for positioning
|
// Get bounding box for positioning
|
||||||
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
||||||
const boundingBox = targetBoundingInfo.boundingBox;
|
const boundingBox = targetBoundingInfo.boundingBox;
|
||||||
@ -116,10 +120,10 @@ export class ResizeGizmo {
|
|||||||
const innerCorners = boundingBox.vectorsWorld;
|
const innerCorners = boundingBox.vectorsWorld;
|
||||||
const worldMatrix = this._targetMesh.getWorldMatrix();
|
const worldMatrix = this._targetMesh.getWorldMatrix();
|
||||||
|
|
||||||
// Calculate handle size once (based on corner distance)
|
// Unit size - actual visual size controlled by updateHandleScaling()
|
||||||
const handleSize = innerCorners[0].subtract(bboxCenter).length() * .5;
|
const handleSize = 1.0;
|
||||||
|
|
||||||
// Create corner handles
|
// Create corner handles - positioned directly at bounding box corners
|
||||||
CORNER_POSITIONS.forEach((cornerDef, index) => {
|
CORNER_POSITIONS.forEach((cornerDef, index) => {
|
||||||
const cornerPos = innerCorners[index];
|
const cornerPos = innerCorners[index];
|
||||||
|
|
||||||
@ -129,10 +133,8 @@ export class ResizeGizmo {
|
|||||||
this._utilityLayer.utilityLayerScene
|
this._utilityLayer.utilityLayerScene
|
||||||
);
|
);
|
||||||
|
|
||||||
// Position outward from center so handle corner touches bounding box corner
|
// Position handle at the corner (scaling will make it small)
|
||||||
const direction = cornerPos.subtract(bboxCenter).normalize();
|
handleMesh.position = cornerPos.clone();
|
||||||
const offset = direction.scale(handleSize * Math.sqrt(3) / 2);
|
|
||||||
handleMesh.position = cornerPos.add(offset);
|
|
||||||
handleMesh.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
handleMesh.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
||||||
handleMesh.material = this._handleMaterial;
|
handleMesh.material = this._handleMaterial;
|
||||||
handleMesh.isPickable = true;
|
handleMesh.isPickable = true;
|
||||||
@ -156,10 +158,8 @@ export class ResizeGizmo {
|
|||||||
this._utilityLayer.utilityLayerScene
|
this._utilityLayer.utilityLayerScene
|
||||||
);
|
);
|
||||||
|
|
||||||
// Position outward from center so handle touches face center
|
// Position handle at the face center (scaling will make it small)
|
||||||
const direction = faceCenterWorld.subtract(bboxCenter).normalize();
|
handleMesh.position = faceCenterWorld.clone();
|
||||||
const offset = direction.scale(handleSize * Math.sqrt(3) / 2);
|
|
||||||
handleMesh.position = faceCenterWorld.add(offset);
|
|
||||||
handleMesh.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
handleMesh.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
||||||
handleMesh.material = this._handleMaterial;
|
handleMesh.material = this._handleMaterial;
|
||||||
handleMesh.isPickable = true;
|
handleMesh.isPickable = true;
|
||||||
@ -197,15 +197,20 @@ export class ResizeGizmo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update handle scaling based on camera distance for consistent visual size
|
* Update handle scaling based on camera distance for constant screen size.
|
||||||
|
* Handles will appear the same visual size regardless of distance.
|
||||||
*/
|
*/
|
||||||
private updateHandleScaling(): void {
|
private updateHandleScaling(): void {
|
||||||
const camera = this._scene.activeCamera;
|
const camera = this._scene.activeCamera;
|
||||||
if (!camera) return;
|
if (!camera) return;
|
||||||
|
|
||||||
|
// Target angular size - tune this for desired visual size
|
||||||
|
// 0.03 means handles appear ~3cm at 1m distance, ~6cm at 2m, etc.
|
||||||
|
const targetAngularSize = 0.03;
|
||||||
|
|
||||||
for (const handle of this._handles) {
|
for (const handle of this._handles) {
|
||||||
const distance = Vector3.Distance(camera.globalPosition, handle.position);
|
const distance = Vector3.Distance(camera.globalPosition, handle.position);
|
||||||
const scaleFactor = distance; // Adjust multiplier to control visual size
|
const scaleFactor = distance * targetAngularSize;
|
||||||
handle.scaling = new Vector3(scaleFactor, scaleFactor, scaleFactor);
|
handle.scaling = new Vector3(scaleFactor, scaleFactor, scaleFactor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,27 +487,24 @@ export class ResizeGizmo {
|
|||||||
* Update handle positions and sizes to match current target mesh bounding box
|
* Update handle positions and sizes to match current target mesh bounding box
|
||||||
*/
|
*/
|
||||||
private updateHandleTransforms(): void {
|
private updateHandleTransforms(): void {
|
||||||
|
// Ensure world matrix and bounding info are current
|
||||||
|
this._targetMesh.computeWorldMatrix(true);
|
||||||
|
this._targetMesh.refreshBoundingInfo();
|
||||||
|
|
||||||
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
const targetBoundingInfo = this._targetMesh.getBoundingInfo();
|
||||||
const boundingBox = targetBoundingInfo.boundingBox;
|
const boundingBox = targetBoundingInfo.boundingBox;
|
||||||
const bboxCenter = boundingBox.centerWorld;
|
|
||||||
const extents = boundingBox.extendSize;
|
const extents = boundingBox.extendSize;
|
||||||
const innerCorners = boundingBox.vectorsWorld;
|
const innerCorners = boundingBox.vectorsWorld;
|
||||||
const worldMatrix = this._targetMesh.getWorldMatrix();
|
const worldMatrix = this._targetMesh.getWorldMatrix();
|
||||||
|
|
||||||
// Recalculate handle size based on new bounding box
|
|
||||||
const newHandleSize = innerCorners[0].subtract(bboxCenter).length() * .2;
|
|
||||||
|
|
||||||
let handleIndex = 0;
|
let handleIndex = 0;
|
||||||
|
|
||||||
// Update corner handles (first 8 handles)
|
// Update corner handles (first 8 handles) - position at corners
|
||||||
for (let i = 0; i < CORNER_POSITIONS.length; i++) {
|
for (let i = 0; i < CORNER_POSITIONS.length; i++) {
|
||||||
const handle = this._handles[handleIndex];
|
const handle = this._handles[handleIndex];
|
||||||
const cornerPos = innerCorners[i];
|
const cornerPos = innerCorners[i];
|
||||||
|
|
||||||
// Update position
|
handle.position = cornerPos.clone();
|
||||||
const direction = cornerPos.subtract(bboxCenter).normalize();
|
|
||||||
const offset = direction.scale(newHandleSize * Math.sqrt(3) / 2);
|
|
||||||
handle.position = cornerPos.add(offset);
|
|
||||||
handle.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
handle.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
||||||
|
|
||||||
handleIndex++;
|
handleIndex++;
|
||||||
@ -520,10 +522,7 @@ export class ResizeGizmo {
|
|||||||
);
|
);
|
||||||
const faceCenterWorld = Vector3.TransformCoordinates(localFacePos, worldMatrix);
|
const faceCenterWorld = Vector3.TransformCoordinates(localFacePos, worldMatrix);
|
||||||
|
|
||||||
// Update position
|
handle.position = faceCenterWorld.clone();
|
||||||
const direction = faceCenterWorld.subtract(bboxCenter).normalize();
|
|
||||||
const offset = direction.scale(newHandleSize * Math.sqrt(3) / 2);
|
|
||||||
handle.position = faceCenterWorld.add(offset);
|
|
||||||
handle.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
handle.rotationQuaternion = this._targetMesh.absoluteRotationQuaternion;
|
||||||
|
|
||||||
handleIndex++;
|
handleIndex++;
|
||||||
|
|||||||
@ -61,9 +61,14 @@ export class PouchData {
|
|||||||
if (info.direction === 'pull' && info.change && info.change.docs) {
|
if (info.direction === 'pull' && info.change && info.change.docs) {
|
||||||
info.change.docs.forEach((doc) => {
|
info.change.docs.forEach((doc) => {
|
||||||
if (doc._deleted) {
|
if (doc._deleted) {
|
||||||
this.onDBEntityRemoveObservable.notifyObservers(doc);
|
// PouchDB deleted documents only have _id, not our custom id field
|
||||||
} else if (doc.id && doc.id !== 'metadata') {
|
// Create an entity with id from _id for the remove handler
|
||||||
this.onDBEntityUpdateObservable.notifyObservers(doc);
|
const entity = { id: doc._id, ...doc };
|
||||||
|
this.onDBEntityRemoveObservable.notifyObservers(entity);
|
||||||
|
} else if ((doc.id || doc._id) && (doc.id || doc._id) !== 'metadata') {
|
||||||
|
// Accept both id and _id for compatibility
|
||||||
|
const entity = doc.id ? doc : { id: doc._id, ...doc };
|
||||||
|
this.onDBEntityUpdateObservable.notifyObservers(entity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user