Initializing 3D Canvas...

The Vector

1 min read1 page

The Simple Definition

Think of a Vector as an arrow.

This arrow has two distinct properties:

  • Direction: Which way it is pointing (e.g., "Up", "North-East", "Towards the Sun").
  • Magnitude: How long it is (e.g., "5 units long", "10 meters", "1 pixel").
1 min read1 page

Direction & Magnitude

If a Point is an absolute location in space, a Vector is a movement through space. It is the architectural arrow that tells you which way to go, and exactly how far.

While a Point is tethered to the global coordinate system, a Vector is completely untethered. It floats freely. It represents pure direction and magnitude (length), but it has no fixed position.

The Illusion of Location: You can move a vector anywhere in your 3D scene, and as long as its length and direction remain exactly the same, it is mathematically the exact same vector.

A More Generalized View

A vector at its basic is just a list of values:

  • (X, Y, Z) is a vector of 3 values
  • (X, Y) is a vector of 2 values
  • (X, Y, Z, W) is a vector of 4 values
  • and the list goes on.
While it might have different interpretations (physics, ML, geometry), the math behind it is always the same.
2 min read1 page

The Data Structure

In AECO, the most common data structure of a vector is an ordered list of three double-precision floating-point numbers (X, Y, Z).

python
1class Vector3d:
2 """
3 Represents a magnitude and direction in 3D space.
4 """
5 def __init__(self, x: float, y: float, z: float):
6 self.X = float(x)
7 self.Y = float(y)
8 self.Z = float(z)
9
10 def __str__(self):
11 return f"Vector3d({self.X}, {self.Y}, {self.Z})"
12
13 @classmethod
14 def ZAxis(cls):
15 """Gets a unit vector pointing straight up."""
16 return cls(0.0, 0.0, 1.0)

There's nothing more to it, all that follows are methods, functions and formulas using addition, subtraction, multiplication, division, dot products, cross products, normalization, and other mathematical operations that can be performed on vectors.


Geometry wise, everything else is just an abstraction over this simple data structure.

1 min read1 page

Length():

The magnitude (intensity) of the vector.
python
1import math
2
3class Vector3d:
4 @property
5 def Length(self) -> float:
6 """Returns the magnitude of the vector."""
7 return math.sqrt(self.X**2 + self.Y**2 + self.Z**2)
Length
1.50

IsUnitVector():

Returns true if the length is exactly 1.0.
1 min read1 page

LengthSquared():

Returns the squared length of the vector. This is equivalent to X² + Y² + Z².
python
1class Vector3d:
2 def LengthSquared(self) -> float:
3 """Returns the squared magnitude of the vector, skipping sqrt."""
4 return self.X**2 + self.Y**2 + self.Z**2
Vector Length
2.00
Why use it? Calculating the actual length requires math.sqrt, which is computationally expensive. For distance comparisons (e.g. "is this point closer than 5 units?"), we can compare LengthSquared() to 25.0 (5**2) instead, saving CPU cycles in large geometry sets.
1 min read1 page

Unitize():

Normalizes the vector. It forces the length to be exactly 1.0 while keeping the direction identical. Essential for lighting and normals.
X
2.00
Y
-2.00

Reverse():

Flips the vector to point in the exact opposite direction.
python
1class Vector3d:
2 def Reverse(self) -> None:
3 """Flips the vector mathematically."""
4 self.X = -self.X
5 self.Y = -self.Y
6 self.Z = -self.Z
1 min read1 page

CrossProduct(Vector3d):

Calculates a completely new vector that is perfectly perpendicular (90 degrees) to both input vectors.
python
1class Vector3d:
2 def CrossProduct(self, other: 'Vector3d') -> 'Vector3d':
3 """Returns the cross product of two vectors."""
4 return Vector3d(
5 self.Y * other.Z - self.Z * other.Y,
6 self.Z * other.X - self.X * other.Z,
7 self.X * other.Y - self.Y * other.X,
8 )
X
4.10
Y
-1.80
Z
2.60
Applications:
  • Compute slopes, determining which side of a wall is exterior/interior, lighting and rendering, solar analysis, CNC fabrication.
1 min read1 page

DotProduct(Vector3d):

Calculates a single scalar number that represents how aligned two vectors are.
python
1class Vector3d:
2 def DotProduct(self, other: 'Vector3d') -> float:
3 """Returns the dot product of two vectors."""
4 return self.X * other.X + self.Y * other.Y + self.Z * other.Z
X
4.00
Y
-3.00
Z
2.00
Applications:
  • Alignment, projection, similarity, angle, or how much one direction contributes to another.
2 min read1 page

VectorAngle(Vector3d, Vector3d):

Calculates the shortest angle between two vectors in radians. This formula uses the dot product and the magnitudes of both vectors.
python
1import math
2
3class Vector3d:
4 def VectorAngle(self, other: 'Vector3d') -> float:
5 """Returns the angle between two vectors in radians."""
6 dot = self.X * other.X + self.Y * other.Y + self.Z * other.Z
7
8 # Calculate magnitudes
9 mag_self = math.sqrt(self.X**2 + self.Y**2 + self.Z**2)
10 mag_other = math.sqrt(other.X**2 + other.Y**2 + other.Z**2)
11
12 # Protect against division by zero
13 if mag_self == 0 or mag_other == 0:
14 return 0.0
15
16 # Cosine of the angle
17 cos_theta = dot / (mag_self * mag_other)
18
19 # Clamp to handle precision errors
20 cos_theta = max(-1.0, min(1.0, cos_theta))
21
22 return math.acos(cos_theta)
Vector A (X)
4.00
Vector A (Y)
4.00
Vector A (Z)
0.00
2 min read1 page

SignedAngleTo(Vector3d, Vector3d):

Calculates the angle from one vector to another, providing a full +/- 180° range.
Standard vector angle functions (`AngleTo`) only return the absolute shortest angle (0 to 180°). They don't tell you if the rotation is left or right. To know the direction (clockwise vs counter-clockwise), you must provide a reference "normal" vector to look down.
python
1def SignedAngle(v1: Vector3d, v2: Vector3d, normal: Vector3d) -> float:
2 """Calculates the signed angle between v1 and v2 around a normal."""
3 # Cross product gives the axis of rotation
4 cross = Vector3d.CrossProduct(v1, v2)
5 angle = math.atan2(cross.Length, v1 * v2)
6
7 # If the cross product points opposite to our reference normal,
8 # it means the rotation is clockwise (negative).
9 if normal * cross < 0:
10 angle = -angle
11
12 return angle
Vector A Angle Z (deg)
45.00
3 min read1 page

IsParallelTo(Vector3d):

Determines if another vector has the exact same or exact opposite direction, within a given tolerance.
python
1import math
2
3class Vector3d:
4 def IsParallelTo(self, other: 'Vector3d', tolerance: float = 1e-6) -> int:
5 """
6 Checks if two vectors are parallel.
7 Returns:
8 +1 if parallel and in the same direction.
9 -1 if parallel and in opposite directions.
10 0 if not parallel.
11 """
12 # Cross product of parallel vectors is zero (or near zero)
13 cross_x = self.Y * other.Z - self.Z * other.Y
14 cross_y = self.Z * other.X - self.X * other.Z
15 cross_z = self.X * other.Y - self.Y * other.X
16
17 cross_mag_sq = cross_x**2 + cross_y**2 + cross_z**2
18
19 if cross_mag_sq <= tolerance**2:
20 # Check dot product to see if they point in the same direction
21 dot = self.X * other.X + self.Y * other.Y + self.Z * other.Z
22 return 1 if dot > 0 else -1
23
24 return 0
Scale Vector A
1.50
Offset Vector A (Y)
0.00

Because floating-point math is imprecise, we rarely check if two values are exactly equal. Instead, we use a small tolerance value to check if they are "close enough".

3 min read1 page

IsPerpendicularTo(Vector3d):

Determines if another vector is perpendicular (orthogonal) to this vector within a given tolerance.
python
1class Vector3d:
2 def IsPerpendicularTo(self, other: 'Vector3d', tolerance: float = 1e-6) -> bool:
3 """
4 Checks if two vectors are perpendicular (orthogonal).
5 Returns:
6 True if the vectors are perpendicular, False otherwise.
7 """
8 # Two vectors are perpendicular if their dot product is zero.
9 # We normalize the check using their lengths to support scaled vectors.
10 mag_self = self.Length
11 mag_other = other.Length
12
13 if mag_self == 0 or mag_other == 0:
14 return False
15
16 dot = self.X * other.X + self.Y * other.Y + self.Z * other.Z
17
18 # Check if dot product is close to zero relative to lengths
19 return abs(dot) / (mag_self * mag_other) <= tolerance
Angle (deg)
90.00

Perpendicularity means the angle between the vectors is exactly 90° (or 270°). The dot product of two perpendicular unit vectors is always exactly 0.0.

3 min read1 page

Orthogonal():

Generates a vector that is guaranteed to be perpendicular (orthogonal) to the given vector.
python
1class Vector3d:
2 def Orthogonal(self) -> 'Vector3d':
3 """
4 Generates a deterministic vector that is perpendicular (orthogonal) to this vector.
5 """
6 # Pick helper vector based on which coordinate is smallest
7 # to avoid parallel cross-products (which result in zero-length vectors)
8 x = abs(self.X)
9 y = abs(self.Y)
10 z = abs(self.Z)
11
12 if x <= y and x <= z:
13 helper = Vector3d(1.0, 0.0, 0.0)
14 elif y <= x and y <= z:
15 helper = Vector3d(0.0, 1.0, 0.0)
16 else:
17 helper = Vector3d(0.0, 0.0, 1.0)
18
19 # Cross product generates a perpendicular vector
20 perp = Vector3d.CrossProduct(self, helper)
21 perp.Unitize()
22 return perp
Vector X
2.00
Vector Y
1.50

Because a single vector has infinite perpendicular vectors in 3D space, this method picks a stable helper axis to generate a single consistent perpendicular vector.

1 min read1 page

Addition(Vector3d):

Adding two vectors together is known as head-to-tail addition. If Vector A tells you to walk 3 steps forward, and Vector B tells you to walk 2 steps right, adding them together gives you a new vector (Vector C) that goes diagonally.
python
1class Vector3d:
2 def Add(self, other: 'Vector3d') -> 'Vector3d':
3 """Returns the addition of two vectors."""
4 return Vector3d(self.X + other.X, self.Y + other.Y, self.Z + other.Z)
X
-2.30
Y
0.90
Z
2.00
1 min read1 page

Subtraction(Vector3d):

Subtracts one vector from another. It's the primary way to find the direction and distance between two points.
python
1class Vector3d:
2 def Subtraction(self, other: 'Vector3d') -> 'Vector3d':
3 """Returns the result of subtracting vector 'other' from 'self'."""
4 return Vector3d(
5 self.X - other.X,
6 self.Y - other.Y,
7 self.Z - other.Z
8 )
9
10# Often used with points:
11# Vector = TargetPoint - SourcePoint
Point B (X)
-2.00
Point B (Y)
3.00
2 min read1 page

ProjectOnVector(Vector3d):

Projects a vector onto another vector. This effectively finds the "shadow" or component of the vector that lies perfectly along the target direction.
python
1class Vector3d:
2 def ProjectOnVector(self, other: 'Vector3d') -> 'Vector3d':
3 """Projects this vector onto another vector."""
4 # Find unit vector of the target
5 mag_other = (other.X**2 + other.Y**2 + other.Z**2)**0.5
6 if mag_other == 0:
7 return Vector3d(0, 0, 0)
8
9 unit_other = Vector3d(other.X / mag_other, other.Y / mag_other, other.Z / mag_other)
10
11 # Calculate dot product
12 dot = self.X * unit_other.X + self.Y * unit_other.Y + self.Z * unit_other.Z
13
14 # Scale unit vector by dot product
15 return Vector3d(unit_other.X * dot, unit_other.Y * dot, unit_other.Z * dot)
Rotate Vector A
1.00
2 min read1 page

Lerp(Vector3d, float):

Linearly interpolates between two vectors. It takes a parameter `t` (typically 0.0 to 1.0) and blends the two directions and magnitudes.
python
1class Vector3d:
2 def Lerp(self, other: 'Vector3d', t: float) -> 'Vector3d':
3 """Linearly interpolates between two vectors based on parameter t (0.0 to 1.0)."""
4 # Clamping t between 0 and 1
5 t = max(0.0, min(1.0, t))
6
7 return Vector3d(
8 self.X + (other.X - self.X) * t,
9 self.Y + (other.Y - self.Y) * t,
10 self.Z + (other.Z - self.Z) * t
11 )
Parameter (t)
0.50
2 min read1 page

Vector Slerp (Spherical Interpolation):

Unlike standard Linear Interpolation (Lerp) which draws a straight line between two vectors and changes their length, Slerp interpolates along an arc, preserving the vector's magnitude (length).
python
1def VectorSlerp(vecA: 'Vector3d', vecB: 'Vector3d', t: float) -> 'Vector3d':
2 """Spherical Linear Interpolation between two vectors."""
3 # Find rotation axis and angle
4 angle = Vector3d.VectorAngle(vecA, vecB)
5 axis = Vector3d.CrossProduct(vecA, vecB)
6
7 # Rotate vecA towards vecB by a fraction 't' of the angle
8 result = vecA.Clone()
9 result.Rotate(angle * t, axis)
10 return result
Interpolate (t)
0.50
1 min read1 page

Move(Vector3d):

You cannot translate a vector! Because a vector only represents direction and magnitude, moving a vector's visual representation does absolutely nothing to its underlying properties.
python
1# This is semantically vector addition, but usually labeled as Move
2def Translate(self, delta: 'Vector3d') -> 'Vector3d':
3 return Vector3d(self.X + delta.X, self.Y + delta.Y, self.Z + delta.Z)
X
0.00
Y
0.00
In most 3D libraries, points and vectors can be added
1 min read1 page

Scale(float):

Scaling a vector is extremely common. It directly multiplies the vector's Length (magnitude). If you have a vector representing the wind blowing North at 5mph, scaling it by 2.0 results in a vector blowing North at 10mph.
python
1class Vector3d:
2 def Scale(self, s: float) -> 'Vector3d':
3 """Scales the vector by a factor."""
4 return Vector3d(self.X * s, self.Y * s, self.Z * s)
Scale
1.50
2 min read1 page

Rotate(Vector3d, float):

Rotating a vector is the primary way of altering its direction. By rotating a vector around an axis, its magnitude stays exactly the same, but it points somewhere entirely new.
python
1import math
2
3class Vector3d:
4 def Rotate(self, axis: 'Vector3d', angle_degrees: float) -> 'Vector3d':
5 """Rotates the vector around the given axis using Rodrigues' Formula."""
6
7 # Ensure axis is unitized
8 k = Vector3d(axis)
9 k.Unitize()
10
11 theta = math.radians(angle_degrees)
12 cos_t = math.cos(theta)
13 sin_t = math.sin(theta)
14
15 # Formula: V_rot = V*cos(θ) + (K × V)*sin(θ) + K*(K · V)*(1 - cos(θ))
16 cross_kv = Vector3d.CrossProduct(k, self)
17 dot_kv = k * self
18
19 return self * cos_t + cross_kv * sin_t + k * (dot_kv * (1 - cos_t))
For smooth rotations free from gimbal lock, see the Quaternion article.
Angle
60.00
2 min read1 page

Reflect(Vector3d, Vector3d):

Calculates the reflection vector when an incident ray hits a surface with a given normal.
python
1def Reflect(incident: Vector3d, normal: Vector3d) -> Vector3d:
2 """Calculates the reflection vector off a surface."""
3 # Ensure normal is unitized
4 n = Vector3d(normal)
5 n.Unitize()
6
7 # Mathematical reflection formula: R = V - 2(V·N)N
8 # (Where V is pointing TOWARDS the surface)
9 dot_product = incident * n
10 reflection = incident - 2 * dot_product * n
11
12 return reflection
This operation is the core of ray tracing, acoustic simulation, and facade glare analysis. It takes an incoming vector (incident) and a surface orientation (normal) to compute the outgoing bounce.
Surface Tilt (deg)
15.00
1 min read1 page

Rejection(Vector3d, Vector3d):

Extracts the component of a vector that is perfectly perpendicular to a reference axis.
python
1def Rejection(v: Vector3d, axis: Vector3d) -> Vector3d:
2 """Calculates the rejection vector of v relative to axis."""
3 projection = Projection(v, axis)
4 return v - projection
Rejection is the mathematical complement to Projection. Together, they perfectly decompose any vector into a parallel component and a perpendicular component. This is critical for structural force decomposition and physics simulations.
Vector X
3.00
Vector Y
4.00