Repository: League-of-Foundry-Developers/foundry-vtt-types
Pull request #3049 (2025-01-31 08:54:16)

Touchup `client/pixi/extensions` and associated `ClipperLib` and `WeilerAthertonClipper` files
- as title
- add tests
- actually make static extentions static
- refactor interfaces and overloads for `intersectPolygon`, removing the need for `RemoveComments`


Patch:
diff --git a/src/foundry/client/pixi/extensions/circle.d.mts b/src/foundry/client/pixi/extensions/circle.d.mts
index 39ad745f1..9e81154ff 100644
--- a/src/foundry/client/pixi/extensions/circle.d.mts
+++ b/src/foundry/client/pixi/extensions/circle.d.mts
@@ -1,140 +1,143 @@
 import type { NullishProps } from "fvtt-types/utils";
 import type { LineCircleIntersection } from "../../../common/utils/geometry.d.mts";
+import * as _PIXI from "pixi.js";
 
-declare module "pixi.js" {
-  interface Circle {
-    /**
-     * Determine the center of the circle.
-     * Trivial, but used to match center method for other shapes.
-     */
-    get center(): PIXI.Point;
-
-    /**
-     * Determine if a point is on or nearly on this circle.
-     * @param point   - Point to test
-     * @param epsilon - Tolerated margin of error
-     *                  (default: `1e-08`)
-     * @returns Is the point on the circle within the allowed tolerance?
-     */
-    pointIsOn(point: Canvas.Point, epsilon: number): boolean;
-
-    /**
-     * Get all intersection points on this circle for a segment A|B
-     * Intersections are sorted from A to B.
-     * @param a - The first endpoint on segment A|B
-     * @param b - The second endpoint on segment A|B
-     * @returns Points where the segment A|B intersects the circle
-     */
-    segmentIntersections(a: Canvas.Point, b: Canvas.Point): LineCircleIntersection["intersections"];
-
-    /**
-     * Calculate an x,y point on this circle's circumference given an angle
-     * 0: due east
-     * π / 2: due south
-     * π or -π: due west
-     * -π/2: due north
-     * @param angle - Angle of the point, in radians
-     * @returns The point on the circle at the given angle
-     */
-    pointAtAngle(angle: number): Canvas.Point;
-
-    /**
-     * Get all the points for a polygon approximation of this circle between two points.
-     * The two points can be anywhere in 2d space. The intersection of this circle with the line from this circle center
-     * to the point will be used as the start or end point, respectively.
-     * This is used to draw the portion of the circle (the arc) between two intersection points on this circle.
-     * @param a       - Point in 2d space representing the start point
-     * @param b       - Point in 2d space representing the end point
-     * @param options - Options passed on to the pointsForArc method
-     * @returns An array of points arranged clockwise from start to end
-     */
-    pointsBetween(a: Canvas.Point, b: Canvas.Point, options?: Circle.PointsForArcOptions): Canvas.Point[];
-
-    /**
-     * Get the points that would approximate a circular arc along this circle, given a starting and ending angle.
-     * Points returned are clockwise. If from and to are the same, a full circle will be returned.
-     * @param fromAngle - Starting angle, in radians. π is due north, π/2 is due east
-     * @param toAngle   - Ending angle, in radians
-     * @param options   - Options which affect how the circle is converted
-     * @returns An array of points along the requested arc
-     */
-    pointsForArc(fromAngle: number, toAngle: number, options?: Circle.PointsForArcOptions): Canvas.Point[];
-
-    /**
-     * Approximate this PIXI.Circle as a PIXI.Polygon
-     * @param options - Options forwarded on to the pointsForArc method
-     * @returns The Circle expressed as a PIXI.Polygon
-     */
-    toPolygon(options?: Circle.PointsForArcOptions): PIXI.Polygon;
-
-    /**
-     * The recommended vertex density for the regular polygon approximation of a circle of a given radius.
-     * Small radius circles have fewer vertices. The returned value will be rounded up to the nearest integer.
-     * See the formula described at:
-     * https://math.stackexchange.com/questions/4132060/compute-number-of-regular-polgy-sides-to-approximate-circle-to-defined-precision
-     * @param radius  - Circle radius
-     * @param epsilon - The maximum tolerable distance between an approximated line segment and the true radius.
-     *                  A larger epsilon results in fewer points for a given radius. (default: `1`)
-     * @returns The number of points for the approximated polygon
-     */
-    approximateVertexDensity(radius: number, epsilon?: number): number;
-
-    /**
-     * Intersect this PIXI.Circle with a PIXI.Polygon.
-     * @param polygon - A PIXI.Polygon
-     * @param options - Options which configure how the intersection is computed
-     * @returns The intersected polygon
-     */
-    intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Circle.WACIntersectPolygonOptions): PIXI.Polygon;
-    intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Circle.ClipperLibIntersectPolygonOptions): PIXI.Polygon;
-    intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Circle.IntersectClipperOptions): PIXI.Polygon;
-
-    /**
-     * Intersect this PIXI.Circle with an array of ClipperPoints.
-     * Convert the circle to a Polygon approximation and use intersectPolygon.
-     * In the future we may replace this with more specialized logic which uses the line-circle intersection formula.
-     * @param clipperPoints - Array of ClipperPoints generated by PIXI.Polygon.toClipperPoints()
-     * @param options       - Options which configure how the intersection is computed
-     * @returns The intersected polygon
-     */
-    intersectClipper(
-      clipperPoints: PIXI.Polygon.ClipperPoint[],
-      options?: PIXI.Circle.IntersectClipperOptions,
-    ): PIXI.Polygon;
-  }
+declare global {
+  namespace PIXI {
+    interface Circle {
+      /**
+       * Determine the center of the circle.
+       * Trivial, but used to match center method for other shapes.
+       */
+      get center(): PIXI.Point;
 
-  namespace Circle {
-    interface WACIntersectPolygonOptions extends PIXI.Rectangle.WACIntersectPolygonOptions {}
+      /**
+       * Determine if a point is on or nearly on this circle.
+       * @param point   - Point to test
+       * @param epsilon - Tolerated margin of error
+       *                  (default: `1e-08`)
+       * @returns Is the point on the circle within the allowed tolerance?
+       */
+      pointIsOn(point: Canvas.Point, epsilon?: number): boolean;
 
-    /** @privateRemarks Property description intentionally omitted to avoid redundant intellisense on overloaded method */
-    interface ClipperLibIntersectPolygonOptions extends PIXI.Rectangle.ClipperLibIntersectPolygonOptions {
-      density: number;
-    }
+      /**
+       * Get all intersection points on this circle for a segment A|B
+       * Intersections are sorted from A to B.
+       * @param a - The first endpoint on segment A|B
+       * @param b - The second endpoint on segment A|B
+       * @returns Points where the segment A|B intersects the circle
+       */
+      segmentIntersections(a: Canvas.Point, b: Canvas.Point): LineCircleIntersection["intersections"];
 
-    /** @privateRemarks Property description intentionally omitted to avoid redundant intellisense on overloaded method */
-    interface IntsectPolygonOptions extends PIXI.Rectangle.IntersectPolygonOptions {
-      density: number;
-    }
+      /**
+       * Calculate an x,y point on this circle's circumference given an angle
+       * 0: due east
+       * π / 2: due south
+       * π or -π: due west
+       * -π/2: due north
+       * @param angle - Angle of the point, in radians
+       * @returns The point on the circle at the given angle
+       */
+      pointAtAngle(angle: number): Canvas.Point;
 
-    /** @internal Helper type to simplify use of optionality- and nullish-permissiveness-modifying helpers */
-    type _PointsForArcOptions = NullishProps<{
       /**
-       * The number of points which defines the density of approximation
-       * @defaultValue `this.constructor.approximateVertexDensity(this.radius)`
+       * Get all the points for a polygon approximation of this circle between two points.
+       * The two points can be anywhere in 2d space. The intersection of this circle with the line from this circle center
+       * to the point will be used as the start or end point, respectively.
+       * This is used to draw the portion of the circle (the arc) between two intersection points on this circle.
+       * @param a       - Point in 2d space representing the start point
+       * @param b       - Point in 2d space representing the end point
+       * @param options - Options passed on to the pointsForArc method
+       * @returns An array of points arranged clockwise from start to end
        */
-      density: number;
+      pointsBetween(a: Canvas.Point, b: Canvas.Point, options?: Circle.PointsForArcOptions): Canvas.Point[];
 
       /**
-       * Whether to include points at the circle where the arc starts and ends
-       * @defaultValue `true`
+       * Get the points that would approximate a circular arc along this circle, given a starting and ending angle.
+       * Points returned are clockwise. If from and to are the same, a full circle will be returned.
+       * @param fromAngle - Starting angle, in radians. π is due north, π/2 is due east
+       * @param toAngle   - Ending angle, in radians
+       * @param options   - Options which affect how the circle is converted
+       * @returns An array of points along the requested arc
        */
-      includeEndpoints: boolean;
-    }>;
+      pointsForArc(fromAngle: number, toAngle: number, options?: Circle.PointsForArcOptions): Canvas.Point[];
 
-    interface PointsForArcOptions extends _PointsForArcOptions {}
+      /**
+       * Approximate this PIXI.Circle as a PIXI.Polygon
+       * @param options - Options forwarded on to the pointsForArc method
+       * @returns The Circle expressed as a PIXI.Polygon
+       */
+      toPolygon(options?: Circle.PointsForArcOptions): PIXI.Polygon;
 
-    interface IntersectClipperOptions
-      extends PIXI.Polygon.IntersectClipperOptions,
-        Pick<PIXI.Circle.PointsForArcOptions, "density"> {}
+      /**
+       * Intersect this PIXI.Circle with a PIXI.Polygon.
+       * @param polygon - A PIXI.Polygon
+       * @param options - Options which configure how the intersection is computed
+       * @returns The intersected polygon
+       * @remarks Slightly bugged in v12; if `options.weilerAtherton` is falsey, it will not pass on `options.clipType` to `PIXI.Polygon#intersectPolygon`,
+       * forcing the default of `ClipperLib.ClipTypes.ctIntersection`
+       */
+      intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Circle.WACIntersectPolygonOptions): PIXI.Polygon;
+      intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Circle.ClipperLibIntersectPolygonOptions): PIXI.Polygon;
+
+      /**
+       * Intersect this PIXI.Circle with an array of ClipperPoints.
+       * Convert the circle to a Polygon approximation and use intersectPolygon.
+       * In the future we may replace this with more specialized logic which uses the line-circle intersection formula.
+       * @param clipperPoints - Array of ClipperPoints generated by PIXI.Polygon.toClipperPoints()
+       * @param options       - Options which configure how the intersection is computed
+       * @returns The intersected polygon
+       */
+      intersectClipper(
+        clipperPoints: PIXI.Polygon.ClipperPoint[],
+        options?: PIXI.Circle.IntersectClipperOptions,
+      ): PIXI.Polygon;
+    }
+
+    namespace Circle {
+      /** @remarks The options for `intersectPolygon` when `weilerAtherton` is true (or not provided) */
+      interface WACIntersectPolygonOptions extends PIXI.Rectangle.WACIntersectPolygonOptions {}
+
+      /** @remarks The options for `intersectPolygon` when `weilerAtherton` is false. */
+      interface ClipperLibIntersectPolygonOptions
+        extends PIXI.Rectangle.ClipperLibIntersectPolygonOptions,
+          Pick<PIXI.Circle.PointsForArcOptions, "density"> {}
+
+      /** @internal Helper type to simplify use of optionality- and nullish-permissiveness-modifying helpers */
+      type _PointsForArcOptions = NullishProps<{
+        /**
+         * The number of points which defines the density of approximation
+         * @defaultValue `PIXI.Circle.approximateVertexDensity(this.radius)`
+         * @remarks Default provided by `??=` in `pointsForArc` function body
+         */
+        density: number;
+
+        /**
+         * Whether to include points at the circle where the arc starts and ends
+         * @defaultValue `true`
+         */
+        includeEndpoints: boolean;
+      }>;
+
+      interface PointsForArcOptions extends _PointsForArcOptions {}
+
+      interface IntersectClipperOptions
+        extends PIXI.Polygon.IntersectClipperOptions,
+          Pick<PIXI.Circle.PointsForArcOptions, "density"> {}
+    }
+
+    class Circle extends _PIXI.Circle {
+      /**
+       * The recommended vertex density for the regular polygon approximation of a circle of a given radius.
+       * Small radius circles have fewer vertices. The returned value will be rounded up to the nearest integer.
+       * See the formula described at:
+       * https://math.stackexchange.com/questions/4132060/compute-number-of-regular-polgy-sides-to-approximate-circle-to-defined-precision
+       * @param radius  - Circle radius
+       * @param epsilon - The maximum tolerable distance between an approximated line segment and the true radius.
+       *                  A larger epsilon results in fewer points for a given radius. (default: `1`)
+       * @returns The number of points for the approximated polygon
+       */
+      static approximateVertexDensity(radius: number, epsilon?: number): number;
+    }
   }
 }
diff --git a/src/foundry/client/pixi/extensions/graphics.d.mts b/src/foundry/client/pixi/extensions/graphics.d.mts
index 067d953c1..39d182961 100644
--- a/src/foundry/client/pixi/extensions/graphics.d.mts
+++ b/src/foundry/client/pixi/extensions/graphics.d.mts
@@ -3,7 +3,9 @@ export {};
 declare global {
   namespace PIXI {
     namespace Graphics {
-      type Path = [number[] | PIXI.IPointData[] | PIXI.Polygon] | Array<number | PIXI.IPointData>;
+      type Path =
+        | [points: number[] | PIXI.IPointData[] | PIXI.Polygon, smoothingFactor?: number]
+        | Array<number | PIXI.IPointData>;
     }
   }
 }
diff --git a/src/foundry/client/pixi/extensions/observable-transform.d.mts b/src/foundry/client/pixi/extensions/observable-transform.d.mts
index a5dfcddd1..dc3044389 100644
--- a/src/foundry/client/pixi/extensions/observable-transform.d.mts
+++ b/src/foundry/client/pixi/extensions/observable-transform.d.mts
@@ -1,11 +1,11 @@
-import type { AnyObject } from "fvtt-types/utils";
+export {};
 
 declare global {
   /**
    * A custom Transform class allowing to observe changes with a callback.
    * @privateRemarks Scope extends object is intentional; `"The scope bound to an ObservableTransform class must be a valid object/class."`
    */
-  class ObservableTransform<CB extends (this: Scope) => unknown, Scope extends AnyObject> extends PIXI.Transform {
+  class ObservableTransform<CB extends (this: Scope) => unknown, Scope extends object> extends PIXI.Transform {
     /**
      *
      * @param callback - The callback called to observe changes.
@@ -28,6 +28,7 @@ declare global {
     protected override updateSkew(): void;
   }
   namespace ObservableTransform {
+    interface Any extends AnyObservableTransform {}
     type AnyConstructor = typeof AnyObservableTransform;
   }
 }
diff --git a/src/foundry/client/pixi/extensions/polygon.d.mts b/src/foundry/client/pixi/extensions/polygon.d.mts
index e0ba373c4..947b0bd44 100644
--- a/src/foundry/client/pixi/extensions/polygon.d.mts
+++ b/src/foundry/client/pixi/extensions/polygon.d.mts
@@ -1,143 +1,154 @@
 import type { InexactPartial, NullishProps } from "fvtt-types/utils";
+import * as _PIXI from "pixi.js";
 
-declare module "pixi.js" {
-  interface Polygon {
-    /**
-     * Test whether the polygon is has a positive signed area.
-     * Using a y-down axis orientation, this means that the polygon is "clockwise".
-     */
-    get isPositive(): boolean;
-
-    /**
-     * @remarks Non enumerable
-     */
-    _isPositive?: boolean;
-
-    /**
-     * Clear the cached signed orientation.
-     */
-    clearCache(): void;
-
-    /**
-     * Compute the signed area of polygon using an approach similar to ClipperLib.Clipper.Area.
-     * The math behind this is based on the Shoelace formula. https://en.wikipedia.org/wiki/Shoelace_formula.
-     * The area is positive if the orientation of the polygon is positive.
-     * @returns The signed area of the polygon
-     */
-    signedArea(): number;
-
-    /**
-     * Reverse the order of the polygon points in-place, replacing the points array into the polygon.
-     * Note: references to the old points array will not be affected.
-     * @returns This polygon with its orientation reversed
-     */
-    reverseOrientation(): this;
-
-    /**
-     * Add a de-duplicated point to the Polygon.
-     * @param point - The point to add to the Polygon
-     * @returns A reference to the polygon for method chaining
-     */
-    addPoint(point: Canvas.Point): this;
-
-    /**
-     * Return the bounding box for a PIXI.Polygon.
-     * The bounding rectangle is normalized such that the width and height are non-negative.
-     * @returns The bounding PIXI.Rectangle
-     */
-    getBounds(): PIXI.Rectangle;
-
-    /**
-     * Construct a PIXI.Polygon instance from an array of clipper points [\{X,Y\}, ...].
-     * @param points  - An array of points returned by clipper
-     * @param options - Options which affect how canvas points are generated
-     * @param scalingFactor -
-     * @returns The resulting PIXI.Polygon
-     */
-    fromClipperPoints(
-      points: PIXI.Polygon.ClipperPoint[],
-      options?: Pick<PIXI.Polygon.IntersectClipperOptions, "scalingFactor">,
-    ): PIXI.Polygon;
-
-    /**
-     * Convert a PIXI.Polygon into an array of clipper points [\{X,Y\}, ...].
-     * Note that clipper points must be rounded to integers.
-     * In order to preserve some amount of floating point precision, an optional scaling factor may be provided.
-     * @param options - Options which affect how clipper points are generated
-     * @returns An array of points to be used by clipper
-     */
-    toClipperPoints(options?: Pick<PIXI.Polygon.IntersectClipperOptions, "scalingFactor">): PIXI.Polygon.ClipperPoint[];
-
-    /**
-     * Determine whether the PIXI.Polygon is closed, defined by having the same starting and ending point.
-     * @remarks Non-enumerable
-     */
-    get isClosed(): boolean;
-
-    /**
-     * Intersect this PIXI.Polygon with another PIXI.Polygon using the clipper library.
-     * @param other   - Another PIXI.Polygon
-     * @param options - Options which configure how the intersection is computed
-     * @returns The intersected polygon
-     */
-    intersectPolygon(other: PIXI.Polygon, options?: PIXI.Polygon.IntersectClipperOptions): PIXI.Polygon;
-
-    /**
-     * Intersect this PIXI.Polygon with an array of ClipperPoints.
-     * @param clipperPoints - Array of clipper points generated by PIXI.Polygon.toClipperPoints()
-     * @param options       - Options which configure how the intersection is computed
-     */
-    intersectClipper(
-      clipperPoints: PIXI.Polygon.ClipperPoint[],
-      options?: PIXI.Polygon.IntersectClipperOptions,
-    ): PIXI.Polygon.ClipperPoint[];
-
-    /**
-     * Intersect this PIXI.Polygon with a PIXI.Circle.
-     * For now, convert the circle to a Polygon approximation and use intersectPolygon.
-     * In the future we may replace this with more specialized logic which uses the line-circle intersection formula.
-     * @param circle  - A PIXI.Circle
-     * @param options - Options which configure how the intersection is computed
-     * @returns The intersected polygon
-     */
-    intersectCircle(circle: PIXI.Circle, options?: PIXI.Circle.IntersectClipperOptions): PIXI.Polygon;
-
-    /**
-     * Intersect this PIXI.Polygon with a PIXI.Rectangle.
-     * For now, convert the rectangle to a Polygon and use intersectPolygon.
-     * In the future we may replace this with more specialized logic which uses the line-line intersection formula.
-     * @param rect    - A PIXI.Rectangle
-     * @param options - Options which configure how the intersection is computed
-     * @returns The intersected polygon
-     */
-    intersectRectangle(rect: PIXI.Rectangle, options?: PIXI.Rectangle.IntersectPolygonOptions): PIXI.Polygon;
-  }
+declare global {
+  namespace PIXI {
+    interface Polygon {
+      /**
+       * Test whether the polygon is has a positive signed area.
+       * Using a y-down axis orientation, this means that the polygon is "clockwise".
+       */
+      get isPositive(): boolean;
 
-  namespace Polygon {
-    /** @privateRemarks Foundry uses this type instead of full `ClipperLib.IntPoint` objects */
-    interface ClipperPoint {
-      X: number;
-      Y: number;
-    }
+      /**
+       * @remarks Non enumerable
+       */
+      _isPositive?: boolean;
+
+      /**
+       * Clear the cached signed orientation.
+       */
+      clearCache(): void;
+
+      /**
+       * Compute the signed area of polygon using an approach similar to ClipperLib.Clipper.Area.
+       * The math behind this is based on the Shoelace formula. https://en.wikipedia.org/wiki/Shoelace_formula.
+       * The area is positive if the orientation of the polygon is positive.
+       * @returns The signed area of the polygon
+       */
+      signedArea(): number;
+
+      /**
+       * Reverse the order of the polygon points in-place, replacing the points array into the polygon.
+       * Note: references to the old points array will not be affected.
+       * @returns This polygon with its orientation reversed
+       */
+      reverseOrientation(): this;
 
-    /** @internal Intermediary type to simplify use of optionality- and nullish-permissiveness-modifying helpers */
-    type _IntersectClipperOptions = InexactPartial<{
       /**
-       * A scaling factor passed to Polygon#toClipperPoints to preserve precision
-       * @remarks Can't be null as, where it has any, it only has defaults provided by `{scalingFactor=1}={}`
-       * @defaultValue `1`
+       * Add a de-duplicated point to the Polygon.
+       * @param point - The point to add to the Polygon
+       * @returns A reference to the polygon for method chaining
        */
-      scalingFactor: number;
-    }> &
-      NullishProps<{
+      addPoint(point: Canvas.Point): this;
+
+      /**
+       * Return the bounding box for a PIXI.Polygon.
+       * The bounding rectangle is normalized such that the width and height are non-negative.
+       * @returns The bounding PIXI.Rectangle
+       */
+      getBounds(): PIXI.Rectangle;
+
+      /**
+       * Convert a PIXI.Polygon into an array of clipper points [\{X,Y\}, ...].
+       * Note that clipper points must be rounded to integers.
+       * In order to preserve some amount of floating point precision, an optional scaling factor may be provided.
+       * @param options - Options which affect how clipper points are generated
+       * @returns An array of points to be used by clipper
+       */
+      toClipperPoints(options?: PIXI.Polygon.ClipperPointsOptions): PIXI.Polygon.ClipperPoint[];
+
+      /**
+       * Determine whether the PIXI.Polygon is closed, defined by having the same starting and ending point.
+       * @remarks Non-enumerable
+       */
+      get isClosed(): boolean;
+
+      /**
+       * Intersect this PIXI.Polygon with another PIXI.Polygon using the clipper library.
+       * @param other   - Another PIXI.Polygon
+       * @param options - Options which configure how the intersection is computed
+       * @returns The intersected polygon
+       */
+      intersectPolygon(other: PIXI.Polygon, options?: PIXI.Polygon.IntersectClipperOptions): PIXI.Polygon;
+
+      /**
+       * Intersect this PIXI.Polygon with an array of ClipperPoints.
+       * @param clipperPoints - Array of clipper points generated by PIXI.Polygon.toClipperPoints()
+       * @param options       - Options which configure how the intersection is computed
+       */
+      intersectClipper(
+        clipperPoints: PIXI.Polygon.ClipperPoint[],
+        options?: PIXI.Polygon.IntersectClipperOptions,
+      ): PIXI.Polygon.ClipperPoint[];
+
+      /**
+       * Intersect this PIXI.Polygon with a PIXI.Circle.
+       * For now, convert the circle to a Polygon approximation and use intersectPolygon.
+       * In the future we may replace this with more specialized logic which uses the line-circle intersection formula.
+       * @param circle  - A PIXI.Circle
+       * @param options - Options which configure how the intersection is computed
+       * @returns The intersected polygon
+       */
+      intersectCircle(circle: PIXI.Circle, options?: PIXI.Circle.IntersectClipperOptions): PIXI.Polygon;
+
+      /**
+       * Intersect this PIXI.Polygon with a PIXI.Rectangle.
+       * For now, convert the rectangle to a Polygon and use intersectPolygon.
+       * In the future we may replace this with more specialized logic which uses the line-line intersection formula.
+       * @param rect    - A PIXI.Rectangle
+       * @param options - Options which configure how the intersection is computed
+       * @returns The intersected polygon
+       */
+      intersectRectangle(rect: PIXI.Rectangle, options?: PIXI.Rectangle.WACIntersectPolygonOptions): PIXI.Polygon;
+      intersectRectangle(
+        rect: PIXI.Rectangle,
+        options?: PIXI.Rectangle.ClipperLibIntersectPolygonOptions,
+      ): PIXI.Polygon;
+    }
+
+    namespace Polygon {
+      /** @privateRemarks Foundry uses this type instead of full `ClipperLib.IntPoint` objects */
+      interface ClipperPoint {
+        X: number;
+        Y: number;
+      }
+
+      /** @internal  */
+      type _IntersectClipperOptions = InexactPartial<{
         /**
-         * The clipper clip type
-         * @remarks ClipperLib functions require a non-nullish `ClipperLib.ClipType` value, but the Foundry functions have `??=` guards for this.
-         * @defaultValue `ClipperLib.ClipType.ctIntersection`
+         * A scaling factor passed to Polygon#toClipperPoints to preserve precision
+         * @remarks Can't be `null` as, where it has any, it only has parameter defaults
+         * @defaultValue `1`
          */
-        clipType: ClipperLib.ClipType;
-      }>;
+        scalingFactor: number;
+      }> &
+        NullishProps<{
+          /**
+           * The clipper clip type
+           * @defaultValue `ClipperLib.ClipType.ctIntersection`
+           * @remarks ClipperLib functions require a non-nullish `ClipperLib.ClipType` value, but the Foundry functions have `??=` guards for this.
+           */
+          clipType: ClipperLib.ClipType;
+        }>;
+
+      interface IntersectClipperOptions extends _IntersectClipperOptions {}
+
+      interface ClipperPointsOptions extends Pick<PIXI.Polygon.IntersectClipperOptions, "scalingFactor"> {}
+    }
 
-    interface IntersectClipperOptions extends _IntersectClipperOptions {}
+    class Polygon extends _PIXI.Polygon {
+      /**
+       * Construct a PIXI.Polygon instance from an array of clipper points [\{X,Y\}, ...].
+       * @param points  - An array of points returned by clipper
+       * @param options - Options which affect how canvas points are generated
+       * @param scalingFactor -
+       * @returns The resulting PIXI.Polygon
+       */
+      static fromClipperPoints(
+        points: PIXI.Polygon.ClipperPoint[],
+        options?: PIXI.Polygon.ClipperPointsOptions,
+      ): PIXI.Polygon;
+    }
   }
 }
diff --git a/src/foundry/client/pixi/extensions/rectangle.d.mts b/src/foundry/client/pixi/extensions/rectangle.d.mts
index e490e70f2..7416fc16a 100644
--- a/src/foundry/client/pixi/extensions/rectangle.d.mts
+++ b/src/foundry/client/pixi/extensions/rectangle.d.mts
@@ -1,290 +1,274 @@
-import type { UnionToIntersection, Brand, InexactPartial, NullishProps } from "fvtt-types/utils";
+import type { Brand, InexactPartial } from "fvtt-types/utils";
+import * as _PIXI from "pixi.js";
 
-/**
- * Typically in a mapped type TypeScript associates your type to the original.
- * This means you keep modifiers (`readonly` and `?`), go to definition takes you
- * to the original type not the mapped type, and comments are inherited.
- *
- * This utility type disables all of that by effectively tricking TypeScript into
- * not associating them anymore. Writing `{ [_ in string as K]: ... }` is the primary
- * trick used in this type.
- *
- * The reason why this type is so verbose though is because it's trying to preserve
- * modifiers which means it has to check for all 4 possibilities:
- * - mutable and optional
- * - mutable and required
- * - readonly and optional
- * - readonly and required
- */
-type RemoveComments<T extends object> = UnionToIntersection<
-  {
-    [K in keyof T]-?: T extends { [_ in string as K]: unknown }
-      ? T extends { [_ in string as K]?: unknown }
-        ? { [_ in string as K]?: T[K] }
-        : { [_ in string as K]: T[K] }
-      : T extends { readonly [_ in string as K]?: unknown }
-        ? { readonly [_ in string as K]?: T[K] }
-        : { readonly [_ in string as K]: T[K] };
-  }[keyof T]
->;
+declare global {
+  namespace PIXI {
+    interface Rectangle {
+      /**
+       * Bit code labels splitting a rectangle into zones, based on the Cohen-Sutherland algorithm.
+       * See https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
+       *          left    central   right
+       * top      1001    1000      1010
+       * central  0001    0000      0010
+       * bottom   0101    0100      0110
+       */
+      CS_ZONES: PIXI.Rectangle.CS_Zones;
 
-declare module "pixi.js" {
-  interface Rectangle {
-    /**
-     * Bit code labels splitting a rectangle into zones, based on the Cohen-Sutherland algorithm.
-     * See https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
-     *          left    central   right
-     * top      1001    1000      1010
-     * central  0001    0000      0010
-     * bottom   0101    0100      0110
-     */
-    CS_ZONES: Record<
-      "INSIDE" | "LEFT" | "RIGHT" | "TOP" | "BOTTOM" | "TOPLEFT" | "TOPRIGHT" | "BOTTOMRIGHT" | "BOTTOMLEFT",
-      PIXI.Rectangle.CS_ZONES
-    >;
+      /**
+       * Calculate center of this rectangle.
+       */
+      get center(): Canvas.Point;
 
-    /**
-     * Calculate center of this rectangle.
-     */
-    get center(): Canvas.Point;
+      /**
+       * Return the bounding box for a PIXI.Rectangle.
+       * The bounding rectangle is normalized such that the width and height are non-negative.
+       */
+      getBounds(): PIXI.Rectangle;
 
-    /**
-     * Return the bounding box for a PIXI.Rectangle.
-     * The bounding rectangle is normalized such that the width and height are non-negative.
-     */
-    getBounds(): PIXI.Rectangle;
+      /**
+       * Determine if a point is on or nearly on this rectangle.
+       * @param p - Point to test
+       * @returns Is the point on the rectangle boundary?
+       */
+      pointIsOn(p: Canvas.Point): boolean;
 
-    /**
-     * Determine if a point is on or nearly on this rectangle.
-     * @param p - Point to test
-     * @returns Is the point on the rectangle boundary?
-     */
-    pointIsOn(p: Canvas.Point): boolean;
+      /**
+       * Calculate the rectangle Zone for a given point located around, on, or in the rectangle.
+       * See https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
+       * This differs from _getZone in how points on the edge are treated: they are not considered inside.
+       * @param point - A point to test for location relative to the rectangle
+       * @returns Which edge zone does the point belong to?
+       */
+      _getEdgeZone(point: Canvas.Point): PIXI.Rectangle.CS_ZONES;
+
+      /**
+       * Get all the points (corners) for a polygon approximation of a rectangle between two points on the rectangle.
+       * The two points can be anywhere in 2d space on or outside the rectangle.
+       * The starting and ending side are based on the zone of the corresponding a and b points.
+       * (See PIXI.Rectangle.CS_ZONES.)
+       * This is the rectangular version of PIXI.Circle.prototype.pointsBetween, and is similarly used
+       * to draw the portion of the shape between two intersection points on that shape.
+       * @param a - A point on or outside the rectangle, representing the starting position.
+       * @param b - A point on or outside the rectangle, representing the starting position.
+       * @returns Points returned are clockwise from start to end.
+       */
+      pointsBetween(a: Canvas.Point, b: Canvas.Point): Canvas.Point[];
 
-    /**
-     * Calculate the rectangle Zone for a given point located around, on, or in the rectangle.
-     * See https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
-     * This differs from _getZone in how points on the edge are treated: they are not considered inside.
-     * @param point - A point to test for location relative to the rectangle
-     * @returns Which edge zone does the point belong to?
-     */
-    _getEdgeZone(point: Canvas.Point): PIXI.Rectangle.CS_ZONES;
+      /**
+       * Get all intersection points for a segment A|B
+       * Intersections are sorted from A to B.
+       * @param a - Endpoint A of the segment
+       * @param b - Endpoint B of the segment
+       * @returns Array of intersections or empty if no intersection.
+       *  If A|B is parallel to an edge of this rectangle, returns the two furthest points on
+       *  the segment A|B that are on the edge.
+       *  The return object's t0 property signifies the location of the intersection on segment A|B.
+       *  This will be NaN if the segment is a point.
+       *  The return object's t1 property signifies the location of the intersection on the rectangle edge.
+       *  The t1 value is measured relative to the intersecting edge of the rectangle.
+       */
+      segmentIntersections(a: Canvas.Point, b: Canvas.Point): Canvas.Point[];
 
-    /**
-     * Get all the points (corners) for a polygon approximation of a rectangle between two points on the rectangle.
-     * The two points can be anywhere in 2d space on or outside the rectangle.
-     * The starting and ending side are based on the zone of the corresponding a and b points.
-     * (See PIXI.Rectangle.CS_ZONES.)
-     * This is the rectangular version of PIXI.Circle.prototype.pointsBetween, and is similarly used
-     * to draw the portion of the shape between two intersection points on that shape.
-     * @param a - A point on or outside the rectangle, representing the starting position.
-     * @param b - A point on or outside the rectangle, representing the starting position.
-     * @returns Points returned are clockwise from start to end.
-     */
-    pointsBetween(a: Canvas.Point, b: Canvas.Point): Canvas.Point[];
+      /**
+       * Compute the intersection of this Rectangle with some other Rectangle.
+       * @param other - Some other rectangle which intersects this one
+       * @returns The intersected rectangle
+       */
+      intersection(other: PIXI.Rectangle): PIXI.Rectangle;
 
-    /**
-     * Get all intersection points for a segment A|B
-     * Intersections are sorted from A to B.
-     * @param a - Endpoint A of the segment
-     * @param b - Endpoint B of the segment
-     * @returns Array of intersections or empty if no intersection.
-     *  If A|B is parallel to an edge of this rectangle, returns the two furthest points on
-     *  the segment A|B that are on the edge.
-     *  The return object's t0 property signifies the location of the intersection on segment A|B.
-     *  This will be NaN if the segment is a point.
-     *  The return object's t1 property signifies the location of the intersection on the rectangle edge.
-     *  The t1 value is measured relative to the intersecting edge of the rectangle.
-     */
-    segmentIntersections(a: Canvas.Point, b: Canvas.Point): Canvas.Point[];
+      /**
+       * Convert this PIXI.Rectangle into a PIXI.Polygon
+       * @returns The Rectangle expressed as a PIXI.Polygon
+       */
+      toPolygon(): PIXI.Polygon;
 
-    /**
-     * Compute the intersection of this Rectangle with some other Rectangle.
-     * @param other - Some other rectangle which intersects this one
-     * @returns The intersected rectangle
-     */
-    intersection(other: PIXI.Rectangle): PIXI.Rectangle;
+      /**
+       * Get the left edge of this rectangle.
+       * The returned edge endpoints are oriented clockwise around the rectangle.
+       */
+      get leftEdge(): PIXI.Rectangle.Edge;
 
-    /**
-     * Convert this PIXI.Rectangle into a PIXI.Polygon
-     * @returns The Rectangle expressed as a PIXI.Polygon
-     */
-    toPolygon(): PIXI.Rectangle;
+      /**
+       * Get the right edge of this rectangle.
+       * The returned edge endpoints are oriented clockwise around the rectangle.
+       */
+      get rightEdge(): PIXI.Rectangle.Edge;
 
-    /**
-     * Get the left edge of this rectangle.
-     * The returned edge endpoints are oriented clockwise around the rectangle.
-     */
-    get leftEdge(): {
-      A: Canvas.Point;
-      B: Canvas.Point;
-    };
+      /**
+       * Get the top edge of this rectangle.
+       * The returned edge endpoints are oriented clockwise around the rectangle.
+       */
+      get topEdge(): PIXI.Rectangle.Edge;
 
-    /**
-     * Get the right edge of this rectangle.
-     * The returned edge endpoints are oriented clockwise around the rectangle.
-     */
-    get rightEdge(): {
-      A: Canvas.Point;
-      B: Canvas.Point;
-    };
+      /**
+       * Get the bottom edge of this rectangle.
+       * The returned edge endpoints are oriented clockwise around the rectangle.
+       */
+      get bottomEdge(): PIXI.Rectangle.Edge;
 
-    /**
-     * Get the top edge of this rectangle.
-     * The returned edge endpoints are oriented clockwise around the rectangle.
-     */
-    get topEdge(): {
-      A: Canvas.Point;
-      B: Canvas.Point;
-    };
+      /**
+       * Calculate the rectangle Zone for a given point located around or in the rectangle.
+       * https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
+       *
+       * @param p - Point to test for location relative to the rectangle
+       */
+      _getZone(p: Canvas.Point): PIXI.Rectangle.CS_ZONES;
 
-    /**
-     * Get the bottom edge of this rectangle.
-     * The returned edge endpoints are oriented clockwise around the rectangle.
-     */
-    get bottomEdge(): {
-      A: Canvas.Point;
-      B: Canvas.Point;
-    };
+      /**
+       * Test whether a line segment AB intersects this rectangle.
+       * @param a       - The first endpoint of segment AB
+       * @param b       - The second endpoint of segment AB
+       * @param options - Options affecting the intersect test.
+       * @returns True if intersects.
+       */
+      lineSegmentIntersects(
+        a: Canvas.Point,
+        b: Canvas.Point,
+        options?: PIXI.Rectangle.LineSegmentIntersectsOptions,
+      ): boolean;
 
-    /**
-     * Calculate the rectangle Zone for a given point located around or in the rectangle.
-     * https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
-     *
-     * @param p - Point to test for location relative to the rectangle
-     */
-    _getZone(p: Canvas.Point): PIXI.Rectangle.CS_ZONES;
+      /**
+       * Intersect this PIXI.Rectangle with a PIXI.Polygon.
+       * @param polygon - A PIXI.Polygon
+       * @param options - Options which configure how the intersection is computed
+       * @returns The intersected polygon
+       */
+      intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Rectangle.WACIntersectPolygonOptions): PIXI.Polygon;
+      intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Rectangle.ClipperLibIntersectPolygonOptions): PIXI.Polygon;
 
-    /**
-     * Test whether a line segment AB intersects this rectangle.
-     * @param a       - The first endpoint of segment AB
-     * @param b       - The second endpoint of segment AB
-     * @param options - Options affecting the intersect test.
-     * @returns True if intersects.
-     */
-    lineSegmentIntersects(
-      a: Canvas.Point,
-      b: Canvas.Point,
-      /** @remarks Can't be NullishProps because `inside` only has a default via `{inside=false}` and it can be returned directly */
-      options?: InexactPartial<{
+      /**
+       * Intersect this PIXI.Rectangle with an array of ClipperPoints. Currently, uses the clipper library.
+       * In the future we may replace this with more specialized logic which uses the line-line intersection formula.
+       * @param clipperPoints - Array of ClipperPoints generated by PIXI.Polygon.toClipperPoints()
+       * @param options       - Options which configure how the intersection is computed
+       * @returns The intersected polygon
+       * @remarks Foundry has this typed as returning `PIXI.Polygon | null`, but this method will actually
+       * return an empty array if this rectangle's width or height are 0. This is almost certainly a bug.
+       */
+      intersectClipper(
+        clipperPoints: PIXI.Polygon.ClipperPoint[],
+        options?: PIXI.Polygon.IntersectClipperOptions,
+      ): PIXI.Polygon | [];
+
+      /**
+       * Determine whether some other Rectangle overlaps with this one.
+       * This check differs from the parent class Rectangle#intersects test because it is true for adjacency (zero area).
+       * @param other - Some other rectangle against which to compare
+       * @returns Do the rectangles overlap?
+       */
+      overlaps(other: PIXI.Rectangle): boolean;
+
+      /**
+       * Normalize the width and height of the rectangle in-place, enforcing that those dimensions be positive.
+       */
+      normalize(): PIXI.Rectangle;
+
+      /**
+       * Fits this rectangle around this rectangle rotated around the given pivot counterclockwise by the given angle in radians.
+       * @param radians - The angle of rotation.
+       * @param pivot   - An optional pivot point (normalized).
+       * @returns A new rotated rectangle
+       */
+      rotate(radians: number, pivot?: PIXI.Point): PIXI.Rectangle;
+    }
+
+    namespace Rectangle {
+      type CS_ZONES = Brand<number, "PIXI.Rectangle.CS_ZONES">;
+
+      interface CS_Zones {
+        INSIDE: 0x0000 & CS_ZONES;
+        LEFT: 0x0001 & CS_ZONES;
+        RIGHT: 0x0010 & CS_ZONES;
+        TOP: 0x1000 & CS_ZONES;
+        BOTTOM: 0x0100 & CS_ZONES;
+        TOPLEFT: 0x1001 & CS_ZONES;
+        TOPRIGHT: 0x1010 & CS_ZONES;
+        BOTTOMRIGHT: 0x0110 & CS_ZONES;
+        BOTTOMLEFT: 0x0101 & CS_ZONES;
+      }
+
+      interface Edge {
+        A: Canvas.Point;
+        B: Canvas.Point;
+      }
+
+      /** @internal */
+      type _LineSegmentIntersectsOptions = InexactPartial<{
         /**
          * If true, a line contained within the rectangle will return true
          * @defaultValue `false`
+         * @remarks Can't be `null` as it only has a parameter default
          */
         inside: boolean;
-      }>,
-    ): boolean;
-
-    /**
-     * Intersect this PIXI.Rectangle with a PIXI.Polygon.
-     * @param polygon - A PIXI.Polygon
-     * @param options - Options which configure how the intersection is computed
-     * @returns The intersected polygon
-     */
-    intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Rectangle.WACIntersectPolygonOptions): PIXI.Polygon;
-    intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Rectangle.ClipperLibIntersectPolygonOptions): PIXI.Polygon;
-    intersectPolygon(polygon: PIXI.Polygon, options?: PIXI.Rectangle.IntersectPolygonOptions): PIXI.Polygon;
-
-    /**
-     * Intersect this PIXI.Rectangle with an array of ClipperPoints. Currently, uses the clipper library.
-     * In the future we may replace this with more specialized logic which uses the line-line intersection formula.
-     * @param clipperPoints - Array of ClipperPoints generated by PIXI.Polygon.toClipperPoints()
-     * @param options       - Options which configure how the intersection is computed
-     * @returns The intersected polygon
-     * @remarks Foundry has this typed as returning `PIXI.Polygon | null`, but this method will actually
-     *          return an empty array if this rectangle's width or height are 0. I believe this is a bug.
-     */
-    intersectClipper(
-      clipperPoints: PIXI.Polygon.ClipperPoint[],
-      options?: PIXI.Polygon.IntersectClipperOptions,
-    ): PIXI.Polygon | [];
+      }>;
 
-    /**
-     * Determine whether some other Rectangle overlaps with this one.
-     * This check differs from the parent class Rectangle#intersects test because it is true for adjacency (zero area).
-     * @param other - Some other rectangle against which to compare
-     * @returns Do the rectangles overlap?
-     */
-    overlaps(other: PIXI.Rectangle): boolean;
+      /** Options affecting the intersect test. */
+      interface LineSegmentIntersectsOptions extends _LineSegmentIntersectsOptions {}
 
-    /**
-     * Normalize the width and height of the rectangle in-place, enforcing that those dimensions be positive.
-     */
-    normalize(): PIXI.Rectangle;
-
-    /**
-     * Fits this rectangle around this rectangle rotated around the given pivot counterclockwise by the given angle in radians.
-     * @param radians - The angle of rotation.
-     * @param pivot   - An optional pivot point (normalized).
-     * @returns A new rotated rectangle
-     */
-    rotate(radians: number, pivot?: PIXI.Point): PIXI.Rectangle;
-
-    /**
-     * Create normalized rectangular bounds given a rectangle shape and an angle of central rotation.
-     * @param x       - The top-left x-coordinate of the un-rotated rectangle
-     * @param y       - The top-left y-coordinate of the un-rotated rectangle
-     * @param width   - The width of the un-rotated rectangle
-     * @param height  - The height of the un-rotated rectangle
-     * @param radians - The angle of rotation about the center
-     * @param pivot   - An optional pivot point (if not provided, the pivot is the centroid)
-     * @param _outRect - (Internal)
-     * @returns The constructed rotated rectangle bounds
-     */
-    fromRotation(
-      x: number,
-      y: number,
-      width: number,
-      height: number,
-      radians: number,
-      pivot?: PIXI.Point,
-      _outRect?: PIXI.Rectangle,
-    ): PIXI.Rectangle;
-  }
+      /** @remarks The options for `intersectPolygon` when `weilerAtherton` is true (or not provided) */
+      interface WACIntersectPolygonOptions extends WeilerAthertonClipper._CombineOptions {
+        /**
+         * One of CLIP_TYPES
+         * @defaultValue `WeilerAthertonClipper.CLIP_TYPES.INTERSECT`
+         * @remarks Default provided by `??=` in function body
+         *
+         * The default is actually `ClipperLib.ClipType.ctIntersection`, but that and `.ctUnion` are
+         * equivalent to  `WeilerAthertonClipper.CLIP_TYPES.INTERSECT` and `.UNION` respectively at runtime
+         */
+        clipType?:
+          | WeilerAthertonClipper.CLIP_TYPES
+          | (typeof ClipperLib.ClipType)["ctIntersection" | "ctUnion"]
+          | undefined
+          | null;
 
-  namespace Rectangle {
-    type CS_ZONES = Brand<number, "PIXI.Rectangle.CS_ZONES">;
+        /**
+         * Use the Weiler-Atherton algorithm. Otherwise, use Clipper.
+         * @defaultValue `true`
+         */
+        weilerAtherton?: true;
+      }
 
-    /**
-     *  @privateRemarks The options for `intersectPolygon` when `weilerAtherton` is true (or not provided)
-     * Property descriptions have been omitted and `RemoveComments` is in use here to avoid redundant
-     * instellisense on overloaded method.
-     */
-    interface WACIntersectPolygonOptions
-      extends RemoveComments<Omit<WeilerAthertonClipper.CombineOptions, keyof WeilerAthertonClipper.ClipOpts>> {
-      weilerAtherton?: true;
-    }
+      /** @remarks The options for `intersectPolygon` when `weilerAtherton` is false. */
+      interface ClipperLibIntersectPolygonOptions extends PIXI.Polygon.ClipperPointsOptions {
+        /**
+         * One of CLIP_TYPES
+         * @defaultValue `ClipperLib.ClipType.ctIntersection`
+         * @remarks Default provided by `??=` in function body
+         *
+         * `WeilerAthertonClipper.CLIP_TYPES.INTERSECT` and `.UNION` are equivalent to
+         * `ClipperLib.ClipType.ctIntersection` and `.ctUnion` respectively at runtime
+         */
+        clipType?: ClipperLib.ClipType | WeilerAthertonClipper.CLIP_TYPES | null | undefined;
 
-    /**
-     * @privateRemarks The options for `intersectPolygon` when `weilerAtherton` is false.
-     * Property descriptions have been omitted and `RemoveComments` is in use here to avoid redundant
-     * instellisense on overloaded method.
-     */
-    interface ClipperLibIntersectPolygonOptions extends RemoveComments<PIXI.Polygon.IntersectClipperOptions> {
-      weilerAtherton: false;
+        /**
+         * Use the Weiler-Atherton algorithm. Otherwise, use Clipper.
+         * @defaultValue `true`
+         */
+        weilerAtherton: false;
+      }
     }
 
-    /** @internal Intermediary type to simplify use of optionality- and nullish-permissiveness-modifying helpers */
-    type _IntersectPolygonOptions = NullishProps<{
+    class Rectangle extends _PIXI.Rectangle {
       /**
-       * The clipper clip type
-       * @remarks If `weilerAtherton` is truthy or not provided, this must be one of the two `WeilerAthertonClipper.CLIP_TYPES`.
-       * However, the first two entries in `ClipperLib.ClipType` map 1:1 with the two `CLIP_TYPES`, so the main restriction is
-       * that a combination of a truthy `weilerAtherton` and a `clipType` that is either `ClipperLib.ClipTypes.ctDifference` or
-       * `.ctXor` is not allowed.
-       * @defaultValue `ClipperLib.ClipType.ctIntersection` (equivalent to `WeilerAthertonClipper.CLIP_TYPES.INTERSECTION`)
+       * Create normalized rectangular bounds given a rectangle shape and an angle of central rotation.
+       * @param x       - The top-left x-coordinate of the un-rotated rectangle
+       * @param y       - The top-left y-coordinate of the un-rotated rectangle
+       * @param width   - The width of the un-rotated rectangle
+       * @param height  - The height of the un-rotated rectangle
+       * @param radians - The angle of rotation about the center
+       * @param pivot   - An optional pivot point (if not provided, the pivot is the centroid)
+       * @param _outRect - (Internal)
+       * @returns The constructed rotated rectangle bounds
        */
-      clipType: ClipperLib.ClipType | WeilerAthertonClipper.CLIP_TYPES;
-
-      /**
-       * Use the Weiler-Atherton algorithm. Otherwise, use Clipper.
-       * @defaultValue `true`
-       * */
-      weilerAtherton: boolean;
-    }> &
-      Pick<PIXI.Polygon.IntersectClipperOptions, "scalingFactor"> &
-      Pick<WeilerAthertonClipper.CombineOptions, "canMutate">;
-
-    interface IntersectPolygonOptions extends _IntersectPolygonOptions {}
+      static fromRotation(
+        x: number,
+        y: number,
+        width: number,
+        height: number,
+        radians: number,
+        pivot?: PIXI.Point,
+        _outRect?: PIXI.Rectangle,
+      ): PIXI.Rectangle;
+    }
   }
 }
diff --git a/src/foundry/client/pixi/perception/weiler-atherton-clipping.d.mts b/src/foundry/client/pixi/perception/weiler-atherton-clipping.d.mts
index c1033dedb..14c1eefb2 100644
--- a/src/foundry/client/pixi/perception/weiler-atherton-clipping.d.mts
+++ b/src/foundry/client/pixi/perception/weiler-atherton-clipping.d.mts
@@ -15,6 +15,9 @@ declare global {
    * https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm
    * https://www.geeksforgeeks.org/weiler-atherton-polygon-clipping-algorithm
    * https://h-educate.in/weiler-atherton-polygon-clipping-algorithm/
+   *
+   * @throws If `!Polygon.isPositive`
+   * @remarks In practice the only thing that uses the constructor is the class's static methods
    */
   class WeilerAthertonClipper {
     /**
@@ -37,14 +40,12 @@ declare global {
      * The supported clip types.
      * Values are equivalent to those in ClipperLib.ClipType.
      */
-    static CLIP_TYPES: Readonly<Record<"INTERSECT" | "UNION", WeilerAthertonClipper.CLIP_TYPES>>;
+    static CLIP_TYPES: WeilerAthertonClipper.ClipTypes;
 
     /**
      * The supported intersection types.
      */
-    static INTERSECTION_TYPES: Readonly<
-      Record<"OUT_IN" | "IN_OUT" | "TANGENT", WeilerAthertonClipper.INTERSECTION_TYPES>
-    >;
+    static INTERSECTION_TYPES: WeilerAthertonClipper.IntersectionTypes;
 
     polygon: PIXI.Polygon;
 
@@ -52,14 +53,8 @@ declare global {
 
     /**
      * Configuration settings
-     * @param clipType  - One of CLIP_TYPES (default: `WeilerAthertonClipper.CLIP_TYPES.INTERSECT`)
-     * @param clipOpts  - Object passed to the clippingObject methods toPolygon and pointsBetween
-     *                    (default: `{}`)
      */
-    config: {
-      clipType: WeilerAthertonClipper.CLIP_TYPES;
-      clipOpts: WeilerAthertonClipper.ClipOpts;
-    };
+    config: WeilerAthertonClipper.Config;
 
     /**
      * Union a polygon and clipObject using the Weiler Atherton algorithm.
@@ -126,13 +121,47 @@ declare global {
   }
 
   namespace WeilerAthertonClipper {
+    interface Any extends AnyWeilerAthertonClipper {}
     type AnyConstructor = typeof AnyWeilerAthertonClipper;
 
     type CLIP_TYPES = Brand<number, "WeilerAthertonClipper.CLIP_TYPES">;
 
+    interface ClipTypes {
+      readonly INTERSECT: 0 & CLIP_TYPES;
+      readonly UNION: 1 & CLIP_TYPES;
+    }
+
     type INTERSECTION_TYPES = Brand<number, "WeilerAthertonClipper.INTERSECTION_TYPES">;
 
-    /** @internal Helper type to simplify use of optionality- and nullish-permissiveness-modifying helpers */
+    interface IntersectionTypes {
+      readonly OUT_IN: -1 & INTERSECTION_TYPES;
+      readonly IN_OUT: 1 & INTERSECTION_TYPES;
+      readonly TANGENT: 0 & INTERSECTION_TYPES;
+    }
+
+    /** Configuration settings */
+    interface Config {
+      /**
+       * One of CLIP_TYPES
+       * @defaultValue `WeilerAthertonClipper.CLIP_TYPES.INTERSECT`
+       * @remarks Set to the value of the equivalent constructor parameter. Default provided by `??=` in the constructor body
+       */
+      clipType: WeilerAthertonClipper.CLIP_TYPES;
+
+      /**
+       * Object passed to the clippingObject methods `toPolygon` and `pointsBetween`
+       * @defaultValue `{}`
+       * @remarks Set to the value of the equivalent constructor parameter. Default provided by `??=` in the constructor body
+       */
+      clipOpts: WeilerAthertonClipper.ClipOpts;
+    }
+
+    /**
+     * @internal
+     * @privateRemarks does *not* contain:
+     * - a `scalingFactor` property, despite one being passed in `PIXI.Rectangle#intersectPolygon` in v12
+     * - a `density` property, despite one being passed in `PIXI.Circle#intersectPolygon` in v12
+     */
     type _CombineOptions = NullishProps<{
       /**
        * If the WeilerAtherton constructor could mutate or not the subject polygon points
diff --git a/src/foundry/index.d.mts b/src/foundry/index.d.mts
index be910d321..4a37cad84 100644
--- a/src/foundry/index.d.mts
+++ b/src/foundry/index.d.mts
@@ -1,5 +1,4 @@
 import "./client/index.d.mts";
 import "./client-esm/client.d.mts";
-import "./clipper/index.d.mts";
 import "./common/prosemirror/index.d.mts";
 import "./public/index.d.mts";
diff --git a/src/foundry/clipper/clipper.d.mts b/src/foundry/public/scripts/clipper/clipper.d.mts
similarity index 94%
rename from src/foundry/clipper/clipper.d.mts
rename to src/foundry/public/scripts/clipper/clipper.d.mts
index 28c9b5b11..7c119f8f3 100644
--- a/src/foundry/clipper/clipper.d.mts
+++ b/src/foundry/public/scripts/clipper/clipper.d.mts
@@ -164,25 +164,54 @@ declare global {
     }
 
     type ClipType = Brand<number, "ClipperLib.ClipType">;
-    const ClipType: Record<"ctIntersection" | "ctUnion" | "ctDifference" | "ctXor", ClipType>;
+    const ClipType: {
+      ctIntersection: 0 & ClipType;
+      ctUnion: 1 & ClipType;
+      ctDifference: 2 & ClipType;
+      ctXor: 3 & ClipType;
+    };
 
     type PolyType = Brand<number, "ClipperLib.PolyType">;
-    const PolyType: Record<"ptSubject" | "ptClip", PolyType>;
+    const PolyType: {
+      ptSubject: 0 & PolyType;
+      ptClip: 1 & PolyType;
+    };
 
     type PolyFillType = Brand<number, "ClipperLib.PolyFillType">;
-    const PolyFillType: Record<"pftEvenOdd" | "pftNonZero" | "pftPositive" | "pftNegative", PolyFillType>;
+    const PolyFillType: {
+      pftEvenOdd: 0 & PolyFillType;
+      pftNonZero: 1 & PolyFillType;
+      pftPositive: 2 & PolyFillType;
+      pftNegative: 3 & PolyFillType;
+    };
 
     type JoinType = Brand<number, "ClipperLib.JoinType">;
-    const JoinType: Record<"jtSquare" | "jtRound" | "jtMiter", JoinType>;
+    const JoinType: {
+      jtSquare: 0 & JoinType;
+      jtRound: 1 & JoinType;
+      jtMiter: 2 & JoinType;
+    };
 
     type EndType = Brand<number, "ClipperLib.EndType">;
-    const EndType: Record<"etOpenSquare" | "etOpenRound" | "etOpenButt" | "etClosedLine" | "etClosedPolygon", EndType>;
+    const EndType: {
+      etOpenSquare: 0 & EndType;
+      etOpenRound: 1 & EndType;
+      etOpenButt: 2 & EndType;
+      etClosedLine: 3 & EndType;
+      etClosedPolygon: 4 & EndType;
+    };
 
     type EdgeSide = Brand<number, "ClipperLib.EdgeSide">;
-    const EdgeSide: Record<"esLeft" | "esRight", EdgeSide>;
+    const EdgeSide: {
+      esLeft: 0 & EdgeSide;
+      esRight: 1 & EdgeSide;
+    };
 
     type Direction = Brand<number, "Direction">;
-    const Direction: Record<"dRightToLeft" | "dLeftToRight", Direction>;
+    const Direction: {
+      dRightToLeft: 0 & Direction;
+      dLeftToRight: 1 & Direction;
+    };
 
     class TEdge {
       Bot: IntPoint;
@@ -465,7 +494,16 @@ declare global {
     }
 
     class Clipper extends ClipperBase {
-      constructor(InitOptions?: number);
+      constructor(
+        /**
+         * @defaultValue `0`
+         * @remarks Bitmask options:
+         * - `ReverseSolution`:  `0b0000001`
+         * - `StrictlySimple`:   `0b0000010`
+         * - `PreserveColinear`: `0b0000100`
+         */
+        InitOptions?: number | null,
+      );
 
       /** @defaultValue `[]` */
       protected m_PolyOuts: OutRec[];
@@ -748,11 +786,17 @@ declare global {
 
       ClosedPathsFromPolyTree(polytree: PolyTree): Paths;
 
-      static NodeType: Record<"ntAny" | "ntOpen" | "ntClosed", Clipper.NodeType>;
+      static NodeType: Clipper.NodeTypes;
     }
 
     namespace Clipper {
       type NodeType = Brand<number, "ClipperLib.Clipper.NodeType">;
+
+      interface NodeTypes {
+        ntAny: 0 & NodeType;
+        ntOpen: 1 & NodeType;
+        ntClosed: 2 & NodeType;
+      }
     }
 
     class ClipperOffset {
diff --git a/src/foundry/clipper/index.d.mts b/src/foundry/public/scripts/clipper/index.d.mts
similarity index 100%
rename from src/foundry/clipper/index.d.mts
rename to src/foundry/public/scripts/clipper/index.d.mts
diff --git a/src/foundry/public/scripts/index.d.mts b/src/foundry/public/scripts/index.d.mts
index bf0f923af..6aa89fea6 100644
--- a/src/foundry/public/scripts/index.d.mts
+++ b/src/foundry/public/scripts/index.d.mts
@@ -1,1 +1,2 @@
 import "./earcut-edges/index.d.mts";
+import "./clipper/index.d.mts";
diff --git a/src/types/augments/pixi.d.mts b/src/types/augments/pixi.d.mts
index d496a095a..ba3ef6720 100644
--- a/src/types/augments/pixi.d.mts
+++ b/src/types/augments/pixi.d.mts
@@ -972,25 +972,29 @@ declare global {
     export import BatchGeometry = _PIXI.BatchGeometry;
 
     namespace BatchGeometry {
+      interface Any extends AnyPIXIBatchGeometry {}
       type AnyConstructor = typeof AnyPIXIBatchGeometry;
     }
 
     export import BatchRenderer = _PIXI.BatchRenderer;
 
     namespace BatchRenderer {
+      interface Any extends AnyPIXIBatchRenderer {}
       type AnyConstructor = typeof AnyPIXIBatchRenderer;
     }
 
     export import Container = _PIXI.Container;
 
     namespace Container {
+      interface Any extends AnyPIXIContainer {}
       type AnyConstructor = typeof AnyPIXIContainer;
     }
 
     export import DisplayObject = _PIXI.DisplayObject;
 
     namespace DisplayObject {
-      type AnyConstructor = typeof AnyDisplayObject;
+      interface Any extends AnyPIXIDisplayObject {}
+      type AnyConstructor = typeof AnyPIXIDisplayObject;
 
       type DestroyOptions = _PIXI.IDestroyOptions | boolean;
     }
@@ -998,12 +1002,14 @@ declare global {
     export import Filter = _PIXI.Filter;
 
     namespace Filter {
+      interface Any extends AnyPIXIFilter {}
       type AnyConstructor = typeof AnyPIXIFilter;
     }
 
     export import Shader = _PIXI.Shader;
 
     namespace Shader {
+      interface Any extends AnyPIXIShader {}
       type AnyConstructor = typeof AnyPIXIShader;
     }
   }
@@ -1085,7 +1091,7 @@ declare abstract class AnyPIXIContainer extends PIXI.Container {
   constructor(arg0: never, ...args: never[]);
 }
 
-declare abstract class AnyDisplayObject extends PIXI.DisplayObject {
+declare abstract class AnyPIXIDisplayObject extends PIXI.DisplayObject {
   constructor(arg0: never, ...args: never[]);
 }
 


Test patch:
diff --git a/tests/foundry/client/pixi/extensions/circle.test-d.ts b/tests/foundry/client/pixi/extensions/circle.test-d.ts
index a3d947249..4b737682b 100644
--- a/tests/foundry/client/pixi/extensions/circle.test-d.ts
+++ b/tests/foundry/client/pixi/extensions/circle.test-d.ts
@@ -1,5 +1,64 @@
 import { expectTypeOf } from "vitest";
 
+expectTypeOf(PIXI.Circle.approximateVertexDensity(128)).toEqualTypeOf<number>();
+
+declare const somePoint: PIXI.Point;
+declare const somePoly: PIXI.Polygon;
 const circle = new PIXI.Circle();
+const clipperPoints = [
+  { X: 50, Y: 50 },
+  { X: 50, Y: 100 },
+  { X: 100, Y: 100 },
+  { X: 100, Y: 50 },
+];
+
+expectTypeOf(circle.center).toEqualTypeOf<PIXI.Point>();
+expectTypeOf(circle.pointIsOn(somePoint)).toEqualTypeOf<boolean>();
+expectTypeOf(circle.segmentIntersections(somePoint, { x: 50, y: 50 })).toEqualTypeOf<[Canvas.Point?, Canvas.Point?]>();
+expectTypeOf(circle.pointAtAngle(Math.PI / 3)).toEqualTypeOf<Canvas.Point>();
+
+expectTypeOf(
+  circle.pointsBetween({ x: 100, y: 72 }, somePoint, {
+    density: 5,
+    includeEndpoints: undefined,
+  }),
+).toEqualTypeOf<Canvas.Point[]>();
+
+expectTypeOf(
+  circle.pointsForArc(Math.PI / 5, Math.PI / 4, {
+    density: null,
+    includeEndpoints: false,
+  }),
+).toEqualTypeOf<Canvas.Point[]>();
+
+expectTypeOf(
+  circle.toPolygon({
+    density: undefined,
+    includeEndpoints: null,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+
+expectTypeOf(circle.intersectPolygon(somePoly)).toEqualTypeOf<PIXI.Polygon>();
+expectTypeOf(
+  circle.intersectPolygon(somePoly, {
+    weilerAtherton: false,
+    clipType: ClipperLib.ClipType.ctDifference,
+    density: 5,
+    scalingFactor: undefined,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+expectTypeOf(
+  circle.intersectPolygon(somePoly, {
+    weilerAtherton: true,
+    canMutate: true,
+    clipType: null,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
 
-expectTypeOf(circle.circle).toEqualTypeOf<PIXI.Point>();
+expectTypeOf(
+  circle.intersectClipper(clipperPoints, {
+    clipType: undefined,
+    density: 2,
+    scalingFactor: 2,
+  }),
+).toEqualTypeOf<PIXI.Polygon>;
diff --git a/tests/foundry/client/pixi/extensions/graphics.test-d.ts b/tests/foundry/client/pixi/extensions/graphics.test-d.ts
index c60319cb5..25b2f003e 100644
--- a/tests/foundry/client/pixi/extensions/graphics.test-d.ts
+++ b/tests/foundry/client/pixi/extensions/graphics.test-d.ts
@@ -1,11 +1,31 @@
+import { expectTypeOf } from "vitest";
+
+declare const somePoly: PIXI.Polygon;
+
 const defaultGraphics = new PIXI.Graphics();
 
-defaultGraphics.drawPath();
+expectTypeOf(defaultGraphics.drawPath(somePoly)).toEqualTypeOf<typeof defaultGraphics>();
+expectTypeOf(defaultGraphics.drawSmoothedPath(1, 2, 3, 4, 5, 6)).toEqualTypeOf<typeof defaultGraphics>();
+expectTypeOf(
+  defaultGraphics.drawSmoothedPolygon([
+    { x: 50, y: 50 },
+    { x: 100, y: 100 },
+  ]),
+).toEqualTypeOf<typeof defaultGraphics>();
 
 const legacyGraphics = new PIXI.LegacyGraphics();
 
-legacyGraphics.drawPath();
+expectTypeOf(legacyGraphics.drawPath(somePoly)).toEqualTypeOf<typeof legacyGraphics>();
+expectTypeOf(legacyGraphics.drawSmoothedPath([1, 2, 3, 4, 5, 6], 2)).toEqualTypeOf<typeof legacyGraphics>();
+expectTypeOf(
+  legacyGraphics.drawSmoothedPolygon([
+    { x: 50, y: 50 },
+    { x: 100, y: 100 },
+  ]),
+).toEqualTypeOf<typeof legacyGraphics>();
 
 const smoothGraphics = new PIXI.smooth.SmoothGraphics();
 
-smoothGraphics.drawPath();
+expectTypeOf(smoothGraphics.drawPath({ x: 50, y: 50 }, { x: 100, y: 100 })).toEqualTypeOf<typeof smoothGraphics>();
+expectTypeOf(smoothGraphics.drawSmoothedPath(1, 2, 3, 4, 5, 6)).toEqualTypeOf<typeof smoothGraphics>();
+expectTypeOf(smoothGraphics.drawSmoothedPolygon(somePoly)).toEqualTypeOf<typeof smoothGraphics>();
diff --git a/tests/foundry/client/pixi/extensions/observable-transform.test-d.ts b/tests/foundry/client/pixi/extensions/observable-transform.test-d.ts
new file mode 100644
index 000000000..ad3d23254
--- /dev/null
+++ b/tests/foundry/client/pixi/extensions/observable-transform.test-d.ts
@@ -0,0 +1,10 @@
+import { expectTypeOf } from "vitest";
+
+const someScope = new PIXI.Polygon();
+function someCallback(this: PIXI.Polygon) {
+  return this.isPositive;
+}
+
+const myOT = new ObservableTransform(someCallback, someScope);
+expectTypeOf(myOT.cb).toEqualTypeOf<typeof someCallback>();
+expectTypeOf(myOT.scope).toEqualTypeOf<PIXI.Polygon>();
diff --git a/tests/foundry/client/pixi/extensions/polygon.test-d.ts b/tests/foundry/client/pixi/extensions/polygon.test-d.ts
index 5f12806a8..5f45c499c 100644
--- a/tests/foundry/client/pixi/extensions/polygon.test-d.ts
+++ b/tests/foundry/client/pixi/extensions/polygon.test-d.ts
@@ -1,5 +1,50 @@
 import { expectTypeOf } from "vitest";
 
+declare const somePoint: PIXI.Point;
+declare const otherPoly: PIXI.Polygon;
+declare const someCircle: PIXI.Circle;
+declare const someRect: PIXI.Rectangle;
+const clipperPoints = [
+  { X: 50, Y: 50 },
+  { X: 50, Y: 100 },
+  { X: 100, Y: 100 },
+  { X: 100, Y: 50 },
+];
+
+expectTypeOf(PIXI.Polygon.fromClipperPoints(clipperPoints, { scalingFactor: 4 })).toEqualTypeOf<PIXI.Polygon>();
+
 const myPoly = new PIXI.Polygon([0, 1, 2, 3]);
 
 expectTypeOf(myPoly.isPositive).toBeBoolean();
+expectTypeOf(myPoly.addPoint(somePoint)).toEqualTypeOf<typeof myPoly>();
+expectTypeOf(myPoly.toClipperPoints({ scalingFactor: 1.2 })).toEqualTypeOf<PIXI.Polygon.ClipperPoint[]>();
+
+expectTypeOf(
+  myPoly.intersectPolygon(otherPoly, {
+    clipType: ClipperLib.ClipType.ctIntersection,
+    scalingFactor: 4,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+
+expectTypeOf(
+  myPoly.intersectClipper(clipperPoints, {
+    clipType: ClipperLib.ClipType.ctXor,
+    scalingFactor: 1.4,
+  }),
+).toEqualTypeOf<PIXI.Polygon.ClipperPoint[]>();
+
+expectTypeOf(
+  myPoly.intersectCircle(someCircle, {
+    clipType: ClipperLib.ClipType.ctUnion,
+    density: 3,
+    scalingFactor: 2,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+
+expectTypeOf(
+  myPoly.intersectRectangle(someRect, {
+    weilerAtherton: true,
+    canMutate: false,
+    clipType: ClipperLib.ClipType.ctXor,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
diff --git a/tests/foundry/client/pixi/extensions/rectangle.test-d.ts b/tests/foundry/client/pixi/extensions/rectangle.test-d.ts
index 174b5e3b9..c6bfe2bc0 100644
--- a/tests/foundry/client/pixi/extensions/rectangle.test-d.ts
+++ b/tests/foundry/client/pixi/extensions/rectangle.test-d.ts
@@ -1,6 +1,69 @@
-import { assertType, expectTypeOf } from "vitest";
+import { expectTypeOf } from "vitest";
 
-const rectangle = new PIXI.Rectangle();
+const rect = new PIXI.Rectangle(64, 64, 100, 100);
+const clipperPoints = [
+  { X: 50, Y: 50 },
+  { X: 50, Y: 100 },
+  { X: 100, Y: 100 },
+  { X: 100, Y: 50 },
+];
 
-assertType<Record<string, number>>(rectangle.CS_ZONES);
-expectTypeOf(rectangle.center).toEqualTypeOf<Point>();
+declare const somePoint: PIXI.Point;
+declare const otherRect: PIXI.Rectangle;
+declare const somePolygon: PIXI.Polygon;
+
+expectTypeOf(PIXI.Rectangle.fromRotation(50, 50, 500, 500, Math.PI / 2, somePoint)).toEqualTypeOf<PIXI.Rectangle>();
+
+expectTypeOf(rect.CS_ZONES).toMatchTypeOf<Record<keyof PIXI.Rectangle.CS_Zones, PIXI.Rectangle.CS_ZONES>>();
+expectTypeOf(rect._getEdgeZone(somePoint)).toEqualTypeOf<PIXI.Rectangle.CS_ZONES>();
+expectTypeOf(rect._getZone(somePoint)).toEqualTypeOf<PIXI.Rectangle.CS_ZONES>();
+expectTypeOf(rect.pointIsOn(somePoint)).toEqualTypeOf<boolean>();
+expectTypeOf(rect.pointsBetween({ x: 40, y: 30 }, { x: 200, y: 300 })).toEqualTypeOf<Canvas.Point[]>();
+expectTypeOf(rect.segmentIntersections(somePoint, { x: 400, y: 200 })).toEqualTypeOf<Canvas.Point[]>();
+expectTypeOf(rect.intersection(otherRect)).toEqualTypeOf<PIXI.Rectangle>();
+expectTypeOf(rect.toPolygon()).toEqualTypeOf<PIXI.Polygon>();
+expectTypeOf(rect.leftEdge).toEqualTypeOf<PIXI.Rectangle.Edge>();
+expectTypeOf(rect.lineSegmentIntersects(somePoint, { x: 40, y: 20 })).toEqualTypeOf<boolean>();
+expectTypeOf<PIXI.Rectangle.CS_ZONES>().toMatchTypeOf<number>();
+
+//no options
+expectTypeOf(rect.intersectPolygon(somePolygon)).toEqualTypeOf<PIXI.Polygon>();
+
+//regular options, split by weilerAtherton truthiness
+expectTypeOf(
+  rect.intersectPolygon(somePolygon, {
+    weilerAtherton: true,
+    canMutate: false,
+    clipType: WeilerAthertonClipper.CLIP_TYPES.INTERSECT,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+expectTypeOf(
+  rect.intersectPolygon(somePolygon, {
+    weilerAtherton: false,
+    clipType: ClipperLib.ClipType.ctDifference,
+    scalingFactor: 2,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+
+//the two WAC CLIP_TYPES map exactly to equivalents in ClipperLib.ClipTypes, so they're allowed, as is the partial inverse
+expectTypeOf(
+  rect.intersectPolygon(somePolygon, {
+    weilerAtherton: false,
+    clipType: WeilerAthertonClipper.CLIP_TYPES.INTERSECT,
+    scalingFactor: undefined,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+expectTypeOf(
+  rect.intersectPolygon(somePolygon, {
+    weilerAtherton: true,
+    clipType: ClipperLib.ClipType.ctIntersection,
+    canMutate: null,
+  }),
+).toEqualTypeOf<PIXI.Polygon>();
+
+//This return type (`| []`) is very probably a Foundry bug in v12.
+expectTypeOf(
+  rect.intersectClipper(clipperPoints, { clipType: ClipperLib.ClipType.ctDifference, scalingFactor: 3 }),
+).toEqualTypeOf<PIXI.Polygon | []>();
+
+expectTypeOf(rect.overlaps(otherRect)).toEqualTypeOf<boolean>();
diff --git a/tests/foundry/client/pixi/perception/weiler-atherton-clipping.test-d.ts b/tests/foundry/client/pixi/perception/weiler-atherton-clipping.test-d.ts
index d772e83b3..856c51961 100644
--- a/tests/foundry/client/pixi/perception/weiler-atherton-clipping.test-d.ts
+++ b/tests/foundry/client/pixi/perception/weiler-atherton-clipping.test-d.ts
@@ -1,12 +1,54 @@
 import { expectTypeOf } from "vitest";
 
-const myPolygon = new PIXI.Polygon([0, 0]);
+expectTypeOf(WeilerAthertonClipper.CLIP_TYPES).toMatchTypeOf<
+  Record<keyof WeilerAthertonClipper.ClipTypes, WeilerAthertonClipper.CLIP_TYPES>
+>();
+expectTypeOf(WeilerAthertonClipper.INTERSECTION_TYPES).toMatchTypeOf<
+  Record<keyof WeilerAthertonClipper.IntersectionTypes, WeilerAthertonClipper.INTERSECTION_TYPES>
+>();
 
-const myCircle = new PIXI.Circle(2, 3, 4);
+declare const someRect: PIXI.Rectangle;
+declare const someCircle: PIXI.Circle;
+declare const somePolygon: PIXI.Polygon;
 
-const myWeiler = new WeilerAthertonClipper(myPolygon, myCircle, WeilerAthertonClipper.CLIP_TYPES.INTERSECT, {
+expectTypeOf(
+  WeilerAthertonClipper.union(somePolygon, someRect, {
+    density: 5,
+    includeEndpoints: false,
+  }),
+).toEqualTypeOf<Array<PIXI.Polygon>>();
+
+expectTypeOf(
+  WeilerAthertonClipper.intersect(somePolygon, someCircle, {
+    density: 2,
+    includeEndpoints: null,
+  }),
+).toEqualTypeOf<Array<PIXI.Polygon>>();
+
+expectTypeOf(
+  WeilerAthertonClipper.combine(somePolygon, someRect, {
+    clipType: WeilerAthertonClipper.CLIP_TYPES.UNION,
+    canMutate: false,
+    density: undefined,
+  }),
+).toEqualTypeOf<Array<PIXI.Polygon>>();
+
+expectTypeOf(
+  WeilerAthertonClipper.testForEnvelopment(somePolygon, someCircle, WeilerAthertonClipper.CLIP_TYPES.INTERSECT, {
+    density: null,
+    includeEndpoints: undefined,
+  }),
+).toEqualTypeOf<Array<PIXI.Polygon>>();
+
+const myWAC = new WeilerAthertonClipper(somePolygon, someCircle, WeilerAthertonClipper.CLIP_TYPES.INTERSECT, {
   density: 3,
   includeEndpoints: true,
 });
 
-expectTypeOf(myWeiler.clipObject).toEqualTypeOf<PIXI.Rectangle | PIXI.Circle>();
+expectTypeOf(myWAC.clipObject).toEqualTypeOf<PIXI.Rectangle | PIXI.Circle>();
+expectTypeOf(myWAC.config.clipType).toMatchTypeOf<WeilerAthertonClipper.CLIP_TYPES>();
+if (myWAC.config.clipOpts.includeEndpoints) {
+  expectTypeOf(myWAC.config.clipOpts.includeEndpoints).toEqualTypeOf<true>();
+} else {
+  expectTypeOf(myWAC.config.clipOpts.includeEndpoints).toEqualTypeOf<false | null | undefined>();
+}
diff --git a/tests/foundry/public/scripts/clipper/clipper.test-d.ts b/tests/foundry/public/scripts/clipper/clipper.test-d.ts
new file mode 100644
index 000000000..f862c18e4
--- /dev/null
+++ b/tests/foundry/public/scripts/clipper/clipper.test-d.ts
@@ -0,0 +1,31 @@
+import { expectTypeOf } from "vitest";
+
+const intPoint = new ClipperLib.IntPoint(50, 50);
+declare const otherIntPoint: ClipperLib.IntPoint;
+const dblPoint = new ClipperLib.DoublePoint(intPoint);
+
+expectTypeOf(ClipperLib.IntPoint.op_Equality(intPoint, otherIntPoint)).toEqualTypeOf<boolean>();
+expectTypeOf(dblPoint.X).toEqualTypeOf<number>();
+
+const clipper = new ClipperLib.Clipper(4 & 2);
+const paths = new ClipperLib.Paths();
+const polyTree = new ClipperLib.PolyTree();
+
+expectTypeOf(
+  clipper.Execute(
+    ClipperLib.ClipType.ctDifference,
+    paths,
+    ClipperLib.PolyFillType.pftNegative,
+    ClipperLib.PolyFillType.pftEvenOdd,
+  ),
+).toEqualTypeOf<boolean>();
+expectTypeOf(
+  clipper.Execute(
+    ClipperLib.ClipType.ctDifference,
+    polyTree,
+    ClipperLib.PolyFillType.pftNegative,
+    ClipperLib.PolyFillType.pftEvenOdd,
+  ),
+).toEqualTypeOf<boolean>();
+expectTypeOf(clipper.Execute(ClipperLib.ClipType.ctDifference, polyTree)).toEqualTypeOf<boolean>();
+expectTypeOf(clipper.Execute(ClipperLib.ClipType.ctDifference, paths)).toEqualTypeOf<boolean>();