How to add a diagonal to a doubly-connected edge list in constant time? - computational-geometry

I'm working through the polygon triangulation algorithm in Computational Geometry:
Algorithms and Applications, 3rd edition, by Mark de Berg and others. The data structure used to represent the polygon is called a "doubly-connected edge list". As the book describes, "[it] contains a record for each face, edge, and vertex". Each edge is actually stored as two "half edges", representing each side of the edge. This makes it easier to walk the half edges around a face. The half edge records look like:
// Pseudocode. No particular language. The properties need to be pointers/references of some kind.
struct HalfEdge {
Vertex origin;
HalfEdge twin;
Face incidentFace;
HalfEdge next;
HalfEdge previous;
}
Now imagine I'm processing this polygon, with one face (so far) called Face1. I need to add a diagonal at the dotted line. This will create a new face (Face2). It seems like I need to walk all those HalfEdges that now surround Face2 and set their incidentFace property to point to Face2.
But the book says:
The diagonals computed for the split and merge vertices are added to the doubly-connected edge list. To access the doubly-connected edge list we use cross-pointers between the edges in the status structure and the corresponding edges in the doubly-connected edge list. Adding a diagonal can then be done in constant time with some simple pointer manipulations.
I don't see any further explanation. I'm not sure what a "cross pointer" means here. The "status structure" refers to a binary search tree of edges that is used to easily find the edge to the left of a given vertex, as the polygon is processed from top to bottom.
Can anyone explain this further? How are they able to update all the edges in constant time?

The same book (page 33) says:
Even more important is to realize that in
many applications the faces of the subdivision carry no interesting meaning
(think of the network of rivers or roads that we looked at before). If that is the case, we can completely forget about the face records, and the IncidentFace() field of half-edges. As we will see, the algorithm of the next section doesn’t need these fields (and is actually simpler to implement if we don’t need to update them).
So, you don't need to modify the incidentFace field in any half-edges after each diagonal insertion - you will be able to find vertices of all the monotone polygons using the next field of the HalfEdge record after all the necessary diagonals are inserted into the DCEL.
The most intricate job here is to set next and prev fields of newly inserted half-edges, and to modify these fields in existing half-edges. Exactly four existing half-edges need to be updated after each insertion - but it's not easy to find them.
As you already know, each DCEL vertex record contains a field, pointing to any half-edge, originating in this vertex. If you keep this field pointing to an internal half-edge or most recently inserted half-edge, then it'll be a little bit easier to find half-edges, which need updates in their next and prev fields after each insertion.

Related

Adding edges dynamically in a DCEL/half-edge based graph?

I'm trying to implement a vector graphics "drafting" system, where, in essence, users can draw lines on the screen and interact with the regions created by intersecting lines. I'm struggling with determining/evaluating what these regions are.
I've tried a few different solutions to this problem, chiefly keeping a list of edges and running a BFS to find shortest cycles, but this brought up a myriad of issues where the BFS would shortcut in illegal ways, and holes and degenerate edges caused more problems than I could count, so I moved on to a DCEL, half-edge system.
I've read seemingly everything I can on this topic, including two articles referenced frequently on here: http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm and http://www.flipcode.com/archives/The_Half-Edge_Data_Structure.shtml. However, neither of these seem to answer this problem that I have when it comes to dynamically adding edges to the graph.
Let's say I start out with this single edge. Image
The half-edges connect with each other in a cycle, and the global, unbounded "outside face" is connected to one of the half edges. Easy, got that.
Then we add another edge, attached to the center vertex: Image
The new half-edges work fine, and we update the edges that flow into v1's next pointers to be the only other edges available that aren't their twins. Again, makes sense to me.
What confuses me to no end is what happens here, when we add a third edge to the center vertex: Image
I know that's what it's supposed to look like and link up to be, but I am so bewildered on how to achieve that programmatically, because I'm not sure how I can determine whether the edge (4,1) should point to edge (1,2), or edge (1,3) (similarly for what edge should point to (1,4)).
The answer seems obvious when looking at the image, but when you try to rationalize it in a robust, airtight algorithmic way, my brain melts and I can't figure it out. The textbook I'm reading (Computational Geometry, Mark de Berg et al., pg 35), just says
"[to test where the edge] should be in the cyclic order of the edges
around vertex v".
The algorithm given in the hilvi.org article for finding the outgoing and incoming edges to link up with doesn't even seem to work, as it will take vertex 1, and follow its outgoing edge's twin until it finds a "free" edge, which in this case, is (2,1) which is wrong. (Unless I'm understanding it incorrectly, I could be understanding this whole problem wrong.)
So I'm absolutely stumped. My only idea now is to create some sort of heading attribute for each half edge, where I measure the angle created by the edge, and choose the edges that way, and maybe that's right, but that seems so against what the half-edge structure seems to support, at least in the articles I'm reading about it, nothing seems to mention anything like that. Any help would be extremely appreciated. I've been on this problem for over a week now and just can't seem to get unstuck.
Right, so I've spent a lot of time thinking about this problem, and to be honest I'm kinda surprised that I can't find a direct answer to this issue. So, in case anyone in the future runs into a similar problem of wanting to populate a half-edge graph from the ground up, here's a solution that works. I don't have a blog, so I'm writing it here.
I have no idea if it's the best answer, but it works in linear time and seems simple to me.
I'll be dealing with the following objects/classes, that vary slightly from the conventional DCEL:
class Vertex {
x;
y;
edges = []; //A list of all Half Edges with their origin at this vertex.
//Technically speaking this could be calculated as needed,
and you could just keep a single outgoing edge, but I'm not
in crucial need of space in my application so I'm just
using an array of all of them.
}
class HalfEdge {
origin; //The Vertex which this half-edge emanates from
twin; // The half-edge pair to this half-edge
face; // The region/face this half-edge is incident to
next; // The half-edge that this half-edge points to
prev; // The half-edge that points to this half-edge
angle; //The number of degrees this hedge is CW from the segment (0, 0) -> (inf, 0)
}
class Face {
outer_edge; //An arbitrary half-edge on the outer boundary defining this face.
inner_edges = []; //A collection of arbitrary half-edges, each defining
//A hole in the face.
global; //A boolean describing if the face is the global face or not.
//This could also be done by having a single "global face" Face instance.
//This is simply how I did it.
}
For initializing a vertex at(x,y):
Verify that a vertex with the given (x,y) coordinates does not already exist. If it does, you don't have to do anything (except maybe return this existing vertex if you're using it immediately).
If it doesn't, allocate space for and create a new vertex with the corresponding x,y values, and with its incident edge null.
For initializing an edge from vertex A to vertex B:
Similarly to many of the articles about this topic, we create two new instances of HalfEdge, one from vertex A to B, one from B to A. They link to each other in that we set their twin, prev, and next pointers all to the other half-edge (hedge).
We also set the angle of the hedge. The angle is calculated clockwise from the positive x-axis. The function I implemented is below. This is super important to making this data structure work properly, and that fact that I haven't read anything in the literature about this being important makes me think there has to be a better way, but I digress.
setAngle(){
const dx = this.destination().x - this.origin.x;
const dy = this.destination().y - this.origin.y;
const l = Math.sqrt(dx * dx + dy * dy);
if (dy > 0) {
this.angle = toDeg(Math.acos(dx / l));
} else {
this.angle = toDeg(Math.PI * 2 - Math.acos(dx / l));
}
function toDeg(rads) {
return 180 * rads / Math.PI;
}
}
Next, we pair the vertices with their new edges by adding them to their Vertex's edge list, and then we sort the edge list by the hedge's angles from smallest (0) to largest (359).
Then and this is the crucial step, in order to link everything up properly, we grab the closest hedge to the new hedge we're trying to link up in CCW order. Basically, wherever our new hedge ends up in the edge list, it's that index - 1 (if index = 0, we return edges[edges.length - 1]). Take that edge's twin, and that becomes our AIn described in the hivli article above. BOut = AIn.next.
We set AIn.next = hedgeAB and similarly, hedgeAB.prev = AIn, then hedgeBA.next = AOut, AOut.prev = hedgeBA. Perform steps 3-5 for the hedgeBA as well, except running the CCW search on vertex B.
Then, if both vertex A and B were "old" vertices, meaning their edge lists now have at least 2 elements each, a new face has potentially been added, and we need to find it (the edge case is having two isolated edges and connecting them to create an unbounded bucket or cap shape)
For initializing a Face:
We need to find all the cycles in a graph. For my first implementation of this, I recalculated all of the cycles every single time, resetting all the faces. This isn't necessary, but it also isn't too expensive, since we're not running a search, everything is in linear time with respect to the number of cycles and number of vertices in each cycle.
To do this, we get a list of all the hedges in the graph. It doesn't really matter how you do this, I decided to keep an array of every hedge that I passed into my cycle-finder function each time.
Then we look through that list, while the list isn't empty, we take the first item and run its cycle, removing every hedge we find along the way from the list, and adding it into a new cycle, which we add to another list
With this new list of cycles, we need to determine whether the cycle is an inside/outside cycle. There are a lot of ways to do this, and the Computational Geometry book mentioned above has a great section about it. The one that I used is to calculate the area defined by each cycle. If the area is >= 0, the cycle is defined by "inside" hedges. Otherwise, it's defined by "outside" hedges.
The last step is to set all the face records, again, the aforementioned textbook has a lot of great details about this, but the basic idea is to basically create a virtual "graph" of these cycles, and connect outside cycles (which are holes in faces), to their corresponding inside cycles, which are the outer boundaries of faces. To do this, you look at the leftmost vertex of the cycle and extend a ray out infinitely to the left, and "connect" the cycle with the first downward-facing hedge of a cycle the ray hits (I'll leave the implementation up to you, I have no idea if my way is the best, in short, I checked every cycle with a leftmost vertex left of the current cycle and calculated the rightmost intersection with the y value of the leftmost vertex of the current cycle, and then checked if that was facing downward).
With this graph of cycles, run a BFS/DFS starting from each "inside-hedge" cycle (not the holes), and create a face with an arbitrary hedge from the inside-hedge cycle as the outer edge, (or null if it's the global face), and an arbitrary hedge from each found hole-cycle to the face's inner components.
Hey presto, that's it. If you check everything every time, that handles everything. It handles face splitting like a charm and is very robust and quick. I don't know if it's right, but it works.

How to find the covering polygon if I know a point all the lines around it

I have a collection of lines in my diagram and I also have a point. What I want is a collection of lines which will together form a polygon through a ordered traversal. I don't need implementation or anything all I want is someone to direct me towards the algorithm I can use.
Similar problem like this have been asked but won't work for me because
One of the common asked problems is that given a polygon I need to find whether the point lies inside it or not but this won't work for me because I don't have any polygons I only have a collection of lines.
the final polygon can be convex too so simply drawing rays on every side from that point and finding intersections won't work I need something more advanced.
Sorry for all the confusion : See this for clarity https://ibb.co/nzbxGF
You need to store your collection of segments inside a suitable data structure. Namely, the chosen data structure should support the concept of faces, as you're looking for a way to find the face in which a given point resides. One such data structure is the Doubly Connected Edge List.
The Doubly Connected Edge List is a data structure that holds a subdivision of the plane. In particular, it contains a record for each face, edge, and vertex of the subdivision. It also supports walking around a face counterclockwise, which allows you to know which segments bound a particular face (such as the face containing the point you're searching for).
You can use a Sweep Line Algorithm to construct the Doubly Connected Edge List in O(nlog(n)+klog(n)) where n is the number of segments and k is the complexity of the resulting subdivision (the total number of vertices, edges, and faces). You don't have to code it from scratch as this data structure and its construction algorithm have already been implemented many times (you can use CGAL's DCEL implementation for example).
With the Doubly Connected Edge List data structure you can solve your problem by applying the approach you've suggested in your post: given an input point, solve the Point in Polygon problem for each face in the Doubly Connected Edge List and return the set of segments bounding the face you've found. However, this approach, while might be good enough for somewhat simple subdivisions, is not efficient for complex ones.
A better approach is to transform the Doubly Connected Edge List into a data structure that is specialized in point location queries: The Trapezoidal Map. This data structure can be constructed in O(nlog(n)) expected time and for any query point the expected search time is O(log(n)). As with the Doubly Connected Edge List, you don't have to implement it yourself (again, you can use CGAL's Trapezoidal Map implementation).

Detecting a cycle given line segments

I'm working on an implementation of the Slab Decomposition algorithm, for point location and I'm stuck in one of its final steps.
I have a list of vertexes and also a list of the vertexes' neighbors (for example, neighbor[0] has all vertexes that are connected to the vertex 0).
I can create the slabs described in the algorithm just fine, but after I detect between which line segments a point is, I don't know how to get the whole partition/cycle/face the point is in.
Basically, I have this
And this is what I want
I could just try to detect all cycles, in a brute force manner, but efficiency is important here. Any idea on how I should approach this problem?
All vertexes and line segments come from an input file, so I could order them in a certain way if it helps with the detection.
Thanks in advance
You could store the planar straight-line graph as a doubly connected edge list. The pertinent feature of the DCEL representation is that the base object be a half-edge (each segment gives rise to two oppositely oriented half-edges, with a tail and a head) with two operations:
DNext(HalfEdge e) - returns the next half-edge with the same head as e
in counterclockwise order around the head
Sym(HalfEdge e) - returns the oppositely oriented half-edge
corresponding to the same edge.
Then you can iterate through the half-edges comprising the face with e = Sym(DNext(e)) until e returns to its starting value.
To compute the DCEL representation in the first place is a matter of sorting the half-edges by angle and then linking them together. There's a way to compare the angles of two half-edges using a 2x2 determinant calculation (avoiding an arctangent, if that's relevant to you).

Nearest neighbour search in a constantly changing set of line segments

I have a set of line segments. I want to perform the following operations on them:
Insert a new line segment.
Find all line segments within radius R of a given point.
Find all points within radium R1 of a given point.
Given a line segment, find if it intersects any of the existing line segments. Exact intersection point is not necessary(though that probably doesnt simplify anything.)
The problem is algorithms like kd/bd tree or BSP trees is that they assume a static set of points, and in my case the points will constantly get augmented with new points, necessitating rebuilding the tree.
What data-structure/algorithms will be most suited for this situation ?
Edit: Accepted the answer that is the simplest and solves my problem. Thanks everyone!
Maintaining a dynamic tree might not be as bad as you think.
As you insert new points/lines etc into the collection it's clear that you'd need to refine the current tree, but I can't see why you'd have to re-build the whole tree from scratch every time a new entity was added, as you're suggesting.
With a dynamic tree approach you'd have a valid tree at all times, so you can then just use the fast range searches supported by your tree type to find the geometric entities you've mentioned.
For your particular problem:
You could setup a dynamic geometric tree, where the leaf elements
maintain a list of geometric entities (points and lines) associated
with that leaf.
When a line is inserted into the collection it should be pushed onto
the lists of all leaf elements that it intersects with. You can do
this efficiently by traversing the subset of the tree from the root
that intersects with the line.
To find all points/lines within a specified radial halo you first need
to find all leaves in this region. Again, this can be done by traversing
the subset of the tree from the root that is enclosed by, or that
intersects with the halo. Since there maybe some overlap, you need to
check that the entities associated with this set of leaf elements
actually lie within the halo.
Once you've inserted a line into a set of leaf elements, you can find
whether it intersects with another line by scanning all of the lines
associated with the subset of leaf boxes you've just found. You can then
do line-line intersection tests on this subset.
A potential dynamic tree refinement algorithm, based on an upper limit to the number of entities associated with each leaf in the tree, might work along these lines:
function insert(x, y)
find the tree leaf element enclosing the new entitiy at (x,y) based on whatever
fast search algorithm your tree supports
if (number of entities per leaf > max allowable) do
refine current leaf element (would typically either be a bisection
or quadrisection based on a 2D tree type)
push all entities from the old leaf element list onto the new child element
lists, based on the enclosing child element
else
push new entity onto list for leaf element
endif
This type of refinement strategy only makes local changes to the tree and is thus generally pretty fast in practice. If you're also deleting entities from the collection you can also support dynamic aggregation by imposing a minimum number of entities per leaf, and collapsing leaf elements to their parents when necessary.
I've used this type of approach a number of times with quadtrees/octrees, and I can't at this stage see why a similar approach wouldn't work with kd-trees etc.
Hope this helps.
One possibility is dividing your space into a grid of boxes - perhaps 10 in the y-axis and 10 in the x-axis for a total of 100.
Store these boxes in an array, so it's very easy/fast to determine neighboring boxes. Each box will hold a list vector of line segments that live in that box.
When you calculate line segments within R of one segment, you can check only the relevant neighboring boxes.
Of course, you can create multiple maps of differing granularities, maybe 100 by 100 smaller boxes. Simply consider space vs time and maintenance trade-offs in your design.
updating segment positions is cheap: just integer-divide by box sizes in the x and y directions. For example, if box-size is 20 in both directions and your new coordinate is 145,30. 145/20==7 and 30/20==1, so it goes into box(7,1), for a 0-based system.
While items 2 & 3 are relatively easy, using a simple linear search with distance checks as each line is inserted, item 4 is a bit more involved.
I'd tend to use a constrained triangulation to solve this, where all the input lines are treated as constraints, and the triangulation is balanced using a nearest neighbour rather than Delaunay criterion. This is covered pretty well in Triangulations and applications by Øyvind Hjelle, Morten Dæhlen and Joseph O'Rourkes Computational Geometry in C Both have source available, including getting sets of all intersections.
The approach I've taken to do this dynamically in the past is as follows;
Create a arbitrary triangulation (TIN) comprising of two triangles
surrounding the extents (current + future) of your data.
For each new line
Insert both points into the TIN. This can be done very quickly by
traversing triangles to find the insertion point, and replacing the
triangle found with three new triangles based on the new point and
old triangle.
Cut a section through the TIN based on the two end points, keeping a
list of points where the section cuts any previously inserted lined.
Add the intersection point details to a list stored against both
lines, and insert them into the TIN.
Force the inserted line as a constraint
Balance all pairs of adjacent triangles modified in the above process
using a nearest neighbour criterion, and repeat until all triangles
have been balanced.
This works better than a grid based method for poorly distributed data, but is more difficult to implement. Grouping end-point and lines into overlapping grids will probably be a good optimization for 2 & 3.
Note that I think using the term 'nearest neighbour' in your question is misleading, as this is not the same as 'all points within a given distance of a line', or 'all points within a given radius of another point'. Nearest neighbour typically implies a single result, and does not equate to 'within a given point to point or point to line distance'.
Instead of inserting and deleting into a tree you can calculate a curve that completely fills the plane. Such a curve reduce the 2d complexity to a 1d complexity and you would be able to find the nearest neighbor. You can find some example like z curve and hilbert curve. Here is a better description of my problem http://en.wikipedia.org/wiki/Closest_pair_of_points_problem.

Suggestions on speeding up edge selection

I am building a graph editor in C# where the user can place nodes and then connect them with either a directed or undirected edge. When finished, an A* pathfinding algorithm determines the best path between two nodes.
What I have: A Node class with an x, y, list of connected nodes and F, G and H scores.
An Edge class with a Start, Finish and whether or not it is directed.
A Graph class which contains a list of Nodes and Edges as well as the A* algorithm
Right now when a user wants to select a node or an edge, the mouse position gets recorded and I iterate through every node and edge to determine whether it should be selected. This is obviously slow. I was thinking I can implement a QuadTree for my nodes to speed it up however what can I do to speed up edge selection?
Since users are "drawing" these graphs I would assume they include a number of nodes and edges that humans would likely be able to generate (say 1-5k max?). Just store both in the same QuadTree (assuming you already have one written).
You can easily extend a classic QuadTree into a PMR QuadTree which adds splitting criteria based on the number of line segments crossing through them. I've written a hybrid PR/PMR QuadTree which supported bucketing both points and lines, and in reality it worked with a high enough performance for 10-50k moving objects (rebalancing buckets!).
So your problem is that the person has already drawn a set of nodes and edges, and you'd like to make the test to figure out which edge was clicked on much faster.
Well an edge is a line segment. For the purpose of filtering down to a small number of possible candidate edges, there is no harm in extending edges into lines. Even if you have a large number of edges, only a small number will pass close to a given point so iterating through those won't be bad.
Now divide edges into two groups. Vertical, and not vertical. You can store the vertical edges in a sorted datastructure and easily test which vertical lines are close to any given point.
The not vertical ones are more tricky. For them you can draw vertical boundaries to the left and right of the region where your nodes can be placed, and then store each line as the pair of heights at which the line intersects those lines. And you can store those pairs in a QuadTree. You can add to this QuadTree logic to be able to take a point, and search through the QuadTree for all lines passing within a certain distance of that point. (The idea is that at any point in the QuadTree you can construct a pair of bounding lines for all of the lines below that point. If your point is not between those lines, or close to them, you can skip that section of the tree.)
I think you have all the ingredients already.
Here's a suggestion:
Index all your edges in a spatial data structure (could be QuadTree, R-Tree etc.). Every edge should be indexed using its bounding box.
Record the mouse position.
Search for the most specific rectangle containing your mouse position.
This rectangle should have one or more edges/nodes; Iterate through them, according to the needed mode.
(The tricky part): If the user has not indicated any edge from the most specific rectangle, you should go up one level and iterate over the edges included in this level. Maybe you can do without this.
This should be faster.

Resources