File size: 17,197 Bytes
c31a861
 
 
cb8c9fe
 
 
c31a861
cb8c9fe
c31a861
 
 
 
 
 
 
 
cb8c9fe
 
 
 
 
 
 
c31a861
 
 
 
cb8c9fe
c31a861
 
 
 
 
cb8c9fe
 
 
 
 
c31a861
 
 
cb8c9fe
c31a861
 
 
cb8c9fe
 
 
c31a861
 
cb8c9fe
c31a861
cb8c9fe
 
 
c31a861
 
cb8c9fe
 
c31a861
 
 
cb8c9fe
 
c31a861
 
 
 
 
 
 
 
 
 
 
 
 
 
cb8c9fe
c31a861
 
 
 
 
 
 
 
 
cb8c9fe
 
afff842
cb8c9fe
c31a861
afff842
cb8c9fe
 
 
 
 
afff842
cb8c9fe
 
 
 
 
afff842
cb8c9fe
 
afff842
cb8c9fe
 
 
 
 
 
 
c31a861
cb8c9fe
 
afff842
cb8c9fe
 
c31a861
 
afff842
cb8c9fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dec0c72
cb8c9fe
 
 
 
 
 
 
 
 
709f0cb
cb8c9fe
 
 
 
 
 
 
 
 
 
afff842
cb8c9fe
c31a861
 
 
cb8c9fe
 
 
8964ba6
cb8c9fe
 
8964ba6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cb8c9fe
c31a861
 
 
 
cb8c9fe
c31a861
cb8c9fe
 
 
 
 
 
 
74c1fa6
cb8c9fe
 
74c1fa6
cb8c9fe
 
 
 
c31a861
cb8c9fe
c31a861
cb8c9fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c31a861
 
 
cb8c9fe
 
 
c31a861
 
cb8c9fe
 
 
 
c31a861
 
 
cb8c9fe
 
 
c31a861
 
ba60310
cb8c9fe
 
 
c31a861
 
 
cb8c9fe
 
 
c31a861
 
cb8c9fe
 
 
 
c31a861
 
 
cb8c9fe
 
 
 
 
 
 
 
 
cd2199d
cb8c9fe
 
365203d
5b9f784
365203d
 
 
 
 
 
 
 
 
 
 
 
 
aa18dc5
365203d
 
 
 
 
 
 
 
 
 
74c1fa6
 
 
 
 
 
0837b32
74c1fa6
 
 
 
 
 
7cc05e8
74c1fa6
 
0837b32
74c1fa6
 
0837b32
74c1fa6
 
 
0837b32
74c1fa6
 
 
 
 
 
 
 
 
 
1351de3
74c1fa6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c31a861
 
 
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Circle
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from functools import lru_cache
from io import BytesIO

# Cached functions for efficiency
@lru_cache(maxsize=128)
def calculate_distance(x1, y1, x2, y2):
    return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

@lru_cache(maxsize=128)
def calculate_angle(a, b, c):
    cos_angle = (b ** 2 + c ** 2 - a ** 2) / (2 * b * c)
    cos_angle = np.clip(cos_angle, -1.0, 1.0)
    return np.degrees(np.arccos(cos_angle))

@lru_cache(maxsize=128)
def is_valid_triangle(a, b, c):
    return a + b > c and b + c > a and c + a > b

@lru_cache(maxsize=128)
def calculate_area(a, b, c):
    s = (a + b + c) / 2
    return np.sqrt(max(0, s * (s - a) * (s - b) * (s - c)))

def calculate_triangle_properties(x1, y1, x2, y2, x3, y3):
    a = calculate_distance(x2, y2, x3, y3)
    b = calculate_distance(x1, y1, x3, y3)
    c = calculate_distance(x1, y1, x2, y2)
    
    if not is_valid_triangle(a, b, c):
        st.error("The entered points do not form a valid triangle. Please enter the coordinates again.")
        return None
    
    area = calculate_area(a, b, c)
    perimeter = a + b + c
    s = perimeter / 2
    
    A = calculate_angle(a, b, c)
    B = calculate_angle(b, a, c)
    C = calculate_angle(c, a, b)
    
    G_x, G_y = (x1 + x2 + x3) / 3, (y1 + y2 + y3) / 3  # Centroid
    
    I_x = (a * x1 + b * x2 + c * x3) / perimeter
    I_y = (a * y1 + b * y2 + c * y3) / perimeter
    
    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
    
    radius_in = area / s
    radius_circum = (a * b * c) / (4 * area)
    
    midpoints = (
        ((x1 + x2) / 2, (y1 + y2) / 2),
        ((x2 + x3) / 2, (y2 + y3) / 2),
        ((x3 + x1) / 2, (y3 + y1) / 2)
    )
    
    return {
        'sides': (a, b, c),
        'angles': (A, B, C),
        'area': area,
        'perimeter': perimeter,
        'centroid': (G_x, G_y),
        'incenter': (I_x, I_y),
        'circumcenter': (U_x, U_y),
        'radius_in': radius_in,
        'radius_circum': radius_circum,
        'midpoints': midpoints
    }

def plot_triangle(vertices, properties):
    fig, ax = plt.subplots(figsize=(18, 12))
    (x1, y1), (x2, y2), (x3, y3) = vertices
    a, b, c = properties['sides']
    A, B, C = properties['angles']
    I_x, I_y = properties['incenter']
    U_x, U_y = properties['circumcenter']
    G_x, G_y = properties['centroid']
    midpoints = properties['midpoints']
    radius_in = properties['radius_in']
    radius_circum = properties['radius_circum']
    perimeter = properties['perimeter']
    area = properties['area']
    
    triangle = Polygon(vertices, closed=True, edgecolor='b', facecolor='lightblue', linewidth=2)
    ax.add_patch(triangle)
    
    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='blue', zorder=3)
        ax.text(vx+0.01*G_x, vy+0.02*G_y, vertex_name[i], fontsize=10, ha="left", va="bottom", color='blue')
    
    key_points = [
        (G_x, G_y, 'green', f"Centroid ({G_x:.1f}, {G_y:.1f})"),
        (I_x, I_y, 'red', f"Incenter ({I_x:.1f}, {I_y:.1f})"),
        (U_x, U_y, 'black', f"Circumcenter ({U_x:.1f}, {U_y:.1f})")
    ]
    
    for x, y, color, label in key_points:
        ax.scatter(x, y, color=color, zorder=5)
    
    mid_points = [
        (midpoints[0], 'orange', f"Mid-point M1 ({midpoints[0][0]:.1f}, {midpoints[0][1]:.1f})"),
        (midpoints[1], 'orange', f"Mid-point M2 ({midpoints[1][0]:.1f}, {midpoints[1][1]:.1f})"),
        (midpoints[2], 'orange', f"Mid-point M3 ({midpoints[2][0]:.1f}, {midpoints[2][1]:.1f})")
    ]

    for i, (midpoint, color, label) in enumerate(mid_points):
        mx, my = midpoint
        ax.scatter(mx, my, color=color, zorder=3)
        ax.text(mx + 0.01 * G_x, my + 0.02 * G_y, f"M{i}", fontsize=10, ha="left", va="bottom", color=color)
    
    incircle = Circle((I_x, I_y), radius_in, color='red', fill=False, linestyle='--', linewidth=1)
    circumcircle = Circle((U_x, U_y), radius_circum, color='black', fill=False, linestyle='--', linewidth=1)
    ax.add_patch(incircle)
    ax.add_patch(circumcircle)
    
    handles = [
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='blue', markersize=7, label=label)
        for label in vertex_labels
    ]
    handles.extend([
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=color, markersize=7, label=label)
        for _,  color, label in mid_points
    ])
    handles.extend([
        plt.Line2D([0], [0], marker='<', color='blue', markersize=7, linestyle='none', label=f"Angle {angle}: {value:.1f}°")
        for angle, value in zip(['A', 'B', 'C'], [A, B, C])
    ])
    handles.extend([
        plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=color, markersize=7, label=label)
        for _, _, color, label in key_points
    ])
    handles.extend([
        plt.Line2D([0], [0], linestyle='-', color='blue', markersize=6, label=f"Side {side}: {length:.1f}")
        for side, length in zip(['a = BC', 'b = AC', 'c = AB'], [a, b, c])
    ])
    handles.extend([
        plt.Line2D([0], [0], marker='o', markerfacecolor='white', markeredgecolor='red', markersize=5, linestyle='none', label=f"Incircle radius: {radius_in:.1f}"),
        plt.Line2D([0], [0], marker='o', markerfacecolor='white', markeredgecolor='black', markersize=8, linestyle='none', label=f"Circumcircle radius: {radius_circum:.1f}")
    ])
    handles.extend([
        plt.Line2D([0], [0], marker='^', markerfacecolor='white', markeredgecolor='blue', markersize=7, linestyle='none', label=f"Perimeter: {perimeter:.1f}"),
        plt.Line2D([0], [0], marker='^', markerfacecolor='lightblue', markeredgecolor='lightblue', markersize=7, linestyle='none', label=f"Area: {area:.1f}")
    ])
    
    ax.legend(handles=handles, loc='upper left', fontsize=8)
    
    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)
    
    plt.grid(color='gray', linestyle='--', linewidth=0.5, alpha=0.5)
    st.pyplot(fig)
    return fig

def save_as_pdf_reportlab(x1, y1, x2, y2, x3, y3, a, b, c, A, B, C, area, perimeter,
                          I_x, I_y, U_x, U_y, G_x, G_y, midpoints, radius_in, radius_circum, fig):
    fig.savefig("triangle_plot.png")
    canvas_obj = canvas.Canvas("Triangle_solver_results.pdf", pagesize=letter)
    width, height = letter

    canvas_obj.setFont("Helvetica", 16)
    canvas_obj.drawString(100, height - 40, "Triangle Solver Results")
    
    canvas_obj.setFont("Helvetica", 12)
    canvas_obj.drawString(30, height - 60, "Coordinates of Triangle:")
    canvas_obj.drawString(50, height - 75, f"Vertex A: ({x1:.3f}, {y1:.3f})")
    canvas_obj.drawString(50, height - 90, f"Vertex B: ({x2:.3f}, {y2:.3f})")
    canvas_obj.drawString(50, height - 105, f"Vertex C: ({x3:.3f}, {y3:.3f})")
    
    canvas_obj.drawString(30, height - 130, "Mid-Points of Triangle:")
    canvas_obj.drawString(50, height - 145, f"Midpoint of AB: ({midpoints[0][0]:.3f}, {midpoints[0][1]:.3f})")
    canvas_obj.drawString(50, height - 160, f"Midpoint of BC: ({midpoints[1][0]:.3f}, {midpoints[1][1]:.3f})")
    canvas_obj.drawString(50, height - 175, f"Midpoint of AC: ({midpoints[2][0]:.3f}, {midpoints[2][1]:.3f})")
    
    canvas_obj.drawString(30, height - 200, "Angles of Triangle:")
    canvas_obj.drawString(50, height - 215, f"Angle A: {A:.3f}°")
    canvas_obj.drawString(50, height - 230, f"Angle B: {B:.3f}°")
    canvas_obj.drawString(50, height - 245, f"Angle C: {C:.3f}°")
    
    canvas_obj.drawString(30, height - 270, "Sides of Triangle:")
    canvas_obj.drawString(50, height - 285, f"Side a = BC: {a:.3f} units")
    canvas_obj.drawString(50, height - 300, f"Side b = AC: {b:.3f} units")
    canvas_obj.drawString(50, height - 315, f"Side c = AB: {c:.3f} units")
    
    canvas_obj.drawString(30, height - 340, "Other Properties:")
    canvas_obj.drawString(50, height - 355, f"Centroid: ({G_x:.3f}, {G_y:.3f})")
    canvas_obj.drawString(50, height - 370, f"Perimeter: {perimeter:.3f} units")
    canvas_obj.drawString(50, height - 385, f"Area: {area:.3f} square units")
    
    canvas_obj.drawString(30, height - 410, "Incenter of Triangle:")
    canvas_obj.drawString(50, height - 425, f"Coordinates: ({I_x:.3f}, {I_y:.3f})")
    canvas_obj.drawString(50, height - 440, f"Radius: {radius_in:.3f} units")
    
    canvas_obj.drawString(30, height - 465, "Circumcenter of Triangle:")
    canvas_obj.drawString(50, height - 480, f"Coordinates: ({U_x:.3f}, {U_y:.3f})")
    canvas_obj.drawString(50, height - 495, f"Radius: {radius_circum:.3f} units")
    
    canvas_obj.drawImage("triangle_plot.png", 30, height - 650, width=500, height=300)
    canvas_obj.save()
    return 

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)
    
    def get_coordinate_inputs():
        st.sidebar.header("Enter the cartesian coordinates for the three points of a triangle:")
        coordinates = {}
        for point, label in [('1', 'A'), ('2', 'B'), ('3', 'C')]:
            cols = st.sidebar.columns([3, 3])
            coordinates[f'x{point}'] = cols[0].number_input(
                f"X{point}", value=0.0, min_value=-100.0, max_value=100.0, step=0.001, format="%.3f"
            )
            coordinates[f'y{point}'] = cols[1].number_input(
                f"Y{point}", value=0.0, min_value=-100.0, max_value=100.0, step=0.001, format="%.3f"
            )
        return coordinates

    coords = get_coordinate_inputs()
    st.sidebar.markdown("<br>", unsafe_allow_html=True)

    button_col = st.sidebar.columns([1, 2, 1])
    if not button_col[1].button("Solve Triangle"):
        return

    properties = calculate_triangle_properties(
        coords['x1'], coords['y1'], 
        coords['x2'], coords['y2'], 
        coords['x3'], coords['y3']
    )
    
    if properties is None:
        return
    
    a, b, c = properties['sides']
    A, B, C = properties['angles']
    area = properties['area']
    perimeter = properties['perimeter']
    G_x, G_y = properties['centroid']
    I_x, I_y = properties['incenter']
    U_x, U_y = properties['circumcenter']
    radius_in = properties['radius_in']
    radius_circum = properties['radius_circum']
    midpoints = properties['midpoints']
    vertices = [(coords['x1'], coords['y1']), 
               (coords['x2'], coords['y2']), 
               (coords['x3'], coords['y3'])]

    st.success("The entered points form a valid triangle.")
        
    def display_coordinates():
        col1, col2 = st.columns(2)
        with col1:
            st.subheader("Coordinates of Triangle:")
            for i, vertex in enumerate(['A', 'B', 'C']):
                st.markdown(f"Vertex {vertex}: **({vertices[i][0]:.3f}, {vertices[i][1]:.3f})**")
        
        with col2:
            st.subheader("Mid-Points of Triangle:")
            for i, points in enumerate([('A','B'), ('B','C'), ('A','C')]):
                st.markdown(f"Midpoint of {points[0]}{points[1]}: **({midpoints[i][0]:.3f}, {midpoints[i][1]:.3f})**")

    def display_measurements():
        col1, col2 = st.columns(2)
        with col1:
            st.subheader("Angles of Triangle:")
            for angle, value in zip(['A', 'B', 'C'], [A, B, C]):
                st.markdown(f"Angle {angle}: **{value:.3f}°**")

        with col2:
            st.subheader("Sides of Triangle:")
            for side, value in zip(['a = BC', 'b  = AC', 'c = AB'], [a, b, c]):
                st.markdown(f"Side {side}: **{value:.3f}** units")

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

    def display_other_properties():
        col1, col2, col3 = st.columns([1, 2, 1])
        with col2:
            st.subheader("Other Properties:")
            st.markdown(f"Centroid: **({G_x:.3f}, {G_y:.3f})**")
            st.markdown(f"Perimeter: **{perimeter:.3f}** units")
            st.markdown(f"Area: **{area:.3f}** square units")

    display_coordinates()
    display_measurements()
    display_centers()
    display_other_properties()

    # Plot triangle
    fig = plot_triangle(vertices, properties)

    ### Code for Save as PDF
    # Generate PDF and get the data
    save_as_pdf_reportlab(
        coords['x1'], coords['y1'], coords['x2'], coords['y2'], coords['x3'], coords['y3'],
        a, b, c, A, B, C, area, perimeter, I_x, I_y, U_x, U_y, G_x, G_y,
        midpoints, radius_in, radius_circum, fig)
    
    # Read the saved PDF file
    with open("Triangle_solver_results.pdf", "rb") as pdf_file:
        pdf_data = pdf_file.read()
    
    # Download button
    st.download_button("Download PDF", pdf_data, 
                      file_name="Triangle_solver_results.pdf", 
                      mime="application/pdf")

## Old code for Save as PDF
    # if st.button("Save as PDF"):
    #     save_as_pdf_reportlab(
    #         coords['x1'], coords['y1'], coords['x2'], coords['y2'], coords['x3'], coords['y3'],
    #         a, b, c, A, B, C, area, perimeter, I_x, I_y, U_x, U_y, G_x, G_y,
    #         midpoints, radius_in, radius_circum, fig
    #     )
    #     st.success("Results saved as PDF!")
    #     with open("Triangle_solver_results.pdf", "rb") as pdf_file:
    #         st.download_button("Download PDF", pdf_file, file_name="Triangle_solver_results.pdf")

    ### New code for Save as PDF
    # if st.button("Save as PDF"):
    #     try:
    #         # Add debug message
    #         st.info("Creating PDF... Please wait")
            
    #         # Call your PDF creation function
    #         save_as_pdf_reportlab(
    #             coords['x1'], coords['y1'], coords['x2'], coords['y2'], coords['x3'], coords['y3'],
    #             a, b, c, A, B, C, area, perimeter, I_x, I_y, U_x, U_y, G_x, G_y,
    #             midpoints, radius_in, radius_circum, fig
    #         )
            
    #         # Check if file exists and has content
    #         pdf_path = "Triangle_solver_results.pdf"
            
    #         if os.path.exists(pdf_path) and os.path.getsize(pdf_path) > 0:
    #             st.success("PDF created successfully!")
                
    #             # Read the PDF file
    #             with open(pdf_path, "rb") as pdf_file:
    #                 pdf_data = pdf_file.read()
                    
    #                 # Create download button
    #                 st.download_button(
    #                     label="📄 Download PDF",
    #                     data=pdf_data,
    #                     file_name="Triangle_solver_results.pdf",
    #                     mime="application/pdf"
    #                 )
    #         else:
    #             st.error("❌ PDF file was not created or is empty!")
    #             st.info("Check if the save_as_pdf_reportlab function is working correctly")
                
    #     except FileNotFoundError as e:
    #         st.error(f"❌ File not found: {e}")
    #     except PermissionError as e:
    #         st.error(f"❌ Permission denied: {e}")
    #     except Exception as e:
    #         st.error(f"❌ Error creating PDF: {str(e)}")
    #         st.info("Please check your save_as_pdf_reportlab function for errors")
    
    # # Alternative: Test if your PDF function works at all
    # if st.button("🔍 Debug PDF Function"):
    #     try:
    #         st.info("Testing PDF creation...")
    #         # Test with minimal parameters
    #         save_as_pdf_reportlab(
    #             coords['x1'], coords['y1'], coords['x2'], coords['y2'], coords['x3'], coords['y3'],
    #             a, b, c, A, B, C, area, perimeter, I_x, I_y, U_x, U_y, G_x, G_y,
    #             midpoints, radius_in, radius_circum, fig
    #         )
    #         st.success("PDF function executed without errors")
    #     except Exception as e:
    #         st.error(f"PDF function failed: {str(e)}")
    #         st.code(str(e))  # Show detailed error

if __name__ == "__main__":
    main()