Graphics drawline scaling vb.net - vb.net

I'm trying to develop a program that has been made by another person.
What I develop is just for showing the measurement point like the picture below.
There are min and max data that appear in the form and it's showing from the smallest to the largest value and every time the mouse moves from left to right, the measuring point value will adjust to the graph value that has been determined as above.
The problem is data accuracy at the measuring point does not match with the value that has been determined, this is because the graphic is out of frame.
Here is the code for showing graphics;
Private Sub UpdateDiaGraph()
Call Me.ClearGraph(Me.gDia, Me.PicDiaGraph)
Call Me.InitializeGraph(Me.gDia, Me.PicDiaGraph, Me.recDia)
If Me.dsData.Tables("Dia").Rows.Count = 0 Then
Exit Sub
End If
Dim drs() As DataRow = Me.dsData.Tables("Dia").Select("", "[PathNo],[EquipmentNo],[Point]")
Dim pathNo As Integer = 0
Dim equipmentNo As Integer = 0
Dim diaCount As Integer = 0
Dim maxPoint As Integer = 0
Dim longestDiaCount As Integer = 0
Dim upper As New List(Of Decimal)
Dim lower As New List(Of Decimal)
For Each dr As DataRow In drs
If pathNo <> dr("PathNo") Or equipmentNo <> dr("EquipmentNo") Then
pathNo = dr("PathNo")
equipmentNo = dr("EquipmentNo")
'Count the graphics
diaCount += 1
' Get standard width
upper.Add(dr("Upper"))
lower.Add(dr("Lower"))
End If
' Get the max number of graph points and the number of graphs at that time
If maxPoint < dr("Point") Then
maxPoint = dr("Point")
longestDiaCount = diaCount
End If
Next
Dim x1, x2 As Decimal
Dim y1, y2 As Decimal
' Get the width to draw the reference line
Dim oneHeight As Decimal = Me.recDia.Height / (diaCount + 1)
'Initialize the graph
For i As Integer = 1 To diaCount
x1 = Me.recDia.Left + 1
x2 = Me.recDia.Left + Me.recDia.Width - 1
y1 = Me.recDia.Top + oneHeight * i
' Tolerance range
'※0.01mm/1 Point
Me.gDia.FillRectangle(New SolidBrush(Me.toleranceRecColor) _
, x1 _
, y1 - upper(diaCount - i) * 100 _
, Me.recDia.Width - 1 _
, (upper(diaCount - i) - lower(diaCount - i)) * 100)
' reference line
Me.gDia.DrawLine(New Pen(Me.centerLineColor), x1, y1, x2, y1)
Next
' Determine the drawing magnification of the X-axis so that the max number of points can be displayed
Dim xMag As Decimal = Me.recDia.Width / maxPoint
'Draw a graph
Dim counter As Integer = 0
Dim centerLineY As Decimal
Dim center As Decimal = 0
Dim p As Integer = -1
pathNo = 0 : equipmentNo = 0
For Each dr As DataRow In drs
' Get reference line and reference value
If pathNo <> dr("PathNo") Or equipmentNo <> dr("EquipmentNo") Then
pathNo = dr("PathNo")
equipmentNo = dr("EquipmentNo")
counter += 1
centerLineY = Me.recDia.Top + oneHeight * (diaCount - (counter - 1))
center = dr("Center")
'Draw chart title and reference value
Dim num As New Common.Numeric
Dim numFormat As String = num.GetNumericFormat(1, 2)
Dim s As String = dr("MeasuringTarget").ToString & ControlChars.CrLf
Dim sSize As Size = TextRenderer.MeasureText(Me.gDia, s, Me.Font)
Me.gDia.DrawString(s, Me.Font, Brushes.White, Me.recDia.Left - sSize.Width - 2, centerLineY - sSize.Height / 2)
s = ControlChars.CrLf & Format(center, numFormat)
sSize = TextRenderer.MeasureText(Me.gDia, s, Me.Font)
Me.gDia.DrawString(s, Me.Font, Brushes.White, Me.recDia.Left - sSize.Width - 2, centerLineY - sSize.Height / 2)
x1 = 0 : x2 = 0 : y1 = 0 : y2 = 0 : p = -1
End If
p += 1
'Change the drawing direction and starting point of the graph depending on the number of direction changes
If Me.extRevCnt.Item(pathNo) Mod 2 = 0 Then
x2 = Me.recDia.Left + p * xMag
Else
x2 = Me.recDia.Left + Me.recDia.Width - p * xMag
End If
y2 = centerLineY - (dr("Data") - center) * 100
If p <> 0 Then
'graph drawing
If Me.recDia.Top < y2 And y2 < Me.recDia.Top + Me.recDia.Height Then
' Change the drawing color of the graph for each outer diameter
If counter < Me.diaGraphColor.Count Then
Me.gDia.DrawLine(New Pen(Me.diaGraphColor(counter)), x1, y1, x2, y2)
Else
Me.gDia.DrawLine(New Pen(Me.graphColor), x1, y1, x2, y2)
End If
End If
' out of tolerance
If dr("DataFlag") = Common.Constant.DataFlag.NG Then
Me.gDia.DrawLine(New Pen(Me.ngColor), x1, centerLineY, x2, centerLineY)
End If
'Draw a scale every 10m * However, only when drawing the longest graph
'Drawn for the first graph (normal mandrel diameter)
If counter = 1 Then
If p Mod 100 = 0 Then
Me.gDia.DrawLine(Pens.White, x2, Me.recDia.Top + Me.recDia.Height, x2, Me.recDia.Top + Me.recDia.Height - 3)
Dim s As String = (p \ 10).ToString & "m"
Dim sSize As Size = TextRenderer.MeasureText(Me.gDia, s, Me.Font)
Me.gDia.DrawString(s, Me.Font, Brushes.White, x2 - sSize.Width / 2, Me.recDia.Top + Me.recDia.Height + 2)
End If
End If
Else
' determine the corners of a polygon
Dim points As Point() = {New Point(x2, centerLineY - 10), New Point(x2 - 10, centerLineY - 40), New Point(x2 + 10, centerLineY - 40)}
Me.gDia.FillPolygon(Brushes.Blue, points, System.Drawing.Drawing2D.FillMode.Alternate)
' Display the division number in the circle above
Dim s As String = dr("DivNo").ToString
Dim sSize As Size = TextRenderer.MeasureText(Me.gDia, s, Me.Font)
Me.gDia.DrawString(s, New Font(Me.Font, FontStyle.Regular), Brushes.White, x2 - sSize.Width / 2 + 2, centerLineY - 35)
End If
x1 = x2
y1 = y2
Next
Type.Variable.divideTargetDia = Me.DGVDataDia.SelectedRows.Item(0).Index
Call Me.DrawingGraphDia(Me.DGVDataDia.SelectedRows.Item(0).Index)
Me.PicDiaGraph.Refresh()
End Sub
Here is the code for showing data pointer
Private Sub PicDiaGraph_Paint(ByVal gDia As Graphics)
If IsNothing(tblDataDia) = True Then Exit Sub
If tblDataDia.Rows.Count = 0 Then Exit Sub
If IsNothing(tblEquipmentNoDia) = True Then Exit Sub
If tblEquipmentNoDia.Rows.Count = 0 Then Exit Sub
' Allocate the drawing range according to the number of device numbers
Dim allHeight As Integer = Me.PicDiaGraph.Size.Height
'Drawing range per device number
Dim heightPerEN As Integer = allHeight / (tblEquipmentNoDia.Rows.Count + 1)
' Device number count
Dim count As Integer = 0
' Draw a graph for each device number
For Each rEN As DataRow In tblEquipmentNoDia.Rows
Dim eNo As Integer = rEN("EquipmentNo")
Dim tblSource As DataTable = tblDataDia.Clone
For Each r As DataRow In tblDataDia.Select("EquipmentNo = '" & eNo & "'", "Point ASC")
tblSource.ImportRow(r)
Next
' Acquisition of standard value
Dim tblStandard As New DataTable
If Me.Today = False Then
Dim SQL As String = ""
SQL &= "SELECT Standard.Center "
SQL &= "FROM Standard "
SQL &= "WHERE Standard.EquipmentNo = " & eNo & " And Standard.MfgID = '" & barcodeNo.Trim & "' "
SQL &= "GROUP BY Standard.Center;"
daDia = New OleDb.OleDbDataAdapter(SQL, cnMdbDia)
daDia.Fill(tblStandard)
Else
'
End If
Dim center As Single = Format(tblStandard.Rows(0)("center"), "0.00")
' Drawing start position (height)
Dim shift As Integer = heightPerEN * count
'get width of data
Dim margin As Integer = 0
_xMaxDia = tblSource.Select("Point = MAX(Point)")(0)("Point")
_xMinDia = tblSource.Select("Point = MIN(Point)")(0)("Point")
Dim yMax As Single = tblSource.Select("Data = MAX(Data)")(0)("Data")
Dim yMin As Single = tblSource.Select("Data = MIN(Data)")(0)("Data")
'Width on X-axis data
Dim xRange As Integer = _xMaxDia - _xMinDia
'Width on Y-axis data
Dim yRange As Single = yMax - yMin
'Get size of control
Dim height As Integer = heightPerEN - (margin * 2)
Dim width As Integer = Me.PicDiaGraph.Size.Width - 21
' Nominal length per unit
_xUnitDia = width / xRange
Dim yUnit As Single = 200
'graph drawing
For i As Integer = 0 To tblSource.Rows.Count - 2
Dim x1 As Decimal = Me.recDia.Left + 1
' starting point data
Dim row1 As DataRow = tblSource.Rows(i)
Dim point1 As Integer = row1("Point")
Dim data1 As Single = row1("Data")
' end point data
Dim row2 As DataRow = tblSource.Rows(i + 1)
Dim point2 As Integer = row2("Point")
Dim data2 As Single = row2("Data")
If point2 < point1 Then MessageBox.Show("X")
Me.gDia.DrawLine(Pens.White, _
CInt((point1 - _xMinDia) * _xUnitDia), _
height - (data1 - center) * yUnit + margin + shift, _
CInt(point2 - _xMinDia) * _xUnitDia, _
height - (data2 - center) * yUnit + margin + shift)
Next
count += 1
Next
End Sub
The problem might appear in this code

Related

Create Mouse Events for controls created by code

I am a beguinner in Access so I need your help with this.
I am try making a "Gannt Chart" and to do that I create some objects by code, but when I do that I can't get the atributes of the event, see
Option Compare Database
Function teste()
MsgBox ("Foi")
End Function
Function gannt()
Dim shpBox As Rectangle
DoCmd.OpenForm "Formulário3", acDesign
Set shpBox = Application.CreateControl("Formulário3", acRectangle, acDetail, "", "", 500, 500, 2000, 500)
shpBox.name = "Objeto1"
shpBox.Visible = True
shpBox.onMouseDown = "=teste()"
DoCmd.OpenForm "Formulário3", acNormal
End Function
The procedure of event has this declaration:
Private Sub Objeto1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
I think that one of solution is getting a mouse position by code, but I don't have a code to do this and probabily this code will bring an absolute position of the mouse.
after thinking a lot I came up with a solution.
First I had create the objects by code assigment public functions to events of MouseDown, MouseUp and MouveMove.
I've declared the public Vars
drag: The object received MouseDown Event
cod_manut: Name of the object
data_manut: Date of the start of maintenance
Option Compare Database
Option Explicit
Public drag(500) As Long
Public cod_manut(500) As Integer
Public data_manut(500, 2) As Date
Public valorX As Long '
Public valorY As Long '
Public clickX As Long '
Public clickY As Long '
Public offset As Long '
Function to populate the Form with the Gannt Objects:
Function gant()
Dim shpBox As Rectangle
Dim inicio As Integer
Dim distancia As Integer
Dim i As Integer
Dim d As Date
Dim aux As Integer
Dim entrada As Integer
Dim largura As Integer
Dim tabela As Recordset
Dim sql As String * 2048
sql = "SELECT [Programadas + status].Código, [Programadas + status].Entrada, [Programadas + status].Saida " _
& "FROM [Programadas + status] " _
& "WHERE ((([Programadas + status].Entrada) < #12/31/2020#) And (([Programadas + status].Saida) >= #1/1/2020#) And (([Programadas + status].Local) = 'SOD')) " _
& "ORDER BY [Programadas + status].Entrada, [Programadas + status].Saida;"
Set tabela = CurrentDb.OpenRecordset(sql)
i = 100
While (Not tabela.EOF)
cod_manut(i) = tabela.Fields("Código").value
d = tabela.Fields("Entrada").value
If (d < #1/1/2020#) Then
d = #1/1/2020#
End If
data_manut(i, 0) = d
d = tabela.Fields("Saida").value
If (d > #12/31/2020#) Then
d = #12/31/2020#
End If
data_manut(i, 1) = d
i = i + 1
tabela.MoveNext
Wend
DoCmd.OpenForm "Formulário4", acDesign
inicio = 1350
distancia = 408
'Set shpBox = Forms!Formulário4!Caixa0
For i = 100 To 173
aux = DateDiff("d", #1/1/2020#, data_manut(i, 0))
entrada = (aux \ 7) * 510 + (aux Mod 7) * 72
aux = DateDiff("d", data_manut(i, 0), data_manut(i, 1))
largura = aux * 72
Set shpBox = Application.CreateControl("Formulário4", acRectangle, acDetail, "", "", entrada, inicio + distancia * (i - 100), largura, 300)
shpBox.name = Replace(Str(i), " ", "")
shpBox.BackColor = 13998939
shpBox.BackStyle = 1
shpBox.Visible = True
shpBox.onMouseDown = Replace("=funcA(""" & Str(i) & """)", " ", "")
shpBox.onMouseUp = Replace("=funcB(""" & Str(i) & """)", " ", "")
shpBox.OnMouseMove = Replace("=funcC(""" & Str(i) & """)", " ", "")
Next i
DoCmd.OpenForm "Formulário4", acNormal
End Function
Function Events
Function funcA(id As String)
Dim b As Integer
Dim i As Integer
Dim nome As String
For i = 0 To 200
nome = (Forms!Formulário4.Controls(i).name)
If nome = id Then
Exit For
End If
Next
b = Get_Cursor_Pos()
clickX = ((valorX - offset) * 15) - Forms!Formulário4.Controls(i).Left
'clickY = (valorX - offset) * 15
drag(i) = True
End Function
Function funcB(id As String)
Dim b As Integer
Dim i As Integer
Dim nome As String
b = Get_Cursor_Pos()
For i = 0 To 200
nome = (Forms!Formulário4.Controls(i).name)
If nome = id Then
Exit For
End If
Next
drag(i) = False
End Function
Function funcC(id As String)
Dim aux As Integer
Dim i As Integer
Dim posX As Integer
Dim posX2 As Integer
Dim nome As String
Dim inicio As Integer
Dim fim As Integer
Dim X As Integer
Dim Y As Integer
inicio = 0
fim = 28720 - 1180
aux = Get_Cursor_Pos()
X = (valorX - offset) * 15
Y = (valorX - offset) * 15
For i = 0 To 200
nome = (Forms!Formulário4.Controls(i).name)
If nome = id Then
Exit For
End If
Next
aux = 0
If drag(i) = True Then 'And Button = acLeftButton Then
'If Shift = acShiftMask Then
posX2 = X - clickX
If Abs(posX2 - posX) > 72 Then
posX = ((posX2 - posX) \ 72) * 72 + posX + 3
posX = posX + (posX \ 504) * 6
End If
'Else
' posX = X - clickX
'End If
If posX < inicio Then
posX = inicio
ElseIf (posX + Forms!Formulário4.Controls(i).Width) > fim Then
posX = fim - Forms!Formulário4.Controls(i).Width
End If
Forms!Formulário4.Controls(i).Left = posX
Forms!Formulário4.mouse1.Caption = ((posX \ 510)) * 7 + (posX - ((posX \ 510) * 510) - 3) \ 72
Forms!Formulário4.Mouse2.Caption = (posX \ 510) + 1
End If
End Function
I had to use this code to get the absolute mouse position, but was necessary do the conversion to use this value
Note.: This value was in pixel, I need to multiply to 15 to get it in twips.
' Access the GetCursorPos function in user32.dll
Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
' Access the GetCursorPos function in user32.dll
Declare PtrSafe Function SetCursorPos Lib "user32" (ByVal X As Long, ByVal Y As Long) As Long
' GetCursorPos requires a variable declared as a custom data type
' that will hold two integers, one for x value and one for y value
Type POINTAPI
X_Pos As Long
Y_Pos As Long
End Type
' Main routine to dimension variables, retrieve cursor position,
' and display coordinates
Function Get_Cursor_Pos()
' Dimension the variable that will hold the x and y cursor positions
Dim Hold As POINTAPI
' Place the cursor positions in variable Hold
GetCursorPos Hold
' Display the cursor position coordinates
valorX = Hold.X_Pos ' \ 15 ' Transform to twips
valorY = Hold.Y_Pos ' \ 15 ' Transform to twips
End Function
And finally I create an object with the defalt arguments of the MouseEvent to the the incremental value of the X and calculate the necessary offset to use:
Private Sub calibracao_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim aux As Integer
aux = Get_Cursor_Pos()
offset = valorX - (X \ 15) ' To Twips
Forms!Formulário4!mouse1.Caption = X ' Twips
Forms!Formulário4.Mouse2.Caption = (valorX - offset) * 15 ' - offset
End Sub
This was the final result:
Gannt Chart
After I drag the manut
Note: I can not make to the file available, because there are confidentially informations.
Thanks for everone that have read and probabily think about a solution, excuse me for some English mistakes.

vb Image grid detection

Im looking for a way to detect the center of grid squares when VB.net is feed an image
I want to start with an image of a grid with blue squares like this:
Grid
and i want the program to make an array of points in the center of each square like this (points aren't centered in picture)
Grid with Red points
i dont want to modify the image, i just want to get the points. I've tried getpixel for x and y but that just returns the same point
Dim search_color As Color = Color.FromArgb(255, 64, 128, 192)
Dim background_color As Color = Color.FromArgb(255, 240, 240, 240)
Dim grid_color As Color = Color.FromArgb(255, 144, 144, 144)
Dim pix As Color
Dim liney = 0, linex = 0
Dim loc, sloc, gloc As Point
For ch As Integer = 1 To 64
For y As Integer = liney To Bmp.Height - 1
For x As Integer = linex To Bmp.Width - 1
If Bmp.GetPixel(x, y) = search_color Then
sloc = New Point(x, y)
linex = x
liney = y
x = Bmp.Width - 1
y = Bmp.Height - 1
End If
Next
Next
Dim xloc = 0
For x As Integer = sloc.X To Bmp.Width - 1
If Bmp.GetPixel(x, sloc.Y) = grid_color Then
xloc = x - 1
End If
If Bmp.GetPixel(x, sloc.Y) = background_color Then
xloc = x - 1
End If
Next
For y As Integer = sloc.Y To Bmp.Height - 1
If Bmp.GetPixel(xloc, y) = grid_color Or Bmp.GetPixel(xloc, y) = background_color Then
gloc = New Point(xloc, y - 1)
End If
Next
loc = New Point((gloc.X + sloc.X) / 2, (gloc.Y + sloc.Y) / 2)
liney = gloc.Y
linex = gloc.X + 20
ListBox1.Items.Add(loc.ToString)
Next
Try this:
I added the following controls to a form to test the code:
pbImageToScan (PictureBox) - btnAnalyzeIMG (Button) - lbResult (ListBox)
Public Class Form1
Dim arrCenters() As Point
Dim bmpToAnalyze As Bitmap
Dim search_color As Color = Color.FromArgb(255, 64, 128, 192)
Dim background_color As Color = Color.FromArgb(255, 240, 240, 240)
Dim grid_color As Color = Color.FromArgb(255, 144, 144, 144)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
bmpToAnalyze = New Bitmap(Application.StartupPath & "\Image.bmp")
pbImageToScan.Image = Image.FromFile(Application.StartupPath & "\Image.bmp")
End Sub
Private Sub btnAnalyzeIMG_Click(sender As Object, e As EventArgs) Handles btnAnalyzeIMG.Click
FindCenters()
End Sub
Private Sub FindCenters()
bmpToAnalyze = New Bitmap(Application.StartupPath & "\Image.bmp")
pbImageToScan.Image = Image.FromFile(Application.StartupPath & "\Image.bmp")
'arrCenters is the array who will contains all centers data
ReDim arrCenters(0)
'arrCenters already starts with an element; this boolean is used to handle the first point insertion
Dim bFirstElementAddedToArray As Boolean
lbResult.Items.Clear()
Dim iIMGWidth As Integer = bmpToAnalyze.Width
Dim iIMGHeight As Integer = bmpToAnalyze.Height
'X, Y coordinates used for iterations
Dim iX As Integer = 0
Dim iY As Integer = 0
'Bitmap limits reached
Dim bExit As Boolean
'Used to skip a great part of Ys, if a match has been found along the current examinated line
Dim iDeltaYMax As Integer = 0
'Main cycle
Do While Not bExit
Dim colCurrentColor As Color = bmpToAnalyze.GetPixel(iX, iY)
If colCurrentColor = search_color Then
Dim iXStart As Integer = iX
Dim iYStart As Integer = iY
Dim iXEnd As Integer
Dim iYEnd As Integer
'Width of the Blue square
For iXEnd = iX + 1 To iIMGWidth - 1
Dim colColorSearchX As Color = bmpToAnalyze.GetPixel(iXEnd, iY)
If (colColorSearchX = background_color) Or (colColorSearchX = grid_color) Then
iXEnd -= 1
Exit For
End If
Next
'Height of the Blue square
For iYEnd = iY + 1 To iIMGHeight - 1
Dim colColorSearchY As Color = bmpToAnalyze.GetPixel(iXEnd, iYEnd)
If (colColorSearchY = background_color) Or (colColorSearchY = grid_color) Then
iYEnd -= 1
Exit For
End If
Next
iDeltaYMax = iYEnd - iYStart
'Blue square center coordinates
Dim pCenter As New Point((iXStart + iXEnd) / 2, (iYStart + iYEnd) / 2)
Dim iArrLenght As Integer = 0
If Not bFirstElementAddedToArray Then
bFirstElementAddedToArray = True
Else
iArrLenght = arrCenters.GetLength(0)
ReDim Preserve arrCenters(iArrLenght)
End If
arrCenters(iArrLenght) = pCenter
lbResult.Items.Add(pCenter.ToString)
iX = iXEnd
'Checks if the Width limit of the bitmap has been reached
If iX = (iIMGWidth - 1) Then
iX = 0
iY += iDeltaYMax + 1
iDeltaYMax = 0
Else
iX += 1
End If
Else
'Checks if the Width limit of the bitmap has been reached
If iX = (iIMGWidth - 1) Then
iX = 0
iY += iDeltaYMax + 1
iDeltaYMax = 0
Else
iX += 1
End If
End If
'Width and Height limit of the bitmap have been reached
If (iX = iIMGWidth - 1) And (iY = iIMGHeight - 1) Then
bExit = True
End If
Loop
'Draws a Red point on every center found
For Each P As Point In arrCenters
bmpToAnalyze.SetPixel(P.X, P.Y, Color.Red)
Next
pbImageToScan.Image = bmpToAnalyze
End Sub
End Class

VBA Split array

I have the following code:
Sub UpdateBlock()
'Define empty variables for each attribute
Dim ent As AcadEntity
Dim oBkRef As AcadBlockReference
Dim Insertpoints As Variant
Dim A As Double
Dim tag As String
Dim material As String
Dim actualLength As String
Dim cutOff As Double
Dim cutLengths As Double
Dim totalLengths As Double
Dim weight As Double
Dim purchaseLength As Double
Dim decimalLength As Double
Dim lengthWeight As Double
Dim totalLengthWeight As Double
Dim cutLengthWeight As Double
Dim cutWeight As Double
Dim order As Double
Dim feet As Double
Dim inches As Double
Dim fraction As Double
Dim fracVal As Variant
'First we go over every object in the modelspace
For Each ent In ThisDrawing.ModelSpace
'Check if the object is a block
If ent.ObjectName = "AcDbBlockReference" Then
Set oBkRef = ent
'If the object is a block then check if its the block we are looking for
If oBkRef.EffectiveName = "AUTOTAG-MATERIAL" Then
A = A + 1
'Get Current Attributes
attlist = oBkRef.GetAttributes
For i = LBound(attlist) To UBound(attlist)
Select Case attlist(i).TagString
Case "ACTUAL-LENGTH"
actualLength = attlist(i).TextString
Case "PURCHASE-LENGTH"
purchaseLength = attlist(i).TextString
Case "CUT-OFF"
cutOff = Frac2Num(attlist(i).TextString)
Case "DECIMAL-LENGTH"
feet = Split(actualLength)(0)
inches = Split(actualLength)(1)
fracVal = Split(actualLength)(2)
If Not IsNull(Split(actualLength)(2)) Then
fraction = Frac2Num(fracVal)
Else
fraction = 0
End If
decimalLength = Round((((feet * 12) + (inches + fraction)) / 12) - cutOff, 2)
attlist(i).TextString = decimalLength
Case "WEIGHT"
weight = attlist(i).TextString
Case "CUT-WEIGHT"
cutWeight = weight * decimalLength
attlist(i).TextString = cutWeight
Case "LENGTH-WEIGHT"
lengthWeight = weight * purchaseLength
attlist(i).TextString = lengthWeight
Case "TOTAL-LENGTHS"
totalLengths = attlist(i).TextString
Case "CUT-LENGTHS"
cutLength = attlist(i).TextString
Case "TOTAL-LENGTH-WEIGHT"
totalLengthWeight = lengthWeight * totalLengths
attlist(i).TextString = totalLengthWeight
Case "CUT-LENGTH-WEIGHT"
totalCutWeight = lengthWeight * cutLength
attlist(i).TextString = totalCutWeight
End Select
Next
End If
End If
Next ent
End Sub
Function Frac2Num(ByVal X As String) As Double
Dim P As Integer, N As Double, Num As Double, Den As Double
X = Trim$(X)
P = InStr(X, "/")
If P = 0 Then
N = Val(X)
Else
Den = Val(Mid$(X, P + 1))
If Den = 0 Then Error 11 ' Divide by zero
X = Trim$(Left$(X, P - 1))
P = InStr(X, " ")
If P = 0 Then
Num = Val(X)
Else
Num = Val(Mid$(X, P + 1))
N = Val(Left$(X, P - 1))
End If
End If
If Den <> 0 Then
N = N + Num / Den
End If
Frac2Num = N
End Function
The variable fraction / fracVal comes from a tag in AutoCAD that is a length, that will always be at least "0 0", but may be "0 0 0" it is a length in feet, inches, and fractional inches. So some possible values could be "8 5", "16 11 11/16", "0 5 3/8" etc.
What I need is a check for when the fraction is not there.
Any suggestions?
I would split the string on the space and see if the ubound of the resulting array is 2. So something like this
If Ubound(Split(thisString, " ")) = 2 then
'fractional part is present
End If
Another option is the Like Operator:
If thisString Like "#* #* #*/#*" Then
# matches any single digit (0–9) and * matches zero or more characters.
but since you split the string anyway, I would store the result of the split in a variable and check the number of items in it with UBound as shown in the other answer.

Excel VBA: Variable as First Row of Selection?

I am creating a function in Excel VBA. I am trying to set a variable equal to the first cell in a selection on the worksheet. Basically the equivalent of something like
x = Worksheets("Data").Range("D2").Offset(i - 1, 0)
y = Worksheets("Data").Range("E2").Offset(i - 1, 0)
z = Worksheets("Data").Range("F2").Offset(i - 1, 0)
except I want "Range("D2")" E2 and F2 to instead refer to the first, second and third cell of whatever I've got highlighted on the sheet, rather than a preset cell.
The specific code I've got is:
Function VarunModel(Table As Range, Optional EndCondition As Integer = 0) As Variant
Dim iNumCols As Integer, iNumRows As Integer
Dim i As Integer
Dim SelectedRange As Range
Set SelectedRange = Selection
iNumCols = Table.Columns.Count
iNumRows = Table.Rows.Count
maturity = Worksheets("KMV-Merton").Range("B2").Value
For i = 1 To iNumRows
equity(i) = SelectedRange.Cells(1).Value
debt(i) = SelectedRange.Cells(2).Value
riskFree(i) = Selection.Cells(3).Value
Next i
Dim equityReturn As Variant: ReDim equityReturn(2 To iNumRows)
Dim sigmaEquity As Double
Dim asset() As Double: ReDim asset(1 To iNumRows)
Dim assetReturn As Variant: ReDim assetReturn(2 To iNumRows)
Dim sigmaAsset As Double, meanAsset As Double
Dim x(1 To 1) As Double, n As Integer, prec As Double, precFlag As Boolean, maxDev As Double
For i = 2 To iNumRows: equityReturn(i) = Log(equity(i) / equity(i - 1)): Next i
sigmaEquity = WorksheetFunction.StDev(equityReturn) * Sqr(260)
sigmaAsset = sigmaEquity * equity(iNumRows) / (equity(iNumRows) + debt(iNumRows))
NextItr: sigmaAssetLast = sigmaAsset
For iptr = 1 To iNumRows
x(1) = equity(iptr) + debt(iptr)
n = 1
prec = 0.00000001
Call NewtonRaphson(n, prec, x, precFlag, maxDev)
asset(iptr) = x(1)
Next iptr
For i = 2 To iNumRows: assetReturn(i) = Log(asset(i) / asset(i - 1)): Next i
sigmaAsset = WorksheetFunction.StDev(assetReturn) * Sqr(260)
meanAsset = WorksheetFunction.Average(assetReturn) * 260
If (Abs(sigmaAssetLast - sigmaAsset) > prec) Then GoTo NextItr
Dim disToDef As Double: disToDef = (Log(asset(iNumRows) / debt(iNumRows)) + (meanAsset - sigmaAsset ^ 2 / 2) * maturity) / (sigmaAsset * Sqr(maturity))
Dim defProb As Double: defProb = WorksheetFunction.NormSDist(-disToDef)
VarunModel = defProb
End Function
Thanks.
Try the below code
Dim SelectedRange As Range
Set SelectedRange = Selection
x = SelectedRange.Cells(1).Value
y = SelectedRange.Cells(2).Value
z = SelectedRange.Cells(3).Value
try this:
Dim Row as integer
Dim Col as Integer
Row = 2
Col = 4 'column "D"
x = Worksheets("Data").cells(row, col).Offset(i - 1, 0)
col = col + 1
y = Worksheets("Data").cells(row, col).Offset(i - 1, 0)
col = col + 1
z = Worksheets("Data").cells(row, col).Offset(i - 1, 0)
See the example below for using the selection on the excel, you can control the column you want by changing the column index. If you select only 1 cell, it will also work:
Sub Solution()
x = Selection.Cells(1, 0) 'By using the zero index on the column, it will get the left cell from the selected one.
y = Selection.Cells(2, 0)
Z = Selection.Cells(3, 0)
End Sub

Excel XY Chart (Scatter plot) Data Label No Overlap

So I've been working on this for the past week. Although it can't do miracles, I can say I've got a pretty good result:
I just wanted to put this code out there for all the poor souls like me that are looking for some kind of vba macro that helps them avoid label overlaps in a scatter plot, because while doing my research on the subject, I wasn't able to find anything helpful.
Const PIXEL_TO_POINT_RATIO As Double = 0.72 '1 Pixel = 72/96*1 Point
Const tStep As Double = 0.1
Const rStep As Double = 0.1
Dim pCount As Integer
Sub ExampleMain()
RearrangeScatterLabels Sheet5
RearrangeScatterLabels Sheet25
End Sub
Sub RearrangeScatterLabels(sht As Worksheet)
Dim plot As Chart
Dim sCollection As SeriesCollection
Dim dLabels() As DataLabel
Dim dPoints() As Point
Dim xArr(), yArr(), stDevX, stDevY As Double
Dim x0, x1, y0, y1 As Double
Dim temp() As Double
Dim theta As Double
Dim r As Double
Dim isOverlapped As Boolean
Dim safetyNet, validEntry, currentPoint As Integer
Set plot = sht.ChartObjects(1).Chart 'XY chart (scatter plot)
Set sCollection = plot.SeriesCollection 'All points and labels
safetyNet = 1
pCount = (sCollection.Count - 1)
ReDim dLabels(1 To 1)
ReDim dPoints(1 To 1)
ReDim xArr(1 To 1)
ReDim yArr(1 To 1)
For pt = 1 To sCollection(1).Points.Count
For i = 1 To pCount
If sCollection(i).Points.Count <> 0 Then
'Dynamically expand the arrays
validEntry = validEntry + 1
If validEntry <> 1 Then
ReDim Preserve dLabels(1 To UBound(dLabels) + 1)
ReDim Preserve dPoints(1 To UBound(dPoints) + 1)
ReDim Preserve xArr(1 To UBound(xArr) + 1)
ReDim Preserve yArr(1 To UBound(yArr) + 1)
End If
Set dLabels(i) = sCollection(i).Points(pt).DataLabel 'Store all label objects
Set dPoints(i) = sCollection(i).Points(pt) 'Store all point objects
temp = getElementDimensions(, dPoints(i))
xArr(i) = temp(0) 'Store all points x values
yArr(i) = temp(2) 'Store all points y values
End If
Next
Next
If UBound(dLabels) < 2 Then Exit Sub
pCount = UBound(dLabels)
stDevX = Application.WorksheetFunction.StDev(xArr) 'Get standard deviation for x
stDevY = Application.WorksheetFunction.StDev(yArr) 'Get standard deviation for y
If stDevX = 0 Then stDevX = 1
If stDevY = 0 Then stDevY = 1
r = 0
For currentPoint = 1 To pCount
theta = Rnd * 2 * Application.WorksheetFunction.Pi()
x0 = xArr(currentPoint)
y0 = yArr(currentPoint)
x1 = xArr(currentPoint)
y1 = yArr(currentPoint)
isOverlapped = True
Do Until Not isOverlapped
safetyNet = safetyNet + 1
If safetyNet < 500 Then
If Not checkForOverlap(dLabels(currentPoint), dLabels, dPoints, plot) Then
'No label is within bounds and not overlapping
isOverlapped = False
r = 0
theta = Rnd * 2 * Application.WorksheetFunction.Pi()
safetyNet = 1
Else
'Move label so it does not overlap
theta = theta + tStep
r = r + rStep * tStep / (2 * Application.WorksheetFunction.Pi())
x1 = x0 + stDevX * r * Cos(theta)
y1 = y0 + stDevY * r * Sin(theta)
dLabels(currentPoint).Left = x1
dLabels(currentPoint).Top = y1
End If
Else
safetyNet = 1
Exit Do
End If
Loop
Next
End Sub
Function checkForOverlap(ByRef dLabel As DataLabel, ByRef dLabels() As DataLabel, ByRef dPoints() As Point, ByRef dChart As Chart) As Boolean
checkForOverlap = False 'Return false by default
'Detect label going over chart area
If detectOverlap(dLabel, , , dChart) Then
checkForOverlap = True
Exit Function
End If
'Detect labels overlap
For i = 1 To pCount
If Not dLabel.Left = dLabels(i).Left Then
If detectOverlap(dLabel, dLabels(i)) Then
checkForOverlap = True
Exit Function
End If
End If
Next
'Detect label overlap with point
For i = 1 To pCount
If detectOverlap(dLabel, , dPoints(i)) Then
checkForOverlap = True
Exit Function
End If
Next
End Function
Function getElementDimensions(Optional dLabel As DataLabel, Optional dPoint As Point, Optional dChart As Chart) As Double()
'Get element dimensions and compensate slack
Dim eDimensions(3) As Double
'Working in IV quadrant
If dPoint Is Nothing And dChart Is Nothing Then
'Get label dimensions and compensate padding
eDimensions(0) = dLabel.Left + PIXEL_TO_POINT_RATIO * 3 'Left
eDimensions(1) = dLabel.Left + dLabel.Width - PIXEL_TO_POINT_RATIO * 3 'Right
eDimensions(2) = dLabel.Top + PIXEL_TO_POINT_RATIO * 6 'Top
eDimensions(3) = dLabel.Top + dLabel.Height - PIXEL_TO_POINT_RATIO * 3 'Bottom
End If
If dLabel Is Nothing And dChart Is Nothing Then
'Get point dimensions
eDimensions(0) = dPoint.Left - PIXEL_TO_POINT_RATIO * 5 'Left
eDimensions(1) = dPoint.Left + PIXEL_TO_POINT_RATIO * 5 'Right
eDimensions(2) = dPoint.Top - PIXEL_TO_POINT_RATIO * 5 'Top
eDimensions(3) = dPoint.Top + PIXEL_TO_POINT_RATIO * 5 'Bottom
End If
If dPoint Is Nothing And dLabel Is Nothing Then
'Get chart dimensions
eDimensions(0) = dChart.PlotArea.Left + PIXEL_TO_POINT_RATIO * 22 'Left
eDimensions(1) = dChart.PlotArea.Left + dChart.PlotArea.Width - PIXEL_TO_POINT_RATIO * 22 'Right
eDimensions(2) = dChart.PlotArea.Top - PIXEL_TO_POINT_RATIO * 4 'Top
eDimensions(3) = dChart.PlotArea.Top + dChart.PlotArea.Height - PIXEL_TO_POINT_RATIO * 4 'Bottom
End If
getElementDimensions = eDimensions 'Return dimensions array in Points
End Function
Function detectOverlap(ByVal dLabel1 As DataLabel, Optional ByVal dLabel2 As DataLabel, Optional ByVal dPoint As Point, Optional ByVal dChart As Chart) As Boolean
'Left, Right, Top, Bottom
Dim AxL, AxR, AyT, AyB As Double 'First label coordinates
Dim BxL, BxR, ByT, ByB As Double 'Second label coordinates
Dim eDimensions() As Double 'Element dimensions
eDimensions = getElementDimensions(dLabel1)
AxL = eDimensions(0)
AxR = eDimensions(1)
AyT = eDimensions(2)
AyB = eDimensions(3)
If dPoint Is Nothing And dChart Is Nothing Then
'Compare with another label
eDimensions = getElementDimensions(dLabel2)
End If
If dLabel2 Is Nothing And dChart Is Nothing Then
'Compare with a point
eDimensions = getElementDimensions(, dPoint)
End If
If dPoint Is Nothing And dLabel2 Is Nothing Then
'Compare with chart area
eDimensions = getElementDimensions(, , dChart)
End If
BxL = eDimensions(0)
BxR = eDimensions(1)
ByT = eDimensions(2)
ByB = eDimensions(3)
If dChart Is Nothing Then
detectOverlap = (AxL <= BxR And AxR >= BxL And AyT <= ByB And AyB >= ByT) 'Reverse De Morgan's Law
Else
detectOverlap = Not (AxL >= BxL And AxR <= BxR And AyT >= ByT And AyB <= ByB) 'Is in chart bounds (working in IV quadrant)
End If
End Function
I realize the code is kinda rough and not optimized, but I can't spend more time on this project. I've left quite a few notes around to help read it, should anyone choose to continue this project. Hope this helps.
Best wishes, Schadenfreude.
Building on your function, I made a routine to randomly reposition the labels, assigning a score according to how much overlap it would cause, and thusly optimize. The results aren't great for my own data set, but I think it can be tuned easily for most usages.
There are some issues with the borders and the axis labels which maybe I'll account for later.
Option Explicit
Sub ExampleUsage()
RearrangeScatterLabels ActiveSheet.ChartObjects(1).Chart, 3
End Sub
Sub RearrangeScatterLabels(plot As Chart, Optional timelimit As Double = 5)
Dim sCollection As SeriesCollection
Set sCollection = plot.SeriesCollection
Dim pCount As Integer
pCount = sCollection(1).Points.Count
If pCount < 2 Then Exit Sub
Dim dPoints() As Point
Dim xArr() As Double ' Label center position X
Dim yArr() As Double ' Label center position Y
Dim wArr() As Double ' Label width
Dim hArr() As Double ' Label height
Dim pArr() As Double ' Marker position X
Dim qArr() As Double ' Marker position Y
Dim mArr() As Double ' Markersize
ReDim dPoints(1 To pCount)
ReDim xArr(1 To pCount)
ReDim yArr(1 To pCount)
ReDim wArr(1 To pCount)
ReDim hArr(1 To pCount)
ReDim pArr(1 To pCount)
ReDim qArr(1 To pCount)
ReDim mArr(1 To pCount)
Dim theta As Double
Dim i As Integer
Dim j As Integer
Dim dblStart As Double
' Loop through all points to get their handles and coordinates
For i = 1 To pCount
' Store all point objects
Set dPoints(i) = sCollection(1).Points(i)
' Extract their coordinates and size
pArr(i) = dPoints(i).Left
qArr(i) = dPoints(i).Top
mArr(i) = dPoints(i).MarkerSize
' Store the size of the corresponding labels
wArr(i) = dPoints(i).DataLabel.Width
hArr(i) = dPoints(i).DataLabel.Height
' Starting position (center of label) is middle below
xArr(i) = pArr(i)
yArr(i) = qArr(i) + mArr(i)
Next
Dim newX As Double
Dim newY As Double
Dim dE As Double
Dim wgtOverlap As Double
Dim wgtDistance As Double
Dim wgtClose As Double
wgtOverlap = 10000 ' Extra penalty for overlapping
wgtDistance = 10000 ' Penalty for being nearby other labels
wgtClose = 10 ' Penalty for being further from marker
' Limit the function by time
dblStart = Timer
Do Until TimerDiff(dblStart, Timer) > timelimit
' Pick a random label to move around
i = Int(Rnd * pCount + 1)
' Pick a new random position by angle
theta = Rnd * 2 * Application.WorksheetFunction.Pi()
' Determine the position it would shift to
If Abs(Sin(theta) * wArr(i)) > Abs(hArr(i) * Cos(theta)) Then
' above or below
If Sin(theta) > 0 Then
' above
newX = pArr(i) + wArr(i) * Cos(theta) / 2
newY = qArr(i) - hArr(i) / 2 - mArr(i) / 2
Else
' below
newX = pArr(i) + wArr(i) * Cos(theta) / 2
newY = qArr(i) + hArr(i) / 2 + mArr(i) / 2
End If
Else
' left or right side
If Cos(theta) < 0 Then
' left
newX = pArr(i) - wArr(i) / 2 - mArr(i) / 2
newY = qArr(i) - hArr(i) * Sin(theta) / 2
Else
' right
newX = pArr(i) + wArr(i) / 2 + mArr(i) / 2
newY = qArr(i) - hArr(i) * Sin(theta) / 2
End If
End If
' Determine increase in energy caused by this shift
dE = 0
For j = 1 To pCount
If i <> j Then
' Current overlap with labels
If 2 * Abs(xArr(i) - xArr(j)) < wArr(i) + wArr(j) _
And 2 * Abs(yArr(i) - yArr(j)) < hArr(i) + hArr(j) Then
dE = dE - Abs(xArr(i) - xArr(j) + (wArr(i) + wArr(j)) / 2) _
* Abs(yArr(i) - yArr(j) + (hArr(i) + hArr(j)) / 2)
dE = dE - wgtOverlap
End If
' New overlap with labels
If 2 * Abs(newX - xArr(j)) < wArr(i) + wArr(j) _
And 2 * Abs(newY - yArr(j)) < hArr(i) + hArr(j) Then
dE = dE + Abs(newX - xArr(j) + (wArr(i) + wArr(j)) / 2) _
* Abs(newY - yArr(j) + (hArr(i) + hArr(j)) / 2)
dE = dE + wgtOverlap
End If
' Current overlap with labels
If Abs(xArr(i) - pArr(j)) < wArr(i) / 2 + mArr(j) _
And Abs(yArr(i) - qArr(j)) < hArr(i) / 2 + mArr(j) Then
dE = dE - wgtOverlap
End If
' New overlap with points
If Abs(newX - pArr(j)) < wArr(i) / 2 + mArr(j) _
And Abs(newY - qArr(j)) < hArr(i) / 2 + mArr(j) Then
dE = dE + wgtOverlap
End If
' We like the neighbours to be far away
dE = dE - wgtDistance / ((xArr(i) - xArr(j)) ^ 2 + (yArr(i) - yArr(j)) ^ 2)
dE = dE + wgtDistance / ((newX - xArr(j)) ^ 2 + (newY - yArr(j)) ^ 2)
End If
' We like the offsets to be low
dE = dE - wgtClose * (Abs(xArr(i) - pArr(i)) + Abs(yArr(i) - qArr(i)))
dE = dE + wgtClose * (Abs(newX - pArr(i)) + Abs(newY - qArr(i)))
Next
' If it didn't get worse, adjust to new position
If dE <= 0 Then
xArr(i) = newX
yArr(i) = newY
End If
Loop
' Actually adjust the labels
For i = 1 To pCount
dPoints(i).DataLabel.Left = xArr(i) - wArr(i) / 2
dPoints(i).DataLabel.Top = yArr(i) - hArr(i) / 2
Next
End Sub
' Timer function from Peter Albert
' http://stackoverflow.com/questions/15634623
Function TimerDiff(dblTimerStart As Double, dblTimerEnd As Double)
Dim dblTemp As Double
dblTemp = dblTimerEnd - dblTimerStart
If dblTemp < -43200 Then
dblTemp = dblTemp + 86400
End If
TimerDiff = dblTemp
End Function