File size: 22,475 Bytes
f37e0dd
 
 
 
 
7a3a321
f37e0dd
 
 
 
 
 
 
 
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a3a321
 
f37e0dd
 
7a3a321
 
 
f37e0dd
 
 
 
 
 
7a3a321
f37e0dd
 
7a3a321
 
 
f37e0dd
 
 
7a3a321
f37e0dd
 
7a3a321
 
 
f37e0dd
 
 
 
 
 
 
7a3a321
 
 
f37e0dd
 
 
 
 
7a3a321
f37e0dd
 
7a3a321
 
 
f37e0dd
 
7a3a321
f37e0dd
 
7a3a321
 
 
f37e0dd
 
7a3a321
 
 
f37e0dd
7a3a321
 
 
 
 
 
 
f37e0dd
 
 
 
 
7a3a321
f37e0dd
7a3a321
f37e0dd
 
 
 
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
 
 
7a3a321
 
 
f37e0dd
 
7a3a321
 
f37e0dd
 
 
 
7a3a321
 
f37e0dd
 
 
 
 
 
7a3a321
 
f37e0dd
 
7a3a321
f37e0dd
7a3a321
 
f37e0dd
7a3a321
f37e0dd
7a3a321
f37e0dd
 
 
7a3a321
f37e0dd
 
 
7a3a321
 
f37e0dd
7a3a321
f37e0dd
7a3a321
f37e0dd
 
 
7a3a321
f37e0dd
 
 
7a3a321
 
 
 
 
 
 
 
f37e0dd
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
7a3a321
 
f37e0dd
 
 
 
 
7a3a321
 
 
 
f37e0dd
7a3a321
f37e0dd
 
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a3a321
 
 
 
f37e0dd
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
 
 
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
 
 
 
 
7a3a321
f37e0dd
7a3a321
 
 
 
 
f37e0dd
 
 
 
 
 
 
7a3a321
 
f37e0dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a3a321
f37e0dd
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
 
 
 
7a3a321
f37e0dd
 
 
 
 
 
 
 
7a3a321
f37e0dd
 
 
7a3a321
 
 
 
 
 
 
f37e0dd
7a3a321
f37e0dd
 
 
 
 
 
7a3a321
 
f37e0dd
 
 
7a3a321
f37e0dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a3a321
f37e0dd
 
7a3a321
 
 
 
 
 
 
 
f37e0dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7a3a321
 
 
f37e0dd
 
7a3a321
f37e0dd
 
 
 
7a3a321
 
 
f37e0dd
 
 
7a3a321
 
f37e0dd
 
7a3a321
 
f37e0dd
 
 
 
 
7a3a321
 
 
 
 
 
 
 
 
 
 
 
f37e0dd
 
7a3a321
f37e0dd
 
 
 
7a3a321
1932b27
f37e0dd
7a3a321
 
 
 
 
 
 
 
f37e0dd
 
 
 
7a3a321
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
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
import gradio as gr
from pint import UnitRegistry, set_application_registry
import matplotlib.pyplot as plt
import io
import base64
from sympy import symbols, Symbol, Eq, solve
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from PIL import Image
import base64
import io
from reportlab.lib.utils import ImageReader
import os
import subprocess
import contextlib


# Initialize unit registry
u = UnitRegistry()
set_application_registry(u)
Q_ = u.Quantity

def generate_beam_diagram(n_spans, lengths, loads, moments, R_sx, R_dx,
                          cantilever_left_length=0 * u.meter, cantilever_left_load=0 * u.newton / u.meter,
                          cantilever_right_length=0 * u.meter, cantilever_right_load=0 * u.newton / u.meter,
                          unit_system='SI'):
    # Define units and their abbreviations based on the unit system
    if unit_system == 'SI':
        length_unit = u.meter
        load_unit = u.newton / u.meter
        moment_unit = u.newton * u.meter
        reaction_unit = u.newton
        length_unit_str = 'm'
        load_unit_str = 'N/m'
        moment_unit_str = 'N路m'
        reaction_unit_str = 'N'
        length_display_unit = u.meter  # For consistent plotting
    else:
        length_unit = u.foot
        load_unit = u.pound_force / u.foot
        moment_unit = u.pound_force * u.foot
        reaction_unit = u.pound_force
        length_unit_str = 'ft'
        load_unit_str = 'lb/ft'
        moment_unit_str = 'lb路ft'
        reaction_unit_str = 'lb'
        length_display_unit = u.foot  # For consistent plotting

    # Adjust lengths to include cantilevers
    lengths_display = []
    x_positions = [0]
    # Left cantilever
    if cantilever_left_length.magnitude > 0:
        l_cant_left_display = cantilever_left_length.to(length_display_unit)
        lengths_display.append(l_cant_left_display)
        x_positions.append(x_positions[-1] + l_cant_left_display.magnitude)
    # Main spans
    for l in lengths:
        l_display = l.to(length_display_unit)
        lengths_display.append(l_display)
        x_positions.append(x_positions[-1] + l_display.magnitude)
    # Right cantilever
    if cantilever_right_length.magnitude > 0:
        l_cant_right_display = cantilever_right_length.to(length_display_unit)
        lengths_display.append(l_cant_right_display)
        x_positions.append(x_positions[-1] + l_cant_right_display.magnitude)

    total_length = x_positions[-1]

    fig, ax = plt.subplots(figsize=(10, 3))
    # Draw the beam as a horizontal line
    ax.hlines(0, 0, total_length, colors='black', linewidth=2)

    # -------------------------------------------
    # Draw supports (rotated 180掳 so the tip is on the beam)
    # -------------------------------------------
    support_width = total_length / 50  # Adjust support width relative to total length
    n_supports = n_spans + 1
    support_positions = []
    idx = 0
    # Left support
    if cantilever_left_length.magnitude > 0:
        # Support at the end of the left cantilever (x_positions[1])
        x = x_positions[1]
        support_positions.append(x)
        support = plt.Polygon([[x - support_width, -0.2],
                               [x + support_width, -0.2],
                               [x, 0]], color='black')
        ax.add_patch(support)
        idx = 2
    else:
        # Support at start (x_positions[0])
        x = x_positions[0]
        support_positions.append(x)
        support = plt.Polygon([[x - support_width, -0.2],
                               [x + support_width, -0.2],
                               [x, 0]], color='black')
        ax.add_patch(support)
        idx = 1

    # Intermediate supports
    for i in range(1, n_supports - 1):
        x = x_positions[idx]
        support_positions.append(x)
        support = plt.Polygon([[x - support_width, -0.2],
                               [x + support_width, -0.2],
                               [x, 0]], color='black')
        ax.add_patch(support)
        idx += 1

    # Right support
    if cantilever_right_length.magnitude > 0:
        # Support before the right cantilever (x_positions[-2])
        x = x_positions[-2]
        support_positions.append(x)
        support = plt.Polygon([[x - support_width, -0.2],
                               [x + support_width, -0.2],
                               [x, 0]], color='black')
        ax.add_patch(support)
    else:
        # Support at end (x_positions[-1])
        x = x_positions[-1]
        support_positions.append(x)
        support = plt.Polygon([[x - support_width, -0.2],
                               [x + support_width, -0.2],
                               [x, 0]], color='black')
        ax.add_patch(support)

    # -------------------------------------------
    # Annotate the TOTAL reaction force (sum of left and right) at each support
    # -------------------------------------------
    for i, x in enumerate(support_positions):
        R_total = R_sx[i] + R_dx[i]
        R_total_conv = R_total.to(reaction_unit)
        R_total_num = round(R_total_conv.magnitude, 6)
        ax.text(x, 0.25, f'$R_{{{i+1}}} = {R_total_num}$ {reaction_unit_str}',
                ha='center', va='bottom', color='green', fontsize=10)

    # Annotate internal moments below the beam
    for i, x in enumerate(support_positions):
        M_value = moments[i]
        M_unit = M_value.to(moment_unit)
        M_num = round(M_unit.magnitude, 6)
        ax.text(x, -0.25, f'$M_{{{i+1}}} = {M_num}$ {moment_unit_str}',
                ha='center', va='top', color='blue', fontsize=10)

    # Remove axes and adjust limits
    ax.axis('off')
    plt.xlim(-0.05 * total_length, total_length * 1.05)
    plt.ylim(-0.4, 0.4)

    # Save plot to a buffer and encode as base64
    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight', dpi=150)
    plt.close(fig)
    buf.seek(0)
    img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8')
    return f'<img src="data:image/png;base64,{img_base64}" alt="Beam Diagram"/>'

def calculate_reactions(n_spans, lengths, distributed_loads, moments,
                        cantilever_left_length=Q_(0.0, u.meter), cantilever_left_load=Q_(0.0, u.newton / u.meter),
                        cantilever_right_length=Q_(0.0, u.meter), cantilever_right_load=Q_(0.0, u.newton / u.meter)):
    """
    Calculate reactions.
    For a cantilever the reaction force is simply:
         R = (distributed load) * (cantilever length)
    """
    n_supports = n_spans + 1

    # Print all torques (moments) at supports
    print("\nTorques at each support:")
    for i, moment in enumerate(moments):
        print(f"Torque {i+1}: {moment}")
    print("\n")

    # Initialize reaction lists (each support will have left (R_sx) and right (R_dx) components)
    R_sx = [Q_(0.0, 'newton') for _ in range(n_supports)]
    R_dx = [Q_(0.0, 'newton') for _ in range(n_supports)]

    # ---------------------------
    # First support (m = 0)
    # ---------------------------
    print("Calculating R1:")
    # For a left cantilever, the left reaction is the cantilever force:
    if cantilever_left_length.magnitude > 0:
        R_sx[0] = cantilever_left_load * cantilever_left_length
        print(f"R1_sx = cantilever_left_load * cantilever_left_length = {R_sx[0]}")
        
    # r1_dx = (l0 * w0)/2 + (M1 - M2)/l0
    R_dx[0] = (distributed_loads[0] * lengths[0]) / 2 + (moments[0] - moments[1]) / lengths[0]
    print(f"R1_dx = ({distributed_loads[0]} * {lengths[0]})/2 + ({moments[0]} - {moments[1]})/{lengths[0]} = {R_dx[0]}")

    # ---------------------------
    # Middle supports (1 .. n-1)
    # ---------------------------
    for m in range(1, n_supports - 1):
        print(f"\nCalculating R{m+1}:")
        # Left reaction at support m: load from previous span minus the right reaction of previous support
        R_sx[m] = distributed_loads[m-1] * lengths[m-1] - R_dx[m-1]
        print(f"R{m+1}_sx = {distributed_loads[m-1]} * {lengths[m-1]} - {R_dx[m-1]} = {R_sx[m]}")
        
        # Right reaction at support m: half the load on the next span plus moment difference contribution
        R_dx[m] = (distributed_loads[m] * lengths[m]) / 2 + (moments[m] - moments[m+1]) / lengths[m]
        print(f"R{m+1}_dx = ({distributed_loads[m]} * {lengths[m]})/2 + ({moments[m]} - {moments[m+1]})/{lengths[m]} = {R_dx[m]}")

    # ---------------------------
    # Last support (m = n_supports - 1)
    # ---------------------------
    m = n_supports - 1
    print(f"\nCalculating R{m+1}:")
    # Left reaction at last support from the previous span
    R_sx[m] = distributed_loads[m-1] * lengths[m-1] - R_dx[m-1]
    print(f"R{m+1}_sx = {distributed_loads[m-1]} * {lengths[m-1]} - {R_dx[m-1]} = {R_sx[m]}")

    # For a right cantilever, the right reaction is the cantilever force;
    # otherwise it is set to zero.
    if cantilever_right_length.magnitude > 0:
        R_dx[m] = cantilever_right_load * cantilever_right_length
        print(f"R{m+1}_dx = cantilever_right_load * cantilever_right_length = {R_dx[m]}")
    else:
        R_dx[m] = Q_(0.0, 'newton')
        print(f"R{m+1}_dx is forced to 0: {R_dx[m]}")

    # Print final reactions summary
    print("\nFinal Reactions Summary:")
    for i in range(n_supports):
        print(f"R{i+1}_sx = {R_sx[i]}")
        print(f"R{i+1}_dx = {R_dx[i]}")
        print(f"R{i+1}_total = {R_sx[i] + R_dx[i]}\n")

    return R_sx, R_dx

def continuous_beam_solver(unit_system, n_spans, lengths_str, loads_str,
                             cantilever_left_length_str='0', cantilever_left_load_str='0',
                             cantilever_right_length_str='0', cantilever_right_load_str='0'):
    # Parse the input strings into lists
    try:
        n_spans = int(n_spans)
        l_values = [float(val.strip()) for val in lengths_str.split(',')]
        p_values = [float(val.strip()) for val in loads_str.split(',')]
        cant_left_len_val = float(cantilever_left_length_str.strip())
        cant_left_load_val = float(cantilever_left_load_str.strip())
        cant_right_len_val = float(cantilever_right_length_str.strip())
        cant_right_load_val = float(cantilever_right_load_str.strip())
    except ValueError:
        return "Invalid input. Please ensure all inputs are numbers.", "", "", "", ""

    if len(l_values) != n_spans or len(p_values) != n_spans:
        return "The number of lengths and loads must match the number of spans.", "", "", "", ""

    n_supports = n_spans + 1  # Total number of supports

    # Define units based on the selected unit system
    if unit_system == 'SI':
        length_unit = u.meter
        load_unit = u.newton / u.meter
        moment_unit = u.newton * u.meter
        reaction_unit = u.newton
    else:
        length_unit = u.foot
        load_unit = u.pound_force / u.foot
        moment_unit = u.pound_force * u.foot
        reaction_unit = u.pound_force

    # Convert lengths and loads to quantities with units
    l = [Q_(length, length_unit) for length in l_values]
    p = [Q_(load, load_unit) for load in p_values]

    # Convert lengths and loads to SI units for calculation
    l_SI = [length.to(u.meter) for length in l]
    p_SI = [load.to(u.newton / u.meter) for load in p]

    # Process cantilever inputs
    cantilever_left_length = Q_(cant_left_len_val, length_unit)
    cantilever_left_load = Q_(cant_left_load_val, load_unit)
    cantilever_right_length = Q_(cant_right_len_val, length_unit)
    cantilever_right_load = Q_(cant_right_load_val, load_unit)

    # Convert cantilever inputs to SI units for calculations
    cantilever_left_length_SI = cantilever_left_length.to(u.meter)
    cantilever_left_load_SI = cantilever_left_load.to(u.newton / u.meter)
    cantilever_right_length_SI = cantilever_right_length.to(u.meter)
    cantilever_right_load_SI = cantilever_right_load.to(u.newton / u.meter)

    # Initialize moments at supports
    M_values_SI = []
    M_values_Imperial = []

    # Handle single-span beam separately
    if n_spans == 1:
        # Cantilever moment contributions (if any)
        M_cantilever_left = 0.0
        if cantilever_left_length_SI.magnitude > 0 and cantilever_left_load_SI.magnitude != 0:
            M_cantilever_left = (cantilever_left_load_SI * cantilever_left_length_SI**2 / 2).to(u.newton * u.meter).magnitude

        M_cantilever_right = 0.0
        if cantilever_right_length_SI.magnitude > 0 and cantilever_right_load_SI.magnitude != 0:
            M_cantilever_right = (cantilever_right_load_SI * cantilever_right_length_SI**2 / 2).to(u.newton * u.meter).magnitude

        # Assign moments for supports A and B
        M_A = M_cantilever_left
        M_B = M_cantilever_right

        M_values_SI = [Q_(M_A, u.newton * u.meter), Q_(M_B, u.newton * u.meter)]
        M_values_Imperial = [M_values_SI[0].to(u.pound_force * u.foot), M_values_SI[1].to(u.pound_force * u.foot)]

        # Calculate reactions+
        f = io.StringIO()
        with contextlib.redirect_stdout(f):
            R_sx_SI, R_dx_SI = calculate_reactions(n_spans, l_SI, p_SI, M_values_SI)
        reaction_log = f.getvalue()

        R_sx_Imperial = [R.to(u.pound_force) for R in R_sx_SI]
        R_dx_Imperial = [R.to(u.pound_force) for R in R_dx_SI]

        results_SI = ""
        results_Imperial = ""
        for i in range(n_supports):
            results_SI += f"M_{i+1} = {M_values_SI[i].magnitude:.6f} N路m\n"
            results_Imperial += f"M_{i+1} = {M_values_Imperial[i].magnitude:.6f} lb路ft\n"

        beam_diagram_SI = generate_beam_diagram(
            n_spans, l, p, M_values_SI, R_sx_SI, R_dx_SI,
            cantilever_left_length=cantilever_left_length,
            cantilever_left_load=cantilever_left_load,
            cantilever_right_length=cantilever_right_length,
            cantilever_right_load=cantilever_right_load,
            unit_system='SI'
        )
        beam_diagram_Imperial = generate_beam_diagram(
            n_spans, l, p, M_values_Imperial, R_sx_Imperial, R_dx_Imperial,
            cantilever_left_length=cantilever_left_length,
            cantilever_left_load=cantilever_left_load,
            cantilever_right_length=cantilever_right_length,
            cantilever_right_load=cantilever_right_load,
            unit_system='Imperial'
        )
        # For single-span, no complex equations need to be shown.
        equations_md = ""
        return equations_md, results_SI, results_Imperial, beam_diagram_SI, beam_diagram_Imperial, reaction_log

    # For multiple spans
    else:
        # Compute fixed-end moments due to cantilever loads
        M_cantilever_left = 0.0
        if cantilever_left_length_SI.magnitude > 0 and cantilever_left_load_SI.magnitude != 0:
            M_cantilever_left = (cantilever_left_load_SI * cantilever_left_length_SI**2 / 2).to(u.newton * u.meter).magnitude

        M_cantilever_right = 0.0
        if cantilever_right_length_SI.magnitude > 0 and cantilever_right_load_SI.magnitude != 0:
            M_cantilever_right = (cantilever_right_load_SI * cantilever_right_length_SI**2 / 2).to(u.newton * u.meter).magnitude

        # Initialize moments M_i (M_1 to M_n)
        M_symbols = []
        M_symbols.append(M_cantilever_left)  # M_1

        for i in range(1, n_supports - 1):
            M_symbols.append(symbols(f'M_{i+1}'))  # M_2 to M_{n_supports-1}

        M_symbols.append(M_cantilever_right)  # M_n

        # Set up the system of equations (for supports 2 to n-1)
        equations = []
        equations_latex = []
        for k in range(1, n_supports - 1):
            l_prev = l_SI[k - 1].magnitude
            l_curr = l_SI[k].magnitude
            p_prev = p_SI[k - 1].magnitude
            p_curr = p_SI[k].magnitude
            M_prev = M_symbols[k - 1]
            M_curr = M_symbols[k]
            M_next = M_symbols[k + 1]

            lhs = (1/24) * (l_prev**3 * p_prev + l_curr**3 * p_curr)
            rhs = (1/6) * (l_prev * M_prev + l_curr * M_next) + (1/3) * (l_prev + l_curr) * M_curr
            equation = Eq(lhs, rhs)
            equations.append(equation)
            equation_latex = f"\\frac{{1}}{{24}}(l_{{{k}}}^3 p_{{{k}}} + l_{{{k+1}}}^3 p_{{{k+1}}}) = \\frac{{1}}{{6}}(l_{{{k}}} M_{{{k}}} + l_{{{k+1}}} M_{{{k+2}}}) + \\frac{{1}}{{3}}(l_{{{k}}} + l_{{{k+1}}}) M_{{{k+1}}}"
            equations_latex.append(equation_latex)

        # Solve the system for the unknown moments
        unknown_M_symbols = [M_symbols[i] for i in range(1, n_supports - 1) if isinstance(M_symbols[i], Symbol)]
        solution = solve(equations, unknown_M_symbols, dict=True)

        if solution:
            solution = solution[0]
            results_SI = ""
            results_Imperial = ""
            M_values_SI = []
            M_values_Imperial = []
            for i in range(n_supports):
                M_i_value = M_symbols[i]
                if isinstance(M_i_value, Symbol):
                    M_i_value = float(solution.get(M_i_value, 0))
                else:
                    M_i_value = float(M_i_value)
                M_quantity_SI = Q_(M_i_value, u.newton * u.meter)
                M_values_SI.append(M_quantity_SI)
                results_SI += f"M_{i+1} = {M_quantity_SI.magnitude:.6f} N路m\n"
                M_quantity_Imperial = M_quantity_SI.to(u.pound_force * u.foot)
                M_values_Imperial.append(M_quantity_Imperial)
                results_Imperial += f"M_{i+1} = {M_quantity_Imperial.magnitude:.6f} lb路ft\n"
        else:
            return "No solution found.", "", "", "", ""

        # Calculate reactions
        f = io.StringIO()
        with contextlib.redirect_stdout(f):
            R_sx_SI, R_dx_SI = calculate_reactions(n_spans, l_SI, p_SI, M_values_SI,
                                                    cantilever_left_length=cantilever_left_length_SI,
                                                    cantilever_left_load=cantilever_left_load_SI,
                                                    cantilever_right_length=cantilever_right_length_SI,
                                                    cantilever_right_load=cantilever_right_load_SI)
        reaction_log = f.getvalue()

        R_sx_Imperial = [R.to(u.pound_force) for R in R_sx_SI]
        R_dx_Imperial = [R.to(u.pound_force) for R in R_dx_SI]

        beam_diagram_SI = generate_beam_diagram(
            n_spans, l, p, M_values_SI, R_sx_SI, R_dx_SI,
            cantilever_left_length=cantilever_left_length,
            cantilever_left_load=cantilever_left_load,
            cantilever_right_length=cantilever_right_length,
            cantilever_right_load=cantilever_right_load,
            unit_system='SI')
        beam_diagram_Imperial = generate_beam_diagram(
            n_spans, l, p, M_values_Imperial, R_sx_Imperial, R_dx_Imperial,
            cantilever_left_length=cantilever_left_length,
            cantilever_left_load=cantilever_left_load,
            cantilever_right_length=cantilever_right_length,
            cantilever_right_load=cantilever_right_load,
            unit_system='Imperial')

        equations_md = "\n\n".join(
            [f"**Equation {i+1}:**\n\n$$ {eq} $$" for i, eq in enumerate(equations_latex)]
        )
        return equations_md, results_SI, results_Imperial, beam_diagram_SI, beam_diagram_Imperial, reaction_log


        

def gradio_interface(unit_system, n_spans, lengths_str, loads_str,
                     cantilever_left_length, cantilever_left_load,
                     cantilever_right_length, cantilever_right_load):
    # Call the continuous beam solver to obtain all outputs including the reaction log.
    (equations_md, results_SI, results_Imperial,
     beam_diagram_SI, beam_diagram_Imperial, reaction_log) = continuous_beam_solver(
        unit_system, n_spans, lengths_str, loads_str,
        cantilever_left_length, cantilever_left_load,
        cantilever_right_length, cantilever_right_load)
    # Return outputs in the new order: equations, moments, diagrams, and then the reaction log.
    return equations_md, results_SI, results_Imperial, beam_diagram_SI, beam_diagram_Imperial, reaction_log


# Build the Gradio interface.
# Note that the input labels now indicate the expected units.
iface = gr.Interface(
    fn=gradio_interface,
    inputs=[
        gr.Radio(['SI', 'Imperial'], label="Unit System", value='SI'),
        gr.Number(label="Number of Spans (n)", value=3, precision=0),
        gr.Textbox(label="Lengths l_i (comma-separated) [m (SI) or ft (Imperial)]", 
                   placeholder="e.g., 7.92, 7.92, 7.92", value='7.91667,7.91667,7.91667'),
        gr.Textbox(label="Loads p_i (comma-separated) [N/m (SI) or lb/ft (Imperial)]", 
                   placeholder="e.g., 200,200,200", value='200,200,200'),
        gr.Textbox(label="Cantilever Left Length [m (SI) or ft (Imperial)]", 
                   placeholder="e.g., 6.67", value='6.66667'),
        gr.Textbox(label="Cantilever Left Load [N/m (SI) or lb/ft (Imperial)]", 
                   placeholder="e.g., 200", value='200'),
        gr.Textbox(label="Cantilever Right Length [m (SI) or ft (Imperial)]", 
                   placeholder="e.g., 6.67", value='6.66667'),
        gr.Textbox(label="Cantilever Right Load [N/m (SI) or lb/ft (Imperial)]", 
                   placeholder="e.g., 200", value='200'),
    ],
    outputs=[
        gr.Markdown(label="Equations Used"),
        gr.Textbox(label="Internal Moments at Supports (SI Units)"),
        gr.Textbox(label="Internal Moments at Supports (Imperial Units)"),
        gr.HTML(label="Beam Diagram (SI Units)"),
        gr.HTML(label="Beam Diagram (Imperial Units)"),
        gr.Textbox(label="Reactions Calculation Log"),
    ],
    title="Continuous Beam Solver with Cantilevers",
    description=(
        "Solve for internal moments at supports of a continuous beam with multiple spans, including cantilevers.\n\n"
        "**Input Units:**\n"
        "- For SI: Lengths in meters (m), Loads in Newtons per meter (N/m), Moments in N路m, Reaction forces in N.\n"
        "- For Imperial: Lengths in feet (ft), Loads in pounds per foot (lb/ft), Moments in lb路ft, Reaction forces in lb.\n\n"
        "The outputs are arranged with the equations on top, followed by the resulting internal moments, "
        "then the beam diagram, and finally the complete reaction calculation log."
    ),
    allow_flagging="never",
)

if __name__ == "__main__":
    iface.launch()