classdef RC_SRP_Calculator < handle % This application provides a modern, peach-themed interface % to calculate singly reinforced sections step-by-step. % We avoid heavy plots and focus purely on real-time equation updates. properties % Window and main grid UIFigure GridLayout % The three main sections LeftPanel % Sidebar for configurable settings CenterPanel % Main panel for step-by-step equations RightPanel % Right sidebar for our activity log % Left Panel Inputs (using f(c)', f(y), A(s) etc. for UI labels) ModeDropdown TitleFc, FieldFc TitleFy, FieldFy TitleB, FieldB TitleH, FieldH TitleBars, FieldBars TitleArea, FieldArea TitleCover, FieldCover % Extra Inputs for Double Layer TitleBars2, FieldBars2 TitleArea2, FieldArea2 TitleSpacing, FieldSpacing % Center Panel Output EquationHTML % Right Panel Output LogTextArea % Keep track of our log messages LogHistory = {} end methods (Access = public) % Constructor. This runs when we launch the calculator. function app = RC_SRP_Calculator() app.createApp(); app.addLog('Calculator app initialized! Welcome.'); app.updateEquations(); % trigger first calculation end % Clean up when the app is nicely closed function delete(app) if isvalid(app.UIFigure) delete(app.UIFigure); end end end methods (Access = private) % Let's build our user interface step-by-step function createApp(app) % Define our peach color palette (very warm, minimalistic aesthetic) peachBase = [1.00, 0.89, 0.80]; % Main background peachDark = [0.95, 0.76, 0.65]; % Panels peachLight = [1.00, 0.95, 0.90]; % Highlights peachText = [0.20, 0.20, 0.20]; % Text color for readability % Setup the main application window app.UIFigure = uifigure('Name', 'RC-SRP Calculator', 'Position', [100, 100, 1200, 750]); app.UIFigure.Color = peachBase; % Setup a 3-column layout app.GridLayout = uigridlayout(app.UIFigure); app.GridLayout.ColumnWidth = {300, '1x', 280}; app.GridLayout.RowHeight = {'1x'}; app.GridLayout.BackgroundColor = peachBase; % --- 1. LEFT SIDEBAR: Configurable Settings --- app.LeftPanel = uipanel(app.GridLayout); app.LeftPanel.Layout.Row = 1; app.LeftPanel.Layout.Column = 1; app.LeftPanel.BackgroundColor = peachDark; app.LeftPanel.Title = 'Configurable Settings'; app.LeftPanel.ForegroundColor = peachText; app.LeftPanel.FontWeight = 'bold'; % Grid inside the left panel to align our inputs beautifully leftGrid = uigridlayout(app.LeftPanel); leftGrid.ColumnWidth = {'1x', '1x'}; leftGrid.RowHeight = repmat({32}, 1, 15); % 15 rows of 32px height leftGrid.BackgroundColor = peachDark; % Calculation mode dropdown uilabel(leftGrid, 'Text', 'Mode:', 'FontWeight', 'bold', 'FontColor', peachText); app.ModeDropdown = uidropdown(leftGrid, ... 'Items', {'Singly Reinforced', 'Single Layer', 'Double Layer'}, ... 'ValueChangedFcn', @(src, event) app.onInputChanged(), ... 'FontColor', peachText, 'BackgroundColor', peachLight); % f(c)' input (concrete compressive strength) app.TitleFc = uilabel(leftGrid, 'Text', 'f(c)'' (psi):', 'FontColor', peachText, 'FontWeight', 'bold'); app.FieldFc = uieditfield(leftGrid, 'numeric', 'Value', 4000, 'ValueChangedFcn', @(src, event) app.onInputChanged()); % f(y) input (steel yield strength) app.TitleFy = uilabel(leftGrid, 'Text', 'f(y) (psi):', 'FontColor', peachText, 'FontWeight', 'bold'); app.FieldFy = uieditfield(leftGrid, 'numeric', 'Value', 60000, 'ValueChangedFcn', @(src, event) app.onInputChanged()); % Beam width app.TitleB = uilabel(leftGrid, 'Text', 'b (in):', 'FontColor', peachText, 'FontWeight', 'bold'); app.FieldB = uieditfield(leftGrid, 'numeric', 'Value', 12, 'ValueChangedFcn', @(src, event) app.onInputChanged()); % Beam height app.TitleH = uilabel(leftGrid, 'Text', 'h (in):', 'FontColor', peachText, 'FontWeight', 'bold'); app.FieldH = uieditfield(leftGrid, 'numeric', 'Value', 20, 'ValueChangedFcn', @(src, event) app.onInputChanged()); % Bars configuration app.TitleBars = uilabel(leftGrid, 'Text', 'Number of Bars:', 'FontColor', peachText, 'FontWeight', 'bold'); app.FieldBars = uieditfield(leftGrid, 'numeric', 'Value', 4, 'ValueChangedFcn', @(src, event) app.onInputChanged()); app.TitleArea = uilabel(leftGrid, 'Text', 'Area per A(s):', 'FontColor', peachText, 'FontWeight', 'bold'); app.FieldArea = uieditfield(leftGrid, 'numeric', 'Value', 0.79, 'ValueChangedFcn', @(src, event) app.onInputChanged()); app.TitleCover = uilabel(leftGrid, 'Text', 'Cover (in):', 'FontColor', peachText, 'FontWeight', 'bold'); app.FieldCover = uieditfield(leftGrid, 'numeric', 'Value', 2.5, 'ValueChangedFcn', @(src, event) app.onInputChanged()); % Double layer specific fields (hidden by default) app.TitleBars2 = uilabel(leftGrid, 'Text', 'L2 Bars:', 'FontColor', peachText, 'Visible', 'off'); app.FieldBars2 = uieditfield(leftGrid, 'numeric', 'Value', 2, 'Visible', 'off', 'ValueChangedFcn', @(src, event) app.onInputChanged()); app.TitleArea2 = uilabel(leftGrid, 'Text', 'L2 Area A(s):', 'FontColor', peachText, 'Visible', 'off'); app.FieldArea2 = uieditfield(leftGrid, 'numeric', 'Value', 1.00, 'Visible', 'off', 'ValueChangedFcn', @(src, event) app.onInputChanged()); app.TitleSpacing = uilabel(leftGrid, 'Text', 'Spacing (in):', 'FontColor', peachText, 'Visible', 'off'); app.FieldSpacing = uieditfield(leftGrid, 'numeric', 'Value', 2.13, 'Visible', 'off', 'ValueChangedFcn', @(src, event) app.onInputChanged()); % --- 2. CENTER PANEL: Step-by-Step Equations --- app.CenterPanel = uipanel(app.GridLayout); app.CenterPanel.Layout.Row = 1; app.CenterPanel.Layout.Column = 2; app.CenterPanel.BackgroundColor = peachLight; app.CenterPanel.Title = 'Formula & Equations'; app.CenterPanel.ForegroundColor = peachText; app.CenterPanel.FontWeight = 'bold'; % We use an HTML view to render nice Math/LaTeX-like output with CSS animations centerGrid = uigridlayout(app.CenterPanel); centerGrid.ColumnWidth = {'1x'}; centerGrid.RowHeight = {'1x'}; app.EquationHTML = uihtml(centerGrid); % --- 3. RIGHT SIDEBAR: Activity Log --- app.RightPanel = uipanel(app.GridLayout); app.RightPanel.Layout.Row = 1; app.RightPanel.Layout.Column = 3; app.RightPanel.BackgroundColor = peachDark; app.RightPanel.Title = 'Log & Summary'; app.RightPanel.ForegroundColor = peachText; app.RightPanel.FontWeight = 'bold'; rightGrid = uigridlayout(app.RightPanel); rightGrid.ColumnWidth = {'1x'}; rightGrid.RowHeight = {'1x'}; app.LogTextArea = uitextarea(rightGrid); app.LogTextArea.Editable = 'off'; app.LogTextArea.BackgroundColor = peachLight; app.LogTextArea.FontColor = peachText; app.LogTextArea.FontName = 'Courier New'; end % Whenever the user touches a variable, we update instantly function onInputChanged(app) % Gently check if we need to show Double Layer inputs if strcmp(app.ModeDropdown.Value, 'Double Layer') app.TitleBars2.Visible = 'on'; app.FieldBars2.Visible = 'on'; app.TitleArea2.Visible = 'on'; app.FieldArea2.Visible = 'on'; app.TitleSpacing.Visible = 'on'; app.FieldSpacing.Visible = 'on'; else app.TitleBars2.Visible = 'off'; app.FieldBars2.Visible = 'off'; app.TitleArea2.Visible = 'off'; app.FieldArea2.Visible = 'off'; app.TitleSpacing.Visible = 'off'; app.FieldSpacing.Visible = 'off'; end app.addLog('Settings modified, recalculating formulas...'); app.updateEquations(); end % This is the core logic where all the math happens, gracefully formatting results function updateEquations(app) % 1. Grab variables from the visual fields fc = app.FieldFc.Value; fy = app.FieldFy.Value; b = app.FieldB.Value; h = app.FieldH.Value; n_bars = app.FieldBars.Value; a_bar = app.FieldArea.Value; cover = app.FieldCover.Value; mode = app.ModeDropdown.Value; % Convert stress to ksi for smaller, readable numbers fc_ksi = fc / 1000; fy_ksi = fy / 1000; Es_ksi = 29000; E_cu = 0.003; % standard concrete strain parameter % 2. Setup our HTML framework with a lovely peach CSS style and transition animations html = [ ... '' ... ]; html = [html, '

Analysis Mode: ', mode, '

']; html = [html, '

Given Variables

']; html = [html, '
', ... 'f_c'' = ', num2str(fc), ' psi     f_y = ', num2str(fy), ' psi
', ... 'b = ', num2str(b), ' in          h = ', num2str(h), ' in
', ... 'Bars = ', num2str(n_bars), ' (A_b = ', num2str(a_bar), ' in²)
', ... 'Cover = ', num2str(cover), ' in
']; % 3. Common mathematical terms % Beta_1 decreases gently for higher strength concrete beta1 = 0.85; if fc > 4000 beta1 = 0.85 - 0.05 * ((fc - 4000) / 1000); end beta1 = max(beta1, 0.65); % Never drops below this rule % Steel minimum base requirement limit_val = 3 * sqrt(fc); As_min_factor = max(limit_val, 200); % chooses the dominating rule automatically % 4. Logic splits neatly based on chosen calculation mode if strcmp(mode, 'Singly Reinforced') || strcmp(mode, 'Single Layer') % Simple straightforward geometry d = h - cover; html = [html, '

1. Solve Effective Depth (d)

']; html = [html, '
d = h - cover
', ... '= ', num2str(h), ' - ', num2str(cover), '
', ... '= ', num2str(d), ' in
']; As = n_bars * a_bar; html = [html, '

2. Solve Steel Area (A_s)

']; html = [html, '
A_s = bars × a_bar
', ... '= ', num2str(n_bars), ' × ', num2str(a_bar), '
', ... '= ', num2str(As), ' in²
']; % Standard assumption: the steel is already yielding T = As * fy_ksi; html = [html, '

3. Assume steel yields (T = A_s · f_y)

']; html = [html, '
T = ', num2str(As), ' × ', num2str(fy_ksi), ' ksi
', ... '= ', num2str(T), ' kips
']; % Concrete compression block depth a = T / (0.85 * fc_ksi * b); html = [html, '

4. Solve Compression Block (a & c)

']; html = [html, 'By matching concrete volume force to tension:
']; html = [html, '
a = T / (0.85 · f_c'' · b)
', ... '= ', num2str(a, '%.2f'), ' in
']; c = a / beta1; html = [html, '
c = a / β&sub1; = ', num2str(a, '%.2f'), ' / ', num2str(beta1, '%.2f'), '
', ... '= ', num2str(c, '%.2f'), ' in
']; % Strain compatibility check eps_y = fy_ksi / Es_ksi; eps_s = ((d - c) / c) * E_cu; html = [html, '

5. Strain Compatibility Check (ε_s ≥ ε_y)

']; html = [html, '
ε_y = f_y / E_s = ', num2str(eps_y, '%.5f'), '
', ... 'ε_s = ((d - c) / c) · ε_cu
', ... '= ', num2str(eps_s, '%.5f'), '
']; if eps_s >= eps_y html = [html, '

Tension steel yielded. Assumption confirmed gracefully.

']; else html = [html, '

Assumption failed! Steel did not yield.

']; end % Calculate the raw moment capacity Mn = T * (d - a/2) / 12; html = [html, '

6. Nominal Moment (M_n)

']; html = [html, '
M_n = T · (d - a/2) / 12
', ... '= ', num2str(Mn, '%.2f'), ' kip-ft
']; % Checking code requirements for minimum reinforcements As_min = (As_min_factor / fy) * b * d; html = [html, '

7. Check Minimum Code Rules

']; html = [html, '
A_s,min = (', num2str(As_min_factor), ' / f_y) · b · d
', ... '= ', num2str(As_min, '%.2f'), ' in²
']; if As >= As_min html = [html, '

A_s exceeds minimum rules. Wonderful.

']; else html = [html, '

A_s falls short of required minimum!

']; end % Extra steps for single layer reduction factor calculation if strcmp(mode, 'Single Layer') eps_t = eps_s; if eps_t >= 0.005 phi = 0.90; elseif eps_t <= 0.002 phi = 0.65; else phi = 0.65 + (eps_t - 0.002) * (250/3); end phi_Mn = phi * Mn; html = [html, '

8. Strength Reduction Factor (φ)

']; html = [html, '
ε_t = ', num2str(eps_t, '%.5f'), '
', ... 'φ = ', num2str(phi, '%.2f'), '
']; html = [html, '

9. Designed Maximum Moment (φM_n)

']; html = [html, '
φM_n = ', num2str(phi, '%.2f'), ' × ', num2str(Mn, '%.2f'), '
', ... '= ', num2str(phi_Mn, '%.2f'), ' kip-ft
']; end elseif strcmp(mode, 'Double Layer') % --- Advanced Multiple Layers processing --- n_bars2 = app.FieldBars2.Value; a_bar2 = app.FieldArea2.Value; spacing = app.FieldSpacing.Value; % Centroid gymnastics for double layers As1 = n_bars * a_bar; As2 = n_bars2 * a_bar2; As = As1 + As2; dist1 = cover; dist2 = cover + spacing; g = (As1 * dist1 + As2 * dist2) / As; d = h - g; d_t = h - cover; html = [html, '

1. Resolve the Steel Centroids

']; html = [html, '
g = (A_s1 · y1 + A_s2 · y2) / A_s
', ... '= (', num2str(As1), ' × ', num2str(dist1), ' + ', num2str(As2), ' × ', num2str(dist2), ') / ', num2str(As), '
', ... '= ', num2str(g, '%.2f'), ' in
']; html = [html, '
d = h - g = ', num2str(h), ' - ', num2str(g, '%.2f'), '
', ... '= ', num2str(d, '%.2f'), ' in
']; % Assume Yield again T = As * fy_ksi; a = T / (0.85 * fc_ksi * b); c = a / beta1; html = [html, '

2. Solve Basic Block (Assuming Yield)

']; html = [html, '
T = ', num2str(T, '%.2f'), ' kips
', ... 'a = ', num2str(a, '%.2f'), ' in
', ... 'c = ', num2str(c, '%.2f'), ' in
']; eps_y = fy_ksi / Es_ksi; eps_s = ((d - c) / c) * E_cu; html = [html, '

3. Verify the Yield Strain

']; html = [html, '
ε_s = ', num2str(eps_s, '%.5f'), ' | ε_y = ', num2str(eps_y, '%.5f'), '
']; % More complex quadratic solving requested for over-reinforced double layers if eps_s < eps_y html = [html, '

Assumption Failed! Solving via Quadratic Exact Match...

']; A_quad = 0.85 * fc_ksi * b * beta1; B_quad = As * Es_ksi * E_cu; C_quad = -1 * As * Es_ksi * E_cu * d; % Find actual real root roots_c = roots([A_quad, B_quad, C_quad]); c_new = max(roots_c(roots_c > 0)); a_new = beta1 * c_new; html = [html, '
Quadratic Form Evaluated for c:
', ... 'c = ', num2str(c_new, '%.2f'), ' in
', ... 'a = ', num2str(a_new, '%.2f'), ' in
']; c = c_new; a = a_new; % Recalculate accurately T = As * Es_ksi * ((d - c)/c) * E_cu; html = [html, '
Accurate Balance Tension T = C_c = ', num2str(T, '%.2f'), ' kips
']; else html = [html, '

Assumption smoothly verified.

']; end % Compute derived raw moment Mn = T * (d - a/2) / 12; html = [html, '

4. Base Moment Geometry (M_n)

']; html = [html, '
M_n = T · (d - a/2) / 12
', ... '= ', num2str(Mn, '%.2f'), ' kip-ft
']; % For double layers, compute reduction on the most extreme layer strain eps_t = ((d_t - c) / c) * E_cu; if eps_t >= 0.005 phi = 0.90; elseif eps_t <= 0.002 phi = 0.65; else phi = 0.65 + (eps_t - 0.002) * (250/3); end html = [html, '

5. Reduction Factor for Design Result

']; html = [html, '
ε_t (extreme) = ', num2str(eps_t, '%.5f'), '
', ... 'φ = ', num2str(phi, '%.2f'), '
', ... 'φM_n = ', num2str(phi*Mn, '%.2f'), ' kip-ft
']; end % Close and assign to the beautiful HTML widget html = [html, '']; app.EquationHTML.HTMLSource = html; % Add to our active log so we have a trace app.addLog(sprintf('Computed [%s] cleanly. End M_n: %.2f k-ft.', mode, Mn)); end % This simple helper lets us comfortably add logs with timestamps function addLog(app, msg) timestamp = datestr(now, 'HH:MM:SS'); logEntry = sprintf('[%s] %s', timestamp, msg); app.LogHistory = [app.LogHistory; {logEntry}]; % Keep logs fairly recent to keep memory efficient if length(app.LogHistory) > 40 app.LogHistory(1) = []; end app.LogTextArea.Value = app.LogHistory; end end end