Transformations

Transformations

All paths support transformations. Transformations return a new path, allowing method chaining.

Basic Transformations

scaled(s)

Uniform scaling around the origin.

local big = h.fullcircle():scaled(100)

xscaled(s) / yscaled(s)

Non-uniform scaling along x or y axis.

local ellipse = h.fullcircle()
    :scaled(50)
    :xscaled(2)      -- stretch horizontally
    :yscaled(0.5)    -- compress vertically

shifted(dx, dy)

Translation by (dx, dy).

local moved = h.fullcircle()
    :scaled(50)
    :shifted(100, 100)

rotated(angle)

Rotation around the origin (angle in degrees).

local rotated = h.unitsquare()
    :scaled(50)
    :rotated(45)

slanted(s)

Horizontal shear transformation.

local slanted = h.unitsquare()
    :scaled(50)
    :slanted(0.5)

Advanced Transformations

rotatedaround(point, angle)

Rotation around a specific point.

local center = h.point(50, 50)
local rotated = h.unitsquare()
    :scaled(20)
    :shifted(40, 40)
    :rotatedaround(center, 45)

Alternative syntax with coordinates:

:rotatedaround(50, 50, 45)

scaledaround(point, s)

Scaling around a specific point.

local center = h.point(50, 50)
local scaled = h.unitsquare()
    :scaled(30)
    :shifted(35, 35)
    :scaledaround(center, 0.5)

zscaled(a, b)

Complex multiplication: scales by √(a² + b²) and rotates by atan2(b, a).

-- Rotate 45° and keep size (cos 45°, sin 45°)
local rotated = h.unitsquare()
    :scaled(30)
    :zscaled(0.707, 0.707)

Can also use a point:

:zscaled(h.dir(30))    -- rotate 30 degrees

reflectedabout(p1, p2)

Reflection about the line through p1 and p2.

local p1 = h.point(50, 0)
local p2 = h.point(50, 100)

local reflected = h.fullcircle()
    :scaled(30)
    :shifted(20, 50)
    :reflectedabout(p1, p2)

Alternative syntax with coordinates:

:reflectedabout(50, 0, 50, 100)

Transformation Order

Transformations are applied in the order they are called. The order matters!

-- Rotate then shift: rotates around origin, then moves
local a = h.unitsquare()
    :scaled(30)
    :rotated(45)
    :shifted(100, 100)

-- Shift then rotate: moves to position, then rotates around origin
local b = h.unitsquare()
    :scaled(30)
    :shifted(100, 100)
    :rotated(45)

Complete Example

local h = require("hobby")

-- Original
local original = h.fullcircle()
    :scaled(20)
    :shifted(30, 80)
    :stroke("black")

-- Stretched horizontally
local xstretched = h.fullcircle()
    :scaled(20)
    :xscaled(2)
    :shifted(90, 80)
    :stroke("blue")

-- Stretched vertically
local ystretched = h.fullcircle()
    :scaled(20)
    :yscaled(2)
    :shifted(150, 80)
    :stroke("red")

-- Slanted square
local slanted = h.unitsquare()
    :scaled(30)
    :slanted(0.5)
    :shifted(30, 20)
    :stroke("green")

-- Combined transformations
local combo = h.fullcircle()
    :scaled(20)
    :xscaled(1.5)
    :yscaled(0.7)
    :slanted(0.3)
    :shifted(130, 30)
    :stroke("purple")

h.svg()
    :padding(10)
    :add(original)
    :add(xstretched)
    :add(ystretched)
    :add(slanted)
    :add(combo)
    :write("transforms.svg")

Transformations example