1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. export type Point = $ReadOnly<{x: number, y: number}>;
    
  11. export type Size = $ReadOnly<{width: number, height: number}>;
    
  12. export type IntrinsicSize = {
    
  13.   ...Size,
    
  14. 
    
  15.   // If content is this height or less, hide the scrollbar entirely,
    
  16.   // so that it doesn't take up vertical space unnecessarily (e.g. for a single row of content).
    
  17.   hideScrollBarIfLessThanHeight?: number,
    
  18. 
    
  19.   // The initial height should be the height of the content, or this, whichever is less.
    
  20.   maxInitialHeight?: number,
    
  21. };
    
  22. export type Rect = $ReadOnly<{origin: Point, size: Size}>;
    
  23. 
    
  24. /**
    
  25.  * Alternative representation of `Rect`.
    
  26.  * A tuple of (`top`, `right`, `bottom`, `left`) coordinates.
    
  27.  */
    
  28. type Box = [number, number, number, number];
    
  29. 
    
  30. export const zeroPoint: Point = Object.freeze({x: 0, y: 0});
    
  31. export const zeroSize: Size = Object.freeze({width: 0, height: 0});
    
  32. export const zeroRect: Rect = Object.freeze({
    
  33.   origin: zeroPoint,
    
  34.   size: zeroSize,
    
  35. });
    
  36. 
    
  37. export function pointEqualToPoint(point1: Point, point2: Point): boolean {
    
  38.   return point1.x === point2.x && point1.y === point2.y;
    
  39. }
    
  40. 
    
  41. export function sizeEqualToSize(size1: Size, size2: Size): boolean {
    
  42.   return size1.width === size2.width && size1.height === size2.height;
    
  43. }
    
  44. 
    
  45. export function rectEqualToRect(rect1: Rect, rect2: Rect): boolean {
    
  46.   return (
    
  47.     pointEqualToPoint(rect1.origin, rect2.origin) &&
    
  48.     sizeEqualToSize(rect1.size, rect2.size)
    
  49.   );
    
  50. }
    
  51. 
    
  52. export function sizeIsValid({width, height}: Size): boolean {
    
  53.   return width >= 0 && height >= 0;
    
  54. }
    
  55. 
    
  56. export function sizeIsEmpty({width, height}: Size): boolean {
    
  57.   return width <= 0 || height <= 0;
    
  58. }
    
  59. 
    
  60. function rectToBox(rect: Rect): Box {
    
  61.   const top = rect.origin.y;
    
  62.   const right = rect.origin.x + rect.size.width;
    
  63.   const bottom = rect.origin.y + rect.size.height;
    
  64.   const left = rect.origin.x;
    
  65.   return [top, right, bottom, left];
    
  66. }
    
  67. 
    
  68. function boxToRect(box: Box): Rect {
    
  69.   const [top, right, bottom, left] = box;
    
  70.   return {
    
  71.     origin: {
    
  72.       x: left,
    
  73.       y: top,
    
  74.     },
    
  75.     size: {
    
  76.       width: right - left,
    
  77.       height: bottom - top,
    
  78.     },
    
  79.   };
    
  80. }
    
  81. 
    
  82. export function rectIntersectsRect(rect1: Rect, rect2: Rect): boolean {
    
  83.   if (
    
  84.     rect1.size.width === 0 ||
    
  85.     rect1.size.height === 0 ||
    
  86.     rect2.size.width === 0 ||
    
  87.     rect2.size.height === 0
    
  88.   ) {
    
  89.     return false;
    
  90.   }
    
  91. 
    
  92.   const [top1, right1, bottom1, left1] = rectToBox(rect1);
    
  93.   const [top2, right2, bottom2, left2] = rectToBox(rect2);
    
  94.   return !(
    
  95.     right1 < left2 ||
    
  96.     right2 < left1 ||
    
  97.     bottom1 < top2 ||
    
  98.     bottom2 < top1
    
  99.   );
    
  100. }
    
  101. 
    
  102. /**
    
  103.  * Returns the intersection of the 2 rectangles.
    
  104.  *
    
  105.  * Prerequisite: `rect1` must intersect with `rect2`.
    
  106.  */
    
  107. export function intersectionOfRects(rect1: Rect, rect2: Rect): Rect {
    
  108.   const [top1, right1, bottom1, left1] = rectToBox(rect1);
    
  109.   const [top2, right2, bottom2, left2] = rectToBox(rect2);
    
  110.   return boxToRect([
    
  111.     Math.max(top1, top2),
    
  112.     Math.min(right1, right2),
    
  113.     Math.min(bottom1, bottom2),
    
  114.     Math.max(left1, left2),
    
  115.   ]);
    
  116. }
    
  117. 
    
  118. export function rectContainsPoint({x, y}: Point, rect: Rect): boolean {
    
  119.   const [top, right, bottom, left] = rectToBox(rect);
    
  120.   return left <= x && x <= right && top <= y && y <= bottom;
    
  121. }
    
  122. 
    
  123. /**
    
  124.  * Returns the smallest rectangle that contains all provided rects.
    
  125.  *
    
  126.  * @returns Union of `rects`. If `rects` is empty, returns `zeroRect`.
    
  127.  */
    
  128. export function unionOfRects(...rects: Rect[]): Rect {
    
  129.   if (rects.length === 0) {
    
  130.     return zeroRect;
    
  131.   }
    
  132. 
    
  133.   const [firstRect, ...remainingRects] = rects;
    
  134.   const boxUnion = remainingRects
    
  135.     .map(rectToBox)
    
  136.     .reduce((intermediateUnion, nextBox): Box => {
    
  137.       const [unionTop, unionRight, unionBottom, unionLeft] = intermediateUnion;
    
  138.       const [nextTop, nextRight, nextBottom, nextLeft] = nextBox;
    
  139.       return [
    
  140.         Math.min(unionTop, nextTop),
    
  141.         Math.max(unionRight, nextRight),
    
  142.         Math.max(unionBottom, nextBottom),
    
  143.         Math.min(unionLeft, nextLeft),
    
  144.       ];
    
  145.     }, rectToBox(firstRect));
    
  146.   return boxToRect(boxUnion);
    
  147. }