Find intersection of polygon and order by line - c#

I was wondering if there is a method to order intersection points in a list based on the direction of the intersected line.
Here is a picture to get the idea:
The red numbers are the polygon lines which exist in a List.
Then I have another List with lines which are parallels of one polygon line intersecting the polygon at a certain offset (image shows parallels of poly line nr. 4).
Iterating through them I get the intersections points displayed in black numbers.
My problem now is that I would like to have the intersection points ordered like shown in the picture. But when iterating through every parallel the order of the intersections found changes at the 22nd intersection point.
Because the algorithm finds the intersection at poly line 1 first because I'm going through the list.
I hope you know what I mean.
I would like to find the intersection points always in the same pattern like shown.
The only idea I came up with is to transform the current line on the coordinate axis and then sort the 2 intersections by x-value but I assume that this is very bad...
I'd appreciate every answer which leads me to the solution.
Thanks in advance
Here's my code snippet:
for (int i = 0; i < parallelLines.Count; i++)
{
for (int j = 0; j < polyLines.Count; j++)
{
var actual = ber.LineSegementsIntersect(
parallelLines[i].v1,
parallelLines[i].v2,
polyLines[j].v1,
polyLines[j].v2,
out intersection);
// if intersection is found
if (actual)
{
intersections.Add(intersection);
}
}
}

iterate through the lines first in an outer loop, then for each line get the 2 points in the specific order you want.
Modify your code to put the two found itxns in temporary list, instead of directly into intersections collection - then, in between the inner and outer loop add those two intx objects to the intersections collection in the proper order ((the one with smaller x value first, or whatever rule you want)
like this: (Assume IntersectionType is the declared type of your Intersection object)
for (int i = 0; i < parallelLines.Count; i++)
{
var pair = new List<IntersectionType>();
for (int j = 0; j < polyLines.Count; j++)
{
var actual = ber.LineSegementsIntersect(
parallelLines[i].v1,
parallelLines[i].v2,
polyLines[j].v1,
polyLines[j].v2,
out intersection);
// if intersection is found
if (actual) pair .Add(intersection);
}
intersections.Add(pair.OrderBy(i=>i.XValue).First());
intersections.Add(pair.OrderByDescending(i=>i.XValue).First());
}
oh, by the way, the variable actual, if it is what I think it is, should be renamed as found, or, better, refactor as:
for (int i = 0; i < parallelLines.Count; i++)
{
var pair = new List<IntersectionType>();
for (int j = 0; j < polyLines.Count; j++)
if(ber.LineSegementsIntersect(
parallelLines[i].v1, parallelLines[i].v2,
polyLines[j].v1, polyLines[j].v2,
out intersection))
pair .Add(intersection);
intersections.Add(pair.OrderBy(i=>i.XValue).First());
intersections.Add(pair.OrderByDescending(i=>i.XValue).First());
}

Related

Optimizing this enormous for loop

So I have this for loop:
for (int i = 0; i < meshes.Count; i++)
{
for (int j = 0; j < meshes.Count; j++)
{
for (int m = 0; m < meshes[i].vertices.Length; m++)
{
for (int n = 0; n < meshes[i].vertices.Length; n++)
{
if ((meshes[i].vertices[m].x == meshes[j].vertices[n].x) && (meshes[i].vertices[m].z == meshes[j].vertices[n].z))
{
if (meshes[i].vertices[m] != meshes[j].vertices[n])
{
meshes[i].vertices[m].y = meshes[j].vertices[n].y;
}
}
}
}
}
}
Which goes through a few million vectors and compares them to all other vectors, to then modify some of their y values. I think it works, however after hitting play it takes an unbelievably long time to load (currently been waiting for 15 minutes, and still going). Is there a way to make it more efficient? Thanks for the help!
As I read this, what you're basically doing, is that for all vertices with the same x and z, you set their y value to the same.
A more optimized way would be to use the Linq method GroupBy which internally uses hash mapping to avoid exponential time complexity like your current approach:
var vGroups = meshes.SelectMany(mesh => mesh.vertices)
.GroupBy(vertex => new { vertex.x, vertex.z });
foreach (var vGroup in vGroups)
{
vGroup.Aggregate((prev, curr) =>
{
// If prev is null (i.e. first iteration of the "loop")
// don't change the 'y' value
curr.Y = prev?.y ?? curr.y;
return curr;
});
}
// All vertices should now be updated in the 'meshes'
Note, that the final y value of the vertices depends on the order of the meshes and vertices in your original list. The first vertex in each vGroup is the deciding vertex. I believe it'll be opposite of your approach, where it's the last vertex that's the deciding one, but it doesn't sound like that's important for you.
Furthermore, be aware that in this (and your) approach you are possibly merging two vertices in the same mesh if two vertices have the same x and z values. I don't know if that's intended but I wanted to point it out.
A additional performance optimization would be to parallelize this. Just start out with call to AsParallel:
var vGroups = meshes.AsParallel()
.SelectMany(mesh => mesh.vertices)
.GroupBy(vertex => new { vertex.x, vertex.z });
// ...
Be aware, that parallelization is not always speeding things up if the computation you are trying to parallelize is not that computationally expensive. The overhead from parallelizing it may outweigh the benefits. I'm not sure if the GroupBy operation is heavy enough for it to be beneficial but you'll have to test that out for yourself. Try without it first.
For a simplified example, see this fiddle.
You want to make Y equal for all vertices with the same X and Z. Lets do just that
var yForXZDict = new Dictionary<(int, int), int>();
foreach (var mesh in meshes)
{
foreach (var vertex in mesh.vertices)
{
var xz = (vertex.x, vertex.z);
if (yForXZDict.TryGetValue(xz, out var y))
{
vertex.y = y;
}
else
{
yForXZDict[xz] = vertex.y;
}
}
}
You should replace int to the exact type you use for coordinates
You are comparing twice unnecessarily.
Here a short example of what I mean:
Let's say we have meshes A, B, C.
You are comparing
A, A
A, B
A, C
B, A
B, B
B, C
C, A
C, B
C, C
while this checks e.g. the combination A and B two times.
One first easy improvement would be to use e.g.
for (int i = 0; i < meshes.Count; i++)
{
// only check the current and following meshes
for (int j = i; j < meshes.Count; j++)
{
...
do you even want to compare a mesh with itself? Otherwise you can actually even use j = i + 1 so only compare the current mesh to the next and following meshes.
Then for the vertices it depends. If you actually also want to check the mesh with itself at least you want int n = m + 1 in the case that i == j.
It makes no sense to check a vertex with itself since the condition will always be true.
A next point is minimize accesses
You are accessing e.g.
meshes[i].vertices
five times!
rather get and store it once like e.g.
// To minimize GC it sometimes makes sense to reuse variables outside of a loop
Mesh meshA;
Mesh meshB;
Vector3[] vertsA;
Vector3[] vertsB;
Vector3 vA;
Vector3 vB;
for (int i = 0; i < meshes.Count; i++)
{
meshA = meshes[i];
vertsA = meshA.vertices;
for (int j = i; j < meshes.Count; j++)
{
meshB = meshes[j];
vertsB = meshB.vertices;
for(int m = 0; m < vertsA.Length; m++)
{
vA = vertsA[m];
...
Also note that a line like
meshes[i].vertices[m].y = meshes[j].vertices[n].y;
Actually shouldn't even compile!
The vertices are Vector3 which is a struct so assigning the
meshes[i].vertices[m].y
only changes the value of a returned Vector3 instance but shouldn't in any way change the content of the array.
You would rather work with the vA as mentioned before and at the end assign it back via
vertsA[m] = vA;
and then at the end of the loop assign the entire array back once via
meshA.vertices = vertsA;
And well finally: I would put this into a Thread or use Unity's JobSystem and the burst compiler and meanwhile e.g. display a progress bar or some User feedback instead of freezing the entire application.
Yet another point is floating point precision
you are directly comparing two float values using ==. Due to the floating point precision this might fail even if it shouldn't e.g.
10f * 0.1f == 1f
is not necessarily true. It might be 0.99999999 or 1.0000000001.
Therefore Unity uses only a precision of 0.00001 for Vector3 == Vector3.
You should either do the same and use
if(Mathf.Abs(vA.x - vB.x) <= 0.00001f)`
or use
if(Mathf.Approximately(vA.x, vB.x))
which equals
if(Mathf.Abs(vA.x - vB.x) <= Mathf.Epsilon)`
where Epsilon is the smallest value two floats can differ

Start array search on the END of the (2D) array

I'd like to start the search of an int in my array on the end of the array, not at the beginning. How do I do this?
Please keep in mind that it's a 2D array.
edit someone asked how I search from the start:
In my code file I'm printing a matrix from 8x8 displaying random numbers (1 - 10). The user gives a number to search in the matrix, and the code gives back the position in which the number is FIRST found:
Position position = new Position();
Position LookForNumber(int[,] matrix, int findNumber)
{
for (int r = 0; r < matrix.GetLength(0); r++)
{
for (int c = 0; c < matrix.GetLength(1); c++)
{
if (matrix[r, c] == findNumber)
{
Console.WriteLine("Your number {0} first appears on position {1},{2}", findNumber, r, c);
position.row = r;
position.column = c;
return position;
}
}
}
return position;
}
Now I'd like to find the last position of the number that the user gave up.
To Search a 2D array you would need 2 loops, however you may do the loops in revearse order:
Position LookForNumber(int[,] matrix, int findNumber)
{
Position position = new Position();
for (int r = matrix.GetLength(0) - 1; r >= 0 ; r--)
{
for (int c = matrix.GetLength(1) - 1; c >= 0 ; c--)
{
if (matrix[r, c] == findNumber)
{
Console.WriteLine("Your number {0} first appears on position
{1},{2}", findNumber, r, c);
position.row = r;
position.column = c;
return position;
}
}
}
return null;
}
The fact the array is 2D is irrelevant to your issue.
See how the most regular basic loops works?
for (int i = 0; i < array.length ; i++)
{
...
}
You're essentially doing this, if you had to translate code to english
For all elements, starting with 0 that we're assigning to i, stopping before we reach array.length, and adding 1 to i between each iteration, do the following (...)
So all you have to do really, is change the parameters of the loop.
For example, you could say your starting point (which is i), could be something other than 0. What would you use instead of zero if you want to start at the end of your array?
Then, if you're starting at the end, can you really ADD values to i between each iterations ? Not really, you'd go out of bounds, so what should you do instead of i++ ?
At that point, you need a stopping point if you're iterating from end to start. And the answer lies in the question : my stopping point is the start of the array. So the middle parameter of your loop (which is the stopping point) should be something that represents the start of your array. How would you write that down?
Note that you could also iterate every second element now that you understand the logic, for example.
And the fact you're in a 2D loop just means you need one inner loop inside your main loop, which will work the same way as the outer loop. Just imagine the indexes as a canvas with coordinates, instead of just a line with points.
If you don't understand all I meant I'll just write down the answer, but I think the explanation is clear enough that you will understand :)

C# Loop to scan top,bottom,left,right elements of 2d Array

I am trying to complete a game over condition for a peg solitaire game i am writing.
I have all of the movements and pieces removing as they should.
Each piece is held within a 2d array as an Ellipse UI Ellement and when a piece is taken it is replaced with a border Ui Element.
I have started to use a method adapted from a Stackoverflow post that scans the 8 adjacent squares around the array element.
public static IEnumerable<T> AdjacentElements<T>(T[,] arr, int row, int column)
{
int rows = arr.GetLength(0);
int columns = arr.GetLength(1);
for (int j = row - 1; j <= row + 1; j++)
for (int i = column - 1; i <= column + 1; i++)
if (i >= 0 && j >= 0 && i < columns && j < rows && !(j == row && i == column))
yield return arr[j, i];
}
}
The method called once a piece is taken.
var results = AdjacentElements(grid, 3, 3);
foreach (var result in results)
Debug.WriteLine(result);
When it encounters an ellipse it should check the squares directly above,below,left and right of the Ellipse, at the moment is all 8 squares, i only need four (top, bottom, left and right).
I am using grid reference 3,3 to test at the moment and it is printing out as expected but for all 8 squares.
If any of the four squares in turn encounter and ellipse the next square in a straight line should be a Border in order to be a possible move.
For example:
Circle 1 is the Ellipse being checked.
The circles below,left and right are ignored.
The Cirle 2 is chosen as Square 3 is empty.
This would produce a valid move so the game would continue.
If no valid move is found the game will end.
I not sure how to proceed with this, I was thinking of putting the method call inside a nested for loop to go over every element but I'm thinking it would be very inefficient.
var results = AdjacentElements(grid, i, j);
foreach (var result in results)
//condition here
I don't think i truly understand what you want to do. However, yes you could do nested loops. but sometimes its just easier to poke at it
Given some array x, y
var x = 23;
var y = 3;
Exmaple
var checks = List<Point>();
checks.Add(new Point(x+1,y));
checks.Add(new Point(x-1,y));
checks.Add(new Point(x,y+1));
checks.Add(new Point(x,y-1));
foreach(var check in checks)
//If Bounds check
//do what you need to do

Generate rectangular graph edges dynamically when we know number of vertices

Imagine we have 4 vertices (1,2,3,4), and I save them in array that way:
var vertices = new[] { 1, 2, 3, 4 };
var edges = new[]{Tuple.Create(1,2), Tuple.Create(2,4), Tuple.Create(4,3), Tuple.Create(4,1), Tuple.Create(3,2), Tuple.Create(3,1) };
Now it will look like this:
but what if I have more vertices, say 100? How should i generate it? will the loop be linear or not?
if i use something like this:
List<Tuple<int,int>> edges = new List<Tuple<int, int>>();
for (int i=1; i<vertices.Length-1; i++)
{
edges.Add(Tuple.Create(i, i+1));
}
I will eventually just get outline of the graph right?
I can't figure out how to make intersections, like 1 to 3, 2 to 4.
Or if i have graph that is 100 vertices in width and 50 vertices in height, or is not necessarily rectangle?
If you will need some more information i will add it.
i use something like this…I will eventually just get outline of the graph right?
Correct.
How should i generate it?
If I understand your question correctly, you just want all the possible combinations of vertexes. This is simple:
List<Tuple<int,int>> edges = new List<Tuple<int, int>>();
for (int i = 1; i < vertices.Length - 1; i++)
{
for (int j = i + 1; j < vertices.Length; j++)
{
edges.Add(Tuple.Create(i, j));
}
}
In other words, this is just a variation on the Handshake Problem.
will the loop be linear or not?
As you can see above, definitely not. It is O(n^2).

How to iterate through a list so that the last iteration step goes back to the first object?

I have a list of points that form a plane. I want to create edges between consecutive points and add them to another list.
Here is the code I currently have:
// Get points forming the plate
ArrayList points = part.points;
// Number of points forming the plate
int pointCount = points.Count;
// Create edges
List<LineSegment> edges = new List<LineSegment>();
for (int i = 0; i < pointCount - 1; i++)
{
// Get start and end points
Point start = points[i];
Point end = points[i+1];
// Create edge
LineSegment edge = new LineSegment(start, end);
// Add edge to the list
edges.Add(edge);
}
It doesn't quite work because it doesn't create the last edge between the last and the first points on the list. What would be the way to correct it? I could make it work with an if statement like this:
for (int i = 0; i < pointCount; i++)
{
// Get start and end points
Point start = points[i] as Point;
Point end;
if (i == pointCount-1) end = points[0] as Point;
else end = points[i+1] as Point;
// Rest of the code here
}
But I'm sure there is a more elegant way to do it. In Python I would start the loop from -1 so that the first edge would actually be connecting the last point to the first point, but that is not possible in C#.
EDIT: The points list is given as an ArrayList by the API.
An 'elegant' solution is using a modulo:
for (int i = 0; i < pointCount; i++)
{
…
// for i+1 == pointCount this will yield points[0]
Point end = points[(i+1) % pointCount] as Point;
…
}
However, I believe the if statement you used is more readable.
Note: Use a List<T> instead of ArrayList, too.
Use the modulo operator (which is ´%´ in C#).
for (int i = 0; i < pointCount; i++)
{
// Get start and end points
Point start = points[i] as Point;
Point end = points[(i + 1) % pointCount];
// Rest of the code here
}
I would kill all off all the indices and the explicit loops and describe the problem itself, rather than how to solve it.
var offsetPoints = points.Skip(1).Concat(new[]{points.First()});
List<LineSegment> edges =
points.Zip(offsetPoints, (p1, p2) => new LineSegment(p1, p2)).ToList();
I just create an offset list that skips the first element at the start and adds it at the end so the offset list and the original list are the same length.
For illustration, I'd be starting with some sequence:
{p1, p2, p3, p4, p5}
And producing a sequence:
{p2, p3, p4, p5, p1}
Then I zip them together, creating a line segment from the points that appear at the same position in each sequence.
Continuing with the above example, creating a new sequence from the elements in both using an arbitrary function:
{f(p1, p2), f(p2, p3), f(p3, p4), f(p4, p5), f(p5, p1)}
where f is my supplied zipping function.
This approach requires far less code and is far less likely to lead to indexing bugs.

Categories