I know that transforming a square into a trapezoid is a linear transformation, and can be done using the projective matrix, but I'm having a little trouble figuring out how to construct the matrix.
Using the projective matrix to translate, scale, rotates, and shear is straightforward. Is there a simple projective matrix which will transform a square to a trapezoid?
a,b,c,d are the four corners of your 2D square.
a,b,c,d are expressed in homogeneous coordinate and so they are 3x1 matrices.
alpha, beta, gamma, delta are the four corners of your 2D trapezoid.
alpha, beta, gamma, delta are expressed in homogeneous coordinate and so they are 3x1 matrices.
H is the 3x3 matrix you are looking for, it is also called an homography
h1 h2 h3
H = h4 h5 h6
h7 h8 h9
H maps a,b,c,d into alpha, beta, gamma, delta and so you have the following four equations
alpha=H*a
beta=H*b
gamma=H*c
delta=H*d
Assuming you know a,b,c,d and alpha, beta, gamma, delta you can solve the previous four equation system for the nine unknowns h1, h2, h3, h4, h5, h6, h7, h8, h9.
Here I have just described a "raw" solution to the problem, that, in principle, can work; for a detailed explanation of the above mentioned method you can see for example this page http://www.corrmap.com/features/homography_transformation.php where they put h9=1 (since H can be expressed just with 8 parameters) and then solve a linear system of eight equations in eight unknowns. You can find a similar explanation in section 2 of the thesis Homography Estimation by Elan Dubrofsky.
Another explanation is Using Projective Geometry to Correct a Camera by David Austin in the 2013 March issue of Feature Column from the AMS.
The above mentioned method, with its disadvantages, is described in chapter 4 "Estimation - 2D Projective Transformation" in the second edition of Multiple view geometry in computer vision by Richard Hartley and Andrew Zissermann where they also describe different and better algorithms; you can check this link http://www.cse.iitd.ac.in/~suban/vision/geometry/node24.html which seems to follow the same book.
You can find another explanation of the homography in section 15.1.4, "Projective transformation model" of the book Computer Vision: Models, Learning, and Inference by Simon J.D. Prince. The algorithm Algorithm 15.4: maximum likelihood learning of projective transformation (homography) is outlined in his Algorithms booklet: the problem is solved by means of a non-linear minimization.
Maybe you could use a quadrilateral? See my answer here:
https://stackoverflow.com/a/12820877/202451
Then you will have full control over each point and can easily make any four-cornered shape. :)
Java implementation with minimal dependencies
For those with limited knowledge and time looking for a quick and dirty solution there is a working and quite reliable Java implementation in the Wii-interact project.
The transormation is in the The Homography source file. It boils down to constructing and solving the matrix:
/**
* Please note that Dr. John Zelle assisted us in developing the code to
* handle the matrices involved in solving for the homography mapping.
*
**/
Matrix A = new Matrix(new double[][]{
{x1, y1, 1, 0, 0, 0, -xp1*x1, -xp1*y1},
{0, 0, 0, x1, y1, 1, -yp1*x1, -yp1*y1},
{x2, y2, 1, 0, 0, 0, -xp2*x2, -xp2*y2},
{0, 0, 0, x2, y2, 1, -yp2*x2, -yp2*y2},
{x3, y3, 1, 0, 0, 0, -xp3*x3, -xp3*y3},
{0, 0, 0, x3, y3, 1, -yp3*x3, -yp3*y3},
{x4, y4, 1, 0, 0, 0, -xp4*x4, -xp4*y4},
{0, 0, 0, x4, y4, 1, -yp4*x4, -yp4*y4}
});
Matrix XP = new Matrix(new double[][]
{{xp1}, {yp1}, {xp2}, {yp2}, {xp3}, {yp3}, {xp4}, {yp4}});
Matrix P = A.solve(XP);
transformation = new Matrix(new double[][]{
{P.get(0, 0), P.get(1, 0), P.get(2,0)},
{P.get(3, 0), P.get(4, 0), P.get(5,0)},
{P.get(6, 0), P.get(7, 0), 1}
});
Usage: the following method does the final transformation:
public Point2D.Double transform(Point2D.Double point) {
Matrix p = new Matrix(new double[][]{{point.getX()}, {point.getY()}, {1}});
Matrix result = transformation.times(p);
double z = result.get(2, 0);
return new Point2D.Double(result.get(0, 0) / z, result.get(1, 0) / z);
}
The Matrix class dependency comes from JAMA: Java Matrix Package
License
Wii-interact GNU GPL v3
JAMA public domain
Related
I am trying to reproduce the degree-3 or degree-4 3D curves typically found in parametric cad programs like Rhino or Autocad, which take any number of 3D points to create long curves. I've found that three.js has Cubic (degree-3) and Quadratic (degree-4) Bezier curves available but they take exactly three and 4 vectors, respectively. I'd like to create curves with 10 or more inputs, not just 3 or 4. I've also found that three.js has 'Path' which allows building a 2D curve of mixed degree segments using the .bezierCurveTo() or .quadraticCurveTo() methods.
So my question:
Is there currently a way to construct long chains of CubicBezierCurve3 curves that join smoothly? Ideally with a constructor that takes a simple array of vertices?
If I need to implement this myself, where is the best place to start? I'm thinking the .quadraticCurveTo() method could be extended to use a z component and added to SplineCurve3? I'm not 100% clear on how the array of curves works in the 'Path' object.
THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
var args = Array.prototype.slice.call( arguments );
var lastargs = this.actions[ this.actions.length - 1 ].args;
var x0 = lastargs[ lastargs.length - 2 ];
var y0 = lastargs[ lastargs.length - 1 ];
var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
new THREE.Vector2( aCPx, aCPy ),
new THREE.Vector2( aX, aY ) );
this.curves.push( curve );
this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
};
Thanks for your help!
Thanks to karatedog and fang for your in-depth answers. In searching for more information about B-spline curve, I stumbled upon this extra library for Three.js NURBS which is exactly what I needed. Upon closer inspection of the THREE.NURBSCurve() constructor in this library, it's implemented exactly as fang described: with arrays of both control points and knots. Knots are defined similarly to the method described above. I'm Marking Fang's answer as correct but I wanted to add this link to the pre-existing library as well, so any n00bs like myself could use it :)
If you are fine with using a high degree Bezier curve, then you can implement it using De Casteljau algorithm. The link in karatedog's answer provides a good source for this algorithm. If you want to stick with degree 3 polynomial curve with many control points, B-spline curve will be a good choice. B-spline curve can be implemented using Cox de Boor algorithm. You can find plenty of reference on the internet. B-spline curve definition requires degree, control points and knot vector. If you want your function to simply take an array of 3d points, you can set degree = 3 and internally define the knot vector as
[0, 0, 0, 0, 1/(N-3), 2/(N-3),....., 1, 1, 1, 1].
where N = number of control points.
For example,
N=4, knot vector=[0, 0, 0, 0, 1, 1, 1, 1],
N=5, knot vector=[0, 0, 0, 0, 1/2, 1, 1, 1, 1],
N=6, knot vector=[0, 0, 0, 0, 1/3, 2/3, 1, 1, 1, 1].
For the N=4 case, the B-spline curve is essentially the same as a cubic Bezier curve.
I suggest to implement your own calculation algorithm, it is fairly easy, the learning process is short and worth the time invested. Check this page: http://pomax.github.io/bezierinfo/
It describes a method (language agnostic) that you can calculate BeziƩr curves with any number of control points, although the a calculation that is specific to a certain number of control points (like cubic or quadratic) can be highly optimized.
Context: Two sets of data, one is the radius, r, and the other is the velocity, v. v can be positive and negative. The following code
p1=ListLogLogPlot[Table[{r[[i]],v[[i]]},{i,1,number_of_data}]];
p2=ListLogLogPlot[Table[{r[[i]],-v[[i]]},{i,1,number_of_data}],PlotStyle->{Red}];
Show[p1,p2]
is used to give a curve, with positive and negative v both plotted in log-log coordinates.
Question: How to draw a circular, contour-like plot, with Log[r] as the distance to the center of the circle, and the velocities (Log[v]) shown as different, but continuously varying colors, according to v's sign and magnitude?
You may use a DensityPlot function:
v[r_] := Sin[r]*r^2
DensityPlot[v[Norm[{x, y}]], {x, -5, 5}, {y, -5, 5}]
You can deal with the tabular data in two ways. You can either interpolate and use the interpolating function as above or you may use a ListDensityPlot function:
ListDensityPlot[Table[With[{r = RandomReal[{0, 4}], t = RandomReal[{0, 2 Pi}]},
{r Cos[t], r Sin[t], v[r]}], {10^4}]]
I hope this helps.
Here is the deal. I have multiple points (X,Y) that form an 'ellipse like' shape.
I would like to evaluate/fit the 'best' ellipse possible and get its properties (a,b,F1,F2), or just the center of the ellipse.
Any ideas/leads would be appreciated.
Gilad.
There's a Matlab function fit_ellipse that can do the job. There's also this paper on methods for orthogonal distance fitting of ellipses. A web search for orthogonal ellipse fit will probably turn up a lot of other resources as well.
The ellipse fitting method proposed by:
Z. L. Szpak, W. Chojnacki, and A. van den Hengel.
Guaranteed ellipse fitting with a confidence region and an uncertainty measure for centre, axes, and orientation.
J. Math. Imaging Vision, 2015.
may be of interest to you. They provide estimates of both algebraic and geometric ellipse
parameters, together with covariance matrices that express the uncertainty of the parameter estimates.
They also provide a means of computing a planar 95% confidence region associated with the estimate
that allows one to visualise the uncertainty in the ellipse fit.
A pre-print version of the paper is available on the authors websites (http://cs.adelaide.edu.au/~wojtek/publicationsWC.html).
A MATLAB implementation of the method is also available for download:
https://sites.google.com/site/szpakz/source-code/guaranteed-ellipse-fitting-with-a-confidence-region-and-an-uncertainty-measure-for-centre-axes-and-orientation
I will explain how I would approach the problem. I would suggest a hill climbing approach. First compute the gravity center of the points as a start point and choose two values for a and b in some way(probably arbitrary positive values will do). You need to have a fit function and I would suggest it to return the number of points (close enough to)lying on a given ellipse:
int fit(x, y, a, b)
int res := 0
for point in points
if point_almost_on_ellipse(x, y, a, b, point)
res = res + 1
end_if
end_for
return res
Now start with some step. I would choose a big enough value to be sure the best center of the elipse will never be more then step away from the first point. Choosing such a big value is not necessary, but the slowest part of the algorithm is the time it takes to get close to the best center so bigger value is better, I think.
So now we have some initial point(x, y), some initial values of a and b and an initial step. The algorithm iteratively chooses the best of the neighbours of the current point if there is any neighbour better then it, or decrease step twice otherwise. Here by 'best' I mean using the fit function. And also a position is defined by four values (x, y, a, b) and it's neighbours are 8: (x+-step, y, a, b),(x, y+-step, a, b), (x, y, a+-step, b), (x, y, a, b+-step)(if results are not good enough you can add more neighbours by also going by diagonal - for instance (x+-step, y+-step, a, b) and so on). Here is how you do that
neighbours = [[-1, 0, 0, 0], [1, 0, 0, 0], [0, -1, 0, 0], [0, 1, 0, 0],
[0, 0, -1, 0], [0, 0, 1, 0], [0, 0, 0, -1], [0, 0, 0, 1]]
iterate (cx, cy, ca, cb, step)
current_fit = fit(cx, cy, ca, cb)
best_neighbour = []
best_fit = current_fit
for neighbour in neighbours
tx = cx + neighbour[0]*step
ty = cx + neighbour[1]*step
ta = ca + neighbour[2]*step
tb = cb + neighbour[3]*step
tfit = fit(tx, ty, ta, tb)
if (tfit > best_fit)
best_fit = tfit
best_neighbour = [tx,ty,ta,tb]
endif
end_for
if best_neighbour.size == 4
cx := best_neighbour[0]
cy := best_neighbour[1]
ca := best_neighbour[2]
cb := best_neighbour[3]
else
step = step * 0.5
end_if
And you continue iterating until the value of step is smaller then a given threshold(for instance 1e-6). I have written everything in pseudo code as I am not sure which language do you want to use.
It is not guaranteed that the answer found this way will be optimal but I am pretty sure it will be good enough approximation.
Here is an article about hill climbing.
I think that Wild Magic library contains a function for ellipse fitting. There is article with method decription
The problem is to define "best". What is best in your case? The ellipse with the smallest area which contains n% of pointS?
If you define "best" in terms of probability, you can simply use the covariance matrix of your points, and compute the error ellipse.
An error ellipse for this "multivariate Gaussian distribution" would then contain the points corresponding to whatever confidence interval you decide.
Many computing packages can compute the covariance, with its corresponding eigenvalues and eigenvectors. The angle of the ellipse is the angle between the x axis and the eigenvector corresponding to the largest eigenvalue. The semi-axes are the reciprocal of the eigenvalues.
If your routine returns everything normalized (which it should), then you can decide by what factor to multiply everything to obtain an alpha-confidence interval.
So, I have this algorithm to calculate cross-section of 3D shape with plane given with normal vector.
However, my current problem is, that the cross-section is set of 3D points (all lying on that given plane) and to display it I need to map this coordinates to XY plane.
This works perfect if the plane normal is something like (0,0,c) - I just copy x and y coordinates discarding z.
And here is my question: Since I have no idea how to convert any other plain could anybody give me any hint as to what should I do now?
Your pane is defined by a normal vector
n=(xn,yn,zn)
For coordination transformation we need 2 base vectors and a zero point for the pane
Base vectors
We chose those "naturally" fitting to the x/y pane (see later for edge case):
b1=(1,0,zb1)
b2=(0,1,zb2)
And we want
b1 x b2 = n*c (c const scalar)
to make sure these two are really bases
Now solve this:
b1 x b2= (0*zb2-zb1*1,zb1*0-1*zb2,1*1-0*0) = (zb1,zb2,1)
zb1*c=xn
zb2*c=yn
1*c=zn
c=zn,
zb2=yn/c=yn/zn
zb1=xn/c=xn/zn
b1=(1,0,yn/zn)
b2=(0,1,xn/zn)
and normalize it
bv1=(1,0,yn/zn)*sqrt(1+(yn/zn*yn/zn))
bv2=(0,1,yn/zn)*sqrt(1+(xn/zn*xn/zn))
An edge case is, when zn=0: In this case the normal vector is parallel to the x/y pane and no natural base vectors exist, ind this case you have to chose base b1 and b2 vectors by an esthetic POV and go through the same solution process or just chose bv1 and bv2.
Zero point
you spoke of no anchor point for your pane in the OQ, but it is necessary to differentiate your pane from the infinite family of parallel panes.
If your anchor point is (0,0,0) this is a perfect anchor point for the coordinate transformation and your pane has
x*xn+y*yn+z*zn=0,
(y0,y0,z0)=(0,0,0)
If not, I assume you have an anchor point of (xa,ya,za) and your pane has
x*xn+y*yn+z*zn=d
with d const scalar. A natural fit would be the point of the pane, that is defined by normal projection of the original zero point onto the pane:
P0=(x0,y0,z0)
with
(x0, y0, z0) = c * (xn,yn,zn)
Solving this against
x*xn+y*yn+z*zn=d
gives
c*xn*xn+c*yn*yn+c*zn*zn=d
and
c=d/(xn*xn+yn*yn+zn*zn)
thus
P0=(x0,y0,z0)=c*(xn,yn,zn)
is found.
Final transformation
is achieved by representing every point of your pane (i.e. those points you want to show) as
P0+x'*bv1+y'*bv2
with x' and y' being the new coordinates. Since we know P0, bv1 and bv2 this is quite trivial. If we are not on the edge case, we have zeroes in bv1.y and bv2.x further reducing the problem.
x' and y' are the new coordinates you want.
I would like to add to Eugen's answer, a suggestion for the case where zn=0 extending his answer and also offer an alternative solution (which is similar).
In the case of zn=0, you can actually think of all the planes as points in a circle around the z-axis and the radius depends on the parameters of the plane.
Any vector orthogonal to the radius should be parallel to the plane, while the radius being the normal of the plane.
So in some way, the problem is reduced to a 2D-space.
The normal to the plane is (xn, yn, 0).
By using a technique to find orthogonal vectors in 2D, we get that a base vector could therefore be (-yn, xn, 0).
The second base vector is (0, 0, 1) which is just the normalized vector of their cross product. We can see that by developing the following expression:
corss_product((-yn, xn, 0), (xn, yn, 0)) =
(xn*0 - 0*yn, 0*xn - (-yn)*0, (-b)*b - a*a) =
(0, 0, -(xn^2 + yn^2)).
Which after normalizing and negating becomes (0, 0, 1).
From here, I suggest b1=normalize(-yn, xn, 0) and b2=(0, 0, 1).
Now, there's an even more general solution using this approach.
If you'll develop the dot product of (-yn, xn, 0) and (xn, yn, zn), you'll see that they are orthogonal for any zn while (-yn, xn, 0) also being part of the plane in question (when d=0). Thus, this actually works as long at least one of xn and yn is not zero (because otherwise (-yn, xn, 0) is actually just (0, 0, 0)).
Just to make sure it's clear, the second base vector is again their cross product, that is: b1=(-yn, xn, 0) and b2=cross_product(b1, n).
Well then, what about the case where both xn and yn are zero? In this case the plane is parallel to the xy plane. Now that's an easy one, just choose b1=(1, 0, 0) and b2=(0, 1, 0).
And as the other approach, use an anchor vector when d is not 0, exactly as it is described there, no changes needed.
Summary: 2 different solutions:
Use Eugen's answer answer and for the case of zn=0, take: b1=(-yn, xn, 0) and b2=(0, 0, 1).
A different approach: If both xn and yn equal 0, take b1=(1, 0, 0) and b2=(0, 1, 0), otherwise take b1=(-yn, xn, 0) and b2=cross_product(b1, n).
In both solutions, use an anchor vector P0 as described by the aforementioned answer.
I am trying to parametrize a 3D geometry for shape optimization. The structure looks like the following. Another real example is here.
Currently I am using BSplines to create the lower part and using symmetry to create the whole down part of the foil. Here is what I get.
Now I have many control points to take care in order to run a shape optimization. I also don't know how to join the upper part with the bottom hydrofoil part in a sensible way. I don't know how to design a good middle part of the foil (fat nose part of the foil) where the upper part is linked to. I also need to accompany a flap with in the geometry.
Please offer some suggestion for parametrization of such a surface so that we can manipulate the geometry from MMA. The less control points are there better the situation is for optimization. May be combination of some analytic function in 3D. But I doubt if that is possible.
BR
I think you have two choices: 1) create the second part of the geometry and then write a face-face intersection algorithm to merge them. 2) create the second part of the geometry and write two functions that return -1 if a query point is inside the geometry and +1 if it is out side (other values will do). Then use RegionPlot3D[ f1[x,y,z]<0 || f2[x,y,z]<0,....]. The idea is the to extract the GraphicsComplex and use that. The question is going to be how well you can approximate the corners with that. Here is an illustration of what I mean.
if1[x_, y_, z_] := If[x^2 + y^2 + z^2 <= 1, -1, 1]
if2[x_, y_, z_] := If[(x - 1)^2 + y^2 <= 1 && -1.5 <= z <= 1.5, -1, 1]
res = RegionPlot3D[
if1[x, y, z] < 0 || if2[x, y, z] < 0, {x, -2, 2}, {y, -2,
2}, {z, -2, 2}, PlotPoints -> 100, Boxed -> False, Axes -> False]
Then extract the coords and the polygons.
coords = res[[1, 1]];
poly = Cases[res[[1]], _Polygon, Infinity];
Graphics3D[GraphicsComplex[coords, poly], Boxed -> False]
Hope this helps.