i am using a method i found on the internet that was in cpp and i changed it a bit for java. it seems to work only half the time. is it a bug with java? because it will return true or false depending on where you are inside the triangle. can anybody help me fix it or find a better way to test for a point inside a triangle? heres the method. sorry if its hard to understand the question
public static float area(float x1, float y1, float x2, float y2, float x3, float y3)
{
return (float) Math.abs((x1*(y2-y3) + x2*(y3-y1)+ x3*(y1-y2))/2.0);
}
/* A function to check whether point P(x, y) lies inside the triangle formed
by A(x1, y1), B(x2, y2) and C(x3, y3) */
public static boolean isInside(float x1, float y1, float x2, float y2, float x3, float y3, float x, float y)
{
/* Calculate area of triangle ABC */
float A = area (x1, y1, x2, y2, x3, y3);
/* Calculate area of triangle PBC */
float A1 = area (x, y, x2, y2, x3, y3);
/* Calculate area of triangle PAC */
float A2 = area (x1, y1, x, y, x3, y3);
/* Calculate area of triangle PAB */
float A3 = area (x1, y1, x2, y2, x, y);
/* Check if sum of A1, A2 and A3 is same as A */
return (A == A1 + A2 + A3);
}
This is a problem of floating point precision. Keep in mind that floating point calculations are executed with a limited amount of precision. Therefore, if you make different calculations that mathematically should produce the same result, the actual result produced by your computer will generally not be identical.
The problem is therefore with this test:
return (A == A1 + A2 + A3);
If the point is inside the triangle, A and A1 + A2 + A3 will have very similar values, but not necessarily identical values due to limited floating point precision during the calculation.
The most direct way to fix it would be to allow for some imprecision in the comparison:
return Math.abs(A - (A1 + A2 + A3) < eps);
where eps is a small floating point constant. Choosing a good value for these kinds of tolerances is always tricky, so it's best to avoid algorithms that make this necessary if anyway possible (see proposed solution below). If you make the tolerance too small, the test will fail for points inside the triangle. If you make it too large, you will allow for more false positives, because points just slightly outside the triangle will pass the test.
If you really wanted to use this kind of test, it would be more stable to test for the relative difference instead of the absolute difference, since the absolute error will typically increase as the values themselves get larger. Testing the relative difference would look like this:
return Math.abs((A - A1 - A2 - A3) / A) < eps;
Then for the value of eps, I would start with something that is safely larger than the relative precision of a float value, which is about 7 digits. Something like 1.0e-5f seems reasonable.
There are better ways of doing a "point in triangle" test. Actually, I think you could use most of the math you already have. I haven't tested the following at all, so it comes without any warranty, but I believe it should work.
The idea is that you don't really need to care that all the partial areas sum up to the area of the triangle. I believe it's sufficient that they are all positive. You can change the area function to return the signed area by removing the abs() call:
public static float area(float x1, float y1, float x2, float y2, float x3, float y3)
{
return 0.5f * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
}
Then calculate A1, A2 and A3 as before, and check that they are all positive:
return A1 >= 0.0f && A2 >= 0.0f && A3 >= 0.0f;
This assumes that the triangle has counter-clockwise orientation. If you want it to also work for clockwise orientation, you would need to check that all three values have the same sign.
Related
I've just started learning java this quarter and on a sample quiz, this is a question. I have no idea where to begin save for the fact that it has to return a boolean type.
Fill in the blank so that this function returns true if the circle at x1, y1, with radius r1, has collided with the circle at x2, y2, with radius r2.
boolean collided(int x1, int y1, int r1, int x2, int y2, int r2) {
return ___________________________________________________________;
}
Appreciate the comments and kind help. Like I said above, if you had read it, I've just started learning java this quarter and on a sample quiz, this is a question I had no idea where to begin. If anyone is willing to direct me to another learning resource that is better suited for a complete beginner, that would be great.
Balls are collided when distance between their centers is less than sum of their radiuses.
Distance is sqrt((x1-x2)^2 + (y1-y2)^2)
sqrt is square root, a^b is power (a^2 is a*a).
In terms of code this is:
boolean collided(int x1, int y1, int r1, int x2, int y2, int r2) {
return Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)) < r1+r2;
}
That statement consists of the calculcation of the distance between the two circles's centers and checking if that distance is less than the sum of the two radiuses. That check returns a boolean value, true if the circles "collided"
boolean collided(int x1, int y1, int r1, int x2, int y2, int r2)
{
return !(Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))>(r1+r2));
}
I tried to create and translate a polygon in openGL, I create a function for translation but this create a white line from x0 to v_size and I don't understand why ?
This is my function for polygon translation
public void translate1(GL2 gl, double x0, double x1, double y0, double y1){
double step = 0.2;
for(double i = 0; i < v_size; i += step){
gl.glBegin(GL2.GL_POLYGON);
gl.glVertex2d(x0 + i, y0);
gl.glVertex2d(x0 + i, y1);
gl.glVertex2d(x1 + i, y1);
gl.glVertex2d(x1 + i, y0);
gl.glEnd();
}
}
Initial x0 = 0, x1 = 10, y0 = 20, y1 = 30.
Thanks !
Have a nice day!
The reason for this is, that you draw squares every step units away from each other. Since nothing gets cleared in the meantime, the overlapping quads form a line.
It is rather unclear what you are trying to achieve. A translation would usually not draw multiple quads. If you are trying to do an animation, then you'll have to split the movement over multiple frames and draw exactly one square in every frame.
I asked about this yesterday as well and was told that I needed to be using radians. I had assumed that was the answer but there does appear to be another problem with the code below. I am trying to write a method which will allow my Gladiators to rotate a central point between the two, to circle each other so to speak. Below is my code. Gladiator variable target is of Gladiator type and is set to the second party in the rotation. center[0] is the x location, center[1] is y.
public void rotate(Gladiator a, int angle) {
double x1;
double y1;
double x2;
double y2;
double r;
double midx;
double midy;
int currentAngle;
r = getDistance(a,a.target)/2;
midx = (a.center[0]-a.target.center[0])/2;
midy = (a.center[1]-a.target.center[1])/2;
currentAngle = (int)Math.toDegrees(Math.atan2(a.center[1] - midy, a.center[0] - midx));
x1 = Math.cos(Math.toRadians(currentAngle+angle)) * r;
y1 = Math.sin(Math.toRadians(currentAngle+angle)) * r;
x2 = Math.cos(Math.toRadians(currentAngle+angle)) * -r;
y2 = Math.sin(Math.toRadians(currentAngle+angle)) * -r;
a.move((int)x1,(int)y1);
a.target.move((int)x2,(int)y2);
}
Anyone see anything wrong with this? At the moment they end up meeting towards what I would think would be the middle of my circle, waaay closer than they were. Any thoughts?
Edit: Also, I am currently running this twice... once for each Gladiator involved. I can do that and just have them rotate half the desired amount each time, but it would be better if I could rotate them as a whole then disinclude the second party from the Gladiator list I am iterating through. What would be the most simple implementation of this?
Edit2: I think part of my problem was that I wasn't calling Math.toDegrees on my atan2 equation. I noticed it wasn't getting any new angle value other than 0, now it is. Still, there is something wrong. They rotate from horizontal to vertical but are moving much further from each other on each rotation and once they get to the vertical alignment they end up rotating the other direction just a few degrees (rather than 45, my current input) and then do get much closer together like before.
Edit3: Note that the move method's parameters are the change needed, not the actual coordinates.
I see you are using int a lot, be very careful since you may get stuck depending on angle.
I did a quick rewrite to simplify your repetition and use the radian logic that was recommended. (Untested).
I also converted your locations to double to avoid odd integer arithmetic problems. (Your midx/midy calculations were in int math)
After finishing I realized you were rotating around (0,0) rather than the midpoint, and your mid variables were confusingly named.
//I would do double inline with the initialization, but left here in case you had another reason
double x1,y1, x2,y2, r, midx,midy, newAngle;
x1 = a.center[0];
y1 = a.center[1];
x2 = a.target.center[0];
y1 = a.target.center[1];
r = getDistance(a, a.target)/2;
midx = x1 + (x2 - x1)/2;
midy = y1 + (y2 - y1)/2;
newAngle = Math.toRadians(angle) +
Math.atan2(midy, midx);
x1 = Math.cos(newAngle) * r + midx;
y1 = Math.sin(newAngle) * r + midy;
x2 = Math.cos(newAngle) * -r + midx;
y2 = Math.sin(newAngle) * -r + midy;
a.move((int)x1,(int)y1);
a.target.move((int)x2,(int)y2);
Given a line segment defined by (x1, y1) and (x2, y2), find the location on the line where y = a certain value.
I understand I could obtain the equation of the line, then solve using simultaneous equations, but is this possible using Line2D or any other Java classes?
Any suggestions or comments would be appreciated!
Kelvin.
I don't think there's anything in the Java API to actually calculate the intersection. The closest you can get, I think, is to use Line2D to test whether the line segment intersects the (vertical) line y = k for some constant k:
Line2D line = new Line2D.Double(x1, y1, x2, y2);
if (line.intersectsLine(Double.MIN_VALUE, k, Double.MAX_VALUE, k)) {
double x = (x2 - x1) * (k - y1) / (y2 - y1);
// intersection is at (x, k)
} else {
// intersection is outside extend of the line segment
}
However, it would probably be more efficient to just check that y1 != y2, compute the intersection, and then check whether the x coordinate is in the range [x1, x2].
I would suggest apache commons math for tasks like this.
http://commons.apache.org/math/apidocs/index.html
There are a number of ways you can do this. One way would be
org.apache.commons.math3.geometry.euclidean.twod.Line lineOne = new org.apache.commons.math3.geometry.euclidean.twod.Line(p,angle);
org.apache.commons.math3.geometry.euclidean.twod.Line horizontalLine = new org.apache.commons.math3.geometry.euclidean.twod.Line(new vector2d(0,yToSolveFor),pi/2);
Vector2D intersection=lineOne.intersect(horizontalLine);
intersection.getX(); // The answer
im trying to draw an arc - just a simple looking arc from point (x1,y1) to point (x2,y2)
how do i do that?
i been using the so complex and not freindly to user method called drawArc on Graphics class. no luck yet tho.
thats what i tried:
void drawArc(Graphics2D g, int x1, int y1, int x2, int y2) {
AffineTransform prev = g.getTransform();
double dx = x2 - x1, dy = y2 - y1;
double angle = Math.atan2(dy, dx);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
at.rotate(angle);
g.transform(at);
g.drawArc(len/2, len/2, len ,len/2, 0, 60);
g.setTransform(prev);
}
thanks ahead.
graphics.drawLine(x1,y1,x2,y2) is the simplest possible arc that you can draw with these information.
Probably it is not what you want. If you want something more ... curvy you need to define somehow how curvy it is, in what direction. The drawArc method requires you to calculate an ellipse that touches both points. The arc is the segment of the circle between those points. There is an infinite number of possible ellipses. (The drawLine example assumes an infinite ellipse.) But this requires more information (what ellipse to chose) and some calculation.
If you want to draw curves between two points and control points (what you probably want) you need to look into QuadCurve2D or CubicCurve2D and drawShape. You can find sample code here.