Diagonal sweep and orthogonal distance of a point to diagonal - c#

I have a problem where I'm required to find the maximum number of points that are less than or equal to a given distance D to a line drawn in a two-dimensional Euclidean plane. To solve this I wrote the algorithms that would compute a possible maximum if the line was orthogonal to either the x-axis or the y-axis. My problem is when only a diagonal line would yield the maximum number of points.
Given the constraints that both x and y have a minimum value of -1000000 and maximum of 1000000. I wrote the following algorithm to try and find out the maximum. I don't seem to be getting the right answer. Could someone please guide me on where I am going wrong. I've tried drawing a regression line as well but that used vertical distance which did not work for my purposes. Maybe I'm going all wrong and this problem can be solved as an optimization problem. Anyways' any help with a descent explanation is much appreciated.
// diagonal sweep
for (int degree = 1; degree < 180; degree++) if (degree % 90 != 0)
{
int k = 1, degrees = degree;
double x1 = -1000000, x2 = 1000000;
if (degree > 90 && degree < 180)
{
degrees = 180 - degrees;
k = -1;
}
//slope
double m1 = Math.Tan(Math.PI * degrees * k / 180.0);
//Point A
Point A = new Point(x1, m1 * x1);
//Point B
Point B = new Point(x2, m1 * x2);
for (int i = 0; i < x.Length; i++)
{
//Point P = household that needs power
Point P = new Point(x[i], y[i]);
double normalLength = Math.Sqrt((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y));
double segmentLength = 1d * Math.Abs((P.X - A.X) * (B.Y - A.Y) - (P.Y - A.Y) * (B.X - A.X)) / normalLength;
if (segmentLength <= D)
tempCnt++;
}
maxConnections = Math.Max(maxConnections, tempCnt);
tempCnt = 0;
}
return maxConnections;

If you want to define this problem as an optimization problem, you should do it as follows, but it doesn't seem to me this optimization problem is solveable efficiently as is.
maximize: x_1 + x_2 + ... + x_n + 0*a + 0*b + 0*c
s.t.
x_i * d(p_i, line(a,b,c))/ MAX_DISTANCE <= 1
x_i is in {0,1}
Explanation:
x_i are inclusion variables - can get a value of 0 / 1 , and it indicates if the point p_i is in the required distance from the line.
a,b,c are the parameters for the line: ax + by + c = 0
The idea is to maximize the sum of included points, such that each included point is in the desired range. This is represented by the constraint, if x_i=0 - there is no restriction on the point p_i, as the constraint is always satisfied. Otherwise, x_i=1, and you need the distance from the line (let it be d) satisfy 1* d/MAX_DISTANCE <= 1 - which is exactly what you want.
Though I don't think there is an optimal efficient solution to this optimization problem, you might want to try some heuristical solutions for this optiization - such as Genetic Algorithms or Hill Climbing
As a side note, my gut says this problem is NP-Complete, though I have no proof for it yet - and will update this part of the answer if I (or someone else) can come up with a reduction/polynomial solution.

Related

How to move the end points of a line to keep its length but make it parallel to another line (C# preferably!)

I have a line segment whose end points I know
Line1 (X1,Y1) (X2,Y2)
I have a second line
Line2 (X3,Y3) (X4,Y4)
I want to calculate new end points for line 1 such that the resulting line is parallel to Line2, and Line1's centre point remains at the same coordinates.
i.e. such that Line1 simply rotates so it is parallel to Line2
I know I can calculate each line's angle
var line1Angle = (Mathf.Atan2(x2 - x1, y2 - y1));
var line2Angle = (Mathf.Atan2(x4 - x3, y4 - y3));
I can also calculate the lengths
var len1 = Math.Sqrt((x2-x1)*(x2-x1)+ (y2-y1) * (y2-y1));
var len2 = Math.Sqrt((x4-x3)*(x4-x3)+ (y4-y3) * (y4-y3));
but everything I have tried seems to fail - either not rotating correctly, or rotating but with the incorrect length.
The closest code I have (below) rotates correctly, but the length of Line1 is not retained.
The code uses an 'offset' which was used by the code this version is based on as it simply drew a parallel line 'offset' pixels from the destination line - I have set it to an arbitrary value but I believe should be the distance of Line1's centre point from the closest point on Line2.
I'd love it if someone could supply a code version, rather than an explanation (or as well as!) as I've read and tried so many non-code solutions, and evidently my understanding / translation to code is flawed!
float len1 = (float)Math.Sqrt((x2-x1)*(x2-x1)+ (y2-y1) * (y2-y1));
float len2 = (float)Math.Sqrt((x4-x3)*(x4-x3)+ (y4-y3) * (y4-y3));
float offset = 3.0f; // This should be the dist from our center to the closest wall but I"m compromising for now!
float newX1 = x3 + offset * (y4 - y3) * (len1 / len2);
float newX2 = x4 + offset * (y4 - y3) * (len1 / len2);
float newY1 = y3 + offset * (x3 - x4) * (len1 / len2);
float newY2 = y4 + offset * (x3 - x4) * (len1 / len2);
Approach without angles: you know segment lengths, and can just form new segment ends with the same direction as line2 defines
dx1 = x2 - x1
dy1 = y2 - y1
dx2 = x4 - x3
dy2 = y4 - y3
len1 = hypot(dx1, dy1)
len2 = hypot(dx2, dy2)
midx = (x1 + x2) / 2
midy = (y1 + y2) / 2
coeff = 0.5 * len1 / len2
//now we make a vector with direction of line2
//and length of half of line1
newx1 = midx - dx2 * coeff
newy1 = midy - dy2 * coeff
newx2 = midx + dx2 * coeff
newy2 = midy + dy2 * coeff
I would highly recommend that you define actual types for your line segment and points. You can use Math.Net or System.Numerics.Vectors if you want something to start with.
Assuming your line segment have a StartPoint and EndPoint we can define a MidPoint and Direction extension methods. I'm going to use the Math.Net types for the example, but it is not difficult to make your own types.
static Vector2D Midpoint(this Line2D l) => (l.StartPoint + l.EndPoint) / 2;
static Vector2D Direction(this Line2D l) => (l.EndPoint - l.StartPoint ).Normalize() ;
We can also define a static method to create a new line from these methods/Properties:
static Line2D FromMidpointDirection(Vector2D midpoint, Vector2D direction, float length){
var halfDir = direction * length/ 2;
return new Line2D(midpoint - halfDir , midpint + halfDir );
Note that you might want to add comments or pick another name for Direction, since it is not obvious if this is normalized or not.
Then you can recreate your line:
var mid = sourceLine.Midpoint();
var dir = targetLine.Direction();
var newLine = FromMidpointDirection(mid, dir,sourceLine.Length);
Using higher level types like this tend to make your code more reusable and easier to read and understand.

Bezier Curve Arc-Length Parameterization

I'm learning about Bezier curves and would like to parameterize the equations for distance using an estimation method. So far, my code seems to work for single points (EG Bezier(start=0, mid=1, end=5, nPoints=6) yields [0 1 2 3 4 5]). However, when I attempt to apply this to multi-dimensional curves, my results are not as expected.
C# code (executed in Unity for visualization). The function (should) get a point on the curve (defined by the points pts) at a length l% of the length.
Vector3 BezierL(Vector3[] pts, float l)
{
int i;
float[] tVals = new float[n];
Vector3[] points = new Vector3[n];
float[] cumDist = new float[n];
for (i = 1; i < n; i++)
{
tVals[i] = i / (float)(n - 1);
points[i] = Bezier(pts, tVals[i]);
cumDist[i] = cumDist[i - 1] +
(points[i] - points[i - 1]).magnitude;
}
// Interpolate to estimate t
float targetLen = l * cumDist[n - 1];
int ind = Array.BinarySearch(cumDist, targetLen);
if (ind < 0)
ind = ~ind;
float t = Mathf.Lerp(tVals[ind - 1], tVals[ind],
(targetLen - cumDist[ind - 1]) / (cumDist[ind] - cumDist[ind - 1]));
return Bezier(pts, t);
}
where Bezier(Vector3[] pts, t) gets a point on the curve defined by pts at time t. For whatever reason, this partially works in that all points are equally spaced, but some points are stacked at the initial point rather than being distributed along the curve.
This was my reference for developing this algorithm, so I'm unsure if my implementation is incorrect, or if it only applies to lower-dimensional curves.
Thanks in advance!
Oof how embarrassing, I just forgot to compute the 0th point!

Smallest difference between two angles?

I'm trying to calculate the smallest difference between two angles.
This is my current code (a slight variation of something I found online):
float a1 = MathHelper.ToDegrees(Rot);
float a2 = MathHelper.ToDegrees(m_fTargetRot);
float dif = (float)(Math.Abs(a1 - a2);
if (dif > 180)
dif = 360 - dif;
dif = MathHelper.ToRadians(dif);
It works fine except for in cases at the edge of a circle. For example if the current angle is 355 and the target angle is 5 it calculates the difference is -350 rather than 10 since 365 degrees is equal to 5 degrees.
Any ideas on what I can do to make this work?
You basically had it. Just take the dif modulus 360 before checking to see if greater than 180:
float a1 = MathHelper.ToDegrees(Rot);
float a2 = MathHelper.ToDegrees(m_fTargetRot);
float dif = (float)Math.Abs(a1 - a2) % 360;
if (dif > 180)
dif = 360 - dif;
dif = MathHelper.ToRadians(dif);
Edit: #Andrew Russell made a great point in comments to your question and the solution below takes advantage of the MathHelper.WrapAngle method as he suggested:
diff = Math.Abs(MathHelper.WrapAngle(a2 - a1));
You would expand the check for out of bound angles:
if (dif < 0) dif = dif + 360;
if (dif > 180) dif = 360 - dif;
I never like handling the zero-wrapping with case statements. Instead, I use the definition of the dot product to compute the (unsigned) angle between two angles:
vec(a) . vec(b) = ||a|| ||b|| cos(theta)
We're just going to make a and b unit vectors, so ||a|| == ||b|| == 1.
Since vec(x) = [cos(x),sin(x)], we get:
unsigned_angle_theta(a,b) = acos(cos(a)cos(b) + sin(a)sin(b))
(n.b. all angles in radians)
You can normalize the result to be 0 <= theta < 360:
while(theta < 0) { theta += 360; }
If you want to keep the answer in radians (recommended):
const Double TwoPi = 2 * Math.Pi;
while(theta < 0) { theta += TwoPi; }
We can use Euler's formula: exp(iA) = cos A + i sin A.
In the case of the difference between two angles this becomes:
exp(i(A-B))
Using the laws of exponents:
= exp(iA).exp(-iB).
-iB is the conjugate of iB thus:
= exp(iA).exp(conjugate(iB)).
The complex exponent can be calcuated by Taylors series:
taylor_e(P={cplx,_,_}) ->
taylor_e(
#{sum => to_complex(1),
term => 0,
term_value => to_complex(1),
min_terms => 3,
quadrature => 1,
error_term => 1.0e-4,
domain => P}
);
taylor_e(P=#{sum := Sum,
term := Term,
term_value := TermValue0,
min_terms := MinTerms,
domain := Dom,
quadrature := Q,
error_term := ErrorTerm
})
when ((Term =< MinTerms) or (abs(1-Q) > ErrorTerm)) and
(Term < 20) ->
NewTerm = Term+1,
TermValue1 = scalar_divide(multiply(TermValue0,Dom),NewTerm),
PartialSum = add(Sum,TermValue1),
taylor_e(P#{sum := PartialSum,
term := Term+1,
term_value := TermValue1,
quadrature := quadrance(PartialSum)
});
taylor_e(#{sum := Result}) ->
Result.
The angle difference is the the argument (direction) of the resulting complex number and is retrieved by atan2.
Of course you will need some basic complex number routines. This method does not have a discontinuity around 0/360 degrees, and the sign of the result gives the direction of turning. Where this is a difference between some reference direction (say in an autopilot) it only needs calculating once and then storing until a new course is chosen. The deviations from the course would need to be calculated from every sample however.

Calculate coordinates of a regular polygon's vertices

I am writing a program in which I need to draw polygons of an arbitrary number of sides, each one being translated by a given formula which changes dynamically. There is some rather interesting mathematics involved but I am stuck on this probelm.
How can I calculate the coordinates of the vertices of a regular polygon (one in which all angles are equal), given only the number of sides, and ideally (but not neccessarily) having the origin at the centre?
For example: a hexagon might have the following points (all are floats):
( 1.5 , 0.5 *Math.Sqrt(3) )
( 0 , 1 *Math.Sqrt(3) )
(-1.5 , 0.5 *Math.Sqrt(3) )
(-1.5 , -0.5 *Math.Sqrt(3) )
( 0 , -1 *Math.Sqrt(3) )
( 1.5 , -0.5 *Math.Sqrt(3) )
My method looks like this:
void InitPolygonVertexCoords(RegularPolygon poly)
and the coordinates need to be added to this (or something similar, like a list):
Point[] _polygonVertexPoints;
I'm interested mainly in the algorithm here but examples in C# would be useful. I don't even know where to start. How should I implement it? Is it even possible?!
Thank you.
for (i = 0; i < n; i++) {
printf("%f %f\n",r * Math.cos(2 * Math.PI * i / n), r * Math.sin(2 * Math.PI * i / n));
}
where r is the radius of the circumsribing circle. Sorry for the wrong language No Habla C#.
Basically the angle between any two vertices is 2 pi / n and all the vertices are at distance r from the origin.
EDIT:
If you want to have the center somewher other than the origin, say at (x,y)
for (i = 0; i < n; i++) {
printf("%f %f\n",x + r * Math.cos(2 * Math.PI * i / n), y + r * Math.sin(2 * Math.PI * i / n));
}
The number of points equals the number of sides.
The angle you need is angle = 2 * pi / numPoints.
Then starting vertically above the origin with the size of the polygon being given by radius:
for (int i = 0; i < numPoints; i++)
{
x = centreX + radius * sin(i * angle);
y = centreY + radius * cos(i * angle);
}
If your centre is the origin then simply ignore the centreX and centreY terms as they'll be 0,0.
Swapping the cos and sin over will point the first point horizontally to the right of the origin.
Sorry, I dont have a full solution at hand right now, but you should try looking for 2D-Rendering of Circles. All classic implementations of circle(x,y,r) use a polygon like you described for drawing (but with 50+ sides).
Say the distance of the vertices to the origin is 1. And say (1, 0) is always a coordinate of the polygon.
Given the number of vertices (say n), the rotation angle required to position the (1, 0) to the next coordinate would be (360/n).
The computation required here is to rotate the coordinates. Here is what it is; Rotation Matrix.
Say theta = 360/n;
[cos(theta) -sin(theta)]
[sin(theta) cos(theta)]
would be your rotation matrix.
If you know linear algebra you already know what i mean. If dont just have a look at Matrix Multiplication
One possible implementation to generate a set of coordinates for regular polygon is to:
Define polygon center, radius and first vertex1. Rotate the vertex n-times2 at an angle of: 360/n.
In this implementation I use a vector to store the generated coordinates and a recursive function to generate them:
void generateRegularPolygon(vector<Point>& v, Point& center, int sidesNumber, int radius){
// converted to radians
double angRads = 2 * PI / double(sidesNumber);
// first vertex
Point initial(center.x, center.y - radius);
rotateCoordinate(v, center, initial, angRads, sidesNumber);
}
where:
void rotateCoordinate(vector<Point>& v, Point& axisOfRotation, Point& initial, double angRads, int numberOfRotations){
// base case: number of transformations < 0
if(numberOfRotations <= 0) return;
else{
// apply rotation to: initial, around pivot point: axisOfRotation
double x = cos(angRads) * (initial.x - axisOfRotation.x) - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
double y = sin(angRads) * (initial.x - axisOfRotation.x) + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
// store the result
v.push_back(Point(x, y));
rotateCoordinate(v, axisOfRotation, Point(x,y), angRads, --numberOfRotations);
}
}
Note:
Point is a simple class to wrap the coordinate into single data structure:
class Point{
public:
Point(): x(0), y(0){ }
Point(int xx, int yy): x(xx), y(yy) { }
private:
int x;
int y;
};
1 in terms of (relative to) the center, radius. In my case the first vertex is translated from the centre up horizontally by the radius lenght.
2 n-regular polygon has n vertices.
The simple method is:
Let's take N-gone(number of sides) and length of side L. The angle will be T = 360/N.
Let's say one vertices is located on origin.
* First vertex = (0,0)
* Second vertex = (LcosT,LsinT)
* Third vertex = (LcosT+Lcos2T, LsinT+Lsin2T)
* Fourth vertex = (LcosT+Lcos2T+Lcos3T, LsinT+Lsin2T+Lsin3T)
You can do in for loop
hmm if you test all the versions that are listed here you'll see that the implementation is not good. you can check the distance from the center to each generated point of the polygon with : http://www.movable-type.co.uk/scripts/latlong.html
Now i have searched a lot and i could not find any good implementation for calculating a polyogon using the center and the radius...so i went back to the math book and tried to implement it myself. In the end i came up with this...wich is 100% good:
List<double[]> coordinates = new List<double[]>();
#region create Polygon Coordinates
if (!string.IsNullOrWhiteSpace(bus.Latitude) && !string.IsNullOrWhiteSpace(bus.Longitude) && !string.IsNullOrWhiteSpace(bus.ListingRadius))
{
double lat = DegreeToRadian(Double.Parse(bus.Latitude));
double lon = DegreeToRadian(Double.Parse(bus.Longitude));
double dist = Double.Parse(bus.ListingRadius);
double angle = 36;
for (double i = 0; i <= 360; i += angle)
{
var bearing = DegreeToRadian(i);
var lat2 = Math.Asin(Math.Sin(lat) * Math.Cos(dist / earthRadius) + Math.Cos(lat) * Math.Sin(dist / earthRadius) * Math.Cos(bearing));
var lon2 = lon + Math.Atan2(Math.Sin(bearing) * Math.Sin(dist / earthRadius) * Math.Cos(lat),Math.Cos(dist / earthRadius) - Math.Sin(lat) * Math.Sin(lat2));
coordinates.Add(new double[] { RadianToDegree(lat2), RadianToDegree(lon2) });
}
poly.Coordinates = new[] { coordinates.ToArray() };
}
#endregion
If you test this you'll see that all the points are at the exact distance that you give ( radius ). Also don't forget to declare the earthRadius.
private const double earthRadius = 6371.01;
This calculates the coordinates of a decagon. You see the angle used is 36 degrees. You can split 360 degrees to any number of sides that you want and put the result in the angle variable.
Anyway .. i hope this helps you #rmx!

Shorten a line by a number of pixels

I'm drawing a custom diagram of business objects using .NET GDI+. Among other things, the diagram consists of several lines that are connecting the objects.
In a particular scenario, I need to shorten a line by a specific number of pixels, let's say 10 pixels, i.e. find the point on the line that lies 10 pixels before the end point of the line.
Imagine a circle with radius r = 10 pixels, and a line with start point (x1, y1) and end point (x2, y2). The circle is centered at the end point of the line, as in the following illustration.
How do I calculate the point marked with a red circle, i.e. the intersection between circle and line? This would give me the new end point of the line, shortening it by 10 pixels.
Solution
Thank you for your answers from which I was able to put together the following procedure. I named it LengthenLine, since I find it more natural to pass a negative number of pixels if I want the line shortened.
Specifically, I was trying to put together a function that could draw a line with rounded corners, which can be found here.
public void LengthenLine(PointF startPoint, ref PointF endPoint, float pixelCount)
{
if (startPoint.Equals(endPoint))
return; // not a line
double dx = endPoint.X - startPoint.X;
double dy = endPoint.Y - startPoint.Y;
if (dx == 0)
{
// vertical line:
if (endPoint.Y < startPoint.Y)
endPoint.Y -= pixelCount;
else
endPoint.Y += pixelCount;
}
else if (dy == 0)
{
// horizontal line:
if (endPoint.X < startPoint.X)
endPoint.X -= pixelCount;
else
endPoint.X += pixelCount;
}
else
{
// non-horizontal, non-vertical line:
double length = Math.Sqrt(dx * dx + dy * dy);
double scale = (length + pixelCount) / length;
dx *= scale;
dy *= scale;
endPoint.X = startPoint.X + Convert.ToSingle(dx);
endPoint.Y = startPoint.Y + Convert.ToSingle(dy);
}
}
Find the direction vector, i.e. let the position vectors be (using floats) B = (x2, y2) and A = (x1, y1), then AB = B - A. Normalize that vector by dividing by its length ( Math.Sqrt(xx + yy) ). Then multiply the direction vector AB by the original length minus the circle's radius, and add back to the lines starting position:
double dx = x2 - x1;
double dy = y2 - y1;
double length = Math.Sqrt(dx * dx + dy * dy);
if (length > 0)
{
dx /= length;
dy /= length;
}
dx *= length - radius;
dy *= length - radius;
int x3 = (int)(x1 + dx);
int y3 = (int)(y1 + dy);
Edit: Fixed the code, aaand fixed the initial explanation (thought you wanted the line to go out from the circle's center to its perimeter :P)
I'm not sure why you even had to introduce the circle. For a line stretching from (x2,y2) to (x1,y1), you can calculate any point on that line as:
(x2+p*(x1-x2),y2+p*(y1-y2))
where p is the percentage along the line you wish to go.
To calculate the percentage, you just need:
p = r/L
So in your case, (x3,y3) can be calculated as:
(x2+(10/L)*(x1-x2),y2+(10/L)*(y1-y2))
For example, if you have the two points (x2=1,y2=5) and (x1=-6,y1=22), they have a length of sqrt(72 + 172 or 18.38477631 and 10 divided by that is 0.543928293. Putting all those figures into the equation above:
(x2 + (10/l) * (x1-x2) , y2 + (10/l) * (y1-y2))
= (1 + 0.543928293 * (-6- 1) , 5 + 0.543928293 * (22- 5))
= (1 + 0.543928293 * -7 , 5 + 0.543928293 * 17 )
= (x3=-2.807498053,y3=14.24678098)
The distance between (x3,y3) and (x1,y1) is sqrt(3.1925019472 + 7.7532190152) or 8.384776311, a difference of 10 to within one part in a thousand million, and that's only because of rounding errors on my calculator.
You can use similar triangles. For the main triangle, d is the hypotenuses and the extension of r is the vertical line that meets the right angle. Inside the circle you will have a smaller triangle with a hypotenuses of length r.
r/d = (x2-a0)/(x2-x1) = (y2-b0)/(y2-y1)
a0 = x2 + (x2-x1)r/d
b0 = y2 + (y2-y1)r/d

Categories

Resources