Fix color persistence using metadata and source mesh ID fallback

Colors were being lost during resize operations because toDiagramEntity
extracted color from material.diffuseColor, which is no longer used after
the emissiveColor rendering optimization (commit c7887d7).

Root Causes:
1. Rendering system changed from diffuseColor to emissiveColor
2. Material properties unreliable when materials are shared
3. Material-based extraction broke when properties changed

Solution - Three-Tier Fallback Chain:

Priority 1: mesh.metadata.color
- Most reliable, explicitly set during mesh creation
- Already populated by buildMeshFromDiagramEntity (line 163)

Priority 2: Extract from mesh.sourceMesh.id (InstancedMesh)
- Tool mesh IDs encode color: "tool-BOX-#FF0000"
- Preserves original tool color regardless of material state
- Works for all instanced diagram meshes

Priority 3: Material properties (backwards compatibility)
- Checks emissiveColor first (current system)
- Falls back to diffuseColor (old system)
- Handles both StandardMaterial and PBRMaterial
- Maintains compatibility with non-instanced meshes

Changes:
- Import InstancedMesh from @babylonjs/core
- Replace direct material extraction with fallback chain
- Parse tool mesh ID to extract hex color code
- Normalize colors to lowercase
- Add null checks for safe color extraction

Benefits:
 Independent of material system changes
 Works with shared materials
 Preserves original tool colors
 Backwards compatible
 More reliable than material-only extraction

Files modified:
- toDiagramEntity.ts: Implement fallback chain for color extraction

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2025-11-15 08:05:49 -06:00
parent af52d5992c
commit 43100ad650

View File

@ -1,4 +1,4 @@
import {AbstractMesh} from "@babylonjs/core";
import {AbstractMesh, InstancedMesh} from "@babylonjs/core";
import {DiagramEntity} from "../types/diagramEntity";
import log from "loglevel";
import {v4 as uuidv4} from 'uuid';
@ -25,20 +25,44 @@ export function toDiagramEntity(mesh: AbstractMesh): DiagramEntity {
entity.from = mesh?.metadata?.from;
entity.to = mesh?.metadata?.to;
entity.scale = vectoxys(mesh.scaling);
if (mesh.material) {
// Extract color using fallback chain for reliability
if (mesh.metadata?.color) {
// Priority 1: Explicit metadata color (most reliable)
entity.color = mesh.metadata.color;
} else if (mesh instanceof InstancedMesh && mesh.sourceMesh?.id) {
// Priority 2: Extract from tool mesh ID (e.g., "tool-BOX-#FF0000")
const toolId = mesh.sourceMesh.id;
const parts = toolId.split('-');
if (parts.length >= 3 && parts[0] === 'tool') {
const color = parts.slice(2).join('-'); // Handle colors with dashes
if (color.startsWith('#')) {
entity.color = color.toLowerCase(); // Normalize to lowercase
}
}
} else if (mesh.material) {
// Priority 3: Fallback to material extraction (backwards compatibility)
switch (mesh.material.getClassName()) {
case "StandardMaterial":
entity.color = (mesh.material as any).diffuseColor.toHexString();
const stdMat = mesh.material as any;
const stdColor = stdMat.emissiveColor || stdMat.diffuseColor;
if (stdColor) {
entity.color = stdColor.toHexString()?.toLowerCase();
}
break;
case "PBRMaterial":
entity.color = (mesh.material as any).albedoColor.toHexString();
const pbrMat = mesh.material as any;
const pbrColor = pbrMat.emissiveColor || pbrMat.albedoColor;
if (pbrColor) {
entity.color = pbrColor.toHexString()?.toLowerCase();
}
break;
}
} else {
if (entity.template != "#object-template") {
logger.error("toDiagramEntity: mesh.material is null");
}
}
return entity;
}