Reference

std/linear/vector2

std/linear/src/vector2.trb

Vector2, the two-component vector, and the three layers of members its scalar decides.

There is one type and not two. Vector2 is the Float vector, Vector2<Int> is the pixel or tile vector, and Vector2<Fixed> is the deterministic one; what each of them can do follows from the scalar's bound. Arithmetic, dot, lengthSquared and the grid operations need only Numeric; a sign and a Manhattan length need Signed; length, normalized, angle and rotated need Real, and a grid vector therefore never gets a square root by accident.

type Vector2

type Vector2<Scalar: Numeric = Float> with Add, Subtract, Multiply<Scalar>, Divide<Scalar>

A point, a direction or a size in the plane, over whatever scalar the program counts in.

Nothing here knows which way is up. y is the second component and that is all it is: a program that draws with y growing downwards and one that draws with y growing upwards use the same vectors, and only the program's own rotated calls look different to a viewer.

Examples

const step = Vector2 3.0, 4.0
const tile = Vector2 3, 4
print "{step.length()} {tile.dot(tile)} {step.add(step)}"

Pitfalls

  • Vector2(1, 2) is a Vector2<Int> and Vector2(1.0, 2.0) is a Vector2<Float>: the literals decide, and the two do not mix. Cross the boundary on purpose with Vector2.toFloat and Vector2.rounded.
  • Overflow panics, here as everywhere. lengthSquared on a Vector2<Int> squares both components, so a pair of coordinates above three billion leaves the range of an Int although the vector itself is ordinary.
  • The constants (Vector2.zero, Vector2.one, Vector2.unitX, Vector2.unitY) exist for every scalar and take it from the expected type: const tile: Vector2<Int> = Vector2.zero is the grid's zero. With nothing expected, the bare Vector2.zero is the Float one, the declared default.

Related

field x

x: Scalar

The first component.

field y

y: Scalar

The second component.

fn filled

static fn filled(value: Scalar): Vector2<Scalar>

Both components set to the same value: the generic way to write a constant vector, where a literal cannot be written at all.

print Vector2.filled 2.0

const zero

static zero: Vector2<Scalar> = Vector2 Scalar.zero, Scalar.zero

Both components zero, over whichever scalar is asked for: Vector2<Int>.zero, Vector2<Fixed>.zero.

const one

static one: Vector2<Scalar> = Vector2 Scalar.one, Scalar.one

Both components one.

const unitX

static unitX: Vector2<Scalar> = Vector2 Scalar.one, Scalar.zero

The first basis vector.

const unitY

static unitY: Vector2<Scalar> = Vector2 Scalar.zero, Scalar.one

The second basis vector.

fn add

fn add(other: Vector2<Scalar>): Vector2<Scalar>

The sum, component by component.

fn subtract

fn subtract(other: Vector2<Scalar>): Vector2<Scalar>

The difference, component by component.

fn multiply

fn multiply(other: Scalar): Vector2<Scalar>

Every component multiplied by the scalar.

fn divide

fn divide(other: Scalar): Vector2<Scalar>

Every component divided by the scalar. Panics on a division by zero.

fn dot

fn dot(other: Vector2<Scalar>): Scalar

The dot product: length * other.length * cosine(angle between them), without a square root and without an angle. Zero exactly where the two are perpendicular, positive where they point the same way.

fn lengthSquared

fn lengthSquared(): Scalar

The square of the length. This is the comparison to reach for: sorting by distance, a radius test and a "did it move at all" test all work on it, and none of them needs the root that Vector2.length would take.

fn cross

fn cross(other: Vector2<Scalar>): Scalar

The one number a cross product has in the plane: x * other.y - y * other.x. It is the signed area of the parallelogram the two span, so its sign says which side of self the other vector is on, and it is zero exactly where the two are parallel.

fn scaled

fn scaled(by: Vector2<Scalar>): Vector2<Scalar>

Component by component, which is what scaling a size by a size means.

fn divided

fn divided(by: Vector2<Scalar>): Vector2<Scalar>

Component by component. Panics where a component of by is zero.

fn min

fn min(other: Vector2<Scalar>): Vector2<Scalar>

The smaller of each pair of components: the corner of the box that holds both.

fn max

fn max(other: Vector2<Scalar>): Vector2<Scalar>

The larger of each pair of components.

fn clamped

fn clamped(low: Vector2<Scalar>, high: Vector2<Scalar>): Vector2<Scalar>

Every component pulled into the box the two corners span.

fn withX

fn withX(value: Scalar): Vector2<Scalar>

The same vector with another first component.

fn withY

fn withY(value: Scalar): Vector2<Scalar>

The same vector with another second component.

fn isZero

fn isZero(): Bool

Whether both components are zero.

fn largestComponent

fn largestComponent(): Scalar

The larger of the two components.

fn smallestComponent

fn smallestComponent(): Scalar

The smaller of the two components.

fn sum

fn sum(): Scalar

Both components added together: the area of a box this size is Vector2.scaled instead.

extend Vector2<Scalar> with Negate

extend<Scalar: Signed> Vector2<Scalar> with Negate

A vector of a signed scalar can be turned around, and that is what a direction needs.

fn negate

fn negate(): Vector2<Scalar>

Every component with its sign flipped.

extend Vector2<Scalar>

extend<Scalar: Signed> Vector2<Scalar>

What a sign buys: a distance that needs no root, and the quarter turn that needs no trigonometry.

fn absolute

fn absolute(): Vector2<Scalar>

Every component without its sign.

fn manhattanLength

fn manhattanLength(): Scalar

The distance along the axes, |x| + |y|: the number of steps on a four-neighbour grid, and the cheapest distance there is.

print Vector2(3, -4).manhattanLength()

fn manhattanDistanceTo

fn manhattanDistanceTo(other: Vector2<Scalar>): Scalar

The Vector2.manhattanLength of the step from here to there.

fn perpendicular

fn perpendicular(): Vector2<Scalar>

A quarter turn from the first axis towards the second, (-y, x). Exact for every scalar, integers included, which is why a right angle in this library is this and not a rotation by Angle.quarterTurn.

extend Vector2<Scalar>

extend<Scalar: Real> Vector2<Scalar>

What a root and an angle buy: lengths, directions and interpolation.

fn length

fn length(): Scalar

The euclidean length. Vector2.lengthSquared is the one to compare with.

fn distanceTo

fn distanceTo(other: Vector2<Scalar>): Scalar

The distance from here to there.

fn distanceSquaredTo

fn distanceSquaredTo(other: Vector2<Scalar>): Scalar

The square of the distance from here to there, without the root.

fn normalized

fn normalized(): Vector2<Scalar>

The same direction with length one. A vector that is already zero answers itself, because there is no direction to keep and a division by zero would be a worse answer than the honest one.

print Vector2(3.0, 4.0).normalized()

fn withLength

fn withLength(value: Scalar): Vector2<Scalar>

The same direction with the length given. A zero vector stays zero.

fn angle

fn angle(): Angle<Scalar>

The direction, measured from the first axis towards the second. A zero vector answers no rotation.

fn angleTo

fn angleTo(other: Vector2<Scalar>): Angle<Scalar>

The rotation that takes this direction onto the other one, in (-pi, pi].

fn rotated

fn rotated(by: Angle<Scalar>): Vector2<Scalar>

Turned by the angle, from the first axis towards the second. Whether a viewer sees that as clockwise depends on which way the program draws its second axis, and this library does not decide that.

print Vector2(1.0, 0.0).rotated(by: Angle.degrees(90.0)).isCloseTo(Vector2(0.0, 1.0), tolerance: 0.0001)

fn rotatedAround

fn rotatedAround(center: Vector2<Scalar>, by: Angle<Scalar>): Vector2<Scalar>

Turned around the given point instead of around the origin.

fn interpolated

fn interpolated(toward: Vector2<Scalar>, by: Scalar): Vector2<Scalar>

The point factor of the way from here to there: 0 is here, 1 is there, and a factor outside [0, 1] carries on past either end.

fn projectedOnto

fn projectedOnto(other: Vector2<Scalar>): Vector2<Scalar>

The part of this vector that lies along the other one. Panics where the other one is zero.

fn reflected

fn reflected(normal: Vector2<Scalar>): Vector2<Scalar>

Mirrored in the line through the origin whose normal is given. The normal is expected to have length one.

fn isCloseTo

fn isCloseTo(other: Vector2<Scalar>, tolerance: Scalar): Bool

Whether every component is within tolerance of the other vector's.

extend Vector2<Float>

extend Vector2<Float>

The float vector: the three ways down to a grid.

fn rounded

fn rounded(): Vector2<Int>

Every component rounded to the nearest whole number, halves away from zero.

print Vector2(1.5, -1.5).rounded()

Panics

When a component is not a number or its rounding does not fit an Int, as wholeOf says.

fn floored

fn floored(): Vector2<Int>

Every component rounded towards negative infinity: which cell of a grid the point is in.

Panics

When a component is not a number or its rounding does not fit an Int, as wholeOf says.

fn ceiling

fn ceiling(): Vector2<Int>

Every component rounded towards positive infinity.

Panics

When a component is not a number or its rounding does not fit an Int, as wholeOf says.

extend Vector2<Int>

extend Vector2<Int>

The grid vector: the two ways up to a scalar that has fractions.

fn toFloat

fn toFloat(): Vector2<Float>

The same vector over Float, exactly.

fn toFixed

fn toFixed(): Vector2<Fixed>

The same vector over Fixed, exactly: the step from a grid into the deterministic scalar.