I have an array consist of Point2D ( which has two members, x and y), e.g., Point2D[] points. You cna think of this array as a series of points on a X Y graph. The array is sorted in such a way that it is arranged from the smaller Point2D.X to the bigger Point2D.X
My question is simple: how do you find the points ( and the corresponding item index right before and after those points) that are the local maxima/minima? Recall that local max/min is defined mathematically as dy/dx=0. So my task is that I would need to find the those points where dy/dx=0.
Please note that the the extreme points may or may not located right inside the Point2D array, as the graph is a smooth curve, not an linearly-piece-wise polyline. An extreme point can be the middle point of two points inside the array. for instance.
Is there any existing libraries/ components that already do this in C#?
Here is my method:
public class Point2D
{
public double X;
public double Y;
}
public class PointWithIndex
{
// the extreme point where dy/dx=0
public Point2D ExtremePoints;
// the index of the array for the point that locates right before this ExtremePoints
public int PrevItemIndex;
}
public static List<PointWithIndex> FindLocalExtrema(List<Point2D> xyPoints)
{
// the algorithm to find the max/min points of xyPoints
}
I suggest running a loop 0 < i < n - 1, check if P[i - 1].Y < P[i].Y && P[i + 1].Y < P[i].Y, then P[i] is a max. do the same with min.
I'm not sure if this is what you are looking for: The Catmull-Rom Spline which you can easily compute using the XNA Framework (in C#) as shown here.
The idea is: you will generate -iteratively or recursively- points using Catmull-Rom until you hit your local maxima.
Related
I'm creating kind of a 2D map where which coord has a tile class attached to it,
The tile class would have its coord inside of it, and some other values that would be accessed later, but I would like to have that map with no size limitations. The problem is, I want to see the values inside of some tile in that map, for example: I'm at coord(25,30) and I want to know a bool value inside the tile class in each adjacent tile.
And if I use an array, I would have maybe the fastest way to check the coord of a tile, e.g. an array of 2 indexes. I could make each index be a x and a y coord respectively, so I would only check that coord when seeing the values on a tile. But the map would have a limit in size.
And if I use a list the map won't have a limit in size but I can't check the coordinate directly, so I would need to go through each created tile with a foreach loop, and check if the coord inside that tile, is the same as the one I am looking for.
My current solution is to have a second List with only the coordinates, and have it assigned when I create a tile, so the index in the coordinate list is the same as in the tile list. So when I need to check for a tile, I do a CoordinateList.Contains(coordinate), and if that is true, then I put the index of that coordinate as the index that the code should look in the tile List.
I want a faster way to check a tile, without a size limitation.
So far, with the tile List I got around 3200ms for each time I checked the whole map (about 2000 tiles in the list).
And with the mapCoord List I got around 1500ms (around 2000 tiles and coords).
And with the array I was getting a pretty fast response (never measured it) but less than I second for sure... Since I never had to check for the whole Array, but one for a certain index.
Examples for easier understanding of my problem:
note1: It does not fill all the array.
note2: It wouldn't always be rectangular.
int size = 50;
Tile[,] mapArray = new Tile[size,size];
List<Tile> mapList = new List<Tile>();
List<Vector2Int> mapCoord = new List<Vector2Int>();
void CreateMap()
{
for(int x = size/2; size <= size/2; x++)
{
for(int y = size/2; size <= size/2; y++)
{
if(x > 2 && y > 3)
{
mapArray[x,y] = new Tile(new Vector2Int(x,y), false, 32);
mapList.add(new Tile(new Vector2Int(x,y), false, 32));
mapCoord.add(new Vector2Int(x,y));
}
}
}
}
So if I was to check a tile in the array, in the array I would just check the coord, since the tile coord would be the same as the array index, but it would have a size limit.
And if I was to check the tile in the list, I would need to do a foreach loop like this. Pretty bad for performance and optimization.
Tile desiredTile = null;
for each(Tile tile in mapTiles)
{
if(tile.Coord == DesiredCoord)
desiredTile = tile;
}
And the best way so far, is checking the mapCoord list like this:
if(mapCoord.Contains(desiredCoord))
{
desiredTile = mapList[mapCoord.IndexOf(DesiredCoord)];
}
Look up "sparse array" as a way to do this. One possible implementation is a Dictionary where the key is effectively a tuple of two ints (x and y). If the game starts with a standard boundary (say +/- 100 of the starting point) you could mix and match, a 200x200 array and the dictionary beyond that. You can also get creative by having multiple rectangular regions, each as an array.
If your total address space fits into a short integer (+/- 32k), then you could do something like this:
[StructLayout(LayoutKind.Explicit)]
struct IntXY
{
[FieldOffset(0)] Int16 X;
[FieldOffset(2)] Int16 Y;
[FieldOffset(0)] UInt32 AsAnUnsignedInteger;
public override int GetHashCode()
{
return AsAnUnsignedInteger.GetHashCode();
}
}
and use that as the key in your Dictionary (using LayoutKind.Explicit makes this is effectively the same as a C/C++ union - the X and Y shorts take up the same combined 32 bits as the unsigned int). It's probably cheaper than a Tuple<int, int> (though you'd probably want to test my guess).
I have created my own class 'Geolocation' which just holds double variables 'longitude' and 'latitude'.
I have a List and another static Geolocation object. I need to loop through my List and analyse which of these locations is closest to the static location. By 'closest' I am referring to real life distance, so I think it would be called a geospatial analysis.
My own pseudo idea is along the lines of this.
Geolocation[] closestPositions = new Geolocation[]
for (int i = 0; i < TrainStations.AllStations.Count; i++)
{
Geolocation A = TrainStations.AllStations[i];
Geolocation B = DataHandler.UserCurrentPosition;
if (A and B are closer than what is stored in closestPosition)
{
closestPosition[0] = A;
closestPosition[1] = B;
}
}
By the end of this loop I would be left with the two closest points (more specifically which train station in the city is closest to the user's current position).
However, I'm really not sure if the way my pesudo code would function is most efficient, and I don't know how to do the actual analysis (get the distances from A to B and measure which is shortest).
Rather than creating my own class 'Geolocation', I should have used the inbuilt System.Device.Location.GeoCoordinate class.
https://msdn.microsoft.com/en-us/library/system.device.location.geocoordinate(v=vs.110).aspx
This class has a function 'GetDistanceTo(Geocoordinate)' which "Returns the distance between the latitude and longitude coordinates that are specified by this GeoCoordinate and another specified GeoCoordinate."
I can use this in my loop, using GeoCordinate objects, to analyse which points are closest.
How can i calulate a valid range (RED) for my object's (BLACK) traveling direction (GREEN). The green is a Vector2 where x and y range is -1 to 1.
What I'm trying to do here is to create rocket fuel burn effekt. So what i got is
rocket speed (float)
rocket direction (Vector2 x = [-1, 1], y = [-1, 1])
I may think that rocket speed does not matter as fuel burn effect (particle) is created on position with its own speed.
A cheap and cheerful trick with 2D vectors is to transpose the x and y, then flip the sign on one of them to get the perpendicular vector (pseudo code):
Vector2 perpendicular ( -original.y, original.x ) // Or original.y, -original.x
Then you could do something like:
direction + perpendicular * rand(-0.3 , 0.3)
Update: having realised the question asks for the opposite vector (too busy looking at the picture!) I figure I had better answer that too. Multiply 'direction' by -1 to get the opposite vector. So this:
perpendicular * rand(-0.3 , 0.3) - direction
should give you a random direction vector somewhere in your range (not normalised, but close enough for these purposes). Then you can multiply that result by a random number depending on how long you want the tail.
If to expend upon OlduwanSteve's answer, you can make is such that it's somewhat physically accurate.
You want to create several vectors that will represent the expulsion (the red lines).
First define the number of vectors you want to represent the expulsion with - lets mark it n.
You want to get a set of n numbers which sum up to Vx. These numbers will be the x components of the expulsion vectors. You can do this like so (semi-pseudo code):
SumX = Vx;
for (i = 0; i < n; i++)
{
Ax[i] = -rand(0..SumX); // Ax is the array of all expulsion vectors x components
SumX -= Ax[i];
}
Now you'll want to calculate Ay (the y components of the expulsion vectors). This is quite similar to calculating the, except that SumY = 0.
Here instead of splitting up SumY among n elements, you need to decide a maximal y component. Best way I can think of to select this is to define a maximal allowed angle for the expulsion vectors and define the maximal Vy using: maxVy = minVx*tan(maxAlpha).
Now you can get Ay using this (semi-pseudo code):
SumY = maxVy*2; // The actual range is (-maxVy, maxVy), but using (0, 2*maxVy) is simpler IMO
for (i = 0; i < n; i++)
{
Ay[i] = rand(0..SumY);
SumY -= Ay[i];
}
for (i = 0; i < n; i++)
{
Ay[i] -= maxVy; // Translate the range back to (-maxVy, maxVy) from (0, 2*maxVy)
}
Now you have arrays of both the x and y components of the expulsion vectors. Iterate over both arrays and pair up elements to create the vectors (you don't have to iterate both arrays in the same order).
Notes:
• I align the axes in my calculations such that X is parallel to the objects speed vector (the green line).
• The calculation for maxVy does NOT guarantee that a vector of angle maxAlpha will be produced, it only guarantees that no vector of larger angle will be.
• The lines Ay[i] = rand(0..SumY) and Ax[i] = -rand(0..SumX) may lead to vectors with components of size 0. This may lead to annoying scenarios, I'd recommend to handle away such cases (for instance "while rand returns zero, call it again").
I have a 2D map which wraps at the edges. So if you move off the right edge you will reappear at the left side of the map. Likewise with the three other edges.
This is inheritable a problem for the KDTree which I use to find elements in range of points. Normally you would check whether the hyper sphere collides with the hyper plane to see if you should continue searching the other side of the tree, but this check does not work with wrapping edges.
Is there any way to modify the KD Tree to work with donut 2D spaces?
The data structure doesn't have to change, but the search procedure does. Represent each point by coordinates (x, y) in [0, w) * [0, h), where w is the width of the map, h is the height, and * denotes a Cartesian product. Store these points in a normal KD tree.
The fundamental primitive for searching a KD tree is, given a point (x, y) and a rectangle [a, b] * [c, d], determine the distance (squared) from the point to the rectangle. Normally this is g(x, a, b)2 + g(y, c, d)2, where
g(z, e, f) = e - z if z < e
0 if e <= z <= f
z - f if f < z
is the one-dimensional distance of z to [e, f]. In a toroidal space, we modify g slightly to account for wraparound.
g(z, e, f, v) = min(e - z, (z + v) - f) if z < e
0 if e < z < f
min(z - f, (e + v) - z) if f < z.
and the distance squared is g(x, a, b, w)2 + g(y, c, d, h)2. I expect that the running times will be comparable for this variant. (I'd redo the recurrences, but the worst-case for regular KD trees is much worse than practice most of the time - O(n1/2) for identifying the nearest neighbor in 2D among n points.)
Jitamaro suggested but didn't explain a method based on a "2x size" quadtree. It's a reasonable suggestion, except the quadtree uses four times as many nodes rather than two, as I'll explain below before tentatively suggesting an alternative method.
Suppose each (x,y) coordinate has -.5 < x <= .5 and -.5 < y <= .5 and whenever j, k are integers, point (x+j,y+k) is identical with point (x,y). Let quadtree T cover points with coordinates in the range -1 < x,y <= 1. Each time you add an item at (x,y) to T, where -.5 < x,y <= .5, let x' = {x-1 if x>0 else x+1}, and y' = {y-1 if y>0 else y+1}. Also add the item at (x,y'), (x',y'), and (x',y). [When you delete points later, again calculate (x', y') et al and delete them too.] It's easy to see that nearest-point lookups will work properly, so long as any lookup coordinate outside interval (-.5,.5] is adjusted properly.
If the four-fold number of nodes is a deal-breaker, note that if coordinates as described above are used in subtrees above say level k=3, and relative coordinates are stored below level k, it should be possible to maintain single copies of subtrees below level k. That is, each subtree at level k would have four parents. (I haven't thought about this enough to know if this totally works; will edit answer if I find it doesn't.)
A quadtree is a KD-tree with 4 leafs. A quadtree doesn't help to wrap because its data structure is a wrap itself. You just need to use a quadtree 2x size of your structure.
So I have some function that receives N random 2D points.
Is there any algorithm to calculate area of the shape defined by the input points?
You want to calculate the area of a polygon?
(Taken from link, converted to C#)
class Point { double x, y; }
double PolygonArea(Point[] polygon)
{
int i,j;
double area = 0;
for (i=0; i < polygon.Length; i++) {
j = (i + 1) % polygon.Length;
area += polygon[i].x * polygon[j].y;
area -= polygon[i].y * polygon[j].x;
}
area /= 2;
return (area < 0 ? -area : area);
}
Defining the "area" of your collection of points may be hard, e.g. if you want to get the smallest region with straight line boundaries which enclose your set then I'm not sure how to proceed. Probably what you want to do is calculate the area of the convex hull of your set of points; this is a standard problem, a description of the problem with links to implementations of solutions is given by Steven Skiena at the Stony Brook Algorithms repository. From there one way to calculate the area (it seems to me to be the obvious way) would be to triangulate the region and calculate the area of each individual triangle.
You can use Timothy Chan's algorithm for finding convex hull in nlogh, where n is the number of points, h is the number of convex hull vertices. If you want an easy algorithm, go for Graham scan.
Also, if you know that your data is ordered like a simple chain, where the points don't cross each other, you can use Melkman's algorithm to compute convex hull in O(N).
Also, one more interesting property of convex hull is that, it has the minium perimeter.
Your problem does not directly imply that there's a ready-made polygon (which is assumed by this answer). I would recommend a triangulation such as a Delaunay Triangulation and then trivially compute the area of each triangle. OpenCV (I've used it with a large number of 2D points and it's very effective) and CGAL provide excellent implementations for determining the triangulation.
I found another function written in Java , so i traslated it to C#
public static double area(List<Double> lats,List<Double> lons)
{
double sum=0;
double prevcolat=0;
double prevaz=0;
double colat0=0;
double az0=0;
for (int i=0;i<lats.Count;i++)
{
double colat=2*Math.Atan2(Math.Sqrt(Math.Pow(Math.Sin(lats[i]*Math.PI/180/2), 2)+ Math.Cos(lats[i]*Math.PI/180)*Math.Pow(Math.Sin(lons[i]*Math.PI/180/2), 2)),
Math.Sqrt(1- Math.Pow(Math.Sin(lats[i]*Math.PI/180/2), 2)- Math.Cos(lats[i]*Math.PI/180)*Math.Pow(Math.Sin(lons[i]*Math.PI/180/2), 2)));
double az=0;
if (lats[i]>=90)
{
az=0;
}
else if (lats[i]<=-90)
{
az=Math.PI;
}
else
{
az=Math.Atan2(Math.Cos(lats[i]*Math.PI/180) * Math.Sin(lons[i]*Math.PI/180),Math.Sin(lats[i]*Math.PI/180))% (2*Math.PI);
}
if(i==0)
{
colat0=colat;
az0=az;
}
if(i>0 && i<lats.Count)
{
sum=sum+(1-Math.Cos(prevcolat + (colat-prevcolat)/2))*Math.PI*((Math.Abs(az-prevaz)/Math.PI)-2*Math.Ceiling(((Math.Abs(az-prevaz)/Math.PI)-1)/2))* Math.Sign(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);
}