From 0b81605bdf59ea236144862a9b3fddf10a1684d1 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Wed, 19 Nov 2025 16:52:04 -0600 Subject: [PATCH] Fix label positioning to use world space bounding boxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Labels were incorrectly mixing local and global transform matrices, causing incorrect positioning on scaled/rotated meshes. Now properly converts world space bounding box positions to mesh local space using temporary TransformNode. Changes: - updateTextNode.ts: Use boundingBox.maximumWorld instead of boundingSphere.maximum - diagramObject.ts: Add empty object param to refreshBoundingInfo() - inputTextView.ts: Adjust input handle default position and mesh offsets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/diagram/diagramObject.ts | 2 +- src/information/inputTextView.ts | 6 ++++-- src/util/functions/updateTextNode.ts | 24 ++++++++++++++++++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/diagram/diagramObject.ts b/src/diagram/diagramObject.ts index 237fa85..1afcb57 100644 --- a/src/diagram/diagramObject.ts +++ b/src/diagram/diagramObject.ts @@ -205,7 +205,7 @@ export class DiagramObject { public updateLabelPosition() { if (this._label) { this._mesh.computeWorldMatrix(true); - this._mesh.refreshBoundingInfo(); + this._mesh.refreshBoundingInfo({}); if (this._from && this._to) { // Connection labels (arrows/lines) diff --git a/src/information/inputTextView.ts b/src/information/inputTextView.ts index 55ece07..4437fa9 100644 --- a/src/information/inputTextView.ts +++ b/src/information/inputTextView.ts @@ -31,9 +31,11 @@ export class InputTextView { this.handle = new Handle({ contentMesh: this.inputMesh, label: 'Input', - defaultPosition: new Vector3(0, .4, .5), - defaultRotation: new Vector3(0, 0, 0) + defaultPosition: new Vector3(0, .8, .7), + defaultRotation: new Vector3(.36, 0, 0) }); + this.inputMesh.position.y = .02; + this.inputMesh.position.z = .01; // Position is now controlled by Handle class this.createKeyboard(); } diff --git a/src/util/functions/updateTextNode.ts b/src/util/functions/updateTextNode.ts index 36ff69f..7ab04b8 100644 --- a/src/util/functions/updateTextNode.ts +++ b/src/util/functions/updateTextNode.ts @@ -1,4 +1,4 @@ -import {AbstractMesh, Color3, DynamicTexture, Material, MeshBuilder, StandardMaterial} from "@babylonjs/core"; +import {AbstractMesh, Color3, DynamicTexture, Material, MeshBuilder, StandardMaterial, TransformNode} from "@babylonjs/core"; import log from "loglevel"; @@ -63,7 +63,6 @@ export function updateTextNode(mesh: AbstractMesh, text: string) { function createPlane(mat: Material, mesh: AbstractMesh, text: string, planeWidth: number, height: number): AbstractMesh { const plane = MeshBuilder.CreatePlane("text" + text, {width: planeWidth, height: height}, mesh.getScene()); - const yOffset = mesh.getBoundingInfo().boundingSphere.maximum.y; plane.parent = mesh; plane.scaling.y = (1 / mesh.scaling.y); @@ -75,9 +74,26 @@ function createPlane(mat: Material, mesh: AbstractMesh, text: string, planeWidth if (mesh.metadata?.template == "#connection-template") { plane.billboardMode = AbstractMesh.BILLBOARDMODE_Y; plane.position.y = mesh.position.y + .1; - } else { - plane.position.y = yOffset + (height * plane.scaling.y); + // Calculate label position using world space bounding box + // This ensures labels are positioned correctly regardless of mesh transforms + mesh.computeWorldMatrix(true); + mesh.refreshBoundingInfo(); + + // Get the top of the bounding box in world space + const top = mesh.getBoundingInfo().boundingBox.maximumWorld; + + // Convert world space position to mesh's local space + // Use temporary TransformNode to handle the transformation + const temp = new TransformNode("temp", mesh.getScene()); + temp.position = top; + temp.setParent(mesh); + const y = temp.position.y; + temp.dispose(); + + // Position label above the mesh with offset + // Add additional offset for the scaled height of the label + plane.position.y = y + 0.06 + (height * plane.scaling.y / 2); } plane.addLODLevel(3, null); return plane;