I am currently trying to solve a problem to place boxes of various sizes (all cubes or rectangular prisms) within a defined volume.
In the application I am working on, a user defines a length, width, and height of a volume, as well as the length, width, and height of n boxes to place within the volume. The output will be a 3D visualization of all the boxes in space within the volume. The placement of these objects does not need to be optimal, nor do I care if boxes are touching or not, just as long as they exist in the space without overlapping.
My original approach to solve this problem was as follows:
for every box
{
place box at (0,0,0);
while True;
{
for every box that has already been placed
{
if the boxes overlap
{
create a vector pointing in the opposite direction of the other box;
move the box along the vector some dist. X, unless at the border of the available volume;
}
}
if the box didn't overlap with any other box
{
break out of while loop and continue to the next box;
}
}
}
This mostly worked. The main issue with this approach is time. When running this, the first 3 boxes take about 10 seconds, the following 3 get placed after 1:30, and the next 3 take over 10 minutes to finish. For larger assemblies, this is completely unusable. Additionally, this approach has a major flaw in that a box can get stuck between other boxes and the sides of the volume because the resulting vector will point outside of the space.
Are there any other more efficient approaches to solve similar problems that I could adapt to this situation? Again, the goal of this is not to find a perfect space optimization or to fill the entire space.
I am working with CAD, so the information available to me is anything I can access with the API, such as the global coordinates of the vertices of all of the boxes and the coordinates of the vertices of the available volume.
What you are trying to do is called a "packing problem", and the specific flavor is trying to pack cubes into another cube. Wikipedia is strangely pithy on this topic, but there's a more detailed article here. I know answering with links is discouraged on SO, but I think it's better than the alternative of writing a lengthy treatise on cuboid packing here :)
Your algorithm has complexity O(n2) (two loops) which would explain why it's slow. In fact, depending on how you move the boxes, it might be even worse than O(n2). I actually am not sure if it even terminates, because I can't tell where the algorithm would realize it if there were too many boxes and they couldn't fit in the space.
Since you say you correctness or optimality is not critical, a fast algorithm would be a greedy strategy:
pack boxes:
x1, y1, z1 = 0
x2, y2, z2 = 0
repeat until no boxes left:
box = take largest box
if box can go at x1, y1, z1:
place it there
x1 = x1 + box length along x
update y2, z2
else:
if box can go to at 0, y2, z1:
place it there
x1 = box length along x
y1 = y2 + box length along y
update y2, z2
else:
if box can go at 0, 0, z2:
place it there
x1 = box length along x
y1 = box length along y
z1 = z2 + box length along z
update y2, z2
else:
give up (too many/too big boxes)
update y2, z2:
y2 = max(y2, y2 + box length along y)
z2 = max(z2, z2 + box length along z)
This is horribly inefficient, but it will run in O(n) and should be easy to implement. We are basically starting with the biggest box at origin, and stacking them along x. When we hit the wall, we do a "carriage return" by moving down y and stacking a new column. When we hit the wall along y, we instead go down z and repeat for a new plane.
When selecting the biggest box, you can use a heuristic such as volume, sum of dimensions, average dimension or even sort by width, length, height. This algorithm fails badly if your boxes are inconsistently shaped (for example, if it has to put a very tall box after a very long box), so you should pick a heuristic that mitigates this.
Some optimizations could be:
Sorting the boxes into several classes like tall-fat-long and stack simultaneously from different corners -- you can use clustering here
Use a zig-zag pattern and always push each box flush with the previous column
Stack in a spiral pattern
Start by rotating all boxes so that their longest edge is on x, next on y, next on z
However a lot of the faster/more optimal methods will involve maintaining an efficient data structure of the growing box pile "surface", so you don't have to do NxN collision checking.
Related
Suppose I have two boxes (each of them is a rectangular cuboid, aka rectangular parallelepiped). I need to write a function that decides if the box with dimensions (a, b, c) can fit into the box with dimensions (A, B, C), assuming any rotations by any angles are allowed (not only by 90°).
The tricky part is the edges of the inner box may be not parallel to corresponding edges of the outer one. For example, a box that is very thin in the dimensions (a, b) but with length 1 < c < √3 can fit into a unit cube (1, 1, 1) if placed along its main diagonal.
I've seen questions [1], [2] but they seem to cover only rotations by 90°.
Not a complete answer, but a good start is to determine the maximum diameter that fits inside the larger box (inscribe the box in a circle) and the minimum diameter needed for the smaller box. That gives a first filter for possibility. This also tells you how to orient the smaller box within the larger one.
If one box can fit inside the other than it can fit also if boxes have same center. So only rotation is enough to check, translation is not needed to check.
2D case: For boxes X=(2A,2B) and x=(2a,2b) positioned around (0,0). That means corners of X are (+-A, +-B).
---------(A,B)
|
-----------(a,b)
(0,0) |
-----------(a,-b)
|
---------(A,-B)
Be rotating x around (0,0), corners are always on circle C with radius sqrt(a^2+b^2). If part of circle lie inside box X, and if part inside X has enough arc length to place 2 points on distance 2a or 2b, than x can fit inside X. For that we need to calculate intersection of C with lines x=A and y=B, and calculate distance between these intersection. If distance is equal or grater than 2a or 2b, than x can fit inside X.
3D case: Similar. For boxes X=(2A,2B,2C) and x=(2a,2b,2c) positioned around (0,0,0). Rotating x around (0,0,0), all corners move on sphere with radius sqrt(a^2+b^2+c^2). To see is there enough space on sphere-box intersection part, find intersection of sphere with planes x=A, y=B and z=C, and check is there enough space to fit any of quads (2a,2b), (2a,2c) or (2b,2c) on that sphere part. It is enough to check are points on part border on sufficient distance. For this part I'm not sure about efficient approach, maybe finding 'center' of intersection part and checking it's distance to border can help.
You basically have to check several cases, some trivial and some needing minimization searches.
First, there are 4 3-parallel-axis cases. If any of them passes (with #jean's test), you fit. Otherwise, continue to the next test cases:
Next, there are 18 2d diagonal cases, where one axis is parallel and the other two are diagonal, with one angle degree of freedom. Discard a case if the parallel axis doesn't fit; otherwise find the minimum of some "impingement" metric function of the single rotation angle. Then check for any actual impingement at that angle. The impingement metric has to be some continuous measure of how the inner box (4 corners) are staying inside the 2 faces of the outer box, allowing that sometimes they may go outside during the search for the minimum impingement angle. Hopefully a) there are a predictable maximum number of minima, and hopefully b) if there is a possible fit, then a fit is guaranteed at the angle of one of these minima.
If none of those cases passes without impingement, then move on to the larger number of 3d no-parallel-axes cases, where the rotation parameter is now three angles instead of one, and you have to search for a (hopefully limited number of) minima of the impingement metric, and test there for actual impingement.
Not really elegant, I think. This is similar to another thread asking how long of a line of given width can fit inside a 2d box of given dimensions. I didn't consider the parallel-axis case there, but the diagonal case requires solving a quartic equation (much worse than a quadratic equation). You may have a similar problem for your one-parallel-axis cases, if you want to go analytic instead of searching for minima of an impingement metric. The analytic solution for the no-parallel-axis 3d diagonal cases probably involves solving (for the correct root of) an even higher order equation.
In fact, any box A with dimensions (a1, a2, a3) can fit in an other box B with dimensions (b1, b2, b3), in the following conditions:
i) Every ai is less than or equal to every bi with i = 1. 2. 3;
ii) Any ai has to be less than or equal to sqrt(b1^2+b2^2+b3^2), the main diagonal of B (diagB). Any box A with one of its dimensions equal to diagB, has the other two dimensions equal to 0, since any plane orthogonal to it would extend outside the box B.
iii) The sum of a1, a2 and a3 must be less than or equal to diagB.
From these, we can see that the greatest dimension ai of a box A for it to fit box B, given ai > bi, should lie in the interval (bi, diagB).
Thus, any box with one dimension bigger than any dimension of a box containing it will necessarily placed along the latter's main diagonal.
Put it simply:
A(a1, a2, a3) fits in B(b1, b2, b3) iff a1+a2+a3 <= diagB.
Can you get box dimensions? Say a0,a1,a2 are the dimensions of box A ordered by size and b0,b1,b2 are the dimensions of box B ordered by size.
A fits inside B if (a0 <= b0 AND a1 <= b1 AND a2 <= b2)
I'm trying to make a program to find the shortest path between 2 points.
What I've come up with is to connect the start point to ALL the vertices of every shape. Each of those points will be connected to all other points - thereby forming a sort of tree. In case of circular shapes - the line will be up to a point where it forms a tangent to the circle or arc(since that's the shortest path around the object). However those lines that pass through other objects are disposed off. The remaining paths are subjected to *A** search.
But now how do I make the program identify the lines that pass through other figures? I'm using visual c++, so i can draw shapes in the client area by passing certain coordinates to the respective functions (eg LineTo(21,23)) . How will it know when a line is entering into another figure?
Straightforward algorithm taking into account what you've done so far:
For every shape store all vertices in an array (or list) in order they appear on the shape (clockwise or counter-clockwise doesn't make a difference). This allows you to easily iterate over edges of any given object, because edges are in that case (P1, P2), (P2, P3), ... (PN, P1) where N is the number of vertices.
For every line you want to check if it collides with any object iterate over all edges you've denoted and if line you're checking is crossing any edge - line collides with given object.
Checking crossing of the line with edge is a geometry problem. If bounding-points of line we are checking are P1=(x1,y1) and P2=(x2,y2), and bounding points of edge are P3=(x3,y3) and P4=(x4,y4) then you should solve the linear system:
(x2 - x1) y + (y1 - y2) x = x1 y2 - x2 y1 ,
(x4 - x3) y + (y3 - y4) x = x3 y4 - x4 y3 .
After getting a value for (x, y) you should check that it is on parts between bounding-points on both lines (line we're checking and the edge). If that is true your lines cross each other.
Note: You can improve running time by not iterating over every object's edges when you are checking for collision, but only over those objects that are in the path of the line. This could be accomplished for example by calculating minimal rectangle that contains every object, and checking if your line passes through the rectangle, dismissing the object from further checking if it doesn't.
You can distinguish two cases :
Either the line is entering a figure through another line. In that case, the easiest way is to represent your lines (including the border of your obstacles) by their equations (they would look like a*x + b*y +c = 0). Then deciding whether or not two lines intersect is quite a simple matter :
Two lines ax+by+c=0 and dx+ey+f=0 intersect if and only if they are not parallel, i.e iff a*e-b*d != 0
Then you have to check whether or not the intersection point of these two lines is inside the segments which you actually consider. The intersection point have coordinates :
y = ( cd - af ) / (ae - db)
x = ( bf - ec ) / (ae - db)
The only remaining task is to check whether or not these x and y belong to the segment (if they are within the intervals defining the segment)
And you repeat this operation for all segments defining you obstacles.
If you have circles, it becomes trickier (this is why you usually only consider polygons as obstacles), but basically it is the same idea :
- You have a line ax+by+c=0 and a circle (x-a)^2 + (y-b)^2 = r^2 (a circle of center (a,b) and radius r). Then you have to determine whether they intersect, the point of intersection and if it belongs to the segment which you consider. I'll leave the calculs to you.
If you are interested in different methods for finding a path between two points, with obstacles, there are other algorithms which you can use, although these will not provide you with the shortest path, but only with a path :
http://en.wikipedia.org/wiki/Sweep_line_algorithm
http://en.wikipedia.org/wiki/Voronoi_diagram
The interest of those algorithm compared to the visibility diagram you're building is that these two works in whatever dimension you are : if you want to upgrade your algorithm to dimensions, your algorithm will have a higher complexity (it will take more time to find a path) then these two and will not yield the shortest path.
I have user input that consists of a drawn rectangle (freestyle). Now this drawn figure isn't perfect, so I would like to redraw the shape for them based on an algorithm.
I have a bunch of coordinates from the user's drawing. I would like to find the greatest (x,y) and the lowest (x,y) coordinates and use the distance between those to determine the diagonal of the rectangle.
But I'm having difficulty determining the greatest (x,y) coordinate and the lowest (x,y) coordinate.
I can't take the greatest y with the greatest x, or the greatest x with the greatest y for example because maybe the user just made an accidental jut out in their line. (Does that make sense?)
Pretend below is a user drawn line.. if i used the greatest y with the greatest x, I would not have the desired coordinate (because it would find the coordinate in the accidental jut out)
----
/ \
----/ \-------- ----- --
--------------/ \---------------/ \------/ \--
Hope you understand what I'm getting at..
I guess another way of putting it is that I would like the coordinate closest to (0,0) and if my canvas was 1000 x 1000, I would like the second coordinate to be closest to (1000,1000). (the two extreme coordinates)
Can anyone help with this algorithm?
Thanks in advance!
Depending on how well you want the algorithm-generated rectangle to fit the user input, you might try the following:
Average all x and y coordinates to give you the center of your rectangle, (Xc, Yc).
Find your highest and lowest x value, subtract the lowest from the highest and divide by two. Repeat for the y values. Let us call these Xs and Ys (s is for 'side').
The important corners (upper left and lower right) would then become (Xc - Xs, Yc - Ys) and (Xc + Xs, Yc + Ys).
Draw lines as appropriate.
Now, this will give you a bounding-box wherein all user given points are contained. If you are looking for more of a best-fit type algorithm, replace the (max - min) / 2 function in step two with an averaging function. A simple one might involve averaging only points to one side of the center point (either above / below or left / right) and using those as offsets from center. Note that this will give you four offsets, only two of which you will use at any given time.
The rough idea presented here can be tuned to taste, depending on what kind of user input you are expecting (e.g. how distorted you expect it might be). Further improvements can be made using linear regression lines, assuming you are able to distinguish sides either via the points themselves or by user input methods (ex. drawing each side of the rectangle with a discrete action rather than all at once).
Hope this quick example will point you in the right direction.
if you want to find the closest point to (0,0), then just find it!
point FindClosestToOrigin(point[] P)
{
point closest = P[0];
foreach(point p in P)
{
if (DistanceOriginS(p) < DistanceOriginS(closest)) closest = p;
}
return closest;
}
float DistanceOriginS(point p)
{
return p.x*p.x + p.y*p.y;
}
you can easily modify the algo to find points closest to the rest of screen edges.
Just do an average over all points and use it as the position of rectangle sides.. of course this assumes you are able to distinguish the four sides of the rectangle otherwise you could try a way to split the coordinates into 4 sides (by checking the horizontal and vertical variation with some threshold) and then compute the average for each side and adjust it to link sides.
Input: a set of rectangles within the area (0, 0) to (1600, 1200).
Output: a point which none of the rectangles contains.
What's an efficient algorithm for this? The only two I can currently think of are:
Create a 1600x1200 array of booleans. Iterate through the area of each rectangle, marking those bits as True. Iterate at the end and find a False bit. Problem is that it wastes memory and can be slow.
Iterate randomly through points. For each point, iterate through the rectangles and see if any of them contain the point. Return the first point that none of the rectangles contain. Problem is that it is really slow for densely populated problem instances.
Why am I doing this? It's not for homework or for a programming competition, although I think that a more complicated version of this question was asked at one (each rectangle had a 'color', and you had to output the color of a few points they gave you). I'm just trying to programmatically disable the second monitor on Windows, and I'm running into problems with a more sane approach. So my goal is to find an unoccupied spot on the desktop, then simulate a right-click, then simulate all the clicks necessary to disable it from the display properties window.
For each rectangle, create a list of runs along the horizontal direction. For example a rectangle of 100x50 will generate 50 runs of 100. Write these with their left-most X coordinate and Y coordinate to a list or map.
Sort the list, Y first then X.
Go through the list. Overlapping runs should be adjacent, so you can merge them.
When you find the first run that doesn't stretch across the whole screen, you're done.
I would allocate an image with my favorite graphics library, and let it do rectangle drawing.
You can try a low res version first (scale down a factor 8), that will work if there is at least a 15x15 area. If it fails, you can try a high res.
Use Windows HRGNs (Region in .net). They were kind of invented for this. But that's not language agnostic no.
Finally you can do rectangle subtraction. Only problem is that you can get up to 4 rectangles each time you subtract one rect from another. If there are lots of small ones, this can get out of hand.
P.S.: Consider optimizing for maximized windows. Then you can tell there are no pixels visible without hit testing.
Sort all X-coordinates (start and ends of rectangles), plus 0 & 1600, remove duplicates. Denote this Xi (0 <= i <= n).
Sort all Y-coordinates (start and ends of rectangles), plus 0 & 1200, remove duplicates. Denote this Yj (0 <= j <= m).
Make a n * m grid with the given Xi and Yj from the previous points, this should be much smaller than the original 1600x1200 one (unless you have a thousand rectangles, in which case this idea doesn't apply). Each point in this grid maps to a rectangle in the original 1600 x 1200 image.
Paint rectangles in this grid: find the coordinates of the rectangles in the sets from the first steps, paint in the grid. Each rectangle will be on the form (Xi1, Yj1, Xi2, Yj2), so you paint in the small grid all points (x, y) such that i1 <= x < i2 && j1 <= y < j2.
Find the first unpainted cell in the grid, take any point from it, the center for example.
Note: Rectangles are assumed to be on the form: (x1, y1, x2, y2), representing all points (x, y) such that x1 <= x < x2 && y1 <= y < y2.
Nore2: The sets of Xi & Yj may be stored in a sorted array or tree for O(log n) access. If the number of rectangles is big.
If you know the minimum x and y dimensions of the rectangles, you can use the first approach (a 2D array of booleans) using fewer pixels.
Take into account that 1600x1200 is less than 2M pixels. Is that really so much memory? If you use a bitvector, you only need 235k.
You first idea is not so bad... you should just change the representation of the data.
You may be interessed in a sparse array of booleans.
A language dependant solution is to use the Area (Java).
If I had to do this myself, I'd probably go for the 2d array of booleans (particularly downscaled as jdv suggests, or using accelerated graphics routines) or the random point approach.
If you really wanted to do a more clever approach, though, you can just consider rectangles. Start with a rectangle with corners (0,0),(1600,1200) = (lx,ly),(rx,ry) and "subtract" the first window (wx1,wy1)(wx2,wy2).
This can generate at most 4 new "still available" rectangles if it is completely contained within the original free rectangle: (eg, all 4 corners of the new window are contained within the old one) they are (lx,ly)-(rx,wy1), (lx,wy1)-(wx1,wy2), (wx2,wy1)-(rx,wy2), and (lx,wy2)-(rx,ry). If just a corner of the window overlaps (only 1 corner is inside the free rectangle), it breaks it into two new rectangles; if a side (2 corners) juts in it breaks it into 3; and if there's no overlap, nothing changes. (If they're all axes aligned, you can't have 3 corners inside).
So then keep looping through the windows, testing for intersection and sub-dividing rectangles, until you have a list (if any) of all remaining free space in terms of rectangles.
This is probably going to be slower than any of the graphics-library powered approaches above, but it'd be more fun to write :)
Keep a list of rectangles that represent uncovered space. Initialize it to the entire area.
For each of the given rectangles
For each rectangle in uncovered space
If they intersect, divide the uncovered space into smaller rectangles around the covering rectangle, and add the smaller rectangles (if any) to your list of uncovered ones.
If your list of uncovered space still has any entries, they contain all points not covered by the given rectangles.
This doesn't depend on the number of pixels in your area, so it will work for large (or infinite) resolution. Each new rectangle in the uncovered list will have corners at unique intersections of pairs of other rectangles, so there will be at most O(n^2) in the list, giving a total runtime of O(n^3). You can make it more efficient by keeping your list of uncovered rectangles an a better structure to check each covering rectangle against.
This is a simple solution with a 1600+1200 space complexity only, it is similar in concept to creating a 1600x1200 matrix but without using a whole matrix:
Start with two boolean arrays W[1600] and H[1200] set to true.
Then for each visible window rectangle with coordinate ranges w1..w2 and h1..h2, mark W[w1..w2] and H[h1..h2] to false.
To check if a point with coordinates (w, h) falls in an empty space just check that
(W[w] && H[h]) == true
I have a list of two-dimensional points and I want to obtain which of them fall within a semi-circle.
Originally, the target shape was a rectangle aligned with the x and y axis. So the current algorithm sorts the pairs by their X coord and binary searches to the first one that could fall within the rectangle. Then it iterates over each point sequentially. It stops when it hits one that is beyond both the X and Y upper-bound of the target rectangle.
This does not work for a semi-circle as you cannot determine an effective upper/lower x and y bounds for it. The semi-circle can have any orientation.
Worst case, I will find the least value of a dimension (say x) in the semi-circle, binary search to the first point which is beyond it and then sequentially test the points until I get beyond the upper bound of that dimension. Basically testing an entire band's worth of points on the grid. The problem being this will end up checking many points which are not within the bounds.
Checking whether a point is inside or outside a semi-circle (or a rectangle for that matter) is a constant-time operation.
Checking N points lie inside or outside a semi-circle or rectangle is O(N).
Sorting your N points is O(N*lg(N)).
It is asymptotically faster to test all points sequentially than it is to sort and then do a fast culling of the points based on a binary search.
This may be one of those times where what seems fast and what is fast are two different things.
EDIT
There's also a dead-simple way to test containment of a point in the semi-circle without mucking about with rotations, transformations, and the like.
Represent the semi-circle as two components:
a line segment from point a to b representing the diameter of the semi-circle
an orientation of either left-of or right-of indicating that the semi-circle is either to the left or right of line segment ab when traveling from a to b
You can exploit the right-hand rule to determine if the point is inside the semicircle.
Then some pseudo-code to test if point p is in the semi-circle like:
procedure bool is_inside:
radius = distance(a,b)/2
center_pt = (a+b)/2
vec1 = b - center_pt
vec2 = p - center_pt
prod = cross_product(vec1,vec2)
if orientation == 'left-of'
return prod.z >= 0 && distance(center_pt,p) <= radius
else
return prod.z <= 0 && distance(center_pt,p) <= radius
This method has the added benefit of not using any trig functions and you can eliminate all square-roots by comparing to the squared distance. You can also speed it up by caching the 'vec1' computation, the radius computation, center_pt computation, and reorder a couple of the operations to bail early. But I was trying to go for clarity.
The 'cross_product' returns an (x,y,z) value. It checks if the z-component is positive or negative. This can also be sped up by not using a true cross product and only calculating the z-component.
First, translate & rotate the semi-circle so that one end is on the negative X-axis, and the other end is on the positive X-axis, centered on the origin (of course, you won't actually translate & rotate it, you'll just get the appropriate numbers that would translate & rotate it, and use them in the next step).
Then, you can treat it like a circle, ignoring all negative y-values, and just test using the square root of the sum of the squares of X & Y, and see if it's less than or equal to the radius.
"Maybe they can brute force it since they have a full GPU dedicated to them."
If you have a GPU at your disposal, then there are more ways to do it. For example, using a stencil buffer:
clear the stencil buffer and set the stencil operation to increment
render your semicircle to the stencil buffer
render your points
read back the pixels and check the values at your points
the points that are inside the semicircle would have been incremented twice.
This article describes how stencil buffers can be used in OpenGL.
If there's a standard algorithm for doing this, I'm sure someone else will come up with it, but if not: you could try sorting the points by distance from the center of the circle and iterating over only those whose distance is less than the semicircle's radius. Or if computing distance is expensive, I'd just try finding the bounding box of the semicircle (or even the bounding square of the circle of which the semicircle is part) and iterating over the points in that range. To some extent it depends on the distribution of the points, i.e. do you expect most of them or only a small fraction of them to fall within the semicircle?
You can find points in a circle and points on one side of a given slope, right?
Just combine the two.
Here's part of a function I wrote do get a cone firing arc for a weapon in a tile based game.
float lineLength;
float lineAngle;
for(int i = centerX - maxRange; i < centerX + maxRange + 1; i++){
if(i < 0){
continue;
}
for(int j = centerY - maxRange; j < centerY + maxRange + 1; j++){
if(j < 0){
continue;
}
lineLength = sqrt( (float)((centerX - i)*(centerX - i)) + (float)((centerY - j)*(centerY - j)));
lineAngle = lineAngles(centerX, centerY, forwardX, forwardY, centerX, centerY, i, j);
if(lineLength < (float)maxRange){
if(lineAngle < arcAngle){
if( (float)minRange <= lineLength){
AddToHighlightedTiles(i,j);
}
}
}
}
}
The variables should be self explanatory and the line angles function takes 2 lines and finds the angle between them. The forwardX and forwardY is just one tile in the correct direction from the center X and Y based on what angle you're pointing in. Those can be gotten easily with a switch statement.
float lineAngles(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4){
int a = x2 - x1;
int b = y2 - y1;
int c = x4 - x3;
int d = y4 - y3;
float ohSnap = ( (a * c) + (b * d) )/(sqrt((float)a*a + b*b) * sqrt((float)c*c + d*d) );
return acos(ohSnap) * 180 / 3.1415926545f;
}
It would appear that a simple scheme will work here.
Reduce the number of points in the set, by first computing the convex hull. Only the points on the convex hull will contribute to any interaction with any convex bounding shape. So retain only the subset of points on the perimeter of the hull.
It can easily be argued that the minimal radius bounding semi-circle must have one edge (two points) of the convex hull coincident along the diameter of the semi-circle. I.e., if some edge of the hull does not lie in the diameter, then there exists a different semi-circle with smaller diameter that contains the same set of points.
Test each edge in sequence. (A convex hull often has relatively few edges, so this will go quickly.) Now it becomes a simple 1-d minimization problem. If we choose to assume the edge in question lies on the diameter, then we merely need to find the center of the sphere. It must lie along the current line which we are considering to be the diameter. So as a function of the position of the point along the current diameter, just find the point which lies farthest away from the nominal center. By minimizing that distance, we find the radius of the minimal semi-circle along that line as a diameter.
Now, just choose the best of the possible semi-circles found over all edges of the convex hull.
If your points have integer co-ordinates, the fastest solution may be a lookup table. Since a semicircle is convex, for each y co-ordinate, you get a fixed range of x, so each entry in your lookup table gives maximum and minimum X co-ordinates.
Of course you still need to precalculate the table, and if your semicircle isn't fixed, you may be doing that a lot. That said, this is basically one part of what would once have been done to render a semicircle - the full shape would be rendered as a series of horizontal spans by repeatedly calling a horizontal line drawing function.
To calculate the spans in the first place (if you need to do it repeatedly), you'd probably want to look for an old copy of Michael Abrash's Zen of Graphics Programming. That described Bresenhams well-known line algorithm, and the not-so-well-known Hardenburghs circle algorithm. It shouldn't be too hard to combine the span-oriented versions of the two to quickly calculate the spans for a semi-circle.
IIRC, Hardenburgh uses the x^2 + y^2 = radius^2, but exploits the fact that you're stepping through spans to avoid calculating square roots - I think it uses the fact that (x+1)^2 = x^2 + 2x + 1 and (y-1)^2 = y^2 - 2y + 1, maintaining running values for x, y, x^2 and (radius^2 - y^2), so each step only requires a comparison (is the current x^2 + y^2 too big) and a few additions. It's done for one octant only (the only way to ensure single-pixel steps), and extended to the full circle through symmetry.
Once you have the spans for the full circle, it should be easy to use Bresenhams to cut off the half you don't want.
Of course you'd only do all this if you're absolutely certain that you need to (and that you can work with integers). Otherwise stick with stbuton.
translate the center of the arc to the origin
compute angle between ordinate axis and end points of radii of semi-cirlce
translate the point in question by same dx,dy
compute distance from origin to translated x,y of point, if d > radius of circle/arc eliminate
compute angle between ordinate axis and end point
if angle is not between starting and ending arc of semi-cirlce, eliminate
points remaning should be inside semi-circle
I guess someone found the same solution as me here but I have no code to show as its pretty far in my memory...
I'd do it by steps...
1. I'd look if i'm within a circle... if yes look on which side of the circle.
2. By drawing a normal vector that come from the vector made by the semi-sphere. I could know if I'm behind or in front of the vector...and if you know which side is the semi sphere and which side is the void...It will be pretty damn easy to find if you're within the semi sphere. You have to do the dot product.
I'm not sure if it's clear enough but the test shouldn't be that hard to do...In the end you have to look for a negative or positive value...if it's 0 you're on the vector of the semisphere so it's up to you to say if it's outside or inside the semi-sphere.
The fastest way to do this will depend on your typical data. If you have real-world data to look at, do that first. When points are outside the semi-circle, is it usually because they are outside the circle? Are your semi-circles typically thin pie slices?
There are several ways to do this with vectors. You can scale the circle to a unit circle and use cross-products and look at the resultant vectors. You can use dot-products and see how the prospective point lands on the other vectors.
If you want something really easy to understand, first check to make sure it's inside the circle, then get the angle and make sure it's between the angle of the two vectors that dictate the semi-circle.
Edit: I had forgotten that a semicircle is always half a circle. I was thinking of any arbitrary section of a circle.
Now that I have remembered what a semicircle is, here's how I would do that. It's similar to stbuton's solution, but it represents the semicircle differently.
I'd represent the semicircle as the unit vector that bisects the semicircle. You can easily get that from either one of the vectors that indicate the boundary of the semicircle (because they are 90 degrees away from the representation) by swapping x and y and negating one of them.
Now you just cross the vector created by subtracting the point to be tested from the circle's center. The sign of z tells you whether the point is in the semicircle, and the length of z can be compared against the radius.
I did all the physics for Cool Pool (from Sierra Online). It's all done in vectors and it's filled with dots and crosses. Vectors solutions are fast. Cool Pool was able to run on a P60, and did reasonable breaks and even spin.
Note: For solutions where you're checking sqrt(xx+yy), don't even do the sqrt. Instead, keep the square of the radius around and compare against that.