xref: /aosp_15_r20/external/perfetto/ui/src/base/geom.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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