# domain/converters/learning.fragment.converter.ts

> 175 lines of code and 45 definitions.

Tree: Site tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/tree#file-domain-converters-learning-fragment-converter-ts
Source text: https://banes-lab.com/assets/sources/source.a8317b578d4da175be072d8ca2a48634606339cbfae347604d1cb0bb0bb0e21e.generated.txt

## Definitions

- `routeBetween` (lexical_declaration, line 78)
- `straighten` (lexical_declaration, line 33)
- `isCollinear` (lexical_declaration, line 21)
- `anchored` (lexical_declaration, line 49)
- `doorFor` (lexical_declaration, line 96)
- `orthogonal` (lexical_declaration, line 167, exported)
- `leaveAt` (lexical_declaration, line 116)
- `tracePoints` (lexical_declaration, line 145, exported)
- `Walker` (interface_declaration, line 5)
- `Leg` (interface_declaration, line 11)
- `level` (lexical_declaration, line 29)
- `trimmed` (lexical_declaration, line 35)
- `held` (lexical_declaration, line 57)
- `[first, head]` (lexical_declaration, line 58)
- `tail` (lexical_declaration, line 66)
- `last` (lexical_declaration, line 67)
- `start` (lexical_declaration, line 83)
- `goal` (lexical_declaration, line 84)
- `came` (lexical_declaration, line 85)
- `walked` (lexical_declaration, line 89)
- `legFor` (lexical_declaration, line 100)
- `dx` (lexical_declaration, line 101)
- `dy` (lexical_declaration, line 102)
- `{ dx, dy, here, stop }` (lexical_declaration, line 121)
- `climbs` (lexical_declaration, line 122)
- `blockedDoor` (lexical_declaration, line 126)
- `leaves` (lexical_declaration, line 127)
- `enterAt` (lexical_declaration, line 131)
- `{ dx, dy, next, there }` (lexical_declaration, line 135)
- `gate` (lexical_declaration, line 136)
- `flatIn` (lexical_declaration, line 137)
- `points` (lexical_declaration, line 149, exported)
- `entered` (lexical_declaration, line 150, exported)
- `next` (lexical_declaration, line 152, exported)
- `leg` (lexical_declaration, line 157, exported)
- `out` (lexical_declaration, line 158, exported)
- `into` (lexical_declaration, line 159, exported)
- `lands` (lexical_declaration, line 160, exported)
- `[first]` (lexical_declaration, line 171, exported)
- `straight` (lexical_declaration, line 172, exported)
- `index` (lexical_declaration, line 173, exported)
- `before` (lexical_declaration, line 174, exported)
- `here` (lexical_declaration, line 175, exported)
- `across` (lexical_declaration, line 180, exported)
- `down` (lexical_declaration, line 181, exported)

## Uses

- [domain/converters/learning.grid.converter.ts](https://banes-lab.com/source/tree/domain/converters/learning.grid.converter.ts.md)

## Source

```typescript
import { CHANNEL, GRID_STEP } from "#configuration/constants/learning.constants";
import type { LaidBlock, LearningGrid, LearningPoint } from "#types/learning.types";
import { cellAt, isSolid, searchGrid, walkBack } from "#domain/converters/learning.grid.converter";

interface Walker {
    readonly block: LaidBlock;
    readonly cx: number;
    readonly cy: number;
}

interface Leg {
    readonly dx: number;
    readonly dy: number;
    readonly flat: boolean;
    readonly here: LaidBlock;
    readonly stop: Walker;
    readonly there: LaidBlock;
    readonly next: Walker;
}

const isCollinear = function isCollinear(
    before: LearningPoint | undefined,
    here: LearningPoint,
    after: LearningPoint | undefined,
): boolean {
    if (before === undefined || after === undefined) {
        return false;
    }
    const level = before.y === here.y && here.y === after.y;
    return (before.x === here.x && here.x === after.x) || level;
};

const straighten = function straighten(walked: readonly LearningPoint[]): LearningPoint[] {
    const [first] = walked;
    const trimmed: LearningPoint[] = first === undefined ? [] : [first];
    for (let index = 1; index < walked.length - 1; index += 1) {
        const here = walked[index];
        if (here !== undefined && !isCollinear(walked[index - 1], here, walked[index + 1])) {
            trimmed.push(here);
        }
    }
    const last = walked.at(-1);
    if (last !== undefined) {
        trimmed.push(last);
    }
    return trimmed;
};

const anchored = function anchored(
    trimmed: readonly LearningPoint[],
    from: LearningPoint,
    to: LearningPoint,
): LearningPoint[] {
    if (trimmed.length < 3) {
        return [...trimmed];
    }
    const held = trimmed.map((point) => ({ ...point }));
    const [first, head] = held;
    if (head !== undefined && first !== undefined) {
        if (first.y === head.y) {
            head.y = from.y;
        } else {
            head.x = from.x;
        }
    }
    const tail = held.at(-2);
    const last = held.at(-1);
    if (tail !== undefined && last !== undefined) {
        if (last.y === tail.y) {
            tail.y = to.y;
        } else {
            tail.x = to.x;
        }
    }
    return held;
};

const routeBetween = function routeBetween(
    grid: LearningGrid,
    from: LearningPoint,
    to: LearningPoint,
): readonly LearningPoint[] {
    const start = cellAt(grid, from);
    const goal = cellAt(grid, to);
    const came = searchGrid(grid, start, goal);
    if (came[goal] === -1) {
        return [from, { x: to.x, y: from.y }, to];
    }
    const walked = walkBack(grid, came, start, goal).map((cell) => ({
        x: (cell % grid.wide) * GRID_STEP,
        y: Math.floor(cell / grid.wide) * GRID_STEP,
    }));
    return anchored(straighten(walked), from, to);
};

const doorFor = function doorFor(here: LaidBlock, cy: number, rightward: boolean): LearningPoint {
    return { x: rightward ? here.x + here.width + CHANNEL : here.x - CHANNEL, y: cy };
};

const legFor = function legFor(grid: LearningGrid, stop: Walker, next: Walker): Leg {
    const dx = next.cx - stop.cx;
    const dy = next.cy - stop.cy;
    const here = stop.block;
    const blockedDoor = isSolid(grid, doorFor(here, stop.cy, dx >= 0));
    return {
        dx,
        dy,
        flat: Math.abs(dx) >= Math.abs(dy) || (dx !== 0 && !blockedDoor),
        here,
        next,
        stop,
        there: next.block,
    };
};

const leaveAt = function leaveAt(
    grid: LearningGrid,
    leg: Leg,
    entered: ReadonlyMap<LaidBlock, boolean | null>,
): LearningPoint {
    const { dx, dy, here, stop } = leg;
    const climbs = dy < 0 ? stop.cy > here.cy : stop.cy < here.cy;
    if (!leg.flat && !climbs) {
        return { x: stop.cx, y: dy < 0 ? here.y - CHANNEL : here.y + here.height + CHANNEL };
    }
    const blockedDoor = isSolid(grid, doorFor(here, stop.cy, dx >= 0));
    const leaves = blockedDoor || entered.get(here) === dx >= 0 ? dx < 0 : dx >= 0;
    return doorFor(here, stop.cy, leaves);
};

const enterAt = function enterAt(
    grid: LearningGrid,
    leg: Leg,
): { readonly flatIn: boolean; readonly point: LearningPoint } {
    const { dx, dy, next, there } = leg;
    const gate = { x: dx >= 0 ? there.x - CHANNEL : there.x + there.width + CHANNEL, y: next.cy };
    const flatIn = leg.flat || (dx !== 0 && !isSolid(grid, gate));
    const lands = dy > 0 ? next.cy > there.cy : next.cy < there.cy;
    if (flatIn || lands) {
        return { flatIn, point: gate };
    }
    return { flatIn, point: { x: next.cx, y: dy > 0 ? there.y - CHANNEL : there.y + there.height + CHANNEL } };
};

export const tracePoints = function tracePoints(
    grid: LearningGrid,
    stops: readonly Walker[],
): readonly LearningPoint[] {
    const points: LearningPoint[] = [];
    const entered = new Map<LaidBlock, boolean | null>();
    for (const [index, stop] of stops.entries()) {
        const next = stops[index + 1];
        points.push({ x: stop.cx, y: stop.cy });
        if (next === undefined || next.block === stop.block) {
            continue;
        }
        const leg = legFor(grid, stop, next);
        const out = leaveAt(grid, leg, entered);
        const into = enterAt(grid, leg);
        const lands = leg.dy > 0 ? next.cy > leg.there.cy : next.cy < leg.there.cy;
        entered.set(leg.there, into.flatIn || lands ? leg.dx < 0 : null);
        points.push(out, ...routeBetween(grid, out, into.point).slice(1, -1), into.point);
    }
    return points;
};

export const orthogonal = function orthogonal(
    grid: LearningGrid,
    points: readonly LearningPoint[],
): readonly LearningPoint[] {
    const [first] = points;
    const straight: LearningPoint[] = first === undefined ? [] : [first];
    for (let index = 1; index < points.length; index += 1) {
        const before = straight.at(-1);
        const here = points[index];
        if (before === undefined || here === undefined) {
            continue;
        }
        if (before.x !== here.x && before.y !== here.y) {
            const across = { x: here.x, y: before.y };
            const down = { x: before.x, y: here.y };
            straight.push(isSolid(grid, across) && !isSolid(grid, down) ? down : across);
        }
        straight.push(here);
    }
    return straight;
};
```
