Transformations

This documentation was generated with the assistance of AI. Please report any inaccuracies.

A transformation maps geometric entities (points, lines, planes, conics, quadrics, cameras) onto other entities of the same kind. irurueta-geometry organizes transformations into a stratified hierarchy of increasing generality — Euclidean, metric, affine and projective — each one a special case of the next. This page explains that hierarchy, what each family preserves, and how the library actually implements it in com.irurueta.geometry.Transformation2D / Transformation3D and their subclasses.

The hierarchy

Transformation hierarchy: Euclidean
Family Adds on top of the previous family DOF (2D / 3D) Preserves / fixes at infinity

Euclidean (rigid)

rotation + translation

3 / 6

distances, angles, areas/volumes; trivially fixes , and and the absolute scale.

Metric (similarity)

+ uniform scale

4 / 7

angles, ratios of distances, shape; fixes the plane at infinity and the absolute conic / dual absolute quadric (up to scale) — but not the absolute scale itself.

Affine

+ independent axis scaling and skew (general linear map)

6 / 12

parallelism, ratios of lengths on parallel lines, ratios of areas/volumes; fixes the plane at infinity as a set (not pointwise), but not / .

Projective

+ a general homography, defined up to scale

8 / 15

incidence (collinearity/coplanarity), cross-ratio of 4 collinear points; fixes none of , or in general.

The DOF and invariants above match the standard 2D hierarchy in Hartley & Zisserman table 2.4 (p.44) and the 3D hierarchy in their table 3.2 (p.78), and are worked out from first principles in Irurueta’s PhD thesis table 1.4 (p.42). The rest of this page is devoted to why those particular quantities — the plane at infinity, the absolute conic and the dual absolute quadric — are exactly the ones that separate affine from metric from Euclidean.

Each family is strictly more general than the previous one: every Euclidean transformation is also a metric transformation (with scale = 1), every metric transformation is also affine, and every affine transformation is also projective. irurueta-geometry exposes this with one-directional "upgrade" converters rather than a shared supertype:

classDiagram class Transformation2D class Transformation3D class EuclideanTransformation2D class MetricTransformation2D class AffineTransformation2D class ProjectiveTransformation2D Transformation2D <|-- EuclideanTransformation2D Transformation2D <|-- AffineTransformation2D Transformation2D <|-- ProjectiveTransformation2D EuclideanTransformation2D <|-- MetricTransformation2D EuclideanTransformation2D ..> MetricTransformation2D : toMetric() MetricTransformation2D ..> AffineTransformation2D : toAffine() AffineTransformation2D ..> ProjectiveTransformation2D : toProjective()

The 3D classes (Transformation3D, EuclideanTransformation3D, MetricTransformation3D, AffineTransformation3D, ProjectiveTransformation3D) mirror this exactly. Two details worth noting:

  • MetricTransformation2D/3D extends EuclideanTransformation2D/3D, adding a single scale field — metric/similarity really is "Euclidean plus scale" in the implementation, not an independent class.

  • AffineTransformation2D/3D and ProjectiveTransformation2D/3D extend Transformation2D/3D directly (they do not extend Metric*/Euclidean*). They only expose one-way upgrade methods (toAffine(), toProjective()); there is no built-in "downgrade" that tries to recover a simpler transformation from a more general one.

ProjectiveTransformation2D/3D makes the nesting explicit in its own javadoc: its internal matrix has an extra "projective parameters" row that is [0, …​, 0, 1] (up to scale) for every transformation that is really just affine, metric, or Euclidean in disguise.

Internal representation

Class Representation

EuclideanTransformation2D/3D

a Rotation2D/Rotation3D plus a translation vector; point maps to .

MetricTransformation2D/3D

the same rotation/translation, plus a scalar scale; point maps to .

AffineTransformation2D/3D

a general (non-orthonormal) linear map matrix A (2×2 / 3×3) plus a translation; point maps to . Rotation and independent-scale/skew components are recovered on demand from A via RQ decomposition (getRotation(), getParameters() / AffineParameters2D/3D).

ProjectiveTransformation2D/3D

a single homogeneous matrix T (3×3 / 4×4), defined up to scale; a homogeneous point maps to directly, with no separate translation step.

How other geometric entities transform

Because points, lines/planes and conics/quadrics play different roles in projective duality, they don’t all transform the same way. Given a transformation matrix T (2D: 3×3, 3D: 4×4) and its application to a point p' = T·p, the library implements:

Entity Transform rule

Point2D / Point3D

(direct forward map)

Line2D / Plane

(inverse-transpose — lines/planes are covectors)

Conic / Quadric

(inverse-transpose on both sides)

DualConic / DualQuadric

(forward on both sides — duals transform contragradiently to their primal)

PinholeCamera

camera matrix

Polygon / Triangle

vertices transformed individually (each vertex is just a point)

Line3D

derived by transforming the two planes that define it

The inverse-transpose rule for lines/planes/conics/quadrics is the standard projective-duality argument, and it appears as an inline comment repeated across every subclass’s transform(Conic, …​) / transform(Line2D, …​) method, e.g.:

// point' * conic * point = 0
// point' * T' * transformedConic * T * point = 0
// where: transformedPoint = T * point
// Hence: transformedConic = T^-1' * conic * T^-1

All of these transform methods normalize the input, invert T once, and re-normalize before multiplying — this is a numerical-stability pattern repeated identically across the Euclidean/Metric/Affine/Projective implementations of every entity above.

Invariants: what fixes what

The table above says that affine transformations preserve parallelism while metric transformations preserve angles. Both facts have a single, precise algebraic root: each stratum is exactly the subgroup of the projective linear group that fixes one particular geometric object living at infinity. Affine transformations are exactly the ones that fix the plane at infinity as a set; metric (similarity) transformations are exactly the ones that, in addition, fix the absolute conic (or, equivalently, the dual absolute quadric ); Euclidean transformations are metric transformations with the remaining scale freedom pinned to 1. This section works through why, following Irurueta’s PhD thesis §1.4 and Hartley & Zisserman §3.5-3.7.

The plane at infinity fixes the affine stratum

In any projective frame of , the points with homogeneous last coordinate form a plane, the plane at infinity . In a frame that is already affine (or metric, or Euclidean) it takes the canonical form

(PHD eq. 1.118; HZ eq. 3.1-analogue for 3-space). This is exactly the constant returned by Plane.createCanonicalPlaneAtInfinity(). Under an arbitrary projective transformation, however, moves like any other plane — the library’s own rule applies to it too, and in an arbitrary projective frame it can be anywhere: 3 vanishing points fix 3 independent directions, and the plane they span identifies where actually sits in that frame (PHD fig. 1.9). This is exactly what plane-at-infinity.svg below illustrates: on the left, a cube seen through an uncalibrated homography, where 3 vanishing points mark 3 points of ; on the right, the same cube after upgrading to the affine stratum, where those vanishing points have moved back out to infinity and the cube’s edges are visibly parallel again.

The plane at infinity identified by 3 vanishing points in a projective frame

Claim (PHD eq. 1.121-1.123; HZ Result 3.7): a projective transformation fixes as a set if, and only if, it is an affinity. Write a general affine transformation in the library’s own block form (the exact shape produced by AffineTransformation3D.toProjective(), see below):

Applying the plane-transform rule directly to the canonical (PHD eq. 1.121):




so is a fixed point of , and hence (taking inverse-transposes, PHD eq. 1.123) also of the library’s covector rule . Notice this is set-invariance, not pointwise invariance: individual points within  — i.e. individual directions/vanishing points — are free to move around, as long as they stay on the plane. The converse (any projective H that fixes as a set must have a last row proportional to , i.e. must be affine) is HZ Result 3.7, proved the same way the 2D analogue (Result 2.17) is: fixing a hyperplane algebraically forces the homography’s action on the complementary coordinate to vanish.

This is also precisely how you find the affine upgrade once has been identified in an arbitrary projective frame: any matrix of the form

(PHD eq. 1.125, with the first 3 coordinates of the identified , scaled so its last coordinate is 1 — PHD eq. 1.126) sends that plane back to its canonical position, because its last row is . Any with non-singular works equally well (PHD eq. 1.127) —  is simply the simplest choice.

The absolute conic and dual absolute quadric fix the metric stratum

Fixing buys parallelism but says nothing about angles: an affine map can still skew a square into an arbitrary parallelogram. To pin down angles, both HZ and PHD start from the elementary fact that two vectors are orthogonal iff their dot product vanishes, (PHD eq. 1.143-1.144). Pushing that relation through an arbitrary homography gives (PHD eq. 1.145-1.148):

and is symmetric, i.e. exactly the shape of a conic transformed by the library’s own rule with in the metric frame. Running the same argument in 3D with planes and their director vectors (PHD eq. 1.154-1.158) shows that the analogous quadric that certifies orthogonality of planes in a metric frame is degenerate —   — because the extra row/column that would encode a translation-like term must vanish for perpendicularity to be translation-independent.

These two constants are, respectively, the absolute conic and the dual absolute quadric : the point conic that lies on (formed by intersecting the absolute quadric ), and the degenerate quadric whose tangent planes are exactly the planes tangent to (PHD §1.4.3.1, fig. 1.12). In a metric frame:


(PHD eq. 1.160/1.162/1.169; HZ eq. 3.21/3.24 — identical constants). Algebraically is a conic of purely imaginary points, i.e. a "circle" of imaginary radius (PHD eq. 1.163, fig. 1.14); is the limit of an ever-more-squashed sphere as (PHD eq. 1.168, fig. 1.15; HZ calls this a "rim quadric" — picture an ellipsoid squashed to a pancake, with as its rim). These are exactly the shapes absolute-conic-quadric.svg below depicts.

The absolute conic Omega-infinity on the plane at infinity

Claim (PHD Proof 1.6, eq. 1.170-1.178; HZ Result 3.9/3.10): a projective transformation fixes (equivalently , up to the overall scale any projective quantity is defined up to) if, and only if, it is a similarity. Sufficiency is a direct computation: writing a similarity in the library’s block form (matching `EuclideanTransformation3D.toMetric()’s output shape) and applying the dual-quadric transform rule (the library’s own forward-forward rule for duals) gives





using for any rotation (PHD eq. 1.170-1.173) — i.e. is reproduced up to the scalar , which is exactly the "up to scale" sense in which any projective entity is defined. For necessity, take an arbitrary projective and demand :





Matching this against (up to the scalar discussed above) forces

(PHD eq. 1.177). So  — the projective row that would otherwise move  — must vanish, and must be a scaled rotation (the condition is exactly "orthogonal up to a positive scalar"). That leaves precisely

a similarity transformation (PHD eq. 1.178) — and since sits in the top-left minor of (PHD eq. 1.166), a transformation that fixes fixes (and hence ) too. HZ Result 3.10 gives the equivalent block-matrix proof for directly, and Result 3.9 proves the corresponding statement for itself by first invoking Result 3.7 (any transformation fixing a conic on must first fix , hence be affine) and then showing the affine part must be orthogonal up to scale.

Upgrading between strata

Because affine upgrade only needs while metric upgrade needs / as well, the two upgrades compose. Combining (eq. 1.125) with the affine→metric matrix that Cholesky-factors the recovered conic out of the affine-stratum dual quadric (PHD eq. 1.179-1.184) gives a single projective→metric matrix (PHD eq. 1.185-1.186):

Inverting it (PHD eq. 1.189-1.192, via blockwise matrix inversion) and substituting into the dual-quadric transform rule shows that in any projective frame (PHD eq. 1.193-1.195):

i.e. the dual absolute quadric in an arbitrary projective frame packages both the dual image of the absolute conic (the "DIAC" that reappears in camera calibration, see Pinhole Camera) and the direction of the plane at infinity into a single symmetric matrix. This is why, in self-calibration literature, recovering is treated as the way to upgrade a projective reconstruction straight to metric in one step, rather than recovering and separately.

irurueta-geometry does not ship such a self-calibration/stratified-upgrade estimator — there is no toAffine()/toMetric() on ProjectiveTransformation2D/3D, and no class under com.irurueta.geometry.estimators or com.irurueta.geometry.refiners computes or from image data. What the library does give you is exactly the algebraic building blocks above — the canonical values of , and , and the one-directional Euclidean→metric→affine→projective upgrade chain — so that, given a Plane/Conic/DualQuadric identified by some other means (vanishing points, orthogonal-plane constraints, Kruppa’s equations, etc.), an application can still build the matrices / above by hand.

From theory to code

The canonical constants above are not just theory — they are literally the values the library’s factory methods return:

// Pi_infinity = (0, 0, 0, 1)^T                          -- PHD eq. 1.118 / HZ Sec. 3.5
Plane planeAtInfinity = Plane.createCanonicalPlaneAtInfinity();

// Omega_infinity = I_3x3                                 -- PHD eq. 1.160 / HZ eq. 3.21
Conic absoluteConic = Conic.createCanonicalAbsoluteConic();

// Omega*_infinity = I_3x3 = (Omega_infinity)^-1           -- PHD eq. 1.162
DualConic dualAbsoluteConic = DualConic.createCanonicalDualAbsoluteConic();

// Q*_infinity = diag(1, 1, 1, 0)                          -- PHD eq. 1.169 / HZ eq. 3.24
DualQuadric dualAbsoluteQuadric = DualQuadric.createCanonicalDualAbsoluteQuadric();

And the one-way upgrade chain (Euclidean → metric → affine → projective) walks the strata exactly in the direction the invariants above allow — each step adds a degree of freedom (scale, then general linear skew, then the projective row) rather than removing one:

Rotation3D rotation = new AxisRotation3D(0.0, 0.0, 1.0, Math.PI / 6.0);
double[] translation = {1.0, 2.0, 3.0};
EuclideanTransformation3D euclidean = new EuclideanTransformation3D(rotation, translation);

// adds a uniform scale (MetricTransformation3D.DEFAULT_SCALE = 1.0 initially)
MetricTransformation3D metric = euclidean.toMetric();

// embeds scale * R as the general 3x3 linear part -- concretely "metric subset of affine"
AffineTransformation3D affine = metric.toAffine();

// appends the implicit last row [0 0 0 1] -- this IS T_A from PHD eq. 1.120 / HZ table 3.2,
// the exact matrix shape whose invariance of Pi_infinity was proved above
ProjectiveTransformation3D projective = affine.toProjective();

AffineTransformation3D.toProjective() (AffineTransformation3D.java:1018-1020) builds new ProjectiveTransformation3D(a, translation), which is precisely the block form used throughout the proofs above — seeing that constructor call is seeing PHD eq. 1.120 typed out in Java. Similarly, MetricTransformation3D.toAffine() (MetricTransformation3D.java:272-274) builds new AffineTransformation3D(scale, getRotation(), getTranslation()), embedding as the linear part — the exact matrix that appeared as in the necessity proof of §Invariants above.

References

The core Transformation* classes carry no external bibliographic citations in their javadoc — the Euclidean/metric/affine/projective stratification is standard projective-geometry material, but it has precise, citable proofs in both sources used throughout this page (see the full bibliography for full citations):

Key classes

The classes exercised by the code examples above, with links to their source and Javadoc:

Class Links

Plane

Source
Javadoc

Conic

Source
Javadoc

DualConic

Source
Javadoc

DualQuadric

Source
Javadoc

Rotation3D

Source
Javadoc

AxisRotation3D

Source
Javadoc

EuclideanTransformation3D

Source
Javadoc

MetricTransformation3D

Source
Javadoc

AffineTransformation3D

Source
Javadoc

ProjectiveTransformation3D

Source
Javadoc

  • Points, Lines and Planes — the point/line/plane entities being transformed, and the plane-at-infinity’s role among them.

  • Conics and Quadrics — conics, dual conics, quadrics and dual quadrics in detail, including the absolute conic and dual absolute quadric introduced above.

  • Pinhole Camera — how a transformation acts on a PinholeCamera, and how the dual absolute quadric relates to camera self-calibration.

  • Rotations — the Rotation2D/Rotation3D classes used internally by Euclidean and metric transformations.