Ray-tracing-simulation / backend /nonreflecting_ray_tracing.py
DebasishDhal99's picture
Add center and axes in reflective, fix edge case in non-refletive
66b2203
import matplotlib.pyplot as plt
import numpy as np
import math as mt
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 normalize(angle):
return angle % (2 * mt.pi)
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 nonreflecting_plotter(a = 20, b = 20, r = 15, ray_count = 50, 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='blue', fill=False)
ax.add_artist(circle)
ax.plot(a, b, 'ro', markersize=5) # Circle center
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]
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 absorbing 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
# raise ValueError("Circle radius is too large for the given center coordinates.")
lower_angle = theta_center - delta
upper_angle = theta_center + delta
lower_angle = normalize(lower_angle)
upper_angle = normalize(upper_angle)
increment = 2*mt.pi/ray_count
total_hits = 0
for angle in np.arange(0, 2 * mt.pi, increment): # 1° steps
dx = mt.cos(angle)
dy = mt.sin(angle)
if is_angle_between(angle, lower_angle, upper_angle):
total_hits += 1
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)
if abs(t1) < abs(t2):
t_hit = t1
else:
t_hit = t2
# t_hit = min(t1, t2)
if t_hit < 0:
continue
x = [0, t_hit * dx]
y = [0, t_hit * dy]
ax.plot(x, y, color='orange', lw=1)
except ValueError:
continue
else:
if clutter == "Yes":
continue
x, y = draw_line(angle)
ax.plot(x, y, color='red', lw=1)
x1, y1 = draw_line(lower_angle)
x2, y2 = draw_line(upper_angle)
ax.plot(x1, y1, color='green', lw=2, linestyle='--')
ax.plot(x2, y2, color='green', lw=2, linestyle='--')
ax.set_title(f'Rays with shadow from a perfectly absorbing 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 = (total_hits / ray_count) * 100
return image_array, f"{hit_ratio:.5f}"