the home site for me: also iteration 3 or 4 of my site
1+++ 2title = "Determining the properties of a spherical mirror with ray diagrams" 3date = 2025-02-26 4slug = "spherical-ray-diagrams" 5description = "yes i made a tool to help with it :)" 6 7[taxonomies] 8tags = ["tool", "fancy", "physics"] 9+++ 10 11I was recently working through the Geometric Optics section of my physics textbook and was having trouble drawing all the ray diagrams (my wrist is still in a cast though that should come off in a few weeks) so I decided to try and make a tool to make them for me instead! I rather expected this to be a fairly simple process but instead it ended up being one of the most math intensive, most difficult — and also most rewarding — projects I've made recently! 12 13<!-- more --> 14 15## the tool (🥁 roll please) 16 17> this tool does support keyboard navigation btw ^-^ 18> `arrow keys` to move and `+` and `-` to zoom 19 20{{ lensDiagram() }} 21 22## the math 23 24I was able to make it a bit simpler by restricting the domain of this tool to spherical mirrors (the only type used in this Module of my physics textbook) but I did tackle both concave and convex mirrors. It generates 3 rays: a horizontal ray, a ray through the focal point, and a ray through the radius of curvature. The first and last are quite easy to generate but the third was a bit more difficult. I ended up using a formula that I don't quite understand to get the point on the mirror where the ray intersects but it does work so 🤷. 25 26The horizontal ray was dead simple. Draw a line from the top of the arrow to the edge of the mirror and then draw another line from focal point through the intersection point in the mirror. The part of that ray that is behind the mirror is simply the extension of the ray for virtual images but the part in front of the mirror is the actual path of the ray. 27 28```javascript 29// Draw the horizontal ray 30ctx.strokeStyle = "green"; 31ctx.beginPath(); 32ctx.lineTo(objX, objY - h); 33let intersectionX = 34 Math.sqrt((R * scale) ** 2 - h ** 2) + circleCenterX; 35ctx.lineTo(intersectionX, objY - h); 36extendRayToCanvasEdge( 37 intersectionX, 38 objY - h, 39 centerX - F * scale, 40 centerY, 41); 42ctx.stroke(); 43``` 44 45The ray through the radius of curvature was also fairly simple but alot more fun to figure out the math for. Since we know that there is a right angle triange between the arrow, center line, and the radius we can use the pythagorean theorem to find the missing side of the intersection height and then we can use the ratio of the radius to the arrow base to find the proper x offset. 46 47```javascript 48// Draw the ray through the radius of curvature 49ctx.strokeStyle = "orange"; 50ctx.beginPath(); 51ctx.lineTo(objX, objY - h); 52ctx.lineTo(circleCenterX, centerY); 53const extendedRay3 = findCircleIntersection( 54 R * scale, 55 objX, 56 h, 57 circleCenterX, 58 centerY, 59 circleCenterX, 60 centerY, 61); 62ctx.lineTo(extendedRay3[0].x, extendedRay3[0].y); 63extendRayToCanvasEdge( 64 extendedRay3[0].x, 65 extendedRay3[0].y, 66 centerX - R * scale, 67 centerY, 68); 69ctx.stroke(); 70``` 71 72The last ray, the one through the focal point, was the most difficult to figure out. I had to do quite a bit of geometry to find where this ray intersects the mirror. To find this intersection point I used a method that finds where a line intersects with a circle by solving a quadratic equation. This was necessary because the mirror is actually just part of a circle, and by finding where the ray intersects with that circle I can then determine if that intersection point is actually on the mirror's surface. 73 74```javascript 75// Draw the ray through the focal point 76ctx.strokeStyle = "purple"; 77ctx.beginPath(); 78ctx.lineTo(objX, objY - h); 79ctx.lineTo(centerX - F * scale, centerY); 80const extendedRay2 = findCircleIntersection( 81 R * scale, 82 objX, 83 h, 84 centerX - F * scale, 85 centerY, 86 circleCenterX, 87 centerY, 88); 89ctx.lineTo(extendedRay2[0].x, extendedRay2[0].y); 90ctx.lineTo(0, extendedRay2[0].y); 91ctx.stroke(); 92``` 93 94The method works by taking the equation of the line between our arrow tip and focal point (y = mx + b) and the equation of our mirror's circle ((x-h)² + (y-k)² = r²) and substituting one into the other. This gives us a quadratic equation that we can solve to find the x coordinates of the intersection points. Once we have these x values, we can plug them back into our line equation to get the y coordinates. 95 96Then we just need to check which of these intersection points is actually on the mirror's surface (since a line can intersect a circle in up to two points) and use that for our ray. From there, we can draw the reflected ray just like with the other two methods. 97 98I will freely admit that I made heavy use of gpt-4o to figure out the inital equations as thats a bit beyond the current scope of my knowledge. The rest of the ray logic was too complex for gemini or claude to figure out so that bit was all me 😎 99 100```javascript 101// fancy complex scary math 👻 102function findCircleIntersection(radius, x1, h, x3, y3, centerX, centerY) { 103 // Check if the input values are valid 104 if (radius <= 0) { 105 throw new Error("Invalid input values."); 106 } 107 108 // Calculate the slope of the line from (x1, h) to (x3, y3) 109 const m = (y3 - (centerY - h)) / (x3 - x1); 110 111 // Define the line equation: y = h + m * (x - x1) 112 // Substitute into circle equation: (x-centerX)^2 + (y-centerY)^2 = radius^2 113 // y = h + m * (x - x1) 114 // (x-centerX)^2 + (h + m*(x-x1) - centerY)^2 = radius^2 115 116 // Coefficients for the quadratic equation 117 const a = 1 + m * m; 118 const b = -2 * centerX + 2 * m * (centerY - h - centerY - m * x1); 119 const c = 120 centerX * centerX + 121 (centerY - h - centerY - m * x1) * 122 (centerY - h - centerY - m * x1) - 123 radius * radius; 124 125 // Calculate the discriminant 126 const discriminant = b * b - 4 * a * c; 127 128 if (discriminant < 0) { 129 throw new Error("No intersection found."); 130 } 131 132 // Calculate the two possible x values 133 const xIntersect1 = (-b + Math.sqrt(discriminant)) / (2 * a); 134 const xIntersect2 = (-b - Math.sqrt(discriminant)) / (2 * a); 135 136 // Calculate the corresponding y values 137 const yIntersect1 = centerY - h + m * (xIntersect1 - x1); 138 const yIntersect2 = centerY - h + m * (xIntersect2 - x1); 139 140 // Return the intersection points 141 return [ 142 { x: xIntersect1, y: yIntersect1 }, 143 { x: xIntersect2, y: yIntersect2 }, 144 ]; 145} 146```