Change Number of Categories between Tick-Mark Labels on Chart X-axis - vba

If I have more (or less) data in my Access 2010 graph, I want to decrease (or increase) the number of tick marks on the X-axis. I looked for the right syntax but I couldn't find it.
I did the same for the Y-axis Title and that works fine:
me.Graph1.Object.Axes(2,1).AxisTitles.Caption="g/ml"
For number of tick-marks of the X-axis; I've tried a number of words can not find the right one. It stopped at:
me.Graph.Object.Axes(1,1).Scale.???????? (I want a number of categories = 3 )

Example code from my db for XYScatter chart. xlCategory is for X-axis and xlValue is Y-axis.
this procedure is in a standard module and called by a form and a report
Sub FormatProcGraph(strObject As String, strLabNum As String, booMetric As Boolean, dblOMC, dblMDD)
'format Proctor graph on form and report
Dim obj As Object
Dim gc As Object
Dim intMaxD As Integer
Dim intMinD As Integer
Dim intM As Integer
If strObject Like "Lab*" Then
Set obj = Reports(strObject)
Else
Set obj = Forms(strObject).Controls("ctrProctor").Form
End If
Set gc = obj("gphDensity")
intMaxD = Nz(Int(dblMDD), 0)
intMinD = Nz(Int(DMin("D", "GraphProctor", "Source='Lab' AND LabNum='" & strLabNum & "'")), 0)
With gc
'format y axis scale
If booMetric = True Then
intMaxD = intMaxD + IIf(intMaxD - intMinD < 125, 50, IIf(intMaxD - intMinD < 250, 25, 0))
.Axes(xlValue).MaximumScale = intMaxD
.Axes(xlValue).MinimumScale = intMaxD - 250
.Axes(xlValue).MajorUnit = 50
.Axes(xlValue).MinorUnit = 10
Else
intMaxD = intMaxD + IIf(intMaxD - intMinD < 6, 2, IIf(intMaxD - intMinD < 10, 1, 0))
.Axes(xlValue).MaximumScale = intMaxD
.Axes(xlValue).MinimumScale = intMaxD - 10
.Axes(xlValue).MajorUnit = 2
.Axes(xlValue).MinorUnit = 0.4
End If
'format x axis scale
If Int(dblOMC) > 6 Then
intM = Int(dblOMC) + IIf(dblOMC - Int(dblOMC) >= 0.5, 1, 0)
.Axes(xlCategory).MaximumScale = intM + 7
.Axes(xlCategory).MinimumScale = intM - 5
End If
'y axis label
.Axes(xlValue, xlPrimary).HasTitle = True
If booMetric = True Then
.Axes(xlValue, xlPrimary).AxisTitle.Text = "Dry Density, kg/cu.m"
End If
End With
End Sub
this procedure is behind report
Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
'format graphs
Dim MinUWT As Double, MaxUWT As Double
Dim MinDry As Double, MaxDry As Double
Dim MinSoak As Double, MaxSoak As Double
Dim MinRat As Double, MaxRat As Double
With Me
If Not IsNull(!MinOfA) Then
MinUWT = Int(!MinOfU) - IIf(!MaxOfU - !MinOfU <= 2, 3, 1)
MaxUWT = MinUWT + 8
MinDry = Int(!MinOfD / 5) * 5 - (40 - Int((Int(!MaxOfD / 5) * 5 + 5 - Int(!MinOfD / 5) * 5) / 10) * 10) / 2
MaxDry = MinDry + 40
MinSoak = Int(!MinOfS / 5) * 5 - (40 - Int((Int(!MaxOfS / 5) * 5 + 5 - Int(!MinOfS / 5) * 5) / 10) * 10) / 2
MaxSoak = MinSoak + 40
MinRat = Int(!MinOfR / 5) * 5 - (40 - Int((Int(!MaxOfR / 5) * 5 + 5 - Int(!MinOfR / 5) * 5) / 10) * 10) / 2
MaxRat = MinRat + 40
.gphWeight.Axes(xlValue).MinimumScale = MinUWT
.gphWeight.Axes(xlValue).MaximumScale = MaxUWT
.gphITSdry.Axes(xlValue).MinimumScale = MinDry
.gphITSdry.Axes(xlValue).MaximumScale = MaxDry
.gphITSsoak.Axes(xlValue).MinimumScale = MinSoak
.gphITSsoak.Axes(xlValue).MaximumScale = MaxSoak
.gphITSret.Axes(xlValue).MinimumScale = MinRat
.gphITSret.Axes(xlValue).MaximumScale = MaxRat
If Me!Metric = True Then
.gphWeight.Axes(xlValue, xlPrimary).AxisTitle.Text = "Unit Weight, kg/cu.cm"
.gphGradation.Axes(xlCategory, xlPrimary).AxisTitle.Text = "Sieve Size (mm)"
.gphITSdry.Axes(xlValue, xlPrimary).AxisTitle.Text = "ITS Dry, kg/cu.cm"
.gphITSsoak.Axes(xlValue, xlPrimary).AxisTitle.Text = "ITS Soaked, kg/cu.cm"
End If
End If
End With
End Sub

Related

Graphics drawline scaling 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

Arcs Around a Circle

I am trying to position an arc around a circle to show in which range our customers perform.
The sub builds an arc which has the same size as the one from the inner circle but I can't position it correctly.
I attached a picture below to demonstrate my problem.
firstang = shp.Chart.ChartGroups(1).FirstSliceAngle
radius = shp.Chart.PlotArea.Height / 2
Pi = 3.14159265358979
z = 1
j = 1
Debug.Print "Charttype: " & shp.Chart.ChartType
gradfaktor = (360 / Pi)
Breite = shp.Chart.PlotArea.Width + 2 * Abstand + 2 * Balkendicke
breitekreissegment = Balkendicke / Breite * 2
For z = 1 To shp.Chart.SeriesCollection(1).Points.Count
Set newshp = sld.Shapes.AddShape(msoShapeBlockArc, 10, 10, Breite, Breite)
x1 = shp.Chart.SeriesCollection(1).Points(z).PieSliceLocation(xlHorizontalCoordinate, xlOuterClockwisePoint)
y1 = shp.Chart.SeriesCollection(1).Points(z).PieSliceLocation(xlVerticalCoordinate, xlOuterClockwisePoint)
x2 = shp.Chart.SeriesCollection(1).Points(z).PieSliceLocation(xlHorizontalCoordinate, xlOuterCounterClockwisePoint)
y2 = shp.Chart.SeriesCollection(1).Points(z).PieSliceLocation(xlVerticalCoordinate, xlOuterCounterClockwisePoint)
newshp.Fill.ForeColor.RGB = farbe
newshp.Line.Transparency = 1
newshp.name = "B1_" & 1
DoEvents
'newshp.Height = shp.Height
DoEvents
newshp.Left = shp.Left + shp.Chart.PlotArea.Left * 0.5 - Balkendicke
newshp.Top = shp.Top + shp.Chart.PlotArea.Top * 0.5 - Balkendicke
newshp.Adjustments.Item(3) = breitekreissegment
l1 = ((x2 - x1) ^ 2 + (y2 - y1) ^ 2) ^ 0.5
alpha1 = (2 * ArcSin((l1 / (2 * radius)))) * 180 / Pi
newshp.Adjustments.Item(1) = alpha1
newshp.Adjustments.Item(2) = firstang
DoEvents
firstang = firstang + alpha1 + WinkelAbstand
Next z
chartcount = chartcount + 1
l1 = ((x2 - x1) ^ 2 + (y2 - y1) ^ 2) ^ 0.5
alpha1 = (2 * ArcSin((l1 / (2 * radius)))) * 180 / Pi
You can do something like this, just make to separate graphs.
Sub test()
Set myCht_01 = ActiveSheet.Shapes.AddChart
Set myCht_02 = ActiveSheet.Shapes.AddChart
With myCht_01
.Chart.ChartType = xlDoughnut
.Chart.SetSourceData Source:=Range("$F$3:$F$4")
.Chart.ChartGroups(1).DoughnutHoleSize = 85
.Chart.Legend.Delete
.Chart.ChartGroups(1).FirstSliceAngle = 180
Set serCol_01 = .Chart.SeriesCollection(1)
With serCol_01
.ApplyDataLabels
For Each lbl In .DataLabels
If lbl.Name = "Text S1P1" Then lbl.Text = "Nein"
If lbl.Name = "Text S1P2" Then lbl.Text = "Ja"
Next lbl
.DataLabels.ShowCategoryName = True
End With
End With
With myCht_02
.Chart.ChartType = xlDoughnut
.Chart.SetSourceData Source:=Range("$E$3:$E$4")
.Line.Visible = msoFalse
.Chart.Legend.Delete
.Chart.SeriesCollection(1).ApplyDataLabels
.Chart.ChartGroups(1).FirstSliceAngle = 270
End With
myCht_02.ScaleWidth 0.75, msoFalse, msoScaleFromMiddle
myCht_02.ScaleHeight 0.75, msoFalse, msoScaleFromMiddle
myCht_02.Fill.Visible = msoFalse
Set shpGroup = ActiveSheet.Shapes.Range(Array(myCht_01.Name, myCht_02.Name)).Group
Exit Sub
shpGroup.Delete
End Sub
Link to example graph

Snakes and ladders Vb.net [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
Form 2 is to enter ladder base and its off set value and for snakes where the snake head is and skakes off set value.
Not able to figure out why its not working . When the values are entered to show simulation it show's up error sandl is private and the other one is the validation one .
Public Class Form2
Dim sandl(99) As Integer
Dim snakeshead As TextBox()
Dim snakesoffset As TextBox()
Dim ladderfoot As TextBox()
Dim ladderoffset As TextBox()
Dim rnd As Random = New Random
Sub initialise()
For i = 0 To 99
sandl(i) = 0 ' reset data
Next
End Sub
Sub snake()
snakeshead = {txthead1, txthead2, txthead3, txthead4, txthead5, txthead6, txthead7, txthead8, txthead9, txthead10}
snakesoffset = {txtoffset1, txtoffset2, txtoffset3, txtoffset4, txtoffset5, txtoffset6, txtoffset7, txtoffset8, txtoffset9, txtoffset10}
' SnakeHead(i).Text = (i + 81).ToString
' SnakeOffset(i).Text = "10" '(i + 10).ToString
For i As Integer = 0 To 9
While True
Dim base = rnd.Next(90) + 11
If sandl(base - 1) <> 0 Then
Continue While
End If
Dim offset = rnd.Next(20) + 10
If base - offset < 1 Then
Continue While
End If
snakeshead(i).Text = base.ToString
snakesoffset(i).Text = offset.ToString
sandl(base - 1) = -offset
Exit While
End While
Next
End Sub
Sub ladders()
ladderfoot = {txtladder1, txtladder2, txtladder3, txtladder4, txtladder5, txtladder6, txtladder7, txtladder8, txtladder9, txtladder10}
ladderoffset = {txtladderoffset1, txtladderoffset2, txtladderoffset3, txtladderoffset4, txtladderoffset5, txtladderoffset6, txtladderoffset7, txtladderoffset8, txtladderoffset9, txtladderoffset10}
'For i As Integer = 0 To 9
' LadderFoot(i).Text = (i + 11).ToString
' LadderOffset(i).Text = "10"
For i As Integer = 0 To 99
sandl(i) = 0 'reset data
Next
For i As Integer = 0 To 9
While True
Dim base = rnd.Next(90) + 1
If sandl(base - 1) <> 0 Then
Continue While
End If
Dim offset = rnd.Next(20) + 10
If base + offset > 100 Then
Continue While
End If
ladderfoot(i).Text = base.ToString
ladderoffset(i).Text = offset.ToString
sandl(base - 1) = offset
Exit While
End While
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For i As Integer = 0 To 99
sandl(i) = 0 'reset data
Next
Dim valid = Validate(ladderfoot, ladderoffset, +1, "Ladder")
If (valid) Then
valid = Validate(snakeshead, snakesoffset, -1, "Snake")
End If
If (valid) Then
'Form3 = New Form3
Form3.ShowDialog()
End If
End Sub
Private Function Validate(tbBase() As TextBox, tbOffset() As TextBox, delta As Integer, s As String) As Boolean
For i As Integer = 0 To 9
Dim base As Integer
If ((Not Integer.TryParse(tbBase(i).Text.Trim(), base)) OrElse (base < 1) OrElse (base > 100) OrElse (sandl(base - 1) <> 0)) Then
MessageBox.Show(s & (i + 1).ToString() & " base is invalid.")
tbBase(i).Select()
tbBase(i).SelectAll()
Return False
End If
base -= 1 'zero based
Dim offset As Integer
If ((Not Integer.TryParse(tbOffset(i).Text.Trim(), offset)) OrElse (offset < 10) OrElse (offset > 30) OrElse (base + offset * delta < 0) OrElse (base + offset * delta >= 100)) Then
MessageBox.Show(s & (i + 1).ToString() & " offset is invalid.")
tbOffset(i).Select()
tbOffset(i).SelectAll()
Return False
End If
sandl(base) = offset * delta 'write offset
Next
Return True
End Function
End Class
Public Class Form3
Enum EState
Dice
Move
Slide
Wait
Win
End Enum
Dim Fnt = New Font("Arial", 16)
Dim FntBig = New Font("Arial", 256)
Dim Frame As Integer = -1 'counter
Dim State = EState.Dice
Dim Rnd As Random = New Random
Dim Dice As Integer
Dim Pos As Point = New Point(32, 640 + 32)
Dim CurrentIndex As Integer = -1
Dim NextIndex As Integer
Dim TargetIndex As Integer
Private Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dice = 0
Frame = -1
State = EState.Dice
Pos = New Point(32, 640 + 32)
CurrentIndex = -1
End Sub
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
DrawBackground(e.Graphics)
Frame += 1
Dim oldState = State
Select Case State
Case EState.Dice
If Frame = 0 Then
Dice = Rnd.Next(6) + 1 'roll dice
TargetIndex = CurrentIndex + Dice
NextIndex = CurrentIndex
ElseIf Frame >= 63 Then
If CurrentIndex + Dice < 100 Then
State = EState.Move 'valid dice
Else
State = EState.Wait 'invalid dice
End If
Dice = 0
End If
Case EState.Move
If Frame Mod 64 = 0 Then
CurrentIndex = NextIndex
If CurrentIndex = TargetIndex Then
If CurrentIndex < 99 Then 'not win
If Form2.sandl(CurrentIndex) <> 0 Then
State = EState.Slide 'snake or ladder
Else
State = EState.Dice 'empty tile
End If
TargetIndex = CurrentIndex + Form2.sandl(CurrentIndex)
Else
State = EState.Win 'win
End If
Else
NextIndex = CurrentIndex + 1 'move
End If
Else
Dim c = GetCoordinate(CurrentIndex)
Dim n = GetCoordinate(NextIndex)
Dim dx = (n.X - c.X)
Dim dy = (n.Y - c.Y)
Pos.X = c.X * 64 + (dx * (Frame Mod 64)) + 32
Pos.Y = c.Y * 64 + (dy * (Frame Mod 64)) + 32
End If
Case EState.Slide
If Frame >= 63 Then
CurrentIndex = TargetIndex
If CurrentIndex < 99 Then
State = EState.Dice 'not win
Else
State = EState.Win 'win
End If
Else
Dim c = GetCoordinate(CurrentIndex)
Dim n = GetCoordinate(TargetIndex)
Dim dx = (n.X - c.X)
Dim dy = (n.Y - c.Y)
Pos.X = c.X * 64 + (dx * (Frame Mod 64)) + 32
Pos.Y = c.Y * 64 + (dy * (Frame Mod 64)) + 32
End If
Case EState.Wait
If Frame >= 63 Then
State = EState.Dice
End If
End Select
e.Graphics.FillEllipse(Brushes.Blue, Pos.X - 16, Pos.Y - 16, 32, 32) 'draw player
If Dice > 0 Then
Dim size = e.Graphics.MeasureString(Dice.ToString, FntBig)
e.Graphics.DrawString(Dice.ToString, FntBig, Brushes.Black, 320 - size.Width / 2, 320 - size.Height / 2) 'print dice
End If
If State <> oldState Then
Frame = -1 'reset counter
End If
If State <> EState.Win Then
PictureBox1.Invalidate() 'schedule next paint
End If
End Sub
Private Sub DrawBackground(g As Graphics)
For y As Integer = 0 To 9
For x As Integer = 0 To 9
If (((x + y) Mod 2) = 0) Then
g.FillRectangle(Brushes.LightGray, x * 64, y * 64, 64, 64) 'dark rectangle
End If
Dim z = (9 - y) * 10 + x + 1
If y Mod 2 = 0 Then
z = (9 - y) * 10 + (9 - x) + 1
End If
g.DrawString(z.ToString, Fnt, Brushes.Black, x * 64, y * 64) 'number
Next
Next
For i As Integer = 0 To 99
If Form2.sandl(i) <> 0 Then
Dim base = GetCoordinate(i)
Dim offset = GetCoordinate(i + Form2.sandl(i))
If Form2.sandl(i) > 0 Then 'ladder
Dim delta = Math.Abs(base.X - offset.X) + 4
g.DrawLine(Pens.Green, base.X * 64 + 32 - delta, base.Y * 64 + 32, offset.X * 64 + 32 - delta, offset.Y * 64 + 32) 'left part
g.DrawLine(Pens.Green, base.X * 64 + 32 + delta, base.Y * 64 + 32, offset.X * 64 + 32 + delta, offset.Y * 64 + 32) 'right part
Else 'snake
g.DrawLine(Pens.Red, base.X * 64 + 32, base.Y * 64 + 32, offset.X * 64 + 32, offset.Y * 64 + 32) 'red line
End If
End If
Next
End Sub
Private Function GetCoordinate(i As Integer) As Point
Dim result As Point
result.Y = 9 - (i \ 10)
result.X = i Mod 10
If result.Y Mod 2 = 0 Then
result.X = 9 - result.X
End If
Return result
End Function
End Class
In Form2, change your declaration from
Dim sandl(99) As Integer
to
Public sandl(99) As Integer
This would allow Form3 to access your integer array
Rename your Validate method to something else, like ValidateTextBoxes, or if you intend to overload the base.Validate, then declare as
Private Overloads Function Validate

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

Simulation runs fine # 10k cycles, but gets error 13 (type mismatch) # 100k cycles

First off, here's my code:
Sub SimulatePortfolio()
Dim lambda As Double
Dim num As Integer
Dim cycles As Long
Column = 12
q = 1.5
lambda = 0.05
cycles = 100000
Dim data(1 To 100000, 1 To 10) As Integer
Dim values(1 To 10) As Double
For i = 1 To 10
values(i) = 0
Next i
temp = lambda
For i = 1 To cycles
lambda = temp
num = 10
t = 0
Dim temps(1 To 10) As Integer
For k = 1 To 10
temps(k) = 1000
Next k
Do While (t < 10 And num > 0)
t = t + tsim(lambda, num)
For j = 1 To 10
If (j > t) Then
temps(j) = temps(j) - 50
End If
Next j
num = num - 1
If (num <= 0) Then
Exit Do
End If
lambda = lambda * q
Loop
For l = 1 To 10
values(l) = values(l) + temps(l)
data(i, l) = temps(l)
Next l
Next i
For i = 1 To 10
Cells(i + 1, Column) = values(i) / cycles
'Problem occurs on this line:
Cells(i + 1, Column + 1).Value = Application.WorksheetFunction.Var(Application.WorksheetFunction.Index(data, i, 0))
Next i
End Sub
Function tsim(lambda As Double, num As Integer) As Double
Dim v As Double
Dim min As Double
Randomize
min = (-1 / lambda) * Log(Rnd)
For i = 1 To (num - 1)
Randomize
v = (-1 / lambda) * Log(Rnd)
If (min > v) Then
min = v
End If
Next i
tsim = min
End Function
When I set the value for cycles to 10000, it runs fine without a hitch. When I go to 100000 cycles, it gets an Error 13 at the indicated line of code.
Having been aware that Application.Tranpose is limited to 65536 rows with variants (throwing the same error) I tested the same issue with Index
It appears that Application.WorksheetFunction.Index also has a limit of 65536 rows when working with variants - but standard ranges are fine
So you will need to either need to dump data to a range and work on the range with Index, or work with two arrays
Sub Test()
Dim Y
Dim Z
'works in xl07/10
Debug.Print Application.WorksheetFunction.Index(Range("A1:A100000"), 1, 1)
Y = Range("A1:A65536")
`works
Debug.Print Application.WorksheetFunction.Index(Y, 1, 1)
'fails in xl07/10
Z = Range("A1:A65537")
Debug.Print Application.WorksheetFunction.Index(Z, 1, 1)
End Sub