I am almost done of getting the coordinates of a diagonal circle.
Here is what I have so far.
// Center point
double centerX;
double centerY;
double centerZ;
for (double degree = 0D; degree < 360D; degree = degree + 8D) {
double angle = degree * Math.PI / 180D;
// Difference from the center
double x = 1.5D * Math.cos(angle);
double y;
if (degree >= 0D && degree < 90D) {
y = degree / 90D;
} else if (degree >= 90D && degree < 180D) {
y = 1D - ((degree - 90D) / 90D);
} else if (degree >= 180D && degree < 270D) {
y = -1D * ((degree - 180D) / 90D);
} else {
y = -1D * (1D - ((degree - 270D) / 90D));
}
double z = 1.5D * Math.sin(angle);
// New point
double pointX = centerX + x;
double pointY = centerY + y;
double pointZ = centerZ + z;
}
Here is the output in a game.
It is not perfect because it creates some edges and it looks inefficient to me.
How do I correct it?
Is there a better way to do this?
This should look similar to what you have already, but it's simpler and smoother:
double y = 1.0D * Math.sin(angle);
Now, with these dimensions, the result is not quite a circle, but a stretched ellipse. If you want a circle, make sure the coefficients on the cosine and sine obey the Pythagorean Theorem. For example:
double x = 1.5D * Math.cos(angle);
double y = 0.9D * Math.sin(angle);
double z = 1.2D * Math.sin(angle);
These coefficients will ensure that x^2 + y^2 + z^2 is a constant for every angle. You can verify that this is true, given the identity cos^2 + sin^2 = 1. (The coefficient representing the hypotenuse should be attached to the coordinate that uses a different trig function than the other two.)
For the most maintainable code, you might find it better to assign (x, y, z) = (cos, sin, 0) and then apply a rotation matrix, or a sequence of rotation matrices, to the vector (x, y, z). This will be easier to read and harder to mess up, if you want to fine-tune the amount of rotation later.
Related
I'm trying to generate a point within a radius and I'm getting incorrect values. Someone mind taking a look and telling me what I'm doing wrong for the longitude? This was a formulaic approach posted on a different question...
public static Location generateLocationWithinRadius(Location myCurrentLocation) {
return getLocationInLatLngRad(1000, myCurrentLocation);
}
protected static Location getLocationInLatLngRad(double radiusInMeters, Location currentLocation) {
double x0 = currentLocation.getLatitude();
double y0 = currentLocation.getLongitude();
Random random = new Random();
// Convert radius from meters to degrees
double radiusInDegrees = radiusInMeters / 111000f;
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
double x = w * Math.cos(t);
double y = w * Math.sin(t);
double new_x = x / Math.cos(y0);
double new_y = y / Math.cos(x0);
double foundLatitude;
double foundLongitude;
boolean shouldAddOrSubtractLat = random.nextBoolean();
boolean shouldAddOrSubtractLon = random.nextBoolean();
if (shouldAddOrSubtractLat) {
foundLatitude = new_x + x0;
} else {
foundLatitude = x0 - new_x;
}
if (shouldAddOrSubtractLon) {
foundLongitude = new_y + y0;
} else {
foundLongitude = y0 - new_y;
}
Location copy = new Location(currentLocation);
copy.setLatitude(foundLatitude);
copy.setLongitude(foundLongitude);
return copy;
}
I should also say that for some reason the valid points yield a uniform line of coordinates when looking at them.
I think the latitude is processing correctly whereas the longitude is not.
Your code seems to be more or less based on an idea
which is presented at gis.stackexchange.com
and discussed some more there in this discussion
and in this discussion.
If we take a closer look at it based on those discussions then maybe it makes more sense.
To easily limit the values to a circle it uses the approach of randomizing a direction and a distance. First we get two random double values between 0.0 ... 1.0:
double u = random.nextDouble();
double v = random.nextDouble();
As the radius is given in meters and the calculations require degrees, it's converted:
double radiusInDegrees = radiusInMeters / 111000f;
The degrees vs. meters ratio of the equator is used here. (Wikipedia suggests 111320 m.)
To have a more uniform distribution of the random points the distance is compensated with a square root:
w = r * sqrt(u)
Otherwise there would be a statistical bias in the amount of points near the center vs. far from the center. The square root of 1 is 1 and 0 of course 0, so
multiplying the root of the random double by the intended max. radius always gives a value between 0 and the radius.
Then the other random double is multiplied by 2 * pi because there are 2 * pi radians in a full circle:
t = 2 * Pi * v
We now have an angle somewhere between 0 ... 2 * pi i.e. 0 ... 360 degrees.
Then the random x and y coordinate deltas are calculated with basic trigonometry using the random distance and random angle:
x = w * cos(t)
y = w * sin(t)
The [x,y] then points some random distance w away from the original coordinates towards the direction t.
Then the varying distance between longitude lines is compensated with trigonometry (y0 being the center's y coordinate):
x' = x / cos(y0)
Above y0 needs to be converted to radians if the cos() expects the angle as radians. In Java it does.
It's then suggested that these delta values are added to the original coordinates. The cos and sin are negative for half of the full circle's angles so just adding is fine. Some of the random points will be to the west from Greenwich and and south from the equator. There's no need to randomize
should an addition or subtraction be done.
So the random point would be at (x'+x0, y+y0).
I don't know why your code has:
double new_y = y / Math.cos(x0);
And like said we can ignore shouldAddOrSubtractLat and shouldAddOrSubtractLon.
In my mind x refers to something going from left to right or from west to east. That's how the longitude values grow even though the longitude lines go from south to north. So let's use x as longitude and y as latitude.
So what's left then? Something like:
protected static Location getLocationInLatLngRad(double radiusInMeters, Location currentLocation) {
double x0 = currentLocation.getLongitude();
double y0 = currentLocation.getLatitude();
Random random = new Random();
// Convert radius from meters to degrees.
double radiusInDegrees = radiusInMeters / 111320f;
// Get a random distance and a random angle.
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
// Get the x and y delta values.
double x = w * Math.cos(t);
double y = w * Math.sin(t);
// Compensate the x value.
double new_x = x / Math.cos(Math.toRadians(y0));
double foundLatitude;
double foundLongitude;
foundLatitude = y0 + y;
foundLongitude = x0 + new_x;
Location copy = new Location(currentLocation);
copy.setLatitude(foundLatitude);
copy.setLongitude(foundLongitude);
return copy;
}
It is hard for me to provide you with a pure Android solution as I never used those API. However I am sure you could easily adapt this solution to generate a random point within a given radius from an existing point.
The problem is solved in a two dimensions space however it is easy to extend to support altitude as well.
Please have a look at the code below. It provides you with a LocationGeneratoras well as my own Location implementation and an unit test proving that it works.
My solution is based on solving the circle equation (x-a)^2 + (y-b)^2 = r^2
package my.test.pkg;
import org.junit.Test;
import java.util.Random;
import static org.junit.Assert.assertTrue;
public class LocationGeneratorTest {
private class Location {
double longitude;
double latitude;
public Location(double longitude, double latitude) {
this.longitude = longitude;
this.latitude = latitude;
}
}
private class LocationGenerator {
private final Random random = new Random();
Location generateLocationWithinRadius(Location currentLocation, double radius) {
double a = currentLocation.longitude;
double b = currentLocation.latitude;
double r = radius;
// x must be in (a-r, a + r) range
double xMin = a - r;
double xMax = a + r;
double xRange = xMax - xMin;
// get a random x within the range
double x = xMin + random.nextDouble() * xRange;
// circle equation is (y-b)^2 + (x-a)^2 = r^2
// based on the above work out the range for y
double yDelta = Math.sqrt(Math.pow(r, 2) - Math.pow((x - a), 2));
double yMax = b + yDelta;
double yMin = b - yDelta;
double yRange = yMax - yMin;
// Get a random y within its range
double y = yMin + random.nextDouble() * yRange;
// And finally return the location
return new Location(x, y);
}
}
#Test
public void shoulRandomlyGeneratePointWithinRadius () throws Exception {
LocationGenerator locationGenerator = new LocationGenerator();
Location currentLocation = new Location(20., 10.);
double radius = 5.;
for (int i=0; i < 1000000; i++) {
Location randomLocation = locationGenerator.generateLocationWithinRadius(currentLocation, radius);
try {
assertTrue(Math.pow(randomLocation.latitude - currentLocation.latitude, 2) + Math.pow(randomLocation.longitude - currentLocation.longitude, 2) < Math.pow(radius, 2));
} catch (Throwable e) {
System.out.println("i= " + i + ", x=" + randomLocation.longitude + ", y=" + randomLocation.latitude);
throw new Exception(e);
}
}
}
}
NOTE:
This is just a generic solution to obtain a random point inside a circle with the center in (a, b) and a radius of r that can be used to solve your problem and not a straight solution that you can use as such. You most likely will need to adapt it to your use case.
I believe this is a natural solution.
Regards
Kotlin version of Markus Kauppinen answer
fun Location.getRandomLocation(radius: Double): Location {
val x0: Double = longitude
val y0: Double = latitude
// Convert radius from meters to degrees.
// Convert radius from meters to degrees.
val radiusInDegrees: Double = radius / 111320f
// Get a random distance and a random angle.
// Get a random distance and a random angle.
val u = Random.nextDouble()
val v = Random.nextDouble()
val w = radiusInDegrees * sqrt(u)
val t = 2 * Math.PI * v
// Get the x and y delta values.
// Get the x and y delta values.
val x = w * cos(t)
val y = w * sin(t)
// Compensate the x value.
// Compensate the x value.
val newX = x / cos(Math.toRadians(y0))
val foundLatitude: Double
val foundLongitude: Double
foundLatitude = y0 + y
foundLongitude = x0 + newX
val copy = Location(this)
copy.latitude = foundLatitude
copy.longitude = foundLongitude
return copy
}
Longitude and Latitude uses ellipsoidal coordinates so for big radius (hundred meters) the error using this method would become sinificant. A possible trick is to convert to Cartesian coordinates, do the radius randomization and then transform back again to ellipsoidal coordinates for the long-lat. I have tested this up to a couple of kilometers with great success using this java library from ibm. Longer than that might work, but eventually the radius would fall off as the earth shows its spherical nature.
I wanted to find all points of a circle with angle of 30 degree and radius r, when i am given center of circle (x,y) till we scan entire 360 degree.
Please let me know if i can use any java library related to this.
Code below :
I have written code as below :
<code>
double xpos = xPosition;
double ypos = yPosition;
double step = 2 * Math.PI / 20;
double radius = 50;
List<DevicePositions> listdevicePos = new ArrayList<DevicePositions>();
DevicePositions devicePos = null;
for (double theta = 0; theta < 2 * Math.PI; theta += step) {
double x = xpos + radius * Math.cos(theta);
double y = ypos - radius * Math.sin(theta);
devicePos = new DevicePositions();
devicePos.setxCoordinate(x);
devicePos.setyCoordinate(y);
listdevicePos.add(devicePos);
}
</code>
Thanks
Brijesh
I already know how to find a point on the circumference of a circle based on an angle. The code I'm using to do so is below.
x = Math.sin(Math.toRadians(angle)) * radius;
y = Math.cos(Math.toRadians(angle)) * radius;
I'm trying to undo this process.
So far, I have this code, which only works fully for angles less than or equal to 90 degrees.
DecimalFormat df = new DecimalFormat("###.####");
angleFromX = normalize(
Double.parseDouble(
df.format(
Math.toDegrees(
Math.asin(
(x / radius)
)
)
)
)
);
angleFromY = normalize(
Double.parseDouble(
df.format(
Math.toDegrees(
Math.acos(
(y / radius)
)
)
)
)
);
And here's normalize method used above.
public static double normalize(double angle) {
angle %= 360;
if (angle < 0) {
angle = angle + 360;
}
return angle;
}
You mixed up sin and cos.
double x = Math.cos(Math.toRadians(angle)) * radius;
double y = Math.sin(Math.toRadians(angle)) * radius;
To convert back, use this formula:
double newRadius = Math.hypot(x, y);
double theta = Math.atan2(y,x);
double newAngle = Math.toDegrees(theta);
Based on the implementation, you may need to adjust your value of theta (angle).
If it's in Quadrant 2 or 3, add 180 degrees.
If it's in Quadrant 4, add 360 degrees.
Also you may need to add:
newAngle = (newAngle+360)%360
To keep the angle positive and between 0 and 360.
im trying to make the player look at at specific point. What i have looks at the point but only if its in its field of vision. and i would like for it to look up and down as well
public static void lookAt(double x, double y, double z) {
double l;
double w;
double c;
l = x - playerX;
w = z - playerZ;
c = Math.sqrt(l * l + w + w);
double alpha1 = -Math.asin(l / c) / Math.PI * 180;
double alpha2 = Math.acos(w / c) / Math.PI * 180;
if (alpha2 > 180) {
playerYaw = 180 - (float) alpha1;
} else {
playerYaw = (float) alpha1;
}
}
i looked for a pitch algorithm but none seem to work.
Late answer but oh well, you can calculate the pitch and yaw with simple trigonometry if you can get a unit vector that represents the direction from your position to the point you want to look at.
To start you need to subtract the your position by the point and normalize the result, that will give us the directional vector(unit vector) that we need.
Now we can calculate the angles, the arc sine of Y will give us the the pitch angle, and the arc tan of X and Z will give us the yaw angle.
To understand why: https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
and now you should have the pitch and yaw, don't forget to convert them to degrees!
here's some code:
public static void LookAt(double px, double py, double pz , EntityPlayer me)
{
double dirx = me.getPosition(0f).X - px;
double diry = me.getPosition(0f).Y - py;
double dirz = me.getPosition(0f).Z - pz;
double len = Math.sqrt(dirx*dirx + diry*diry + dirz*dirz);
dirx /= len
diry /= len;
dirz /= len;
double pitch = Math.asin(dir.y);
double yaw = Math.atan2(dir.z, dir.x);
//to degree
pitch = pitch * 180.0 / Math.PI;
yaw = yaw * 180.0 / Math.PI;
yaw += 90f;
me.rotationPitch = (float)pitch;
me.rotationYaw = (float)yaw;
}
I have a large bulk of photographs taken with a fisheye lens. As I want to do some image-processing (e.g. edge detection) on the photos I want to remove the barrel distortion which effects my results heavily.
After some research and lots of read articles I found this page: They describe an algorithm (and some formulas) to solve this problem.
M = a *rcorr^3 + b * rcorr^2 + c * rcorr + d
rsrc = (a * rcorr^3 + b * rcorr^2 + c * rcorr + d) * rcorr
rsrc = distance of a pixel from the center of the source image
rcorr = distance of a pixel from the center in the corrected image
a,b,c = distortion of image
d = linear scaling of image
I used these formulas and tried to implement this in a Java application. Unfortunately it doesn't work and I failed to make it work. "Corrected" image look nothing like the original photograph and instead show some mysterious circles in the middle. Look here:
http://imageshack.us/f/844/barreldistortioncorrect.jpg/
(this used to be a photograph of a white cow in front a blue wall)
Here is my code:
protected int[] correction(int[] pixels) {
//
int[] pixelsCopy = pixels.clone();
// parameters for correction
double paramA = 0.0; // affects only the outermost pixels of the image
double paramB = -0.02; // most cases only require b optimization
double paramC = 0.0; // most uniform correction
double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image
//
for(int x = 0; x < dstView.getImgWidth(); x++) {
for(int y = 0; y < dstView.getImgHeight(); y++) {
int dstX = x;
int dstY = y;
// center of dst image
double centerX = (dstView.getImgWidth() - 1) / 2.0;
double centerY = (dstView.getImgHeight() - 1) / 2.0;
// difference between center and point
double diffX = centerX - dstX;
double diffY = centerY - dstY;
// distance or radius of dst image
double dstR = Math.sqrt(diffX * diffX + diffY * diffY);
// distance or radius of src image (with formula)
double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR;
// comparing old and new distance to get factor
double factor = Math.abs(dstR / srcR);
// coordinates in source image
double srcXd = centerX + (diffX * factor);
double srcYd = centerY + (diffX * factor);
// no interpolation yet (just nearest point)
int srcX = (int)srcXd;
int srcY = (int)srcYd;
if(srcX >= 0 && srcY >= 0 && srcX < dstView.getImgWidth() && srcY < dstView.getImgHeight()) {
int dstPos = dstY * dstView.getImgWidth() + dstX;
pixels[dstPos] = pixelsCopy[srcY * dstView.getImgWidth() + srcX];
}
}
}
return pixels;
}
My questions are:
1) Is this formula correct?
2) Do I have made a mistake turning that formula into a piece of software?
3) There are other algorithms out there (e.g. How to simulate fisheye lens effect by openCV? or wiki/Distortion_(optics)), are they better?
Thanks for your help!
The main bug you have is that the algorithm specifies that r_corr and r_src are in units of min((xDim-1)/2, (yDim-1)/2). That needs to be done to normalise the calculation so that the parameter values are not dependent on the size of the source image. With the code as it is you'll need to use much smaller values for paramB, e.g. it worked ok for me with paramB = 0.00000002 (for an image with dimensions 2272 x 1704).
You also have a bug in calculating the difference from the center that causes the resulting image to be rotated 180 degree compared to the source image.
Fixing both these bugs should give you something like this:
protected static int[] correction2(int[] pixels, int width, int height) {
int[] pixelsCopy = pixels.clone();
// parameters for correction
double paramA = -0.007715; // affects only the outermost pixels of the image
double paramB = 0.026731; // most cases only require b optimization
double paramC = 0.0; // most uniform correction
double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int d = Math.min(width, height) / 2; // radius of the circle
// center of dst image
double centerX = (width - 1) / 2.0;
double centerY = (height - 1) / 2.0;
// cartesian coordinates of the destination point (relative to the centre of the image)
double deltaX = (x - centerX) / d;
double deltaY = (y - centerY) / d;
// distance or radius of dst image
double dstR = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// distance or radius of src image (with formula)
double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR;
// comparing old and new distance to get factor
double factor = Math.abs(dstR / srcR);
// coordinates in source image
double srcXd = centerX + (deltaX * factor * d);
double srcYd = centerY + (deltaY * factor * d);
// no interpolation yet (just nearest point)
int srcX = (int) srcXd;
int srcY = (int) srcYd;
if (srcX >= 0 && srcY >= 0 && srcX < width && srcY < height) {
int dstPos = y * width + x;
pixels[dstPos] = pixelsCopy[srcY * width + srcX];
}
}
}
return pixels;
}
With this version you can use parameter values from existing lens databases like LensFun (though you'll need to flip the sign of each parameter). The page describing the algorithm can now be found at http://mipav.cit.nih.gov/pubwiki/index.php/Barrel_Distortion_Correction
I think your circles are caused by this line:
double srcYd = centerY + (diffX * factor);
which I'm guessing should be:
double srcYd = centerY + (diffY * factor);
Probably your radial distortion parameters are too large, and the image became packed on a sphere. Try to put smaller values in a,b,c and d.
Your values are very extreme, so you see extreme results.
Try a=0, b=0, c=1. That describes no correction at all, if your program is correct you should see the original image. Then gradually change c and b. Changing in increments of 0.1 is a good start.