Skip to content

Architecture

Annota follows a clean, layered architecture that separates concerns for maintainability and performance.

┌─────────────────────────────────────────────────────┐
│ React Layer │
│ Components, Hooks, Context │
├─────────────────────────────────────────────────────┤
│ Core Engine │
│ Store, R-tree Index, Layer Manager, Events │
├─────────────────────────────────────────────────────┤
│ Rendering Engine │
│ PixiJS WebGL, Viewport Culling, LOD, Caching │
├─────────────────────────────────────────────────────┤
│ OpenSeadragon Adapter │
│ Viewport Integration, Coordinate Transform │
└─────────────────────────────────────────────────────┘

The React layer provides the developer-facing API:

  • AnnotaProvider: Context provider for annotation state
  • AnnotaViewer: OpenSeadragon viewer wrapper
  • Annotator: Annotation overlay component
  • Hooks: useAnnotator, useAnnotations, useTool, etc.

The core engine manages annotation data and state:

The store holds all annotation data and emits events on changes:

store.on("createAnnotation", handler);
store.on("updateAnnotation", handler);
store.on("deleteAnnotation", handler);

Sub-millisecond spatial queries using an R-tree:

// Find annotations at point
const hits = spatialIndex.query({ x: 100, y: 200 });
// Find annotations in bounds
const inView = spatialIndex.queryBounds({ x: 0, y: 0, width: 1000, height: 1000 });

Manages annotation layers with visibility, opacity, and locking:

layerManager.createLayer({ id: "tumor", name: "Tumor Regions" });
layerManager.updateLayer("tumor", { visible: false });

High-performance rendering with PixiJS:

Only renders annotations visible in the current viewport:

// On viewport change
const visible = spatialIndex.queryBounds(viewportBounds);
renderer.render(visible);

Simplifies rendering when zoomed out:

  • High zoom: Full detail with all vertices
  • Medium zoom: Simplified polygons
  • Low zoom: Bounding boxes or points

Graphics are cached and only re-rendered when:

  • Annotation data changes
  • LOD level changes
  • Significant scale change

Bridges OpenSeadragon and the annotation system:

Converts between image coordinates and viewport coordinates:

// Image → Viewport
const viewportPoint = adapter.imageToViewport(imagePoint);
// Viewport → Image
const imagePoint = adapter.viewportToImage(viewportPoint);

Intercepts OpenSeadragon events for annotation tools:

adapter.on("pointerdown", handlePointerDown);
adapter.on("pointermove", handlePointerMove);
adapter.on("pointerup", handlePointerUp);

Key optimizations:

  1. R-tree indexing — O(log n) spatial queries
  2. Viewport culling — Only render what’s visible
  3. LOD rendering — Simplify when zoomed out
  4. Graphics caching — Reuse PixiJS graphics objects
  5. Batch rendering — Combine draw calls
  6. WebGL acceleration — Hardware-accelerated rendering