Conversion between RGB and RYB color spaces - math

I am currently trying to convert colours between RGB (red, green, blue) colour space and RYB (red, yellow, blue) colour space and back again.
Based on the details in the following paper, I am able to convert from RYB to RGB using trilinear interpolation - where the parametric weightings (s, t, u) are the RYB colors, and the vertices of the cube are 3d points in RGB space.
Paint Inspired Color Mixing and Compositing for Visualisation - Gossett and Chen - Section 2.1 - Realization Details
My difficulties are in reversing the conversion process.
A second paper references the use of this technique and also indicates that the reverse conversion was achieved using Newton's Method. But provides no further details. This would probably indicate root finding in solving the trilinear interpolation equations.
On the Transfer of Painting Style to Photographic Images through Attention to Colour Contrast - Xiaoyan Zhang; Constable, M.; Ying He;
Before I expand on this question with the equations, has anybody seen, or solved this in a language such as Java/C/C++/C#?
My current approach is to take the forward equations of the trilinear interpolation (RYB to RGB), expand and rearrange to provide 3 simultaneous equations for 3 unknowns (the parametric weightings: s, t, and u) then work out how to find the roots using the Newton-Raphson method. Am I going about this in the right way?

I managed to solve it in the end.
Take the equations for a trilinear interpolation:
wikipedia
Edit: Wikipedia revision at the time
Substitute the first equations into the last, the expand and collect the coefficients for:
Xd, Yd, Zd, XdYd, XdZd, YdZd, ZdYdZd and the constant.
Then find the partial differentiation of the equation in each of the 3 dimensions each in respect to Xd, Yd and Zd. Use these new equations to populate the (3x3) Jacobian matrix and then use Newton's method to solve in software.
Newton-Raphson Method

I found this JavaScript implementation of RYB->RGB conversion based on cubic splines. Here is my Lua port (all values lie in the interval 0-1):
local ryb2rgb = function( R, Y, B )
local R, Y, B = R*R*(3-R-R), Y*Y*(3-Y-Y), B*B*(3-B-B)
return 1.0 + B * ( R * (0.337 + Y * -0.137) + (-0.837 + Y * -0.163) ),
1.0 + B * ( -0.627 + Y * 0.287) + R * (-1.0 + Y * (0.5 + B * -0.693) - B * (-0.627) ),
1.0 + B * (-0.4 + Y * 0.6) - Y + R * ( -1.0 + B * (0.9 + Y * -1.1) + Y )
end

Here is a category on UIColor that does the same thing, returning elements between RGB, RYB, and CMYK. Further, you can mix any number of colors in the respective color space (they mix differently, of course, depending).
https://github.com/ddelruss/UIColor-Mixing

Related

How to use 4d rotors

I'm trying to create a 4D environment, similar to Miegakure's.
I'm having trouble understanding how to represent rotations. The creator of Miegakure wrote this small article explaining he made a class for 4d rotors.
http://marctenbosch.com/news/2011/05/4d-rotations-and-the-4d-equivalent-of-quaternions/
How can I implement the functions of this class ? In particular the functions to rotate vectors and other rotors, and getting the inverse ?
I would appreciate some pseudocode examples.
Thanks a lot to anyone who bothers answering.
The most common way to represent goemetric algebra multivectors (including rotors) in a computer is via an array of coefficients, one for each canonical form algebra basis element (canonical basis blade) ie. for a 4D basis space you will have a 2^4 dimensional algebra and have 2^4 dimensional array of coefficients. An alternate but probably faster way to represent them is with a dynamically resizing list with each element containing an index to a blade and the coefficient of the associated blade. In this case the multiplication of two multivectors will only use non zero basis blades and so should be algorithmically cheaper and lighter on memory usage.
In terms of practical usage I found the easiest place to get started with playing around with geometric algebra is probably in python with https://github.com/pygae/clifford . Full disclaimer I use this library daily and contribute to it extensively. This library uses the flat array of coefficients approach. With this python library you can apply 4D rotors via the sandwich product and do reversion (inversion of a rotor) via the tilde operator:
# Create a 4D geometric algebra with euclidean metric
from clifford.g4 import *
# Create a rotor
R = layout.randomRotor()
# Create a vector to rotate
V = layout.randomV()
# Apply the rotor to the vector
V2 = R*V*~R
The specific definition of the geometric product and reverse for multivectors from an N-dimensional geometric algebra can be found in Chapter 4 of Geometric algebra for Physicists by Chris Doran and Anthony Lasenby.
A good C++ GA reference implementation for N-dimensional GAs using the list of elements approach can be found in Leo Dorst's book Geometric Algebra for Physicists or on his website:
http://www.geometricalgebra.net/code.html . In general this is a great resource for GA, especially the conformal model and numerical implementations and concerns.
I was able to use Rotors after learning more on the subject thanks to this youtube series about Geometric Algebra : https://www.youtube.com/watch?v=PNlgMPzj-7Q&list=PLpzmRsG7u_gqaTo_vEseQ7U8KFvtiJY4K
It's really well explained and I recommend it to whoever wants use geometric algebra.
If you already know about Quaternions multiplication, Rotor multiplication won't be any different, and the i, j, k units of quaternions are analog to the basis bivectors of Geometric Algebra : e12, e13, e23 (or e01, e02, e12)
So a Rotor in 4D will be (A + B*e12 + C*e13 + D*e14 + E*e23 + F*e24 + G*e34 + H*e1234).
A table showing how to multiply those units can be found on this page :
http://www.euclideanspace.com/maths/algebra/clifford/d4/arithmetic/index.htm
To get the gist of it, consider the 2D Rotors.
They're of the form: R = A + B*e12
Now, if we compute product between 2 arbitrary rotors R_1 and R_2, we get:
R_1*R_2 = (
R_1.a * R_2.a
+ R_1.a * R_2.b*e12
+ R_1.b*e12 * R_2.a
+ R_1.b*e12 * R_2.b*e12 )
// but: e12*e12 = e1e2e1e2 = -e1e2e2e1= -e1e1 = -1
// this is confirmed by the computation rules I linked above
=
( (R_1.a * R_1.a - R_2.b * R_2.b)
+ (R_1.a * R_2.b + R_1.b * R_2.a) * e12 )
So in code, you would do something like :
R_3.a = R_1.a * R_2.a - R_1.b * R_2.b
R_3.b = R_1.a * R_2.b + R_1.b * R_2.a
Now it's only a matter of doing the same with those big 4D rotors, applying the multiplication rules for dimension 4 as linked above.
Here is the resulting code (using e0, e1, e2, e3 as basis vectors):
e: self.e*other.e - self.e01*other.e01 - self.e02*other.e02 - self.e03*other.e03 - self.e12*other.e12 - self.e13*other.e13 - self.e23*other.e23 + self.e0123*other.e0123,
e01: self.e*other.e01 + self.e01*other.e - self.e02*other.e12 - self.e03*other.e13 + self.e12*other.e02 + self.e13*other.e03 - self.e23*other.e0123 - self.e0123*other.e23,
e02: self.e*other.e02 + self.e01*other.e12 + self.e02*other.e - self.e03*other.e23 - self.e12*other.e01 + self.e13*other.e0123 + self.e23*other.e03 + self.e0123*other.e13,
e03: self.e*other.e03 + self.e01*other.e13 + self.e02*other.e23 + self.e03*other.e - self.e12*other.e0123 - self.e13*other.e01 - self.e23*other.e02 - self.e0123*other.e12,
e12: self.e*other.e12 - self.e01*other.e02 + self.e02*other.e01 - self.e03*other.e0123 + self.e12*other.e - self.e13*other.e23 + self.e23*other.e13 - self.e0123*other.e03,
e13: self.e*other.e13 - self.e01*other.e03 + self.e02*other.e0123 + self.e03*other.e01 + self.e12*other.e23 + self.e13*other.e - self.e23*other.e12 + self.e0123*other.e02,
e23: self.e*other.e23 - self.e01*other.e0123 - self.e02*other.e03 + self.e03*other.e02 - self.e12*other.e13 + self.e13*other.e12 + self.e23*other.e - self.e0123*other.e01,
e0123: self.e*other.e0123 + self.e01*other.e23 - self.e02*other.e13 + self.e03*other.e12 + self.e12*other.e03 - self.e13*other.e02 + self.e23*other.e01 + self.e0123*other.e,
Solving rotation around arbitrary vector will make you insane in 4D. Yes there are equations for that out there like The Euler–Rodrigues formula for 3D rotations expansion to 4D but all of them need to solve system of equations and its use is really not intuitive for us in 4D.
I am using rotation parallel to planes instead (similar to rotations around main axises in 3D) In 4D there are 6 of them XY,YZ,ZX,XW,YW,ZW so just create rotation matrices (similar to 3D). I am using 5x5 homogenuous transform matrices for 4D so the rotations looks like this:
xy:
( c , s ,0.0,0.0,0.0)
(-s , c ,0.0,0.0,0.0)
(0.0,0.0,1.0,0.0,0.0)
(0.0,0.0,0.0,1.0,0.0)
(0.0,0.0,0.0,0.0,1.0)
yz:
(1.0,0.0,0.0,0.0,0.0)
(0.0, c , s ,0.0,0.0)
(0.0,-s , c ,0.0,0.0)
(0.0,0.0,0.0,1.0,0.0)
(0.0,0.0,0.0,0.0,1.0)
zx:
( c ,0.0,-s ,0.0,0.0)
(0.0,1.0,0.0,0.0,0.0)
( s ,0.0, c ,0.0,0.0)
(0.0,0.0,0.0,1.0,0.0)
(0.0,0.0,0.0,0.0,1.0)
xw:
( c ,0.0,0.0, s ,0.0)
(0.0,1.0,0.0,0.0,0.0)
(0.0,0.0,1.0,0.0,0.0)
(-s ,0.0,0.0, c ,0.0)
(0.0,0.0,0.0,0.0,1.0)
yw:
(1.0,0.0,0.0,0.0,0.0)
(0.0, c ,0.0,-s ,0.0)
(0.0,0.0,1.0,0.0,0.0)
(0.0, s ,0.0, c ,0.0)
(0.0,0.0,0.0,0.0,1.0)
zw:
(1.0,0.0,0.0,0.0,0.0)
(0.0,1.0,0.0,0.0,0.0)
(0.0,0.0, c ,-s ,0.0)
(0.0,0.0, s , c ,0.0)
(0.0,0.0,0.0,0.0,1.0)
Where c=cos(a),s=sin(a) and a is angle of rotation. The rotation axis goes through coordinate system origin (0,0,0,0). For more info take a look at these:
Understanding 4x4 homogenous transform matrices mine 3D math
4D rendering techniques mine C++ 4D render
Visualising 4D objects in OpenGL older QAs
4D to 3D perspective projection older QAs
Four-Space Visualization of 4D Objects this is the most comprehensive stuff on the topic I found read it !!!

Calculate transformation which is needed to transform a rectangle into its perspective form

I have the same rectangle in an untransformed form and in a perspective form.
Both, the coordinates from the untransformed form as well as from the perspective form are in the untransformed coordinate system.
Is there a way to reconstruct the transformation matrix which leads to this transformation?
I think it should be possible to do that by solving the 4 equations given by the 4 corners but I am not sure where to start.
// Edit:
It looks like I am victim of a xy problem here. All answers are based in a 3d environment. But I have a rectangle on an image + I know the real dimensions of this rectangle. What I need to know is how to transform other known points onto the perspective image
Note that you have 8 pairs of corresponding parameters (x and y for every point), and need to calculate 8 parameters of matrix using 8 equations
//four pairs of such equaions:
x' = (A * x + B * y + C) / (G * x + H * y + 1.0)
y' = (D * x + E * y + F) / (G * x + H * y + 1.0)
Theory of finding perspective transformation matrix is described in Paul Heckbert article.
C++ implementation could be found in antigrain library (file agg_trans_perspective.h)
One way is to plug in the values given in the transformation matrix. That has the merit of being easy and working, but it won't help you to understand it.
To understand the transform, draw a diagram with the screen vertical and looking top-down. Draw the eye. The line from the eye to the screen should be perpendicular. Then draw a point, somewhere off the screen. Then draw a line from the eye to the point, going through the screen.
It's then an exercise in highschool-level trigonometry to work out the projection of the point onto the screen.

Standard form of ellipse

I'm getting ellipses as level curves of a fit dataset. After selecting a particular ellipse, I would like to report it as a center point, semi-major and minor axes lengths, and a rotation angle. In other words, I would like to transform (using mathematica) my ellipse equation from the form:
Ax^2 + By^2 + Cx + Dy + Exy + F = 0
to a more standard form:
((xCos[alpha] - ySin[alpha] - h)^2)/(r^2) + ((xSin[alpha] + yCos[alpha] - k)^2)/(s^2) = 1
where (h,k) is the center, alpha is the rotation angle, and r and s are the semi-axes
The actual equation I'm attempting to transform is
1.68052 x - 9.83173 x^2 + 4.89519 y - 1.19133 x y - 9.70891 y^2 + 6.09234 = 0
I know the center point is the fitted maximum, which is:
{0.0704526, 0.247775}
I posted a version of this answer on Math SE since it benefits a lot from proper mathematical typesetting. The example there is simpler as well, and there are some extra details.
The following description follows the German Wikipedia article Hauptachsentransformation. Its English counterpart, according to inter-wiki links, is principal component analysis. I find the former article a lot more geometric than the latter. The latter has a strong focus on statistical data, though, so it might be useful for you nevertheless.
Rotation
Your ellipse is described as
[A E/2] [x] [x]
[x y] * [E/2 B] * [y] + [C D] * [y] + F = 0
First you identify the rotation. You do this by identifying the eigenvalues and eigenvectors of this 2×2 matrix. These eigenvectors will form an orthogonal matrix describing your rotation: its entries are the Sin[alpha] and Cos[alpha] from your formula.
With your numbers, you get
[A E/2] [-0.74248 0.66987] [-10.369 0 ] [-0.74248 -0.66987]
[E/2 B] = [-0.66987 -0.74248] * [ 0 -9.1715] * [ 0.66987 -0.74248]
The first of the three factors is the matrix formed by the eigenvectors, each normalized to unit length. The central matrix has the eigenvalues on the diagonal, and the last one is the transpose of the first. If you multiply the vector (x,y) with that last matrix, then you will change the coordinate system in such a way that the mixed term vanishes, i.e. the x and y axes are parallel to the main axes of your ellipse. This is just what happens in your desired formula, so now you know that
Cos[alpha] = -0.74248 (-0.742479398678 with more accuracy)
Sin[alpha] = 0.66987 ( 0.669868899516)
Translation
If you multiply the row vector [C D] in the above formula with the first of the three matrices, then this effect will exactly cancel the multiplication of (x, y) by the third matrix. Therefore in that changed coordinate system, you use the central diagonal matrix for the quadratic term, and this product for the linear term.
[-0.74248 0.66987]
[1.68052, 4.89519] * [-0.66987 -0.74248] = [-4.5269 -2.5089]
Now you have to complete the square independently for x and y, and you end up with a form from which you can read the center coordinates.
-10.369x² -4.5269x = -10.369(x + 0.21829)² + 0.49408
-9.1715y² -2.5089y = -9.1715(y + 0.13677)² + 0.17157
h = -0.21829 (-0.218286476695)
k = -0.13677 (-0.136774259156)
Note that h and k describe the center in the already rotated coordinate system; to obtain the original center you'd multiply again with the first matrix:
[-0.74248 0.66987] [-0.21829] [0.07045]
[-0.66987 -0.74248] * [-0.13677] = [0.24778]
which fits your description.
Scaling
The completed squares above contributed some more terms to the constant factor F:
6.09234 + 0.49408 + 0.17157 = 6.7580
Now you move this to the right side of the equation, then divide the whole equation by this number so that you get the = 1 from your desired form. Then you can deduce the radii.
1 -10.369
-- = ------- = 1.5344
r² -6.7580
1 -9.1715
-- = ------- = 1.3571
s² -6.7580
r = 0.80730 (0.807304599162099)
s = 0.85840 (0.858398019487315)
Verifying the result
Now let's check that we didn't make any mistakes. With the parameters we found, you can piece together the equation
((-0.74248*x - 0.66987*y + 0.21829)^2)/(0.80730^2)
+ (( 0.66987*x - 0.74248*y + 0.13677)^2)/(0.85840^2) = 1
Move the 1 to the left side, and multiply by -6.7580, and you should end up with the original equation. Expanding that (with the extra precision versions printed in parentheses), you'll get
-9.8317300000 x^2
-1.1913300000 x y
+1.6805200000 x
-9.7089100000 y^2
+4.8951900000 y
+6.0923400000
which is a perfect match for your input.
If you have h and k, you can use Lagrange Multipliers to maximize / minimize the function (x-h)^2+(y-k)^2 subject to the constraint of being on the ellipse. The maximum distance will be the major radius, the minimum distance the minor radius, and alpha will be how much they are rotated from horizontal.

Trouble understanding the use of dot product in this example?

Usually, I use the dot product of 2 vectors either to find out how perpendicular they are or the cosine of the angle between them. In this shader, a toon shader, the dot product is used on 2 colors and I cannot wrap my head around what exactly this is doing.
uniform vec2 resolution;
uniform sampler2D backBuffer;
void main(void)
{
vec4 to_gray = vec4(0.3,0.59,0.11,0);
float x1 = dot(to_gray, texture2D(backBuffer,vec2((gl_FragCoord.xy
+ vec2(1.0,1.0)) /resolution.xy)).rgba);
float x0 = dot(to_gray, texture2D(backBuffer,vec2((gl_FragCoord.xy
+ vec2(-1.0,-1.0)) /resolution.xy)).rgba);
float x3 = dot(to_gray, texture2D(backBuffer,vec2((gl_FragCoord.xy
+ vec2(1.0,-1.0)) /resolution.xy)).rgba);
float x2 = dot(to_gray, texture2D(backBuffer,vec2((gl_FragCoord.xy
+ vec2(-1.0,1.0)) /resolution.xy)).rgba);
float edge = (x1 - x0) * (x1 - x0);
float edge2 = (x3 - x2) * (x3 - x2);
edge += edge2;
vec4 color = texture2D(backBuffer,vec2(gl_FragCoord.xy / resolution.xy));
gl_FragColor = max(color - vec4(edge, edge, edge, edge) * 12.0,
vec4(0,0,0,color.a));
}
The "geometric" scalar (dot) product properties don't really matter in this case. What you have here is an ordinary conversion of some (R, G, B) color to the corresponding grayscale intensity I in accordance with the formula
I = R * 0.30 + G * 0.59 + B * 0.11
(You can learn more about these coefficients here: https://en.wikipedia.org/wiki/Grayscale#Luma_coding_in_video_systems).
As you can immediately see this formula looks like a scalar product of two vectors: one is our color (R, G, B) and the other is (0.30, 0.59, 0.11). So, the author of the code just used the "dot product" function to evaluate this formula for four different color values obtained at four different points: point gl_FragCoord.xy shifted in four different directions (like an x pattern).
In other words, the dot product in this case is not used on "two colors", as you seemed to assume initially. It is used on a color (pixel taken from backBuffer at some coordinates) and a conversion coefficient vector (0.30, 0.59, 0.11) (aptly named to_gray). The latter is not really a color. It just a vector of conversion coefficients. You can think of it as a "color" if you want, but there's not much sense in it.
That's it for the dot product.
Then they do some extra computations to combine the four grayscale values into a single grayscale value. Then they use that grayscale value to modify the original color at point gl_FragCoord.xy (gray values are subtracted from RGB values at gl_FragCoord.xy). The purpose of all this is not fully clear without context.

transforming coordinates from one distorted coordinate system to another

the problem is best explained with an example:
http://dl.dropbox.com/u/1013446/distortedcoordinatespace.exe
drag and drop the little red square inside the small square on the right.
it corresponds to the red square in the big quadrilateral on the left.
you can also drag the 4 corners of the big quadrilateral on the left to see how it occupies a distorted version of the space within the square.
given the absolute coordinates for the 4 points of a square and the coordinates of an arbitrary point within the square, it's simple matter to remap the point's coordinates to an arbitrary quadrilateral.
what I want is to be able to start off with an arbitrary quadrilateral, and be able to do the same thing, transforming the quadrilateral to any other 4 sided shape, but maintaining the relative distorted position of the point,
so given the 4 absolute coordinates of each of 2 irregular quadrilaterals, A and B, how can I convert the coordinates of point C given it's absolute coordinates?
also helpful, would be any terminology that I'm missing here for what these transformations would be called, because I'd like to look into them more
ok, I'm attempting to implement btilly's solution, and here's what I have so far:
#include<complex>
#define cf complex<float>
cf i=sqrt(complex<float>(-1));
cf GetZ(float x,float y)
{
return cf(x)+(cf(y)*i);
}
cf GetPathIntegral(cf p1,cf p2,cf q1,cf q2, int n)
{
cf sum;
for (int index=0;index<=n;index++)
{
cf s=cf(float(index)/float(n));
cf weight;
if (index==0||index==n)
weight=1;
else if(index%2)
weight=4;
else weight =2;
sum+=(((cf(1)-s)*q1)+(s*q2))*(p2-p1)*weight;
}
return sum/cf((3.0*(n-1.0)));
}
before I move on from here, I want to make sure I'm right so far...
also, this paragraph confused me a bit:
OK, so we can do path integrals. What
is the value of that? Well suppose we
take a random point z0 = x + iy
somewhere in our region. Suppose that
f(z) is defined on the path. Then the
Cauchy Integral Formula says that the
integral around our region (which is
the sum of 4 piecewise integrals that
we know how to do) of f(z)/(2 * π * i
* (z - z0)) is a really nice function that is going to match our original
function on the boundary.
what does the function do exactly?
(My first pass was a complicated derivation of a natural seeming formula for this. But then I realized that there is a far, far better solution. Which I would have remembered earlier if I had used Complex Analysis in the last 20 years.)
The right way to do this is to apply the Cauchy Integral Formula. With this you can map any polygon to any other polygon. If the polygons don't self-intersect, it will send the boundary to the boundary and the interior to the interior. The mapping will also have the excellent property that it is conformal, meaning that angles are preserved. By that I mean that if a pair of curves intersect in your region, then they will be mapped to a pair of curves that intersect at the same angle. (Many of Escher's drawings are based on conformal mappings.)
Enough hype. How do you do it? I'll explain it, assuming that you know absolutely nothing about complex analysis. I'll use some Calculus terms, but you should be able to follow my directions even if you don't know any Calculus at all. Since I am assuming so little, the explanation has to be kind of long. I'm sorry for that.
Every point (x, y) in the real plane can be viewed as a complex number z = x + iy. We can add and multiply complex numbers using the usual rules of algebra and the fact that i * i = -1. Furthermore note that 1 = (x + iy) * (x - iy)/(x2 + y2) so we can divide if we let 1/z = (x - iy)/(x2 + y2). We therefore have all of the usual rules of arithmetic.
But we can do better than that. We can do Calculus. In particular we can do path integrals around curves. An integral of a function along a curve is a kind of weighted average of that function over the points in that curve. You can read up on how to do it in general. But here is how to do it in this case.
Suppose that the starting region has corners P1, P2, P3, P4. The path around the region is defined by the four line segments (P1, P2), (P2, P3), (P3, P4), (P4, P1). I'll talk about how to handle the first line segment. The others are similar.
The path integral of f(z) over (P1, P2) is the integral from 0 to 1 of f((1-s)P1 + sP2)(P2 - P1). To evaluate that integral, the easiest thing to do is numerical integration using Simpson's Rule. To do this pick an odd number n and for the values s = 0, 1/n, 2/n, ..., (n-1)/n, 1 assign them weights in the pattern 1, 4, 2, 4, 2, ..., 2, 4, 1. (The end points are 1, everything else alternates between 4 and 2.) Now for each point calculate f((1-s)P1 + sP2)(P2 - P1), multiply by the weight, and add them all together. Then divide by the magic value 3 * (n-1). The result is approximately your integral. (As n grows, the error in this approximation is O(1/n4). In your case if you take n = 21 then the approximation should wind up good enough to map pixels to the right pixel, except for some pixels near the boundary. Make it a little larger, and the problematic area will get smaller. Right at the edge you'll want some multiple of the number of pixels on a side to make the error small .)
OK, so we can do path integrals. What is the value of that? Well suppose we take a random point z0 = x + iy somewhere in our region. Suppose that f(z) is defined on the path. Then the Cauchy Integral Formula says that the integral around our region (which is the sum of 4 piecewise integrals that we know how to do) of f(z)/(2 * π * i * (z - z0)) is a really nice function that is going to match our original function on the boundary. I won't get into all of the "really nice" things about it, but what I was saying above about conformal is part of it.
Now what function f do we use? Well suppose that our region is being mapped to a region with corners Q1, Q2, Q3, Q4. We want the first path piece to map to the second path piece. So we want f((1-s)P1 + sP2) to be (1-s)Q1 + sQ2. That tells us how to calculate f at all of the points we need to do our integral.
Now, you ask, how do you reverse it? That's simple. Just reverse the role of the two polygons, and calculate the reverse transformation! Which brings a really good unit test. You should define a couple of weird regions, pick a point in the middle, and verify that if you map from the first to the second and back again that you wind up close to where you started. If you pass that test, then you probably have made no mistakes.
And finally what about my general polygon claim that I made? Well we defined our path as four pieces we traversed linearly. A higher degree polygon just has more pieces to its path, but otherwise the calculation is done in exactly the same way.
found the solution. I have to say, it's much more complicated than I had expected:
assuming a square or quadrilateral has the four corners:
AB
CD
you need an interpolation factor: xt for the x axis, and yt for the y axis, so that
if you define a linear interpolation formula:
lerp(j,k,t)
{
return (t*(k-j))+j;
}
a point p within the ABCD quad is defined as:
p.x=lerp(lerp(a.x,b.x,xt),lerp(c.x,d.x,xt),yt)
and
p.y=lerp(lerp(a.y,c.y,yt),lerp(b.y,d.y,yt),xt)
then the values you need to define are xt and yt
xt= ((2* c.x* a.y) - (d.x* a.y) - (2
*a.x c.y) + (b.x c.y) - (c.x* b.y) + (a.x* d.y) - (a.y* p.x) + (c.y* p.x )+
(b.y p.x) - (d.y p.x) + (a.x p.y)
- (b.x p.y) - (c.x* p.y) + (d.x* p.y) - Sqrt(-4* ((c.x* a.y) - (d.x*
a.y) - (a.x* c.y) + (b.x* c.y) - (c.x*
b.y) + (d.x* b.y) + (a.x d.y) -
(b.x d.y))* ((c.x* a.y) - (a.x* c.y)
- (a.y* p.x) + (c.y* p.x) + (a.x* p.y) - (c.x* p.y)) + ((-2 *c.x a.y) + (d.x a.y) + (2 *a.x c.y) - (b.x
c.y) + (c.x* b.y) - (a.x* d.y) + (a.y*
p.x) - (c.y* p.x) - (b.y* p.x) + (d.y*
p.x) - (a.x* p.y) + (b.x* p.y) +
(c.x* p.y) - (d.x p.y))^2))/(2
((c.x* a.y) - (d.x* a.y) - (a.x* c.y)
+ (b.x* c.y) - (c.x* b.y) + (d.x *b.y) + (a.x *d.y) - ( b.x *d.y)))
and once you have that
yt=(p.x-lerp(a.x,b.x,('xt')))/(lerp(c.x,d.x,('xt'))-lerp(a.x,b.x,('xt')))

Resources