Skip to main content

Geometric Transformations

Introduction

Geometric transformations are operations that change the position, size, orientation, or shape of geometric objects while preserving certain properties. These transformations are fundamental building blocks in computer graphics, game development, robotics, and many other fields.

In this tutorial, we'll explore the most common geometric transformations, understand their mathematical basis, and learn how to implement them in code. We'll cover:

  • Translation
  • Rotation
  • Scaling
  • Reflection
  • Shearing
  • Homogeneous coordinates
  • Combining transformations

Basic Concepts

Before diving into specific transformations, let's understand what we're working with:

  • Points: Positions in space, typically represented as coordinates (x, y) in 2D or (x, y, z) in 3D.
  • Vectors: Quantities with magnitude and direction, also represented using coordinates.
  • Matrices: Arrays of numbers arranged in rows and columns, used to efficiently represent transformations.

Translation

Translation is the simplest transformation—it moves an object from one position to another along a straight line.

Mathematical Representation

In 2D, to translate a point (x, y) by tx units along the x-axis and ty units along the y-axis, we compute:

x' = x + tx
y' = y + ty

Implementation

Here's how to implement translation in JavaScript:

javascript
function translate(point, tx, ty) {
return {
x: point.x + tx,
y: point.y + ty
};
}

// Example usage
const point = { x: 3, y: 4 };
const translatedPoint = translate(point, 2, -1);
console.log(translatedPoint); // Output: { x: 5, y: 3 }

For translating multiple points (like vertices of a polygon), we apply the same operation to each point:

javascript
function translatePolygon(points, tx, ty) {
return points.map(point => translate(point, tx, ty));
}

// Example usage
const square = [
{ x: 0, y: 0 },
{ x: 0, y: 1 },
{ x: 1, y: 1 },
{ x: 1, y: 0 }
];
const translatedSquare = translatePolygon(square, 2, 3);
console.log(translatedSquare);
// Output: [
// { x: 2, y: 3 },
// { x: 2, y: 4 },
// { x: 3, y: 4 },
// { x: 3, y: 3 }
// ]

Rotation

Rotation transforms an object by rotating it around a specified point (usually the origin) by a given angle.

Mathematical Representation

To rotate a point (x, y) counterclockwise by an angle θ around the origin, we use:

x' = x * cos(θ) - y * sin(θ)
y' = x * sin(θ) + y * cos(θ)

Implementation

Here's how to implement rotation in JavaScript:

javascript
function rotate(point, angleInDegrees, centerOfRotation = { x: 0, y: 0 }) {
// Convert degrees to radians
const angleInRadians = (angleInDegrees * Math.PI) / 180;

// Translate point to origin
const translatedX = point.x - centerOfRotation.x;
const translatedY = point.y - centerOfRotation.y;

// Rotate point
const rotatedX = translatedX * Math.cos(angleInRadians) - translatedY * Math.sin(angleInRadians);
const rotatedY = translatedX * Math.sin(angleInRadians) + translatedY * Math.cos(angleInRadians);

// Translate point back
return {
x: rotatedX + centerOfRotation.x,
y: rotatedY + centerOfRotation.y
};
}

// Example usage
const point = { x: 1, y: 0 };
const rotatedPoint = rotate(point, 90); // 90-degree rotation around origin
console.log(rotatedPoint); // Output approximately: { x: 0, y: 1 }

Scaling

Scaling changes the size of an object, either uniformly in all directions or with different factors along different axes.

Mathematical Representation

To scale a point (x, y) by factors sx along the x-axis and sy along the y-axis:

x' = x * sx
y' = y * sy

Implementation

Here's how to implement scaling in JavaScript:

javascript
function scale(point, sx, sy, centerOfScaling = { x: 0, y: 0 }) {
// Translate to origin
const translatedX = point.x - centerOfScaling.x;
const translatedY = point.y - centerOfScaling.y;

// Scale
const scaledX = translatedX * sx;
const scaledY = translatedY * sy;

// Translate back
return {
x: scaledX + centerOfScaling.x,
y: scaledY + centerOfScaling.y
};
}

// Example usage
const point = { x: 2, y: 3 };
const scaledPoint = scale(point, 2, 0.5);
console.log(scaledPoint); // Output: { x: 4, y: 1.5 }

Reflection

Reflection creates a mirror image of an object across a line (in 2D) or a plane (in 3D).

Mathematical Representation

For reflection across the x-axis:

x' = x
y' = -y

For reflection across the y-axis:

x' = -x
y' = y

Implementation

javascript
function reflectAcrossX(point) {
return { x: point.x, y: -point.y };
}

function reflectAcrossY(point) {
return { x: -point.x, y: point.y };
}

// More general reflection across a line y = mx + b
function reflectAcrossLine(point, m, b) {
// Calculate perpendicular line through the point
const perpM = -1 / m;
const perpB = point.y - perpM * point.x;

// Find intersection point of the two lines
const ix = (b - perpB) / (perpM - m);
const iy = m * ix + b;

// Calculate the reflection
return {
x: 2 * ix - point.x,
y: 2 * iy - point.y
};
}

// Example usage
const point = { x: 3, y: 4 };
console.log(reflectAcrossX(point)); // Output: { x: 3, y: -4 }
console.log(reflectAcrossY(point)); // Output: { x: -3, y: 4 }

Shearing

Shearing transforms an object, shifting it in a direction parallel to an axis by an amount proportional to the coordinate along another axis.

Mathematical Representation

For shearing along the x-axis:

x' = x + ky
y' = y

For shearing along the y-axis:

x' = x
y' = y + kx

Implementation

javascript
function shearX(point, k) {
return { x: point.x + k * point.y, y: point.y };
}

function shearY(point, k) {
return { x: point.x, y: point.y + k * point.x };
}

// Example usage
const point = { x: 2, y: 3 };
console.log(shearX(point, 0.5)); // Output: { x: 3.5, y: 3 }
console.log(shearY(point, 0.5)); // Output: { x: 2, y: 4 }

Homogeneous Coordinates

Homogeneous coordinates provide a unified way to represent all the transformations we've discussed using matrices.

In homogeneous coordinates, a 2D point (x, y) becomes (x, y, 1), and transformations can be represented as 3×3 matrices.

Matrix Representation of Transformations

Here are the matrix forms of the transformations we've covered:

Translation by (tx, ty):

[1  0  tx]
[0 1 ty]
[0 0 1]

Rotation by angle θ:

[cos(θ)  -sin(θ)  0]
[sin(θ) cos(θ) 0]
[0 0 1]

Scaling by factors (sx, sy):

[sx  0   0]
[0 sy 0]
[0 0 1]

Implementation

Let's implement a general transformation function using matrices:

javascript
function matrixMultiply(matrix1, matrix2) {
const result = [];

for (let i = 0; i < matrix1.length; i++) {
result[i] = [];
for (let j = 0; j < matrix2[0].length; j++) {
let sum = 0;
for (let k = 0; k < matrix1[0].length; k++) {
sum += matrix1[i][k] * matrix2[k][j];
}
result[i][j] = sum;
}
}

return result;
}

function transformPoint(point, transformMatrix) {
// Convert point to homogeneous coordinates
const pointMatrix = [[point.x], [point.y], [1]];

// Apply transformation
const resultMatrix = matrixMultiply(transformMatrix, pointMatrix);

// Convert back from homogeneous coordinates
return { x: resultMatrix[0][0], y: resultMatrix[1][0] };
}

// Example - Create a translation matrix
function createTranslationMatrix(tx, ty) {
return [
[1, 0, tx],
[0, 1, ty],
[0, 0, 1]
];
}

// Example usage
const point = { x: 2, y: 3 };
const translationMatrix = createTranslationMatrix(3, 4);
const translatedPoint = transformPoint(point, translationMatrix);
console.log(translatedPoint); // Output: { x: 5, y: 7 }

Combining Transformations

One of the key advantages of matrix representation is the ability to combine multiple transformations by multiplying their matrices.

javascript
function createRotationMatrix(angleInDegrees) {
const angleInRadians = (angleInDegrees * Math.PI) / 180;
const cos = Math.cos(angleInRadians);
const sin = Math.sin(angleInRadians);
return [
[cos, -sin, 0],
[sin, cos, 0],
[0, 0, 1]
];
}

function createScalingMatrix(sx, sy) {
return [
[sx, 0, 0],
[0, sy, 0],
[0, 0, 1]
];
}

// Combine a rotation followed by a translation
const rotationMatrix = createRotationMatrix(45);
const translationMatrix = createTranslationMatrix(3, 4);

// Combined transformation: first rotate, then translate
const combinedMatrix = matrixMultiply(translationMatrix, rotationMatrix);

// Apply to a point
const point = { x: 1, y: 0 };
const transformedPoint = transformPoint(point, combinedMatrix);
console.log(transformedPoint);
// Output will be the point (1, 0) rotated 45° then translated by (3, 4)

Real-World Applications

Game Development

In game development, geometric transformations are used to move characters, rotate objects, and implement camera views:

javascript
class GameObject {
constructor(x, y, width, height) {
this.position = { x, y };
this.width = width;
this.height = height;
this.rotation = 0; // in degrees
this.scale = { x: 1, y: 1 };
}

getTransformMatrix() {
// First scale, then rotate, then translate
const scaleMatrix = createScalingMatrix(this.scale.x, this.scale.y);
const rotationMatrix = createRotationMatrix(this.rotation);
const translationMatrix = createTranslationMatrix(this.position.x, this.position.y);

// Combine matrices: scale -> rotate -> translate
let transformMatrix = matrixMultiply(rotationMatrix, scaleMatrix);
transformMatrix = matrixMultiply(translationMatrix, transformMatrix);

return transformMatrix;
}

render(ctx) {
ctx.save();

// Apply transformations
ctx.translate(this.position.x, this.position.y);
ctx.rotate((this.rotation * Math.PI) / 180);
ctx.scale(this.scale.x, this.scale.y);

// Draw object centered at origin
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);

ctx.restore();
}
}

// Example usage in a game loop
const player = new GameObject(100, 100, 20, 20);
player.rotation = 45;

function gameLoop() {
// Update player position, rotation, scale
player.position.x += 1;
player.rotation += 1;

// Render the player
player.render(ctx);

requestAnimationFrame(gameLoop);
}

Computer Graphics

Image processing applications use transformations to adjust images:

javascript
function applyTransformToImage(imageData, transformMatrix) {
const width = imageData.width;
const height = imageData.height;
const resultData = new ImageData(width, height);

// For each pixel in the result image
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// Apply inverse transform to find the source pixel
const inverseMatrix = invertMatrix(transformMatrix);
const sourcePoint = transformPoint({ x, y }, inverseMatrix);

const sourceX = Math.round(sourcePoint.x);
const sourceY = Math.round(sourcePoint.y);

// If the source point is within the image bounds
if (sourceX >= 0 && sourceX < width && sourceY >= 0 && sourceY < height) {
// Copy the pixel data
const sourceIndex = (sourceY * width + sourceX) * 4;
const targetIndex = (y * width + x) * 4;

resultData.data[targetIndex] = imageData.data[sourceIndex]; // R
resultData.data[targetIndex + 1] = imageData.data[sourceIndex + 1]; // G
resultData.data[targetIndex + 2] = imageData.data[sourceIndex + 2]; // B
resultData.data[targetIndex + 3] = imageData.data[sourceIndex + 3]; // A
}
}
}

return resultData;
}

// Example usage (in a web context)
function rotateImage(imageElement, angleDegrees) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = imageElement.width;
canvas.height = imageElement.height;

// Draw the original image
ctx.drawImage(imageElement, 0, 0);

// Get the image data
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

// Create rotation matrix
const rotationMatrix = createRotationMatrix(angleDegrees);

// Apply transformation
const rotatedImageData = applyTransformToImage(imageData, rotationMatrix);

// Put the transformed image back on the canvas
ctx.putImageData(rotatedImageData, 0, 0);

return canvas;
}

Map Projections and GIS

Geometric transformations are crucial in map projections, converting positions on a globe to positions on a flat map:

javascript
// A simple Mercator projection
function mercatorProjection(latitude, longitude) {
// Convert latitude/longitude to radians
const latRad = (latitude * Math.PI) / 180;
const lonRad = (longitude * Math.PI) / 180;

// Mercator projection formula
const x = lonRad;
const y = Math.log(Math.tan(Math.PI / 4 + latRad / 2));

return { x, y };
}

// Convert to pixel coordinates for a 800x600 map
function latLongToPixel(latitude, longitude, mapWidth, mapHeight) {
const projection = mercatorProjection(latitude, longitude);

// Map from projection coordinates to pixel coordinates
const pixelX = (projection.x + Math.PI) * (mapWidth / (2 * Math.PI));
const pixelY = (mapHeight / 2) - (projection.y * mapHeight / (2 * Math.PI));

return { x: pixelX, y: pixelY };
}

// Example: Plot New York City on a map
const nyc = { latitude: 40.7128, longitude: -74.0060 };
const pixelCoordinates = latLongToPixel(nyc.latitude, nyc.longitude, 800, 600);
console.log(`NYC is at pixel (${pixelCoordinates.x}, ${pixelCoordinates.y})`);

Summary

Geometric transformations allow us to change the position, orientation, size, and shape of objects in a coordinate space. We've covered:

  • Translation: Moving objects in a straight line
  • Rotation: Turning objects around a point
  • Scaling: Changing an object's size
  • Reflection: Creating mirror images
  • Shearing: Skewing objects along an axis
  • Homogeneous Coordinates: A unified approach using matrices
  • Combining Transformations: Using matrix multiplication to apply multiple transformations efficiently

These concepts are fundamental to computer graphics, game development, robotics, and many other fields. By understanding how to implement and combine transformations, you have a powerful toolset for manipulating geometric objects programmatically.

Exercises

  1. Implement a function that reflects a point across an arbitrary line defined by two points.
  2. Create a function to compute the center point of a polygon.
  3. Implement a function that applies a rotation around an arbitrary point (not just the origin).
  4. Write a function that transforms a rectangle to a parallelogram using shear.
  5. Create an interactive web page where users can apply different transformations to a shape using sliders.
  6. Implement a basic 2D physics engine that applies transformations to rigid bodies.
  7. Write a function that tests whether a transformed polygon contains a given point.

Additional Resources

  • Linear Algebra Books: For a deeper mathematical understanding, explore textbooks on linear algebra.
  • Computer Graphics Literature: Books like "Computer Graphics: Principles and Practice" provide comprehensive coverage.
  • Online Platforms: Interactive platforms like GeoGebra allow you to experiment with transformations visually.
  • Graphics Libraries: Explore libraries like Three.js (JavaScript), OpenGL, or Processing for practical implementations.

Happy coding and transforming!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)