How to make a circle within another circle?
$begingroup$
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.
geometry random
$endgroup$
add a comment |
$begingroup$
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.
geometry random
$endgroup$
2
$begingroup$
Although this is a maths QA stack, if you could provide an MWE that might rule out any mathematical errors on your part... Otherwise, perhaps good old Stack Overflow might be your friend, too.
$endgroup$
– MacRance
Dec 26 '18 at 4:18
$begingroup$
To get a point inside the circle, the relation isx+rndInt(d)
,y-rndInt(d)$
$endgroup$
– Shubham Johri
Dec 26 '18 at 5:11
$begingroup$
I don't see why this should work even "in theory." You have two different objects (cir
andcir1
) contributing parameters to your constructor. One of these presumably is not the "preexisting" circle you wanted to stay inside. Moreover, all you would have done (if it worked like you said) is to put one corner of the bounding box inside the other bounding box--almost all the rest could be outside.
$endgroup$
– David K
Dec 26 '18 at 5:20
$begingroup$
By the way, there are multiple Java classes named Circle (or in one case Circle2D) documented online; so far yours is the only one I've seen that describes a circle by a corner of its bounding box, its width, and its height. (Can the width and height be different?) Everyone else uses the center and the radius.
$endgroup$
– David K
Dec 26 '18 at 5:26
add a comment |
$begingroup$
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.
geometry random
$endgroup$
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.
geometry random
geometry random
asked Dec 26 '18 at 4:07
Not Sp33dyNot Sp33dy
84
84
2
$begingroup$
Although this is a maths QA stack, if you could provide an MWE that might rule out any mathematical errors on your part... Otherwise, perhaps good old Stack Overflow might be your friend, too.
$endgroup$
– MacRance
Dec 26 '18 at 4:18
$begingroup$
To get a point inside the circle, the relation isx+rndInt(d)
,y-rndInt(d)$
$endgroup$
– Shubham Johri
Dec 26 '18 at 5:11
$begingroup$
I don't see why this should work even "in theory." You have two different objects (cir
andcir1
) contributing parameters to your constructor. One of these presumably is not the "preexisting" circle you wanted to stay inside. Moreover, all you would have done (if it worked like you said) is to put one corner of the bounding box inside the other bounding box--almost all the rest could be outside.
$endgroup$
– David K
Dec 26 '18 at 5:20
$begingroup$
By the way, there are multiple Java classes named Circle (or in one case Circle2D) documented online; so far yours is the only one I've seen that describes a circle by a corner of its bounding box, its width, and its height. (Can the width and height be different?) Everyone else uses the center and the radius.
$endgroup$
– David K
Dec 26 '18 at 5:26
add a comment |
2
$begingroup$
Although this is a maths QA stack, if you could provide an MWE that might rule out any mathematical errors on your part... Otherwise, perhaps good old Stack Overflow might be your friend, too.
$endgroup$
– MacRance
Dec 26 '18 at 4:18
$begingroup$
To get a point inside the circle, the relation isx+rndInt(d)
,y-rndInt(d)$
$endgroup$
– Shubham Johri
Dec 26 '18 at 5:11
$begingroup$
I don't see why this should work even "in theory." You have two different objects (cir
andcir1
) contributing parameters to your constructor. One of these presumably is not the "preexisting" circle you wanted to stay inside. Moreover, all you would have done (if it worked like you said) is to put one corner of the bounding box inside the other bounding box--almost all the rest could be outside.
$endgroup$
– David K
Dec 26 '18 at 5:20
$begingroup$
By the way, there are multiple Java classes named Circle (or in one case Circle2D) documented online; so far yours is the only one I've seen that describes a circle by a corner of its bounding box, its width, and its height. (Can the width and height be different?) Everyone else uses the center and the radius.
$endgroup$
– David K
Dec 26 '18 at 5:26
2
2
$begingroup$
Although this is a maths QA stack, if you could provide an MWE that might rule out any mathematical errors on your part... Otherwise, perhaps good old Stack Overflow might be your friend, too.
$endgroup$
– MacRance
Dec 26 '18 at 4:18
$begingroup$
Although this is a maths QA stack, if you could provide an MWE that might rule out any mathematical errors on your part... Otherwise, perhaps good old Stack Overflow might be your friend, too.
$endgroup$
– MacRance
Dec 26 '18 at 4:18
$begingroup$
To get a point inside the circle, the relation is
x+rndInt(d)
,y-rndInt(d)$
$endgroup$
– Shubham Johri
Dec 26 '18 at 5:11
$begingroup$
To get a point inside the circle, the relation is
x+rndInt(d)
,y-rndInt(d)$
$endgroup$
– Shubham Johri
Dec 26 '18 at 5:11
$begingroup$
I don't see why this should work even "in theory." You have two different objects (
cir
and cir1
) contributing parameters to your constructor. One of these presumably is not the "preexisting" circle you wanted to stay inside. Moreover, all you would have done (if it worked like you said) is to put one corner of the bounding box inside the other bounding box--almost all the rest could be outside.$endgroup$
– David K
Dec 26 '18 at 5:20
$begingroup$
I don't see why this should work even "in theory." You have two different objects (
cir
and cir1
) contributing parameters to your constructor. One of these presumably is not the "preexisting" circle you wanted to stay inside. Moreover, all you would have done (if it worked like you said) is to put one corner of the bounding box inside the other bounding box--almost all the rest could be outside.$endgroup$
– David K
Dec 26 '18 at 5:20
$begingroup$
By the way, there are multiple Java classes named Circle (or in one case Circle2D) documented online; so far yours is the only one I've seen that describes a circle by a corner of its bounding box, its width, and its height. (Can the width and height be different?) Everyone else uses the center and the radius.
$endgroup$
– David K
Dec 26 '18 at 5:26
$begingroup$
By the way, there are multiple Java classes named Circle (or in one case Circle2D) documented online; so far yours is the only one I've seen that describes a circle by a corner of its bounding box, its width, and its height. (Can the width and height be different?) Everyone else uses the center and the radius.
$endgroup$
– David K
Dec 26 '18 at 5:26
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
I am not absolutely sure if I understood your problem statement correctly, so here is the question as I understood it:
How do I ensure that a randomly generated circle is completely contained in an existing circle?
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 $0text{°} le varphi lt 360text{°}$, or $-180text{°} le varphi lt 180text{°}$, or $0 le varphi lt 2pi$ 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{ leftlbrace ; 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
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let radians = 2.0 * 3.14159265358979323846 * Random()
Let deviation = (radius - newRadius) * Sqrt(Random())
Let newX = centerX + deviation * Cos(radians)
Let newY = centerY + deviation * Sin(radians)
Return (newX, newY, newRadius)
End Function
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); and Sin()
and Cos()
are the respective trigonometric functions, with argument in radians; and Sqrt()
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 newRadius
parameter to the function (and not calculate it from radius * Random()
); the function will work as long as newRadius
≤ radius
.
If you cannot use trigonometric functions, we can rewrite the function as
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let deviation = (radius - newRadius) * Random()
Let dd = deviation * deviation
Do:
dX = deviation - 2 * deviation * Random()
dY = deviation - 2 * deviation * Random()
While (dX*dX + dY*dY > dd)
Let newX = centerX + dX
Let newY = centerY + dY
Return (newX, newY, newRadius)
End Function
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
.
$endgroup$
$begingroup$
@DavidK: Good point. That radial distribution in an unit disc is obtained by taking the square root of the uniform distribution; now fixed. Thank you!
$endgroup$
– Nominal Animal
Dec 26 '18 at 10:51
$begingroup$
Yes, that looks good. Either of these methods should be fine for what OP asked for.
$endgroup$
– David K
Dec 26 '18 at 14:57
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["$", "$"], ["\\(","\\)"]]);
});
});
}, "mathjax-editing");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "69"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
noCode: true, onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmath.stackexchange.com%2fquestions%2f3052626%2fhow-to-make-a-circle-within-another-circle%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
I am not absolutely sure if I understood your problem statement correctly, so here is the question as I understood it:
How do I ensure that a randomly generated circle is completely contained in an existing circle?
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 $0text{°} le varphi lt 360text{°}$, or $-180text{°} le varphi lt 180text{°}$, or $0 le varphi lt 2pi$ 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{ leftlbrace ; 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
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let radians = 2.0 * 3.14159265358979323846 * Random()
Let deviation = (radius - newRadius) * Sqrt(Random())
Let newX = centerX + deviation * Cos(radians)
Let newY = centerY + deviation * Sin(radians)
Return (newX, newY, newRadius)
End Function
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); and Sin()
and Cos()
are the respective trigonometric functions, with argument in radians; and Sqrt()
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 newRadius
parameter to the function (and not calculate it from radius * Random()
); the function will work as long as newRadius
≤ radius
.
If you cannot use trigonometric functions, we can rewrite the function as
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let deviation = (radius - newRadius) * Random()
Let dd = deviation * deviation
Do:
dX = deviation - 2 * deviation * Random()
dY = deviation - 2 * deviation * Random()
While (dX*dX + dY*dY > dd)
Let newX = centerX + dX
Let newY = centerY + dY
Return (newX, newY, newRadius)
End Function
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
.
$endgroup$
$begingroup$
@DavidK: Good point. That radial distribution in an unit disc is obtained by taking the square root of the uniform distribution; now fixed. Thank you!
$endgroup$
– Nominal Animal
Dec 26 '18 at 10:51
$begingroup$
Yes, that looks good. Either of these methods should be fine for what OP asked for.
$endgroup$
– David K
Dec 26 '18 at 14:57
add a comment |
$begingroup$
I am not absolutely sure if I understood your problem statement correctly, so here is the question as I understood it:
How do I ensure that a randomly generated circle is completely contained in an existing circle?
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 $0text{°} le varphi lt 360text{°}$, or $-180text{°} le varphi lt 180text{°}$, or $0 le varphi lt 2pi$ 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{ leftlbrace ; 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
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let radians = 2.0 * 3.14159265358979323846 * Random()
Let deviation = (radius - newRadius) * Sqrt(Random())
Let newX = centerX + deviation * Cos(radians)
Let newY = centerY + deviation * Sin(radians)
Return (newX, newY, newRadius)
End Function
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); and Sin()
and Cos()
are the respective trigonometric functions, with argument in radians; and Sqrt()
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 newRadius
parameter to the function (and not calculate it from radius * Random()
); the function will work as long as newRadius
≤ radius
.
If you cannot use trigonometric functions, we can rewrite the function as
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let deviation = (radius - newRadius) * Random()
Let dd = deviation * deviation
Do:
dX = deviation - 2 * deviation * Random()
dY = deviation - 2 * deviation * Random()
While (dX*dX + dY*dY > dd)
Let newX = centerX + dX
Let newY = centerY + dY
Return (newX, newY, newRadius)
End Function
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
.
$endgroup$
$begingroup$
@DavidK: Good point. That radial distribution in an unit disc is obtained by taking the square root of the uniform distribution; now fixed. Thank you!
$endgroup$
– Nominal Animal
Dec 26 '18 at 10:51
$begingroup$
Yes, that looks good. Either of these methods should be fine for what OP asked for.
$endgroup$
– David K
Dec 26 '18 at 14:57
add a comment |
$begingroup$
I am not absolutely sure if I understood your problem statement correctly, so here is the question as I understood it:
How do I ensure that a randomly generated circle is completely contained in an existing circle?
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 $0text{°} le varphi lt 360text{°}$, or $-180text{°} le varphi lt 180text{°}$, or $0 le varphi lt 2pi$ 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{ leftlbrace ; 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
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let radians = 2.0 * 3.14159265358979323846 * Random()
Let deviation = (radius - newRadius) * Sqrt(Random())
Let newX = centerX + deviation * Cos(radians)
Let newY = centerY + deviation * Sin(radians)
Return (newX, newY, newRadius)
End Function
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); and Sin()
and Cos()
are the respective trigonometric functions, with argument in radians; and Sqrt()
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 newRadius
parameter to the function (and not calculate it from radius * Random()
); the function will work as long as newRadius
≤ radius
.
If you cannot use trigonometric functions, we can rewrite the function as
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let deviation = (radius - newRadius) * Random()
Let dd = deviation * deviation
Do:
dX = deviation - 2 * deviation * Random()
dY = deviation - 2 * deviation * Random()
While (dX*dX + dY*dY > dd)
Let newX = centerX + dX
Let newY = centerY + dY
Return (newX, newY, newRadius)
End Function
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
.
$endgroup$
I am not absolutely sure if I understood your problem statement correctly, so here is the question as I understood it:
How do I ensure that a randomly generated circle is completely contained in an existing circle?
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 $0text{°} le varphi lt 360text{°}$, or $-180text{°} le varphi lt 180text{°}$, or $0 le varphi lt 2pi$ 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{ leftlbrace ; 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
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let radians = 2.0 * 3.14159265358979323846 * Random()
Let deviation = (radius - newRadius) * Sqrt(Random())
Let newX = centerX + deviation * Cos(radians)
Let newY = centerY + deviation * Sin(radians)
Return (newX, newY, newRadius)
End Function
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); and Sin()
and Cos()
are the respective trigonometric functions, with argument in radians; and Sqrt()
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 newRadius
parameter to the function (and not calculate it from radius * Random()
); the function will work as long as newRadius
≤ radius
.
If you cannot use trigonometric functions, we can rewrite the function as
Function RandomCircleInside(centerX, centerY, radius):
Let newRadius = radius * Random()
Let deviation = (radius - newRadius) * Random()
Let dd = deviation * deviation
Do:
dX = deviation - 2 * deviation * Random()
dY = deviation - 2 * deviation * Random()
While (dX*dX + dY*dY > dd)
Let newX = centerX + dX
Let newY = centerY + dY
Return (newX, newY, newRadius)
End Function
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
.
edited Dec 26 '18 at 10:55
answered Dec 26 '18 at 4:47
Nominal AnimalNominal Animal
7,0332517
7,0332517
$begingroup$
@DavidK: Good point. That radial distribution in an unit disc is obtained by taking the square root of the uniform distribution; now fixed. Thank you!
$endgroup$
– Nominal Animal
Dec 26 '18 at 10:51
$begingroup$
Yes, that looks good. Either of these methods should be fine for what OP asked for.
$endgroup$
– David K
Dec 26 '18 at 14:57
add a comment |
$begingroup$
@DavidK: Good point. That radial distribution in an unit disc is obtained by taking the square root of the uniform distribution; now fixed. Thank you!
$endgroup$
– Nominal Animal
Dec 26 '18 at 10:51
$begingroup$
Yes, that looks good. Either of these methods should be fine for what OP asked for.
$endgroup$
– David K
Dec 26 '18 at 14:57
$begingroup$
@DavidK: Good point. That radial distribution in an unit disc is obtained by taking the square root of the uniform distribution; now fixed. Thank you!
$endgroup$
– Nominal Animal
Dec 26 '18 at 10:51
$begingroup$
@DavidK: Good point. That radial distribution in an unit disc is obtained by taking the square root of the uniform distribution; now fixed. Thank you!
$endgroup$
– Nominal Animal
Dec 26 '18 at 10:51
$begingroup$
Yes, that looks good. Either of these methods should be fine for what OP asked for.
$endgroup$
– David K
Dec 26 '18 at 14:57
$begingroup$
Yes, that looks good. Either of these methods should be fine for what OP asked for.
$endgroup$
– David K
Dec 26 '18 at 14:57
add a comment |
Thanks for contributing an answer to Mathematics Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmath.stackexchange.com%2fquestions%2f3052626%2fhow-to-make-a-circle-within-another-circle%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
$begingroup$
Although this is a maths QA stack, if you could provide an MWE that might rule out any mathematical errors on your part... Otherwise, perhaps good old Stack Overflow might be your friend, too.
$endgroup$
– MacRance
Dec 26 '18 at 4:18
$begingroup$
To get a point inside the circle, the relation is
x+rndInt(d)
,y-rndInt(d)$
$endgroup$
– Shubham Johri
Dec 26 '18 at 5:11
$begingroup$
I don't see why this should work even "in theory." You have two different objects (
cir
andcir1
) contributing parameters to your constructor. One of these presumably is not the "preexisting" circle you wanted to stay inside. Moreover, all you would have done (if it worked like you said) is to put one corner of the bounding box inside the other bounding box--almost all the rest could be outside.$endgroup$
– David K
Dec 26 '18 at 5:20
$begingroup$
By the way, there are multiple Java classes named Circle (or in one case Circle2D) documented online; so far yours is the only one I've seen that describes a circle by a corner of its bounding box, its width, and its height. (Can the width and height be different?) Everyone else uses the center and the radius.
$endgroup$
– David K
Dec 26 '18 at 5:26