I'm scratching my head with the implementation of an algorithm I have been led to believe will calculate the coefficients to an equation that will give me an ellipse that will bound a set of points. Given that I don't know how the algorithm actually does what it is supposed to do, I'm stumped as to why the algorithm isn't actually working as I think it should. I am as sure as I can be that I have implemented the algorithm accurately. I realise that I probably have stuffed up somewhere.
My implementation was modelled from this implementation in C++ because I found it easier to work with than the pseudo code given here. The OP of the C++ implementation says that it is based on the same pseudo code.
Here is my implementation:
// double tolerance = 0.2;
// int n = 10;
// int d = 2;
double tolerance=0.02;
int n=10;
int d=2;
// MatrixXd p = MatrixXd::Random(d,n);
RealMatrix p=new BlockRealMatrix(d,n,new double[][]{{70,56,44,93,77,12,30,51,35,82,74,38,92,49,22,69,71,91,39,13}},false);
// MatrixXd q = p;
// q.conservativeResize(p.rows() + 1, p.cols());
RealMatrix q=p.createMatrix(d+1,n);
q.setSubMatrix(p.getData(),0,0);
// for(size_t i = 0; i < q.cols(); i++)
// {
// q(q.rows() - 1, i) = 1;
// }
//
// const double init_u = 1.0 / (double) n;
// MatrixXd u = MatrixXd::Constant(n, 1, init_u);
double[]ones=new double[n];
double[]uData=new double[n];
for(int i=0;i<n;i++){
ones[i]=1;
uData[i]=((double)1)/((double)n);
}
q.setRow(d,ones);
// int count = 1;
// double err = 1;
int count=0;
double err=1;
while(err>tolerance){
// MatrixXd Q_tr = q.transpose();
RealMatrix qTr=q.transpose();
// MatrixXd X = q * u.asDiagonal() * Q_tr;
RealMatrix uDiag=MatrixUtils.createRealDiagonalMatrix(uData);
RealMatrix qByuDiag=q.multiply(uDiag);
RealMatrix x=qByuDiag.multiply(qTr);
// MatrixXd M = (Q_tr * X.inverse() * q).diagonal();
RealMatrix qTrByxInverse=qTr.multiply(MatrixUtils.inverse(x));
RealMatrix qTrByxInverseByq=qTrByxInverse.multiply(q);
int r=qTrByxInverseByq.getRowDimension();
double mData[]=new double[r];
for(int i=0;i<r;i++){
mData[i]=qTrByxInverseByq.getEntry(i,i);
}
// double maximum = M.maxCoeff(&j_x, &j_y);
// As M is a matrix formed from mData where only cells on the
// diagonal are populated with values greater than zero, the row
// and column values will be identical, and will be equal to the
// place where the maximum value occurs in mData. The matrix M
// is never used again in the algorithm, and hence creation of
// the matrix M is unnecessary.
int iMax=0;
double dMax=0;
for(int i=0;i<mData.length;i++){
if(mData[i]>dMax){
dMax=mData[i];
iMax=i;
}
}
// double step_size = (maximum - d - 1) / ((d + 1) * (maximum + 1));
double stepSize=(dMax-d-1)/((d+1)*(dMax+1));
// MatrixXd new_u = (1 - step_size) * u;
double[]uDataNew=new double[n];
for(int i=0;i<n;i++){
uDataNew[i]=(((double)1)-stepSize)*uData[i];
}
// new_u(j_x, 0) += step_size;
uDataNew[iMax]+=stepSize;
// MatrixXd u_diff = new_u - u;
// for(size_t i = 0; i < u_diff.rows(); i++)
// {
// for(size_t j = 0; j < u_diff.cols(); j++)
// u_diff(i, j) *= u_diff(i, j); // Square each element of the matrix
// }
// err = sqrt(u_diff.sum());
double sum=0;
for(int i=1;i<n;i++){
double cell=uDataNew[i]-uData[i];
sum+=(cell*cell);
}
err=Math.sqrt(sum);
// count++
// u = new_u;
count++;
uData=uDataNew;
}
// MatrixXd U = u.asDiagonal();
RealMatrix uFinal=MatrixUtils.createRealDiagonalMatrix(uData);
// MatrixXd A = (1.0 / (double) d) * (p * U * p.transpose() - (p * u) * (p * u).transpose()).inverse();
// Broken down into the following 9 sub-steps:
// 1 p * u
double[][]uMatrixData=new double[1][];
uMatrixData[0]=uData;
RealMatrix u=new BlockRealMatrix(n,1,uMatrixData,false);
RealMatrix cFinal=p.multiply(u);
// 2 (p * u).transpose()
RealMatrix two=cFinal.transpose();
// 3 (p * u) * (p * u).transpose()
RealMatrix three=cFinal.multiply(two);
// 4 p * U
RealMatrix four=p.multiply(uFinal);
// 5 p * U * p.transpose()
RealMatrix five=four.multiply(p.transpose());
// 6 p * U * p.transpose() - (p * u) * (p * u).transpose()
RealMatrix six=five.subtract(three);
// 7 (p * U * p.transpose() - (p * u) * (p * u).transpose()).inverse()
RealMatrix seven=MatrixUtils.inverse(six);
// 8 1.0 / (double) d
double eight=((double)1)/d;
// 9 MatrixXd A = (1.0 / (double) d) * (p * U * p.transpose() - (p * u) * (p * u).transpose()).inverse()
RealMatrix aFinal=seven.scalarMultiply(eight);
// MatrixXd c = p * u; This has been calculated in sub-step (1) above and stored as cFinal.
System.out.println();
System.out.println("The coefficients of ellipse's equation are given as follows:");
for(int i=0;i<aFinal.getRowDimension();i++){
for(int j=0;j<aFinal.getColumnDimension();j++){
System.out.printf(" %3.8f",aFinal.getEntry(i,j));
}
System.out.println();
}
System.out.println();
System.out.println("The the axis shifts are given as follows:");
for(int i=0;i<cFinal.getRowDimension();i++){
for(int j=0;j<cFinal.getColumnDimension();j++){
System.out.printf(" %3.8f",cFinal.getEntry(i,j));
}
System.out.println();
}
// Get the centre of the set of points, which will be the centre of the
// ellipse. This part was not actually included in the C++
// implementation. I guess the OP considered it too trivial.
double xmin=0;
double xmax=0;
double ymin=0;
double ymax=0;
for(int i=0;i<p.getRowDimension();i++){
double x=p.getEntry(i,0);
double y=p.getEntry(i,1);
if(i==0){
xmin=xmax=x;
ymin=ymax=y;
}else{
if(x<xmin){
xmin=x;
}else if(x>xmax){
xmax=x;
}
if(y<ymin){
ymin=y;
}else if(y>ymax){
ymax=y;
}
}
}
double x=(xmax-xmin)/2+xmin;
double y=(ymax-ymin)/2+ymin;
System.out.println();
System.out.println("The centre of the ellipse is given as follows:");
System.out.printf(" The x axis is %3.8f.\n",x);
System.out.printf(" The y axis is %3.8f.\n",y);
System.out.println();
System.out.println("The algorithm completed ["+count+"] iterations of its while loop.");
// This code constructs and displays a yellow ellipse with a black border.
ArrayList<Integer>pointsx=new ArrayList<>();
ArrayList<Integer>pointsy=new ArrayList<>();
for (double t=0;t<2*Math.PI;t+=0.02){ // <- or different step
pointsx.add(this.getWidth()/2+(int)(cFinal.getEntry(0,0)*Math.cos(t)*aFinal.getEntry(0,0)-cFinal.getEntry(1,0)*Math.sin(t)*aFinal.getEntry(0,1)));
pointsy.add(this.getHeight()/2+(int)(cFinal.getEntry(0,0)*Math.cos(t)*aFinal.getEntry(1,0)+cFinal.getEntry(1,0)*Math.sin(t)*aFinal.getEntry(1,1)));
}
int[]xpoints=new int[pointsx.size()];
Iterator<Integer>xpit=pointsx.iterator();
for(int i=0;xpit.hasNext();i++){
xpoints[i]=xpit.next();
}
int[]ypoints=new int[pointsy.size()];
Iterator<Integer>ypit=pointsy.iterator();
for(int i=0;ypit.hasNext();i++){
ypoints[i]=ypit.next();
}
g.setColor(Color.yellow);
g.fillPolygon(xpoints,ypoints,pointsx.size());
g.setColor(Color.black);
g.drawPolygon(xpoints,ypoints,pointsx.size());
This program generates the following output:
The coefficients of ellipse's equation are given as follows:
0.00085538 0.00050693
0.00050693 0.00093474
The axis shifts are given as follows:
54.31114965
55.60647648
The centre of the ellipse is given as follows:
The x axis is 72.00000000.
The y axis is 47.00000000.
The algorithm completed [23] iterations of its while loop.
I find it somewhat strange that entries of the 2x2 matrix are very small. I am led to believe that these entries are the coefficients used to describe the orientation of the ellipse, while the second 2x1 matrix describes the size of the ellipse's x and y axes.
I understand that the equations used to obtain the points are referred to as parametric equations. They have a form that is quoted here.
The location of the centre of the ellipse and the computation of these values has been added by me. They do not appear in the C++ implementation, and after I married the output of this algorithm to the parametric equations used to describe the ellipse, I was lead to believe that the OP of the C++ implementation gave the wrong impression that this 2x1 matrix described the ellipse's centre. I admit that the impression I formed could be wrong, because if one assumes I am right, then the centre (half-way between the lowest and highest values of both axes) appears to be wrong; it is less than the size of the y axis, which I take is a radial measure.
When I plug these values into the parametric equations for the ellipse to generate the points I then use to create a Polygon, the generated shape occupies a single pixel. Considering the values given in the 2x2 matrix describing the orientation, this is what I would expect.
Hence, it seems to me that there is some problem in how I generate the 2x2 matrix that produces the orientation.
I have done my best to be concise and to provide all the relevant facts, as well as any relevant impressions I have formed whether they be right or wrong. I hope someone can provide an answer to this question.
Unfortunately, I haven't been able to find help for this question.
However, I was able to find a compromise solution involving implementations in multiple languages for an enclosing circle here. I'll leave this question for someone else to answer if a better solution can be offered.
I took a stab at implementing the Minimum Bounding Ellipse in Java today using the same psuedocode you referenced in your question. I must admit, I don't understand the math but I do know that the super small numbers that you see in the "coefficients of ellipse" matrix are normal.
For my implementation, I ported MatLab code by Nima Moshtagh. In the comments section on the MatLab page, there is some code by Peter Lawrence which looks something like this:
C=inv(C);
tq=linspace(-pi,pi,M); % for display purposes, M is the number of points on the ellipse
[Ve,De]=eig(C);
De=sqrt(diag(De));
[l1,Ie] = max(De);
veig=Ve(:,Ie);
thu=atan2(veig(2),veig(1));
l2=De(setdiff([1 2],Ie));
U=[cos(thu) -sin(thu);sin(thu) cos(thu)]*[l1*cos(tq);l2*sin(tq)];
plot(U(1,:)+m(1),U(2,:)+m(2))
C is the 2x2 matrix describing the ellipse and m is the center point.
The resultant U is an array of x,y coordinates. If you add the coordinates to the center point, you'll get a ring of points that can be used to render the ellipse.
Again, I'm not 100% sure what the math is doing but it works.
Related
So, I've got a method that returns the area of a shape defined by its points (given in CCW or CW order, it doesn't really matter). I tested it with some shapes and it seems to be working.
The problem is, I want to use this method with GPS coordinates, and I want to return the result in m² or km², but that's definitly not what happens. In fact, I don't even know in which unit the result is when I use this method with that kind of coordinates.
So the question is, how to convert the result I have into m² or km² ? I tried some things, but either it does not work or it's inaccurate.
Here's my method, if you want to check :
public static double getArea(List<Vector2D> points) {
double firstSum = 0, secondSum = 0;
for (int i = 0 ; i < points.size()-1 ; i++) {
firstSum += points.get(i).x * points.get(i+1).y;
secondSum += points.get(i).y * points.get(i+1).x;
}
firstSum += points.get( points.size()-1 ).x * points.get(0).y;
secondSum += points.get( points.size()-1 ).y * points.get(0).x;
return Math.abs((firstSum-secondSum)/2);
}
(Vector2D is the class I use for points, with x as the latitude and y as the longitude)
The problem is that you're not taking the (approximately) spherical nature of the Earth into account. For a start, you need to take into account the radius of the Earth - you could have the same list of latitude and longitude on a smaller (or bigger) planet, but the radius would be different, and consequently the area would be different too.
You can use the approach in this question. It's trivial to convert that to Java:
public static double CalculatePolygonArea(List<Vector2D> coordinates)
{
double area = 0;
if (coordinates.size() > 2)
{
for (int i = 0; i < coordinates.size()-1; i++)
{
Vector2D p1, p2;
p1 = coordinates.get(i);
p2 = coordinates.get(i + 1);
area += Math.toRadians(p2.x - p1.x) * (2 + Math.sin(Math.toRadians(p1.y))
+ Math.sin(Math.toRadians(p2.y)));
}
area = area * R * R / 2;
}
return Math.abs(area);
}
(assuming Vector2D.x is the longitude and Vector2D.y is the latitude).
R is the radius of the Earth. Use a value in the unit you want the area result to be in (e.g. 6_371_000 metres for square metres, 6_371 km for square km, 3_959 miles for square miles...)
I'm programming a software renderer in Java, and am trying to use Z-buffering for the depth calculation of each pixel. However, it appears to work inconsistently. For example, with the Utah teapot example model, the handle will draw perhaps half depending on how I rotate it.
My z-buffer algorithm:
for(int i = 0; i < m_triangles.size(); i++)
{
if(triangleIsBackfacing(m_triangles.get(i))) continue; //Backface culling
for(int y = minY(m_triangles.get(i)); y < maxY(m_triangles.get(i)); y++)
{
if((y + getHeight()/2 < 0) || (y + getHeight()/2 >= getHeight())) continue; //getHeight/2 and getWidth/2 is for moving the model to the centre of the screen
for(int x = minX(m_triangles.get(i)); x < maxX(m_triangles.get(i)); x++)
{
if((x + getWidth()/2 < 0) || (x + getWidth()/2 >= getWidth())) continue;
rayOrigin = new Point2D(x, y);
if(pointWithinTriangle(m_triangles.get(i), rayOrigin))
{
zDepth = zValueOfPoint(m_triangles.get(i), rayOrigin);
if(zDepth > zbuffer[x + getWidth()/2][y + getHeight()/2])
{
zbuffer[x + getWidth()/2][y + getHeight()/2] = zDepth;
colour[x + getWidth()/2][y + getHeight()/2] = m_triangles.get(i).getColour();
g2.setColor(m_triangles.get(i).getColour());
drawDot(g2, rayOrigin);
}
}
}
}
}
Method for calculating the z value of a point, given a triangle and the ray origin:
private double zValueOfPoint(Triangle triangle, Point2D rayOrigin)
{
Vector3D surfaceNormal = getNormal(triangle);
double A = surfaceNormal.x;
double B = surfaceNormal.y;
double C = surfaceNormal.z;
double d = -(A * triangle.getV1().x + B * triangle.getV1().y + C * triangle.getV1().z);
double rayZ = -(A * rayOrigin.x + B * rayOrigin.y + d) / C;
return rayZ;
}
Method for calculating if the ray origin is within a projected triangle:
private boolean pointWithinTriangle(Triangle triangle, Point2D rayOrigin)
{
Vector2D v0 = new Vector2D(triangle.getV3().projectPoint(modelViewer), triangle.getV1().projectPoint(modelViewer));
Vector2D v1 = new Vector2D(triangle.getV2().projectPoint(modelViewer), triangle.getV1().projectPoint(modelViewer));
Vector2D v2 = new Vector2D(rayOrigin, triangle.getV1().projectPoint(modelViewer));
double d00 = v0.dotProduct(v0);
double d01 = v0.dotProduct(v1);
double d02 = v0.dotProduct(v2);
double d11 = v1.dotProduct(v1);
double d12 = v1.dotProduct(v2);
double invDenom = 1.0 / (d00 * d11 - d01 * d01);
double u = (d11 * d02 - d01 * d12) * invDenom;
double v = (d00 * d12 - d01 * d02) * invDenom;
// Check if point is in triangle
if((u >= 0) && (v >= 0) && ((u + v) <= 1))
{
return true;
}
return false;
}
Method for calculating surface normal of a triangle:
private Vector3D getNormal(Triangle triangle)
{
Vector3D v1 = new Vector3D(triangle.getV1(), triangle.getV2());
Vector3D v2 = new Vector3D(triangle.getV3(), triangle.getV2());
return v1.crossProduct(v2);
}
Example of the incorrectly drawn teapot:
What am I doing wrong? I feel like it must be some small thing. Given that the triangles draw at all, I doubt it's the pointWithinTriangle method. Backface culling also appears to work correctly, so I doubt it's that. The most likely culprit to me is the zValueOfPoint method, but I don't know enough to know what's wrong with it.
My zValueOfPoint method was not working correctly. I'm unsure why :( however, I changed to a slightly different method of calculating the value of a point in a plane, found here: http://forum.devmaster.net/t/interpolation-on-a-3d-triangle-using-normals/20610/5
To make the answer here complete, we have the equation of a plane:
A * x + B * y + C * z + D = 0
Where A, B, and C are the surface normal x/y/z values, and D is -(Ax0 + By0 + Cz0).
x0, y0, and z0 are taken from one of the vertices of the triangle. x, y, and z are the coordinates of the point where the ray intersects the plane. x and y are known values (rayOrigin.x, rayOrigin.y) but z is the depth which we need to calculate. From the above equation we derive:
z = -A / C * x - B / C * y - D
Then, copied from the above link, we do:
"Note that for every step in the x-direction, z increments by -A / C, and likewise it increments by -B / C for every step in the y-direction.
So these are the gradients we're looking for to perform linear interpolation. In the plane equation (A, B, C) is the normal vector of the plane.
It can easily be computed with a cross product.
Now that we have the gradients, let's call them dz/dx (which is -A / C) and dz/dy (which is -B / C), we can easily compute z everywhere on the triangle.
We know the z value in all three vertex positions.
Let's call the one of the first vertex z0, and it's position coordinates (x0, y0). Then a generic z value of a point (x, y) can be computed as:"
z = z0 + dz/dx * (x - x0) + dz/dy * (y - y0)
This found the Z value correctly and fixed my code. The new zValueOfPoint method is:
private double zValueOfPoint(Triangle triangle, Point2D rayOrigin)
{
Vector3D surfaceNormal = getNormal(triangle);
double A = surfaceNormal.x;
double B = surfaceNormal.y;
double C = surfaceNormal.z;
double dzdx = -A / C;
double dzdy = -B / C;
double rayZ = triangle.getV1().z * modelViewer.getModelScale() + dzdx * (rayOrigin.x - triangle.getV1().projectPoint(modelViewer).x) + dzdy * (rayOrigin.y - triangle.getV1().projectPoint(modelViewer).y);
return rayZ;
}
We can optimize this by only calculating most of it once, and then adding dz/dx to get the z value for the next pixel, or dz/dy for the pixel below (with the y-axis going down). This means that we cut down on calculations per polygon significantly.
this must be really slow
so much redundant computations per iteration/pixel just to iterate its coordinates. You should compute the 3 projected vertexes and iterate between them instead look here:
triangle/convex polygon rasterization
I dislike your zValueOfPoint function
can not find any use of x,y coordinates from the main loops in it so how it can compute the Z value correctly ?
Or it just computes the average Z value per whole triangle ? or am I missing something? (not a JAVA coder myself) in anyway it seems that this is your main problem.
if you Z-value is wrongly computed then Z-Buffer can not work properly. To test that look at the depth buffer as image after rendering if it is not shaded teapot but some incoherent or constant mess instead then it is clear ...
Z buffer implementation
That looks OK
[Hints]
You have too much times terms like x + getWidth()/2 why not compute them just once to some variable? I know modern compilers should do it anyway but the code would be also more readable and shorter... at least for me
I'm trying to get my head around this- and I've literally been looking for a whole day!
I think I understand the main concepts behind it, but I'm struggling to figure out the math I need to create the axis on which to project my shapes on to?
So if I have a rectange I find out each of the points and then use these to find the side of the shape edge = v(n) - v(n-1) and go through all sides.
But I don't know how to then create the separating axis.
The theorem is not difficult to understand: If you can find a line for which all points of shape A are on the one side, and all points of shape B are on the other (dot product positive or negative), that line is separating the shapes.
What do you want to do? Find separating lines for arbitrary shapes?
I would recommend to have a look at projective geometry, as the edge for two vertices of a polygon extended to infinity can be represented by the cross product of the two vertices (x, y, 1). For convex polygons you can simply create lines for all edges and then take the dot product of all vertices of your other polygon to check on which side they are. If for one edge all points are outside, that edge is a separating line.
Also keep in mind that by keeping the line normalized you get the distance of a point to the line using dot product. The sign identifies the side on which the point lies.
If your problem is more difficult, please explain it in more detail. Maybe you can use some sort of clipping to solve it fast.
Example: projective geometry
public double[] e2p(double x, double y) {
return new double[] { x, y, 1 };
}
// standard vector maths
public double[] getCrossProduct(double[] u, double[] v) {
return new double[] { u[1] * v[2] - u[2] * v[1],
u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0] };
}
public double getDotProduct(double[] u, double[] v) {
return u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
}
// collision check
public boolean isCollision(List<Point2D> coordsA, List<Point2D> coordsB) {
return !(isSeparate(pointsA, pointsB) || isSeparate(pointsB, pointsA));
}
// the following implementation expects the convex polygon's vertices to be in counter clockwise order
private boolean isSeparate(List<Point2D> coordsA, List<Point2D> coordsB) {
edges: for (int i = 0; i < coordsA.size(); i++) {
double[] u = e2p(coordsA.get(i).getX(), coordsA.get(i).getY());
int ni = i + 1 < coordsA.size() ? i + 1 : 0;
double[] v = e2p(coordsA.get(ni).getX(), coordsA.get(ni).getY());
double[] pedge = getCrossProduct(u, v);
for (Point2D p : coordsB) {
double d = getDotProduct(pedge, e2p(p.getX(), p.getY()));
if (d > -0.001) {
continue edges;
}
}
return true;
}
return false;
}
The separating axis is one of the sides. You can find the signs of the vertexes of the shape itself when plugged in the equation of this side:
(X - Xn).(Y - Yn-1) - (X - Xn-1).(Y - Yn) = 0
Check that the vertices of the other shape yield opposite signs.
For school, I have recently started creating my own raytracer. However, I've hit a snag with either computing the viewing rays, or checking for an intersection between a triangle and a ray. As far as I can tell, the computations seem to be executed correctly, as I place my camera in the origin and have it face the -z axis towards an object right in front of it, allowing for simple vector maths by hand. Everything seems to check out, but nothing gets painted on the screen.
I will post the code I am using the calculate the viewing rays.
public Ray generateRay(float nX, float nY , Point2f coordinates)
{
// Compute l, r, b and t.
Vector3f temp = VectorHelper.multiply(u, nX/2.0f);
float r = temp.x + Position.x;
temp = VectorHelper.multiply(u, -nX/2.0f);
float l = temp.x + Position.x;
temp = VectorHelper.multiply(v, nY/2.0f);
float t = temp.y + Position.y;
temp = VectorHelper.multiply(v, -nY/2.0f);
float b = temp.y + Position.y;
// Compute the u and v coordinates.
float uCo = (l + (r - l) * (coordinates.x + 0.5f)/nX);
float vCo = (b + (t - b) * (coordinates.y + 0.5f)/nY);
// Compute the ray's direction.
Vector3f rayDirection = VectorHelper.multiply(w, -FocalLength);
temp = VectorHelper.add(VectorHelper.multiply(u, uCo), VectorHelper.multiply(v, vCo));
rayDirection = VectorHelper.add(rayDirection, temp);
rayDirection = VectorHelper.add(rayDirection, Position);
rayDirection = VectorHelper.normalize(VectorHelper.add(rayDirection, temp));
// Create and return the ray.
return new Ray(Position, rayDirection);
}
The following code is what I use to calculate an intersection. It uses Cramer's Rule to solve the matrix equation.
public static Point3f rayTriangleIntersection(
Ray ray, Point3f vertexA, Point3f vertexB, Point3f vertexC)
{
// Solve the linear system formed by the ray and the parametric surface
// formed by the points of the triangle.
// | a d g | | B | | j |
// | b e h | * | Y | = | k |
// | c f i | * | t | = | l |
// The following uses Cramer's rule to that effect.
float a = vertexA.x - vertexB.x; float d = vertexA.x - vertexC.x; float g = ray.getDirection().x;
float b = vertexA.y - vertexB.y; float e = vertexA.y - vertexC.y; float h = ray.getDirection().y;
float c = vertexA.z - vertexB.z; float f = vertexA.z - vertexC.z; float i = ray.getDirection().z;
float j = vertexA.x - ray.getOrigin().x;
float k = vertexA.y - ray.getOrigin().y;
float l = vertexA.z - ray.getOrigin().z;
// Compute some subterms in advance.
float eihf = (e * i) - (h * f);
float gfdi = (g * f) - (d * i);
float dheg = (d * h) - (e * g);
float akjb = (a * k) - (j * b);
float jcal = (j * c) - (a * l);
float blkc = (b * l) - (k * c);
// Compute common division number.
float m = (a * eihf) + (b * gfdi) + (c * dheg);
// Compute unknown t and check whether the point is within the given
// depth interval.
float t = -((f * akjb) + (e * jcal) + (d * blkc)) / m;
if (t < 0)
return null;
// Compute unknown gamma and check whether the point intersects the
// triangle.
float gamma = ((i * akjb) + (h * jcal) + (g * blkc)) / m;
if (gamma < 0 || gamma > 1)
return null;
// Compute unknown beta and check whether the point intersects the
// triangle.
float beta = ((j * eihf) + (k * gfdi) + (l * dheg)) / m;
if (beta < 0 || beta > (1 - gamma))
return null;
// Else, compute the intersection point and return it.
Point3f result = new Point3f();
result.x = ray.getOrigin().x + t * ray.getDirection().x;
result.y = ray.getOrigin().y + t * ray.getDirection().y;
result.z = ray.getOrigin().z + t * ray.getDirection().z;
return result;
}
My question is rather simple. What am I doing wrong? I've looked and debugged this code to death and cannot single out the errors, google offers little more than the theory I already have in the book I am using. Also, the code is still rather rough as I'm just focusing on getting it to work before cleaning it up.
Thanks in advance,
Kevin
Hard to say precisely what is going wrong. Especially since you aren't using descriptive variable names (what are nX, nY etc.??)
Here are some tips:
First make sure it's not a bug in your display code. Fake an intersection to prove that you get visible output when you hit something, e.g. make all rays in the bottom right of the screen hit an axis-aligned plane or something similar so that you can easily verify the co-ordinates
Try a ray/sphere intersection first. It's easier than a ray/triangle intersection.
Consider using vector/matrix operations rather than computing all the components by hand. It's too easy to make a mistake in many lines of jumbled letters.
If you have a scale problem (e.g. the object is too small) then double-check your conversions between world and screen co-ordinates. World co-ordinates will be in the range of small double numbers (0.2 ....5.0 for example) while screen co-ordinates should be pixel locations according to your view size (0 .. 1024 for example). You should be doing most of your maths in world co-ordinates, only converting from/to screen co-ordinates at the beginning and the end of your rendering code.
Step through the top level raytracing code in a debugger and make sure that you are producing rays in sensible directions for each screen co-ordinate(especially the corners of the screen)
Check that your camera direction is pointing towards the target object. It is quite an easy mistake to have it looking in exactly the opposite direction!
Example set-up that should work:
Camera position [0 0 4]
Object position [0 0 0]
Camera DIRECTION [0 0 -1] (note the minus if you want it to look towards the origin!)
UP vector [0 0.75 0]
RIGHT vector [+/-1 0 0]
Then your ray direction should be something like (for a pixel [screenX, screenY]):
ray = DIRECTION + (2*(screenX / screenWidth)-1)*RIGHT + (1-2*(screenY/screenHeight))*UP
ray = normalize(ray)
I have an array of points, as well as two more points (A and B). The last two points form a line, and I'm trying to find which of the points in my array are furthest from the line. How would I do this in Java?
I'm wondering if it's something along the lines of finding the distance from A and B, but that doesn't sit well in my head.
Additional info:
I think it's a line segment. Given this is QuickHull, I don't know if it makes a difference.
I've never been the greatest when it comes to math and formulas, so more explanation is better. Thanks!
Note that each 3 points [a,b,p] for each p in the array form a trianle, whose area is denoted by: (ab) * h /2 [where h is the distance from p to ab]
You can compute the area these trianles create, and select the minimal. Since ab is constant for all - it guarantees that the trianle with the minimal area will also have the minimal h.
You can find it [the area of each triangle] using
T=(1/2)* abs((x_a - x_p) * (y_b-y_a) - (x_a - x_b)* (y_p - y_a))
[where x_a,x_b,x_p and y_a,y_b,y_p are the x,y coordinates of a,b,p respectively].
Though I find this method very elegant, I believe there are better
ways to do it.
ArrayList<Point> points=new ArrayList();//YOUR POINTS
Point a=new Point(1,1);
Point b=new Point(1,1);
Point ABcenter=new Point((a.x+b.x)/2,(a.y+b.y)/2);//THE CENTER OF THE A AND B POINTS ,USE A OR B IF YOU WANT
int furthestid=0;
float furthestdis=0;
for(int c=0;c<points.size();c++)
{
if(calculate_distance(ABcenter.x,ABcenter.y,points.get(c).x,points.get(c).y)>furthestdis)
{
furthestid=c;
}
}
//closestid now contains the id of the furthest point ,use it like this points.get(c).x ...
public static double calculate_distance (float x1,float y1,float x2 ,float y2){
return Math.sqrt((((x1-x2) * (x1-x2)) + ((y1- y2) * (y1- y2))));
}
I'm assuming you mean the Euclidean distance. If you're working in the plane, then the answer is simple.
First, compute the equation of the line in the form
ax + by + c = 0
In slope-intercept form, this is the same as
y = (-a/b)x + (-c/b)
Now compute the distance from any point (p,q) to the line by
|a*p + b*q + c| / (a^2 + b^2)^(1/2)
For more than 2 dimensions, it's probably easiest to think in terms of parametrized vectors. This means think of points on the line as
p(t) = (A1 + (B1-A1)*t, A2 + (B2-A2)*t, ..., An + (Bn-An)*t)
where the two points are A = (A1,...,An) and B = (B1,...,Bn). Let X = (X1,...,Xn) be any other point. Then the distance between X and p(t), the point on the line corresponding to t, is the square root of
[(A1-X1) + (B1-A1)t]^2 + ... + [(An-Xn) + (Bn-An)t]^2
The distance to the line is the distance to p(t) where t is the unique value minimizing this distance. To compute that, just take the derivative with respect to t and set it to 0. It's a very straightforward problem from here, so I'll leave that bit to you.
If you want a further hint, then check out this link for the 3-dimensional case which reduces nicely.
If the problem is as you have stated it, you can't do much better then computing the distance for each point and choosing the smallest of these.
However you can simplify the calculation of the distance a bit by using a generalized line equation for the line passing through A and B. This would be an equation of the form ax + by + c = 0
You can compute such equation for a line passing through two arbitrary points quite easily:
x * (A.y - B.y) + y * (B.x - A.x) + A.x * B.y - A.y * B.x,
i.e. a = A.y - B.y, b = B.x - A.x and c = A.x * B.y - A.y * B.x
Now that you have computed such equation for the line, you can compute the distance from an arbitrary point P in the plane to the line a * x + b * y + c by substituting x and y with the coordinates of P:
abs(a * P.x + b * P.y + c) / sqrt(a * a + b * b). But as the denominator will be the same for all points, you may igonore it and simply choose the point for which abs(a * P.x + b * P.y + c) is smallest
Here is a link that explains how to compute 2-d distance to a line once you have it's generalized equation.
I'm assuming you talking about line segment not line. First you should find your points distance from your line segment, and you can do it, as the way suggested in this similar question, after that find minimum/maximum distance over all inputs.
Edit: Also from this top coder article you can find distance simply:
//Compute the dot product AB ⋅ BC
int dot(int[] A, int[] B, int[] C){
AB = new int[2];
BC = new int[2];
AB[0] = B[0]-A[0];
AB[1] = B[1]-A[1];
BC[0] = C[0]-B[0];
BC[1] = C[1]-B[1];
int dot = AB[0] * BC[0] + AB[1] * BC[1];
return dot;
}
//Compute the cross product AB x AC
int cross(int[] A, int[] B, int[] C){
AB = new int[2];
AC = new int[2];
AB[0] = B[0]-A[0];
AB[1] = B[1]-A[1];
AC[0] = C[0]-A[0];
AC[1] = C[1]-A[1];
int cross = AB[0] * AC[1] - AB[1] * AC[0];
return cross;
}
//Compute the distance from A to B
double distance(int[] A, int[] B){
int d1 = A[0] - B[0];
int d2 = A[1] - B[1];
return sqrt(d1*d1+d2*d2);
}
//Compute the distance from AB to C
//if isSegment is true, AB is a segment, not a line.
double linePointDist(int[] A, int[] B, int[] C, boolean isSegment){
double dist = cross(A,B,C) / distance(A,B);
if(isSegment){
int dot1 = dot(A,B,C);
if(dot1 > 0)return distance(B,C);
int dot2 = dot(B,A,C);
if(dot2 > 0)return distance(A,C);
}
return abs(dist);
}
I think code has self explanation, if you are familiar with basic geometry, but if you aren't familiar, you should to read the article, if there is any problem for you we can help you.