← Cycles
Qmap · c15

QMap V1 — Production Component

archivedcreated 2025-03-18· last activity 2026-03-23

Promote 14 cycles of experimental work to a clean, consumable `src/` component

Why

QMap has 14 cycles of experiments culminating in c14 with 6 actor types, animated transitions, layout engine, flow visualization, and grouping strategies. None of this had been promoted to src/ — which still rendered a spinning dodecahedron. The goal: create a clean v1 component that other projects can import and pass reactive data to.

What Changed

This is not an experiment cycle — it's a promotion cycle. No new features were invented. Instead, c14's battle-tested code was reorganized, cleaned, and wired into a props-driven Vue component.

New src/ Structure (37 files)

src/
├── index.js                          # Public exports
├── components/QMap.vue               # Props → scene (full rewrite)
├── composables/
│   ├── useQMap.js                    # Extended with entity animation loop
│   ├── useSceneLoader.js             # From c14, stripped test scenarios
│   ├── useActorSelection.js          # From c14, options object pattern
│   └── useSceneReactivity.js         # NEW: watches props → reconciler → transitions
├── core/
│   ├── SceneManager.js               # Existing (documented)
│   ├── CameraControls.js             # Existing (added public isDragging/updateFrustum)
│   ├── SceneReconciler.js            # From c14 (pure, unchanged)
│   ├── TransitionOrchestrator.js     # From c14 (RAF leak fixed)
│   ├── LayoutEngine.js               # From c14
│   ├── ViewOrchestrator.js           # From c14
│   ├── GroupingStrategy.js           # From c14
│   ├── animation.js                  # From c14
│   └── cameraAnimation.js            # From c14 (uses public API now)
├── entities/                         # All 7 files from c14
├── flows/                            # 3 files from c14
├── boundaries/GroupRug.js            # From c14
├── layout/                           # 4 files from c14
├── styles/                           # colorScheme, labelMetrics
└── utils/transitionHelpers.js        # Defensive copy utilities

Props API

defineProps({
  actors:      { type: Array, default: () => [] },
  connections: { type: Array, default: () => [] },
  groupBy:     { type: Object, default: () => ({}) },
  width, height, backgroundColor, frustumSize, selectable, autoStart, faviconProxy
})

Events

defineEmits(['ready', 'error', 'actor-select', 'actor-deselect', 'transition-start', 'transition-end'])

Reactivity Model

Props change → useSceneReactivity watches → LayoutEngine.compute() → SceneReconciler.reconcile() → TransitionOrchestrator.execute() (4-phase) → fit camera → refresh selection
  • First load: direct build (no transition)
  • Subsequent: animated reconciliation
  • During transition: queue pending update, apply after completion

Public Exports

export { QMap } from './components/QMap.vue'
export { useQMap } from './composables/useQMap.js'
export { SceneManager } from './core/SceneManager.js'
export { CameraControls } from './core/CameraControls.js'
export { computeGrouping, getAvailableTags, getAvailableTagsByType } from './core/GroupingStrategy.js'

Bugs Fixed During Promotion

  1. Race condition: dual load paths — The isReady watcher manually called loadData(), AND useSceneReactivity's deep watch also fired on initial props. Fixed with explicit activate()/deactivate() lifecycle so the watcher doesn't register until after initialize().
  2. RAF leak in TransitionOrchestrator._phaseMove — The syncFlows RAF loop wasn't stored or cancelled on abort. Wrapped in try/finally with cancelAnimationFrame().
  3. Render target leak in SceneManager.captureImage — If readRenderTargetPixels() threw, the WebGLRenderTarget was never disposed. Wrapped in try/finally.
  4. Private API access — _isDragging and _updateFrustum accessed from outside CameraControls. Added public isDragging() and updateFrustum() methods. Updated all callers.
  5. freezeTransitionData unused — Imported but never applied in useSceneReactivity. Now used on connection updates and pending update queuing.

Documentation Pass

Added module-level docs and JSDoc to all core/public API files:

  • SceneManager, CameraControls, TransitionOrchestrator, ViewOrchestrator, GroupingStrategy
  • GroupRug (extracted magic numbers to named constants)
  • GroupItem (documented skyline bin-packing algorithm)
  • LineFlow (documented LINE_CONFIG values with units)
  • LayoutEngine, useQMap

Design Project Integration

Updated /visualization/qmap.vue in ../design with:

  • 18 actors (apps, services, databases, vendors, agents, users)
  • Scenario switcher (Full / Small / Empty)
  • Grouping mode buttons (Flat / By Type / By Cloud / By Team)
  • Selection event display
  • Transition indicator

Known Debt (acceptable for v1)

  • Rug creation duplication between ViewOrchestrator and TransitionOrchestrator
  • Deep watch on large actor arrays — optimize with identity-based comparison if >500 actors
  • LayoutEngine not yet exposed as a public export (internal to composable)

Artifacts

Qpoint Brand Style Guide