fvtt-types
Version:
TypeScript type definitions for Foundry VTT
282 lines (236 loc) • 11.4 kB
text/typescript
import type { Identity, InexactPartial } from "#utils";
import type { Canvas } from "#client/canvas/_module.d.mts";
import type { Edge, PolygonVertex, CollisionResult } from "#client/canvas/geometry/edges/_module.d.mts";
import type { PointSourcePolygon, Ray } from "#client/canvas/geometry/_module.d.mts";
/**
* A PointSourcePolygon implementation that uses CCW (counter-clockwise) geometry orientation.
* Sweep around the origin, accumulating collision points based on the set of active walls.
* This algorithm was created with valuable contributions from https://github.com/caewok
*/
declare class ClockwiseSweepPolygon extends PointSourcePolygon {
/** @privateRemarks Necessary type override */
static override benchmark(
iterations: number,
origin: Canvas.PossiblyElevatedPoint,
config: ClockwiseSweepPolygon.Config,
): Promise<void>;
/** @privateRemarks Necessary type override */
static override create(origin: Canvas.Point, config: ClockwiseSweepPolygon.Config): ClockwiseSweepPolygon;
/** @privateRemarks Necessary type override */
static override testCollision<Mode extends PointSourcePolygon.CollisionModes | undefined = undefined>(
origin: Canvas.PossiblyElevatedPoint,
destination: Canvas.PossiblyElevatedPoint,
{ mode, ...config }: ClockwiseSweepPolygon.TestCollisionOptions<Mode>,
): PointSourcePolygon.TestCollision<Mode>;
/** @privateRemarks Necessary type override */
override config: ClockwiseSweepPolygon.StoredConfig;
/**
* A mapping of vertices which define potential collision points
*/
vertices: Map<number, PolygonVertex>;
/**
* The set of edges which define potential boundaries of the polygon
*/
edges: Set<Edge>;
/**
* A collection of rays which are fired at vertices
*/
rays: ClockwiseSweepPolygon.Ray[];
/**
* Is this polygon using inner bounds?
*/
get useInnerBounds(): boolean;
override initialize(origin: Canvas.PossiblyElevatedPoint, config: ClockwiseSweepPolygon.Config): void;
/**
* Determine the edge types and their manner of inclusion for this polygon instance.
* @param config - Optional polygon config which may include deprecated properties
* @remarks The deprecated properties referred to are {@linkcode ClockwiseSweepPolygon.Config.useInnerBounds | useInnerBounds}
* and {@linkcode ClockwiseSweepPolygon.Config.includeDarkness | includeDarkness}
*/
protected _determineEdgeTypes(
type: Edge.EdgeTypes,
priority: number,
config?: ClockwiseSweepPolygon.Config,
): ClockwiseSweepPolygon.EdgeTypesConfiguration;
clone(): this;
protected override _compute(): void;
/**
* Retrieves the super-set of walls that could potentially apply to this polygon.
* Utilizes a custom collision test and the Quadtree to obtain candidate edges efficiently.
*/
protected _identifyEdges(): void;
/**
* Test whether a wall should be included in the computed polygon for a given origin and type
* @param edge - The Edge being considered
* @param edgeTypes - Which types of edges are being used? 0=no, 1=maybe, 2=always
* @returns Should the edge be included?
* @remarks See {@linkcode ClockwiseSweepPolygon.Config.edgeTypes}
*/
protected _testEdgeInclusion(edge: Edge, edgeTypes: ClockwiseSweepPolygon.EdgeTypesConfiguration): boolean;
/**
* Compute the aggregate bounding box which is the intersection of all boundary shapes.
* Round and pad the resulting rectangle by 1 pixel to ensure it always contains the origin.
*/
protected _defineBoundingBox(): PIXI.Rectangle;
/**
* Consolidate all vertices from identified edges and register them as part of the vertex mapping.
*/
protected _identifyVertices(): void;
/**
* Add additional vertices for intersections between edges.
*/
protected _identifyIntersections(edgeMap: Map<string, Edge>): void;
/**
* Execute the sweep over wall vertices
*/
protected _executeSweep(): void;
/**
* Determine the initial set of active edges as those which intersect with the initial ray
* @returns A set of initially active edges
*/
protected _initializeActiveEdges(): Set<Edge>;
/**
* Sort vertices clockwise from the initial ray (due west).
* @returns The array of sorted vertices
*/
protected _sortVertices(): PolygonVertex[];
/**
* Test whether a target vertex is behind some closer active edge.
* If the vertex is to the left of the edge, is must be behind the edge relative to origin.
* If the vertex is collinear with the edge, it should be considered "behind" and ignored.
* We know edge.VertexA is ccw to edge.VertexB because of the logic in _identifyVertices.
* @param vertex - The target vertex
* @param activeEdges - The set of active edges
* @returns Is the target vertex behind some closer edge?
*/
protected _isVertexBehindActiveEdges(
vertex: PolygonVertex,
activeEdges: Set<Edge>,
): ClockwiseSweepPolygon.IsVertexBehindActiveEdges;
/**
* Determine the result for the sweep at a given vertex
* @param vertex - The target vertex
* @param activeEdges - The set of active edges
* @param hasCollinear - Are there collinear vertices behind the target vertex? (default: `false`)
*/
protected _determineSweepResult(vertex: PolygonVertex, activeEdges: Set<Edge>, hasCollinear?: boolean): void;
/**
* Switch to a new active edge.
* Moving from the origin, a collision that first blocks a side must be stored as a polygon point.
* Subsequent collisions blocking that side are ignored. Once both sides are blocked, we are done.
*
* Collisions that limit a side will block if that side was previously limited.
*
* If neither side is blocked and the ray internally collides with a non-limited edge, n skip without adding polygon
* endpoints. Sight is unaffected before this edge, and the internal collision can be ignored.
*
* @param result - The pending collision result
* @param activeEdges - The set of currently active edges
*/
protected _switchEdge(result: CollisionResult, activeEdges: Set<Edge>): void;
/** @remarks Does not take the new-as-of-v13 `destination` param from {@linkcode PointSourcePolygon._testCollision | super} (yet?) */
protected override _testCollision<Mode extends PointSourcePolygon.CollisionModes>(
ray: Ray,
mode: Mode,
): PointSourcePolygon.TestCollision<Mode>;
override visualize(): PIXI.Graphics;
/**
* Visualize the polygon, displaying its computed area, rays, and collision points
*/
protected _visualizeCollision(ray: Ray, collisions: PolygonVertex[]): void;
/**
* This function has been adapted from {@linkcode ClipperLib.Clipper.CleanPolygon | Clipper's CleanPolygon function}.
* When adding a new point to the polygon, check for collinearity with prior points to cull unnecessary points.
* This also removes spikes where we traverse points (a, b, a).
* We also enforce a minimum distance between two points, or a minimum perpendicular distance between three almost
* collinear points.
*/
override addPoint(point: PIXI.IPointData): this;
#ClockwiseSweepPolygon: true;
}
declare namespace ClockwiseSweepPolygon {
interface Any extends AnyClockwiseSweepPolygon {}
interface AnyConstructor extends Identity<typeof AnyClockwiseSweepPolygon> {}
/**
* CSP-added properties of the config that have defaults applied in {@linkcode ClockwiseSweepPolygon.initialize | ClockwiseSweepPolygon#initialize}
* @internal
*/
interface _Config {
/**
* Optional priority when it comes to ignore edges from darkness and light sources
* @defaultValue `0`
*/
priority: number;
/**
* Edge types configuration object. This is not required by most polygons and will be inferred based on the polygon type and priority.
*
* How modes are working:
* - `0` (no): The edges of this type are rejected and not processed (equivalent of not having an edgeType.)
* - `1` (maybe): The edges are processed and tested for inclusion.
* - `2` (always): The edges are automatically included.
* @defaultValue {@linkcode ClockwiseSweepPolygon._determineEdgeTypes | this._determineEdgeTypes(config.type, config.priority, config)}
*/
edgeTypes: EdgeTypesConfiguration;
/**
* The computed bounding box for the polygon
* @defaultValue {@linkcode ClockwiseSweepPolygon._defineBoundingBox | this._defineBoundingBox()}
* @privateRemarks Foundry includes this in the `PointSourcePolygonConfig` typedef, but it is only used in `ClockwiseSweepPolygon`
*/
boundingBox: PIXI.Rectangle;
}
/**
* CSP-added properties of the config that are not guaranteed to exist by {@linkcode ClockwiseSweepPolygon.initialize | ClockwiseSweepPolygon#initialize}
* @internal
*/
type _InexactConfig = InexactPartial<{
/**
* Deactivate/Activate specific edge types behaviors.
*/
edgeOptions: EdgeOptions;
/**
* @deprecated "`config.useInnerBounds` is now deprecated, replaced by {@linkcode ClockwiseSweepPolygon.Config.edgeTypes | edgeTypes}
* polygon configuration behaviors." (since v13, until v15)
*/
useInnerBounds: boolean;
/**
* @deprecated "`config.includeDarkness` is now deprecated, replaced by {@linkcode ClockwiseSweepPolygon.Config.edgeTypes | edgeTypes}
* polygon configuration behaviors." (since v13, until v15)
*/
includeDarkness: boolean;
}>;
interface Config extends PointSourcePolygon.Config, InexactPartial<_Config>, _InexactConfig {}
interface StoredConfig extends PointSourcePolygon.StoredConfig, _Config, _InexactConfig {}
interface TestCollisionConfig extends PointSourcePolygon._TestCollisionConfig, Omit<Config, "type"> {}
interface TestCollisionOptions<Mode extends PointSourcePolygon.CollisionModes | undefined = undefined>
extends PointSourcePolygon._TestCollisionOptions<Mode>,
TestCollisionConfig {}
/**
* @remarks See {@linkcode Config.edgeTypes}
*
* @privateRemarks Foundry never sets any keys to `0`, they're simply omitted, then tested for truthiness in
* {@linkcode ClockwiseSweepPolygon._testEdgeInclusion | #_testEdgeInclusion}.
*/
interface EdgeTypesConfiguration extends InexactPartial<Record<Edge.EdgeTypes, 0 | 1 | 2>> {}
/**
* @privateRemarks Entries are only checked for `===` or `!== false` in {@linkcode ClockwiseSweepPolygon._determineEdgeTypes | ClockwiseSweepPolygon#_determineEdgeTypes},
* so `undefined` is fine
*/
interface EdgeOptions extends InexactPartial<Record<Edge.EdgeTypes, boolean>> {}
interface IsVertexBehindActiveEdges {
isBehind: boolean;
wasLimited: boolean;
}
interface Ray extends foundry.canvas.geometry.Ray {
/**
* @remarks Only set in {@linkcode ClockwiseSweepPolygon._switchEdge | ClockwiseSweepPolygon#_switchEdge} and only consumed in
* {@linkcode ClockwiseSweepPolygon.visualize | #visualize}, despite unrelated method(s) having parameters claiming to want a `PolygonRay`
*
* Tested for truthiness in `#visualize` before use, so a nullish value is fine but never attains in core
*/
result?: CollisionResult | undefined;
}
}
export default ClockwiseSweepPolygon;
declare abstract class AnyClockwiseSweepPolygon extends ClockwiseSweepPolygon {
constructor(...args: never);
}