← Qmap · c2

CYCLE.md

c2.entity-meshes - Cycle Notes

Summary

Established a solid OOP foundation for actor entity meshes with distinct visual identities, state management, and animation systems.


Architecture

Class Hierarchy

BaseMesh (abstract)
├── AppMesh      - Dodecahedron, full tumble spin
├── ServiceMesh  - Hexagonal prism, grounded spin
├── DatabaseMesh - Stacked octagon slabs, grounded spin
└── VendorMesh   - Octahedron + favicon sprite, full tumble

Key Patterns

1. Animation Config Merging Subclasses define their defaults, then spread incoming options on top:

const animation = {
  bob: false,
  spin: true,
  spinMode: "grounded",  // subclass default
  ...options.animation,   // caller can override
};

This ensures type-specific behaviors persist even when callers provide partial options.

2. State Material Registration Materials that should respond to state changes register via registerStateMaterial(). The base setState() iterates and updates color/opacity. Materials that should stay constant (like vendor favicons) simply don't register.

3. Disposable Resource Tracking All geometries, materials, and textures call registerDisposable(). The dispose() method cleans up everything - prevents GPU memory leaks.

4. Edge Line HelperscreateEdgeLines(geometry, options) centralizes the LineMaterial/LineSegments2 boilerplate. Supports optional material reuse for efficiency (DatabaseMesh shares one edge material across 3 slabs).

5. Canvas Texture Redrawing Labels and favicons store their canvas/context for dynamic updates. On state change, redraw the canvas and set texture.needsUpdate = true.


Entity Shapes & Behaviors

TypeGeometrySpin ModeVisual Meaning
AppDodecahedronfull (tumble)Complex, multi-faceted internal app
ServiceHexagonal prismgrounded (Y-axis)Modular, stable backend service
Database3 stacked octagonsgrounded (Y-axis)Layered data storage
VendorOctahedron (diamond)full (tumble)External, valuable third-party

State System

Four states with distinct visual treatments:

StateMesh ColorMesh OpacityLabel OpacityLabel Border
activePurple1.01.0Purple
highlightedTeal1.01.0Teal
warningRed1.01.0Red
ignoredWhite0.350.3White

Vendor favicons: icon fades to 0.3 when ignored, white background stays full opacity.


What Works Well

  1. Clean separation - BaseMesh handles lifecycle/animation, subclasses define geometry
  2. Semantic shapes - Each type is instantly distinguishable
  3. Grounded vs full spin - Services/databases feel "infrastructure-like", apps/vendors feel "dynamic"
  4. State reactivity - Single setState() call updates everything consistently
  5. Factory pattern - createEntity(type, options) for runtime type selection

Lessons Learned

Animation override trap: Initially subclasses overrode update() just to skip spin. Wasteful - the base class already checks animState.spin. Removed overrides, used config flags instead.

Options merging matters: Using options.animation ?? defaults completely replaces defaults. Using { ...defaults, ...options.animation } preserves type-specific behaviors.

Canvas textures need explicit updates: Three.js caches textures. After redrawing a canvas, must set texture.needsUpdate = true or changes won't appear.

Favicon state handling: Registering favicon as a state material caused unwanted color tinting. Solution: don't register it, handle opacity separately in _drawFavicon() using ctx.globalAlpha.


Technical Debt / Future Work

  1. Label text truncation - Long labels overflow the canvas. Consider auto-sizing or ellipsis.
  2. Edge line resolution - LineMaterial needs window resolution. Currently set at creation time - won't adapt to window resize.
  3. Favicon error states - Failed favicon loads silently. Could show a placeholder icon.
  4. Performance at scale - Each entity creates its own canvas texture. For 100+ entities, consider texture atlasing.
  5. Animation easing - Current bob uses raw Math.sin(). Could add easing functions for more organic motion.

Files

entities/
├── index.js         - Factory + exports
├── BaseMesh.js      - Base class (~285 lines)
├── AppMesh.js       - Dodecahedron (~63 lines)
├── ServiceMesh.js   - Hexagon (~64 lines)
├── DatabaseMesh.js  - Stacked slabs (~90 lines)
└── VendorMesh.js    - Diamond + favicon (~185 lines)

shared/
├── testData.js      - Sample actors + state colors
├── colorScheme.js   - Color definitions
└── ExperimentBase.js - Three.js scene setup

Ready for Next Cycle

The entity system is production-ready for connecting actors with traffic flows. Next logical step: traffic lines/flows between entities (c3?).

Visual record

outcomes

All states (9)

active

bright

dark

default

grid heavy

highlighted

ignored

warning

Qpoint Brand Style Guide