Looking for alternatives or improvements for accomplishing the following:
Given the position of points A, B, and C in both the x-y and u-v planes, determine transformation functions to map values between the x-y and u-v planes.
Points A and B are on the v axis.
Point C is on the u axis.
Purpose: The transform functions will be used to compute the uv coordinates of a point P given its xy coordinates, or its xy coordinates given its uv coordinates.
Below is what I have developed so far.
- Recommendations for improvements are welcome.
- Can this the result is expressed as an AffineTransform? This implementation uses (r + v).m but the AffineTransform uses r.m + v, perhaps there is a way to revise the implementation?
(*example data*)
{Axy, Bxy, Cxy,
Pxy} = {{0.2, 0.8}, {0.1, 0.15}, {0.8, 0.25}, {0.6, 0.7}};
{Auv, Cuv} = {{0, 75}, {100, 0}};
(*the unit vectors for the u-v plane, w.r.t. the x-y coordinate*)
Vxy = Normalize[ Axy - Bxy];
Uxy = Normalize[{Last[Vxy], -
First[Vxy] }]; (*axis orthogonal to Vxy*)
(*the origin of the u-v plane in x-y coordinates is the intersection \
of the v and u axis*)
UVoxy = First@(
Axy + k1 Vxy /. Solve[ Axy + k1 Vxy == Cxy + k2 Uxy , {k1, k2} ] );
(*scale factors for u-v axis*)
Uscale = First@Cuv / EuclideanDistance[UVoxy, Cxy];
Vscale = Last@Auv / EuclideanDistance[UVoxy, Axy];
mUVscale = {{Uscale, 0}, {0, Vscale}};
(*rotation from x-y to u-v*)
r1 = RotationMatrix[{{1, 0}, Uxy}] ;
(*combined rotation and scaling*)
mXYtoUV = r1 . mUVscale;
mUVtoXY = DiagonalMatrix[(1 / Diagonal@mUVscale)] . Transpose[r1] ;
(*test: original xy points to uv*)
testUV = (# - UVoxy) . mXYtoUV & /@ {Axy, Bxy, Cxy, Pxy} ;
(*test: computed uv points to xy*)
testXY01 = (# . mUVtoXY + UVoxy) & /@ testUV;
(*test: original uv points to xy*)
testXY02 = (# . mUVtoXY + UVoxy) & /@ {Auv, Cuv} ;
(*report: xy to uv*)
Framed@Labeled[
Grid[{ { "pt", Style["UV data", Bold], Style["UV computed", Bold]}
, { Column[{"A", "B", "C", "P" }]
, {Auv, {"-", "-"}, Cuv, {"-", "-"}} // MatrixForm
, testUV // MatrixForm
}
}
, Frame -> All], Style["xy to uv", 16, Bold], Top]
(*report: uv to xy*)
Framed@Labeled[
Grid[{ { "pt", Style["xy data", Bold], Style["xy computed", Bold]}
, { Column[{"A", "B", "C", "P" }]
, {Axy, Bxy, Cxy, Pxy} // MatrixForm
, testXY01 // MatrixForm
}
}
, Frame -> All], Style["uv to xy", 16, Bold], Top]
results
Finally, here is the code to display a sketch of the system
(*sketch of the system*)
Graphics[{AbsolutePointSize[10]
, Point[{Axy, Bxy, Cxy, Pxy}]
, AbsoluteThickness[1]
, AbsoluteDashing[10]
, Darker@Blue
, Arrowheads[{-0.05, +0.05}]
, Arrow[{UVoxy - 0.35 Vxy, UVoxy + 0.65 Vxy}]
, Arrowheads[+0.05]
, Arrow[{UVoxy, UVoxy + 0.95 Uxy}]
, Darker@Green
, Text[Style["A", Bold, 18], Axy + {0.03, 0.03}, {-1, 0}]
, Text[Style["B", Bold, 18], Bxy + {0.03, 0.03}, {-1, 0}]
, Text[Style["C", Bold, 18], Cxy + {0.03, 0.03}, {-1, 0}]
, Darker@Red
, Text[Style["P", Bold, 18], Pxy + {0.03, 0.03}, {-1, 0}]
, Darker@Blue
, Text[Style["u", Bold, Italic, 14], UVoxy + 0.95 Uxy , {-1, 0}]
, Text[Style["v", Bold, Italic, 14], UVoxy + 0.65 Vxy , {-1, -1}]
, Text[Style["-v", Bold, Italic, 14], UVoxy - 0.35 Vxy , {0.5, 1}]
}
, Axes -> True
, PlotRange -> {{-0.1, 1.2}, {-0.1, 1.1} }
, Frame -> True
]
EDIT #1 and #2 Explaining the motivation for the question may help address any mistakes or ambiguities in the problem statement.
The transformations described above will be used in a program where the user imports a scanned image of a plot, like the one shown below, and the plot is displayed in a Mathematica Graphics object. It is likely that the axis of the plot and the axis of the Graphics object will not align. Rather than align the axes, the intent is to have transformation functions that will allow mapping values between the Graphics coordinate system (the xy axis) and the scanned image coordinate system (the uv axis).
As a first step, the system will have the user identify points A, B, and C with locator points; the Mathematica graphics object will "know" the xy values of those locator points. The system will also have the user manually enter the uv coordinates of the A,B,C points; the user can read those values from the plot. (The intent in this step is for the user to enter the minimum amount of information required to define the uv coordinate system. In my first implementation, only the "uv coordinates" of points A and C were required.)
With that information, the transformation functions can be computed. Those functions will be used to overlay the results of other calculations on the plot or to select features of the plot (with different locator points).
For example, in a second step, the user will also place locator points on points D and E in the sketch below. The Mathematica Graphics object will know the "xy coordinates" of these two points; a transform function will be used to compute their "uv coordinates" of points D and E. Then the "uv coordinates" of A,B,C, D and E will be known.
In a third step, the system will compute the "uv coordinates" of F and G, using the "uv coordinates" of A, B, C, D and E.
Then in a fourth step, the system will display the F and G points on the the Mathematica Graphics object. This will employ the other transform function which will convert the "uv coordinates" of the F and G points, computed in step 3, into "xy coordinates" required by the Mathematica Graphics object.
Below is an example of an scanned image that has coordinates that do not align with the horizontal and vertical and points A through G.
Projection[C-B,A-B]+B
. But where should xy $(1,0)$ be mapped? To $C$? Your transform is unique only up to two independent scalars; one for the x axis and one for the y axis. I'm not sure what choice your code makes. $\endgroup$