How to find closest point on multisegment line - c#

I'm trying to find a solution for best performance.
I need to find closet point on multisegment line (List points) to given point.
My line have thousands of points and I need to check distance to this line few times per second. So solution need to be very fast.
Right now I have something like below. It works but it is going to be slow when line have 10000+ points.
Maybe someone have idea how to make it faster?
public static float GetSqrDistXZ(Vector3 a, Vector3 b)
{
Vector3 vector = new Vector3(a.x - b.x, 0, a.z - b.z);
return vector.sqrMagnitude;
}
public static Vector3 NearestPointOnFiniteLine(Vector3 start, Vector3 end, Vector3 pnt)
{
Vector3 line = (end - start);
float len = Mathf.Sqrt(line.sqrMagnitude);
line.Normalize();
Vector3 v = pnt - start;
float d = (v.x * line.x) + (v.z * line.z);
d = Mathf.Clamp(d, 0f, len);
return start + line * d;
}
int pointsCount = line.points3.Count - 1; // line - List<Vector3> points.
float[] distances = new float[pointsCount];
for (int i = 0; i < pointsCount+1; i++) {
if (i >= 1) {
distances [i - 1] = GetSqrDistXZ (point, NearestPointOnFiniteLine (line.points3 [i - 1], line.points3 [i], point));
}
}
int minListIndexLeft = System.Array.IndexOf (distances, Mathf.Min (distances));
float minimalDistance = distances[minListIndexLeft];
Vector3 closestPoint = NearestPointOnFiniteLine (line.points3[minListIndexLeft], line.points3[minListIndexLeft+1], point);

You'll want to think about space partitioning. In this example I'm going to assume a 2D space, but this works in 3D just as well. Also there are much better solutions like BSP trees and stuff, but we'll keep it simple here.
Imagine putting a grid over your 2D space. Every segment (distance between 2 points) of your line intersects with one or more cells of that grid. What you have to do is to store the intersecting segments for every cell. If your line does not change, you can do that in one single pass on startup, or even store that information statically in an Asset.
But once you have that information, all you have to do is calculate the cell that your point is inside and then only check the line segments that intersect with that specific cell or a number of direct neighbours (see below). This makes finding the closest point lightning fast in comparison.
If you play with this idea on a piece of paper you may come across cases where this solution does not yield the closest point, because it did not consider a neighboring cell that contained a closer point. The easiest way to solve this is the following approach:
1. Find cell C, which is the cell your point is in
2. Let cellRange = 0
3. Let point B be undefined
4. Find closest point P among all segments that intersect cell C and its neighboring cells of range cellRange*
5. If B is the same as newly found point P then P is the solution. You are done.
6. Increase cellRange by 1
7. Let B = P
8. Repeat from step 4
* "neighboring cells of range cellRange" means:
cellRange 0: only cell C, no neighbours
cellRange 1: cell C and direct neighbours
cellRange 2: cell C, direct neighbours and their direct neighbours
...
This solution basically checks if increasing the search range improves the solution. As soon as increasing the range did not improve the solution, you found the closest point.

Related

How to get a parabola shape according to a moved point and nearest points

Being not very good at math, I have a problem with my project.
The objective is boundary correction on 3D files.
In my application, the user moves a 3D point on X-axis in order to correct or modify the boundary of the object.
I want to move the nearest boundary points in the same direction but decreasingly. I mean no point should move more than the main point. The nearest points move most and, the farthest points should move less.
On the image, the red dots represent the initial status of points. And the user pulls the P0 in the x-direction. And the other points follow it. The last status of the points is represented by violet dots.
Here is what I tried.
//On point moved event
//Get nearest boundary Points (Uses Geometry3D to get boundary points).
(var clothDMesh, _) = Utilities3D.BuildDMesh(baseMesh);
CreateClothModel(clothDMesh);
var boundryVertices = nodes.Where(ro => ro.Value.isBorder).Select(ro => ro.Value.vertex).ToList();
var refPoint = CustomPoint.FromPoint3D(movedPoint);
//Gets the delta X.
var deltaX = p.X - initialX;
//Gets nearest country points, so 15 points above and 15 points below to move only a given number of points (I know maybe this should be calculated according to delta).
var nearestPoints = refPoint.GetNearesPoints(boundryVertices, 30);
foreach (var item in nearestPoints)
{
//This is only one of what I tried as a function. None of them worked correctly.
item.X += deltaX - (deltaX * 1/ Math.Pow(item.Distance, 2));
}
Any help will be appreciated.
Thanks in advance.
Here's the math part:
I call "a" your "deltaX".
We also need a second parameter: "b", the maximum height of the red dots. I assume it is symetrical and "-b" would be the minimum height of the red dots.
So, if you look for the value X, horizontal move, in fonction of the coordinate Y of the dot:
X = a - a * Y * Y / (b * b);
You can verify that for Y = 0, you obtain X = a and for Y = b (or -b) you get X = 0.
You have your parabola (X is function of Y^2).

Line offset (parallel lines) without cusps

I'm trying to draw lines with offset to main line like on attachment.
I have problems with my code. It generating intersections and cusps on the lines. (attachment)
Maybe someone can help me with this code provide any working example that I can follow.
// LEFT SIDE OF MAIN LINE
int numberOfLines = 10;
float offset = 10f;
lastLinePoints = outerPoints; // outerPoint = Points from Main Line
for(int i = 0; i < numberOfLines; i++)
{
List<Vector3> tempPoints = new List<Vector3> ();
for (int k = 0; k < lastLinePoints.Count; k++) {
if (k + 1 < lastLinePoints.Count) {
Vector3 direction = lastLinePoints [k + 1] - lastLinePoints [k];
// up direction:
Vector3 up = new Vector3(0.0f, 1.0f, 0.0f);
// find right vector:
Vector3 right = Vector3.Cross(direction.normalized, up.normalized);
Vector3 newPoint = lastLinePoints [k] + (right * offset);
tempPoints.Add (newPoint);
}
}
VectorLine lineTemp = new VectorLine ("lineCurved", tempPoints, 120f / _camera2DObject.GetComponent<Camera> ().orthographicSize, LineType.Continuous);
lineTemp.Draw3D ();
lastLinePoints = tempPoints;
}
After some research I know that solution for drawing curved parallel lines can be difficult. I found also some algorithms (https://hal.inria.fr/inria-00518005/document) but this mathematics is to hard for me to make code from it.
After suggestion from #jstreet I tried CLIPPER library. Results are very good but is it possible to draw only parallel line instead closed polygon around line (like on attachment)
UPDATE
I wrote another question becouse I think that using CLIPPER for parallel lines is worth it. LINK TO question
From My Previous Experience, lot of time will be spent to solve your problem without applying a polyline curves offset algorithm,so my advice is to start implementing any of the algorithms regardless the mathematical difficulties. choose one of the published algorithm that suits exactly your case, it could be easier than implementing an algorithm for any shape.
But you can get the below link a shot
https://github.com/skyrpex/clipper
calculate parallel line parameters: you will need to calculate offset as coefficient (angle) remains the same.
calculate line intersections between neighbouring lines based on calculated values from step 1.
use splines over sequential sets of three intersection points. For splines you can use any cubic spline library, there are plenty in guthub (https://gist.github.com/dreikanter/3526685) or codeproject (http://www.codeproject.com/Articles/560163/Csharp-Cubic-Spline-Interpolation).

2d game vector movement C# [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm new in game development and I'm stuck in a problem.
I would like to know the new player position each seconds, here an example :
The player start at (2.5;2.5) and he go to (6.5;3.8).
His velocity is by example 2 units per seconds, and I would like to know the player position after 1sec. So something like this :
What I would like to know it's A every seconds but I don't know at all how I can do that...
I hope you will be able to help me, thanks in advance !
His velocity is by example 2 units per seconds.
I assume, that 'unit' means 'vector of length = 1'.
First of all, you need to calculate the AB vector (movement vector):
mov_vec = [xb-xa, yb-ya] = [6.5 - 2.5, 3.8 - 2.5] = [4, 1.3]
So, we know, that total unit did movement by [4, 1.3]. We need to normalize this vector. Normalized vector (unit vector) 'norm_mov_vec' will be codirectional with 'mov_vec', but it's length will be 1. See this link if you want to know more abut unit vectors.
Compute the length of movement vector:
mov_vec_len = sqrt( 4^2 + 1.3^2 ) ~= 4.2059
Compute normalized vector:
norm_mov_vec = [4/4.2059, 1.3/4.2059] ~= [0.9510, 0.3090]
And that's it. 'norm_mov_vec' is your 'unit-movement-vector', so if player is moving in that direction with speed of N units per second, you can very easily compute it's position after T seconds:
pos_after_T_sec_with_speed_N_units_per_sec = start_pos + ( N * T * norm_mov_vec )
EDIT:
Sample code, using Vector2 type from XNA. Can't test it, but I hope you will get the idea:
//In your case:
//start_pos = 'A' point
//end_pos = 'B' point
//time = number of seconds that elapsed
//speed = number of units per second
Vector2 calculatePosition(ref Vector2 start_pos, ref Vector2 end_pos, Uint32 time, Uint32 speed)
{
Vector2 mov_vec = Vector2.Substract(end_pos, start_pos);
Vector2 norm_mov_vec = Vector2.Normalize(mov_vec);
Vector2 delta_vec = norm_mov_vec * time * speed;
return Vector2.Add(start_pos, delta_vec);
}
First you need to work out the total distance covered, that's your vector. A vector is a movement, not two points in space.
Then you just divide each dimension, x and y in this case, by the time taken to do the move in unit of measurement (seconds) to get the distance per second.
Then you multiply each x and y by the number of seconds from 0, i.e. 1 second in your example, to get the position after 1 second.
I don't know what's available to you in your framework or libraries but a good Vector class will be so helpful, you'll want to be able to do math on the vector instance directly, such as:
Point origin = sprite.Position; // Assumes some sprite object with a position.
Point dest = new Point(200,344); // Destination.
Vector totalTranslation = new Vector(dest.X - origin.X, dest.Y - origin.Y);
Vector perSecond = totalTranslation / 60; // assuming takes a minute to move.
Vector distanceMoved = perSecond * 4; // distance moved after 4 seconds.
Point newPosition = new Point(origin.X + distanceMoved.X, origin.Y + distanceMoved.Y);
sprite.Position = newPosition; // Or using some orchestration class...
spriteManager.Move(sprite, newPosition); // ...like this.
Note being able to divide a vector directly. Else you have to divide each spatial dimension of the vector and make a new vector, or make a helper class to do it.
In real life, you might want to calculate based on milliseconds. I wouldn't use a fixed frame counter since it could look juddery, but work everything out based on a timer.
As I say, a good library or immutable Vector struct/class is the key here. Then its a case of thinking about the problem on graph paper.
Also, build up a palette of small functions you can chain together to do cooler, bigger stuff.
Another interesting problem is using an easing function to work out a coordinate after a given time to achieve the effect of a sprite slowing down as it 'lands'.
This is not programming, but vector math mostly, but anyway:
Your player is moving along the vector BA ( Point B minus Point A ) which is
Direction Vector: ( 4.0 / 1.3 )
This vector has a length of:
SquareRoot(4.0 * 4.0 + 1.3 * 1.3) = 4.2
A vector of the same direction and length of one unit would therefore be the vector with both components divided by the length of 4.2:
Direction Vector of length 1: (0.95 / 0.30)
As your player is fast and moves two units, it would be double length:
Direction Vector of length 2: (1.90 / 0.60)
Now each tick, add 1.90 and 0.60 respectively to your player coordinates, until they equal (roughly) the target coordinates.
x-displacement: 6.5-2.5 = 4
y-displacement: 3.8-2.5 = 1.3
Math.sqrt((4n)(4n)+(1.3n)(1.3n)) = 2
n=2/Math.sqrt(17.69)
x-displacement/second = 4n = 8/Math.sqrt(17.69) = 1.90207
y-displacement/second = 1.3n = 2.6/Math.sqrt(17.69) = 0.61817
so after get these values, it is really easy to calculate the position each second
You can use (as a general solution) these simple trigonometry formulae:
x = A.x + v * cos(fi) * t;
y = B.y + v * sin(fi) * t;
fi = atan2(B.y - A.y, B.x - A.x);
sample solution
// Since there's no common 2d Point double based type,
// let (x, y) point be represented as Tuple<Double, Double>
// where Item1 is x, and Item2 is y
public static Tuple<Double, Double> Move(Tuple<Double, Double> fromPoint,
Tuple<Double, Double> toPoint,
Double velocity,
Double time) {
Double fi = Math.Atan2(toPoint.Item2 - fromPoint.Item2, toPoint.Item1 - fromPoint.Item1);
return new Tuple<Double, Double>(
fromPoint.Item1 + velocity * Math.Cos(fi) * time,
fromPoint.Item2 + velocity * Math.Sin(fi) * time);
}
...
for (int t = 0; t < 10; ++t) {
Tuple<Double, Double> position =
Move(new Tuple<Double, Double>(2.5, 2.5),
new Tuple<Double, Double>(6.5, 3.8),
2.0,
t);
Console.Write("t = ");
Console.Write(t);
Console.Write(" x = ");
Console.Write(position.Item1);
Console.Write(" y = ");
Console.Write(position.Item2);
Console.WriteLine();
}

How do I calculate opposite of a vector, add some slack

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").

algorithm for a random space bordered by elements of equal length

I am an architecture student trying to solve a spatial problem with C# in Grasshopper for Rhino.
The space I am trying to create is an exhibition space in an airport. The space will be made up of elements of similar length. The idea is to connect them with a hinge and thereby allow them to create spaces of different layout and size according to how many elements are used.
As you can see from the illustration I would like the space to end with an opening an element length away from the starting point.
My first attempt has been to create equilateral triangles depending on the number of segments (walls) needed.
In short, from the starting point, triangles are created, and then the sides of the triangle that form the outer border are added to a list of points. This point list is returned to the Grasshopper application, which draws lines between the points. A little point is that I made the creation of the next triangle randomly either from the side AC or BC from the last triangle.
Here is an example of the spaces created (for 12 - 8 - 14 - 20 elements):
Here is the source code that creates these point lists:
private void RunScript(double radius, int walls, ref object A)
{
//
List<Point3d> pointList = new List<Point3d>();
List<Point3d> lastList = new List<Point3d>();
bool alternate = true;
bool swapped = false;
Random turn = new Random();
// set up the first part of the triangle
Point3d point1 = new Point3d(0, 0, 0);
Point3d point2 = new Point3d(0, radius, 0);
pointList.Add(point1);
pointList.Add(point2);
Point3d calcPoint;
for(int i = 0; i < walls - 1; i++) // walls - 1, is because I need one less triangle to get to the amount of walls
{
// use the method to create two similar circles and return the intersection point
// in order to create an equilateral triangle
calcPoint = FindCircleIntersections(point1.X, point1.Y, point2.X, point2.Y, radius, alternate);
// random generator: will decide if the new triangle should be created from side BC or AC
bool rotate = turn.Next(2) != 0;
Print("\n" + rotate);
// set the 2nd and 3rd point as 1st and 2nd - depending on random generator.
if(rotate)
{
point1 = point2;
if(swapped == true)
swapped = false;
else
swapped = true;
}
// if the direction is swapped, the next point created will not be a part of the outer border
if(swapped)
lastList.Add(calcPoint);
else
pointList.Add(calcPoint);
point2 = calcPoint;
// swap direction of intersection
if(rotate)
{
if(alternate)
alternate = false;
else
alternate = true;
}
}
lastList.Reverse();
foreach (Point3d value in lastList)
{
pointList.Add(value);
}
A = pointList;
}
// Find the points where the two circles intersect.
private Point3d FindCircleIntersections(
double cx0, double cy0, double cx1, double cy1, double rad, bool alternate)
{
// Find the distance between the centers.
double dx = cx0 - cx1;
double dy = cy0 - cy1;
double dist = Math.Sqrt(dx * dx + dy * dy);
// Find a and h.
double a = (rad * rad - rad * rad + dist * dist) / (2 * dist);
double h = Math.Sqrt(rad * rad - a * a);
// Find P2.
double cx2 = cx0 + a * (cx1 - cx0) / dist;
double cy2 = cy0 + a * (cy1 - cy0) / dist;
// Get the points P3.
if(alternate)
return new Point3d((double) (cx2 + h * (cy1 - cy0) / dist), (double) (cy2 - h * (cx1 - cx0) / dist), 0);
else
return new Point3d((double) (cx2 - h * (cy1 - cy0) / dist), (double) (cy2 + h * (cx1 - cx0) / dist), 0);
}
What I would like to do, is to vary the creation of these shapes, so that they are not only corridors, but resemble my initial sketches. I would like an algorithm to take an input of segments (number and length) and then propose different space layouts which are possible to create with this number of segments. I guess because of tesselation the space would have to be created with triangles, squares or hexagons? Do you think I should look into this "maximum area" algorithm : Covering an arbitrary area with circles of equal radius here on stackoverflow?
I would greatly appreciate any help on this algorithm. Cheers, Eirik
If you're merely interested in a program to generate instances to be externally evaluated (and not all such instances), you could "inflate" your curve. For example, in the 14-segment instance in your second image, there is a place where the curve goes inward and doubles back -- so your list of points has one point repeated. For curves like this you could cut out everything between the two (identical) points (A and B), as well as one of the surrounding points (A or B), and you have reclaimed some points to expand your curve - possibly resulting in a non-corridor structure. You may have to work some magic to ensure it is a "closed" curve, though, buy alternately adding segments to the front and the back of the curve.
Another opportunity: if you can identify the curve's interior (and there are algorithms for this), then anywhere that two segments form a concave angle with respect to your curve, you could blow it out to make a non-corridorish area. E.g. the second and third segments of your 14-segment curve above could be blown out to the left.
Successively applying these two methods to your corridor-like curve should generate many of the shapes you're looking for. Good luck!

Categories