I have an array of box objects, defined by their (x,y,width,height) properties like so:
Box Q is anchored at corner point C. How can I programatically expand box Q to take up all the available space it has, while maintaining its aspect ratio?
I have had some luck by expanding box to be very large (from the top right corner) and then aligning to the top edge of the furthest box (in this case 5). If at that point other boxes overlap with Q, I remove the furthest box (5) and repeat (align to the top edge of 4), until no boxes overlap. The problem with this approach is that a box may overlap with Q (box 2 in the next image), but when I scale to meet its top edge, it is no longer contained, like this:
Any thoughts on an approach would be much appreciated,
Josh
but when I scale to meet its top edge, it is no longer contained
Instead scale to meet its
top edge
bottom edge
left edge
right edge
Then, see which scaling is valid (the box is contained after scaling) and results in the biggest box.
I can see two approaches here.
First is to iterate over all other boxes. For each box B, see how much (by what factor) you can expand your given box Q so that it will touch box B; after that take the minimal of all such factors. However, finding this factor for a given B is a non-trivial task, though definitely solvable.
At the same time, if you already have a code that checks for overlaps for a given factor, then you can apply binary search to find the maximal factor that does not lead to overlap.
So you know that if you expand it a lot (say by x times), it does overlap. If you do not expand it (that is, expand by 1 times), it does not overlap. So you have a segment [1,x] where to search for an answer. Try the middle --- expand by (x+1)/2 times and see whether it overlaps. If it overlaps, continue with segment [1, (x+1)/2], otherwise with segment [(x+1)/2, x]. Take the middle of the new segment, and so on until the end values of your segment are close enough.
Create a function that will take in a scaling factor as a parameter, and have it return true or false depending on if there is overlap found or not. It seems like you already have something like this function written.
Then use bisection search https://en.wikipedia.org/wiki/Bisection_method to find your scaling factor to a threshold that is satisfactory.
Related
I am trying to find an optimal way to place a set of ranges in a larger range. I like to think of it as flat boxes that can move left or right. Some things to consider:
There are N boxes, each of them with a center point Ci.
There are N attractor points (one per box), we can call them Pi. Each box is attracted to one attractor point with a force proportional to the distance.
The order of the boxes is fixed. The order of the attractor points and of the boxes is the same. So C1 is attracted to P1, C2 to P2, etc.
The boxes cannot overlap.
I made a diagram that may make it easier to understand:
The question is, what algorithm can I use to move the boxes around so that each Ci is the closest possible to its respective Pi. In other words, how do I find the locations for the Ci points that minimizes the distance (Li) between all Ci-Pi pairs?
I'd also be helpful if you can point me in to some material to read or something, I'm not very familiar with this type of problems... My guess is that some sort of force-directed algorithm would work but I'm not sure how to implement those.
Since "each box is attracted to one attractor point with a force proportional to the distance", you are describing a system where the boxes are attached to the attractor points by springs (see Hooke's law), and you want to determine the state of the system at rest (the state of minimum potential energy).
Because the forces are proportional to the distances, what you want is to minimize the sum of the distances squared, or the sum of Li^2 from i=0 to i=n. Here is an algorithm to do that.
The idea is to group boxes that need to touch by the end and figure out their position as a group based on their corresponding attractor points.
The first step is not to find these groups, because we can actually start with one big group and cut it later if necessary. For simplicity, let's treat all Li as signed distances. So Li = Ci-Pi. Let's also name the sizes of the boxes, though it will be easier to handle half-sizes. So let Si be half the size of the i-th box. Finally, let's write the sum of Xi from i=a to i=b like sum[a,b](Xi).
Here is how to compute the position of a group of boxes, assuming each one touches the next. Li is a function of the position of the group: if x is that position, Li(x) = Ci(x) - Pi (where Ci(x) is just x plus some constant). x can be point of the group of box, for example the left edge of the first box.
We also know that sum[a,b](Li(x)^2) must be minimal. This means the derivative of that sum must be zero: sum[a,b](2*Li(x)) = 0. So:
sum[a,b](2*Li) = 0
sum[a,b](Li) = 0
sum[a,b](Ci - Pi) = 0
sum[a,b](Ci) = sum[a,b](Pi)
Computing sum[a,b](Pi) is trivial, and sum[a,b](Ci) can be expressed in terms of Ca (center of the first box), since C[i+1] = Ci + Si + S[i+1].
Now that you can compute the position of a group of boxes, do it first with a group made of all boxes, and then remove boxes from that group as follows.
Starting from the left, consider all boxes with Li > 0 and compute Q = sum(Li) for all corresponding i. Similarly, starting from the right, consider all boxes with Li < 0 and compute R = -sum(Li) for all corresponding i (note that negative sign, because we want the absolute value). Now, if Q > R, remove the boxes on the left and make a new group with them, otherwise remove the boxes on the right and make a new group with them.
You cannot make these two new groups at the same time, because removing boxes from one end can change the position of the original group, where boxes you would have removed from the other end should not be removed.
If you made a new group, repeat: compute the position of each separate group of boxes (they will never overlap at this point), and remove boxes if necessary. Otherwise, you have your solution.
It seems the objective is a quadratic function and all the constraints are linear. So I think you can solve it by standard quadratic programming solvers.
If we write S_i be the half-size of i-th box, and the Pi's are given, then:
Minimize y
with respect to C_1, C_2, ...C_n
subject to
y = sum_i (P_i - C_i)^2
C_i + S_i + S_{i+1} <= C_{i+1} for each i = 1, ... n-1
Edit: this is a crude solution to minimize the sum of all Li, which is no longer the question.
Let's name the boxes B, so Bi has center Ci. Let n be the number of boxes and points.
Assuming all the boxes can fit into the larger range, here is how I would do it:
Let Q(a, b) be the average of Pi from i=a to i=b.
Place all the boxes next to each other (in order) to form a superbox, so that the center of this superbox is at Q(1, n).
If it goes over one end of the larger range, move it so that it sits at the limit.
Then, for each Bi, move it as close to Pi as possible without moving other boxes (and while still being inside the larger range). Repeat until you can't move any more box.
Now, the only way to minimize the sum of all Li is as follows.
Let G be a group of boxes that touch. Let F(G) be the predicate: if the center boxes of a series are Bi and Bj (if there are an odd number of boxes in the series, i=j), then Ci != Pi and Cj != Pj.
Find a G such that F(G) is true, and move the corresponding boxes so that F(G) becomes false. If the group of boxes hit another box while moving, add that box to the group and repeat. Of course, don't move any box outside the larger range.
Once there is no G for which F(G) is true or for which you would need to move outside the larger range, you have your solution (one of potentially an infinite number).
Just for completion, I found a (probably subtompimal) solution that works pretty well and is very easy to implement.
Place all boxes with their Ci's at their Pi's.
Go over all boxes, from left to right and do the following:
Check if box i overlaps with the box to its left. If it is the first box, check if it overlaps with the range minimum.
If there is overlap, move the box to the right so that there is no left overlap.
Repeat step 2 but from right to left, checking right overlaps (or range maximum for the last box).
Repeat steps 2-3 until no more overlaps remain or a maximum number of repetitions is reached.
It's quite efficient for my relatively small dataset and I get good results with 10 repetitions of steps 2-3 (5 left to right checks, 5 right to left checks).
Problem: You are given a set of n types of rectangular 3-D boxes, where the i^th box has height h(i), width w(i) and depth d(i) (all real numbers). You want to create a stack of boxes which is as tall as possible, but you can only stack a box on top of another box if the dimensions of the 2-D base of the lower box are each strictly larger than those of the 2-D base of the higher box. Of course, you can rotate a box so that any side functions as its base. It is also allowable to use multiple instances of the same type of box.
solution: I found the below solution at http://www.geeksforgeeks.org/dynamic-programming-set-21-box-stacking-problem/
1) Generate all 3 rotations of all boxes. The size of rotation array becomes 3 times the size of original array. For simplicity, we consider depth as always smaller than or equal to width.
2) Sort the above generated 3n boxes in decreasing order of base area.
3) After sorting the boxes, the problem is same as LIS with following optimal substructure property.
MSH(i) = Maximum possible Stack Height with box i at top of stack
MSH(i) = { Max ( MSH(j) ) + height(i) } where j < i and width(j) > width(i) and depth(j) > depth(i).
If there is no such j then MSH(i) = height(i)
4) To get overall maximum height, we return max(MSH(i)) where 0 < i < n
I think the solution is wrong as it just considers 3 rotation of all the boxes. To get the correct solution, it should generate 6 possible rotations. So, is the given solution incorrect, or is their any flaw in using 6 rotations?
Note this:
For simplicity, we consider depth as always smaller than or equal to width.
So, if the dimensions of a box are, for example, 3, 4 and 5, we consider the following three ways to put it on the stack:
d=3, w=4, h=5
d=3, w=5, h=4
d=4, w=5, h=3
The other three rotations have depth larger than width, so we do not consider them:
d=4, w=3, h=5
d=5, w=3, h=4
d=5, w=4, h=3
In order to see if one rectangle a x b can be fully covered by another one c x d, it is sufficient to turn them both so that a <= b and c <= d, and then check whether a <= c and b <= d. That's why it works.
No, it's sufficient to consider 3 "rotations" of each box, because the only possibilities to consider are which dimension to make the top and bottom (say) of the box perpendicular to, and there are only 3 of these. It might help to think of the 3 different possibilities for each box in that way, instead of as rotations.
The main point is that, after we have chosen which pair of sides of a box will be top and bottom (which we can do in 3 ways), we don't need to try the 2 different rotations of the box in the plane. We can always just go with (say) the rotation in which the box is wider than it is deep. How do we know that we won't miss any potentially good solutions by doing this? Because in any solution having a box that is deeper than it is wide, there must be a lowest such box b, and that box, plus everything above it, can be safely rotated 90 degrees. (We know it's safe to do this because it's the lowest such box in the solution -- so the box underneath it, if any, must itself be wider than it is deep, meaning that if we rotate b 90 degrees, it must still fit inside this lower box (I recommend verifying this algebraically).) We can keep transforming the lowest deeper-than-it-is-wide box in the solution until none remain, without ever changing the height of the solution. Since we can do this for any solution containing one or more deeper-than-it-is-wide boxes, it means that each one is exactly equivalent in quality to some solution in which every box is wider-than-it-is-deep, so we can totally ignore the former solutions.
Let's say I have n number of equally sized and equally rotated squared boxes inside a limited area in a 2D coordinate system (floating point coordinates). The boxes should not overlap.
Now I want to find a free space for one more box. I need some tips for an algorithm to solve this. Any ideas?
There ought to be a scan line algorithm for this. You say the boxes are equally rotated, so you should be able to rotate the co-ordinate system, if necessary, so that the edges of the boxes are parallel to the x and y coordinates. I would then sort the boxes in order of y coordinate.
Now try placing a box in the lowest possible position. Read from the sorted boxes to find all the boxes low enough to interfere with your placement and create an ordered set (e.g. red-black tree or similar container class) of these boxes. Now scan along this set of boxes and see if there is a gap big enough to place a box. If not, use the original sorted list of boxes to find and remove the lowest box, so you can consider putting the new box in just above that lowest box, so it cannot interfere with this. Add more boxes from the sorted list to cover all boxes high enough to interfere with this new possible height of box. Keep track of where you have removed boxes from the list and check there to see if a gap big enough to hold a box has opened up. If not, repeat the exercise until you find a gap or run out of space at the top of the possible area.
This looks like cost N log N for the initial sort, and then a cost of at most log N per box to insert and delete boxes from the ordered set. Checking for gaps is no more expensive than this, because you only check for a gap in a location where you have just removed a box. So I think the total cost is N log N.
I have a fairly large set of 2D points (~20000) in a set, and for each point in the x-y plane want to determine which point from the set is closest. (Actually, the points are of different types, and I just want to know which type is closest. And the x-y plane is a bitmap, say 640x480.)
From this answer to the question "All k nearest neighbors in 2D, C++" I got the idea to make a grid. I created n*m C++ vectors and put the points in the vector, depending on which bin it falls into. The idea is that you only have to check the distance of the points in the bin, instead of all points. If there is no point in the bin, you continue with the adjacent bins in a spiralling manner.
Unfortunately, I only read Oli Charlesworth's comment afterwards:
Not just adjacent, unfortunately (consider that points in the cell two
to the east may be closer than points in the cell directly north-east,
for instance; this problem gets much worse in higher dimensions).
Also, what if the neighbouring cells happen to have less than 10
points in them? In practice, you will need to "spiral out".
Fortunately, I already had the spiraling code figured out (a nice C++ version here, and there are other versions in the same question). But I'm still left with the problem:
If I find a hit in a cell, there could be a closer hit in an adjacent cell (yellow is my probe, red is the wrong choice, green the actual closest point):
If I find a hit in an adjacent cell, there could be a hit in a cell 2 steps away, as Oli Charlesworth remarked:
But even worse, if I find a hit in a cell two steps away, there could still be a closer hit in a hit three steps away! That means I'd have to consider all cells with dx,dy= -3...3, or 49 cells!
Now, in practice this won't happen often, because I can choose my bin size so the cells are filled enough. Still, I'd like to have a correct result, without iterating over all points.
So how do I find out when to stop "spiralling" or searching? I heard there is an approach with multiple overlapping grids, but I didn't quite understand it. Is it possible to salvage this grid technique?
Since the dimensions of your bitmap are not large and you want to calculate the closest point for every (x,y), you can use dynamic programming.
Let V[i][j] be the distance from (i,j) to the closest point in the set, but considering only the points in the set that are in the "rectangle" [(1, 1), (i, j)].
Then V[i][j] = 0 if there is a point in (i, j), or V[i][j] = min(V[i'][j'] + dist((i, j), (i', j'))) where (i', j') is one of the three neighbours of (i,j):
i.e.
(i - 1, j)
(i, j - 1)
(i - 1, j - 1)
This gives you the minimum distance, but only for the "upper left" rectangle. We do the same for the "upper right", "lower left", and "lower right" orientations, and then take the minimum.
The complexity is O(size of the plane), which is optimal.
For you task usually a Point Quadtree is used, especially when the points are not evenly distributed.
To save main memory you als can use a PM or PMR-Quadtree which uses buckets.
You search in your cell and in worst case all quad cells surounding the cell.
You can also use a k-d tree.
A solution im trying
First make a grid such that you have an average of say 1 (more if you want larger scan) points per box.
Select the center box. Continue selecting neighbor boxes in a circular manner until you find at least one neighbor. At this point you can have 1 or 9 or so on boxes selected
Select one more layer of adjacent boxes
Now you have a fairly small list of points, usually not more than 10 which you can punch into the distance formula to find the nearest neighbor.
Since you have on average 1 points per box, you will mostly be selecting 9 boxes and comparing 9 distances. Can adjust grid size according to your dataset properties to achieve better results.
Also, if your data has a lot of variance, you can try 2 levels of grid (or even more) so if selection works and returns more than 50 points in a single query, start a next grid search with a grid 1/10th the size ...
One solution would be to construct multiple partitionings with different grid sizes.
Assume you create partitions at levels 1,2,4,8,..
Now, search for a point in grid size 1 (you are basically searching in 9 squares). If there is a point in the search area and if distance to that point is less than 1, stop. Otherwise move on to the next grid size.
The number of grids you need to construct is about twice as compared to creating just one level of partitioning.
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.