Point
Single coordinate for cell counting, nuclei detection, or markers.
{ type: "point", geometry: { x: 100, y: 200 } }Understanding these core concepts will help you work effectively with Annota.
An annotation is the fundamental unit in Annota. Each annotation contains:
interface Annotation { id: string; // Unique identifier shape: Shape; // Geometry (point, rectangle, polygon) properties?: object; // Custom metadata style?: AnnotationStyle; // Visual appearance layerId?: string; // Layer assignment}Annota supports three primary shape types:
Point
Single coordinate for cell counting, nuclei detection, or markers.
{ type: "point", geometry: { x: 100, y: 200 } }Rectangle
Axis-aligned bounding box for ROI selection.
{ type: "rectangle", geometry: { x: 0, y: 0, width: 100, height: 50 } }Polygon
Arbitrary polygon for tumor boundaries, segmentation.
{ type: "polygon", geometry: { points: [{x: 0, y: 0}, ...] } }All coordinates are in image space (pixels), not viewport space. This means:
Layers organize annotations into logical groups with independent controls:
interface Layer { id: string; name: string; visible: boolean; // Show/hide all annotations opacity: number; // 0-1 transparency locked: boolean; // Prevent editing style?: AnnotationStyle; // Default style for layer}Every annotation belongs to a layer. If no layer is specified, annotations go to the default layer.
Tools define how users interact with the viewer to create or modify annotations:
| Tool | Creates | Interaction |
|---|---|---|
PointTool | Point annotations | Single click |
RectangleTool | Rectangle annotations | Click and drag |
PolygonTool | Polygon annotations | Click vertices, double-click to finish |
PushTool | N/A (edits polygons) | Click and drag to push/pull vertices |
ContourTool | Polygon annotations | Click to auto-detect contours |
SAMTool | Polygon annotations | Click for AI segmentation |
import { useTool, PolygonTool } from "annota";
function MyComponent({ viewer }) { const polygonTool = new PolygonTool();
useTool({ viewer, handler: polygonTool, enabled: true, // Toggle to activate/deactivate });
return null;}Selection represents the currently selected annotations. Selected annotations:
selectionChanged eventsimport { useSelection } from "annota";
function SelectionInfo() { const selection = useSelection();
return <div>Selected: {selection.length} annotations</div>;}Annota uses an event-driven architecture for loose coupling:
annotator.on("createAnnotation", (annotation) => { console.log("Created:", annotation);});
annotator.on("updateAnnotation", (annotation) => { console.log("Updated:", annotation);});
annotator.on("deleteAnnotation", (annotation) => { console.log("Deleted:", annotation);});
annotator.on("selectionChanged", ({ selected, added, removed }) => { console.log("Selection changed");});| Event | When Fired | Data |
|---|---|---|
createAnnotation | New annotation created | Annotation |
updateAnnotation | Annotation modified | Annotation |
deleteAnnotation | Annotation deleted | Annotation |
selectionChanged | Selection changes | { selected, added, removed } |
hoverChanged | Hover state changes | Annotation | null |
toolChanged | Active tool changes | Tool | null |
Every annotation can have custom visual styles:
interface AnnotationStyle { fill?: string; // Fill color (CSS color) fillOpacity?: number; // Fill transparency (0-1) stroke?: string; // Stroke color strokeWidth?: number; // Stroke width in pixels strokeOpacity?: number; // Stroke transparency}Styles are applied in this order (later overrides earlier):
Annota uses a sophisticated rendering pipeline for performance:
Annotations → Spatial Index → Viewport Culling → LOD → Caching → PixiJS → WebGLNow that you understand the core concepts: