|
| 1 | +""" |
| 2 | +Snell's Law — Refraction Angle Calculation. |
| 3 | +
|
| 4 | +Calculates the angle of refraction when light passes between two |
| 5 | +media using Snell's Law: n1 * sin(theta1) = n2 * sin(theta2). |
| 6 | +
|
| 7 | +Reference: https://en.wikipedia.org/wiki/Snell%27s_law |
| 8 | +""" |
| 9 | + |
| 10 | +import math |
| 11 | + |
| 12 | + |
| 13 | +def calculate_refraction_angle( |
| 14 | + index_of_refraction_1: float, |
| 15 | + index_of_refraction_2: float, |
| 16 | + incident_angle_degrees: float, |
| 17 | +) -> float: |
| 18 | + """ |
| 19 | + Calculates the refraction angle of light passing from one medium to another. |
| 20 | + The law states that, for a given pair of media, the ratio of the sines |
| 21 | + of angle of incidence and angle of refraction s equal to the refractive |
| 22 | + index of the second medium with regard to the first which is equal to the |
| 23 | + ratio of the refractive indices of the two media, or equivalently, to the |
| 24 | + ratio of the phase velocities in the two media. |
| 25 | +
|
| 26 | +
|
| 27 | + Formula: n1 * sin(theta1) = n2 * sin(theta2) |
| 28 | + or : (sin(theta1) / sin(theta2)) = (n2 / n1) |
| 29 | + Where: |
| 30 | + n1 = refractive index of the first medium |
| 31 | + n2 = refractive index of the second medium |
| 32 | + theta1 = angle of incidence |
| 33 | + theta2 = angle of refraction |
| 34 | +
|
| 35 | + Note: Total Internal Reflection (TIR) occurs when light travels from a |
| 36 | + denser medium to a rarer medium and the incident angle exceeds the |
| 37 | + critical angle, making refraction impossible. |
| 38 | +
|
| 39 | + Sources: |
| 40 | + - https://en.wikipedia.org/wiki/Snell%27s_law |
| 41 | +
|
| 42 | + ----------------------------------------------------------------------------- |
| 43 | +
|
| 44 | + >>> calculate_refraction_angle(1.0, 1.33, 60.0) |
| 45 | + 40.63 |
| 46 | + >>> calculate_refraction_angle(2.0, 1.0, 30.0) |
| 47 | + 90.0 |
| 48 | + >>> calculate_refraction_angle(1.33, 1.0, 60.0) |
| 49 | + Traceback (most recent call last): |
| 50 | + ... |
| 51 | + ValueError: Invalid physical inputs: ratio cannot be outside [-1, 1]. |
| 52 | + >>> calculate_refraction_angle(-1.33, 1.0, 60.0) |
| 53 | + Traceback (most recent call last): |
| 54 | + ... |
| 55 | + ValueError: Invalid physical inputs: ratio cannot be outside [-1, 1]. |
| 56 | + >>> |
| 57 | + """ |
| 58 | + |
| 59 | + incident_angle_radians = math.radians(incident_angle_degrees) |
| 60 | + refraction_sine = (index_of_refraction_1 / index_of_refraction_2) * math.sin( |
| 61 | + incident_angle_radians |
| 62 | + ) |
| 63 | + |
| 64 | + # If the sine value is approximately 1.0 or -1.0, it's at the critical angle |
| 65 | + # We use math.isclose to account for floating-point precision errors |
| 66 | + if math.isclose(refraction_sine, 1.0): |
| 67 | + return 90.0 |
| 68 | + if math.isclose(refraction_sine, -1.0): |
| 69 | + return -90.0 |
| 70 | + |
| 71 | + if not -1 <= refraction_sine <= 1: |
| 72 | + raise ValueError("Invalid physical inputs: ratio cannot be outside [-1, 1].") |
| 73 | + |
| 74 | + refraction_angle = round(math.degrees(math.asin(refraction_sine)), 2) |
| 75 | + return refraction_angle |
0 commit comments