Calculating area enclosed by arbitrary polygon on Earth's surface - math

Say I have an arbitrary set of latitude and longitude pairs representing points on some simple, closed curve. In Cartesian space I could easily calculate the area enclosed by such a curve using Green's Theorem. What is the analogous approach to calculating the area on the surface of a sphere? I guess what I am after is (even some approximation of) the algorithm behind Matlab's areaint function.

There several ways to do this.
1) Integrate the contributions from latitudinal strips. Here the area of each strip will be (Rcos(A)(B1-B0))(RdA), where A is the latitude, B1 and B0 are the starting and ending longitudes, and all angles are in radians.
2) Break the surface into spherical triangles, and calculate the area using Girard's Theorem, and add these up.
3) As suggested here by James Schek, in GIS work they use an area preserving projection onto a flat space and calculate the area in there.
From the description of your data, in sounds like the first method might be the easiest. (Of course, there may be other easier methods I don't know of.)
Edit – comparing these two methods:
On first inspection, it may seem that the spherical triangle approach is easiest, but, in general, this is not the case. The problem is that one not only needs to break the region up into triangles, but into spherical triangles, that is, triangles whose sides are great circle arcs. For example, latitudinal boundaries don't qualify, so these boundaries need to be broken up into edges that better approximate great circle arcs. And this becomes more difficult to do for arbitrary edges where the great circles require specific combinations of spherical angles. Consider, for example, how one would break up a middle band around a sphere, say all the area between lat 0 and 45deg into spherical triangles.
In the end, if one is to do this properly with similar errors for each method, method 2 will give fewer triangles, but they will be harder to determine. Method 1 gives more strips, but they are trivial to determine. Therefore, I suggest method 1 as the better approach.

I rewrote the MATLAB's "areaint" function in java, which has exactly the same result.
"areaint" calculates the "suface per unit", so I multiplied the answer by Earth's Surface Area (5.10072e14 sq m).
private double area(ArrayList<Double> lats,ArrayList<Double> lons)
{
double sum=0;
double prevcolat=0;
double prevaz=0;
double colat0=0;
double az0=0;
for (int i=0;i<lats.size();i++)
{
double colat=2*Math.atan2(Math.sqrt(Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)+ Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)),Math.sqrt(1- Math.pow(Math.sin(lats.get(i)*Math.PI/180/2), 2)- Math.cos(lats.get(i)*Math.PI/180)*Math.pow(Math.sin(lons.get(i)*Math.PI/180/2), 2)));
double az=0;
if (lats.get(i)>=90)
{
az=0;
}
else if (lats.get(i)<=-90)
{
az=Math.PI;
}
else
{
az=Math.atan2(Math.cos(lats.get(i)*Math.PI/180) * Math.sin(lons.get(i)*Math.PI/180),Math.sin(lats.get(i)*Math.PI/180))% (2*Math.PI);
}
if(i==0)
{
colat0=colat;
az0=az;
}
if(i>0 && i<lats.size())
{
sum=sum+(1-Math.cos(prevcolat + (colat-prevcolat)/2))*Math.PI*((Math.abs(az-prevaz)/Math.PI)-2*Math.ceil(((Math.abs(az-prevaz)/Math.PI)-1)/2))* Math.signum(az-prevaz);
}
prevcolat=colat;
prevaz=az;
}
sum=sum+(1-Math.cos(prevcolat + (colat0-prevcolat)/2))*(az0-prevaz);
return 5.10072E14* Math.min(Math.abs(sum)/4/Math.PI,1-Math.abs(sum)/4/Math.PI);
}

You mention "geography" in one of your tags so I can only assume you are after the area of a polygon on the surface of a geoid. Normally, this is done using a projected coordinate system rather than a geographic coordinate system (i.e. lon/lat). If you were to do it in lon/lat, then I would assume the unit-of-measure returned would be percent of sphere surface.
If you want to do this with a more "GIS" flavor, then you need to select an unit-of-measure for your area and find an appropriate projection that preserves area (not all do). Since you are talking about calculating an arbitrary polygon, I would use something like a Lambert Azimuthal Equal Area projection. Set the origin/center of the projection to be the center of your polygon, project the polygon to the new coordinate system, then calculate the area using standard planar techniques.
If you needed to do many polygons in a geographic area, there are likely other projections that will work (or will be close enough). UTM, for example, is an excellent approximation if all of your polygons are clustered around a single meridian.
I am not sure if any of this has anything to do with how Matlab's areaint function works.

I don't know anything about Matlab's function, but here we go. Consider splitting your spherical polygon into spherical triangles, say by drawing diagonals from a vertex. The surface area of a spherical triangle is given by
R^2 * ( A + B + C - \pi)
where R is the radius of the sphere, and A, B, and C are the interior angles of the triangle (in radians). The quantity in the parentheses is known as the "spherical excess".
Your n-sided polygon will be split into n-2 triangles. Summing over all the triangles, extracting the common factor of R^2, and bringing all of the \pi together, the area of your polygon is
R^2 * ( S - (n-2)\pi )
where S is the angle sum of your polygon. The quantity in parentheses is again the spherical excess of the polygon.
[edit] This is true whether or not the polygon is convex. All that matters is that it can be dissected into triangles.
You can determine the angles from a bit of vector math. Suppose you have three vertices A,B,C and are interested in the angle at B. We must therefore find two tangent vectors (their magnitudes are irrelevant) to the sphere from point B along the great circle segments (the polygon edges). Let's work it out for BA. The great circle lies in the plane defined by OA and OB, where O is the center of the sphere, so it should be perpendicular to the normal vector OA x OB. It should also be perpendicular to OB since it's tangent there. Such a vector is therefore given by OB x (OA x OB). You can use the right-hand rule to verify that this is in the appropriate direction. Note also that this simplifies to OA * (OB.OB) - OB * (OB.OA) = OA * |OB| - OB * (OB.OA).
You can then use the good ol' dot product to find the angle between sides: BA'.BC' = |BA'|*|BC'|*cos(B), where BA' and BC' are the tangent vectors from B along sides to A and C.
[edited to be clear that these are tangent vectors, not literal between the points]

Here is a Python 3 implementation, loosely inspired by the above answers:
def polygon_area(lats, lons, algorithm = 0, radius = 6378137):
"""
Computes area of spherical polygon, assuming spherical Earth.
Returns result in ratio of the sphere's area if the radius is specified.
Otherwise, in the units of provided radius.
lats and lons are in degrees.
"""
from numpy import arctan2, cos, sin, sqrt, pi, power, append, diff, deg2rad
lats = np.deg2rad(lats)
lons = np.deg2rad(lons)
# Line integral based on Green's Theorem, assumes spherical Earth
#close polygon
if lats[0]!=lats[-1]:
lats = append(lats, lats[0])
lons = append(lons, lons[0])
#colatitudes relative to (0,0)
a = sin(lats/2)**2 + cos(lats)* sin(lons/2)**2
colat = 2*arctan2( sqrt(a), sqrt(1-a) )
#azimuths relative to (0,0)
az = arctan2(cos(lats) * sin(lons), sin(lats)) % (2*pi)
# Calculate diffs
# daz = diff(az) % (2*pi)
daz = diff(az)
daz = (daz + pi) % (2 * pi) - pi
deltas=diff(colat)/2
colat=colat[0:-1]+deltas
# Perform integral
integrands = (1-cos(colat)) * daz
# Integrate
area = abs(sum(integrands))/(4*pi)
area = min(area,1-area)
if radius is not None: #return in units of radius
return area * 4*pi*radius**2
else: #return in ratio of sphere total area
return area
Please find a somewhat more explicit version (and with many more references and TODOs...) here.

You could also have a look at this code of the spherical_geometry package: Here and here. It does provide two different methods for calculating the area of a spherical polygon.

Related

Find all 4 possible normals to an ellipse

Given a point p exterior to an axially aligned, origin centered ellipse E, find the (upto) four unique normals to E passing through p.
This is not a Mathematica question. Direct computation is too slow; I am willing to sacrifice precision and accuracy for speed.
I have searched the web, but all I found involved overly complex calculations which if implemented directly appear to lack the performance I need. Is there a more "programmatical" way to do this, like using matrices or scaling the ellipse into a circle?
Let's assume the ellipse E is in "standard position", center at the origin and axes parallel to the coordinate axes:
(x/a)^2 + (y/b)^2 = 1 where a > b > 0
The boundary cases a=b are circles, where the normal lines are simply ones that pass through the center (origin) and are thus easy to find. So we omit discussion of these cases.
The slope of the tangent to the ellipse at any point (x,y) may be found by implicit differentiation:
dy/dx = -(b^2 x)/(a^2 y)
For the line passing through (x,y) and a specified point p = (u,v) not on the ellipse, that is normal to ellipse E when its slope is the negative reciprocal of dy/dx:
(y-v)/(x-u) * (-b^2 x)/(a^2 y) = -1 (N)
which simplifies to:
(x - (1+g)u) * (y + gv) = -g(1+g)uv where g = b^2/(a^2 - b^2)
In this form we recognize it is the equation for a right rectangular hyperbola. Depending on how many points of intersection there are between the ellipse and the hyperbola (2,3,4), we have that many normals to E passing through p.
By reflected symmetry, if p is assumed exterior to E, we may take p to be in the first quadrant:
(u/a)^2 + (v/b)^2 > 1 (exterior to E)
u,v > 0 (1'st quadrant)
We could have boundary cases where u=0 or v=0, i.e. point p lies on an axis of E, but these cases may be reduced to solving a quadratic, because two normals are the (coinciding) lines through the endpoints of that axis. We defer further discussion of these special cases for the moment.
Here's an illustration with a=u=5,b=v=3 in which only one branch of the hyperbola intersects E, and there will be only two normals:
If the system of two equations in two unknowns (x,y) is reduced to one equation in one unknown, the simplest root-finding method to code is a bisection method, but knowing something about the possible locations of roots/intersections will expedite our search. The intersection in the first quadrant is the nearest point of E to p, and likewise the intersection in the third quadrant is the farthest point of E from p. If the point p were a good bit closer to the upper endpoint of the minor axis, the branches of the hyperbola would shift together enough to create up to two more points of intersection in the fourth quadrant.
One approach would be to parameterize E by points of intersection with the x-axis. The lines from p normal to the ellipse must intersect the major axis which is a finite interval [-a,+a]. We can test both the upper and lower points of intersection q=(x,y) of a line passing through p=(u,v) and (z,0) as z sweeps from -a to +a, looking for places where the ellipse and hyperbola intersect.
In more detail:
1. Find the upper and lower points `q` of intersection of E with the
line through `p` and `(z,0)` (amounts to solving a quadratic)
3. Check the sign of a^2 y(x-u) - b^2 x(y-v) at `q=(x,y)`, because it
is zero if and only `q` is a point of normal intersection
Once a subinterval is detected (either for upper or lower portion) where the sign changes, it can be refined to get the desired accuracy. If only modest accuracy is needed, there may be no need to use faster root finding methods, but even if they are needed, having a short subinterval that isolates a root (or root pair in the fourth quadrant) will be useful.
** more to come comparing convergence of various methods **
I had to solve a problem similar to this, for GPS initialization. The question is: what is the latitude of a point interior to the Earth, especially near the center, and is it single-valued? There are lots of methods for converting ECEF cartesian coordinates to geodetic latitude, longitude and altitude (look up "ECEF to Geodetic"). We use a fast one with only one divide and sqrt per iteration, instead of several trig evaluations like most methods, but since I can't find it in the wild, I can't give it to you here. I would start with Lin and Wang's method, since it only uses divisions in its iterations. Here is a plot of the ellipsoid surface normals to points within 100 km of Earth's center (North is up in the diagram, which is really ECEF Z, not Y):
The star-shaped "caustic" in the figure center traces the center of curvature of the WGS-84 ellipsoid as latitude is varied from pole to equator. Note that the center of curvature at the poles is on the opposite side of the equator, due to polar flattening, and that the center of curvature at the equator is nearer to the surface than the axis of rotation.
Wherever lines cross, there is more than one latitude for that cartesian position. The green circle shows where our algorithm was struggling. If you consider that I cut off these normal vectors where they reach the axis, you would have even more normals for a given position for the problem considered in this SO thread. You would have 4 latitudes / normals inside the caustic, and 2 outside.
The problem can be expressed as the solution of a cubic equation which
gives 1, 2, or 3 real roots. For the derivation and closed form
solution see Appendix B of Geodesics on an ellipsoid of revolution. The boundary between 1 and 3 solutions is an astroid.

Algorithm to find all points on a 2D grid some distance away from another point

I have some point on a 2D grid (x, y) and I need to find all points that are n distance away from that point. The way I'm measuring distance is by using the distance formula between the two points. Anyone know how to do this?
Edit: Just for reference, what I'm trying to do is to write some AI path finding that will maintain some distance away from a target in a system that uses grid based locations. Currently I'm using A* path finding, but I'm not sure if that matters or makes a difference since I'm kind of new to this stuff.
Here's what I would do:
First filter out all points that are further than D on either x or y. These are certainly outside the circle of radius D. This is a much simpler computation, and it can quickly eliminate a lot of work. This is a outer bounding-box optimization.
You can also use an inner bounding-box optimization. If the points are closer than D * sqrt(2)/2 on either x or y, then they're certainly within the circle of radius D. This is also cheaper than calculating the distance formula.
Then you have a smaller number of candidate points that may be within the circle of radius D. For these, use the distance formula. Remember that if D = sqrt(Δx2+Δy2), then D2 = Δx2+Δy2.
So you can skip the cost of calculating square root.
So in pseudocode, you could do the following:
for each point
begin
if test 1 indicates the point is outside the outer bounding box,
then skip this point
if test 2 indicates the point is inside the inner bounding box,
then keep this point
if test 3 indicates the point is inside the radius of the circle,
then keep this point
end
This problem is known as range query. The brute force solution is just as you described: computed the distance of all points from the reference point and return those whose distance is less than the desired range value.
The brute force algorithm is O(N^2). There are, however, more efficient algorithms that employ spatial indexes to reduce algorithm complexity and the number of distance calculations. For example, you can use a R-Tree to index your points.
Its called nearest neighbor search. More at http://en.wikipedia.org/wiki/Nearest_neighbor_search
There are open libraries for that. I have used one written for C and recommend it: http://www.cs.umd.edu/~mount/ANN/. ANN stands for Approximate Nearest Neighbor, however, you can turn the approximation off and find the exact nearest neighbors.
This wouldn't use the distance formula, but if you're looking for points exactly n distance away, perhaps you could use sin/cos?
In pseudocode:
for degrees in range(360):
x = cos(degrees) * n
y = sin(degrees) * n
print x, y
That would print every point n away in 360 degree increments.
Java implementation:
public static Set<Point> findNearbyPoints(Set<Point> pts, Point centerPt, double radius) {
Set<Point> nearbyPtsSet = new HashSet<Point>();
double innerBound = radius * (Math.sqrt(2.0) / 2.0);
double radiusSq = radius * radius;
for (Point pt : pts) {
double xDist = Math.abs(centerPt.x - pt.x);
double yDist = Math.abs(centerPt.y - pt.y);
if (xDist > radius || yDist > radius)
continue;
if (xDist > innerBound || yDist > innerBound)
continue;
if (distSq(centerPt, pt) < radiusSq)
nearbyPtsSet.add(pt);
}
return nearbyPtsSet;
}

width of a frustum at a given distance from the near plane

I'm using CML to manage the 3D math in an OpenGL-based interface project I'm making for work. I need to know the width of the viewing frustum at a given distance from the eye point, which is kept as a part of a 4x4 matrix that represents the camera. My goal is to position gui objects along the apparent edge of the viewport, but at some distance into the screen from the near clipping plane.
CML has a function to extract the planes of the frustum, giving them back in Ax + By + Cz + D = 0 form. This frustum is perpendicular to the camera, which isn't necessarily aligned with the z axis of the perspective projection.
I'd like to extract x and z coordinates so as to pin graphical elements to the sides of the screen at different distances from the camera. What is the best way to go about doing it?
Thanks!
This seems to be a duplicate of Finding side length of a cross-section of a pyramid frustum/truncated pyramid, if you already have a cross-section of known width a known distance from the apex. If you don't have that and you want to derive the answer yourself you can follow these steps.
Take two adjacent planes and find
their line of intersection L1. You
can use the steps here. Really
what you need is the direction
vector of the line.
Take two more planes, one the same
as in the previous step, and find
their line of intersection L2.
Note that all planes of the form Ax + By + Cz + D = 0 go through the origin, so you know that L1 and L2
intersect.
Draw yourself a picture of the
direction vectors for L1 and L2,
tails at the origin. These form an
angle; call it theta. Find theta
using the formula for the angle
between two vectors, e.g. here.
Draw a bisector of that angle. Draw
a perpendicular to the bisector at
the distance d you want from the
origin (this creates an isosceles
triangle, bisected into two
congruent right triangles). The
length of the perpendicular is your
desired frustum width w. Note that w is
twice the length of one of the bases
of the right triangles.
Let r be the length of the
hypotenuses of the right triangles.
Then rcos(theta/2)=d and
rsin(theta/2)=w/2, so
tan(theta/2)=(w/2)/d which implies
w=2d*tan(theta/2). Since you know d
and theta, you are done.
Note that we have found the length of one side of a cross-section of a frustrum. This will work with any perpendicular cross-section of any frustum. This can be extended to adapt it to a non-perpendicular cross-section.

Zip Code Radius Search

I'm wondering if it's possible to find all points by longitude and latitude within X radius of one point?
So, if I provide a latitude/longitude of -76.0000, 38.0000, is it possible to simply find all the possible coordinates within (for example) a 10 mile radius of that?
I know that there's a way to calculate the distance between two points, which is why I'm not clear as to whether this is possible. Because, it seems like you need to know the center coordinates (-76 and 38 in this case) as well as the coordinates of every other point in order to determine whether it falls within the specified radius. Is that right?
#David's strategy is correct, his implementation is seriously flawed. I suggest that before you perform the calculations you transform your lat,long pair to UTM coordinates and work in distance, not angular, measurements. If you are not familiar with Universal Transverse Mercator, hit Google or Wikipedia.
I reckon that your point (-76,38) is at UTM 37C 472995 (Easting) 1564346 (Northing). So you want to do your calculations of distance from that point. You'll find it easier, working with UTM, to work in metres, so your distance is (if you are using statute miles of 5280 feet) 16040 metres.
Incidentally, (-76,38) is well outside the contintental US -- does the US Post Office define zip codes for Antarctica ?
If you accept that the Earth is a perfect sphere, you can obtain the spatial coordinates of a point by
x = R.cos(Lat).cos(Long)
y = R.cos(Lat).sin(Long)
z = R.sin(Lat)
Now, take two points and compute the angle they form with the center of the Earth (using a dot product):
cos(Phi) = (x'.x" + y'.y" + z'.z") / R²
(the value of R gets simplified).
In your case, the angular distance, Phi, equals 2Pi.D/R. (R=6 378.1 km).
A point P" is inside the ground distance (D) of P' when the dot product is larger than cos(Phi).
CAUTION: all angles must be in radians.
Depending on the precision, the data set of points within a certain distance may be extremely large or even infinite (impossible). In a given area of a circle with a positive radius you will have infinitely many points. Thus, it is trivial to determine if a point falls within a circle, however to enumerate over all the points is impossible.
If you do set a fixed precision (such as a single digit), you can loop over all possible latitude and longitude combinations and perform the distance test.
Kevin is correct. There is no reason to calculate every possible coordinate-pair in the radius.
If you start at the centerpoint pC = Point(-76.0000, 38.0000) and are testing to find out if arbitrary point pA = Point(Ax, Ay) is within a 10 mile radius... use the Pythagorean theorem:
xDist = abs( pCx - Ax )
yDist = abs ( pCy - Ay )
r^2 = (xDist)^2 + (yDist)^2
A reasonable approximation is to only query the points where
pAx >= (-76.0000 - 10.0000) && pAx <= (-76.0000 + 10.0000)
pAy >= ( 38.0000 - 10.0000) && pAy <= ( 38.0000 + 10.0000)
then perform the more intensive calculation above.

Triangle mathematics for game development

I'm trying to make a triangle (isosceles triangle) to move around the screen and at the same time slightly rotate it when a user presses a directional key (like right or left).
I would like the nose (top point) of the triangle to lead the triangle at all times. (Like that old asteroids game).
My problem is with the maths behind this. At every X time interval, I want the triangle to move in "some direction", I need help finding this direction (x and y increments/decrements).
I can find the center point (Centroid) of the triangle, and I have the top most x an y points, so I have a line vector to work with, but not a clue as to "how" to work with it.
I think it has something to do with the old Sin and Cos methods and the amount (angle) that the triangle has been rotated, but I'm a bit rusty on that stuff.
Any help is greatly appreciated.
The arctangent (inverse tangent) of vy/vx, where vx and vy are the components of your (centroid->tip) vector, gives you the angle the vector is facing.
The classical arctangent gives you an angle normalized to -90° < r < +90° degrees, however, so you have to add or subtract 90 degrees from the result depending on the sign of the result and the sign of vx.
Luckily, your standard library should proive an atan2() function that takes vx and vy seperately as parameters, and returns you an angle between 0° and 360°, or -180° and +180° degrees. It will also deal with the special case where vx=0, which would result in a division by zero if you were not careful.
See http://www.arctangent.net/atan.html or just search for "arctangent".
Edit: I've used degrees in my post for clarity, but Java and many other languages/libraries work in radians where 180° = π.
You can also just add vx and vy to the triangle's points to make it move in the "forward" direction, but make sure that the vector is normalized (vx² + vy² = 1), else the speed will depend on your triangle's size.
#Mark:
I've tried writing a primer on vectors, coordinates, points and angles in this answer box twice, but changed my mind on both occasions because it would take too long and I'm sure there are many tutorials out there explaining stuff better than I ever can.
Your centroid and "tip" coordinates are not vectors; that is to say, there is nothing to be gained from thinking of them as vectors.
The vector you want, vForward = pTip - pCentroid, can be calculated by subtracting the coordinates of the "tip" corner from the centroid point. The atan2() of this vector, i.e. atan2(tipY-centY, tipX-centX), gives you the angle your triangle is "facing".
As for what it's relative to, it doesn't matter. Your library will probably use the convention that the increasing X axis (---> the right/east direction on presumably all the 2D graphs you've seen) is 0° or 0π. The increasing Y (top, north) direction will correspond to 90° or (1/2)π.
It seems to me that you need to store the rotation angle of the triangle and possibly it's current speed.
x' = x + speed * cos(angle)
y' = y + speed * sin(angle)
Note that angle is in radians, not degrees!
Radians = Degrees * RadiansInACircle / DegreesInACircle
RadiansInACircle = 2 * Pi
DegressInACircle = 360
For the locations of the vertices, each is located at a certain distance and angle from the center. Add the current rotation angle before doing this calculation. It's the same math as for figuring the movement.
Here's some more:
Vectors represent displacement. Displacement, translation, movement or whatever you want to call it, is meaningless without a starting point, that's why I referred to the "forward" vector above as "from the centroid," and that's why the "centroid vector," the vector with the x/y components of the centroid point doesn't make sense. Those components give you the displacement of the centroid point from the origin. In other words, pOrigin + vCentroid = pCentroid. If you start from the 0 point, then add a vector representing the centroid point's displacement, you get the centroid point.
Note that:
vector + vector = vector
(addition of two displacements gives you a third, different displacement)
point + vector = point
(moving/displacing a point gives you another point)
point + point = ???
(adding two points doesn't make sense; however:)
point - point = vector
(the difference of two points is the displacement between them)
Now, these displacements can be thought of in (at least) two different ways. The one you're already familiar with is the rectangular (x, y) system, where the two components of a vector represent the displacement in the x and y directions, respectively. However, you can also use polar coordinates, (r, Θ). Here, Θ represents the direction of the displacement (in angles relative to an arbitary zero angle) and r, the distance.
Take the (1, 1) vector, for example. It represents a movement one unit to the right and one unit upwards in the coordinate system we're all used to seeing. The polar equivalent of this vector would be (1.414, 45°); the same movement, but represented as a "displacement of 1.414 units in the 45°-angle direction. (Again, using a convenient polar coordinate system where the East direction is 0° and angles increase counter-clockwise.)
The relationship between polar and rectangular coordinates are:
Θ = atan2(y, x)
r = sqrt(x²+y²) (now do you see where the right triangle comes in?)
and conversely,
x = r * cos(Θ)
y = r * sin(Θ)
Now, since a line segment drawn from your triangle's centroid to the "tip" corner would represent the direction your triangle is "facing," if we were to obtain a vector parallel to that line (e.g. vForward = pTip - pCentroid), that vector's Θ-coordinate would correspond to the angle that your triangle is facing.
Take the (1, 1) vector again. If this was vForward, then that would have meant that your "tip" point's x and y coordinates were both 1 more than those of your centroid. Let's say the centroid is on (10, 10). That puts the "tip" corner over at (11, 11). (Remember, pTip = pCentroid + vForward by adding "+ pCentroid" to both sides of the previous equation.) Now in which direction is this triangle facing? 45°, right? That's the Θ-coordinate of our (1, 1) vector!
keep the centroid at the origin. use the vector from the centroid to the nose as the direction vector. http://en.wikipedia.org/wiki/Coordinate_rotation#Two_dimensions will rotate this vector. construct the other two points from this vector. translate the three points to where they are on the screen and draw.
double v; // velocity
double theta; // direction of travel (angle)
double dt; // time elapsed
// To compute increments
double dx = v*dt*cos(theta);
double dy = v*dt*sin(theta);
// To compute position of the top of the triangle
double size; // distance between centroid and top
double top_x = x + size*cos(theta);
double top_y = y + size*sin(theta);
I can see that I need to apply the common 2d rotation formulas to my triangle to get my result, Im just having a little bit of trouble with the relationships between the different components here.
aib, stated that:
The arctangent (inverse tangent) of
vy/vx, where vx and vy are the
components of your (centroid->tip)
vector, gives you the angle the vector
is facing.
Is vx and vy the x and y coords of the centriod or the tip? I think Im getting confused as to the terminology of a "vector" here. I was under the impression that a Vector was just a point in 2d (in this case) space that represented direction.
So in this case, how is the vector of the centroid->tip calculated? Is it just the centriod?
meyahoocomlorenpechtel stated:
It seems to me that you need to store
the rotation angle of the triangle and
possibly it's current speed.
What is the rotation angle relative to? The origin of the triangle, or the game window itself? Also, for future rotations, is the angle the angle from the last rotation or the original position of the triangle?
Thanks all for the help so far, I really appreciate it!
you will want the topmost vertex to be the centroid in order to achieve the desired effect.
First, I would start with the centroid rather than calculate it. You know the position of the centroid and the angle of rotation of the triangle, I would use this to calculate the locations of the verticies. (I apologize in advance for any syntax errors, I have just started to dabble in Java.)
//starting point
double tip_x = 10;
double tip_y = 10;
should be
double center_x = 10;
double center_y = 10;
//triangle details
int width = 6; //base
int height = 9;
should be an array of 3 angle, distance pairs.
angle = rotation_angle + vertex[1].angle;
dist = vertex[1].distance;
p1_x = center_x + math.cos(angle) * dist;
p1_y = center_y - math.sin(angle) * dist;
// and the same for the other two points
Note that I am subtracting the Y distance. You're being tripped up by the fact that screen space is inverted. In our minds Y increases as you go up--but screen coordinates don't work that way.
The math is a lot simpler if you track things as position and rotation angle rather than deriving the rotation angle.
Also, in your final piece of code you're modifying the location by the rotation angle. The result will be that your ship turns by the rotation angle every update cycle. I think the objective is something like Asteroids, not a cat chasing it's tail!

Resources