# core/analyzers/panel.analyzer.ts

> 53 lines of code and 16 definitions.

Tree: Site tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/tree#file-core-analyzers-panel-analyzer-ts
Source text: https://banes-lab.com/assets/sources/source.1b764a0cf609c0abcd17e47dff5dd1fb8316e2a04c7453cd47bc4794701d62f1.generated.txt

## Definitions

- `extentOf` (lexical_declaration, line 12, exported)
- `clamp` (lexical_declaration, line 8)
- `contentExtentOf` (lexical_declaration, line 17, exported)
- `boundedView` (lexical_declaration, line 22, exported)
- `zoomedView` (lexical_declaration, line 32, exported)
- `fittedView` (lexical_declaration, line 38, exported)
- `centerOf` (lexical_declaration, line 54, exported)
- `distanceBetween` (lexical_declaration, line 58, exported)
- `midpointOf` (lexical_declaration, line 62, exported)
- `ORIGIN_VIEW` (lexical_declaration, line 4, exported)
- `HALF` (lexical_declaration, line 6)
- `rect` (lexical_declaration, line 18, exported)
- `spanX` (lexical_declaration, line 23, exported)
- `spanY` (lexical_declaration, line 24, exported)
- `ratio` (lexical_declaration, line 34, exported)
- `scale` (lexical_declaration, line 42, exported)

## Used by

- [presentation/components/stage.component.ts](https://banes-lab.com/source/tree/presentation/components/stage.component.ts.md)

## Source

```typescript
import type { PanelExtent as Extent, PanelView, PanelPoint as Point } from "#types/panel.types";
import { PANEL_MAX_SCALE, PANEL_MIN_SCALE } from "#configuration/constants/panel.constants";

export const ORIGIN_VIEW: PanelView = { scale: 1, x: 0, y: 0 };

const HALF = 2;

const clamp = function clamp(value: number, low: number, high: number): number {
    return Math.min(Math.max(value, low), high);
};

export const extentOf = function extentOf(element: Element): Extent {
    const rect = element.getBoundingClientRect();
    return { height: rect.height, width: rect.width };
};

export const contentExtentOf = function contentExtentOf(stage: Element, scale: number): Extent {
    const rect = extentOf(stage);
    return { height: rect.height / scale, width: rect.width / scale };
};

export const boundedView = function boundedView(view: PanelView, viewport: Extent, content: Extent): PanelView {
    const spanX = viewport.width - content.width * view.scale;
    const spanY = viewport.height - content.height * view.scale;
    return {
        scale: view.scale,
        x: clamp(view.x, Math.min(0, spanX), Math.max(0, spanX)),
        y: clamp(view.y, Math.min(0, spanY), Math.max(0, spanY)),
    };
};

export const zoomedView = function zoomedView(view: PanelView, factor: number, anchor: Point): PanelView {
    const scale = clamp(view.scale * factor, PANEL_MIN_SCALE, PANEL_MAX_SCALE);
    const ratio = scale / view.scale;
    return { scale, x: anchor.x - (anchor.x - view.x) * ratio, y: anchor.y - (anchor.y - view.y) * ratio };
};

export const fittedView = function fittedView(viewport: Extent, content: Extent, ceiling = 1): PanelView {
    if (content.width === 0 || content.height === 0) {
        return ORIGIN_VIEW;
    }
    const scale = clamp(
        Math.min(viewport.width / content.width, viewport.height / content.height, ceiling),
        PANEL_MIN_SCALE,
        ceiling,
    );
    return {
        scale,
        x: (viewport.width - content.width * scale) / HALF,
        y: (viewport.height - content.height * scale) / HALF,
    };
};

export const centerOf = function centerOf(extent: Extent): Point {
    return { x: extent.width / HALF, y: extent.height / HALF };
};

export const distanceBetween = function distanceBetween(a: Point, b: Point): number {
    return Math.hypot(a.x - b.x, a.y - b.y);
};

export const midpointOf = function midpointOf(a: Point, b: Point): Point {
    return { x: (a.x + b.x) / HALF, y: (a.y + b.y) / HALF };
};
```
