Minkowski difference with concave polygons - c#

When trying to calculate the Minkowski difference of two convex polygons I can simply find the set of vertices and use a gift wrapping algorithm to find the convex hull. (see below.)
However for concave polygons the convex hull algorithm is not appropriate as it will give me a false positive on collisions, is there a way I can adapt my code to easily find the correct expansion using the points already generated?
public List<Vector2D> MinkowskiDifference(Polygon other)
{
List<Vector2D> vList = new List<Vector2D>();
foreach (Vector2D vT in this.vertices)
{
foreach (Vector2D vO in other.vertices)
{
vList.Add((vT+this.position) - (vO+other.position));
}
}
return vList;
}
public static Polygon ConvexHull(List<Vector2D> vList)
{
List<Vector2D> S = vList;
List<Vector2D> P = new List<Vector2D>();
Vector2D endpoint;
Vector2D pointOnHull = LeftMostPoint(S);
int i = 0;
do
{
P.Add(pointOnHull);
endpoint = S[0];
for (int j = 1; j < S.Count; j++)
{
if (endpoint == pointOnHull || RelPosition(P[i], endpoint, S[j]) == -1)
{
endpoint = S[j];
}
}
i++;
pointOnHull = endpoint;
} while (endpoint != P[0]);
return new Polygon(P);
}

The usual method is to decompose the concave polygon in to convex pieces, and iterate pairwise between the convex pieces of each polygon looking for a collision. If one of the polygons is too big to do this naively (made up of 100s of convex peices), you can add each piece to a broad phase.
Note that if you're doing something like GJK, you don't explicitly construct the Minkowski difference as a polygon. Rather, you walk around it implicitly by finding "support" vertices on it that are furthest along a given direction. Because the Minkowski difference is linearly separable, you can do this for each polygon in isolation then find their difference.
The idea can be a little hard to grok, but see eg: http://www.dyn4j.org/2010/04/gjk-distance-closest-points/

Related

Implementation of Minimum Vertex Cover in a Bipartite Graph

I have a bipartite graph that's quite large (~200 vertices per part, usually with 20,000 or more edges in between), and I'm trying to find a Minimum Vertex Cover in it because I'm looking for an assignment between the vertices of the two parts.
According to Koenig's theorem, there is such a cover with the same size as the cardinality of a Maximum Matching (https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_(graph_theory)).
I have implemented the Hopcroft Karp algorithm which gives me a Maximum Matching. If needed, I can provide my implementation of that, but I doubt that's where my problem is.
What's the actual problem?
I suspect my implementation, taken from the Wikipedia article above (https://en.wikipedia.org/wiki/K%C5%91nig%27s_theorem_(graph_theory)#Constructive_proof), has an error in it, but after several hours of checking it I am unable to find the cause of the bug: While the Hopcroft Karp algorithm finds a maximum matching with 192 edges, the Minimum Vertex Cover returns 200 vertices. As this is a bipartite graph, these numbers shouldn't differ (because of the theorem). Maybe you can help me out and tell me where my mistake is. Thanks in advance!!
(Student's and Project's are my two types of vertices in the bipartite graph)
internal static List<Vertex> FindMinimumVertexCover(IReadOnlyList<Edge> matching, IReadOnlyList<Vertex> studentVertices, IReadOnlyList<Vertex> projectVertices)
{
var unmatchedStudentNodes = studentVertices.Except(matching.Select(e => e.GetStudentVertex())).ToList();
var visitedVertices = new List<Vertex>();
var edgeComparer = new EdgeComparer();
foreach (var unmatchedStudentNode in unmatchedStudentNodes)
{
visitedVertices = visitedVertices.Union(FindAlternatingNodes(matching, unmatchedStudentNode, visitedVertices, edgeComparer)).ToList();
}
visitedVertices = unmatchedStudentNodes.Union(visitedVertices).ToList();
return studentVertices.Except(visitedVertices).Union(projectVertices.Intersect(visitedVertices)).ToList();
}
private static List<Vertex> FindAlternatingNodes(IReadOnlyList<Edge> matching, Vertex initialVertex, List<Vertex> visitedVertices, EdgeComparer edgeComparer)
{
if (visitedVertices.Contains(initialVertex))
return Enumerable.Empty<Vertex>().ToList();
visitedVertices.Add(initialVertex);
List<Edge> unmatchedEdges = initialVertex.Edges.Except(matching, edgeComparer).ToList();
foreach (Edge unmatchedEdge in unmatchedEdges)
{
Vertex visitedVertex = unmatchedEdge.GetProjectVertex();
Edge matchedEdge = matching.SingleOrDefault(e => e.GetProjectVertex().Equals(visitedVertex));
if (matchedEdge != default(Edge))
{
visitedVertices.Add(visitedVertex);
visitedVertex = matchedEdge.GetStudentVertex();
visitedVertices = visitedVertices.Union(FindAlternatingNodes(matching, visitedVertex, visitedVertices, edgeComparer)).ToList();
}
}
return visitedVertices;
}
class EdgeComparer : IEqualityComparer<Edge>
{
public bool Equals(Edge x, Edge y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (x is null || y is null)
return false;
return Object.ReferenceEquals(x.GetStudentVertex(), y.GetStudentVertex()) && Object.ReferenceEquals(x.GetProjectVertex(), y.GetProjectVertex());
}
public int GetHashCode(Edge edge)
{
return (Student: edge.GetStudentVertex(), Project: edge.GetProjectVertex()).GetHashCode();
}
}
I now found the problem. I want to thank #David Eisenstat, as he suggested generating small random graphs repeatedly.
The problem was something in my implementation of the Vertex class.
Every time I create an instance of the Edge class, I add that Edge to the corresponding vertices as well (meaning I effectively got 3 references to an edge). Calling the outer algorithm again (which calls the method above) only recreated the edge list, but left the old references in the vertices intact. Thus, following calls didn't start freshly, and the Minimum Vertex Cover found edges in the graph that weren't existent anymore (namely the List<Edge> unmatchedEdges = initialVertex.Edges.Except(matching, edgeComparer).ToList(); line).

Getting Number of Cubes with Integer Coordinates with Certain Distance from Origin

I am working on a voxel system for my game that uses dynamic loading of chunks. To optimize it, I have a pool of chunks and a render distance, and what I want to do is fill the pool with a proper amount of chunks. So, I need a way to find that amount. I have tried the following but it seems very inefficient.
private void CreatePool()
{
int poolSize = 0;
for (int x = -m_RenderDistance; x <= m_RenderDistance; x++) {
for (int y = -m_RenderDistance; y <= m_RenderDistance; y++) {
for (int z = -m_RenderDistance; z <= m_RenderDistance; z++) {
if (Position3Int.DistanceFromOrigin(new Position3Int(x, y, z)) <= m_RenderDistance)
poolSize++;
}
}
}
}
More formally, the question involes finding the amount of unique cubes with integer coorindates with a certain distance from the origin.
If you think there is a better way to approach this or I am doing something fundamentally wrong, let me know.
Thanks,
Quintin
I assume its the distance check that you think is inefficient? What you've got shouldn't be too bad if you're just getting the count on Start() or Awake().
Draco18s solution is fine if you are okay with a cubed result. If you want a spherical result without a distance check, you can try some formulation of the volume of a sphere: 4/3*PI*r^3
checkout Bresenham's circle.
Here's a approximation algorithm for a filled 3d Bresenham Circle that I have. It is very similar to what you have already, just with a more effecient squared dist check and a minor adjustment to get a more attractive bresenham-looking circle):
public static List<Vector3> Get3DCircleKeys(int radius){
List<Vector3> keys = new List<Vector3>();
for(int y=-radius; y<=radius; y++){
for(int x=-radius; x<=radius; x++){
for(int z =-radius; z<=radius; z++){
// (+ radius*.08f) = minor modification to match Bresenham result
if(x*x+y*y+z*z <= radius*radius + radius*.08f){
keys.Add(new Vector3(x,y,z));
}
}
}
}
return keys;
}
This, however, will deliver a different count than the volume of sphere would give you, but with some tweaking to it or to the sphere volume calculation, it could be good enough, or at least, more efficient than instantiating a full volume of a cube, where many of the voxels will be outside of the bounds of the render distance.

C# - Per Pixel Collision Detection

Here's my situation I'm making a 2D maze game(XNA 4.0). I have figured out that the best way to do collision detection is by using per-pixel detection. While searching it up on the internet I have found people explaining or showing code for two things colliding(i.e. mouse & player, player & player, two shapes). What I would like to do is have this collision detect whether the player collides with a wall or not(the background is black but the maze walls are white). Could someone explain how to do this or to give some sort of starting point with the code. Much Appreciated.
P.S. A link to a website or anything relating to my question would also be helpful
The best way to go about this CPU-intensive operation is checking for hitbox collision first, then the per-pixel collision.
Most of this code can be found in this helpful video.
static bool IntersectsPixel(Rectangle hitbox1, Texture2D texture1, Rectangle hitbox2, Texture2D texture2)
{
Color[] colorData1 = new Color[texture1.Width * texture1.Height];
texture1.GetData(colorData1);
Color[] colorData2 = new Color[texture2.Width * texture2.Height];
texture2.GetData(colorData2);
int top = Math.Max(hitbox1.Top, hitbox2.Top);
int bottom = Math.Min(hitbox1.Bottom, hitbox2.Bottom);
int right = Math.Max(hitbox1.Right, hitbox2.Right);
int left = Math.Min(hitbox1.Left, hitbox2.Left);
for(y = top; y< bottom; y++)
{
for(x = left; x < right; x++)
{
Color color1 = colorData1[(x - hitbox1.Left) + (y - hitbox1.Top) * hitbox1.Width]
Color color2 = colorData2[(x - hitbox2.Left) + (y - hitbox2.Top) * hitbox2.Width]
if (color1.A != 0 && color2.A != 0)
return true;
}
}
return false;
}
You can call this method like so:
if (IntersectsPixel(player.hitbox, player.texture, obstacle.hitbox, obstacle.texture))
{
// Action that happens upon collision goes here
}
Hope I could help you out,
- GHC
Create a matrix of bools representing your sprite and a matrix of bools representing your maze (the matrix representing your sprite needs to have the same dimensions as your maze).
then you can do something simple like iterate over all x-y coordinates and check whether or not they're both true
// as an optimization, store a bounding box to minimize the
// coordinates of what you need to check
for(int i = 0; i < width, i++) {
for(int j = 0; j < height, j++) {
if(sprite[i][j] && maze[i][j]) {
collision = true
//you might want to store the coordinates
}
}
}
If you want to be very fancy you can flatten your maze matrix and use bit operations

Ignore external points when finding rectangles

I have some images like this where I need to find the central rectangle
Im using a variation of the EmguCV examples to find rectangles and came with this
using (MemStorage storage = new MemStorage())
{ //allocate storage for contour approximation
//Contour<Point> contours = gray.FindContours()
Contour<Point> contours = gray.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST,
storage);
for (; contours != null; contours = contours.HNext)
{
Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.05, storage);
//Seq<Point> currentContour = contours.GetConvexHull(Emgu.CV.CvEnum.ORIENTATION.CV_CLOCKWISE);
if (contours.Area > MinRectangleArea) //only consider contours with area greater than 20000
{
if (currentContour.Total == 4) //The contour has 4 vertices.
{
bool isRectangle = true;
Point[] pts = currentContour.ToArray();
LineSegment2D[] edges = PointCollection.PolyLine(pts, true);
for (int i = 0; i < edges.Length; i++)
{
double angle = Math.Abs(edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
if (angle < 90 - RectangleAngleMargin || angle > RectangleAngleMargin + 90)
{
isRectangle = false;
break;
}
}
if (isRectangle)
{
boxList.Add(currentContour.GetMinAreaRect());
}
}
}
}
}
And the result of executing that over those images sometimes finds this two rectangles:
The orange rectangle is ok, thats what I need. But I dont want the blue. Sometimes the four vertex are in the border of the image, usually one of them is out.
Changing the RETR_TYPE of the FindContours function to CV_RETR_EXTERNAL, I only get the blue rectangle, so I wonder if there is an option of NOT getting the contours with external points.
The real image actually can have smaller rectangles inside the orange (or a line appears splitting the rectangle), so after that I´m selecting the bigger rectangle to be the one I want, but cant do it that way with that blue one.
Taking a look at your sample image I would choose another approach.
Instead of classical contour detection, If you perform Hough line detection and then peform intersections of line found, you will find exactly the four vertices of the rectangle you are searching for...
If you need some help in coding let me know and I will edit my answer.

Is there any algorithm for calculating area of a shape given co-ordinates that define the shape?

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);
}

Categories