Created
May 25, 2025 14:38
-
-
Save 110threat/d87f1db59ca746991ef5cb63075eed96 to your computer and use it in GitHub Desktop.
SBMD and Reactions calculator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Imports System.Drawing | |
Imports System.Linq | |
Imports System.Collections.Generic | |
Imports System.Drawing.Printing | |
Public Structure BeamLoads | |
Public PointLoad As (Magnitude As Double, Position As Double) | |
Public RectLoad As (Magnitude As Double, StartPos As Double, EndPos As Double) | |
Public TriLoad As (Magnitude As Double, StartPos As Double, EndPos As Double, IsLeftHigh As Boolean) | |
Public MomentLoad As (Magnitude As Double, Position As Double, Direction As Integer) ' 0=CCW, 1=CW | |
End Structure | |
Public Structure BeamResult | |
Public Position As Double | |
Public Shear As Double | |
Public Moment As Double | |
End Structure | |
Public Class Form2 | |
Public Const BeamLength As Double = 10.0 | |
' Calculate support reactions for simply supported beam | |
Public Shared Function CalculateReactions(loads As BeamLoads) As (RA As Double, RB As Double) | |
Try | |
If loads.RectLoad.EndPos < loads.RectLoad.StartPos Then | |
MessageBox.Show("Please enter a value greater than " & loads.RectLoad.StartPos, "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning) | |
End If | |
If loads.TriLoad.EndPos < loads.TriLoad.StartPos Then | |
MessageBox.Show("Please enter a value greater than " & loads.TriLoad.StartPos, "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning) | |
End If | |
Dim totalLoad As Double = 0 | |
Dim momentAboutA As Double = 0 | |
' Point load | |
totalLoad += loads.PointLoad.Magnitude | |
momentAboutA += loads.PointLoad.Magnitude * loads.PointLoad.Position | |
' Rectangular uniform load | |
Dim w = loads.RectLoad.Magnitude | |
Dim a = loads.RectLoad.StartPos | |
Dim b = loads.RectLoad.EndPos | |
Dim lengthR = b - a | |
If lengthR > 0 Then | |
Dim Frect = w * lengthR | |
totalLoad += Frect | |
momentAboutA += Frect * (a + lengthR / 2) | |
End If | |
' Triangular load (linear) | |
With loads.TriLoad | |
Dim baseT = .EndPos - .StartPos | |
If baseT > 0 Then | |
Dim Ftri = 0.5 * .Magnitude * baseT | |
Dim centroidX = If(.IsLeftHigh, | |
.StartPos + baseT / 3, | |
.StartPos + baseT * 2 / 3) | |
totalLoad += Ftri | |
momentAboutA += Ftri * centroidX | |
End If | |
End With | |
' Moment load (external) | |
Dim momentLoadVal = loads.MomentLoad.Magnitude | |
If loads.MomentLoad.Direction = 0 Then | |
momentAboutA -= momentLoadVal ' CCW positive | |
Else | |
momentAboutA += momentLoadVal ' CW negative | |
End If | |
' Reactions by equilibrium | |
Dim RB = momentAboutA / BeamLength | |
Dim RA = totalLoad - RB | |
Return (RA, RB) | |
Catch ex As Exception | |
MsgBox(ex.Message) | |
End Try | |
End Function | |
' Calculate shear at position x | |
Public Shared Function CalculateShearAt(x As Double, loads As BeamLoads, RA As Double) As Double | |
Try | |
Dim V = RA | |
' Point load effect | |
If x >= loads.PointLoad.Position Then | |
V -= loads.PointLoad.Magnitude | |
End If | |
' Rectangular load effect | |
If x >= loads.RectLoad.StartPos Then | |
Dim endX = Math.Min(x, loads.RectLoad.EndPos) | |
Dim length = endX - loads.RectLoad.StartPos | |
If length > 0 Then | |
V -= loads.RectLoad.Magnitude * length | |
End If | |
End If | |
' Triangular load effect | |
If x >= loads.TriLoad.StartPos Then | |
Dim endX = Math.Min(x, loads.TriLoad.EndPos) | |
Dim baseT = endX - loads.TriLoad.StartPos | |
If baseT > 0 Then | |
Dim h = loads.TriLoad.Magnitude | |
If loads.TriLoad.IsLeftHigh Then | |
' load linearly decreasing from h to 0 over baseT | |
Dim loadAtX = h * (1 - baseT / (loads.TriLoad.EndPos - loads.TriLoad.StartPos)) | |
V -= 0.5 * baseT * (h + loadAtX) | |
Else | |
' load linearly increasing from 0 to h over baseT | |
Dim loadAtX = h * (baseT / (loads.TriLoad.EndPos - loads.TriLoad.StartPos)) | |
V -= 0.5 * baseT * loadAtX | |
End If | |
End If | |
End If | |
Return V | |
Catch ex As Exception | |
MsgBox(ex.Message) | |
Return 0 | |
End Try | |
End Function | |
' Calculate bending moment at position x | |
Public Shared Function CalculateMomentAt(x As Double, loads As BeamLoads, RA As Double) As Double | |
Try | |
Dim M = RA * x | |
' Point load | |
If x >= loads.PointLoad.Position Then | |
M -= loads.PointLoad.Magnitude * (x - loads.PointLoad.Position) | |
End If | |
' Rectangular load moment | |
If x >= loads.RectLoad.StartPos Then | |
Dim endX = Math.Min(x, loads.RectLoad.EndPos) | |
Dim lengthR = endX - loads.RectLoad.StartPos | |
If lengthR > 0 Then | |
Dim w = loads.RectLoad.Magnitude | |
M -= w * lengthR * (x - (loads.RectLoad.StartPos + lengthR / 2)) | |
End If | |
End If | |
' Triangular load moment | |
If x >= loads.TriLoad.StartPos Then | |
Dim endX = Math.Min(x, loads.TriLoad.EndPos) | |
Dim baseT = endX - loads.TriLoad.StartPos | |
If baseT > 0 Then | |
Dim h = loads.TriLoad.Magnitude | |
Dim area = 0.5 * h * baseT | |
Dim centroidX = If(loads.TriLoad.IsLeftHigh, | |
loads.TriLoad.StartPos + baseT / 3, | |
loads.TriLoad.StartPos + baseT * 2 / 3) | |
M -= area * (x - centroidX) | |
End If | |
End If | |
' Moment load (external) | |
If x >= loads.MomentLoad.Position Then | |
Dim mVal = loads.MomentLoad.Magnitude | |
If loads.MomentLoad.Direction = 0 Then | |
M -= mVal | |
Else | |
M += mVal | |
End If | |
End If | |
Return M | |
Catch ex As Exception | |
MsgBox(ex.Message) | |
Return 0 | |
End Try | |
End Function | |
' Generate shear and moment data for plotting | |
Public Shared Function GetShearMomentData(loads As BeamLoads, RA As Double, Optional stepSize As Double = 0.1) As List(Of BeamResult) | |
Try | |
Dim results As New List(Of BeamResult) | |
Dim x As Double = 0 | |
While x <= BeamLength | |
Dim shear = CalculateShearAt(x, loads, RA) | |
Dim moment = CalculateMomentAt(x, loads, RA) | |
results.Add(New BeamResult With {.Position = x, .Shear = shear, .Moment = moment}) | |
x += stepSize | |
End While | |
Return results | |
Catch ex As Exception | |
MsgBox(ex.Message) | |
Return New List(Of BeamResult)() | |
End Try | |
End Function | |
Private Sub btnCompute_Click(sender As Object, e As EventArgs) Handles btnReactions.Click | |
Dim loads As New BeamLoads With { | |
.PointLoad = (txtMagP.Text, txtLocP.Text), | |
.RectLoad = (txtMagR.Text, txtLocSR.Text, txtLocER.Text), | |
.TriLoad = (txtMagT.Text, txtLocST.Text, txtLocET.Text, cmbT.SelectedIndex), | |
.MomentLoad = (txtMagM.Text, txtLocM.Text, cmbM.SelectedIndex) | |
} | |
' Calculate reactions | |
Dim reactions = Form2.CalculateReactions(loads) | |
' Display reactions in textboxes | |
txtRA.Text = reactions.RA.ToString("F2") ' 2 decimal places | |
txtRB.Text = reactions.RB.ToString("F2") | |
End Sub | |
Private Sub btnReset_Click(sender As Object, e As EventArgs) Handles btnReset.Click | |
' Clear all input TextBoxes | |
txtMagP.Clear() | |
txtLocP.Clear() | |
txtMagR.Clear() | |
txtLocSR.Clear() | |
txtLocER.Clear() | |
txtMagT.Clear() | |
txtLocST.Clear() | |
txtLocET.Clear() | |
txtMagM.Clear() | |
txtLocM.Clear() | |
txtMagP.Focus() | |
' Reset ComboBoxes to default index (e.g., 0 or -1 if none) | |
cmbT.SelectedIndex = -1 | |
cmbM.SelectedIndex = -1 | |
' Clear reaction output TextBoxes | |
txtRA.Clear() | |
txtRB.Clear() | |
' Clear the drawing area (PictureBox) | |
picDiagram.Refresh() | |
End Sub | |
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load | |
txtRA.ReadOnly = True | |
txtRB.ReadOnly = True | |
txtMagP.Focus() | |
End Sub | |
Private Sub TextBoxes_OnlyDigitsAndPeriod_KeyPress(sender As Object, e As KeyPressEventArgs) _ | |
Handles txtMagP.KeyPress, txtLocP.KeyPress, txtMagR.KeyPress, txtLocSR.KeyPress, txtLocER.KeyPress, txtMagT.KeyPress, txtLocST.KeyPress, txtLocET.KeyPress, txtMagM.KeyPress, txtLocM.KeyPress | |
' Allow digits, control keys (Backspace), and one period (.) | |
If Not Char.IsDigit(e.KeyChar) AndAlso | |
Not Char.IsControl(e.KeyChar) AndAlso | |
Not (e.KeyChar = "."c) Then | |
e.Handled = True | |
End If | |
Dim txt As TextBox = CType(sender, TextBox) | |
' Prevent entering more than one period | |
If e.KeyChar = "."c AndAlso txt.Text.Contains(".") Then | |
e.Handled = True | |
End If | |
End Sub | |
Private Sub LocationTextBoxes_ValidateRange(sender As Object, e As System.ComponentModel.CancelEventArgs) _ | |
Handles txtLocP.Validating, txtLocSR.Validating, txtLocER.Validating, | |
txtLocST.Validating, txtLocET.Validating, txtLocM.Validating | |
Dim txt As TextBox = CType(sender, TextBox) | |
Dim value As Double | |
' Check if input is a valid number | |
If Double.TryParse(txt.Text, value) Then | |
If value < 0 OrElse value > 10 Then | |
MessageBox.Show("Value must be between 0 and 10.", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning) | |
e.Cancel = True ' Keep focus on textbox | |
End If | |
Else | |
MessageBox.Show("Please enter a valid numeric value.", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning) | |
e.Cancel = True ' Keep focus on textbox | |
End If | |
End Sub | |
Private Sub DrawShearMomentDiagrams(results As List(Of BeamResult), loads As BeamLoads) | |
Dim g As Graphics = picDiagram.CreateGraphics() | |
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias | |
g.Clear(Color.White) | |
Dim pic = picDiagram | |
Dim width = pic.Width | |
Dim height = pic.Height | |
Dim margin As Integer = 40 | |
' Find max shear and moment values for scaling | |
Dim maxShear = results.Max(Function(r) Math.Abs(r.Shear)) | |
Dim maxMoment = results.Max(Function(r) Math.Abs(r.Moment)) | |
' Reserve vertical space for each diagram | |
Dim midY = height \ 2 | |
Dim shearYBase = midY \ 2 ' Top half center (shear axis) | |
Dim momentYBase = midY + midY \ 2 ' Bottom half center (moment axis) | |
' Define scaling functions | |
Dim xScale = Function(x As Double) margin + x / BeamLength * (width - 2 * margin) | |
Dim shearScale = Function(V As Double) shearYBase - V / maxShear * (midY \ 2 - margin) | |
Dim momentScale = Function(M As Double) momentYBase - M / maxMoment * (midY \ 2 - margin) | |
' Draw separate horizontal axes | |
Dim axisPen As New Pen(Color.Black, 1) | |
g.DrawLine(axisPen, margin, shearYBase, width - margin, shearYBase) ' Shear axis | |
g.DrawLine(axisPen, margin, momentYBase, width - margin, momentYBase) ' Moment axis | |
' Collect important x-locations | |
Dim importantX As New List(Of Double) From {0, BeamLength} | |
If loads.PointLoad.Magnitude <> 0 Then importantX.Add(loads.PointLoad.Position) | |
If loads.RectLoad.Magnitude <> 0 Then | |
importantX.Add(loads.RectLoad.StartPos) | |
importantX.Add(loads.RectLoad.EndPos) | |
End If | |
If loads.TriLoad.Magnitude <> 0 Then | |
importantX.Add(loads.TriLoad.StartPos) | |
importantX.Add(loads.TriLoad.EndPos) | |
End If | |
If loads.MomentLoad.Magnitude <> 0 Then importantX.Add(loads.MomentLoad.Position) | |
Dim distinctX = importantX.Distinct().OrderBy(Function(x) x).ToList() | |
Dim dashedPen As New Pen(Color.Gray) With {.DashStyle = Drawing2D.DashStyle.Dash} | |
Dim font = New Font("Arial", 8) | |
' Draw vertical lines and labels at key x-positions | |
For Each xPos In distinctX | |
Dim xPixel = CSng(xScale(xPos)) | |
g.DrawLine(dashedPen, xPixel, margin, xPixel, height - margin) | |
' Find closest result | |
Dim pt = results.OrderBy(Function(r) Math.Abs(r.Position - xPos)).FirstOrDefault() | |
' Draw shear and moment values | |
g.DrawString($"V={pt.Shear:F2}", font, Brushes.Red, xPixel + 2, shearScale(pt.Shear) - 15) | |
g.DrawString($"M={pt.Moment:F2}", font, Brushes.Blue, xPixel + 2, momentScale(pt.Moment) + 2) | |
Next | |
' Draw Shear Diagram | |
Dim shearPen As New Pen(Color.Red, 2) | |
For i = 1 To results.Count - 1 | |
Dim x1 = CSng(xScale(results(i - 1).Position)) | |
Dim y1 = CSng(shearScale(results(i - 1).Shear)) | |
Dim x2 = CSng(xScale(results(i).Position)) | |
Dim y2 = CSng(shearScale(results(i).Shear)) | |
g.DrawLine(shearPen, x1, y1, x2, y2) | |
Next | |
' Draw Moment Diagram | |
Dim momentPen As New Pen(Color.Blue, 2) | |
For i = 1 To results.Count - 1 | |
Dim x1 = CSng(xScale(results(i - 1).Position)) | |
Dim y1 = CSng(momentScale(results(i - 1).Moment)) | |
Dim x2 = CSng(xScale(results(i).Position)) | |
Dim y2 = CSng(momentScale(results(i).Moment)) | |
g.DrawLine(momentPen, x1, y1, x2, y2) | |
Next | |
' Draw diagram labels | |
g.DrawString("Shear Diagram", font, Brushes.Red, margin, shearYBase - margin \ 2) | |
g.DrawString("Moment Diagram", font, Brushes.Blue, margin, momentYBase - margin \ 2) | |
' Clean up | |
font.Dispose() | |
axisPen.Dispose() | |
shearPen.Dispose() | |
momentPen.Dispose() | |
dashedPen.Dispose() | |
g.Dispose() | |
End Sub | |
Private Sub btnDraw_Click(sender As Object, e As EventArgs) Handles btnDraw.Click | |
' Prepare loads from user input | |
Dim loads As New BeamLoads With { | |
.PointLoad = (Double.Parse(txtMagP.Text), Double.Parse(txtLocP.Text)), | |
.RectLoad = (Double.Parse(txtMagR.Text), Double.Parse(txtLocSR.Text), Double.Parse(txtLocER.Text)), | |
.TriLoad = (Double.Parse(txtMagT.Text), Double.Parse(txtLocST.Text), Double.Parse(txtLocET.Text), cmbT.SelectedIndex = 0), | |
.MomentLoad = (Double.Parse(txtMagM.Text), Double.Parse(txtLocM.Text), cmbM.SelectedIndex) | |
} | |
' Calculate reactions | |
Dim reactions = CalculateReactions(loads) | |
' Get results | |
Dim results = GetShearMomentData(loads, reactions.RA) | |
' Draw diagrams | |
DrawShearMomentDiagrams(results, loads) | |
End Sub | |
'VISUALIZING BEAM BUTTON | |
Private Sub btnBeam_Click(sender As Object, e As EventArgs) Handles btnBeam.Click | |
' Create Bitmap and Graphics | |
Dim bmp As New Bitmap(picDiagram.Width, picDiagram.Height) | |
Dim g As Graphics = Graphics.FromImage(bmp) | |
g.Clear(Color.White) | |
' Beam settings | |
Dim beamLength As Integer = 400 ' fixed beam length in pixels | |
Dim beamStartX As Integer = (picDiagram.Width - beamLength) \ 2 ' center horizontally | |
Dim beamY As Integer = 200 ' center vertically | |
Dim beamEndX As Integer = beamStartX + beamLength | |
' Draw the beam | |
g.FillRectangle(Brushes.White, beamStartX, beamY - 5, beamLength, 10) | |
g.DrawRectangle(Pens.Black, beamStartX, beamY - 5, beamLength, 10) | |
' Draw supports | |
DrawPinSupport(g, beamStartX, beamY) | |
DrawRollerSupport(g, beamEndX, beamY) | |
' Distance line (closer to beam) | |
Dim lineY As Integer = beamY + 25 | |
g.DrawLine(Pens.Red, beamStartX, lineY, beamEndX, lineY) | |
g.DrawLine(Pens.Red, beamStartX, lineY - 5, beamStartX, lineY + 5) | |
g.DrawLine(Pens.Red, beamEndX, lineY - 5, beamEndX, lineY + 5) | |
' Distance label in red | |
Dim labelFont As New Font("Arial", 10) | |
Dim labelText As String = "10 m" | |
Dim labelSize As SizeF = g.MeasureString(labelText, labelFont) | |
Dim labelX As Integer = beamStartX + (beamLength - labelSize.Width) \ 2 | |
Dim labelY As Integer = lineY + 5 | |
g.DrawString(labelText, labelFont, Brushes.Red, labelX, labelY) | |
' ========== Point Load ========== | |
Dim magnitudeP As Double | |
Dim positionMetersP As Double | |
Dim drawPointLoadFlag As Boolean = False | |
If Not String.IsNullOrWhiteSpace(txtMagP.Text) OrElse Not String.IsNullOrWhiteSpace(txtLocP.Text) Then | |
If Not Double.TryParse(txtMagP.Text, magnitudeP) Then | |
MessageBox.Show("Please enter a valid number for point load magnitude.") | |
ElseIf Not Double.TryParse(txtLocP.Text, positionMetersP) Then | |
MessageBox.Show("Please enter a valid number for point load position.") | |
ElseIf positionMetersP < 0 OrElse positionMetersP > 10 Then | |
MessageBox.Show("Point load position must be between 0 and 10 meters.") | |
Else | |
drawPointLoadFlag = True | |
End If | |
End If | |
If drawPointLoadFlag Then | |
Dim loadXP As Integer = beamStartX + CInt(positionMetersP / 10 * beamLength) | |
DrawPointLoad(g, loadXP, beamY, magnitudeP, Math.Abs(magnitudeP).ToString("0.##") & " kN") | |
End If | |
' ========== Rectangular Load ========== | |
Dim magnitudeR As Double | |
Dim startPosR As Double | |
Dim endPosR As Double | |
Dim drawRectLoadFlag As Boolean = False | |
If Not String.IsNullOrWhiteSpace(txtMagR.Text) OrElse Not String.IsNullOrWhiteSpace(txtLocSR.Text) OrElse Not String.IsNullOrWhiteSpace(txtLocER.Text) Then | |
If Not Double.TryParse(txtMagR.Text, magnitudeR) Then | |
MessageBox.Show("Enter a valid number for rectangular load magnitude.") | |
ElseIf Not Double.TryParse(txtLocSR.Text, startPosR) Then | |
MessageBox.Show("Enter a valid number for rectangular load start position.") | |
ElseIf Not Double.TryParse(txtLocER.Text, endPosR) Then | |
MessageBox.Show("Enter a valid number for rectangular load end position.") | |
ElseIf startPosR < 0 OrElse startPosR > 10 OrElse endPosR < 0 OrElse endPosR > 10 Then | |
MessageBox.Show("Rectangular load positions must be between 0 and 10 meters.") | |
ElseIf startPosR >= endPosR Then | |
MessageBox.Show("Rectangular load end position must be greater than start position.") | |
Else | |
drawRectLoadFlag = True | |
End If | |
End If | |
If drawRectLoadFlag Then | |
Dim loadStartXR As Integer = beamStartX + CInt((startPosR / 10) * beamLength) | |
Dim loadEndXR As Integer = beamStartX + CInt((endPosR / 10) * beamLength) | |
DrawRectangularLoad(g, loadStartXR, loadEndXR, beamY, magnitudeR) | |
End If | |
' ========== Triangular Load ========== | |
Dim magnitudeT As Double | |
Dim startPosT As Double | |
Dim endPosT As Double | |
Dim drawTriLoadFlag As Boolean = False | |
' Check if user started input for triangular load | |
If Not String.IsNullOrWhiteSpace(txtMagT.Text) OrElse Not String.IsNullOrWhiteSpace(txtLocST.Text) OrElse Not String.IsNullOrWhiteSpace(txtLocET.Text) OrElse cmbT.SelectedIndex <> -1 Then | |
' Validate combo box | |
If cmbT.SelectedIndex = -1 Then | |
MessageBox.Show("Please select a triangular load direction (Left High or Right High).") | |
ElseIf Not Double.TryParse(txtMagT.Text, magnitudeT) Then | |
MessageBox.Show("Enter a valid number for triangular load magnitude.") | |
ElseIf Not Double.TryParse(txtLocST.Text, startPosT) Then | |
MessageBox.Show("Enter a valid number for triangular load start position.") | |
ElseIf Not Double.TryParse(txtLocET.Text, endPosT) Then | |
MessageBox.Show("Enter a valid number for triangular load end position.") | |
ElseIf startPosT < 0 OrElse startPosT > 10 OrElse endPosT < 0 OrElse endPosT > 10 Then | |
MessageBox.Show("Triangular load positions must be between 0 and 10 meters.") | |
ElseIf startPosT >= endPosT Then | |
MessageBox.Show("Triangular load end position must be greater than start position.") | |
Else | |
drawTriLoadFlag = True | |
End If | |
End If | |
If drawTriLoadFlag Then | |
Dim isLeftHigh As Boolean = (cmbT.SelectedIndex = 0) | |
Dim loadStartXT As Integer = beamStartX + CInt(startPosT / 10 * beamLength) | |
Dim loadEndXT As Integer = beamStartX + CInt(endPosT / 10 * beamLength) | |
DrawTriangularLoad(g, loadStartXT, loadEndXT, beamY, magnitudeT, isLeftHigh) | |
End If | |
' ========== Moment Load ========== | |
Dim magnitudeM As Double | |
Dim positionMetersM As Double | |
Dim drawMomentLoadFlag As Boolean = False | |
If Not String.IsNullOrWhiteSpace(txtMagM.Text) OrElse Not String.IsNullOrWhiteSpace(txtLocM.Text) OrElse cmbM.SelectedIndex <> -1 Then | |
' Validate combo box | |
If cmbM.SelectedIndex = -1 Then | |
MessageBox.Show("Select moment direction: Clockwise or Counterclockwise.") | |
ElseIf Not Double.TryParse(txtMagM.Text, magnitudeM) Then | |
MessageBox.Show("Enter a valid number for moment magnitude.") | |
ElseIf Not Double.TryParse(txtLocM.Text, positionMetersM) Then | |
MessageBox.Show("Enter a valid number for moment position.") | |
ElseIf positionMetersM < 0 OrElse positionMetersM > 10 Then | |
MessageBox.Show("Moment position must be between 0 and 10 meters.") | |
Else | |
drawMomentLoadFlag = True | |
End If | |
End If | |
If drawMomentLoadFlag Then | |
Dim direction As String = cmbM.SelectedItem?.ToString() | |
Dim isClockwise As Boolean = (direction = "Clockwise") | |
Dim loadXM As Integer = beamStartX + CInt(positionMetersM / 10 * beamLength) | |
DrawMomentLoad(g, loadXM, beamY, magnitudeM, Math.Abs(magnitudeM).ToString("0.##") & " kNm", isClockwise) | |
End If | |
' Set the image to the PictureBox | |
picDiagram.Image = bmp | |
' Dispose graphics | |
g.Dispose() | |
End Sub | |
' Draws a Pin Support symbol at (x, y) | |
Private Sub DrawPinSupport(g As Graphics, x As Integer, y As Integer) | |
Dim pinPoints() As Point = { | |
New Point(x, y), | |
New Point(x - 8, y + 15), | |
New Point(x + 8, y + 15) | |
} | |
g.FillPolygon(Brushes.White, pinPoints) | |
g.DrawPolygon(Pens.Black, pinPoints) | |
End Sub | |
' Draws a Roller Support symbol at (x, y) | |
Private Sub DrawRollerSupport(g As Graphics, x As Integer, y As Integer) | |
Dim rollerPoints() As Point = { | |
New Point(x, y), | |
New Point(x - 8, y + 15), | |
New Point(x + 8, y + 15) | |
} | |
g.FillPolygon(Brushes.White, rollerPoints) | |
g.DrawPolygon(Pens.Black, rollerPoints) | |
g.FillEllipse(Brushes.White, x - 6, y + 15, 3, 3) | |
g.DrawEllipse(Pens.Black, x - 6, y + 15, 3, 3) | |
g.FillEllipse(Brushes.White, x - 1, y + 15, 3, 3) | |
g.DrawEllipse(Pens.Black, x - 1, y + 15, 3, 3) | |
g.FillEllipse(Brushes.White, x + 4, y + 15, 3, 3) | |
g.DrawEllipse(Pens.Black, x + 4, y + 15, 3, 3) | |
End Sub | |
' Draws a Point Load arrow at (x, beamY), magnitude positive = upward below beam, negative = downward above beam | |
Private Sub DrawPointLoad(g As Graphics, x As Integer, beamY As Integer, magnitude As Double, label As String) | |
Dim arrowLength As Integer = 50 | |
Dim headSize As Integer = 6 | |
Dim offset As Integer = 5 | |
Dim arrowPen As Pen = Pens.Green | |
Dim labelFont As New Font("Arial", 9) | |
Dim labelBrush As Brush = Brushes.Green | |
If magnitude > 0 Then | |
' Arrow below beam pointing up | |
Dim arrowTipY As Integer = beamY + offset | |
Dim arrowStartY As Integer = arrowTipY + arrowLength | |
g.DrawLine(arrowPen, x, arrowStartY, x, arrowTipY) | |
g.DrawLine(arrowPen, x, arrowTipY, x - headSize, arrowTipY + headSize) | |
g.DrawLine(arrowPen, x, arrowTipY, x + headSize, arrowTipY + headSize) | |
Dim labelSize As SizeF = g.MeasureString(label, labelFont) | |
g.DrawString(label, labelFont, labelBrush, x - labelSize.Width \ 2, arrowStartY + 2) | |
ElseIf magnitude < 0 Then | |
' Arrow above beam pointing down | |
Dim arrowTipY As Integer = beamY - offset | |
Dim arrowStartY As Integer = arrowTipY - arrowLength | |
g.DrawLine(arrowPen, x, arrowStartY, x, arrowTipY) | |
g.DrawLine(arrowPen, x, arrowTipY, x - headSize, arrowTipY - headSize) | |
g.DrawLine(arrowPen, x, arrowTipY, x + headSize, arrowTipY - headSize) | |
Dim labelSize As SizeF = g.MeasureString(label, labelFont) | |
g.DrawString(label, labelFont, labelBrush, x - labelSize.Width \ 2, arrowStartY - labelSize.Height - 2) | |
Else | |
' zero load - no arrow drawn | |
End If | |
End Sub | |
' Draws a Rectangular Load distributed between startX and endX at beamY, positive magnitude below beam, negative above | |
Private Sub DrawRectangularLoad(g As Graphics, startX As Integer, endX As Integer, beamY As Integer, magnitude As Double) | |
Dim arrowLength As Integer = 50 | |
Dim headSize As Integer = 6 | |
Dim offset As Integer = 5 | |
Dim arrowPen As Pen = Pens.Green | |
Dim labelFont As New Font("Arial", 9) | |
Dim labelBrush As Brush = Brushes.Green | |
Dim arrowSpacing As Integer = 20 | |
Dim arrowCount As Integer = CInt((endX - startX) / arrowSpacing) | |
If arrowCount < 1 Then arrowCount = 1 | |
Dim arrowBaseYs As New List(Of Integer) | |
For i As Integer = 0 To arrowCount | |
Dim x As Integer = startX + CInt(i * (endX - startX) / arrowCount) | |
If magnitude > 0 Then | |
Dim arrowTipY As Integer = beamY + offset | |
Dim arrowStartY As Integer = arrowTipY + arrowLength | |
g.DrawLine(arrowPen, x, arrowStartY, x, arrowTipY) | |
g.DrawLine(arrowPen, x, arrowTipY, x - headSize, arrowTipY + headSize) | |
g.DrawLine(arrowPen, x, arrowTipY, x + headSize, arrowTipY + headSize) | |
arrowBaseYs.Add(arrowStartY) | |
ElseIf magnitude < 0 Then | |
Dim arrowTipY As Integer = beamY - offset | |
Dim arrowStartY As Integer = arrowTipY - arrowLength | |
g.DrawLine(arrowPen, x, arrowStartY, x, arrowTipY) | |
g.DrawLine(arrowPen, x, arrowTipY, x - headSize, arrowTipY - headSize) | |
g.DrawLine(arrowPen, x, arrowTipY, x + headSize, arrowTipY - headSize) | |
arrowBaseYs.Add(arrowStartY) | |
End If | |
Next | |
If arrowBaseYs.Count >= 2 Then | |
g.DrawLine(arrowPen, startX, arrowBaseYs(0), endX, arrowBaseYs(0)) | |
End If | |
Dim textY As Integer | |
If magnitude > 0 Then | |
textY = arrowBaseYs(0) + 10 | |
Else | |
textY = arrowBaseYs(0) - 25 | |
End If | |
Dim magnitudeText As String = Math.Abs(magnitude).ToString("0.##") & " kN" | |
Dim textSize As SizeF = g.MeasureString(magnitudeText, labelFont) | |
Dim textX As Integer = startX + (endX - startX) \ 2 - CInt(textSize.Width / 2) | |
g.DrawString(magnitudeText, labelFont, labelBrush, textX, textY) | |
End Sub | |
' Draws a Triangular Load between startX and endX at beamY, magnitude positive below beam, negative above, isLeftHigh defines slope direction | |
Private Sub DrawTriangularLoad(g As Graphics, startX As Integer, endX As Integer, beamY As Integer, magnitude As Double, isLeftHigh As Boolean) | |
Dim maxArrowLength As Integer = 50 | |
Dim headSize As Integer = 6 | |
Dim offset As Integer = 5 | |
Dim arrowPen As Pen = Pens.Blue | |
Dim labelFont As New Font("Arial", 9) | |
Dim labelBrush As Brush = Brushes.Blue | |
Dim arrowSpacing As Integer = 20 | |
Dim arrowCount As Integer = CInt((endX - startX) / arrowSpacing) | |
If arrowCount < 1 Then arrowCount = 1 | |
Dim arrowPositions As New List(Of Integer) | |
Dim arrowBaseYs As New List(Of Integer) | |
For i As Integer = 0 To arrowCount | |
Dim x As Integer = startX + CInt(i * (endX - startX) / arrowCount) | |
arrowPositions.Add(x) | |
Next | |
For i As Integer = 0 To arrowCount | |
Dim x As Integer = arrowPositions(i) | |
Dim t As Double | |
If isLeftHigh Then | |
t = CDbl(i) / arrowCount | |
Else | |
t = 1.0 - CDbl(i) / arrowCount | |
End If | |
Dim arrowLength As Integer = CInt(maxArrowLength * t) | |
Dim isLastArrow As Boolean = If(isLeftHigh, i = arrowCount, i = 0) | |
If magnitude > 0 Then | |
Dim arrowTipY As Integer = beamY + offset | |
Dim arrowStartY As Integer = arrowTipY + arrowLength | |
g.DrawLine(arrowPen, x, arrowStartY, x, arrowTipY) | |
arrowBaseYs.Add(arrowStartY) | |
If Not (isLastArrow And arrowLength = 0) Then | |
g.DrawLine(arrowPen, x, arrowTipY, x - headSize, arrowTipY + headSize) | |
g.DrawLine(arrowPen, x, arrowTipY, x + headSize, arrowTipY + headSize) | |
End If | |
ElseIf magnitude < 0 Then | |
Dim arrowTipY As Integer = beamY - offset | |
Dim arrowStartY As Integer = arrowTipY - arrowLength | |
g.DrawLine(arrowPen, x, arrowStartY, x, arrowTipY) | |
arrowBaseYs.Add(arrowStartY) | |
If Not (isLastArrow And arrowLength = 0) Then | |
g.DrawLine(arrowPen, x, arrowTipY, x - headSize, arrowTipY - headSize) | |
g.DrawLine(arrowPen, x, arrowTipY, x + headSize, arrowTipY - headSize) | |
End If | |
End If | |
Next | |
If arrowBaseYs.Count >= 2 Then | |
g.DrawLine(arrowPen, startX, arrowBaseYs(0), endX, arrowBaseYs(arrowBaseYs.Count - 1)) | |
End If | |
Dim textY As Integer | |
If magnitude > 0 Then | |
textY = arrowBaseYs(0) + 10 | |
Else | |
textY = arrowBaseYs(0) - 25 | |
End If | |
Dim magnitudeText As String = Math.Abs(magnitude).ToString("0.##") & " kN" | |
Dim textSize As SizeF = g.MeasureString(magnitudeText, labelFont) | |
Dim textX As Integer = startX + (endX - startX) \ 2 - CInt(textSize.Width / 2) | |
g.DrawString(magnitudeText, labelFont, labelBrush, textX, textY) | |
End Sub | |
' Draws a Moment Load at (x, y), magnitude positive for clockwise, negative for counterclockwise | |
Sub DrawMomentLoad(g As Graphics, x As Integer, y As Integer, magnitude As Double, label As String, isClockwise As Boolean) | |
Dim pen As New Pen(Color.Blue, 1) | |
Dim radius As Integer = 20 | |
Dim startAngle As Single | |
Dim sweepAngle As Single | |
If isClockwise Then | |
startAngle = 210.0F | |
sweepAngle = -300.0F | |
Else | |
startAngle = 30.0F | |
sweepAngle = 300.0F | |
End If | |
' Draw arc | |
Dim rect As New Rectangle(x - radius, y - radius, radius * 2, radius * 2) | |
g.DrawArc(pen, rect, startAngle, sweepAngle) | |
' Arrowhead at end of arc | |
Dim endAngleRad As Double = (startAngle + sweepAngle) * Math.PI / 180 | |
' TIP of the arrow is ON the arc | |
Dim tipX As Integer = CInt(x + radius * Math.Cos(endAngleRad)) | |
Dim tipY As Integer = CInt(y + radius * Math.Sin(endAngleRad)) | |
' Tangent direction (arrow points along tangent) — FLIPPED by 180° | |
Dim tangentAngle As Double | |
If isClockwise Then | |
tangentAngle = endAngleRad + Math.PI / 2 + Math.PI ' flipped direction | |
Else | |
tangentAngle = endAngleRad - Math.PI / 2 + Math.PI ' flipped direction | |
End If | |
' Wing angle offset (how "bent" the V is) | |
Dim wingAngleOffset As Double = Math.PI / 6 ' 30 degrees | |
' Arrowhead wings go BACK from the tip | |
Dim wingLength As Integer = 10 | |
Dim leftWingAngle As Double = tangentAngle - wingAngleOffset | |
Dim rightWingAngle As Double = tangentAngle + wingAngleOffset | |
Dim leftWingX As Integer = CInt(tipX - wingLength * Math.Cos(leftWingAngle)) | |
Dim leftWingY As Integer = CInt(tipY - wingLength * Math.Sin(leftWingAngle)) | |
Dim rightWingX As Integer = CInt(tipX - wingLength * Math.Cos(rightWingAngle)) | |
Dim rightWingY As Integer = CInt(tipY - wingLength * Math.Sin(rightWingAngle)) | |
' Draw V-shaped arrowhead | |
g.DrawLine(pen, tipX, tipY, leftWingX, leftWingY) | |
g.DrawLine(pen, tipX, tipY, rightWingX, rightWingY) | |
' Label | |
g.DrawString(label, SystemFonts.DefaultFont, Brushes.Black, x + radius + 5, y - 10) | |
pen.Dispose() | |
End Sub | |
Private Sub ExitToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExitToolStripMenuItem.Click | |
If MessageBox.Show("Exit?", "Exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = vbYes Then End | |
End Sub | |
Private Sub ResetToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ResetToolStripMenuItem.Click | |
' Clear all input TextBoxes | |
txtMagP.Clear() | |
txtLocP.Clear() | |
txtMagR.Clear() | |
txtLocSR.Clear() | |
txtLocER.Clear() | |
txtMagT.Clear() | |
txtLocST.Clear() | |
txtLocET.Clear() | |
txtMagM.Clear() | |
txtLocM.Clear() | |
txtMagP.Focus() | |
' Reset ComboBoxes to default index (e.g., 0 or -1 if none) | |
cmbT.SelectedIndex = -1 | |
cmbM.SelectedIndex = -1 | |
' Clear reaction output TextBoxes | |
txtRA.Clear() | |
txtRB.Clear() | |
' Clear the drawing area (PictureBox) | |
picDiagram.Refresh() | |
End Sub | |
End Class |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment