A few days ago, my daughters wanted to know how to find out the bounded area of two intersecting circles. This program is an attempt to teach them both Trigonometry as well as computer science. We started off doing only the basic condition when both circles play nice and intersect with the distance between centers parallel to the horizontal axis. Slowly, we extended the code to cater for other cases where the circles only touch at one point, may not touch at all or one circle is completely inside the other circle.
Basically, the input takes three parameters -> the distance between the centers of the two circles and the radii of the two circles in question. The common area is the output of the program.
Looking for ways to make this a more optimal program as I'm new to python.
import sys
import math
# Return the area of the smaller circle
def smallercirclearea(r1, r2):
radsmaller = min(r1, r2)
return math.pi * radsmaller * radsmaller
def calcAreaIntersectingCircles(d, rad1, rad2):
rad1sqr = rad1 * rad1
rad2sqr = rad2 * rad2
#if the circle centers are the same
if d == 0:
print ('\nCircles have the same center. Intersecting area will be area of smaller circle')
return smallercirclearea(rad1,rad2)
angle1 = (rad1sqr + (d * d) - rad2sqr) / (2 * rad1 * d)
angle2 = (rad2sqr + (d * d) - rad1sqr) / (2 * rad2 * d)
# Check to see if the circles are overlapping
if ((angle1 < 1 and angle1 >= -1) or (angle2 < 1 and angle2 >= -1)):
theta1 = (math.acos(angle1) * 2)
theta2 = (math.acos(angle2) * 2)
area1 = (0.5 * theta2 * rad2sqr) - (0.5 * rad2sqr * math.sin(theta2))
area2 = (0.5 * theta1 * rad1sqr) - (0.5 * rad1sqr * math.sin(theta1))
return area1 + area2
elif angle1 == 1 and angle2 == 1:
print ('\nCircles touch at a single degenerate point and do not intersect\n')
return 0
elif angle1 < -1 or angle2 < -1:
print('\nSmaller circle is completely inside the larger circle. Intersecting area will be area of smaller circle')
return smallercirclearea(rad1,rad2)
else:
print ('\nImaginary touch points\n')
return -1
#Read the distance and radii from the command line
if len(sys.argv) == 4:
distance = int(sys.argv[1])
radiusCircle1 = int(sys.argv[2])
radiusCircle2 = int(sys.argv[3])
if distance < 0 or radiusCircle1 < 0 or radiusCircle2 < 0:
print('Values cannot be negative. Goodbye')
exit(1)
print('\nThe intersecting area of the two circles is', round(calcAreaIntersectingCircles(float(distance), float(radiusCircle1), float(radiusCircle2)), 2), 'square units\n')
else:
print('\nNEED 3 VALUES IN THE INPUT!!\n')
exit(1)
Top comments (5)
This is really cool! It's neat that you're doing this with your daughters. Hopefully, they liked it!
Since it's pretty much a straightforward one-off calculation, I don't think there's a whole lot of optimizing you can do. I do have a couple "this is how you'll see it a lot in Python code" kind of things, though.
Generally, in a Python script like this where your interface (argument processing, getting user input, etc.) is in the same file as your main logic, you'll see bottom section written like this:
Python modules get a
__name__
attribute when they're run. If they're called directly (a lapython gimme-that-area.py
), the__name__
attribute is set to__main__
. If you import them, (likeimport gimme-that-area
), the__name__
is set to "gimme-that-area". So this way, it allows you to import your code and use yourcalcAreaIntersectingCircles
function in other places without erroring out due to missing command line args.Other than that, if you want to get a feel for traditional Python style, you should have a read through PEP8, the main part of Python's style guide. You can run your code through this checker for some automatic feedback. Once you get comfortable installing packages with
pip
, try out pep8 or, my favorite, flake8.If you ever have any questions about Python, feel free to reach out to me. I'm always happy to answer questions or help out.
Very helpful. Will check out the style guide. And will definitely reach out for any questions. Thank you
No real comments on making it more optimal, but I'd press them for some code cleanliness.
I'd consolidate my variable names if I were you. You use r1, rad1, radiusCircle1, rad1sqr and radius - and d and distance for that matter. I'd not be able to guess whether "d" meant distance or diameter without inspecting the code further.
The position of the numbers in your variable names is inconsistent meaning that it takes me a second longer to parse between rad1sqr (lowercase, number in middle) and radiusCircle1 (camelCase, number at end)
You switch between lowercase and camelCase for function names, sometimes abbreviating parts of compound names ("calc") and sometimes opting for the more verbose.
I'd probably make the first function shorter:
`
because the additional variable doesn't really help with readability, and this replacement follows the familiar pi - r - squared more closely. In fact, I'd be tempted to split it into
python
``def circleArea(radius):
"""Calculate the area of a circle from its radius."""
return math.pi * (radius ** 2)
and call it with
python
``circleArea(min(r1, r2))
when you need the smaller area, so that everything is a little more general and it can be used to get the larger area without having to duplicate the function. I've replaced the comment with a docstring so you can inspect it with other tools - in the simplest case in a debugger you can ask for "help circleArea" and it will come back with that message.
You cast the program's arguments to integers but the only place you use them you re-cast them to floats. You don't need to do this; either ints or floats will work fine.
I'd also suggest putting the main code inside a main function, which isn't absolutely required but it's traditional.
Thank you. Appreciate the detailed feedback.
I've made the changes suggested. Thanks everyone!