| import matplotlib.pyplot as plt |
| import numpy as np |
| import math as mt |
|
|
| def reflect_vector(v, n): |
| n = n / np.linalg.norm(n) |
| return v - 2 * np.dot(v, n) * n |
| def quad_solver(a, b, c): |
| det = b**2 - 4*a*c |
| if det < 0: |
| raise ValueError("No real roots") |
| elif det == 0: |
| return -b / (2*a), -b / (2*a) |
| else: |
| return (-b - mt.sqrt(det)) / (2*a), (-b + mt.sqrt(det)) / (2*a) |
| |
| def plot_reflection_on_circle(ax, angle, center, radius, ray_length=50, color='blue'): |
| a, b = center |
| origin = np.array([0, 0]) |
| dx = np.cos(angle) |
| dy = np.sin(angle) |
|
|
| A = dx**2 + dy**2 |
| B = -2 * (a * dx + b * dy) |
| C = a**2 + b**2 - radius**2 |
|
|
| roots = np.roots([A, B, C]) |
| ts = [t for t in roots if t > 0] |
| if not ts: |
| print(f"No intersection at angle {angle}") |
| return |
|
|
| t_hit = min(ts) |
| x_hit = t_hit * dx |
| y_hit = t_hit * dy |
| hit_point = np.array([x_hit, y_hit]) |
|
|
| ax.plot([0, x_hit], [0, y_hit], color='blue', lw=1, zorder=10) |
| |
| normal_vector = hit_point - np.array([a, b]) |
| |
|
|
| |
| incident_vector = hit_point - origin |
| reflected_vector = reflect_vector(incident_vector, normal_vector) |
| reflected_unit = 1000* reflected_vector / np.linalg.norm(reflected_vector) |
|
|
| ax.arrow(x_hit, y_hit, |
| reflected_unit[0] * ray_length, |
| reflected_unit[1] * ray_length, |
| head_width=1.8, head_length=1.5, |
| fc=color, ec=color, zorder=10) |
|
|
| return incident_vector, reflected_vector |
|
|
|
|
|
|
| def reflecting_plotter(a = 20, b = 20, r = 15, ray_count = 15, clutter = "No"): |
| max_dim = max(abs(a), abs(b), r) * 3 |
| fig, ax = plt.subplots() |
| ax.set_xlim(-max_dim, max_dim) |
| ax.set_ylim(-max_dim, max_dim) |
| ax.set_aspect('equal', adjustable='box') |
| ax.set_xlabel('X-axis') |
| ax.set_ylabel('Y-axis') |
| ax.axhline(0, color='black', lw=1) |
| ax.axvline(0, color='black', lw=1) |
|
|
| circle = plt.Circle((a, b), r, color='black', fill=False) |
| ax.add_artist(circle) |
| ax.plot(a, b, 'ro', markersize=5) |
| |
| def inside_circle_plotter(): |
| """Function to plot the rays inside the circle""" |
| increment = 2 * mt.pi / ray_count |
|
|
| for angle in np.arange(0, 2 * mt.pi, increment): |
| dx = mt.cos(angle) |
| dy = mt.sin(angle) |
|
|
| A = dx**2 + dy**2 |
| B = -2 * (a * dx + b * dy) |
| C = a**2 + b**2 - r**2 |
|
|
| try: |
| t1, t2 = quad_solver(A, B, C) |
|
|
| valid_ts = [t for t in (t1, t2) if t > 0] |
| if not valid_ts: |
| continue |
| t_hit = min(valid_ts) |
| |
| x = [0, t_hit * dx] |
| y = [0, t_hit * dy] |
| ax.plot(x, y, color='orange', lw=1) |
| except ValueError: |
| continue |
|
|
| theta_center = mt.atan2(b, a) |
| d = mt.hypot(a, b) |
| |
| try: |
| delta = mt.asin(r / d) |
| except: |
| inside_circle_plotter() |
| ax.set_title(f'Rays origin - (0,0). From inside a perfectly reflective circle\nCenter-({a},{b}), Radius-{r}') |
| plt.grid(True) |
| plt.show() |
|
|
| fig.canvas.draw() |
| image_array = np.array(fig.canvas.renderer.buffer_rgba()) |
| plt.close(fig) |
| return image_array, 100 |
|
|
| |
| |
| lower_angle = theta_center - delta |
| upper_angle = theta_center + delta |
| |
| def normalize(angle): |
| return angle % (2 * mt.pi) |
| |
| lower_angle = normalize(lower_angle) |
| upper_angle = normalize(upper_angle) |
| |
| def is_angle_between(angle, start, end): |
| angle = normalize(angle) |
| start = normalize(start) |
| end = normalize(end) |
| if start < end: |
| return start <= angle <= end |
| else: |
| return angle >= start or angle <= end |
| |
| |
| def draw_line(angle, length=max(max_dim, 500), x_0=0, y_0=0): |
| x_1 = length * mt.cos(angle) + x_0 |
| y_1 = length * mt.sin(angle) + y_0 |
| return [x_0, x_1], [y_0, y_1] |
| |
| increment = 2*mt.pi/ray_count |
| total_hits = 0 |
| for angle in np.arange(0, 2 * np.pi, increment): |
| |
| |
| if is_angle_between(angle, lower_angle, upper_angle): |
| total_hits += 1 |
| plot_reflection_on_circle(ax, angle, center=(a, b), radius=r) |
| |
| else: |
| if clutter == "No": |
| x, y = draw_line(angle) |
| ax.plot(x, y, color='red', lw=1, zorder=5) |
| |
| ax.set_title(f'Rays with shadow from a perfectly reflective circle,\nCenter-({a},{b}), Radius-{r}') |
| plt.grid(True) |
| plt.show() |
| fig.canvas.draw() |
| image_array = np.array(fig.canvas.renderer.buffer_rgba()) |
| plt.close(fig) |
|
|
| hit_ratio = 100*total_hits / ray_count |
| return image_array, f"{hit_ratio:.5f}" |