Skip to content

Instantly share code, notes, and snippets.

@110threat
Created May 25, 2025 14:38
Show Gist options
  • Save 110threat/d87f1db59ca746991ef5cb63075eed96 to your computer and use it in GitHub Desktop.
Save 110threat/d87f1db59ca746991ef5cb63075eed96 to your computer and use it in GitHub Desktop.
SBMD and Reactions calculator
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