mmongan

ChartXR: 3D Dependency Visualization Skill

- MeshBuilder API: https://doc.babylonjs.com/features/featuresDeepDive/Meshes/Mesh/CreateMesh_Details

mmongan 0 Updated 1mo ago

Resources

28
GitHub

Install

npx skillscat add mmongan/mmongan-github-io

Install via the SkillsCat registry.

SKILL.md

ChartXR: 3D Dependency Visualization Skill

Skill Overview

This specialized skill handles the complete implementation of dependency tree analysis, visualization generation, and interactive 3D scene management for ChartXR. Use this skill when building features that involve analyzing project structure, creating 3D representations, or enhancing scene interaction.

When to Use This Skill

  • Adding new visualization elements (spheres, lines, annotations)
  • Implementing complex camera behaviors or navigation
  • Debugging dependency tree generation or parsing issues
  • Optimizing scene rendering performance
  • Creating interactive drilling/zoom features into nested structures
  • Handling WebGL errors or graphics state issues

Core Knowledge Areas

Dependency Tree Architecture

The system analyzes source code and creates a hierarchical tree:

  1. Parsing Phase (buildDependencyTree.js)

    • Extracts imports: import X from 'path', require('path')
    • Extracts exports: export const/function/class NAME, export { NAME }
    • Detects cycles with visited/inProgress Sets
    • Limits depth to 20 levels to prevent runaway recursion
  2. Data Structure (NodeInfo interface)

    interface NodeInfo {
      name: string;              // "package.json"
      imageUrl: string;          // Profile image
      children: NodeInfo[];      // Dependencies
      exports: string[];         // Exported symbols
      position: Vector3;         // 3D world position
      size: number;              // Cube dimension
      nodeMesh: Mesh;            // Babylon.js geometry
    }
  3. Visualization Mapping

    • One cube per file node
    • Cyan spheres = imported dependencies (children)
    • Gold spheres = exported objects (exports[])
    • Blue tubes = dependency connections with red arrow heads

3D Scene Mathematics

Position Calculation

  • Root node: (0, -10, 0)
  • Child nodes arranged in circular orbit around parent
  • Angle per child: (i / numChildren) * 2π
  • Height alternates: ±nodeSize * 0.15
  • Radius: nodeSize * 0.25 (adjustable parameter)

Surface-Based Connections

// Direction from source to destination
const direction = toNode.position.subtract(fromNode.position);
const normalizedDir = direction.normalize();

// Surface points (not center-to-center)
const fromSurfaceOffset = (fromSize / 2) * 1.05; // 1.05 = slight overshoot
const toSurfaceOffset = (toSize / 2) * 1.05;

const startPoint = fromNode.position.add(normalizedDir.scale(fromSurfaceOffset));
const endPoint = toNode.position.add(normalizedDir.scale(-toSurfaceOffset));

// Create tube between these points
MeshBuilder.CreateTube(..., { path: [startPoint, endPoint], ... })

Arrow Head Orientation

// Rotate cone to point along direction vector
const yAxis = Vector3.Up();
const rotationAxis = Vector3.Cross(yAxis, normalizedDir).normalize();
const rotationAngle = Math.acos(Vector3.Dot(yAxis, normalizedDir));

if (rotationAxis.length() > 0.001) {
  const quat = Quaternion.RotationAxis(rotationAxis, rotationAngle);
  arrow.rotationQuaternion = quat;
}

Texture Generation

  • DynamicTexture at 256x256 for file name labels
  • Canvas-based text rendering with fallback handling
  • Single texture applied to all 6 cube faces (shared material)
  • Try-catch wraps creation to handle WebGL context issues
const faceTex = new DynamicTexture(`face_${name}`, 256, scene, false);
const ctx = faceTex.getContext();
ctx.fillStyle = '#4A90E2';
ctx.fillRect(0, 0, 256, 256);
ctx.font = 'bold 32px Arial';
ctx.fillStyle = '#FFFFFF';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(displayText, 128, 128);
faceTex.update();

Camera System

  • Type: FreeCamera with minZ = 0.5 (near clipping plane)
  • Collision: Automatic detection prevents camera clipping into geometry
  • Landing: Face-center mode to zoom into specific cube surfaces
  • Movement: WASD keys + mouse look (via inputController)

Collision Detection Logic

// Check nearby nodes within search radius
const nearby = nodeManager.getNearbyNodes(cameraPosition, 200);

// If colliding, back away smoothly
if (collision) {
  camera.position = previousSafePosition;
}

Material System

All materials follow a consistent pattern:

  • diffuseColor: Base color visible in light
  • emissiveColor: Self-luminosity for glow effect (usually darker than diffuse)
  • specularColor: Highlight response (typically 0.5, 0.5, 0.5)
  • specularPower: Shininess (32 for glossy)
  • alpha: Transparency (0.6-0.9 depending on element type)

Implementation Patterns

Adding a New Mesh Type

  1. Create MeshBuilder geometry: CreateSphere(), CreateBox(), CreateCone(), etc.
  2. Create StandardMaterial with appropriate colors
  3. Parent to correct TransformNode
  4. Set pickable/visible properties
  5. Add to scene via parent hierarchy

Debugging 3D Issues

  1. Use babylon.js debug layer: scene.debugLayer.show()
  2. Check WebGL errors: browser DevTools → Console → filter WebGL
  3. Verify positions: log Vector3 coordinates during creation
  4. Test camera: manually adjust minZ value to isolate clipping issues
  5. Profile rendering: DevTools Performance tab during interaction

Performance Optimization

  • Limit mesh count per node (8 refs + 16 exports = 24 max per file)
  • Use TransformNode hierarchies for grouped movement
  • Single texture per node instead of per-face
  • Freeze meshes after creation if not animating

Common Tasks

Task: Extract Exports from New File Type

// Pattern to add to extractExports() function
const newExportRegex = /pattern_for_declaration/g;
while ((match = newExportRegex.exec(content)) !== null) {
  exports.push(match[1]);
}

Task: Add Interactive Feature

  1. Bind event in pointerHandler.ts or inputController.ts
  2. Get intersected mesh: scene.pick(x, y)
  3. Identify node: look up in nodeInfoMap by mesh name
  4. Execute action (zoom, highlight, navigate, etc.)
  5. Update scene state if needed

Task: Optimize Large Dependency Trees

  • Implement LOD (level of detail) - hide distant node details
  • Use bounding box culling to skip invisible meshes
  • Profile to identify bottleneck (parsing vs rendering vs interaction)
  • Consider async mesh creation for complex trees

Known Limitations & Workarounds

WebGL Context Loss

Issue: Graphics context can be lost on some devices/browsers
Workaround: Try-catch around texture creation with fallback to solid color
Prevention: Minimize memory usage; avoid creating too many large textures

Large Dependency Trees

Issue: Performance degrades with very large projects (1000+ files)
Workaround: Implement LOD or culling
Future: Consider GPU-accelerated rendering or WebWorker for parsing

Arrow Head Rotation

Issue: Cone must point along arbitrary direction vector
Solution: Use quaternion rotation with cross product to find axis
Edge case: Collinear vectors (direction = -Y axis) require special handling

Testing Checklist

  • Scene initializes without WebGL errors
  • All node meshes render with correct colors and transparency
  • Connection tubes connect cube surfaces (not centers)
  • Arrow heads point toward destination
  • Export spheres visible and distinct from reference spheres
  • Texture labels readable on all faces
  • Camera collision works (can't clip into cubes)
  • Performance acceptable (60 FPS on target hardware)

Resources