Repository: League-of-Foundry-Developers/foundry-vtt-types
Pull request #2624 (2024-07-11 11:56:46)

Geometry to v12
added:

- [ ] - foundry.utils.polygonCentroid
- [ ] - foundry.utils.pathCircleIntersects
- [ ] - foundry.utils.circleCircleIntersects

And appropriate tests.

Solves #2603 


Patch:
diff --git a/src/foundry/common/types.d.mts b/src/foundry/common/types.d.mts
index b87a89d9f..e177a4839 100644
--- a/src/foundry/common/types.d.mts
+++ b/src/foundry/common/types.d.mts
@@ -124,6 +124,12 @@ declare global {
         height: number;
       };
 
+  type RGBColorVector = [r: number, g: number, b: number];
+  type HSVColorVector = [h: number, s: number, v: number];
+  type HSLColorVector = [h: number, s: number, l: number];
+
+  type ColorSource = number | RGBColorVector | string | Color;
+
   /** A Client Setting */
   interface SettingConfig<T = unknown> {
     /** A unique machine-readable id for the setting */
diff --git a/src/foundry/common/utils/color.d.mts b/src/foundry/common/utils/color.d.mts
index 6377f5f3c..86f8a5404 100644
--- a/src/foundry/common/utils/color.d.mts
+++ b/src/foundry/common/utils/color.d.mts
@@ -1,6 +1,3 @@
-type RGBColorVector = [r: number, g: number, b: number];
-type HSVColorVector = [h: number, s: number, v: number];
-
 export default Color;
 /**
  * A representation of a color in hexadecimal format.
@@ -8,8 +5,14 @@ export default Color;
  */
 // @ts-expect-error 2417: Override of Color.fromString does not match Number.fromString
 declare class Color extends Number {
+  /**
+   * Is this a valid color?
+   */
+  get valid(): boolean;
+
   /**
    * A CSS-compatible color string.
+   * If this color is not valid, the empty string is returned.
    * An alias for Color#toString.
    */
   get css(): string;
@@ -51,13 +54,38 @@ declare class Color extends Number {
 
   /**
    * The color represented as an HSV array.
-   * Conversion formula adapted from http://en.wikipedia.org/wiki/HSV_color_space.
+   * Conversion formula adapted from {@link http://en.wikipedia.org/wiki/HSV_color_space}.
    * Assumes r, g, and b are contained in the set [0, 1] and returns h, s, and v in the set [0, 1].
    */
   get hsv(): HSVColorVector;
 
+  /**
+   * The color represented as an HSL array.
+   * Assumes r, g, and b are contained in the set [0, 1] and returns h, s, and l in the set [0, 1].
+   */
+  get hsl(): HSLColorVector;
+
+  /**
+   * The color represented as a linear RGB array.
+   * Assumes r, g, and b are contained in the set [0, 1] and returns linear r, g, and b in the set [0, 1].
+   * {@link https://en.wikipedia.org/wiki/SRGB#Transformation}
+   */
+  get linear(): RGBColorVector;
+
   override toString(): string;
 
+  /**
+   * Serialize the Color.
+   * @returns The color as a CSS string
+   */
+  toJSON(): string;
+
+  /**
+   * Returns the color as a CSS string.
+   * @returns The color as a CSS string
+   */
+  toHTML(): string;
+
   /**
    * Test whether this color equals some other color
    * @param other - Some other color or hex number
@@ -219,7 +247,7 @@ declare class Color extends Number {
    * @param color - A color input
    * @returns The hex color instance or NaN
    */
-  static from(color: null | string | number | RGBColorVector | Color): Color | number;
+  static from(color: ColorSource | null | undefined): Color;
 
   /**
    * Create a Color instance from a color string which either includes or does not include a leading #.
@@ -237,10 +265,27 @@ declare class Color extends Number {
 
   /**
    * Create a Color instance from an HSV array.
-   * Conversion formula adapted from http://en.wikipedia.org/wiki/HSV_color_space.
+   * Conversion formula adapted from {@link http://en.wikipedia.org/wiki/HSV_color_space}.
    * Assumes h, s, and v are contained in the set [0, 1].
    * @param hsv - An HSV tuple
    * @returns The hex color instance
    */
   static fromHSV(hsv: HSVColorVector): Color;
+
+  /**
+   * Create a Color instance from an HSL array.
+   * Assumes h, s, and l are contained in the set [0, 1].
+   * @param hsl - An HSL tuple
+   * @returns The hex color instance
+   */
+  static fromHSL(hsl: HSLColorVector): Color;
+
+  /**
+   * Create a Color instance (sRGB) from a linear rgb array.
+   * Assumes r, g, and b are contained in the set [0, 1].
+   * {@link https://en.wikipedia.org/wiki/SRGB#Transformation}
+   * @param linear - The linear rgb array
+   * @returns The hex color instance
+   */
+  static fromLinearRGB(linear: RGBColorVector): Color;
 }
diff --git a/src/foundry/common/utils/geometry.d.mts b/src/foundry/common/utils/geometry.d.mts
index 0d8df68f2..324fc1840 100644
--- a/src/foundry/common/utils/geometry.d.mts
+++ b/src/foundry/common/utils/geometry.d.mts
@@ -161,3 +161,38 @@ export declare function quadraticIntersection(
   radius: number,
   epsilon?: number,
 ): [Point?, Point?];
+
+declare type Points = Point[] | number[];
+
+/**
+ * Calculate the centroid non-self-intersecting closed polygon.
+ * See https://en.wikipedia.org/wiki/Centroid#Of_a_polygon.
+ *
+ * @param points    - The points of the polygon
+ * @returns         - The centroid of the polygon
+ */
+export declare function polygonCentroid(points: Points): Point;
+
+/**
+ * Test whether the circle given by the center and radius intersects the path (open or closed).
+ *
+ * @param points    - The points of the path
+ * @param close     - If true, the edge from the last to the first point is tested
+ * @param center    - The center of the circle
+ * @param radius    - The radius of the circle
+ * @returns         - Whether the circle intersect the path
+ */
+export declare function pathCircleIntersects(points: Points, close: boolean, center: Point, radius: number): boolean;
+
+/**
+ * Test whether two circles (with position and radius) intersect.
+ *
+ * @param x0        - x center coordinate of circle A.
+ * @param y0        - y center coordinate of circle A.
+ * @param r0        - radius of circle A.
+ * @param x1        - x center coordinate of circle B.
+ * @param y1        - y center coordinate of circle B.
+ * @param r1        - radius of circle B.
+ * @returns         - True if the two circles intersect, false otherwise.
+ */
+export function circleCircleIntersects(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): boolean;


Test patch:
diff --git a/tests/foundry/common/utils/geometry.mjs.test-d.ts b/tests/foundry/common/utils/geometry.mjs.test-d.ts
index 2c8465dd9..a16ad4fad 100644
--- a/tests/foundry/common/utils/geometry.mjs.test-d.ts
+++ b/tests/foundry/common/utils/geometry.mjs.test-d.ts
@@ -35,3 +35,55 @@ expectTypeOf(foundry.utils.lineCircleIntersection(a, b, c, 0).intersections).toE
 expectTypeOf(foundry.utils.lineCircleIntersection(a, b, c, 0).intersections).toEqualTypeOf(
   foundry.utils.quadraticIntersection(a, b, c, 0),
 );
+
+// utils.polygonCentroid
+
+const points = [
+  new PIXI.Point(0, 0),
+  new PIXI.Point(1, 0),
+  new PIXI.Point(1, 2),
+  new PIXI.Point(0, 3),
+] satisfies Point[];
+
+const returnPoint = new PIXI.Point(0, 0);
+
+expectTypeOf(foundry.utils.polygonCentroid(points)).toMatchTypeOf<Point>(returnPoint);
+
+// utils.pathCircleIntersects
+
+type CircleIntersectTest = {
+  points: Parameters<typeof foundry.utils.pathCircleIntersects>[0];
+  close: Parameters<typeof foundry.utils.pathCircleIntersects>[1];
+  center: Parameters<typeof foundry.utils.pathCircleIntersects>[2];
+  radius: Parameters<typeof foundry.utils.pathCircleIntersects>[3];
+  result: ReturnType<typeof foundry.utils.pathCircleIntersects>;
+};
+
+// Testing for intersecting circle so that it actually returns what we expect
+const testIntersectingCircle: CircleIntersectTest = {
+  points: points,
+  close: true,
+  center: new PIXI.Point(0.5, 0.5),
+  radius: 1,
+  result: true,
+};
+
+expectTypeOf(
+  foundry.utils.pathCircleIntersects(
+    testIntersectingCircle.points,
+    testIntersectingCircle.close,
+    testIntersectingCircle.center,
+    testIntersectingCircle.radius,
+  ),
+).toEqualTypeOf(testIntersectingCircle.result);
+
+// utils.circleCircleIntersects
+
+expectTypeOf(foundry.utils.circleCircleIntersects).parameter(0).toBeNumber();
+expectTypeOf(foundry.utils.circleCircleIntersects).parameter(1).toBeNumber();
+expectTypeOf(foundry.utils.circleCircleIntersects).parameter(2).toBeNumber();
+expectTypeOf(foundry.utils.circleCircleIntersects).parameter(3).toBeNumber();
+expectTypeOf(foundry.utils.circleCircleIntersects).parameter(4).toBeNumber();
+expectTypeOf(foundry.utils.circleCircleIntersects).parameter(5).toBeNumber();
+
+expectTypeOf(foundry.utils.circleCircleIntersects(0, 0, 1, 0, 0, 1)).toEqualTypeOf<boolean>();