RC-SRP / RC_SRP_Calculator.m
algorembrant's picture
Upload 89 files
a8ced59 verified
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><head><style>' ...
'body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; ' ...
'color: #4A3B32; background-color: #FFF2E6; padding: 20px; line-height: 1.6; } ' ...
'h2 { color: #D35400; border-bottom: 2px solid #FAD7A1; padding-bottom: 5px; } ' ...
'h3 { color: #E67E22; margin-top: 25px; } ' ...
'@keyframes popup { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } } ' ...
'.equation { background-color: #FFE6CC; padding: 15px; border-radius: 12px; ' ...
'margin: 12px 0; font-family: "Courier New", Courier, monospace; font-weight: bold; ' ...
'box-shadow: 0 4px 6px rgba(0,0,0,0.06); animation: popup 0.4s ease-out; } ' ...
'.highlight { color: #C0392B; font-size: 1.1em; } ' ...
'.success { color: #27AE60; font-weight: bold; } ' ...
'.danger { color: #E74C3C; font-weight: bold; } ' ...
'</style></head><body>' ...
];
html = [html, '<h2>Analysis Mode: ', mode, '</h2>'];
html = [html, '<h3>Given Variables</h3>'];
html = [html, '<div class="equation">', ...
'f_c'' = ', num2str(fc), ' psi &nbsp;&nbsp;&nbsp; f_y = ', num2str(fy), ' psi<br>', ...
'b = ', num2str(b), ' in &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h = ', num2str(h), ' in<br>', ...
'Bars = ', num2str(n_bars), ' (A_b = ', num2str(a_bar), ' in&sup2;)<br>', ...
'Cover = ', num2str(cover), ' in</div>'];
% 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, '<h3>1. Solve Effective Depth (d)</h3>'];
html = [html, '<div class="equation">d = h - cover<br>', ...
'= ', num2str(h), ' - ', num2str(cover), '<br>', ...
'= <span class="highlight">', num2str(d), ' in</span></div>'];
As = n_bars * a_bar;
html = [html, '<h3>2. Solve Steel Area (A_s)</h3>'];
html = [html, '<div class="equation">A_s = bars &times; a_bar<br>', ...
'= ', num2str(n_bars), ' &times; ', num2str(a_bar), '<br>', ...
'= <span class="highlight">', num2str(As), ' in&sup2;</span></div>'];
% Standard assumption: the steel is already yielding
T = As * fy_ksi;
html = [html, '<h3>3. Assume steel yields (T = A_s &middot; f_y)</h3>'];
html = [html, '<div class="equation">T = ', num2str(As), ' &times; ', num2str(fy_ksi), ' ksi<br>', ...
'= <span class="highlight">', num2str(T), ' kips</span></div>'];
% Concrete compression block depth
a = T / (0.85 * fc_ksi * b);
html = [html, '<h3>4. Solve Compression Block (a &amp; c)</h3>'];
html = [html, 'By matching concrete volume force to tension:<br>'];
html = [html, '<div class="equation">a = T / (0.85 &middot; f_c'' &middot; b)<br>', ...
'= <span class="highlight">', num2str(a, '%.2f'), ' in</span></div>'];
c = a / beta1;
html = [html, '<div class="equation">c = a / &beta;&sub1; = ', num2str(a, '%.2f'), ' / ', num2str(beta1, '%.2f'), '<br>', ...
'= <span class="highlight">', num2str(c, '%.2f'), ' in</span></div>'];
% Strain compatibility check
eps_y = fy_ksi / Es_ksi;
eps_s = ((d - c) / c) * E_cu;
html = [html, '<h3>5. Strain Compatibility Check (&epsilon;_s &ge; &epsilon;_y)</h3>'];
html = [html, '<div class="equation">&epsilon;_y = f_y / E_s = ', num2str(eps_y, '%.5f'), '<br>', ...
'&epsilon;_s = ((d - c) / c) &middot; &epsilon;_cu<br>', ...
'= <span class="highlight">', num2str(eps_s, '%.5f'), '</span></div>'];
if eps_s >= eps_y
html = [html, '<p class="success">Tension steel yielded. Assumption confirmed gracefully.</p>'];
else
html = [html, '<p class="danger">Assumption failed! Steel did not yield.</p>'];
end
% Calculate the raw moment capacity
Mn = T * (d - a/2) / 12;
html = [html, '<h3>6. Nominal Moment (M_n)</h3>'];
html = [html, '<div class="equation">M_n = T &middot; (d - a/2) / 12<br>', ...
'= <span class="highlight">', num2str(Mn, '%.2f'), ' kip-ft</span></div>'];
% Checking code requirements for minimum reinforcements
As_min = (As_min_factor / fy) * b * d;
html = [html, '<h3>7. Check Minimum Code Rules</h3>'];
html = [html, '<div class="equation">A_s,min = (', num2str(As_min_factor), ' / f_y) &middot; b &middot; d<br>', ...
'= <span class="highlight">', num2str(As_min, '%.2f'), ' in&sup2;</span></div>'];
if As >= As_min
html = [html, '<p class="success">A_s exceeds minimum rules. Wonderful.</p>'];
else
html = [html, '<p class="danger">A_s falls short of required minimum!</p>'];
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, '<h3>8. Strength Reduction Factor (&phi;)</h3>'];
html = [html, '<div class="equation">&epsilon;_t = ', num2str(eps_t, '%.5f'), '<br>', ...
'&phi; = <span class="highlight">', num2str(phi, '%.2f'), '</span></div>'];
html = [html, '<h3>9. Designed Maximum Moment (&phi;M_n)</h3>'];
html = [html, '<div class="equation">&phi;M_n = ', num2str(phi, '%.2f'), ' &times; ', num2str(Mn, '%.2f'), '<br>', ...
'= <span class="highlight">', num2str(phi_Mn, '%.2f'), ' kip-ft</span></div>'];
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, '<h3>1. Resolve the Steel Centroids</h3>'];
html = [html, '<div class="equation">g = (A_s1 &middot; y1 + A_s2 &middot; y2) / A_s<br>', ...
'= (', num2str(As1), ' &times; ', num2str(dist1), ' + ', num2str(As2), ' &times; ', num2str(dist2), ') / ', num2str(As), '<br>', ...
'= ', num2str(g, '%.2f'), ' in</div>'];
html = [html, '<div class="equation">d = h - g = ', num2str(h), ' - ', num2str(g, '%.2f'), '<br>', ...
'= <span class="highlight">', num2str(d, '%.2f'), ' in</span></div>'];
% Assume Yield again
T = As * fy_ksi;
a = T / (0.85 * fc_ksi * b);
c = a / beta1;
html = [html, '<h3>2. Solve Basic Block (Assuming Yield)</h3>'];
html = [html, '<div class="equation">T = ', num2str(T, '%.2f'), ' kips<br>', ...
'a = ', num2str(a, '%.2f'), ' in<br>', ...
'c = <span class="highlight">', num2str(c, '%.2f'), ' in</span></div>'];
eps_y = fy_ksi / Es_ksi;
eps_s = ((d - c) / c) * E_cu;
html = [html, '<h3>3. Verify the Yield Strain</h3>'];
html = [html, '<div class="equation">&epsilon;_s = ', num2str(eps_s, '%.5f'), ' | &epsilon;_y = ', num2str(eps_y, '%.5f'), '</div>'];
% More complex quadratic solving requested for over-reinforced double layers
if eps_s < eps_y
html = [html, '<p class="danger">Assumption Failed! Solving via Quadratic Exact Match...</p>'];
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, '<div class="equation">Quadratic Form Evaluated for c:<br>', ...
'c = <span class="highlight">', num2str(c_new, '%.2f'), ' in</span><br>', ...
'a = <span class="highlight">', num2str(a_new, '%.2f'), ' in</span></div>'];
c = c_new;
a = a_new;
% Recalculate accurately
T = As * Es_ksi * ((d - c)/c) * E_cu;
html = [html, '<div class="equation">Accurate Balance Tension T = C_c = <span class="highlight">', num2str(T, '%.2f'), ' kips</span></div>'];
else
html = [html, '<p class="success">Assumption smoothly verified.</p>'];
end
% Compute derived raw moment
Mn = T * (d - a/2) / 12;
html = [html, '<h3>4. Base Moment Geometry (M_n)</h3>'];
html = [html, '<div class="equation">M_n = T &middot; (d - a/2) / 12<br>', ...
'= <span class="highlight">', num2str(Mn, '%.2f'), ' kip-ft</span></div>'];
% 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, '<h3>5. Reduction Factor for Design Result</h3>'];
html = [html, '<div class="equation">&epsilon;_t (extreme) = ', num2str(eps_t, '%.5f'), '<br>', ...
'&phi; = ', num2str(phi, '%.2f'), '<br>', ...
'&phi;M_n = <span class="highlight">', num2str(phi*Mn, '%.2f'), ' kip-ft</span></div>'];
end
% Close and assign to the beautiful HTML widget
html = [html, '</body></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