Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have solved this problem but im not sure if its correct..
User should give the coordinates of a point and I should check if that point is within,outside or on the circle. I used the distance formula to solve this .
The given information about the circle are:
Circle is centered at ( 0,0 )
and radius is 10
public static void main(String[] strings) {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter a point with two coordinates");
double y1 = scanner.nextDouble();
double x1 = scanner.nextDouble();
// circle is centered at 0,0
double y2 = 0;
double x2 = 0;
double radius = 10;
double distance;
// using the distance formula to calculate the distance
// from the point given from the user and point where circle is centered
/**
* distance formula
* d = √ ( x2 - x1 )^2 + (y2 - y1 )^2
*/
distance = Math.pow( x2 - x1,2) + Math.pow(y2-y1,2);
// find square root
distance = Math.sqrt(distance);
String result="";
if(distance < radius) {
result = "("+y1+" , "+x1+")" +" is within the circle";
}
else if(distance > radius) {
result = y1+" , "+x1 +" is outside the circle";
}
else if(distance == radius) {
result =y1+" , "+x1 +" is on the circle";
}
System.out.println(result);
}
It's fine but sloppy.
There's no need to compute the square root. Work in units of distance squared.
Then compare using distance < radius * radius etc., perhaps renaming distance for the sake of clarity. Computing square roots is costly and imprecision can creep in which can be difficult to control. This is particularly important in your case where you want to test for a point being on the circle's edge.
Also consider writing (x2 - x1) * (x2 - x1) rather than using pow for the second power. Although Java possibly (I never remember for sure which is certainly a good enough reason for my not using it) optimises to the longer form, other languages (such as C) don't and imprecision can creep in there too.
Are you sure this question requires doubles as input? The examples given are integers. With integers you can be sure of a points location, with real numbers (doubles) you cannot be sure about the "on circle" or not which is another reason I think the question expects you to use integers.
And the trick for performance and accuracy is to not use Math.sqrt and only work with integers.
int x;
int y;
int centreX;
int centreY;
int deltaX = x - centreX;
int deltaY = y - centreY;
int distanceSquared = deltaX * deltaX + deltaY * deltaY;
int radiusSquared = radius * radius;
if (distanceSquared == radiusSquared) { //distance == radius
//on circle
} else if (distanceSquared < radiusSquared) { //distance < radius
//in circle
} else {
//out of circle
}
Consider using Math.hypot() to calculate a distance and compare double values using some small threshold:
static final double DELTA = 1E-5; // not necessarily 1E-5; see comments
//...
distance = Math.hypot(x2 - x1, y2 - y1);
if (Math.abs(distance - radius) < DELTA)
// on the circle
else if (distance > radius)
// outside
else
// inside
The reason for using DELTA is that there is a very small chance to get equal double values by calculations. If they differ in at least one bit, direct comparison will return false.
By appliying threshold, you check not whether the point lays exactly on the circle, but whether it lays inside a ring between radius - DELTA and radius + DELTA. So DELTA is a kind of tolerance limit which value should be chosen particularily for application, e. g. depending on absolute or relative input inaccuracy.
Related
I started learning java just over a year ago so i'm still fairly new.
I'm trying to make an object travel from one point to another at a constant net velocity no matter where the second point is in the frame. Currently it's working fairly well as long as I run the method every few frames.
The only problem is that it it will only move horizontally unless the second point is approximately between 45 and 135 degrees or between 225 and 315 degrees (1/2π and 3/2π or 5/2π and 7/2π).
It may be because of the 'if' statements meant to stop it from dividing by 0 but it doesn't seem like it. Also if there is any way to simplify those equations or remove 'if' statements I wouldn't mind some advice there too.
Note: vel is the net velocity the objects travel at and Prime.mx and Prime.my is the location of the target point.
public void target()
{
if (Prime.mx > x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x + (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.mx < x)
{
if (Math.abs(x-Prime.mx) != 0)
x = Math.round(Math.round((x - (vel*Math.cos(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my > y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y + (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
if (Prime.my < y)
{
if (Math.abs(x-Prime.mx) != 0)
y = Math.round(Math.round((y - (vel*Math.sin(Math.atan(Math.abs(y-Prime.my)/Math.abs(x-Prime.mx)))))));
}
}
I use Math.round twice because the first brings it to a float from a double and the second makes it an int. I need the x and y as ints so the paint method can draw the objects.
I found a few simillar problems on the site but the closest one was in python and and the anwer didn't seem applicable to my problem.
I believe you are overcomplicating this. If your starting point is (sx, sy) and your destination is (dx, dy) then you can easily calculate any point (x, y) that is p distance along the line (0.0 <= p <= 1.0). You can use this to move at velocity v. So I suggest finding your end point and then using simple arithmetic to move on the x and y axis.
float dx = dist * Math.cos(angle);
float dy = dist * Math.sin(angle);
for (float p = 0.0; p <= 1.0; p = Math.min(1.0, p + dist / v) {
x = sx + p * (dx - sx);
y = sy + p * (dy - sy);
}
The Math.min expression in the for loop ensures that you end up exactly at the destination point.
If you already have the destination point then it's just as easy. Instead of finding dx and dy from dist and angle you find dist from dx and dy using pythagoras.
More than solution these are some advices.
First, implement all you coordinate variables as floats to prevent rounding precision loss errors and round only right before painting.
Second, define float dx = Prime.mx - x; float dy = Prime.my - y; distance to target from current point (to use later). I would use Math.atan2(dy,dx) to compute angle between current point and target. Then use that angle to increment coordinates like this:
x += Math.cos(angle)*vel;
y += Math.sin(angle)*vel;
Third, check if your object is at target using (dx*dx + dy*dy <= radius*radius) for suitable radius (can be 1).
Also note that if the y axis goes down, then the angle will be CW (clock-wise) instead of CCW (counter-clock-wise).
Is there a way to move a bitmap from point1 to point 2 using the angle?
x += speed * Math.sin(getAngle(pointDestination));
y += speed * Math.cos(getAngle(pointDestination));
edit:
public double getAngle(Point target) {
double angle = (Math.atan2(target.y - y, target.x - x));
double angledeg = angle*0.0174532925;
return angledeg;
}
Should getAngle() be executed on every iteration or just once at the beginning?
Unfortunately the sprite moves to a wrong direction.
Your problem is that you increment the x value and when you go to increment the y too you are using the new x that you just incremented to calculate the angle.
Change it to:
float angle=getAngle(pointDestination);
x += speed * Math.cos(angle);
y += speed * Math.sin(angle);
public double getAngle(Point target) {
return Math.atan2(target.y - y, target.x - x);
}
Instead of doing a incremental update of the bitmap position, you better define a (mathematical) function that computes the position (x, y) over time. The advantage is that this will result in very exact and predictable movements independent of CPU speed / frames per second.
Assuming that the bitmap should move at constant speed from (x1, y1) to (x2, y2) in time milliseconds, so your (time dependent) position functions are as follows:
x(t) := x1 + (x2 - x1) * t / time // t in range [0, time]
y(t) := y1 + (y2 - y1) * t / time // t in range [0, time]
(Note: By doing some physics/maths, you can define more sophisticated functions that result in more complex movements).
This two functions can then be used in your animation thread to update the position of the bitmap:
bitmap.setX(x(currentTime - animationStartTime));
bitmap.setY(y(currentTime - animationStartTime));
Have a look at Trident animation library. It supports multiple UI frameworks and seems to be exactly what you're looking for!
Update: In case you really want to do a incremental update, e.g. based on your current frame rate, you don't need trigonometric functions (sin, cos, tan, ...) at all, just vectors:
// x, y is the current position of the bitmap
// calculate vector (dx, dy) to target:
double dx = x2 - x;
double dy = y2 - y;
// calculate length of this vector:
double l = Math.hypot(dx, dy); // calculates sqrt(dx² + dy²)
// calculate unit vector of (dx, dy):
double vx = dx / l;
double vy = dy / l;
// update bitmap position:
// distance is the number of pixels to travel in this iteration (frame)
x += distance * vx;
y += distance * vy;
Note that all values should be of type double. Otherwise, if int is used for x and y and the increment is lower than 1 (e.g. due to slow movement, i.e. distance is very low), the bitmap won't move at all due to rounding errors!
Also note that in this approach you have to measure the frame rate to adjust distance accordingly to compensate deviation. The formula could be something like:
double distance = (time elapsed since last frame in sec) * (distance to travel per sec)
I hava a class representing a point (x and y coordinates are double type) and a function to rotate the point around another point:
public Point2D rotate(double angle, Point2D origin) {
double sin = Math.sin(angle);
double cos = Math.cos(angle);
x -= origin.getX();
y -= origin.getY();
x = x*cos - y*sin;
y = x*sin + y*cos;
x += origin.getX();
y += origin.getY();
return this;
}
However when I repeat the rotation many times (i.e. by 1 Degree) I loose much of precision. Example:
Point2D point = new Point2D(10, 10);
System.out.println(point);
for(int i = 0; i < 360; i++)
point.rotate(Math.toRadians(1), new Point2D(300, 150));
System.out.println(point);
And the results:
[10.0, 10.0]
[25.5048671135757, 17.40466547204096]
Do you have any idea how to solve this issue? Thanks in advance.
firstly, there's an error in your formula...
the line
x = x*cos - y*sin;
modifies the value of x and the modified value is used in the next line
y = x*sin + y*cos;
You have to use a temp variable to store the new value of x and y, this is what user2602548 meant with his sugestion.
I guess you're already using double since you would otherwise need a cast to float for those two lines.
If you fix the algorithmic error you get something like [9.99999999999659, 9.999999999998778] .
If that's not good enough for you, you could either round after the rotations and if that's also not good enough use a lib that provides trigonometric functions with more precision like apfloat.
Using BigDecimal with that problem won't give you any more precision because the problem here is that the results of sin() and cos() are still only double precision.
I've issued myself a sort of challenge, and thought I could stand to ask for help getting my head around it. I want to use java Graphics to draw something that looks like lightning striking a given point.
Right now I just have this, which shoots cheap "lightning" in random directions, and I don't care where it ends up.
lightning[0] = new Point(370,130); //This is a given start point.
// Start in a random direction, each line segment has a length of 25
double theta = rand.nextDouble()*2*Math.PI;
int X = (int)(25*Math.cos(theta));
int Y = (int)(25*Math.sin(theta));
//Populate the array with more points
for (int i = 1 ; i < lightning.length ; i++)
{
lightning[i] = new Point(X + lightning[i-1].x, Y + lightning[i-1].y);
boolean plusminus = rand.nextBoolean();
if (plusminus) theta = theta + rand.nextDouble()*(Math.PI/2);
else theta = theta - rand.nextDouble()*(Math.PI/2);
X = (int)(25*Math.cos(theta));
Y = (int)(25*Math.sin(theta));
}
// Draw lines connecting each point
canvas.setColor(Color.WHITE);
for (int i = 1 ; i < lightning.length ; i++)
{
int Xbegin = lightning[i-1].x;
int Xend = lightning[i].x;
int Ybegin = lightning[i-1].y;
int Yend = lightning[i].y;
canvas.drawLine(Xbegin, Ybegin, Xend, Yend);
//if (Xend != Xbegin) theta = Math.atan((Yend - Ybegin)/(Xend - Xbegin));
// Restrict the angle to 90 degrees in either direction
boolean plusminus = rand.nextBoolean();
if (plusminus) theta = theta + rand.nextDouble()*(Math.PI/2);
else theta = theta - rand.nextDouble()*(Math.PI/2);
// 50/50 chance of creating a half-length off-shoot branch on the end
if (rand.nextBoolean())
{
int Xoff = (int)(Xend+(12*Math.cos(theta)));
int Yoff = (int)(Yend+(12*Math.sin(theta)));
canvas.drawLine(Xend, Yend, Xoff, Yoff);
}
}
I'm trying to think of some similar way to create this effect, but have the last point in the array pre-defined, so that the lightning can "strike" a specific point. In other words, I want to populate a Point array in a way that is random, but still converges on one final point.
Anyone care to weigh in?
I think this is fairly simple, accurate, and elegant approach. It uses a divide and conquer strategy. Start with only 2 values:
start point
end point
Calculate the midpoint. Offset that midpoint some value variance (which can be calculated relative to the length). The offset should ideally be normal to the vector connecting start and end, but you could be cheap by making that offset horizontal, as long as your bolts travel mostly vertically, like real lightning. Repeat above procedure for both (start, offset_mid) and (offset_mid, end), but this time using a smaller number for variance. This is a recursive approach which can terminate when either a threshold variance is achieved, or a threshold line segment length. As the recursion unwinds, you can draw all the connector segments. The idea is that the largest variance happens in the center of the bolt (when the start-to-end distance is the longest), and with each recursive call, the distance between points shrinks, and so does the variance. This way, the global variance of the bolt will be much greater than any local variances (like a real lightning bolt).
Here is an image of 3 different bolts generated from the same pre-determined points with this algorithm. Those points happen to be (250,100) and (500,800). If you want bolts that travel in any direction (not just "mostly vertical"), then you'll need to add more complexity to the point shifting code, shifting both X and Y based on the angle of travel of the bolt.
And here is some Java code for this approach. I used an ArrayList since the the divide and conquer approach doesn't know ahead of time how many elements it will end up with.
// play with these values to fine-tune the appearance of your bolt
private static final double VAR_FACTOR = 0.40;
private static final double VAR_DECREASE = 0.55;
private static final int MIN_LENGTH = 50;
public static ArrayList<Point> buildBolt(Point start, Point end) {
ArrayList<Point> bolt = new ArrayList<Point>();
double dx = start.getX() - end.getX();
double dy = start.getY() - end.getY();
double length = Math.sqrt(dx*dx + dy*dy);
double variance = length * VAR_FACTOR;
bolt.add(start);
buildBolt(start, end, bolt, variance);
return bolt;
}
private static void buildBolt(Point start, Point end,
List<Point> bolt, double variance) {
double dx = start.getX() - end.getX();
double dy = start.getY() - end.getY();
double length = Math.sqrt(dx*dx + dy*dy);
if (length > MIN_LENGTH) {
int varX = (int) ((Math.random() * variance * 2) - variance);
int midX = (start.x + end.x)/2 + varX;
int midY = (start.y + end.y)/2;
Point mid = new Point(midX, midY);
buildBolt(start, mid, bolt, variance * VAR_DECREASE);
buildBolt(mid, end, bolt, variance * VAR_DECREASE);
} else {
bolt.add(end);
}
return;
}
Without any graphics experience, I have this advice: it's going to be hard to pick a specific point, then try to reach it in a "random" way. Instead, I'd recommend creating a straight line from the origin to destination, then randomly bending parts of it. You could make several passes, bending smaller and smaller segments down to a certain limit to get the desired look. Again, I'm saying this without any knowledge of the graphics API.
I need to draw a smooth line through a set of vertices. The set of vertices is compiled by a user dragging their finger across a touch screen, the set tends to be fairly large and the distance between the vertices is fairly small. However, if I simply connect each vertex with a straight line, the result is very rough (not-smooth).
I found solutions to this which use spline interpolation (and/or other things I don't understand) to smooth the line by adding a bunch of additional vertices. These work nicely, but because the list of vertices is already fairly large, increasing it by 10x or so has significant performance implications.
It seems like the smoothing should be accomplishable by using Bezier curves without adding additional vertices.
Below is some code based on the solution here:
http://www.antigrain.com/research/bezier_interpolation/
It works well when the distance between the vertices is large, but doesn't work very well when the vertices are close together.
Any suggestions for a better way to draw a smooth curve through a large set of vertices, without adding additional vertices?
Vector<PointF> gesture;
protected void onDraw(Canvas canvas)
{
if(gesture.size() > 4 )
{
Path gesturePath = new Path();
gesturePath.moveTo(gesture.get(0).x, gesture.get(0).y);
gesturePath.lineTo(gesture.get(1).x, gesture.get(1).y);
for (int i = 2; i < gesture.size() - 1; i++)
{
float[] ctrl = getControlPoint(gesture.get(i), gesture.get(i - 1), gesture.get(i), gesture.get(i + 1));
gesturePath.cubicTo(ctrl[0], ctrl[1], ctrl[2], ctrl[3], gesture.get(i).x, gesture.get(i).y);
}
gesturePath.lineTo(gesture.get(gesture.size() - 1).x, gesture.get(gesture.size() - 1).y);
canvas.drawPath(gesturePath, mPaint);
}
}
}
private float[] getControlPoint(PointF p0, PointF p1, PointF p2, PointF p3)
{
float x0 = p0.x;
float x1 = p1.x;
float x2 = p2.x;
float x3 = p3.x;
float y0 = p0.y;
float y1 = p1.y;
float y2 = p2.y;
float y3 = p3.y;
double xc1 = (x0 + x1) / 2.0;
double yc1 = (y0 + y1) / 2.0;
double xc2 = (x1 + x2) / 2.0;
double yc2 = (y1 + y2) / 2.0;
double xc3 = (x2 + x3) / 2.0;
double yc3 = (y2 + y3) / 2.0;
double len1 = Math.sqrt((x1-x0) * (x1-x0) + (y1-y0) * (y1-y0));
double len2 = Math.sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
double len3 = Math.sqrt((x3-x2) * (x3-x2) + (y3-y2) * (y3-y2));
double k1 = len1 / (len1 + len2);
double k2 = len2 / (len2 + len3);
double xm1 = xc1 + (xc2 - xc1) * k1;
double ym1 = yc1 + (yc2 - yc1) * k1;
double xm2 = xc2 + (xc3 - xc2) * k2;
double ym2 = yc2 + (yc3 - yc2) * k2;
// Resulting control points. Here smooth_value is mentioned
// above coefficient K whose value should be in range [0...1].
double k = .1;
float ctrl1_x = (float) (xm1 + (xc2 - xm1) * k + x1 - xm1);
float ctrl1_y = (float) (ym1 + (yc2 - ym1) * k + y1 - ym1);
float ctrl2_x = (float) (xm2 + (xc2 - xm2) * k + x2 - xm2);
float ctrl2_y = (float) (ym2 + (yc2 - ym2) * k + y2 - ym2);
return new float[]{ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y};
}
Bezier Curves are not designed to go through the provided points! They are designed to shape a smooth curve influenced by the control points.
Further you don't want to have your smooth curve going through all data points!
Instead of interpolating you should consider filtering your data set:
Filtering
For that case you need a sequence of your data, as array of points, in the order the finger has drawn the gesture:
You should look in wiki for "sliding average".
You should use a small averaging window. (try 5 - 10 points). This works as follows: (look for wiki for a more detailed description)
I use here an average window of 10 points:
start by calculation of the average of points 0 - 9, and output the result as result point 0
then calculate the average of point 1 - 10 and output, result 1
And so on.
to calculate the average between N points:
avgX = (x0+ x1 .... xn) / N
avgY = (y0+ y1 .... yn) / N
Finally you connect the resulting points with lines.
If you still need to interpolate between missing points, you should then use piece - wise cubic splines.
One cubic spline goes through all 3 provided points.
You would need to calculate a series of them.
But first try the sliding average. This is very easy.
Nice question. Your (wrong) result is obvious, but you can try to apply it to a much smaller dataset, maybe by replacing groups of close points with an average point. The appropriate distance in this case to tell if two or more points belong to the same group may be expressed in time, not space, so you'll need to store the whole touch event (x, y and timestamp). I was thinking of this because I need a way to let users draw geometric primitives (rectangles, lines and simple curves) by touch
What is this for? Why do you need to be so accurate? I would assume you only need something around 4 vertices stored for every inch the user drags his finger. With that in mind:
Try using one vertex out of every X to actually draw between, with the middle vertex used for specifying the weighted point of the curve.
int interval = 10; //how many points to skip
gesture.moveTo(gesture.get(0).x, gesture.get(0).y);
for(int i =0; i +interval/2 < gesture.size(); i+=interval)
{
Gesture ngp = gesture.get(i+interval/2);
gesturePath.quadTo(ngp.x,ngp.y, gp.x,gp.y);
}
You'll need to adjust this to actually work but the idea is there.