1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2024 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Worker// This library provides interfaces and classes for handling 2D geometry 16*6dbdd20aSAndroid Build Coastguard Worker// operations. 17*6dbdd20aSAndroid Build Coastguard Worker 18*6dbdd20aSAndroid Build Coastguard Worker/** 19*6dbdd20aSAndroid Build Coastguard Worker * Interface representing a point in 2D space. 20*6dbdd20aSAndroid Build Coastguard Worker */ 21*6dbdd20aSAndroid Build Coastguard Workerexport interface Point2D { 22*6dbdd20aSAndroid Build Coastguard Worker readonly x: number; 23*6dbdd20aSAndroid Build Coastguard Worker readonly y: number; 24*6dbdd20aSAndroid Build Coastguard Worker} 25*6dbdd20aSAndroid Build Coastguard Worker 26*6dbdd20aSAndroid Build Coastguard Worker/** 27*6dbdd20aSAndroid Build Coastguard Worker * Class representing a 2D vector with methods for vector operations. 28*6dbdd20aSAndroid Build Coastguard Worker * 29*6dbdd20aSAndroid Build Coastguard Worker * Note: This class is immutable in TypeScript (not enforced at runtime). Any 30*6dbdd20aSAndroid Build Coastguard Worker * method that modifies the vector returns a new instance, leaving the original 31*6dbdd20aSAndroid Build Coastguard Worker * unchanged. 32*6dbdd20aSAndroid Build Coastguard Worker */ 33*6dbdd20aSAndroid Build Coastguard Workerexport class Vector2D implements Point2D { 34*6dbdd20aSAndroid Build Coastguard Worker readonly x: number; 35*6dbdd20aSAndroid Build Coastguard Worker readonly y: number; 36*6dbdd20aSAndroid Build Coastguard Worker 37*6dbdd20aSAndroid Build Coastguard Worker constructor({x, y}: Point2D) { 38*6dbdd20aSAndroid Build Coastguard Worker this.x = x; 39*6dbdd20aSAndroid Build Coastguard Worker this.y = y; 40*6dbdd20aSAndroid Build Coastguard Worker } 41*6dbdd20aSAndroid Build Coastguard Worker 42*6dbdd20aSAndroid Build Coastguard Worker /** 43*6dbdd20aSAndroid Build Coastguard Worker * Adds the given point to this vector and returns a new vector. 44*6dbdd20aSAndroid Build Coastguard Worker * 45*6dbdd20aSAndroid Build Coastguard Worker * @param point - The point to add. 46*6dbdd20aSAndroid Build Coastguard Worker * @returns A new Vector2D instance representing the result. 47*6dbdd20aSAndroid Build Coastguard Worker */ 48*6dbdd20aSAndroid Build Coastguard Worker add(point: Point2D): Vector2D { 49*6dbdd20aSAndroid Build Coastguard Worker return new Vector2D({x: this.x + point.x, y: this.y + point.y}); 50*6dbdd20aSAndroid Build Coastguard Worker } 51*6dbdd20aSAndroid Build Coastguard Worker 52*6dbdd20aSAndroid Build Coastguard Worker /** 53*6dbdd20aSAndroid Build Coastguard Worker * Subtracts the given point from this vector and returns a new vector. 54*6dbdd20aSAndroid Build Coastguard Worker * 55*6dbdd20aSAndroid Build Coastguard Worker * @param point - The point to subtract. 56*6dbdd20aSAndroid Build Coastguard Worker * @returns A new Vector2D instance representing the result. 57*6dbdd20aSAndroid Build Coastguard Worker */ 58*6dbdd20aSAndroid Build Coastguard Worker sub(point: Point2D): Vector2D { 59*6dbdd20aSAndroid Build Coastguard Worker return new Vector2D({x: this.x - point.x, y: this.y - point.y}); 60*6dbdd20aSAndroid Build Coastguard Worker } 61*6dbdd20aSAndroid Build Coastguard Worker 62*6dbdd20aSAndroid Build Coastguard Worker /** 63*6dbdd20aSAndroid Build Coastguard Worker * Scales this vector by the given scalar and returns a new vector. 64*6dbdd20aSAndroid Build Coastguard Worker * 65*6dbdd20aSAndroid Build Coastguard Worker * @param scalar - The scalar value to multiply the vector by. 66*6dbdd20aSAndroid Build Coastguard Worker * @returns A new Vector2D instance representing the scaled vector. 67*6dbdd20aSAndroid Build Coastguard Worker */ 68*6dbdd20aSAndroid Build Coastguard Worker scale(scalar: number): Vector2D { 69*6dbdd20aSAndroid Build Coastguard Worker return new Vector2D({x: this.x * scalar, y: this.y * scalar}); 70*6dbdd20aSAndroid Build Coastguard Worker } 71*6dbdd20aSAndroid Build Coastguard Worker 72*6dbdd20aSAndroid Build Coastguard Worker /** 73*6dbdd20aSAndroid Build Coastguard Worker * Computes the Manhattan distance, which is the sum of the absolute values of 74*6dbdd20aSAndroid Build Coastguard Worker * the x and y components of the vector. This represents the distance 75*6dbdd20aSAndroid Build Coastguard Worker * travelled along axes at right angles (grid-based distance). 76*6dbdd20aSAndroid Build Coastguard Worker */ 77*6dbdd20aSAndroid Build Coastguard Worker get manhattanDistance(): number { 78*6dbdd20aSAndroid Build Coastguard Worker return Math.abs(this.x) + Math.abs(this.y); 79*6dbdd20aSAndroid Build Coastguard Worker } 80*6dbdd20aSAndroid Build Coastguard Worker 81*6dbdd20aSAndroid Build Coastguard Worker /** 82*6dbdd20aSAndroid Build Coastguard Worker * Computes the Euclidean magnitude (or length) of the vector. This is the 83*6dbdd20aSAndroid Build Coastguard Worker * straight-line distance from the origin (0, 0) to the point (x, y) in 2D 84*6dbdd20aSAndroid Build Coastguard Worker * space. 85*6dbdd20aSAndroid Build Coastguard Worker */ 86*6dbdd20aSAndroid Build Coastguard Worker get magnitude(): number { 87*6dbdd20aSAndroid Build Coastguard Worker return Math.sqrt(this.x * this.x + this.y * this.y); 88*6dbdd20aSAndroid Build Coastguard Worker } 89*6dbdd20aSAndroid Build Coastguard Worker} 90*6dbdd20aSAndroid Build Coastguard Worker 91*6dbdd20aSAndroid Build Coastguard Worker/** 92*6dbdd20aSAndroid Build Coastguard Worker * Interface representing the vertical bounds of an object (top and bottom). 93*6dbdd20aSAndroid Build Coastguard Worker */ 94*6dbdd20aSAndroid Build Coastguard Workerexport interface VerticalBounds { 95*6dbdd20aSAndroid Build Coastguard Worker readonly top: number; 96*6dbdd20aSAndroid Build Coastguard Worker readonly bottom: number; 97*6dbdd20aSAndroid Build Coastguard Worker} 98*6dbdd20aSAndroid Build Coastguard Worker 99*6dbdd20aSAndroid Build Coastguard Worker/** 100*6dbdd20aSAndroid Build Coastguard Worker * Interface representing the horizontal bounds of an object (left and right). 101*6dbdd20aSAndroid Build Coastguard Worker */ 102*6dbdd20aSAndroid Build Coastguard Workerexport interface HorizontalBounds { 103*6dbdd20aSAndroid Build Coastguard Worker readonly left: number; 104*6dbdd20aSAndroid Build Coastguard Worker readonly right: number; 105*6dbdd20aSAndroid Build Coastguard Worker} 106*6dbdd20aSAndroid Build Coastguard Worker 107*6dbdd20aSAndroid Build Coastguard Worker/** 108*6dbdd20aSAndroid Build Coastguard Worker * Interface combining vertical and horizontal bounds to describe a 2D bounding 109*6dbdd20aSAndroid Build Coastguard Worker * box. 110*6dbdd20aSAndroid Build Coastguard Worker */ 111*6dbdd20aSAndroid Build Coastguard Workerexport interface Bounds2D extends VerticalBounds, HorizontalBounds {} 112*6dbdd20aSAndroid Build Coastguard Worker 113*6dbdd20aSAndroid Build Coastguard Worker/** 114*6dbdd20aSAndroid Build Coastguard Worker * Interface representing the size of a 2D object. 115*6dbdd20aSAndroid Build Coastguard Worker */ 116*6dbdd20aSAndroid Build Coastguard Workerexport interface Size2D { 117*6dbdd20aSAndroid Build Coastguard Worker readonly width: number; 118*6dbdd20aSAndroid Build Coastguard Worker readonly height: number; 119*6dbdd20aSAndroid Build Coastguard Worker} 120*6dbdd20aSAndroid Build Coastguard Worker 121*6dbdd20aSAndroid Build Coastguard Worker/** 122*6dbdd20aSAndroid Build Coastguard Worker * Class representing a 2D rectangle, implementing bounds and size interfaces. 123*6dbdd20aSAndroid Build Coastguard Worker */ 124*6dbdd20aSAndroid Build Coastguard Workerexport class Rect2D implements Bounds2D, Size2D { 125*6dbdd20aSAndroid Build Coastguard Worker readonly left: number; 126*6dbdd20aSAndroid Build Coastguard Worker readonly top: number; 127*6dbdd20aSAndroid Build Coastguard Worker readonly right: number; 128*6dbdd20aSAndroid Build Coastguard Worker readonly bottom: number; 129*6dbdd20aSAndroid Build Coastguard Worker readonly width: number; 130*6dbdd20aSAndroid Build Coastguard Worker readonly height: number; 131*6dbdd20aSAndroid Build Coastguard Worker 132*6dbdd20aSAndroid Build Coastguard Worker constructor({left, top, right, bottom}: Bounds2D) { 133*6dbdd20aSAndroid Build Coastguard Worker this.left = left; 134*6dbdd20aSAndroid Build Coastguard Worker this.top = top; 135*6dbdd20aSAndroid Build Coastguard Worker this.right = right; 136*6dbdd20aSAndroid Build Coastguard Worker this.bottom = bottom; 137*6dbdd20aSAndroid Build Coastguard Worker this.width = right - left; 138*6dbdd20aSAndroid Build Coastguard Worker this.height = bottom - top; 139*6dbdd20aSAndroid Build Coastguard Worker } 140*6dbdd20aSAndroid Build Coastguard Worker 141*6dbdd20aSAndroid Build Coastguard Worker /** 142*6dbdd20aSAndroid Build Coastguard Worker * Returns a new rectangle representing the intersection with another 143*6dbdd20aSAndroid Build Coastguard Worker * rectangle. 144*6dbdd20aSAndroid Build Coastguard Worker * 145*6dbdd20aSAndroid Build Coastguard Worker * @param bounds - The bounds of the other rectangle to intersect with. 146*6dbdd20aSAndroid Build Coastguard Worker * @returns A new Rect2D instance representing the intersected rectangle. 147*6dbdd20aSAndroid Build Coastguard Worker */ 148*6dbdd20aSAndroid Build Coastguard Worker intersect(bounds: Bounds2D): Rect2D { 149*6dbdd20aSAndroid Build Coastguard Worker return new Rect2D({ 150*6dbdd20aSAndroid Build Coastguard Worker top: Math.max(this.top, bounds.top), 151*6dbdd20aSAndroid Build Coastguard Worker left: Math.max(this.left, bounds.left), 152*6dbdd20aSAndroid Build Coastguard Worker bottom: Math.min(this.bottom, bounds.bottom), 153*6dbdd20aSAndroid Build Coastguard Worker right: Math.min(this.right, bounds.right), 154*6dbdd20aSAndroid Build Coastguard Worker }); 155*6dbdd20aSAndroid Build Coastguard Worker } 156*6dbdd20aSAndroid Build Coastguard Worker 157*6dbdd20aSAndroid Build Coastguard Worker /** 158*6dbdd20aSAndroid Build Coastguard Worker * Expands the rectangle by the given amount on all sides and returns a new 159*6dbdd20aSAndroid Build Coastguard Worker * rectangle. 160*6dbdd20aSAndroid Build Coastguard Worker * 161*6dbdd20aSAndroid Build Coastguard Worker * @param amount - The amount to expand the rectangle by. 162*6dbdd20aSAndroid Build Coastguard Worker * @returns A new Rect2D instance representing the expanded rectangle. 163*6dbdd20aSAndroid Build Coastguard Worker */ 164*6dbdd20aSAndroid Build Coastguard Worker expand(amount: number): Rect2D { 165*6dbdd20aSAndroid Build Coastguard Worker return new Rect2D({ 166*6dbdd20aSAndroid Build Coastguard Worker top: this.top - amount, 167*6dbdd20aSAndroid Build Coastguard Worker left: this.left - amount, 168*6dbdd20aSAndroid Build Coastguard Worker bottom: this.bottom + amount, 169*6dbdd20aSAndroid Build Coastguard Worker right: this.right + amount, 170*6dbdd20aSAndroid Build Coastguard Worker }); 171*6dbdd20aSAndroid Build Coastguard Worker } 172*6dbdd20aSAndroid Build Coastguard Worker 173*6dbdd20aSAndroid Build Coastguard Worker /** 174*6dbdd20aSAndroid Build Coastguard Worker * Reframes the rectangle by shifting its origin by the given point. 175*6dbdd20aSAndroid Build Coastguard Worker * 176*6dbdd20aSAndroid Build Coastguard Worker * @param point - The point by which to shift the origin. 177*6dbdd20aSAndroid Build Coastguard Worker * @returns A new Rect2D instance representing the reframed rectangle. 178*6dbdd20aSAndroid Build Coastguard Worker */ 179*6dbdd20aSAndroid Build Coastguard Worker reframe(point: Point2D): Rect2D { 180*6dbdd20aSAndroid Build Coastguard Worker return new Rect2D({ 181*6dbdd20aSAndroid Build Coastguard Worker left: this.left - point.x, 182*6dbdd20aSAndroid Build Coastguard Worker right: this.right - point.x, 183*6dbdd20aSAndroid Build Coastguard Worker top: this.top - point.y, 184*6dbdd20aSAndroid Build Coastguard Worker bottom: this.bottom - point.y, 185*6dbdd20aSAndroid Build Coastguard Worker }); 186*6dbdd20aSAndroid Build Coastguard Worker } 187*6dbdd20aSAndroid Build Coastguard Worker 188*6dbdd20aSAndroid Build Coastguard Worker /** 189*6dbdd20aSAndroid Build Coastguard Worker * Checks if this rectangle fully contains another set of bounds. 190*6dbdd20aSAndroid Build Coastguard Worker * 191*6dbdd20aSAndroid Build Coastguard Worker * @param bounds - The bounds to check containment for. 192*6dbdd20aSAndroid Build Coastguard Worker * @returns True if this rectangle contains the given bounds, false otherwise. 193*6dbdd20aSAndroid Build Coastguard Worker */ 194*6dbdd20aSAndroid Build Coastguard Worker contains(bounds: Bounds2D): boolean { 195*6dbdd20aSAndroid Build Coastguard Worker return !( 196*6dbdd20aSAndroid Build Coastguard Worker bounds.top < this.top || 197*6dbdd20aSAndroid Build Coastguard Worker bounds.bottom > this.bottom || 198*6dbdd20aSAndroid Build Coastguard Worker bounds.left < this.left || 199*6dbdd20aSAndroid Build Coastguard Worker bounds.right > this.right 200*6dbdd20aSAndroid Build Coastguard Worker ); 201*6dbdd20aSAndroid Build Coastguard Worker } 202*6dbdd20aSAndroid Build Coastguard Worker 203*6dbdd20aSAndroid Build Coastguard Worker /** 204*6dbdd20aSAndroid Build Coastguard Worker * Translates the rectangle by the given point and returns a new rectangle. 205*6dbdd20aSAndroid Build Coastguard Worker * 206*6dbdd20aSAndroid Build Coastguard Worker * @param point - The point by which to translate the rectangle. 207*6dbdd20aSAndroid Build Coastguard Worker * @returns A new Rect2D instance representing the translated rectangle. 208*6dbdd20aSAndroid Build Coastguard Worker */ 209*6dbdd20aSAndroid Build Coastguard Worker translate(point: Point2D): Rect2D { 210*6dbdd20aSAndroid Build Coastguard Worker return new Rect2D({ 211*6dbdd20aSAndroid Build Coastguard Worker top: this.top + point.y, 212*6dbdd20aSAndroid Build Coastguard Worker left: this.left + point.x, 213*6dbdd20aSAndroid Build Coastguard Worker bottom: this.bottom + point.y, 214*6dbdd20aSAndroid Build Coastguard Worker right: this.right + point.x, 215*6dbdd20aSAndroid Build Coastguard Worker }); 216*6dbdd20aSAndroid Build Coastguard Worker } 217*6dbdd20aSAndroid Build Coastguard Worker} 218