All I have the following implementation of a line detecting Hough transform for point clouds (collection of points in 3-space)
internal sealed class LineHoughTransform : ILineHoughTransform
{
private readonly double _dX;
private readonly double _maxX;
private readonly long _countX;
private readonly long _countB;
private readonly IDiscreetSphere _sphere;
public LineHoughTransform(Vector3 minParameterVector, Vector3 maxParameterVector, double dX, int sphereGranularity)
{
_dX = dX;
_sphere = new Icosahedron();
_sphere.Create(sphereGranularity);
_countB = _sphere.Points.Count;
_maxX = Math.Max(maxParameterVector.Norm(), minParameterVector.Norm());
var rangeX = 2 * _maxX;
if (_dX == 0.0)
_dX = rangeX / 64.0;
_countX = (long)(rangeX / _dX).RoundToNearest();
VotingSpace = new Dictionary<long, int>();
}
public int GetLine(ref Vector3 a, ref Vector3 b)
{
int votes = 0;
long index = 0;
foreach (var storedVote in VotingSpace)
{
if (storedVote.Value > votes)
{
votes = storedVote.Value;
index = storedVote.Key;
}
}
// Retrieve x' coordinate from VotingSpace[_countX * _countX * _countB].
double x = index / (_countX * _countB);
index -= (long)(x * _countX * _countB);
x = x * _dX - _maxX;
// Retrieve y' coordinate from VotingSpace[_countX * _countX * _countB].
double y = index / _countB;
index -= (long)y * _countB;
y = y * _dX - _maxX;
// Retrieve directional vector and Compute anchor point according to Eq. (3).
b = _sphere.Points[(int)index];
a.X = (float)(x * (1 - ((b.X * b.X) / (1 + b.Z))) - y * ((b.X * b.Y) / (1 + b.Z)));
a.Y = (float)(x * (-((b.X * b.Y) / (1 + b.Z))) + y * (1 - ((b.Y * b.Y) / (1 + b.Z))));
a.Z = (float)(-x * b.X - y * b.Y);
return votes;
}
public void Add(IPointCloud pointCloud)
{
CastVote(pointCloud, true);
}
public void Subtract(IPointCloud pointCloud)
{
CastVote(pointCloud, false);
}
private void CastVote(IPointCloud pointCloud, bool add)
{
if (pointCloud == null || pointCloud.Vertices == null)
return;
foreach (var vertex in pointCloud.Vertices)
PointVote(vertex.Point, add);
}
private void PointVote(Vector3 point, bool add)
{
// Loop over directions B.
for (int j = 0; j < _sphere.Points.Count; ++j)
{
// Denominator in Eq. (2).
Vector3 b = _sphere.Points[j];
double beta = 1 / (1 + b.Z);
// Compute x' and y' according to Eq. (2).
double newX = ((1 - (beta * (b.X * b.X))) * point.X) - (beta * (b.X * b.Y) * point.Y) - (b.X * point.Z);
double newY = (-beta * (b.X * b.Y) * point.X) + ((1 - (beta * (b.Y * b.Y))) * point.Y) - (b.Y * point.Z);
long x_i = (long)((newX + _maxX) / _dX).RoundToNearest();
long y_i = (long)((newY + _maxX) / _dX).RoundToNearest();
// Compute one-dimensional index from three indices.
// x_i * <number of planes> * <number of direction vectors> + y_i * <number of direction vectors> + <loop index>
long index = (x_i * _countX * _countB) + (y_i * _countB) + j;
if (!VotingSpace.ContainsKey(index))
VotingSpace.Add(index, 0);
if (add)
VotingSpace[index]++;
else
VotingSpace[index]--;
}
}
public Dictionary<long, int> VotingSpace { get; private set; }
}
I would like to improve the speed of this code, so I attempted to use
public ConcurrentDictionary<long, int> VotingSpace { get; private set; }
with
private void CastVote(IPointCloud pointCloud, bool add)
{
if (pointCloud == null || pointCloud.Vertices == null)
return;
Parallel.ForEach(pointCloud.Vertices, vertex => PointVote(vertex.Point, add));
}
Note, pointCloud in CastVote can contain vast numbers of points, and the VotingSpace incrementation becoming
if (!VotingSpace.ContainsKey(index))
VotingSpace.TryAdd(index, 0);
if (add)
VotingSpace[index]++;
else
VotingSpace[index]--;
However, sometimes the TryAdd is failing, causing my calling algorithm to fail. I have attempted to put a retry on the TryAdd but this does not seem to help the problem of dropped indexes. How can I make this class optimally multi-threaded, as simply as possible and working in exactly the same way as the original?
When working with concurrent collections, you normally use the special atomic APIs they offer. In this case you should probably use the ConcurrentDictionary.AddOrUpdate method:
VotingSpace.AddOrUpdate(index,
addValueFactory: (key) => add ? 1 : -1,
updateValueFactory: (key, existingValue) => existingValue + (add ? 1 : -1));
Related
I have a 2d space with multiple objects(Lets call them B). Lets say object A our automated actor, he moves in a specific path and he has to shoot only the objects it can destroy. The other objects might or might not move.
I need to find the direction that I should fire the bullet that will collide with the object B. The bullet is moving with a different speed that object A and it has a specific lifetime.
I've tried to solve it with Quadratic but I always get infinity, is this a wrong approach?
Vector3 vectorFromVictim = bullet.Position - victim.Position;
float distanceToVictim = vectorFromVictim.Length();
double victimSpeed = victim.Position.Length();
double a = bulletSpeed * bulletSpeed - victimSpeed * victimSpeed;
double b = 2 * vectorFromVictim.Dot(victim.LinearVelocity);
double c = -distanceToVictim * distanceToVictim;
float t = (QuadraticSolver(a, b, c));
if (float.IsInfinity(t))
{
return;
}
interceptionPosition = victim.Position + victim.LinearVelocity * t;
if (t <= bulletLifetime)
{
ShootAtDirection(interceptionPosition);
}
Edit: My QuadraticSolver is this
double d = Math.Pow(b, 2) - (4 * a * c);
if (d < 0)
{
return float.PositiveInfinity;
}
float t;
if (d == 0)
{
t = (float) (-b / (2 * a));
if (float.IsNaN(t))
{
return float.PositiveInfinity;
}
return t;
}
t = (float) ((-b - Math.Sqrt(d)) / (2 * a));
float t2 = (float) ((-b + Math.Sqrt(d)) / (2 * a));
if (t < t2)
{
return t < 0 ? float.PositiveInfinity : t;
}
return t2 < 0 ? float.PositiveInfinity : t2;
B (target) coordinates are
bx + ux * t, by + uy * t
where ux, uy are components of B velocity vector
Bullet coordinates are
ax + v * cos(f) * t, ay + v * sin(f) * t
where v is bullet speed, f is directional angle (unknown yet)
ax + v * cos(f) * t = bx + ux * t
ay + v * sin(f) * t = y + uy * t
t * (v * cos(f) - ux) = bx - ax = dx
t * (v * sin(f) - uy) = bx - ax = dy
dx, dy is position difference, negated your vectorFromVictim
exclude t
dy * (v * cos(f) - ux) = dx * (v * sin(f) - uy)
dy * v * cos(f) - dy * ux = dx * v * sin(f) - dx * uy
v * (dy*cos(f) - dx*sin(f)) = dy * ux - dx * uy
let
g = atan2(dy, dx)
L = vectorFromVictim.Length
so
v * sin(g - f) = L * (dy * ux - dx * uy)
sin(g - f) = L/v * (dy * ux - dx * uy)
g - f = arcsin(L/v * (dy * ux - dx * uy) )
and finally
f = g - arcsin(L/v * (dy * ux - dx * uy) )
Quiclk Python test
import math
def aiming(ax, ay, bx, by, ux, uy, v):
dx = bx - ax
dy = by - ay
g = math.atan2(dy, dx)
L = math.hypot(dy, dx)
if (v * math.cos(ang) - ux):
t = dx / (v * math.cos(ang) - ux)
elif (v * math.sin(ang) - uy):
t = dy / (v * math.sin(ang) - uy)
else:
return None
coll_x = bx + ux * t
coll_y = by + uy * t
return ang, coll_x, coll_y
print(aiming(0, 0, 0, 1, 1, 0, 1.4142))
gives correct value 0.7854 = Pi/4 radians = 45 degrees, and point (1,1)
I read in stackoverflow before about Bresenham algorithm use for a particular environment like low-language, but I have to compare it in my report so any ideal for my report. How can I prove Bresenham is faster than DDA. Now I created a simple paint on winform C# and the time when I draw with 2 different method is likely equal, sometime DDA is faster. Here is my code in both method
Bresenham Algorithm
List<Point> vertices = new List<Point>();
int deltaX = xN - x0; int signX = deltaX >= 0 ? 1 : -1;
int deltaY = yN - y0; int signY = deltaY >= 0 ? 1 : -1;
if (deltaX == deltaY && deltaX * deltaY == 0) return vertices;
// |dy|/|dx| < 1 => |dy| < |dx| => m < 1
if (Math.Abs(deltaX) > Math.Abs(deltaY))
{
int _2deltaX = deltaX * 2 * signX;
int _2deltaY = deltaY * 2 * signY;
int p0 = _2deltaY - deltaX * signX;
// create array to contain vertices
vertices.Add(new Point(x0, y0));
int xCurrent = x0;
int yCurrent = y0;
while (true)
{
if (count >= (Math.Abs(deltaX) + 1)) return vertices;
if (p0 < 0)
{
xCurrent += signX;
// pk + 1 = pk + 2.∆y.signX
p0 = p0 + _2deltaY;
}
else
{
xCurrent += signX;
yCurrent += signY;
// pk+1= pk + 2.∆y.signX - 2.∆x.signY
p0 = p0 + _2deltaY - _2deltaX;
}
vertices.Add(new Point(xCurrent, yCurrent));
}
}
// |dy|/|dx| > 1 => |dy| > |dx| => m > 1
else if (Math.Abs(deltaX) <= Math.Abs(deltaY))
{
int _2deltaX = deltaX * 2 * signX;
int _2deltaY = deltaY * 2 * signY;
int p0 = _2deltaX - deltaY * signY;
// create array to contain vertices
vertices.Add(new Point(x0, y0));
int xCurrent = x0;
int yCurrent = y0;
while (true)
{
if (count >= (Math.Abs(deltaY) + 1)) return vertices;
if (p0 < 0)
{
yCurrent += signY;
// pk + 1 = pk + 2.∆x.signY
p0 = p0 + _2deltaX;
}
else
{
xCurrent += signX;
yCurrent += signY;
// pk+1= pk + 2.∆x.signY - 2.∆y.signX
p0 = p0 + _2deltaX - _2deltaY;
}
vertices.Add(new Point(xCurrent, yCurrent));
}
}
return vertices;
DDA Algorithm
List<Point> vertices = new List<Point>();
int deltaX = xN - x0; int signX = deltaX >= 0 ? 1 : -1;
int deltaY = yN - y0; int signY = deltaY >= 0 ? 1 : -1;
if (deltaX == deltaY && deltaX * deltaY == 0) return vertices;
int step = Math.Abs(deltaX) > Math.Abs(deltaY) ? Math.Abs(deltaX) : Math.Abs(deltaY);
// x(k + 1) = xk + x'
double stepX = deltaX * 1.0 / step;
double stepY = deltaY * 1.0 / step;
vertices.Add(new Point(x0, y0));
double xCurrent = x0;
double yCurrent = y0;
for (int i = 0; i < step; i++)
{
xCurrent += stepX;
yCurrent += stepY;
vertices.Add(new Point((int)Math.Round(xCurrent), (int)Math.Round(yCurrent)));
}
return vertices;
When i call my funtion with a startingAngle=0 it produce a good shape with the correct size.
Example:
var points = GetPolygonVertices(sides:4, radius:5, center:(5, 5), startingAngle:0), produces:
points[0] = {X = 10 Y = 5}
points[1] = {X = 5 Y = 0}
points[2] = {X = 0 Y = 5}
points[3] = {X = 5 Y = 10}
As observed the side length is 10px, which is correct, but produce a rotated square at 45º from human eye prespective.
To fix this i added a switch/case to offset the startAngle so it will put the square at correct angle for human eye, by rotating 45º. The rotation works, but the shape is no longer a square of 10x10px, instead i lose 1 to 2px from sides:
[0] = {X = 9 Y = 1}
[1] = {X = 1 Y = 1}
[2] = {X = 1 Y = 9}
[3] = {X = 9 Y = 9}
and become worse as radius grow, for example with radius=10:
[0] = {X = 17 Y = 3}
[1] = {X = 3 Y = 3}
[2] = {X = 3 Y = 17}
[3] = {X = 17 Y = 17}
I tried with both floor and ceil instead of round, but it always end in lose 1 or 2px...
Is there a way to improve the function to keep the shape size equal no matter the number of sides and rotation angle?
My function:
public static Point[] GetPolygonVertices(int sides, int radius, Point center, double startingAngle = 0)
{
if (sides < 3)
throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
// Fix rotation
switch (sides)
{
case 3:
startingAngle += 90;
break;
case 4:
startingAngle += 45;
break;
case 5:
startingAngle += 22.5;
break;
}
var points = new Point[sides];
var step = 360.0 / sides;
int i = 0;
for (var angle = startingAngle; angle < startingAngle + 360.0; angle += step) //go in a circle
{
if (i == sides) break; // Fix floating problem
double radians = angle * Math.PI / 180.0;
points[i++] = new(
(int) Math.Round(Math.Cos(radians) * radius + center.X),
(int) Math.Round(Math.Sin(-radians) * radius + center.Y)
);
}
return points;
}
EDIT: I updated the function to get rid of the switch condition and product shapes in correct orientation for human eye when angle is not given. Still it suffer from same "problem"
public static Point[] GetPolygonVertices(int sides, int radius, Point center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false)
{
if (sides < 3)
throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
var vertices = new Point[sides];
double deg = 360.0 / sides;//calculate the rotation angle
var rad = Math.PI / 180.0;
var x0 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + startingAngle) * rad);
var y0 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + startingAngle) * rad);
var x1 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + deg + startingAngle) * rad);
var y1 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + deg + startingAngle) * rad);
vertices[0] = new(
(int) Math.Round(x0),
(int) Math.Round(y0)
);
vertices[1] = new(
(int) Math.Round(x1),
(int) Math.Round(y1)
);
for (int i = 0; i < sides - 2; i++)
{
double dsinrot = Math.Sin((deg * (i + 1)) * rad);
double dcosrot = Math.Cos((deg * (i + 1)) * rad);
vertices[i + 2] = new(
(int)Math.Round(center.X + dcosrot * (x1 - center.X) - dsinrot * (y1 - center.Y)),
(int)Math.Round(center.Y + dsinrot * (x1 - center.X) + dcosrot * (y1 - center.Y))
);
}
if (flipHorizontally)
{
var startX = center.X - radius;
var endX = center.X + radius;
for (int i = 0; i < sides; i++)
{
vertices[i].X = endX - (vertices[i].X - startX);
}
}
if (flipVertically)
{
var startY = center.Y - radius;
var endY = center.Y + radius;
for (int i = 0; i < sides; i++)
{
vertices[i].Y = endY - (vertices[i].Y - startY);
}
}
return vertices;
}
EDIT 2: From Tim Roberts anwser here the functions to calculate side length from radius and radius from side length, this solve my problem. Thanks!
public static double CalculatePolygonSideLengthFromRadius(double radius, int sides)
{
return 2 * radius * Math.Sin(Math.PI / sides);
}
public static double CalculatePolygonVerticalLengthFromRadius(double radius, int sides)
{
return radius * Math.Cos(Math.PI / sides);
}
public static double CalculatePolygonRadiusFromSideLength(double length, int sides)
{
var theta = 360.0 / sides;
return length / (2 * Math.Cos((90 - theta / 2) * Math.PI / 180.0));
}
Your problem is one of mathematics. You said "As observed, the side length is 10px". It very definitely is not 10px. The distance from (10,5) to (5,0) is sqrt(5*5 + 5*5), which is 7.07. That's exactly what we expect for a square that is inscribed in a circle of radius 5: 5 x sqrt(2).
And that's what the other squares are as well.
FOLLOWUP
As an added bonus, here is a function that returns the radius of the circle that circumscribes a regular polygon with N sides of length L:
import math
def rad(length,nsides):
theta = 360/nsides
r = length / (2 * math.cos( (90-theta/2) * math.pi / 180))
return r
for s in range(3,9):
print(s, rad(10,s))
I'm in need of concave algorithm to outline shape from set of points, is there implementation in AForge.NET that I could use, I have read somewhere that AForge.NET has implementation of that algorithm but I can't find it in documentation.
Any help would be greatly appreciated,
Best regards,
Daniel
I was also looking for a simple .NET implementation creating an alpha shape but couldn't find one. So I did my own. The crucial insights were provided by ETH Zurich‘s Kaspar Fischer in this document.
The idea is simply eating up the surrounding space of a finite point set with a circular spoon of radius alpha without actually hitting the points. Here's an image from Kaspar's paper:
Now, every circle that contains exactly two points on its boundary but none inside is said to be alpha-exposed (AEC), and it's these AEC that give you the eventual alpha shape--just replace the two points defining an AEC by an edge.
Note: If your alpha shape looks too much like a convex hull, make alpha smaller. If, on the other hand, your alpha shape is fragmented or has too many holes in it, make alpha larger.
Here's the minimalist code (it runs in O(n³), where n ist the number of points):
public class Edge
{
public PointF A { get; set; }
public PointF B { get; set; }
}
public class AlphaShape
{
public List<Edge> BorderEdges { get; private set; }
public AlphaShape(List<PointF> points, float alpha)
{
// 0. error checking, init
if (points == null || points.Count < 2) { throw new ArgumentException("AlphaShape needs at least 2 points"); }
BorderEdges = new List<Edge>();
var alpha_2 = alpha * alpha;
// 1. run through all pairs of points
for (int i = 0; i < points.Count - 1; i++)
{
for (int j = i + 1; j < points.Count; j++)
{
if (points[i] == points[j]) { throw new ArgumentException("AlphaShape needs pairwise distinct points"); } // alternatively, continue
var dist = Dist(points[i], points[j]);
if (dist > 2 * alpha) { continue; } // circle fits between points ==> p_i, p_j can't be alpha-exposed
float x1 = points[i].X, x2 = points[j].X, y1 = points[i].Y, y2 = points[j].Y; // for clarity & brevity
var mid = new PointF((x1 + x2) / 2, (y1 + y2) / 2);
// find two circles that contain p_i and p_j; note that center1 == center2 if dist == 2*alpha
var center1 = new PointF(
mid.X + (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (y1 - y2) / dist,
mid.Y + (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (x2 - x1) / dist
);
var center2 = new PointF(
mid.X - (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (y1 - y2) / dist,
mid.Y - (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (x2 - x1) / dist
);
// check if one of the circles is alpha-exposed, i.e. no other point lies in it
bool c1_empty = true, c2_empty = true;
for (int k = 0; k < points.Count && (c1_empty || c2_empty); k++)
{
if (points[k] == points[i] || points[k] == points[j]) { continue; }
if ((center1.X - points[k].X) * (center1.X - points[k].X) + (center1.Y - points[k].Y) * (center1.Y - points[k].Y) < alpha_2)
{
c1_empty = false;
}
if ((center2.X - points[k].X) * (center2.X - points[k].X) + (center2.Y - points[k].Y) * (center2.Y - points[k].Y) < alpha_2)
{
c2_empty = false;
}
}
if (c1_empty || c2_empty)
{
// yup!
BorderEdges.Add(new Edge() { A = points[i], B = points[j] });
}
}
}
}
// Euclidian distance between A and B
public static float Dist(PointF A, PointF B)
{
return (float)Math.Sqrt((A.X - B.X) * (A.X - B.X) + (A.Y - B.Y) * (A.Y - B.Y));
}
}
And, as a proof of concept, here's the output of the code used in an actual app:
i know that AForge DONT have the Concave Hull.
You need to use EmguCV if you want compute it.
I need to implement Geofence in C#. Geofence area can be round, rectangle, polygon etc. Does anyone have Geofence implementation in C#?
I found Geo Fencing - point inside/outside polygon. But, it supports polygon only.
I have tested various implementations and this example worked properly for me:
Example
public static bool PolyContainsPoint(List<Point> points, Point p) {
bool inside = false;
// An imaginary closing segment is implied,
// so begin testing with that.
Point v1 = points[points.Count - 1];
foreach (Point v0 in points)
{
double d1 = (p.Y - v0.Y) * (v1.X - v0.X);
double d2 = (p.X - v0.X) * (v1.Y - v0.Y);
if (p.Y < v1.Y)
{
// V1 below ray
if (v0.Y <= p.Y)
{
// V0 on or above ray
// Perform intersection test
if (d1 > d2)
{
inside = !inside; // Toggle state
}
}
}
else if (p.Y < v0.Y)
{
// V1 is on or above ray, V0 is below ray
// Perform intersection test
if (d1 < d2)
{
inside = !inside; // Toggle state
}
}
v1 = v0; //Store previous endpoint as next startpoint
}
return inside;
}
Refer to my Implementation:
Polygon
Circle
Adding both C# implementation here
It worked for me!
//Location will hold the latitude and longitude.
public class Location
{
public double lat { get; set; }
public double lng { get; set; }
public Location(double lat, double lng)
{
this.lat = lat;
this.lng = lng;
}
}
//Implementation for the Polygon.
bool IsPointInPolygon(List<Location> poly, Location point)
{
int i, j;
bool c = false;
for (i = 0, j = poly.Count - 1; i < poly.Count; j = i++)
{
if ((((poly[i].lat <= point.lat) && (point.lat < poly[j].lat))
|| ((poly[j].lat <= point.lat) && (point.lat < poly[i].lat)))
&& (point.lng < (poly[j].lng - poly[i].lng) * (point.lat - poly[i].lat)
/ (poly[j].lat - poly[i].lat) + poly[i].lng))
{
c = !c;
}
}
return c;
}
//Geofencing for the Circle.
//GetDistance will return total Kilometers
//p1 is the Center lat,long and p2 is the current location lat,long
//radius in meters
public bool IsPointInCircle(Location p1,Location p2,double radius)
{
return GetDistance(p1,p2)>radius*0.001?false:true;
}
public double GetDistance(Location pos1, Location pos2)
{
double e = pos1.lat * (Math.PI / 180);
double f = pos1.lng * (Math.PI / 180);
double g = pos2.lat * (Math.PI / 180);
double h = pos2.lng * (Math.PI / 180);
double i =
(Math.Cos(e) * Math.Cos(g) * Math.Cos(f) * Math.Cos(h)
+ Math.Cos(e) * Math.Sin(f) * Math.Cos(g) * Math.Sin(h)
+ Math.Sin(e) * Math.Sin(g));
double j = Math.Acos(i);
return (6371 * j);
}