# Why a Facing Arc?

When a token turns, it’s helpful—especially in tactical or role-playing scenarios—to see exactly which direction it’s “looking.” A subtle 20º arc projected in front of the token can communicate much more clearly than a bare rotation value.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1749615175062/ec814bde-61e1-4eb8-84ac-30705cef41b0.png align="center")

But how do I hook into Foundry’s event system, check permissions, and still keep my code clean and loosely coupled?

## FoundryHookAdapter (Infrastructure Layer)

Detects raw Foundry events and converts them to domain events. By hiding Foundry specifics here, the rest of the app remains blissfully ignorant of Foundry’s peculiarities.

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Bear in mind that when Foundry fires the <code>updateToken</code> hook, the Token’s <code>document</code> hasn’t yet been fully updated. If you read <code>document.rotation</code> at that moment, you may still get the <em>old</em> value. Always rely first on the <code>changedState.rotation</code> from the hook payload—and only fall back to <code>document.rotation</code> as a default if no change was provided.</div>
</div>

```typescript
// When a token is updated in Foundry:
handleTokenUpdate() {
  // Captures the update from Foundry's updateToken hook
  // Creates a TokenUpdateEvent with rotation data
  eventBus.emit('token:update', {
    tokenId: document.id,
    currentState: { rotation: 45, x: 100, y: 100, ... },
    changedState: { rotation: 90 },  // New rotation
    userId: 'abc123'
  });
}
```

## TokenRotationCoordinator (Application Layer)

Listens for token updates and coordinates overlay rendering when rotation changes.

1. **Detect rotation change**
    
2. **Fetch overlay types** that respond to token rotation
    
3. **Check who’s allowed** to see them
    
4. **Build a render context** (dimensions, angles, positions)
    
5. **Fire off render requests**
    

```typescript
// Subscribed to 'token:update' events
handleTokenUpdate(event: TokenUpdateEvent) {
  // Check if rotation changed
  if (event.changedState.rotation !== undefined) {
    // Get overlays that respond to rotation (like facing-arc)
    const rotationOverlays = overlayRegistry.getTypesForEvent('tokenRotate');
    
    // Check permissions - can this user see overlays on this token?
    const viewableOverlays = await permissionCoordinator.getViewableOverlaysForToken();
    
    // Build render context with rotation data
    const context: OverlayRenderContext = {
      rotation: {
        currentRotation: 45,
        newRotation: 90,
        tokenXCoordinate: 100,
        tokenYCoordinate: 100,
        tokenWidth: 50,
        tokenHeight: 50
      }
    };
```

## OverlayRenderingService (Presentation Layer)

Manages the PixiJS graphics containers and coordinates rendering.

* Retrieves the overlay definition (`FacingArcDefinition`)
    
* Instantiates—or reuses—a `PIXI.Graphics` object
    
* Positions it at the token’s centre
    
* Hands off drawing to the renderer
    

```typescript
renderTokenOverlay(overlayTypeId: 'facing-arc', tokenId: string, context: OverlayRenderContext) {
  // Get the overlay definition from registry
  const overlayDef = overlayRegistry.get('facing-arc'); // Returns FacingArcDefinition
  
  // Get the renderer from the definition
  const renderer = overlayDef.getRenderer(); // Returns FacingArcRenderer instance
  
  // Get or create PIXI graphics object for this token+overlay
  const graphics = getOrCreateGraphicsInstance(`${tokenId}-facing-arc`);
  
  // Position graphics at token centre
  const centerX = context.rotation.tokenXCoordinate + context.rotation.tokenWidth / 2;
  const centerY = context.rotation.tokenYCoordinate + context.rotation.tokenHeight / 2;
  graphics.position.set(centerX, centerY);
  
  // Delegate actual drawing to the renderer
  renderer.render(graphics, context);
}
```

## FacingArcDefinition (Infrastructure Layer)

Defines the facing arc overlay configuration.

```typescript
export const FacingArcDefinition: OverlayDefinition = {
  id: 'facing-arc',
  renderTriggers: ['tokenRotate', 'tokenMove'],
};
```

Because it lives in a registry, adding new overlays later (boundary, aura, custom shapes…) is just a matter of dropping in another definition.

## FacingArcRenderer (Presentation Layer)

Actually draws the facing arc using PixiJS.

```typescript
render(graphics: PIXI.Graphics, context: OverlayRenderContext) {
  // Clear previous drawing
  graphics.clear();
  
  // Get rotation from context
  const rotation = context.rotation?.currentRotation ?? 0;
  const radius = Math.max(context.rotation.tokenWidth, context.rotation.tokenHeight) / 2;
  
  // Calculate arc angles (20 degree arc)
  const arcAngle = 20 * Math.PI / 180;
  const facingAngle = ((rotation - 90) * (Math.PI / 180));
  const startAngle = facingAngle - arcAngle / 2;
  const endAngle = facingAngle + arcAngle / 2;
  
  // Draw the arc line
  graphics.lineStyle({
    width: 8,
    color: 0x8A6A1C,
    alpha: 0.8,
    cap: PIXI.LINE_CAP.ROUND
  });
  
  graphics.arc(0, 0, radius, startAngle, endAngle, false);
}
```

## Complete Flow Sequence

1. **User** rotates token in Foundry UI
    
2. **Foundry** fires `updateToken` hook
    
3. **FoundryHookAdapter** catches hook, emits `'token:update'` event
    
4. **TokenRotationCoordinator** receives event, checks if rotation changed
    
5. **Coordinator** queries `OverlayRegistry` for overlays with `'tokenRotate'` trigger
    
6. **Coordinator** checks permissions via `OverlayPermissionCoordinator`
    
7. **Coordinator** builds render context with rotation data
    
8. **Coordinator** calls `overlayRenderingService.renderTokenOverlay()`
    
9. **OverlayRenderingService** gets `FacingArcDefinition` from registry
    
10. **Service** calls `definition.getRenderer()` to get `FacingArcRenderer`
    
11. **Service** creates/positions PIXI `Graphics` object
    
12. **Service** calls `renderer.render(graphics, context)`
    
13. **FacingArcRenderer** draws the arc at the new rotation angle
    
14. **Facing arc** appears on screen at token’s new rotation
    

## Key Design Patterns

* **Event-Driven**: Loose coupling via an EventBus
    
* **Clean Architecture**: Clear separation between infrastructure, application, and presentation layers
    
* **Strategy Pattern**: Different renderers implement a common interface for each overlay type
    
* **Registry Pattern**: Dynamic registration and lookup of overlay definitions
    
* **Permission System**: Fine-grained control over who can see each overlay
    

This modular architecture makes it trivial to extend—simply drop in a new overlay definition and renderer, and the core flow remains untouched. Happy coding!
