Calculate the number of circles that fit on the circumference of another circle - geometry

I'm looking for an algorithm (or pseudo code) that can calculate the maximum number of (smaller) circles with diameter "s" that can be squeezed into the circumference of another (larger) circle with radius "r" ...
Image: http://teasy.space/images/terracolony-squeezingcircles2.jpg
You can alternate between radius/diameter etc if you wish -- as these are the only 2 parameters (other than the center (large circle) coordinate) that i have, i.e. that are known ...
The outer circles may not overlap but can fit "snug" together ...
After various upgrades to my routine through the years, I'm currently using an algorithm that is not perfect (and it needs to be accurate or the galaxy breaks down lol)
which does a broad interpolation between small outside circle diameter and large inside circle circumference, to somewhat accurately plot the circle count in a polygon style fitting pattern, which causes problems (i.e. overlaps) when using larger outside circles ...
; try to fit a random number of circles
num_Circles = Rand( min,max )
; check if the number of circles exceed the maximum that can fit
If num_Circles * SmallCircle_Diameter > LargeCircle_Circumference
; adjust the amount accordingly
num_Circles = LargeCircle_Circumference / SmallCircle_Diameter
End If
Another assumption is that the size of the smaller outer circles never exceeds that of the larger inner circle ...
something less to worry about ;)
I'm using this algorithm for one of my projects called Terra Colony, based on Gravity Well, a 2D space/gravity realtime colonization simulation game with moons, planets, stars, black/white holes, etc
Image: http://teasy.space/images/terracolony-squeezingcircles1.jpg
This is an issue that has plagued this project for over a decade!
Hopefully you can point me in the right direction :D
I have previously done many experiments and wrote different programs to find a solution, and have traveled the internet looking for formulas and solutions which in the end are very close, but not close enough! :P
Thank you! <3
Teasy
P.S. I tried to add the tag "circumference" but it apparently requires "1500 reputation" (points i guess, maybe to prevent spam)

There is formula that establishes relation between radius of big circle R, radius of small circle r and number of (touching) small circles N
R = r / Sin(Pi/N)
So maximum number of small circles might be found as
Sin(Pi/N) = r / R
Pi / N = arcsin(r / R)
and finally
N = Pi / arcsin(r / R)
Example:
R=5
r=2.5
so
N = Pi / arcsin(1/2) =
Pi / (Pi/6) =
6

Given the diam. of the small circle 'd' and the number of them 'c'
then the dia. of the large circle 'D' is
D=d/sin(180/c)

Related

What is the fastest way to find the center of an irregular convex polygon?

I'm interested in a fast way to calculate the rotation-independent center of a simple, convex, (non-intersecting) 2D polygon.
The example below (on the left) shows the mean center (sum of all points divided by the total), and the desired result on the right.
Some options I've already considered.
bound-box center (depends on rotation, and ignores points based on their relation to the axis).
Straight skeleton - too slow to calculate.
I've found a way which works reasonably well, (weight the points by the edge-lengths) - but this means a square-root call for every edge - which I'd like to avoid.(Will post as an answer, even though I'm not entirely satisfied with it).
Note, I'm aware of this questions similarity with:What is the fastest way to find the "visual" center of an irregularly shaped polygon?
However having to handle convex polygons increases the complexity of the problem significantly.
The points of the polygon can be weighted by their edge length which compensates for un-even point distribution.
This works for convex polygons too but in that case the center point isn't guaranteed to be inside the polygon.
Psudo-code:
def poly_center(poly):
sum_center = (0, 0)
sum_weight = 0.0
for point in poly:
weight = ((point - point.next).length +
(point - point.prev).length)
sum_center += point * weight
sum_weight += weight
return sum_center / sum_weight
Note, we can pre-calculate all edge lengths to halve the number of length calculations, or reuse the previous edge-length for half+1 length calculations. This is just written as an example to show the logic.
Including this answer for completeness since its the best method I've found so far.
There is no much better way than the accumulation of coordinates weighted by the edge length, which indeed takes N square roots.
If you accept an approximation, it is possible to skip some of the vertices by curve simplification, as follows:
decide of a deviation tolerance;
start from vertex 0 and jump to vertex M (say M=N/2);
check if the deviation along the polyline from 0 to M exceeds the tolerance (for this, compute the height of the triangle formed by the vertices 0, M/2, M);
if the deviation is exceeded, repeat recursively with 0, M/4, M/2 and M/2, 3M/4, M;
if the deviation is not exceeded, assume that the shape is straight between 0 and M.
continue until the end of the polygon.
Where the points are dense (like the left edge on your example), you should get some speedup.
I think its easiest to do something with the center of masses of the delaunay triangulation of the polygon points. i.e.
def _centroid_poly(poly):
T = spatial.Delaunay(poly).simplices
n = T.shape[0]
W = np.zeros(n)
C = 0
for m in range(n):
sp = poly[T[m,:],:]
W[m] = spatial.ConvexHull(sp).volume
C += W[m] +np.mean(sp, axis = 0)
return C / np.sum(W)
This works well for me!

Positioning random points on a 2D plane

So here's a little bit of geometry for you. I've been stuck on this for a while now:
I need to write a script (in C#, but feel free to answer in whatever script you'd like) that generates random points. A points has to values, x and y.
I must generate N points total (where N > 1 and is also randomly up to 100).
point 1 must be x = 0, y = 0. point 2 must be of distance 1 from point 1. So that Root(x2 + y2) = 1.
point 3 must be of distance 1 from point 2 and so on and so forth.
Now here's the tricky part - point N must be of distance 1 from point 1. So if you were to connect all points into a single shape, you'd get a closed shape with each vertices being the same length.
(vertices may cross and you may even have two points at exactly the same location. As long as it's random).
Any idea how you'd do that?
I would do it with simulation of chain there are 2 basic ways one is start from regular polygon and then randomize one point a bit (rotate a bit) then iterate the rest to maintain the segment size=1.
The second one is start with full random open chain (like in MBo answer) and then iteratively change the angles until the last point is on desired distance from first point. I think the second approach is a bit simpler to code...
If you want something more complicated then you can generate M random points and handle them as closed Bezier curve cubic patches loop control points. Then just find N equidistant points on it (this is hard task) and rescale the whole thing to match segment line size = 1
If you want to try first approach then
Regular polygon start (closed loop)
Start with regular polygon (equidistant points on circle). So divide circle to N angular segments. Select radius r so line length match l=1
so r=0.5/cos(pi/N) ... from half angle triangle
Make function to rotate i-th point by some single small step
So just rotate the i-th point around (i-1)th point with radius 1 and then iteratively change the {i+1,...N} points to match segments sizes
you can exploit symmetry to avoid bullet #2
but this will lead not to very random result for small N. Just inverse rotation of 2 touching segments for random point p(i) and loop this many times.
to make it more random you can apply symmetry on whole parts (between 2 random points) instead of on 2 lines only
The second approach is like this:
create randomized open chain (like in MBo's answer)
so all segments are already with size=1.0. Remember also the angle not just position
i-th point iteration
for simplicity let the points be called p1,p2,...pn
compute d0=||pn-p1|-1.0|
rotate point pi left by some small da angle step
compute dl=||pn-p1|-1.0|
rotate point pi right by 2.0*da
compute dr=||pn-p1|-1.0|
rotate point pi to original position ... left by da
now chose direction closer to the solution (min dl,dr,d0) so:
if d0 is minimal do not change this point at all and stop
if dl is minimal then rotate left by da while dl is lowering
if dr is minimal then rotate right by da while dr is lowering
solution
loop bullet #2 while the d=||pn-p0|-1.0| is lowering then change da to da*=0.1 and loop again. Stop if da step is too small or no change in d after loop iteration.
[notes]
Booth solutions are not precise your distances will be very close to 1.0 but can be +/- some error dependent on the last da step size. If you rotate point pi then just add/sub angle to all pi,pi+1,pi+2,..pn points
Edit: This is not an answer, closeness has not been taken into account.
It is known that Cos(Fi)^2 + Sin(Fi)^2 = 1 for any angle Fi
So you may use the next approach:
P[0].X = 0
P[0].Y = 0
for i = 1 .. N - 1:
RandomAngle = 2 * Pi * Random(0..1)
P[i].X = P[i-1].X + Cos(RandomAngle)
P[i].Y = P[i-1].Y + Sin(RandomAngle)

Find all pixels a given radius from a point, confined within an arc

I'm working on an autonomous rover that navigates partially by ultrasound proximity sensors. Before we implement the hardware we want to do some testing of our algorithms with a simulator, which I am now writing.
One task that I'm having some trouble with is that the ultrasound sensor has a 60 degree field of view. If an object is detected, any point along that 60 degree arc at that radius may have an object, but all points below that radius are guaranteed not to have an object.
What I need to do is write a function that is given an (x,y) coordinate and a bearing (I'm restricting this to the 4 cardinals for now) and have it return to me a list of pixels within a radius and a list of pixels at that radius. With repeated scans from multiple locations and bearings all objects can be found.
My initial thought was to work iterative-ly. Start at the row in front of the sensor and sweep back and forth in progressively wider scans (1,1,3,3,5,5,7,7,etc). However eventually the radii stop aligning with the rows. My new search path would be to figure out how to draw an arc with pixels, then step the radius up to the first collision.
This question asks a similar question, but is only interested in specific points so I believe it is a fundamentally different problem.
how to calculate all points(longitude,latitude) within a given radius from given point (longitude,latitude)?
You can use any Floodfill method to get all integer points in the sector.
Precalculate starting and ending angles as
S_Angle = Center_Bearing - Pi/6
E_Angle = Center_Bearing + Pi/6
Important values:
S_Cos = Cos(S_Angle)
S_Sin = Sin(S_Angle)
E_Cos = Cos(E_Angle)
E_Sin = Sin(E_Angle)
Border conditions for sector floodfill:
(x-x0)*S_Sin-(y-y0)*S_Cos >= 0 //point is left to starting ray
(x-x0)*E_Sin-(y-y0)*E_Cos <= 0 //point is right to ending ray
(x-x0)^2+(y-y0)^2 <= R^2 //point is in the range
(probably you may need to exchange >= and <= in the first inequalities pair)

Simple Trigonometry?

EDIT - Thanks for all the answers everyone. I think I accidentally led you slightly wrong as the square in the picture below should be a rectangle (I see most of you are referencing squares which seems like it would make my life a lot easier). Also, the x/y lines could go in any direction, so the red dot won't always be at the top y boundary. I was originally going for a y = mx + b solution, but then I got stuck trying to figure out how I know whether to plug in the x or the y (one of them has to be known, obviously).
I have a very simple question (I think) that I'm currently struggling with for some reason. I'm trying to have a type of minimap in my game which shows symbols around the perimeter of the view, pointing towards objectives off-screen.
Anyway, I'm trying to find the value of the red point (while the black borders and everything in green is known):
It seems like simple trigonometry, but for some reason I can't wrap my head around it. I just need to find the "new" x value from the green point to the red point, then I can utilize basic math to get the red point, but how I go about finding that new x is puzzling me.
Thanks in advance!
scale = max(abs(x), abs(y))
x = x / scale
y = y / scale
This is the simple case, for a square from (-1, -1) to (1, 1). If you want a different sized square, multiply the coordinates by sidelen / 2.
If you want a rectangle instead of a square, use the following formula. (This is another solution to the arbitrarily-sized square version)
scale = max(abs(x) / (width / 2), abs(y) / (height / 2))
x = x / scale
y = y / scale
Let's call the length of one side of the square l. The slope of the line is -y/x. That means, if you move along the line and rise a distance y toward the top of the square, then you'll move a distance x to the left. But since the green point is at the center of the square, you can rise only l/2. You can express this as a ratio:
-y -l/2
——— = ———
x d
Where d is the distance you'll move to the left. Solving for d, we have
d = xl/2y
So if the green dot is at (0, 0), the red dot is at (-l/2, xl/2y).
All you need is the angle and the width of the square w.
If the green dot is at (0,0), then the angle is a = atan(y/x), the y-coordinate of the dot is w/2, and therefore the x-coordinate of the dot is tan(1/a) * (w/2). Note that tan(1/a) == pi/2 - tan(a), or in other words the angle you really want to plug into tan is the one outside the box.
Edit: yes, this can be done without trig, too. All you need is to interpolate the x-coordinate of the dot on the line. So you know the y-coordinate is w/2, then the x-coordinate is (w/2) * x/y. But, be careful which quadrant of the square you're working with. That formula is only valid for -y<x<y, otherwise you want to reverse x and y.

Rotating 3D cube perspective problem

Since I was 13 and playing around with AMOS 3D I've been wanting to learn how to code 3D graphics. Now, 10 years later, I finally think I have have accumulated enough maths to give it a go.
I have followed various tutorials, and defined screenX (and screenY, equivalently) as
screenX = (pointX * cameraX) / distance
(Plus offsets and scaling.)
My problem is with what the distance variable actually refers to. I have seen distance being defined as the difference in z between the camera and the point. However, that cannot be completely right though, since x and y have the same effect as z on the actual distance from the camera to the point. I implemented distance as the actual distance, but the result gives a somewhat skewed perspective, as if it had "too much" perspective.
My "actual distance" implementation was along the lines of:
distance = new Vector(pointX, pointY, cameraZ - pointZ).magnitude()
Playing around with the code, I added an extra variable to my equation, a perspectiveCoefficient as follows:
distance = new Vector(pointX * perspectiveCoefficient,
pointY * perspectiveCoefficient, cameraZ - pointZ).magnitude()
For some reason, that is beyond me, I tend to get the best result setting the perspectiveCoefficient to 1/sqrt(2).
My 3D test cube is at http://vega.soi.city.ac.uk/~abdv866/3dcubetest/3dtest.svg. (Tested in Safari and FF.) It prompts you for a perspectiveCoefficient, where 0 gives a perspective without taking x/y distance into consideration, and 1 gives you a perspective where x, y and z distance is equally considered. It defaults to 1/sqrt(2). The cube can be rotated about x and y using the arrow keys. (For anyone interested, the relevant code is in update() in the View.js file.)
Grateful for any ideas on this.
Usually, projection is done on the Z=0 plane from an eye position behind this plane. The projected point is the intersection of the line (Pt,Eye) with the Z=0 plane. At the end you get something like:
screenX = scaling * pointX / (1 + pointZ/eyeDist)
screenY = scaling * pointY / (1 + pointZ/eyeDist)
I assume here the camera is at (0,0,0) and eye at (0,0,-eyeDist). If eyeDist becomes infinite, you obtain a parallel projection.

Resources