Add the explicit breaker option instead of .next(true) protocol.
This commit is contained in:
parent
96d85d3164
commit
3bc89af23b
|
|
@ -1,6 +1,6 @@
|
|||
import { clamp } from "@common/utils";
|
||||
|
||||
export interface Point {
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
|
@ -21,16 +21,27 @@ interface BresenhamOptions {
|
|||
|
||||
export interface BresenhamLineOptions extends BresenhamOptions {}
|
||||
|
||||
export interface BresenhamCircleOptions<T = boolean | 'fov'> extends BresenhamOptions {
|
||||
interface BresenhamCircleBaseOptions<T extends boolean | 'fov'> extends BresenhamOptions {
|
||||
/**
|
||||
* - `false` (default) — outline only.
|
||||
* - `true` — filled disc (span-from-outline scanline).
|
||||
* - `'fov'` — ray-casting field of view; the generator accepts `true` via
|
||||
* `.next(true)` to signal an obstacle and skip the rest of that ray.
|
||||
* - `'fov'` — ray-casting field of view.
|
||||
*/
|
||||
fill?: T;
|
||||
}
|
||||
|
||||
type BresenhamCircleFillOptions = BresenhamCircleBaseOptions<boolean>;
|
||||
interface BresenhamCircleFovOptions extends BresenhamCircleBaseOptions<'fov'> {
|
||||
/**
|
||||
* A callback that is called for each point on the ray casted from the start
|
||||
* point to the current point. If the callback returns `true`, the ray is
|
||||
* terminated and the generator skips to the next ray.
|
||||
*/
|
||||
breaker?: (x: number, y: number) => boolean;
|
||||
}
|
||||
|
||||
type BresenhamCircleOptions = BresenhamCircleFillOptions | BresenhamCircleFovOptions;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cohen-Sutherland outcodes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -247,30 +258,16 @@ export const bresenhamLine = (
|
|||
*
|
||||
* ## FOV mode (`fill: 'fov'`) — ray-casting field of view
|
||||
*
|
||||
* For each outline point, casts an 8-connected Bresenham ray from the centre
|
||||
* to that point and yields every cell along the ray. The generator uses the
|
||||
* **send protocol**: the caller can pass `true` to `.next(true)` after
|
||||
* receiving a cell to signal an obstacle — the rest of that ray is then
|
||||
* For each outline point, casts an 4-connected Bresenham ray from the centre
|
||||
* to that point and yields every cell along the ray. You can pass `breaker`
|
||||
* to signal an obstacle — the rest of that ray is then
|
||||
* skipped and the next outline point's ray begins. The obstacle cell itself
|
||||
* is always yielded (it is visible; only the cells behind it are blocked).
|
||||
*
|
||||
* ```ts
|
||||
* const fov = bresenhamCircleGen(cx, cy, r, { fill: 'fov' });
|
||||
* let result = fov.next();
|
||||
* while (!result.done) {
|
||||
* const blocked = isWall(result.value);
|
||||
* result = fov.next(blocked); // pass true to stop this ray
|
||||
* }
|
||||
* ```
|
||||
* is not yielded (obstacle is hidden).
|
||||
*
|
||||
* **Bounds:** The outline is always generated without clipping so that rays
|
||||
* extend to the full circle perimeter. The individual rays are clipped to
|
||||
* the supplied bounds, so only in-bounds cells are yielded.
|
||||
*
|
||||
* **Coverage:** One ray is cast per outline point (`4r` rays total). At large
|
||||
* radii, the angular gap between adjacent rays may leave interior cells
|
||||
* uncovered. For precise FOV at large radii, prefer a shadowcasting algorithm.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* ## Clipping
|
||||
|
|
@ -284,25 +281,13 @@ export const bresenhamLine = (
|
|||
* yields only the centre point.
|
||||
* @param options - Optional `fill` flag and clip bounds.
|
||||
*/
|
||||
export function bresenhamCircleGen(
|
||||
x: number,
|
||||
y: number,
|
||||
r: number,
|
||||
options?: BresenhamCircleOptions<boolean>,
|
||||
): Generator<Point, void, void>;
|
||||
export function bresenhamCircleGen(
|
||||
x: number,
|
||||
y: number,
|
||||
r: number,
|
||||
options: BresenhamCircleOptions<'fov'>,
|
||||
): Generator<Point, void, boolean | void>;
|
||||
|
||||
export function* bresenhamCircleGen(
|
||||
x: number,
|
||||
y: number,
|
||||
r: number,
|
||||
options?: BresenhamCircleOptions,
|
||||
): Generator<Point, void, boolean | void> {
|
||||
): Generator<Point, void, void> {
|
||||
if (options?.fill === 'fov') {
|
||||
const lineBounds: BresenhamLineOptions = {
|
||||
directions: options.directions,
|
||||
|
|
@ -313,8 +298,8 @@ export function* bresenhamCircleGen(
|
|||
};
|
||||
for (const { x: ox, y: oy } of bresenhamCircleGen(x, y, r, { fill: false })) {
|
||||
for (const linePoint of bresenhamLineGen(x, y, ox, oy, lineBounds)) {
|
||||
const skipLine = yield linePoint;
|
||||
if (skipLine) break;
|
||||
if (options?.breaker?.(linePoint.x, linePoint.y)) break;
|
||||
yield linePoint;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
@ -403,5 +388,5 @@ export const bresenhamCircle = (
|
|||
x: number,
|
||||
y: number,
|
||||
r: number,
|
||||
options?: BresenhamCircleOptions<boolean>,
|
||||
options?: BresenhamCircleFillOptions,
|
||||
): Point[] => Array.from(bresenhamCircleGen(x, y, r, options));
|
||||
|
|
|
|||
Loading…
Reference in New Issue