File size: 5,167 Bytes
e08e802
 
 
 
 
 
 
64b00ed
 
 
 
 
 
 
 
 
e08e802
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
adc8d2e
e08e802
 
 
 
 
 
 
66b2203
 
 
e08e802
 
adc8d2e
e08e802
64b00ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e08e802
 
 
 
 
 
64b00ed
cc6e945
64b00ed
 
 
 
 
 
cc6e945
64b00ed
 
e08e802
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c4cb15
cc6e945
e08e802
64b00ed
 
e08e802
cc6e945
e08e802
 
 
adc8d2e
 
 
e08e802
cc6e945
dc9063e
e08e802
 
 
 
cc6e945
 
66b2203
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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) # This is the incident ray
    
    normal_vector = hit_point - np.array([a, b]) #Normal at point of reflection
    # ax.plot([a, x_hit], [b, y_hit], color='green', lw=1)

    # Reflection, this is key
    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

        # raise ValueError("Circle radius is too large for the given center coordinates.")
    
    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
    
        # Function to generate a line from origin at a given angle
    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):
        # dx = mt.cos(angle)
        # dy = mt.sin(angle)
        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)
    # plot_reflection_on_circle(ax, angle, center=(a, b), radius=r)
    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}"