Revision overlay: lining up two sheets and moving the takeoff
Fits the move, turn and scale that lays an old sheet over its revision from two pairs of picked points, and carries the marks and the scale across.
How the precision is kept
The alignment is a similarity (no mirror) solved in closed form from two point pairs, in 64-bit. Marks are moved by applying it to every point; bulges stay as they are because a similarity keeps every arc's turn. The fit is exact for the four picks, so its accuracy is the accuracy of the picks, magnified when the two points of a pair are close together.
Calculations under this policy
7 calculations in 3 source files. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
The move, turn and uniform scale that take two points of the old sheet to two points of the new one.
c + i·s = (b₂ − b₁) ÷ (a₂ − a₁); t = b₁ − (c + i·s)·a₁
c and s are the real and imaginary parts of (b₂ − b₁) ÷ (a₂ − a₁), each a sum of two products over |a₂ − a₁|². The translation is b₁ minus the rotated a₁, a subtraction that loses digits when the points are far from the origin compared with their spacing. Only an exactly zero distance is refused.
overlay.js
lines 23 to 31
TakeoffCraft 0.8.1
/** The similarity taking a1 → b1 and a2 → b2 (no mirror). → matrix | null when a1 = a2 or b1 = b2. */
export function fitTwoPoints(a1, a2, b1, b2) {
const ax = a2.x - a1.x, ay = a2.y - a1.y, bx = b2.x - b1.x, by = b2.y - b1.y;
const la = ax * ax + ay * ay;
if (!(la > 0) || !(bx * bx + by * by > 0)) return null;
// b = s·R·a: as a complex number, (bx + i by) / (ax + i ay)
const c = (bx * ax + by * ay) / la, s = (by * ax - bx * ay) / la;
return [c, s, -s, c, b1.x - (c * a1.x - s * a1.y), b1.y - (s * a1.x + c * a1.y)];
}
Scale of an alignment
64-bit
How much longer a length becomes under the alignment.
scale = √(m₀² + m₁²)
Math.hypot of the first matrix column; one call, no cancellation.
The matrix from a pixel of the new sheet's raster to a pixel of the old one.
A product of five matrices, two of them inverses, in 64-bit. It is used to compare the two pictures, not to move marks.
overlay.js
lines 35 to 43
TakeoffCraft 0.8.1
/**
* Pixel map for warp(): NEW raster pixel → OLD raster pixel.
* Tn, To: the sheets' display transforms (user → display); px: raster pixels per display unit; A: old user → new user.
*/
export function pixelMap(Tn, To, A, px) {
const S = [px, 0, 0, px, 0, 0], Si = [1 / px, 0, 0, 1 / px, 0, 0];
// new px → new display → new user → old user → old display → old px
return multiply(S, multiply(To, multiply(invert(A), multiply(invert(Tn), Si))));
}
The 2 × 3 matrix operations behind sheet transforms.
det = m₀·m₃ − m₁·m₂; M⁻¹ = adj(M) ÷ det
Plain 64-bit sums of products. invert divides every entry by det = m₀·m₃ − m₁·m₂ with no check for zero, and its translation terms are each a difference of two products.
Collects two points on the old sheet and two on the new one and fits the alignment.
Old-sheet picks are taken back through the inverse of the current alignment before the fit. The scale shown in the status line is rounded to 3 decimals; the stored matrix is not.
The scale the moved marks are measured with on the new sheet.
ftPerUnit′ = ftPerUnit ÷ scale(A)
carried ftPerUnit = old ftPerUnit ÷ the alignment's scale, one division. When that scale is within 1e-6 of 1 the old scale record is used unchanged. If the new sheet already has a scale that differs by more than 0.1 % (relative), the person is asked before anything moves.
The complex ratio (b₂ − b₁) ÷ (a₂ − a₁) gives the scaled rotation; the translation follows from the first pair. Matrices are 2 × 3 in PDF order.
Number types
64-bit numbers.
Tolerances
A pair with zero distance is refused (exact test). The old scale is kept as it is when the alignment's scale is within 1e-6 of 1. A warning is shown when the new sheet's own scale differs from the carried one by more than 0.1 %.
Limits
The matrix inverse divides by the determinant without a guard. With two pairs there is no redundancy and so no measure of how well the sheets fit.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.