I'm coding a "storm" for a battle royale map, and I'm trying to make a new safe zone (a perfect circle) inside of a preexisting circle.
Right now, it looks like this: Link to circle picture
This, as you can see, is a big bug in my code, which is:
cir = new Circle(random.nextInt(cir.Width-cir.X)+cir.X, random.nextInt(Math.abs(cir1.Height-cir.Y))+cir.Y, 270, 270, 135, Color.cyan, true);
cir1 is a preexisting Circle I created, which I am changing the values of.
cir is the Circle I created before (e.g. for Safe Zone 3, cir1 is the new Circle, and cir is Safe Zone 2.
My math to get the circles' $x$'s and $y$'s explained:
The math is: $rndInt(d-x)+x$ and $rndInt(|d-y|)+y$.
$d$ is the diameter of the circle, $x$ is the left x value, and $y$ is the top y of the circle.
This - in theory - should work, but it doesn't, as seen in the image.
In it, I basically get a random number between $0$ and the difference of the diameter and x.
From there, I add it to x. In theory, this will make a number and add it to x, making it between the preexisting circle.
Is there anything I'm doing wrong? I've spent weeks trying to figure out a formula, and this is the best I've got.
I am not absolutely sure if I understood your problem statement correctly, so here is the question as I understood it:
Let's assume the existing circle is centered at $(X, Y)$ and has radius $R = D/2$, where $D$ is its diameter.
First, pick a radius $r$, $0 \lt r \le R$ for the new circle.
Then, pick an uniform random angle $0\text{°} \le \varphi \lt 360\text{°}$, or $-180\text{°} \le \varphi \lt 180\text{°}$, or $0 \le \varphi \lt 2\pi$ in radians.
Finally, pick an uniform random deviation $d$ from center, $0 \le d \le R - r$.
The center of the new smaller circle is then $$\bbox{ \left\lbrace \; \begin{aligned} x &= X + d \cos \varphi \\ y &= Y + d \sin \varphi \\ \end{aligned} \right . }$$
The reason for using trigonometric functions instead of the axis-aligned bounding box is that you can fit a small circle completely within the bounding box of a larger circle, without overlapping at all with the larger circle. Using the radius, and choosing the direction from the center of the larger circle randomly, avoids that sort of error completely.
In pseudocode (in no particular programming language), you could implement this using
where
Random()is a function that returns an uniform random floating-point number between 0 and 1.0, inclusive (meaning it can return 0 and 1); andSin()andCos()are the respective trigonometric functions, with argument in radians; andSqrt()is the square root operation. The square root is needed to ensure an uniform distribution within the disc.If you want, you can also supply the
newRadiusparameter to the function (and not calculate it fromradius * Random()); the function will work as long asnewRadius≤radius.If you cannot use trigonometric functions, we can rewrite the function as
This is just as good as the trigonometric function using one, except that it calls
Random()about 27% "extra times" on average. (This uses the "exclusion method" to generate a random point within a circular disc. It generates a random point within the axis-aligned square that contains the disc, but ignores any point outside the disc. Since the ratio of their areas is $4 / \pi \approx 1.27324$, the do-while loop iterates 1.27324 times on average. That is, most times it only does one iteration, and sometimes more than one.)If your circles are determined by their axis-aligned bounding box, note that the left edge (minimum $x$) is at $X - R$; right edge (maximum $x$) at $X + R$; top edge (minimum $y$) is at $Y - R$; and bottom edge (maximum $y$) at $Y + R$.
If you define your circles based on their axis-aligned bounding box, then center X is
(right + left) / 2=left + width/2, center Y is(bottom + top) / 2=top + height/2, and radius is(right - left) / 2=(bottom - top) / 2=(right - left + bottom - top) / 4=width / 2=height / 2=(width + height) / 2.