File size: 15,980 Bytes
6372af3
 
d5acdc1
360cc19
6372af3
 
 
d5acdc1
6372af3
 
 
9d36ff7
d5acdc1
9d36ff7
 
6372af3
 
 
 
 
2aa8a14
6372af3
 
 
 
 
 
 
 
388dedf
9d36ff7
 
388dedf
 
9d36ff7
6372af3
 
 
 
388dedf
9d36ff7
388dedf
 
9d36ff7
6372af3
 
 
 
 
 
 
 
 
 
388dedf
 
 
 
9d36ff7
6372af3
 
 
 
388dedf
 
1745bbb
 
388dedf
1745bbb
 
6372af3
9d36ff7
 
 
 
 
 
 
 
 
 
 
 
 
6372af3
 
0bd62ab
6372af3
 
 
168dae3
2c1bf78
c05f8a9
 
14afed2
 
168dae3
 
23c3e38
9d36ff7
168dae3
 
23c3e38
168dae3
0a90659
e71e4e1
32e4e2e
0a90659
6ba59ad
e312bc0
9d36ff7
168dae3
0a90659
9d36ff7
1745bbb
9d36ff7
0a90659
e71e4e1
a249c25
9d36ff7
e71e4e1
9d36ff7
 
e71e4e1
 
 
32e4e2e
 
e71e4e1
e312bc0
32e4e2e
ce87c5c
 
1745bbb
1a6449a
ce87c5c
 
 
1745bbb
1a6449a
ce87c5c
a4c9092
3d9fb08
544121c
 
 
 
3d9fb08
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a4c9092
 
683f81a
 
 
 
 
 
3d9fb08
 
 
683f81a
 
d70e9fb
e312bc0
 
 
3d9fb08
 
 
 
a4c9092
3d9fb08
a4c9092
d55fa25
f69cb08
d55fa25
 
 
 
0bd62ab
d55fa25
 
 
 
 
 
 
6372af3
9d36ff7
 
 
 
 
6372af3
 
7d38b03
 
51f5ca0
7d38b03
 
1604b39
869830c
1a6449a
 
 
 
 
 
 
 
869830c
3d9fb08
1a6449a
 
544121c
3d9fb08
 
 
 
1a6449a
 
544121c
3d9fb08
 
 
 
1a6449a
 
 
3d9fb08
 
6372af3
3d9fb08
 
 
869830c
6372af3
 
 
 
9d36ff7
 
 
 
 
869830c
6372af3
 
 
 
9d36ff7
 
 
 
 
869830c
6372af3
 
 
 
 
869830c
6372af3
 
77b8003
6372af3
9d36ff7
 
 
869830c
cb88584
869830c
 
 
 
 
 
 
 
78598a6
3d9fb08
 
 
78598a6
869830c
cb88584
 
 
869830c
f6669c3
 
 
869830c
cb88584
78598a6
f6669c3
 
 
78598a6
cb88584
18ca239
6372af3
 
869830c
6372af3
 
869830c
6372af3
869830c
77b8003
6372af3
d55fa25
8385c0f
 
d55fa25
 
 
 
6372af3
9d36ff7
2c1bf78
6372af3
 
168dae3
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Circle

# Function to calculate the distance between two points
def calculate_distance(x1, y1, x2, y2):
    return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

# Function to calculate angles using the Law of Cosines
def calculate_angle(a, b, c):
    try:
        angle = np.degrees(np.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)))
    except ValueError:
        angle = 0  # Handle possible domain error in acos
    return angle

# Function to calculate area using Heron's formula
def calculate_area(a, b, c):
    s = (a + b + c) / 2
    area = np.sqrt(s * (s - a) * (s - b) * (s - c))
    return area

# Function to calculate the perimeter
def calculate_perimeter(a, b, c):
    return a + b + c

# Function to calculate the radius of the inscribed circle
def calculate_radius_inscribed_circle(a, b, c):
    try:
        s = (a + b + c) / 2
        area = calculate_area(a, b, c)
        radius = area / s
    except ZeroDivisionError:
        radius = 0  # Handle case where area or perimeter is zero
    return radius

# Function to calculate the radius of the circumscribed circle
def calculate_radius_circumscribed_circle(a, b, c):
    try:
        area = calculate_area(a, b, c)
        radius = (a * b * c) / (4 * area)
    except ZeroDivisionError:
        radius = 0  # Handle case where area is zero
    return radius

# Function to calculate the centroid coordinates
def calculate_centroid(x1, y1, x2, y2, x3, y3):
    G_x = (x1 + x2 + x3) / 3
    G_y = (y1 + y2 + y3) / 3
    return G_x, G_y

# Function to calculate the incenter coordinates
def calculate_incenter(x1, y1, x2, y2, x3, y3, a, b, c):
    try:
        I_x = (a * x1 + b * x2 + c * x3) / (a + b + c)
        I_y = (a * y1 + b * y2 + c * y3) / (a + b + c)
    except ZeroDivisionError:
        I_x, I_y = 0, 0  # Handle division by zero if sides sum to zero
    return I_x, I_y

# Function to calculate the circumcenter coordinates
def calculate_circumcenter(x1, y1, x2, y2, x3, y3, a, b, c):
    try:
        D = 2 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2))
        U_x = ((x1**2 + y1**2) * (y2 - y3) + (x2**2 + y2**2) * (y3 - y1) + (x3**2 + y3**2) * (y1 - y2)) / D
        U_y = ((x1**2 + y1**2) * (x3 - x2) + (x2**2 + y2**2) * (x1 - x3) + (x3**2 + y3**2) * (x2 - x1)) / D
    except ZeroDivisionError:
        U_x, U_y = 0, 0  # Handle division by zero in circumcenter calculation
    return U_x, U_y

# Function to calculate midpoints of sides
def calculate_midpoints(x1, y1, x2, y2, x3, y3):
    # Midpoint of AB
    M1_x = (x1 + x2) / 2
    M1_y = (y1 + y2) / 2
    # Midpoint of BC
    M2_x = (x2 + x3) / 2
    M2_y = (y2 + y3) / 2
    # Midpoint of CA
    M3_x = (x3 + x1) / 2
    M3_y = (y3 + y1) / 2
    return (M1_x, M1_y), (M2_x, M2_y), (M3_x, M3_y)

# Function to format values close to zero as 0
def format_zero(val):
    if abs(val) < 1e-4:
        return 0.0
    return val

# Function to plot the triangle with all points in different colors and a legend
def plot_triangle(x1, y1, x2, y2, x3, y3, I_x, I_y, U_x, U_y, G_x, G_y, midpoints, a, b, c):
    fig, ax = plt.subplots(figsize=(10, 8))
    triangle = Polygon([(x1, y1), (x2, y2), (x3, y3)], closed=True, edgecolor='b', facecolor='lightblue', linewidth=2)
    ax.add_patch(triangle)
    
    # Define colors for different points
    vertex_color = 'blue'
    midpoint_color = 'green'
    centroid_color = 'orange'
    incenter_color = 'red'
    circumcenter_color = 'purple'
    
    # Plot the triangle vertices
    vertices = [(x1, y1), (x2, y2), (x3, y3)]
    vertex_labels = [f"Vertex A ({x1:.1f}, {y1:.1f})", f"Vertex B ({x2:.1f}, {y2:.1f})", f"Vertex C ({x3:.1f}, {y3:.1f})"]
    vertex_name = ["A", "B", "C"]
    for i, (vx, vy) in enumerate(vertices):
        ax.scatter(vx, vy, color=vertex_color, zorder=3)
        ax.text(vx+0.01*G_x, vy+0.02*G_y, vertex_name[i], fontsize=10, ha="left", va="bottom", color=vertex_color)
        
    # Plot key points with their corresponding colors
    key_points = [
        (I_x, I_y, incenter_color),
        (U_x, U_y, circumcenter_color),
        (G_x, G_y, centroid_color)
    ]
    key_points_labels = [f"Incenter ({I_x:.1f}, {I_y:.1f})", f"Circumcenter ({U_x:.1f}, {U_y:.1f})", f"Centroid ({G_x:.1f}, {G_y:.1f})"]
    
    for x, y, color in key_points:
        ax.scatter(x, y, color=color, zorder=5)

    # Plot midpoints of sides
    midpoints_labels = [f"Mid-Point M1 ({(x1 + x2) / 2:.1f}, {(y1 + y2) / 2:.1f})", 
                        f"Mid-Point M2 ({(x2 + x3) / 2:.1f}, {(y2 + y3) / 2:.1f})", 
                        f"Mid-Point M3 ({(x1 + x3) / 2:.1f}, {(y1 + y3) / 2:.1f})"]
    midpoint_name = ["M1", "M2", "M3"]
    for i, (mx, my) in enumerate(midpoints):
        ax.scatter(mx, my, color=midpoint_color, zorder=3)
        ax.text(mx+0.01*G_x, my+0.02*G_y, midpoint_name[i], fontsize=10, ha="left", va="bottom", color=midpoint_color)


    # Draw the inscribed circle (incircle)
    radius_in = calculate_radius_inscribed_circle(a, b, c)
    incircle = Circle((I_x, I_y), radius_in, color=incenter_color, fill=False, linestyle='--', linewidth=1, label="Inscribed Circle")
    ax.add_patch(incircle)
    
    # Draw the circumscribed circle (circumcircle)
    radius_circum = calculate_radius_circumscribed_circle(a, b, c)
    circumcircle = Circle((U_x, U_y), radius_circum, color=circumcenter_color, fill=False, linestyle='--', linewidth=1, label="Circumscribed Circle")
    ax.add_patch(circumcircle)


    # Calculate area and perimeter of triangle
    area = calculate_area(a, b, c)
    perimeter = calculate_perimeter(a, b, c)

    # Calculate the lengths of the sides of the triangle using Euclidean distance
    a = calculate_distance(x2, y2, x3, y3)
    b = calculate_distance(x1, y1, x3, y3)
    c = calculate_distance(x1, y1, x2, y2)
    sides = [f"Side a: {a:.1f}", f"Side b: {b:.1f}", f"Side c: {c:.1f}"]

    # Validate if it's a valid triangle
    if not is_valid_triangle(a, b, c):
        st.error("The entered points do not form a valid triangle.")
        return
        
    # Calculate angles using the Law of Cosines
    A = calculate_angle(a, b, c)
    B = calculate_angle(b, a, c)
    C = calculate_angle(c, a, b)
    vertex_angles = [f"A: {A:.1f}°", f"B: {B:.1f}°", f"C: {C:.1f}°"]

    # Check if angles sum up to 180 degrees
    if abs(A + B + C - 180) > 1e-2:
        st.error("The sum of the angles is not 180 degrees.")
        return
    
    # Add legend
    handles = [
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=vertex_color, markersize=7, label=vertex_labels[0]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=vertex_color, markersize=7, label=vertex_labels[1]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=vertex_color, markersize=7, label=vertex_labels[2]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=midpoint_color, markersize=7, label=midpoints_labels[0]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=midpoint_color, markersize=7, label=midpoints_labels[1]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=midpoint_color, markersize=7, label=midpoints_labels[2]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=vertex_color, markersize=7, label=vertex_angles[0]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=vertex_color, markersize=7, label=vertex_angles[1]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=vertex_color, markersize=7, label=vertex_angles[2]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=incenter_color, markersize=7, label=key_points_labels[0]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=circumcenter_color, markersize=7, label=key_points_labels[1]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=centroid_color, markersize=7, label=key_points_labels[2]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor="blue", markersize=7, label=sides[0]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor="blue", markersize=7, label=sides[1]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor="blue", markersize=7, label=sides[2]),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor="lightblue", markersize=7, label=f"Area: {area:.1f}"),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor="blue", markersize=7, label=f"Perimeter: {perimeter:.1f}"),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=incenter_color, markersize=7, label=f"Incircle radius: {radius_in:.1f}"),
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=circumcenter_color, markersize=7, label=f"Circumcircle radius: {radius_circum:.1f}")
    ]
    ax.legend(handles=handles, loc='upper left', fontsize=8)

    # Adjust the plot limits and aspect ratio
    padding = 5
    ax.set_xlim([min(x1, x2, x3) - padding, max(x1, x2, x3) + padding])
    ax.set_ylim([min(y1, y2, y3) - padding, max(y1, y2, y3) + padding])
    ax.set_aspect('equal', adjustable='datalim')
    
    ax.set_title('Triangle Visualization', fontsize=23)
    ax.set_xlabel('X-axis', fontsize=12)
    ax.set_ylabel('Y-axis', fontsize=12)
    
    # Add a light grid
    plt.grid(color='gray', linestyle='--', linewidth=0.5, alpha=0.5)
    st.pyplot(fig)


# Function to check if the sides form a valid triangle
def is_valid_triangle(a, b, c):
    # Check if the sum of two sides is greater than the third side (Triangle Inequality Theorem)
    return a + b > c and b + c > a and c + a > b

# Main function to interact with the user
def main():
    st.markdown("""
    <h1 style='text-align: left;'>
        <span style="display: inline-block; transform: scaleX(-1);">◭</span>  Advanced Triangle Solver ◭
    </h1> """, unsafe_allow_html=True)
    
    st.sidebar.header("Enter the cartesian coordinates for the three points of a triangle:")

    # # Coordinates input (X1, Y1), (X2, Y2), (X3, Y3)
    # x1 = st.sidebar.number_input("X1", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    # y1 = st.sidebar.number_input("Y1", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    # x2 = st.sidebar.number_input("X2", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    # y2 = st.sidebar.number_input("Y2", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    # x3 = st.sidebar.number_input("X3", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    # y3 = st.sidebar.number_input("Y3", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")

    # Coordinates input (X1, Y1), (X2, Y2), (X3, Y3)
    col_x1y1 = st.sidebar.columns([3, 3])  # Wider columns for sliders
    x1 = col_x1y1[0].number_input("X1", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    y1 = col_x1y1[1].number_input("Y1", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")

    # # Reduced spacing between sections
    # st.sidebar.markdown("<br>", unsafe_allow_html=True)

    col_x2y2 = st.sidebar.columns([3, 3])  # Wider columns for sliders
    x2 = col_x2y2[0].number_input("X2", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    y2 = col_x2y2[1].number_input("Y2", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")

    # # Reduced spacing between sections
    # st.sidebar.markdown("<br>", unsafe_allow_html=True)

    col_x3y3 = st.sidebar.columns([3, 3])  # Wider columns for sliders
    x3 = col_x3y3[0].number_input("X3", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")
    y3 = col_x3y3[1].number_input("Y3", min_value=-100.0, max_value=100.0, step=0.1, format="%.3f")

    # Add extra vertical spacing below the inputs
    st.sidebar.markdown("<br>", unsafe_allow_html=True)

    # Add "Solve Triangle" button centered in a new row
    button_col = st.sidebar.columns([1, 2, 1])  # Center button using column proportions
    if button_col[1].button("Solve Triangle"):
        # Calculate the lengths of the sides of the triangle using Euclidean distance
        a = calculate_distance(x2, y2, x3, y3)
        b = calculate_distance(x1, y1, x3, y3)
        c = calculate_distance(x1, y1, x2, y2)

        # Validate if it's a valid triangle
        if not is_valid_triangle(a, b, c):
            st.error("The entered points do not form a valid triangle.")
            return
        
        # Calculate angles using the Law of Cosines
        A = calculate_angle(a, b, c)
        B = calculate_angle(b, a, c)
        C = calculate_angle(c, a, b)

        # Check if angles sum up to 180 degrees
        if abs(A + B + C - 180) > 1e-2:
            st.error("The sum of the angles is not 180 degrees.")
            return

        # Calculate area, perimeter, and radius of inscribed and circumscribed circles
        area = calculate_area(a, b, c)
        perimeter = calculate_perimeter(a, b, c)
        radius_in = calculate_radius_inscribed_circle(a, b, c)
        radius_circum = calculate_radius_circumscribed_circle(a, b, c)

        # Calculate centroid, incenter, and circumcenter coordinates
        G_x, G_y = calculate_centroid(x1, y1, x2, y2, x3, y3)
        I_x, I_y = calculate_incenter(x1, y1, x2, y2, x3, y3, a, b, c)
        U_x, U_y = calculate_circumcenter(x1, y1, x2, y2, x3, y3, a, b, c)

        # Calculate midpoints of the sides
        midpoints = calculate_midpoints(x1, y1, x2, y2, x3, y3)

        # Display results in columns
        col1, col2 = st.columns(2)
        
        with col1:
            st.subheader("Coordinates of Triangle:")
            st.markdown(f"Vertex A: **({x1:.3f}, {y1:.3f})**")
            st.markdown(f"Vertex B: **({x2:.3f}, {y2:.3f})**")
            st.markdown(f"Vertex C: **({x3:.3f}, {y3:.3f})**")
        
        with col2:
            st.subheader("Mid-Points of Triangle:")
            st.markdown(f"Midpoint of AB: **({midpoints[0][0]:.3f}, {midpoints[0][1]:.3f})**")
            st.markdown(f"Midpoint of BC: **({midpoints[1][0]:.3f}, {midpoints[1][1]:.3f})**")
            st.markdown(f"Midpoint of CA: **({midpoints[2][0]:.3f}, {midpoints[2][1]:.3f})**")

        
        col1, col2 = st.columns(2)
           
        with col1:
            st.subheader("Angles of Triangle:")
            st.markdown(f"Angle A: **{format_zero(A):.3f}°**")
            st.markdown(f"Angle B: **{format_zero(B):.3f}°**")
            st.markdown(f"Angle C: **{format_zero(C):.3f}°**")

        with col2:
            st.subheader("Sides of Triangle:")
            st.markdown(f"Side a: **{format_zero(a):.3f}** units")
            st.markdown(f"Side b: **{format_zero(b):.3f}** units")
            st.markdown(f"Side c: **{format_zero(c):.3f}** units")

        
        col1, col2 = st.columns(2)

        with col1:
            st.subheader("Incenter of Triangle:")
            st.markdown(f"Coordinates: **({format_zero(I_x):.3f}, {format_zero(I_y):.3f})**")
            st.markdown(f"Radius: **{radius_in:.3f}** units")
        
        with col2:
            st.subheader("Circumcenter of Triangle:")
            st.markdown(f"Coordinates: **({format_zero(U_x):.3f}, {format_zero(U_y):.3f})**")
            st.markdown(f"Radius: **{radius_circum:.3f}** units")

        col1, col2, col3 = st.columns([1, 2, 1])  # Create three columns with relative widths
        with col2:  # Center column
            st.subheader("Other Properties:")
            st.markdown(f"Area: **{format_zero(area):.3f}** square units")
            st.markdown(f"Perimeter: **{format_zero(perimeter):.3f}** units")
            st.markdown(f"Centroid: **({format_zero(G_x):.3f}, {format_zero(G_y):.3f})**")

        # Display triangle graph with midpoints and colored points
        plot_triangle(x1, y1, x2, y2, x3, y3, I_x, I_y, U_x, U_y, G_x, G_y, midpoints, a, b, c)

if __name__ == "__main__":
    main()