diff --git a/src/diagram/diagramObject.ts b/src/diagram/diagramObject.ts
index 0862846..2abe86c 100644
--- a/src/diagram/diagramObject.ts
+++ b/src/diagram/diagramObject.ts
@@ -1,6 +1,7 @@
import {
AbstractActionManager,
AbstractMesh,
+ Color3,
Curve3,
GreasedLineMesh,
InstancedMesh,
@@ -9,6 +10,7 @@ import {
Observer,
Ray,
Scene,
+ StandardMaterial,
TransformNode,
Vector3
} from "@babylonjs/core";
@@ -20,6 +22,21 @@ import {createLabel} from "./functions/createLabel";
import {DiagramEventObserverMask} from "./types/diagramEventObserverMask";
import log, {Logger} from "loglevel";
import {xyztovec} from "./functions/vectorConversion";
+import {AnimatedLineTexture} from "../util/animatedLineTexture";
+import {getToolboxColors} from "../toolbox/toolbox";
+import {findClosestColor} from "../util/functions/findClosestColor";
+
+/**
+ * Converts a Color3 to a hex color string
+ * @param color - BabylonJS Color3
+ * @returns Hex color string (e.g., '#ff0000')
+ */
+function color3ToHex(color: Color3): string {
+ const r = Math.floor(color.r * 255).toString(16).padStart(2, '0');
+ const g = Math.floor(color.g * 255).toString(16).padStart(2, '0');
+ const b = Math.floor(color.b * 255).toString(16).padStart(2, '0');
+ return `#${r}${g}${b}`;
+}
type DiagramObjectOptionsType = {
diagramEntity?: DiagramEntity,
@@ -375,6 +392,52 @@ export class DiagramObject {
curve.setPoints([p]);
this._baseTransform.position = c.getPoints()[Math.floor(c.getPoints().length / 2)];
+ // Update connection texture color to match the "from" mesh using toolbox color
+ let hexColor: string | null = null;
+
+ // Extract color using same priority system as toDiagramEntity
+ if (this._fromMesh.metadata?.color) {
+ // Priority 1: Explicit metadata color (most reliable)
+ hexColor = this._fromMesh.metadata.color;
+ } else if (this._fromMesh instanceof InstancedMesh && this._fromMesh.sourceMesh?.id) {
+ // Priority 2: Extract from tool mesh ID (e.g., "tool-#box-template-#FF0000")
+ const toolId = this._fromMesh.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('#')) {
+ hexColor = color.toLowerCase(); // Normalize to lowercase
+ }
+ }
+ } else {
+ // Priority 3: Fallback to material extraction
+ const fromMaterial = this._fromMesh.material as StandardMaterial;
+ if (fromMaterial) {
+ const fromColor = fromMaterial.diffuseColor || fromMaterial.emissiveColor || Color3.White();
+ hexColor = color3ToHex(fromColor);
+ }
+ }
+
+ if (hexColor) {
+ // Find the closest toolbox color
+ const availableColors = getToolboxColors();
+ const closestColor = findClosestColor(hexColor, availableColors);
+
+ // Get or create material
+ const material = curve.material as StandardMaterial;
+ if (material) {
+ // Dispose old texture if it exists
+ if (material.emissiveTexture) {
+ AnimatedLineTexture.DisposeTexture(material.emissiveTexture);
+ }
+
+ // Create new colored texture using the closest toolbox color
+ const coloredTexture = AnimatedLineTexture.CreateColoredTexture(closestColor);
+ material.emissiveTexture = coloredTexture;
+ material.opacityTexture = coloredTexture;
+ }
+ }
+
// Update cached positions after successful update
this._lastFromPosition = this._fromMesh.getAbsolutePosition().clone();
this._lastToPosition = this._toMesh.getAbsolutePosition().clone();
diff --git a/src/util/animatedLineTexture.ts b/src/util/animatedLineTexture.ts
index 7459525..08a3fed 100644
--- a/src/util/animatedLineTexture.ts
+++ b/src/util/animatedLineTexture.ts
@@ -1,19 +1,74 @@
import {Texture} from "@babylonjs/core";
import {DefaultScene} from "../defaultScene";
+/**
+ * Creates an SVG arrow as a data URL
+ * @param hexColor - Hex color string (e.g., '#00ff00')
+ * @returns Base64-encoded SVG data URL
+ */
+function createArrowSvg(hexColor: string): string {
+ const svg = ``;
+ return `data:image/svg+xml;base64,${btoa(svg)}`;
+}
+
export class AnimatedLineTexture {
- private static _textureColors = new Uint8Array([10, 10, 10, 10, 10, 10, 25, 25, 25, 10, 10, 255])
private static _texture: Texture;
+ private static _animatedTextures: Set = new Set();
+ private static _animationObserverAdded: boolean = false;
public static Texture() {
if (!AnimatedLineTexture._texture) {
- this._texture = new Texture('/assets/textures/arrow.png', DefaultScene.Scene);
+ this._texture = new Texture(createArrowSvg('#00ff00'), DefaultScene.Scene);
this._texture.name = 'connection-texture';
this._texture.uScale = 30;
- DefaultScene.Scene.onBeforeRenderObservable.add(() => {
- this._texture.uOffset -= 0.01 * DefaultScene.Scene.getAnimationRatio()
- });
+ this._animatedTextures.add(this._texture);
+
+ if (!this._animationObserverAdded) {
+ DefaultScene.Scene.onBeforeRenderObservable.add(() => {
+ this._animatedTextures.forEach(texture => {
+ texture.uOffset -= 0.01 * DefaultScene.Scene.getAnimationRatio();
+ });
+ });
+ this._animationObserverAdded = true;
+ }
}
return this._texture;
}
+
+ /**
+ * Creates a new texture with a specific color
+ * @param hexColor - Hex color string (e.g., '#ff0000')
+ * @returns A new texture instance with the specified color
+ */
+ public static CreateColoredTexture(hexColor: string): Texture {
+ const texture = new Texture(createArrowSvg(hexColor), DefaultScene.Scene);
+ texture.name = `connection-texture-${hexColor}`;
+ texture.uScale = 30;
+
+ // Track this texture for animation updates
+ this._animatedTextures.add(texture);
+
+ // Ensure animation observer is set up
+ if (!this._animationObserverAdded) {
+ DefaultScene.Scene.onBeforeRenderObservable.add(() => {
+ this._animatedTextures.forEach(t => {
+ t.uOffset -= 0.01 * DefaultScene.Scene.getAnimationRatio();
+ });
+ });
+ this._animationObserverAdded = true;
+ }
+
+ return texture;
+ }
+
+ /**
+ * Removes a texture from the animation set when disposed
+ * @param texture - The texture to stop animating
+ */
+ public static DisposeTexture(texture: Texture): void {
+ this._animatedTextures.delete(texture);
+ texture.dispose();
+ }
}
\ No newline at end of file