# Affine transformations

An affine transformation is a geometrical transformation that preserves lines (they remain straight). It may alter distances, angles, scales, orientations. Affine transforms are linear transformations and commonly implemented using matrix algebra.

The `Transform2d` class in djutils-draw performs affine transforms on 2D objects; the `Transform3d` class operates on 3D objects. Unlike all other objects in the djutils-draw package, the transform objects are mutable. The djutils-draw transformations are constructed as the identity transformation, then modified by applying some sequence of translations, rotations, shear-operations and reflections. As each of these modifications of a transform returns the resulting transform; these operations can be chained. Finally the transformation can operate on a `Point` or `Bounds` object or on an `Iterator<Point>`. (This will probably be extended to all `Drawable` objects.)

Setting up a `Transform2d` or `Transform3d` involves some math that is relatively expensive when rotations are involved. Subsequently applying such a transformation to coordinates is very fast as that only involves a few multiplications and additions.

#### Translating and Rotating

In this example, a unity Transform3d object is constructed and then a translation is added to it, and then a rotation. The net effect on the `Point3d` is that is first rotated and then translated.

``````    Point3d point = new Point3d(1, 0, 0);
System.out.println(point);
Transform3d transform1 = new Transform3d();
transform1.translate(2, 5, 8); // Translate
System.out.println(transform1.transform(point));
transform1.rotY(Math.PI / 2);
System.out.println(transform1.transform(point));``````

Outputs

```Point3d [x=1.000000, y=0.000000, z=0.000000]
Point3d [x=3.000000, y=5.000000, z=8.000000]
Point3d [x=2.000000, y=5.000000, z=7.000000]
```

The resulting transformation could have been created in a single line of Java code using chaining:

``````    Transform3d transform2 = new Transform3d().translate(2, 5, 8).rotY(Math.PI / 2);
System.out.println(transform2.transform(point));``````

Outputs:

```Point3d [x=2.000000, y=5.000000, z=7.000000]
```

Of course, the `Transform3d` class also implements rotations around the X and Z axes. As the `Transform2d` class only implements rotation perpendicular to the X-Y plane this method is simply named `rotation`.

#### Shearing

The shear operator transforms a rectangle into a parallelogram. The `Transform2d` class implements one `shear(double, double)` method. The `Transform3d` class implements three shear methods: `shearXY(double, double)`, `shearYZ(double, double)` and `shearZX(double, double)`.

``````    Transform2d transform = new Transform2d().shear(2, 3);
for (int x = 0; x < 2; x++)
{
for (int y = 0; y < 2; y++)
{
Point2d p = new Point2d(x, y);
System.out.println(p + " -> " + transform.transform(p));
}
}``````

Outputs:

```Point2d [x=0.000000, y=0.000000] -> Point2d [x=0.000000, y=0.000000]
Point2d [x=0.000000, y=1.000000] -> Point2d [x=2.000000, y=1.000000]
Point2d [x=1.000000, y=0.000000] -> Point2d [x=1.000000, y=3.000000]
Point2d [x=1.000000, y=1.000000] -> Point2d [x=3.000000, y=4.000000]
```

The X-coordinates are incremented by the first parameter of the shear times the Y-coordinate. The Y-coordinates are incremented by the second parameter of the shear times the X-coordinate.

``````    Transform3d transform = new Transform3d().shearXY(2, 3);
for (int x = 0; x < 2; x++)
{
for (int y = 0; y < 2; y++)
{
for (int z = 0; z < 2; z++)
{
Point3d p = new Point3d(x, y, z);
System.out.println(p + " -> " + transform.transform(p));
}
}
}``````

Outputs:

```Point3d [x=0.000000, y=0.000000, z=0.000000] -> Point3d [x=0.000000, y=0.000000, z=0.000000]
Point3d [x=0.000000, y=0.000000, z=1.000000] -> Point3d [x=2.000000, y=3.000000, z=1.000000]
Point3d [x=0.000000, y=1.000000, z=0.000000] -> Point3d [x=0.000000, y=1.000000, z=0.000000]
Point3d [x=0.000000, y=1.000000, z=1.000000] -> Point3d [x=2.000000, y=4.000000, z=1.000000]
Point3d [x=1.000000, y=0.000000, z=0.000000] -> Point3d [x=1.000000, y=0.000000, z=0.000000]
Point3d [x=1.000000, y=0.000000, z=1.000000] -> Point3d [x=3.000000, y=3.000000, z=1.000000]
Point3d [x=1.000000, y=1.000000, z=0.000000] -> Point3d [x=1.000000, y=1.000000, z=0.000000]
Point3d [x=1.000000, y=1.000000, z=1.000000] -> Point3d [x=3.000000, y=4.000000, z=1.000000]
```

The `shearXY` operation does not modify the Z coordinates. The X-coordinates are incremented by the first parameter of the shear times the Z value. The Y-coordinates are incremented by the second parameter of the shear times the Z value. Similarly, there `shearYZ` operation preserves the X-coordinate values and the `shearZX` operation preserves the Y-coordinate values.

#### Scaling

A transform can also perform scaling by independent factors along each axis.

``````    Transform2d transform = new Transform2d().scale(4, 6);
for (int x = 0; x < 2; x++)
{
for (int y = 0; y < 2; y++)
{
Point2d p = new Point2d(x, y);
System.out.println(p + " -> " + transform.transform(p));
}
}``````

Outputs:

```Point2d [x=0.000000, y=0.000000] -> Point2d [x=0.000000, y=0.000000]
Point2d [x=0.000000, y=1.000000] -> Point2d [x=0.000000, y=6.000000]
Point2d [x=1.000000, y=0.000000] -> Point2d [x=4.000000, y=0.000000]
Point2d [x=1.000000, y=1.000000] -> Point2d [x=4.000000, y=6.000000]
```

#### Reflecting

The `Transform2d` object implements `reflectX()` and `reflectY()`. the `Transform3d` object additionally implements `reflectZ()`. These methods invert that particular coordinate.

#### Transforming multi-point Drawable objects

All preceding examples of using transforms showed how a transform can be applied to a `Point`. Each `Drawable` object implements `getPoints()` which constructs an `Iterator<Point>` that will yields all points of the `Drawable`. Also, `Drawable` objects that are constructed from a varying number of points have a constructor that takes such an iterator. This can be used to construct a new transformed `Drawable` with a single line of code:

``````    PolyLine2d line = new PolyLine2d(new Point2d(1, 2), new Point2d(2, 3), new Point2d(5, 0));
System.out.println(line);
Transform2d transform = new Transform2d().scale(2, 3);
PolyLine2d transformedLine = new PolyLine2d(transform.transform(line.getPoints()));
System.out.println(transformedLine);``````

Outputs:

```PolyLine2d [x=1.000000, y=2.000000, x=2.000000, y=3.000000, x=5.000000, y=0.000000]
PolyLine2d [x=2.000000, y=6.000000, x=4.000000, y=9.000000, x=10.000000, y=0.000000]
```