Circle Line Intersection points - java

public static ArrayList<IntPoint> getCircleLineIntersectionPoint(IntPoint pointA, IntPoint pointB, IntPoint center, int radius) {
// returns a list of intersection points between a line which passes through given points,
// pointA and pointB, and a circle described by given radius and center coordinate
double disc, A, B, C, slope, c;
double x1, x2, y1, y2;
IntPoint point1, point2;
ArrayList<IntPoint> intersections = new ArrayList<IntPoint>();
try{
slope = Util.calculateSlope(pointA, pointB);
}catch (UndefinedSlopeException e){
C = Math.pow(center.y, 2) + Math.pow(pointB.x, 2) - 2 * pointB.x * center.x + Math.pow(center.x, 2) - Math.pow(radius, 2);
B = -2 * center.y;
A = 1;
disc = Math.pow(B, 2) - 4 * 1 * C;
if (disc < 0){
return intersections;
}
else{
y1 = (-B + Math.sqrt(disc)) / (2 * A);
y2 = (-B - Math.sqrt(disc)) / (2 * A);
x1 = pointB.x;
x2 = pointB.x;
}
point1 = new IntPoint((int)x1, (int)y1);
point2 = new IntPoint((int)x2, (int)y2);
if (Util.euclideanDistance(pointA, point2) > Util.euclideanDistance(pointA, point1)){
intersections.add(point1);
}
else{
intersections.add(point2);
}
return intersections;
}
if (slope == 0){
C = Math.pow(center.x, 2) + Math.pow(center.y, 2) + Math.pow(pointB.y, 2) - 2 * pointB.y * center.y - Math.pow(radius, 2);
B = -2 * center.x;
A = 1;
disc = Math.pow(B, 2) - 4 * 1 * C;
if (disc < 0){
return intersections;
}
else{
x1 = (-B + Math.sqrt(disc)) / (2*A);
x2 = (-B - Math.sqrt(disc)) / (2*A);
y1 = pointB.y;
y2 = pointB.y;
}
}
else{
c = slope * pointA.x + pointA.y;
B = (2 * center.x + 2 * center.y * slope + 2 * c * slope);
A = 1 + Math.pow(slope, 2);
C = (Math.pow(center.x, 2) + Math.pow(c, 2) + 2 * center.y * c + Math.pow(center.y, 2) - Math.pow(radius, 2));
disc = Math.pow(B, 2) - (4 * A * C);
if (disc < 0){
return intersections;
}
else{
x1 = (-B + Math.sqrt(disc)) / (2 * A);
x2 = (-B - Math.sqrt(disc)) / (2 * A);
y1 = slope * x1 - c;
y2 = slope * x2 - c;
}
}
point1 = new IntPoint((int)x1, (int)y1);
point2 = new IntPoint((int)x2, (int)y2);
if (Util.euclideanDistance(pointA, point2) > Util.euclideanDistance(pointA, point1)){
//if (Util.angleBetween(pointA, pointB, point1) < Math.PI/2){
intersections.add(point1);
//}
}
else{
//if (Util.angleBetween(pointA, pointB, point1) < Math.PI/2){
intersections.add(point2);
//}
}
return intersections;
}
I am using the above algorithm to test for intersection between a circle and a line. It works fine sometimes but at other times it fails. The code represents the equation which is derived from solving for x simultaneously from circle and line equations (x-a)^+(y-b)^2=r^2 and y = mx - mx1 + y1. Has anyone got an idea where I am going wrong either in my maths or elsewhere?

Your calculations seem quite long, and I do not see the use of the different cases you test.
Anyway, since I found the problem interesting I attempted to solve it myself and came up with the following. Feel free to replace double radius by int radius and use IntPoints, but be aware that every time you cast, as discussed in the comments, results that are not exact integer intersection points will become wrong.
The background of the calculations performed is this: From point A, a scaled version of vector AB points to a point on the circle. That point has distance radius from center. Hence, |AC + scalingFactor * AB|=r.
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CircleLine {
public static List<Point> getCircleLineIntersectionPoint(Point pointA,
Point pointB, Point center, double radius) {
double baX = pointB.x - pointA.x;
double baY = pointB.y - pointA.y;
double caX = center.x - pointA.x;
double caY = center.y - pointA.y;
double a = baX * baX + baY * baY;
double bBy2 = baX * caX + baY * caY;
double c = caX * caX + caY * caY - radius * radius;
double pBy2 = bBy2 / a;
double q = c / a;
double disc = pBy2 * pBy2 - q;
if (disc < 0) {
return Collections.emptyList();
}
// if disc == 0 ... dealt with later
double tmpSqrt = Math.sqrt(disc);
double abScalingFactor1 = -pBy2 + tmpSqrt;
double abScalingFactor2 = -pBy2 - tmpSqrt;
Point p1 = new Point(pointA.x - baX * abScalingFactor1, pointA.y
- baY * abScalingFactor1);
if (disc == 0) { // abScalingFactor1 == abScalingFactor2
return Collections.singletonList(p1);
}
Point p2 = new Point(pointA.x - baX * abScalingFactor2, pointA.y
- baY * abScalingFactor2);
return Arrays.asList(p1, p2);
}
static class Point {
double x, y;
public Point(double x, double y) { this.x = x; this.y = y; }
#Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
}
public static void main(String[] args) {
System.out.println(getCircleLineIntersectionPoint(new Point(-3, -3),
new Point(-3, 3), new Point(0, 0), 5));
System.out.println(getCircleLineIntersectionPoint(new Point(0, -2),
new Point(1, -2), new Point(1, 1), 5));
System.out.println(getCircleLineIntersectionPoint(new Point(1, -1),
new Point(-1, 0), new Point(-1, 1), 5));
System.out.println(getCircleLineIntersectionPoint(new Point(-3, -3),
new Point(-2, -2), new Point(0, 0), Math.sqrt(2)));
}

Here a solution with import javax.vecmath.Vector2d;
static Vector2d[] circleLineIntersection1(Vector2d a, Vector2d b, Vector2d o, double radius) {
Vector2d p1 = new Vector2d(a);
Vector2d p2 = new Vector2d(b);
p1.sub(o);
p2.sub(o);
Vector2d d = new Vector2d();
d.sub(p2, p1);
double det = p1.x * p2.y - p2.x * p1.y;
double dSq = d.lengthSquared();
double discrimant = radius * radius * dSq - det * det;
if (discrimant < 0) {
return new Vector2d[0];
}
if (discrimant == 0) {
Vector2d[] t = new Vector2d[1];
t[0] = new Vector2d(det * d.y / dSq + o.x, -det * d.x / dSq + o.y);
return t;
}
double discSqrt = Math.sqrt(discrimant);
double sgn = 1;
if (d.y < 0) {
sgn = -1;
}
Vector2d[] t = new Vector2d[2];
t[0] = new Vector2d((det * d.y + sgn * d.x * discSqrt) / dSq + o.x, (-det * d.x + Math.abs(d.y) * discSqrt) / dSq + o.y);
t[1] = new Vector2d((det * d.y - sgn * d.x * discSqrt) / dSq + o.x, (-det * d.x - Math.abs(d.y) * discSqrt) / dSq + o.y);
return t;
}

Related

circle collision causes circle to go supersonic

can anyone tell me why the circles i draw are getting stuck on each other or going super fast and flying off the window. all i want is for the circles to bounce off each other, but instead they get stuck 1/3 of the time, accelerate drastically another 1/3 of the time and only bounce the other 1/3. sorry if its messy, i've been trying a lot of different ideas and it kind of got out of hand. code uses maths that i got from gist.github/christopher4lis/f9ccb589ee8ecf751481f05a8e59b1dc. its horribly done but its the best i can do for now.
Please let me know if there is any other code that you need to help fix this.
public void bounce() {
collision = false;
for (int i = 0; i < size; i++) {
for (int a = 0; a < size; a++) {
float xVelDiff = dots[i][2] - dots[a][2];
float yVelDiff = dots[i][3] - dots[a][3];
float xDist = dots[i][0] - dots[a][0];
float yDist = dots[i][1] - dots[a][1];
if (xVelDiff * xDist + yVelDiff * yDist <= 0) {
angle = (float) -Math.atan2(dots[a][0] - dots[i][0], dots[a][1] - dots[i][1]);
float m1 = dots[i][7];
BigDecimal bd = new BigDecimal(m1).setScale(2, RoundingMode.HALF_UP);
m1 = bd.floatValue();
float m2 = dots[a][7];
float[] u1 = rotate(dots[i][2], dots[i][3], (float) angle);
float[] u2 = rotate(dots[a][2], dots[a][3], (float) angle);
float[] v1 = new float[2];
v1[0] = u1[0] * (m1 - m2) / (m1 + m2) + u2[0] * 2 * m2 / (m1 + m2);
v1[1] = u1[1];
float[] v2 = new float[2];
v2[0] = u2[0] * (m1 - m2) / (m1 + m2) + u1[0] * 2 * m2 / (m1 + m2);
v2[1] = u2[1];
float[] vFinal1 = rotate(v1[0], v1[1], (float) -angle);
float[] vFinal2 = rotate(v2[0], v2[1], (float) -angle);
if (a != i && !(dots[a][0] == 0 && dots[a][1] == 0)) {
boolean thisCollision = (dots[a][0] - dots[i][0]) * (dots[a][0] - dots[i][0]) + (dots[a][1] - dots[i][1]) * (dots[a][1] - dots[i][1]) <= (dots[a][4] + dots[i][4]) * (dots[a][4] + dots[i][4]);
// System.out.println("collision: "+collision+" i="+i+" a="+a);
if (thisCollision) {
System.out.println(vFinal2[0] + " " + vFinal2[1]);
collision = true;
dots[i][2] = vFinal1[0];
dots[i][3] = vFinal1[1];
dots[a][2] = vFinal2[0];
dots[a][3] = vFinal2[1];
return;
}
}
}
}
}
}
public float[] rotate(float velocityX, float velocityY, float angle) {
float x1 = (float) (velocityX * Math.cos(angle) - velocityY * Math.sin(angle));
float y1 = (float) (velocityX * Math.cos(angle) - velocityY * Math.sin(angle));
float vel[] = new float[2];
vel[0] = x1;
vel[1] = y1;
return vel;
}

Placement of Math.toRadians for solving quadratic and cubic equations

I am writing a program which solves either quadratic or cubic equations. The thing is that I don't know if I am placing the Math.toRadians correctly.
The code is the following:
public double[] getRaices(double a,double b, double c, double d) throws ComplexException {
if (a==0){
double discriminante=Math.pow(c,2)+((-4)*b*d);
if(discriminante>=0){
this.Raices[0]=(c*(-1)+Math.sqrt(discriminante))/(2*b);
this.Raices[1]=(c*(-1)-Math.sqrt(discriminante))/(2*b);
}else{
throw new ComplexException("No hay solucion Real");
}
} else{
double f=((3*c/a)-(Math.pow(b,2)/Math.pow(a,2)))/3;
double g=((2*Math.pow(b,3)/Math.pow(a,3))-(9*b*c/Math.pow(a,2))+(27*d/a))/27;
double h=(Math.pow(g,2)/4)+(Math.pow(f,3)/27);
if(f+g+h==0){
Raices [0]=Math.cbrt(d/a)*(-1);
Raices [1]=Math.cbrt(d/a)*(-1);
Raices [2]=Math.cbrt(d/a)*(-1);
}else{
if(h<=0){
double i=Math.sqrt((Math.pow(g,2)/4)-h);
double j=Math.cbrt(i);
double k=Math.acos(Math.toRadians(-1*(g/2*i)));
System.out.println(" "+k+" ");
double l=j*(0-1);
double m=Math.toRadians(Math.cos(Math.toRadians(k/3)));
System.out.println(" "+m+" ");
double n=Math.sqrt(3)*Math.sin(Math.toRadians(k/3));
System.out.println(" "+n+" ");
double p=(b/(3*a)*(0-1));
Raices [0]=2*j*Math.cos(Math.toRadians(k/3))-(b/(3*a));
Raices [1]=(l*(m+n))+p;
Raices [2]=(l*(m-n))+p;
}else{
double r=((0-1)*(g/2))+Math.sqrt(h);
double s=Math.cbrt(r);
double t=((0-1)*(g/2))-Math.sqrt(h);
double u=Math.cbrt(t);
throw new ComplexException("2 de las raices son imaginarias pero una raiz es real: "+Math.floor(Raices [0]=(s+u)-(b/(3*a))));
}
}
}
return Raices;
}
But the problem is in the if (h<=0).
I tested your code against the web page and found several errors.
First is g /2i, you wrote g/2*i instead of g/2/i or (g/(2*i). And several Math.toRadians not necessary (webpage said calculations is in radians, so no need to convert).
I added println to help following the formula :
package test;
public class Cubic {
private double[] Raices = new double[3];
public static void main(String[] args) throws ComplexException {
double[] raices = new Cubic().getRaices(2, -4, -22, 24);
System.out.println(raices[0] + "," + raices[1] + "," + raices[2]);
}
public double[] getRaices(double a, double b, double c, double d) throws ComplexException {
if (a == 0) {
double discriminante = Math.pow(c, 2) + ((-4) * b * d);
if (discriminante >= 0) {
this.Raices[0] = (c * (-1) + Math.sqrt(discriminante)) / (2 * b);
this.Raices[1] = (c * (-1) - Math.sqrt(discriminante)) / (2 * b);
} else {
throw new ComplexException("No hay solucion Real");
}
} else {
double f = ((3 * c / a) - (Math.pow(b, 2) / Math.pow(a, 2))) / 3;
System.out.println("f=" + f);
double g = ((2 * Math.pow(b, 3) / Math.pow(a, 3)) - (9 * b * c / Math.pow(a, 2)) + (27 * d / a)) / 27;
System.out.println("g=" + g);
double h = (Math.pow(g, 2) / 4) + (Math.pow(f, 3) / 27);
System.out.println("h=" + h);
if (f + g + h == 0) {
Raices[0] = Math.cbrt(d / a) * (-1);
Raices[1] = Math.cbrt(d / a) * (-1);
Raices[2] = Math.cbrt(d / a) * (-1);
} else {
if (h <= 0) {
double i = Math.sqrt((Math.pow(g, 2) / 4) - h);
double j = Math.cbrt(i);
double k = Math.acos(-1 * (g / 2 / i));
System.out.println("k=" + k + " ");
double l = j * (0 - 1);
System.out.println("l=" + l + " ");
double m = Math.cos(k / 3);
System.out.println("m= " + m + " ");
double n = Math.sqrt(3) * Math.sin(k / 3);
System.out.println("n= " + n + " ");
double p = (b / (3 * a) * (0 - 1));
System.out.println("p= " + p + " ");
Raices[0] = 2 * j * Math.cos(k / 3) - (b / (3 * a));
Raices[1] = (l * (m + n)) + p;
Raices[2] = (l * (m - n)) + p;
} else {
double r = ((0 - 1) * (g / 2)) + Math.sqrt(h);
double s = Math.cbrt(r);
double t = ((0 - 1) * (g / 2)) - Math.sqrt(h);
double u = Math.cbrt(t);
throw new ComplexException(
"2 de las raices son imaginarias pero una raiz es real: " + Math.floor(Raices[0] = (s + u) - (b / (3 * a))));
}
}
}
return Raices;
}
}

Android MapView direction arrows

I want to add direction arrows to my application to make it looks like this. Is there any solutions or advices?
Here is my solution:
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.jettaxi.R;
import java.util.ArrayList;
public class ArrowDirectionsOverlay extends Overlay {
private Context context;
private ArrayList<GeoPoint> directions;
private Bitmap arrowBitmap;
private int maximalPositionX;
private int maximalPositionY;
public ArrowDirectionsOverlay(Context context) {
this.context = context;
directions = new ArrayList<GeoPoint>();
arrowBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.direction);
}
public void addDirectionPoint(GeoPoint destination) {
directions.add(destination);
}
public void clear() {
directions.clear();
}
private double getAngle(GeoPoint center, GeoPoint destination) {
double lat1 = center.getLatitudeE6() / 1000000.;
double lon1 = center.getLongitudeE6() / 1000000.;
double lat2 = destination.getLatitudeE6() / 1000000.;
double lon2 = destination.getLongitudeE6() / 1000000.;
int MAXITERS = 20;
// Convert lat/long to radians
lat1 *= Math.PI / 180.0;
lat2 *= Math.PI / 180.0;
lon1 *= Math.PI / 180.0;
lon2 *= Math.PI / 180.0;
double a = 6378137.0; // WGS84 major axis
double b = 6356752.3142; // WGS84 semi-major axis
double f = (a - b) / a;
double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
double L = lon2 - lon1;
double A = 0.0;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
double cosU1 = Math.cos(U1);
double cosU2 = Math.cos(U2);
double sinU1 = Math.sin(U1);
double sinU2 = Math.sin(U2);
double cosU1cosU2 = cosU1 * cosU2;
double sinU1sinU2 = sinU1 * sinU2;
double sigma = 0.0;
double deltaSigma = 0.0;
double cosSqAlpha = 0.0;
double cos2SM = 0.0;
double cosSigma = 0.0;
double sinSigma = 0.0;
double cosLambda = 0.0;
double sinLambda = 0.0;
double lambda = L; // initial guess
for (int iter = 0; iter < MAXITERS; iter++) {
double lambdaOrig = lambda;
cosLambda = Math.cos(lambda);
sinLambda = Math.sin(lambda);
double t1 = cosU2 * sinLambda;
double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
double sinSqSigma = t1 * t1 + t2 * t2; // (14)
sinSigma = Math.sqrt(sinSqSigma);
cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
sigma = Math.atan2(sinSigma, cosSigma); // (16)
double sinAlpha = (sinSigma == 0) ? 0.0 :
cosU1cosU2 * sinLambda / sinSigma; // (17)
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SM = (cosSqAlpha == 0) ? 0.0 :
cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
A = 1 + (uSquared / 16384.0) * // (3)
(4096.0 + uSquared *
(-768 + uSquared * (320.0 - 175.0 * uSquared)));
double B = (uSquared / 1024.0) * // (4)
(256.0 + uSquared *
(-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
double C = (f / 16.0) *
cosSqAlpha *
(4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
double cos2SMSq = cos2SM * cos2SM;
deltaSigma = B * sinSigma * // (6)
(cos2SM + (B / 4.0) *
(cosSigma * (-1.0 + 2.0 * cos2SMSq) -
(B / 6.0) * cos2SM *
(-3.0 + 4.0 * sinSigma * sinSigma) *
(-3.0 + 4.0 * cos2SMSq)));
lambda = L +
(1.0 - C) * f * sinAlpha *
(sigma + C * sinSigma *
(cos2SM + C * cosSigma *
(-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
double delta = (lambda - lambdaOrig) / lambda;
if (Math.abs(delta) < 1.0e-12) {
break;
}
}
float distance = (float) (b * A * (sigma - deltaSigma));
float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
return finalBearing;
}
private void drawArrow(Canvas canvas, MapView mapView, GeoPoint destination) {
Point screenPts = mapView.getProjection().toPixels(destination, null);
int deltaX = mapView.getMapCenter().getLatitudeE6() - destination.getLatitudeE6();
int deltaY = mapView.getMapCenter().getLongitudeE6() - destination.getLongitudeE6();
double angle = getAngle(mapView.getMapCenter(), destination);
double tan = Math.tan(Math.toRadians(angle));
Matrix matrix = new Matrix();
matrix.postRotate((float) angle);
Bitmap rotatedBmp = Bitmap.createBitmap(
arrowBitmap,
0, 0,
arrowBitmap.getWidth(),
arrowBitmap.getHeight(),
matrix,
true
);
int currentPositionX = screenPts.x - (rotatedBmp.getWidth() / 2);
int currentPositionY = screenPts.y - (rotatedBmp.getHeight() / 2);
if ((currentPositionX < 0) || (currentPositionY < 0) ||
(currentPositionX > maximalPositionX) || (currentPositionY > maximalPositionY)) {
int arrowPositionX = (int) (mapView.getWidth() / 2 - Math.signum(deltaX) * mapView.getHeight() / 2 * tan);
arrowPositionX = Math.min(Math.max(arrowPositionX, 0), maximalPositionX);
int arrowSpanX = (int) (mapView.getWidth() / 2.);
int arrowPositionY = (int) (mapView.getHeight() / 2 + Math.signum(deltaY) * arrowSpanX / tan);
arrowPositionY = Math.min(Math.max(arrowPositionY, 0), maximalPositionY);
canvas.drawBitmap(
rotatedBmp,
arrowPositionX,
arrowPositionY,
null
);
}
}
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
maximalPositionX = mapView.getWidth() - arrowBitmap.getWidth();
maximalPositionY = mapView.getHeight() - arrowBitmap.getHeight();
for (GeoPoint geoPoint : directions) {
drawArrow(canvas, mapView, geoPoint);
}
}
}
Bitmap arrow points top in my case
My solution would be to do a search for the places of interest (in this case coffee shops) which are within a certain distance from the users current location. This distance could be a 'screen' north, south, east or west of your current location - the screen distance will depend on your zoom level. The shops which are not on the current screen would then be represented by an arrow placed on the screen edge with the direction set between your current location and the shop.

How to create a curve between 2 points in 2D and get back Points that makes that curve every d distance?

I'm not good in math.
I have 2 points, A(x1, y1) and B(x2, y2) in 2D.
I need to create a virtual path from point A to B curved at R(radius), and then return an array of points which are describing this curved path, not all maybe every D(distance) from each other.
In Java I need a method like this:
private ArrayList<PointF> generateCurve(PointF pFrom,PointF pTo,float pRadius,float pMinDistance){
ArrayList<PointF> pOutPut = new ArrayList<PointF>();
// ...generate result to pOutPut
return pOutPut;
}
How to do this ?
I didn't gave up and I've been working on it for a few more hours. And here is the result:
I created a method where you can specify if you want the shortest of the longest arc between the points.
Here are some calls to it, with the produced output:
generateCurve(pFrom, pTo, 100f, 7f, false, false);
generateCurve(pFrom, pTo, 100f, 7f, true, false);
generateCurve(pFrom, pTo, 100f, 7f, false, true);
generateCurve(pFrom, pTo, 100f, 7f, true, true);
As you can see, it is working like a charm. Here is the code:
package curve;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
/**
*
* #author martijn
*/
public class Main
{
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException
{
PointF pFrom = new PointF(-10f, 30.0f);
PointF pTo = new PointF(-100f, 0.0f);
List<PointF> points = generateCurve(pFrom, pTo, 100f, 7f, true, true);
System.out.println(points);
// Calculate the bounds of the curve
Rectangle2D.Float bounds = new Rectangle2D.Float(points.get(0).x, points.get(0).y, 0, 0);
for (int i = 1; i < points.size(); ++i) {
bounds.add(points.get(i).x, points.get(i).y);
}
bounds.add(pFrom.x, pFrom.y);
bounds.add(pTo.x, pTo.y);
BufferedImage img = new BufferedImage((int) (bounds.width - bounds.x + 50), (int) (bounds.height - bounds.y + 50), BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.translate(25.0f - bounds.getX(), 25.0f - bounds.getY());
g.setStroke(new BasicStroke(1.0f));
g.setColor(Color.DARK_GRAY);
g.drawLine(-1000, 0, 1000, 0);
g.drawLine(0, -1000, 0, 1000);
g.setColor(Color.RED);
for (int i = 0; i < points.size(); ++i) {
if (i > 0) {
Line2D.Float f = new Line2D.Float(points.get(i - 1).x, points.get(i - 1).y, points.get(i).x, points.get(i).y);
System.out.println("Dist : " + f.getP1().distance(f.getP2()));
// g.draw(f);
}
g.fill(new Ellipse2D.Float(points.get(i).x - 0.8f, points.get(i).y - 0.8f, 1.6f, 1.6f));
}
g.setColor(Color.BLUE);
g.fill(new Ellipse2D.Float(pFrom.x - 1, pFrom.y - 1, 3, 3));
g.fill(new Ellipse2D.Float(pTo.x - 1, pTo.y - 1, 3, 3));
g.dispose();
ImageIO.write(img, "PNG", new File("result.png"));
}
static class PointF
{
public float x, y;
public PointF(float x, float y)
{
this.x = x;
this.y = y;
}
#Override
public String toString()
{
return "(" + x + "," + y + ")";
}
}
private static List<PointF> generateCurve(PointF pFrom, PointF pTo, float pRadius, float pMinDistance, boolean shortest, boolean side)
{
List<PointF> pOutPut = new ArrayList<PointF>();
// Calculate the middle of the two given points.
PointF mPoint = new PointF(pFrom.x + pTo.x, pFrom.y + pTo.y);
mPoint.x /= 2.0f;
mPoint.y /= 2.0f;
System.out.println("Middle Between From and To = " + mPoint);
// Calculate the distance between the two points
float xDiff = pTo.x - pFrom.x;
float yDiff = pTo.y - pFrom.y;
float distance = (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
System.out.println("Distance between From and To = " + distance);
if (pRadius * 2.0f < distance) {
throw new IllegalArgumentException("The radius is too small! The given points wont fall on the circle.");
}
// Calculate the middle of the expected curve.
float factor = (float) Math.sqrt((pRadius * pRadius) / ((pTo.x - pFrom.x) * (pTo.x - pFrom.x) + (pTo.y - pFrom.y) * (pTo.y - pFrom.y)) - 0.25f);
PointF circleMiddlePoint = new PointF(0, 0);
if (side) {
circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) + factor * (pTo.y - pFrom.y);
circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) + factor * (pFrom.x - pTo.x);
} else {
circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) - factor * (pTo.y - pFrom.y);
circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) - factor * (pFrom.x - pTo.x);
}
System.out.println("Middle = " + circleMiddlePoint);
// Calculate the two reference angles
float angle1 = (float) Math.atan2(pFrom.y - circleMiddlePoint.y, pFrom.x - circleMiddlePoint.x);
float angle2 = (float) Math.atan2(pTo.y - circleMiddlePoint.y, pTo.x - circleMiddlePoint.x);
// Calculate the step.
float step = pMinDistance / pRadius;
System.out.println("Step = " + step);
// Swap them if needed
if (angle1 > angle2) {
float temp = angle1;
angle1 = angle2;
angle2 = temp;
}
boolean flipped = false;
if (!shortest) {
if (angle2 - angle1 < Math.PI) {
float temp = angle1;
angle1 = angle2;
angle2 = temp;
angle2 += Math.PI * 2.0f;
flipped = true;
}
}
for (float f = angle1; f < angle2; f += step) {
PointF p = new PointF((float) Math.cos(f) * pRadius + circleMiddlePoint.x, (float) Math.sin(f) * pRadius + circleMiddlePoint.y);
pOutPut.add(p);
}
if (flipped ^ side) {
pOutPut.add(pFrom);
} else {
pOutPut.add(pTo);
}
return pOutPut;
}
}
Enjoy!
PS: I created two questions on Mathematics to solve your question:
Analytic Geometry: Point coordinates, same distance from two points.
Trigonometry: Solve (1−cosα)2+sin2α=d2 for α
This works:
private static double GetAngle(Point2D x, Point2D o, double R){
double cosa = (x.getX()-o.getX())/R;
double sina = (x.getY()-o.getY())/R;
double angle = Math.acos(cosa);
return Math.sin(angle)*sina >= 0 ? angle : 2*Math.PI - angle;
}
private static ArrayList<Point2D> generateCurve(Point2D pFrom,Point2D pTo,float pRadius,float pMinDistance){
ArrayList<Point2D> pOutPut = new ArrayList<Point2D>();
double dist = pFrom.distance(pTo);
double h = Math.sqrt(pRadius * pRadius - (dist * dist / 4.0));
double angleStep = pMinDistance/pRadius;
if(2*pRadius <= dist)
throw new Error("Radius is too small");
//find center
double x1 = pFrom.getX(), x2 = pFrom.getY();
double y1 = pTo.getX(), y2 = pTo.getY();
double m1 = (x1+y1)/2, m2 = (x2+y2)/2;
double u1 = - (y2-x2)/dist, u2 = (y1-x1)/dist;
double o1 = m1 + h * u1, o2 = m2 + h * u2;
Point2D o = new Point2D.Double(o1, o2);
double startAngle = GetAngle(pFrom, o, pRadius);
double endAngle = GetAngle(pTo, o, pRadius);
if(endAngle < startAngle)
endAngle += 2 * Math.PI;
for(double a = startAngle; a < endAngle; a+=angleStep){
pOutPut.add(new Point2D.Double(o1+pRadius*Math.cos(a), o2+pRadius*Math.sin(a)));
}
pOutPut.add(pTo);
return pOutPut;
}
Here is what I get when I call it like this: generateCurve(new Point2D.Double(10,10), new Point2D.Double(400, 400), 300, 15)

Application not working as expected

I have a standalone Java application below that is:
Generating a random line
Applied to a 2D grid where each cell value is the distance along the line perpindicular to the line
Finds the rise/run and attempts to calculate the original linear equation from the grid
Applies new line to another grid and prints out the greatest difference compared to the first grid
I expected the two grids to have identical values. The gradient lines may be different since the lines can extend outside the area of the grid, but should be similar and in two cases identical.
So is the problem a poor understanding of math, a bug in my code or a misunderstanding of floating point values?
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height - 1);
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
int endX = (int)Math.round(radius * Math.sin(theta));
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height - 1);
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a + b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a + b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE - 1, SIZE - 1, 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
I think I have fixed your program.
a) I took out the integer cast.
b) I removed all the 'x + 1' and 'x - 1' fudges you had used.
I think when dealing with floats and doubles, subtracting '1' from the end of a line is a No-No! What is 1 anyway? - it's ok to do this just before you plot it on the screen once it's an integer. But not while calculating! line length is a 'zero-based' quantity.
This version returns approx 4E-16 always.
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.awt.geom.QuadCurve2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height );
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
float endX = (float)(radius * Math.sin(theta));
float endY = (float)(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height );
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a+b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a+b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE, SIZE , 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
why do you multiply by -1 at the end of this line?
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
this means that endY is always negative except radius is below 0. (cosinus always returns positive value)
is this intended or am i getting something wrong?
regards
You probably misunderstand float and/or double. This is a common problem with any language that implements the ieee spec for floats and doubles, which Java, C, C++ and just about every other language does.
Essentially
double val = 0;
for(int i=0;i<10;i++) {
val+=0.1;
System.out.println(val);
}
results in
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
And sometimes even worse. Either use BigDecimal, which alleviates a lot of the problem, or use integers.

Categories

Resources