mirror of
https://github.com/jlfwong/speedscope.git
synced 2024-12-25 19:31:50 +03:00
404aacfa46
* Switch to `bracketSpacing: false`. * Add prettier-ignore in one case.
320 lines
7.8 KiB
TypeScript
320 lines
7.8 KiB
TypeScript
export function clamp(x: number, minVal: number, maxVal: number) {
|
|
if (x < minVal) return minVal
|
|
if (x > maxVal) return maxVal
|
|
return x
|
|
}
|
|
|
|
export class Vec2 {
|
|
constructor(readonly x: number, readonly y: number) {}
|
|
withX(x: number) {
|
|
return new Vec2(x, this.y)
|
|
}
|
|
withY(y: number) {
|
|
return new Vec2(this.x, y)
|
|
}
|
|
|
|
plus(other: Vec2) {
|
|
return new Vec2(this.x + other.x, this.y + other.y)
|
|
}
|
|
minus(other: Vec2) {
|
|
return new Vec2(this.x - other.x, this.y - other.y)
|
|
}
|
|
times(scalar: number) {
|
|
return new Vec2(this.x * scalar, this.y * scalar)
|
|
}
|
|
timesPointwise(other: Vec2) {
|
|
return new Vec2(this.x * other.x, this.y * other.y)
|
|
}
|
|
dividedByPointwise(other: Vec2) {
|
|
return new Vec2(this.x / other.x, this.y / other.y)
|
|
}
|
|
dot(other: Vec2) {
|
|
return this.x * other.x + this.y * other.y
|
|
}
|
|
equals(other: Vec2) {
|
|
return this.x === other.x && this.y === other.y
|
|
}
|
|
length2() {
|
|
return this.dot(this)
|
|
}
|
|
length() {
|
|
return Math.sqrt(this.length2())
|
|
}
|
|
abs() {
|
|
return new Vec2(Math.abs(this.x), Math.abs(this.y))
|
|
}
|
|
|
|
static min(a: Vec2, b: Vec2) {
|
|
return new Vec2(Math.min(a.x, b.x), Math.min(a.y, b.y))
|
|
}
|
|
|
|
static max(a: Vec2, b: Vec2) {
|
|
return new Vec2(Math.max(a.x, b.x), Math.max(a.y, b.y))
|
|
}
|
|
|
|
static zero = new Vec2(0, 0)
|
|
static unit = new Vec2(1, 1)
|
|
|
|
flatten(): [number, number] {
|
|
return [this.x, this.y]
|
|
}
|
|
}
|
|
|
|
export class AffineTransform {
|
|
constructor(
|
|
readonly m00 = 1,
|
|
readonly m01 = 0,
|
|
readonly m02 = 0,
|
|
readonly m10 = 0,
|
|
readonly m11 = 1,
|
|
readonly m12 = 0,
|
|
) {}
|
|
|
|
withScale(s: Vec2) {
|
|
let {m00, m01, m02, m10, m11, m12} = this
|
|
m00 = s.x
|
|
m11 = s.y
|
|
return new AffineTransform(m00, m01, m02, m10, m11, m12)
|
|
}
|
|
static withScale(s: Vec2) {
|
|
return new AffineTransform().withScale(s)
|
|
}
|
|
scaledBy(s: Vec2) {
|
|
return AffineTransform.withScale(s).times(this)
|
|
}
|
|
getScale() {
|
|
return new Vec2(this.m00, this.m11)
|
|
}
|
|
|
|
withTranslation(t: Vec2) {
|
|
let {m00, m01, m02, m10, m11, m12} = this
|
|
m02 = t.x
|
|
m12 = t.y
|
|
return new AffineTransform(m00, m01, m02, m10, m11, m12)
|
|
}
|
|
static withTranslation(t: Vec2) {
|
|
return new AffineTransform().withTranslation(t)
|
|
}
|
|
getTranslation() {
|
|
return new Vec2(this.m02, this.m12)
|
|
}
|
|
translatedBy(t: Vec2) {
|
|
return AffineTransform.withTranslation(t).times(this)
|
|
}
|
|
|
|
static betweenRects(from: Rect, to: Rect) {
|
|
return AffineTransform.withTranslation(from.origin.times(-1))
|
|
.scaledBy(new Vec2(to.size.x / from.size.x, to.size.y / from.size.y))
|
|
.translatedBy(to.origin)
|
|
}
|
|
|
|
times(other: AffineTransform) {
|
|
const m00 = this.m00 * other.m00 + this.m01 * other.m10
|
|
const m01 = this.m00 * other.m01 + this.m01 * other.m11
|
|
const m02 = this.m00 * other.m02 + this.m01 * other.m12 + this.m02
|
|
|
|
const m10 = this.m10 * other.m00 + this.m11 * other.m10
|
|
const m11 = this.m10 * other.m01 + this.m11 * other.m11
|
|
const m12 = this.m10 * other.m02 + this.m11 * other.m12 + this.m12
|
|
return new AffineTransform(m00, m01, m02, m10, m11, m12)
|
|
}
|
|
|
|
equals(other: AffineTransform) {
|
|
return (
|
|
this.m00 == other.m00 &&
|
|
this.m01 == other.m01 &&
|
|
this.m02 == other.m02 &&
|
|
this.m10 == other.m10 &&
|
|
this.m11 == other.m11 &&
|
|
this.m12 == other.m12
|
|
)
|
|
}
|
|
|
|
timesScalar(s: number) {
|
|
const {m00, m01, m02, m10, m11, m12} = this
|
|
return new AffineTransform(s * m00, s * m01, s * m02, s * m10, s * m11, s * m12)
|
|
}
|
|
|
|
det() {
|
|
const {m00, m01, m02, m10, m11, m12} = this
|
|
const m20 = 0
|
|
const m21 = 0
|
|
const m22 = 1
|
|
|
|
return (
|
|
m00 * (m11 * m22 - m12 * m21) - m01 * (m10 * m22 - m12 * m20) + m02 * (m10 * m21 - m11 * m20)
|
|
)
|
|
}
|
|
|
|
adj() {
|
|
const {m00, m01, m02, m10, m11, m12} = this
|
|
const m20 = 0
|
|
const m21 = 0
|
|
const m22 = 1
|
|
|
|
// Adjugate matrix (a) is the transpose of the
|
|
// cofactor matrix (c).
|
|
//
|
|
// 00 01 02
|
|
// 10 11 12
|
|
// 20 21 22
|
|
|
|
const a00 = /* c00 = */ +(m11 * m22 - m12 * m21)
|
|
const a01 = /* c10 = */ -(m01 * m22 - m02 * m21)
|
|
const a02 = /* c20 = */ +(m01 * m12 - m02 * m11)
|
|
const a10 = /* c01 = */ -(m10 * m22 - m12 * m20)
|
|
const a11 = /* c11 = */ +(m00 * m22 - m02 * m20)
|
|
const a12 = /* c21 = */ -(m00 * m12 - m02 * m10)
|
|
|
|
return new AffineTransform(a00, a01, a02, a10, a11, a12)
|
|
}
|
|
|
|
inverted(): AffineTransform | null {
|
|
const det = this.det()
|
|
if (det === 0) return null
|
|
const adj = this.adj()
|
|
return adj.timesScalar(1 / det)
|
|
}
|
|
|
|
transformVector(v: Vec2) {
|
|
return new Vec2(v.x * this.m00 + v.y * this.m01, v.x * this.m10 + v.y * this.m11)
|
|
}
|
|
|
|
inverseTransformVector(v: Vec2): Vec2 | null {
|
|
const inv = this.inverted()
|
|
if (!inv) return null
|
|
return inv.transformVector(v)
|
|
}
|
|
|
|
transformPosition(v: Vec2) {
|
|
return new Vec2(
|
|
v.x * this.m00 + v.y * this.m01 + this.m02,
|
|
v.x * this.m10 + v.y * this.m11 + this.m12,
|
|
)
|
|
}
|
|
|
|
inverseTransformPosition(v: Vec2): Vec2 | null {
|
|
const inv = this.inverted()
|
|
if (!inv) return null
|
|
return inv.transformPosition(v)
|
|
}
|
|
|
|
transformRect(r: Rect) {
|
|
const size = this.transformVector(r.size)
|
|
const origin = this.transformPosition(r.origin)
|
|
|
|
if (size.x < 0 && size.y < 0) {
|
|
return new Rect(origin.plus(size), size.abs())
|
|
} else if (size.x < 0) {
|
|
return new Rect(origin.withX(origin.x + size.x), size.abs())
|
|
} else if (size.y < 0) {
|
|
return new Rect(origin.withY(origin.y + size.y), size.abs())
|
|
}
|
|
|
|
return new Rect(origin, size)
|
|
}
|
|
|
|
inverseTransformRect(r: Rect): Rect | null {
|
|
const inv = this.inverted()
|
|
if (!inv) return null
|
|
return inv.transformRect(r)
|
|
}
|
|
|
|
flatten(): [number, number, number, number, number, number, number, number, number] {
|
|
// Flatten into GLSL format
|
|
// prettier-ignore
|
|
return [
|
|
this.m00, this.m10, 0,
|
|
this.m01, this.m11, 0,
|
|
this.m02, this.m12, 1,
|
|
]
|
|
}
|
|
}
|
|
|
|
export class Rect {
|
|
constructor(readonly origin: Vec2, readonly size: Vec2) {}
|
|
|
|
isEmpty() {
|
|
return this.width() == 0 || this.height() == 0
|
|
}
|
|
|
|
width() {
|
|
return this.size.x
|
|
}
|
|
height() {
|
|
return this.size.y
|
|
}
|
|
|
|
left() {
|
|
return this.origin.x
|
|
}
|
|
right() {
|
|
return this.left() + this.width()
|
|
}
|
|
top() {
|
|
return this.origin.y
|
|
}
|
|
bottom() {
|
|
return this.top() + this.height()
|
|
}
|
|
|
|
topLeft() {
|
|
return this.origin
|
|
}
|
|
topRight() {
|
|
return this.origin.plus(new Vec2(this.width(), 0))
|
|
}
|
|
|
|
bottomRight() {
|
|
return this.origin.plus(this.size)
|
|
}
|
|
bottomLeft() {
|
|
return this.origin.plus(new Vec2(0, this.height()))
|
|
}
|
|
|
|
withOrigin(origin: Vec2) {
|
|
return new Rect(origin, this.size)
|
|
}
|
|
withSize(size: Vec2) {
|
|
return new Rect(this.origin, size)
|
|
}
|
|
|
|
closestPointTo(p: Vec2) {
|
|
return new Vec2(clamp(p.x, this.left(), this.right()), clamp(p.y, this.top(), this.bottom()))
|
|
}
|
|
|
|
distanceFrom(p: Vec2) {
|
|
return p.minus(this.closestPointTo(p)).length()
|
|
}
|
|
|
|
contains(p: Vec2) {
|
|
return this.distanceFrom(p) === 0
|
|
}
|
|
|
|
hasIntersectionWith(other: Rect) {
|
|
const top = Math.max(this.top(), other.top())
|
|
const bottom = Math.max(top, Math.min(this.bottom(), other.bottom()))
|
|
if (bottom - top === 0) return false
|
|
|
|
const left = Math.max(this.left(), other.left())
|
|
const right = Math.max(left, Math.min(this.right(), other.right()))
|
|
if (right - left === 0) return false
|
|
return true
|
|
}
|
|
|
|
intersectWith(other: Rect): Rect {
|
|
const topLeft = Vec2.max(this.topLeft(), other.topLeft())
|
|
const bottomRight = Vec2.max(topLeft, Vec2.min(this.bottomRight(), other.bottomRight()))
|
|
|
|
return new Rect(topLeft, bottomRight.minus(topLeft))
|
|
}
|
|
|
|
equals(other: Rect) {
|
|
return this.origin.equals(other.origin) && this.size.equals(other.size)
|
|
}
|
|
|
|
static empty = new Rect(Vec2.zero, Vec2.zero)
|
|
static unit = new Rect(Vec2.zero, Vec2.unit)
|
|
static NDC = new Rect(new Vec2(-1, -1), new Vec2(2, 2))
|
|
}
|