Find out how to flood fill a polygon with the smallest number of vector lines - algorithm

Say I have a vector polygon with holes. I need to flood fill it by drawing connected segments. Of course, since there are holes, I can't fill it using a single continous polyline: I'll need to interrupt my path sometimes, then move to an area which was skipped and start another polyline there.
My goal is to find a set of polylines needed to fill the whole polygon. Better if I can find the smallest set (that is, the way I can fill the polygon with the minimum number of interruptions).
Bonus question: how could I do that for partial density fills? Say, I don't want to fill at 100% density but I want a 50% (this will require that fill lines, supposing they're parallel each other and have a single-unit width, are put at a distance of two units).
I couldn't find a similar question here, although there are many related to flood-fill algorithms.
Any ideas or pointers?
Update: this picture from Wikipedia shows a good hypotetical flood path. I believe I could do that using a bitmap. However I've got a vector polygon. Should I rasterize it?

I'm assuming here that the distance between lines is 1 unit.
A crude implementation, with no guarantee to find the minimum number of polyline, is:
Start with an empty set of polylines.
Determine minx and maxx of the polygon.
Loop x from xmin to xmax, with a step of 1. Line L is the vertical line at x.
Intersect vertical line L with your polygon (quick algorithm, easy to find). That will give you a set of segments: {(x,y1)-(x,y2)}.
For all polylines, and all segments, merge segment + end of polylines (see note 1 below). When you merge a segment and a polyline, append a small stretch at the end of the polyline (to joint it to the segment), and the segment itself. For all segments that you can't merge using that, add a new polyline in the global set.
At the end, try to merge again polylines if possible (ends close together).
Optimal algorithm for merging new segments to existing polylines should be easy to find (hashing on y), or a brute force algorithm may suffice:
number of new segments per line scan should not be too high if your polygons do not have zillions of holes,
number of global polylines at every step should not be too large,
you compare only with the end segment of each polylines, not the whole of it.
Added note (1): To cover the case where your polygon has nearly-vertical edges, the merge process should not look only at y-delta, but allow a merge if any two y range overlaps (that means end of polyline y-range overlap segment y-range).

Related

Find the rectangle with the maximum area, containing a specific point in an occupancy grid

Problem
Given an occupancy grid, for example:
...................*
*...............*...
*..*.............*..
...........*........
....................
..*.......X.........
............*.*.*...
....*..........*....
...*........*.......
..............*.....
Where, * represents an occupied block, . represents a free block and X represents a point (or block) of interest, what is the most time-efficient algorithm to find the largest rectangle which includes X, but does not include any obstacles, i.e. any *?
For example, the solution to the provided grid would be:
.....######........*
*....######.....*...
*..*.######......*..
.....######*........
.....######.........
..*..#####X.........
.....######.*.*.*...
....*######....*....
...*.######.*.......
.....######...*.....
My Thoughts
Given we have a known starting point X, I can't help but think there must be a straightforwards solution to "snap" lines to the outer boundaries to create the largest rectangle.
My current thinking is to snap lines to the maximum position offsets (i.e. go to the next row or column until you encounter an obstacle) in a cyclic manner. E.g. you propagate a horizontal line from the point X down until there is a obstacle along that line, then you propagate a vertical line left until you encounter an obstacle, then a horizontal line up and a vertical line right. You repeat this starting at with one of the four moving lines to get four rectangles, and then you select the rectangle with the largest area. However, I do not know if this is optimal, nor the quickest approach.
This problem is a well-known one in Computational Geometry. A simplified version of this problem (without a query point) is briefly described here. The problem with query point can be formulated in the following way:
Let P be a set of n points in a fixed axis-parallel rectangle B in the plane. A P-empty rectangle (or just an empty rectangle for short) is any axis-parallel rectangle that is contained in
B and its interior does not contain any point of P. We consider the problem of preprocessing
P into a data structure so that, given a query point q, we can efficiently find the largest-area
P-empty rectangle containing q.
The paragraph above has been copied from this paper, where authors describe an algorithm and data structure for the set with N points in the plane, which allow to find a maximal empty rectangle for any query point in O(log^4(N)) time. Sorry to say, it's a theoretic paper, which doesn't contain any algorithm implementation details.
A possible approach could be to somehow (implicitly) rule out irrelevant occupied cells: those that are in the "shadow" of others with respect to the starting point:
0 1 X
01234567890123456789 →
0....................
1....................
2...*................
3...........*........
4....................
5..*.......X.........
6............*.......
7....*...............
8....................
9....................
↓ Y
Looking at this picture, you could state that
there are only 3 relevant xmin values for the rectangle: [3,4,5], each having an associated ymin and ymax, respectively [(3,6),(0,6),(0,9)]
there are only 3 relevant xmax values for the rectangle: [10,11,19], each having an associated ymin and ymax, respectively [(0,9),(4,9),(4,5)]
So the problem can be reduced to finding the rectangle with the highest area out of the 3x3 set of unique combinations of xmin and xmax values
If you take into account the preparation part of selecting relevant occupied cells, this has the complexity of O(occ_count), not taking into sorting if this would still be needed and with occ_count being the number of occupied cells.
Finding the best solution (in this case 3x3 combinations) would be O(min(C,R,occ_count)²). The min(C,R) includes that you could choose the 'transpose' the approach in case R<C, (which is actually true in this example) and that that the number of relevant xmins and xmaxs have the number of occupied cells as an upper limit.

CGAL arrangements: compute the ordered intersection of a polyline with a grid

Given a general polyline and an orthogonal grid, I would like to compute a simpler polyline whose vertices lie on the grid edges/vertices. This can look like this:
Left: A dense polyline as input, Right: A coarser polyline whose vertices lie on the intersection of the input polyline with the grid edges/vertices
(Sorry about the link to the image, but stack overflow apparently doesn't allow me to embed pictures before getting 10 credit points).
The grid is always orthogonal but its vertices do not necessarily have integer coordinates as some x or y lines might have coordinates defined by a previous geometric intersection computation. The initial curve can be represented as a polyline (though it would be nice to have also bezier curve support), not necessarily x-monotone, and it might intersect the grid also along whole edges.
My first thought was to call CGAL::compute_subcurves(..) with the grid lines and the curve I'm adding. I was hoping to get back a list of polylines, each composed of maximal multiple segments inside a face of the original grid. In practice even if the input is composed of polylines and the output of monotone polylines, I get back a list of separated segments. These include also the grid segments and also the polyline segments, and these are not ordered by "walking on the curve segments" as needed to compute the ordered interesection points. If they would have been ordered, a solution would be to iteratively go over them and check which one intersects the original grid, and then save the points.
Another option I thought of is to start with an arrangement of the grid lines, incrementally add polyline segements and have a notification mechanism notifying me on new edges that are pairwise disjoint in their interior, but in the case an edge of the intersected polylines is an original edge of the grid I won't get a notification and I'll miss it. Incrementally adding segments and checking for collisions also doesn't seem to be straightforward as the arrangement API do_intersect(..) seems to return at most a single point, while a given segment of the input polyline might easily intersect two grid lines next to a corner or even lie entirely on a grid segment.
I'm probably missing some simple solution. Can someone give me a pointer, or some api call that might help here?
Edit: There might have been a confusion. The grid is orthogonal but not necessarily regular and might have coordinates that could not globally scale to integers such as here.
Use Arrangement_with_history_2 (instead of Arrangement_2); see https://doc.cgal.org/latest/Arrangement_on_surface_2/classCGAL_1_1Arrangement__with__history__2.html. After you compute the arrangement, you can use point location to locate the end points of your polylines in the arrangement. Then, for each one, you can easily follow the original curve. If you are concerned with performance, you can try inserting (at least) the grid lines incrementally. Another option is to extend the halfedge records and insert the grid lines and each polyline separately. With the appropriate observer, you can mark the generated halfedges that correspond to a given polyline uniquely. I think you can even save the extra point location, by locating one of the two end points of a polyline before inserting it, and then provide the resulting location to the (incremental) insertion function.

Approximate a curve with a limited number of line segments and arcs of circles

Is there any algorithm that would allow to approximate a path on the x-y plane (i.e. an ordered suite of points defined by x and y) with a limited number of line segments and arcs of circles (constant curvature)? The resulting curve needs to be C1 (continuity of slope).
The maximum number or segments and arcs could be a parameter. An additional interesting constraint would be to prevent two consecutive circles of arcs without an intermediate line segment joining them.
I do not see any way to do this, and I do not think that there exists a method for it, but any hint towards this objective is welcome.
Example:
Sample file available here
Consider this path. It looks like a line, but is actually an ordered suite of very close points. There is no noise and the order of the sequence of points is well known.
I would like to approximate this curve with a minimum number of succession of line segments and circular arcs (let's say 10 line segments and 10 circular arcs) and a C1 continuity. The number of segments/arcs is not an objective itself but I need any parameter which would allow to reduce/increase this number to attain a certain simplicity of the parametrization, at the cost of accuracy loss.
Solution:
Here is my solution, based on Spektre's answer. Red curve is original data. Black lines are segments and blue curves are circle arcs. Green crosses are arc centers with radii shown and blue ones are points where segments potentially join.
Detect line segments, based on slope max deviation and segment minimal length as parameters. The slope of the new segment step is compared with the average step of the existing segment. I would prefer an optimization-based method, but I do not think that it exists for disjoint segments with unknown number, position and length.
Join segments with tangent arcs. To close the system, the radius is chosen such that the segments extremities are the least possible moved. A minimum radius constraint has been added for my purposes. I believe that there will be some special cases to treat in the inflexion points are far away when (e.g. lines are nearly parallel) and interact with neigboring segments.
so you got a point cloud ... for such Usually points close together are considered connected so:
you need to add info about what points are close to which ones
points close only to 2 neighbors signaling interior of curve/line. Only one neighbor means endpoint of curve/lines and more then 2 means intersection or too close almost or parallel lines/curves. No neighbors means either noise or just a dot.
group path segments together
This is called connected component analysis. So you need to form polylines from your neighbor info table.
detect linear path chunks
these have the same slope among neighboring segments so you can join them to single line.
fit the rest with curves
Here related QAs:
Finding holes in 2d point sets?
Algorithms: Ellipse matching
How approximation search works see the sublinks there are quite a bit of examples of fitting
Trace a shape into a polygon of max n sides
[Edit1] simple line detection from #3 on your data
I used 5.0 deg angle change as threshold for lines and also minimal size fo detected line as 50 samples (too lazy to compute length assuming constant point density). The result looks like this:
dots are detected line endpoints, green lines are the detected lines and white "lines" are the curves so I do not see any problem with this approach for now.
Now the problem is with the points left (curves) I think there should be also geometric approach for this as it is just circular arcs so something like this
Formula to draw arcs ending in straight lines, Y as a function of X, starting slope, ending slope, starting point and arc radius?
And this might help too:
Circular approximation of polygon (or its part)
the C1 requirement demands the you must have alternating straights and arcs. Also realize if you permit a sufficient number of segments you can trivially fit every pair of points with a straight and use a tiny arc to satisfy slope continuity.
I'd suggest this algorithm,
1 best fit with a set of (specified N) straight segments. (surely there are well developed algorithms for that.)
2 consider the straight segments fixed and at each joint place an arc. Treating each joint individually i think you have a tractable problem to find the optimum arc center/radius to satisfy continuity and improve the fit.
3 now that you are pretty close attempt to consider all arc centers and radii (segments being defined by tangency) as a global optimization problem. This of course blows up if N is large.
A typical constraint when approximating a given curve by some other curve is to bound the approximate curve to an epsilon-hose within the original curve (in terms if Minkowski sum with a disk of fixed radius epsilon).
For G1- or C2-continuous approximation (which people from CNC/CAD like) with biarcs (and a straight-line segment could be seen as an arc with infinite radius) former colleagues of mine developed an algorithm that gives solutions like this [click to enlarge]:
The above picture is taken from the project website: https://www.cosy.sbg.ac.at/~held/projects/apx/apx.html
The algorithm is fast, that is, it runs in O(n log n) time and is based on the generalized Voronoi diagram. However, it does not give an approximation with the exact minimum number of elements. If you look for the theoretical optimum I would refer to a paper by Drysdale et al., Approximation of an Open Polygonal Curve with
a Minimum Number of Circular Arcs and Biarcs, CGTA, 2008.

Algorithm for the decomposition of polygons

Does anyone know a relatively fast algorithm for decomposing a set of polygons into their distinct overlapping and non-overlapping regions, i.e. Given a set of n polygons, find all the distinct regions among them?
For instance, the input would be 4 polygons representing circles as shown below
and the output will be all polygons representing the distinct regions shown in the different colours.
I can write my own implementation using polygon operations but the algorithm will probably be slow and time consuming. I'm wondering if there's any optimised algorithm out there for this sort of problem.
Your problem in called the map overlay problem. It can be solved in O(n*log(n)+k*log(k)) time, where n is the number of segments and k is the number of segment intersections.
First you need to represent your polygons as a doubly connected edge list, different faces corresponding to the interiors of different polygons.
Then use the Bentley–Ottmann algorithm to find all segment intersections and rebuild the edge list. See: Computing the Overlay of Two Subdivisions or Subdivision representation and map overlay.
Finally, walk around each cycle in the edge list and collect faces of that cycle's half-edges. Every set of the faces will represent a distinct overlapping region.
See also: Shapefile Overlay Using a Doubly-Connected Edge List.
I don't think it is SO difficult.
I have answered the similar question on the friendly site and it was checked by a smaller community:
https://cs.stackexchange.com/questions/20039/detect-closed-shapes-formed-by-points/20247#20247
Let's look for a more common question - let's take curves instead of polygons. And let's allow them to go out of the picture border, but we'll count only for simple polygons that wholly belong to the picture.
find all intersections by checking all pairs of segments, belonging to different curves. Of course, filter them before real check for intersection.
Number all curves 1..n. Set some order of segments in them.
For every point create a sequence of intersections SOI, so: if it starts from the border end, SOI[1] is null. If not, SOI[1]= (number of the first curve it is intersecting with, the sign of the left movement on the intersecting curve). Go on, writing down into SOI every intersection - number of curve if there is some, or 0 if it is the intersection with the border.
Obviously, you are looking only for simple bordered areas, that have no curves inside.
Pieces of curves between two adjacent non-null intersection points we'll call segments.
Having SOI for each curve:
for segment of the curve 1, starting from the first point of the segment, make 2 attempts to draw a polygon of segments. It is 2 because you can go to 2 sides along the first intersecting curve.
For the right attempt, make only left turns, for the left attempt, make only the right turns.
If you arrive at point with no segment in the correct direction, the attempt fails. If you return to the curve 1, it success. You have a closed area.
Remember all successful attempts
Repeat this for all segments of curve 1
Repeat this for all other curves, checking all found areas against the already found ones. Two same adjacent segments is enough to consider areas equal.
How to find the orientation of the intersection.
When segment p(p1,p2) crosses segment q(q1,q2), we can count the vector multiplication of vectors pXq. We are interested in only sign of its Z coordinate - that is out of our plane. If it is +, q crosses p from left to right. If it is -, the q crosses p from right to left.
The Z coordinate of the vector multiplication is counted here as a determinant of matrix:
0 0 1
p2x-p1x p2y-p1y 0
q2x-q1x q2y-q1y 0
(of course, it could be written more simply, but it is a good memorization trick)
Of course, if you'll change all rights for lefts, nothing really changes in the algorithm as a whole.

Randomly and efficiently filling space with shapes

What is the most efficient way to randomly fill a space with as many non-overlapping shapes? In my specific case, I'm filling a circle with circles. I'm randomly placing circles until either a certain percentage of the outer circle is filled OR a certain number of placements have failed (i.e. were placed in a position that overlapped an existing circle). This is pretty slow, and often leaves empty spaces unless I allow a huge number of failures.
So, is there some other type of filling algorithm I can use to quickly fill as much space as possible, but still look random?
Issue you are running into
You are running into the Coupon collector's problem because you are using a technique of Rejection sampling.
You are also making strong assumptions about what a "random filling" is. Your algorithm will leave large gaps between circles; is this what you mean by "random"? Nevertheless it is a perfectly valid definition, and I approve of it.
Solution
To adapt your current "random filling" to avoid the rejection sampling coupon-collector's issue, merely divide the space you are filling into a grid. For example if your circles are of radius 1, divide the larger circle into a grid of 1/sqrt(2)-width blocks. When it becomes "impossible" to fill a gridbox, ignore that gridbox when you pick new points. Problem solved!
Possible dangers
You have to be careful how you code this however! Possible dangers:
If you do something like if (random point in invalid grid){ generateAnotherPoint() } then you ignore the benefit / core idea of this optimization.
If you do something like pickARandomValidGridbox() then you will slightly reduce the probability of making circles near the edge of the larger circle (though this may be fine if you're doing this for a graphics art project and not for a scientific or mathematical project); however if you make the grid size 1/sqrt(2) times the radius of the circle, you will not run into this problem because it will be impossible to draw blocks at the edge of the large circle, and thus you can ignore all gridboxes at the edge.
Implementation
Thus the generalization of your method to avoid the coupon-collector's problem is as follows:
Inputs: large circle coordinates/radius(R), small circle radius(r)
Output: set of coordinates of all the small circles
Algorithm:
divide your LargeCircle into a grid of r/sqrt(2)
ValidBoxes = {set of all gridboxes that lie entirely within LargeCircle}
SmallCircles = {empty set}
until ValidBoxes is empty:
pick a random gridbox Box from ValidBoxes
pick a random point inside Box to be center of small circle C
check neighboring gridboxes for other circles which may overlap*
if there is no overlap:
add C to SmallCircles
remove the box from ValidBoxes # possible because grid is small
else if there is an overlap:
increase the Box.failcount
if Box.failcount > MAX_PERGRIDBOX_FAIL_COUNT:
remove the box from ValidBoxes
return SmallCircles
(*) This step is also an important optimization, which I can only assume you do not already have. Without it, your doesThisCircleOverlapAnother(...) function is incredibly inefficient at O(N) per query, which will make filling in circles nearly impossible for large ratios R>>r.
This is the exact generalization of your algorithm to avoid the slowness, while still retaining the elegant randomness of it.
Generalization to larger irregular features
edit: Since you've commented that this is for a game and you are interested in irregular shapes, you can generalize this as follows. For any small irregular shape, enclose it in a circle that represent how far you want it to be from things. Your grid can be the size of the smallest terrain feature. Larger features can encompass 1x2 or 2x2 or 3x2 or 3x3 etc. contiguous blocks. Note that many games with features that span large distances (mountains) and small distances (torches) often require grids which are recursively split (i.e. some blocks are split into further 2x2 or 2x2x2 subblocks), generating a tree structure. This structure with extensive bookkeeping will allow you to randomly place the contiguous blocks, however it requires a lot of coding. What you can do however is use the circle-grid algorithm to place the larger features first (when there's lot of space to work with on the map and you can just check adjacent gridboxes for a collection without running into the coupon-collector's problem), then place the smaller features. If you can place your features in this order, this requires almost no extra coding besides checking neighboring gridboxes for collisions when you place a 1x2/3x3/etc. group.
One way to do this that produces interesting looking results is
create an empty NxM grid
create an empty has-open-neighbors set
for i = 1 to NumberOfRegions
pick a random point in the grid
assign that grid point a (terrain) type
add the point to the has-open-neighbors set
while has-open-neighbors is not empty
foreach point in has-open-neighbors
get neighbor-points as the immediate neighbors of point
that don't have an assigned terrain type in the grid
if none
remove point from has-open-neighbors
else
pick a random neighbor-point from neighbor-points
assign its grid location the same (terrain) type as point
add neighbor-point to the has-open-neighbors set
When done, has-open-neighbors will be empty and the grid will have been populated with at most NumberOfRegions regions (some regions with the same terrain type may be adjacent and so will combine to form a single region).
Sample output using this algorithm with 30 points, 14 terrain types, and a 200x200 pixel world:
Edit: tried to clarify the algorithm.
How about using a 2-step process:
Choose a bunch of n points randomly -- these will become the centres of the circles.
Determine the radii of these circles so that they do not overlap.
For step 2, for each circle centre you need to know the distance to its nearest neighbour. (This can be computed for all points in O(n^2) time using brute force, although it may be that faster algorithms exist for points in the plane.) Then simply divide that distance by 2 to get a safe radius. (You can also shrink it further, either by a fixed amount or by an amount proportional to the radius, to ensure that no circles will be touching.)
To see that this works, consider any point p and its nearest neighbour q, which is some distance d from p. If p is also q's nearest neighbour, then both points will get circles with radius d/2, which will therefore be touching; OTOH, if q has a different nearest neighbour, it must be at distance d' < d, so the circle centred at q will be even smaller. So either way, the 2 circles will not overlap.
My idea would be to start out with a compact grid layout. Then take each circle and perturb it in some random direction. The distance in which you perturb it can also be chosen at random (just make sure that the distance doesn't make it overlap another circle).
This is just an idea and I'm sure there are a number of ways you could modify it and improve upon it.

Resources