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
Related
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
So I have a "main" function (SolveSixODES) that calls a secondary function (AllODEs). And when it does this, the x value in the main function gets modified. I don't understand how this can be possible, seeing as it is not a global variable.
Here is the code, my inputs I used are as follows:
x=0, xmax=3, y=0-6, h=0.1, error=0.1
Public Function SolveSixODE(x As Double, xmax As Double, Y As Range, h As Double, error As Double) 'Weird bug: You must leave the first y4 value blank
Dim i As Integer, k(7, 7) As Double, j As Integer, m As Integer 'k(Order #, equation #)
Dim Y5(7) As Double, Y4(7) As Double, Y4Old(7) As Double
Dim delta0(7) As Double, delta1(7) As Double, delRatio(7) As Double, Rmin As Double
For i = 1 To 6 'Moving the input data so it can acutally be used
Y4(i) = Y(i)
Next i
While x < xmax
If x + h < xmax Then
x = x + h
Else
h = xmax - x
x = xmax
End If
For j = 1 To 6 'j is the order i is equation number
For i = 1 To 6 'Calculating all of the k(1) values for eq 1 to 6
k(j, i) = AllODES(x, Y4, i, j, k, h) '!!!!!SOME HOW THIS LOOP MAKES X negative...!!!!!!!
Next i
Next j
For i = 1 To 6
Y4Old(i) = Y4(i) 'Saving old y4 value to calc delta0
Y4(i) = Y4(i) + h * (k(1, i) * (37 / 378) + k(3, i) * (250 / 621) + k(4, i) * (125 / 594) + k(6, i) * (512 / 1771))
Y5(i) = Y4(i) + h * (k(1, i) * (2825 / 27648) + k(3, i) * (18575 / 48384) + k(4, i) * (13525 / 55296) + k(5, i) * (277 / 14336) + k(6, i) * (0.25))
delta0(i) = error * (Abs(Y4Old(i)) + Abs(h * AllODES(x, Y4Old, i, 1, k, h))) 'First order because we don't want to use the k vals
delta1(i) = Abs(Y5(i) - Y4(i))
delRatio(i) = Abs(delta0(i) / delta1(i)) 'Ratio of errors
Next i
Rmin = delRatio(1)
For i = 2 To 6
If delRatio(i) < Rmin Then
Rmin = delRatio(i) 'Determine the smallest error ratio
End If
Next i
If Rmin < 1 Then 'If this is true then the step size was too big must repeat step
x = x - h 'Set x and y's back to previous values
For i = 1 To 6
Y4(i) = Y4Old(i)
Next i
h = 0.9 * h * Rmin ^ 0.25 'adjust h value; 0.9 is a safety factor
Else
h = 0.9 * h * Rmin ^ 0.2 'Otherwise, we march on
End If
m = m + 1
Wend
SolveSixODE = Y4
End Function
Public Function AllODES(x As Double, Y() As Double, EqNumber As Integer, order As Integer, k() As Double, h As Double) As Double
Dim conc(7) As Double, i As Integer, j As Integer
If order = 1 Then
x = x - h
For i = 1 To 6 'Movin the data so I can use it
conc(i) = Y(i) 'also adjusting the x and y values for RK4 (Cash Karp values)
Next i
ElseIf order = 2 Then
x = x - h + h * 0.2
For i = 1 To 6
conc(i) = Y(i) + h * k(1, i) * 0.2
Next i
ElseIf order = 3 Then
x = x - h + 0.3 * h
For i = 1 To 6
conc(i) = Y(i) + h * (0.075 * k(1, i) + 0.225 * k(2, i))
Next i
ElseIf order = 4 Then
x = x - h + 0.6 * h
For i = 1 To 6
conc(i) = Y(i) + h * (0.3 * k(1, i) - 0.9 * k(2, i) + 1.2 * k(3, i))
Next i
ElseIf order = 5 Then
x = x - h + h
For i = 1 To 6
conc(i) = Y(i) + h * ((-11 / 54) * k(1, i) + 2.5 * k(2, i) - (70 / 27) * k(3, i) + (35 / 27) * k(4, i))
Next i
ElseIf order = 6 Then
x = x - h + 0.875 * h
For i = 1 To 6
conc(i) = Y(i) + h * ((1631 / 55296) * k(1, i) + (175 / 512) * k(2, i) + (575 / 13824) * k(3, i) + (44275 / (110592) * k(4, i) + (253 / 4096) * k(5, i)))
Next i
Else
MsgBox ("error")
End If
If EqNumber = 1 Then 'These are the actual equations
AllODES = x + Y(1)
ElseIf EqNumber = 2 Then
AllODES = x
ElseIf EqNumber = 3 Then
AllODES = Y(3)
ElseIf EqNumber = 4 Then
AllODES = 2 * x
ElseIf EqNumber = 5 Then
AllODES = 2 * Y(2)
ElseIf EqNumber = 6 Then
AllODES = 3 * x
Else
MsgBox ("You entered an Eq Number that was dumb")
End If
End Function
It's possible that it is something really trivial that I missed but this seems to contradict my knowledge of how variables work. So if you understand how the function is able to manipulate a variable from another function in this case, I would appreciate any advice and/or explanation!
Thanks in advance!
the x value in the main function gets modified. I don't understand how this can be possible, seeing as it is not a global variable
This is normal because you are passing x by reference to the function AllODES and you do change it there. When the keyword ByVal is not explicitly specified in the function/sub prototype, the default passing mechanism is ByRef, that is, by reference.
Public Function AllODES(x As Double, ...
means
Public Function AllODES(ByRef x As Double, ....
We observe that x is manipulated in this function, so the change will appear in the caller. If you want that the change of x does not report back in the caller's scope, pass x by value:
Public Function AllODES(ByVal x As Double, ....
' ^^^^^
Only in this case the x of the caller and the x of the callee will be two different variables.
First if this a duplicate (that I can't find) I am sorry.
I have been searching for more then a week for an answer to this.
I have an Access 2003 Database that has a Front End (fe) a Back End (be) and a third db that holds the data. I know this seems weird but its a German built Access DB from way back.
Here is the problem. From the main data entry form there is a print button that calls another form. This second form has two text boxes that have a starting and ending order number fields. The starting number is passed from the main form.
There is a another print button on the second form that when clicked creates and prints a report. The report is where the problem is. If I add ANY vba code to the report, even just making a label visible, the report prints nothing. The printer dialog window appears like it is sending something but nothing is ever printed.
I have tried sending to a PDF but nothing. Now if I go to the report code and comment out the line to that makes the label visible the report prints fine. I have been working with DB's along time and I have never seen anything like this, Hell I have never even heard of anything like this.
Be warned that this code has a lot of German in it.
Option Compare Database
Option Explicit
Private Sub Detailbereich_Format(Cancel As Integer, FormatCount As Integer) 'Detail Area Format
'On Error GoTo Err_Detailbereich_Format
Dim dbDatenbank As Database
Dim dtPPSDaten, dtEinfassung, dtToleranz As Recordset
Dim sql As String
Dim T As Double 'Angepasste Tragstablänge
'Adjusted Bearing Bar rod-length
Dim TA As Double 'Anzahl Tragstäbe
'Number of Bearing Bar rods
Dim TA_Anzahl As String 'Zwischenfeld für Tragstabanzahlberechnung ohne Rundung
'Inter-field for Bearing Bar-number-calculation of curve
Dim FA_Anzahl As String 'Zwischenfeld für Füllstabanzahlberechnung ohne Rundung
'Inter-field for Cross Bar-number-calculation of curve
Dim Komma As Byte 'Zwischenfeld zum Filtern der Dezimalzalen
'Inter-field to filtering the decimal zal en
Dim FA As Double 'Füllstabanzahl
'Cross Bar-number
Dim TEM As Double 'Tragstabendmasche
'Bearing Bar Mesh
Dim FEM As Double 'Füllstabendmasche
'Cross Bar Mesh
Dim ZFEM As Double 'Zusatzwert Füllstabendmasche
'Addition-value Cross Bar Mesh
Dim MEMT As Double 'Mindestendmasche Tragstab (Toleranz)
'Bearing Bar Mesh Tolerance
Dim MEMF As Double 'Mindestendmasche Füllstab (Toleranz)
'Cross Bar Mesh Tolerance
Dim Rand1_2 As Double 'Technologischer Abzug Rand1+2 (Toleranz)
'Technological departure edge
Dim Rand3_4 As Double 'Technologischer Abzug Rand3+4 (Toleranz)
'Technological departure edge
Dim Einfassungsdicke As Single '(aus Tabelle: Einfassung)
'Banding Thickness
Dim Einfassungsmaterial As String * 35 '(aus Tabelle: Einfassung)
'Banding Material
Dim Toleranz_Füllstab As Single '(aus Tabelle: Toleranz)
'Cross Bar Tolerance
Dim Toleranz_Tragstab As Single '(aus Tabelle: Toleranz)
'Bearing Bar Tolerance
Dim ProdAuftrag As Long '(aus Tabelle: PPS_Daten_IN)
Dim PositionsNr As String * 3 'Zwischenfeld zum auffüllen der PositionsNr mit Nullen
'Position Number with zeros
Dim Auffüllen As Long 'Zwischenfeld
Dim Barcode_ProdAuftrag As String * 9 'Zwischenfeld
Dim Artikeltyp As Byte 'Zwischenfeld
Dim Durchlauf As Byte 'Zwischenfeld
Dim Inch As Single
Dim Feet As Single
Feet = 10.764
Inch = 25.4
ZFEM = 0
Komma = 0
Durchlauf = 1
'Einfassungsbereich sichtbar machen, evtl. durch Mattenfertigung ausgeblendet
' Border-area visible does, evtl. through mat-production faded out
[Report_Fertigungszettel]![Jahreszahl].Visible = True
[Report_Fertigungszettel]![AuftragsNr].Visible = True
[Report_Fertigungszettel]![Kunde].Visible = True
[Report_Fertigungszettel]![txtEinfassung_Einfassung].Visible = True
[Report_Fertigungszettel]![Position].Visible = True
[Report_Fertigungszettel]![Rostbezeichnung].Visible = True
[Report_Fertigungszettel]![Bestellmenge].Visible = True
'********************************************************************
'Unterschiedliche Einblendung zwischen SP- und P-Rosten
[Report_Fertigungszettel]![lbl_ef_lob1].Visible = True
[Report_Fertigungszettel]![lbl_ef_lob2].Visible = True
[Report_Fertigungszettel]![lbl_ef_lob3].Visible = True
[Report_Fertigungszettel]![Einfassungslänge].Visible = True
[Report_Fertigungszettel]![EinfassungslängeTS].Visible = True
[Report_Fertigungszettel]![EinfassungslängeFS].Visible = True
[Report_Fertigungszettel]![Einfassungslänge_Inch].Visible = True
[Report_Fertigungszettel]![EinfassungslängeTS_Inch].Visible = True
[Report_Fertigungszettel]![EinfassungslängeFS_Inch].Visible = True
[Report_Fertigungszettel]![MaterialbezeichnungTS].Visible = True
[Report_Fertigungszettel]![MaterialbezeichnungEF2].Visible = True
[Report_Fertigungszettel]![lbl_ef_quantity1].Visible = True
[Report_Fertigungszettel]![lbl_ef_quantity2].Visible = True
[Report_Fertigungszettel]![lbl_ef_quantity3].Visible = True
[Report_Fertigungszettel]![Einfassungsanzahl].Visible = True
[Report_Fertigungszettel]![EinfassungsanzahlTS].Visible = True
[Report_Fertigungszettel]![EinfassungsanzahlFS].Visible = True
'********************************************************************
Set dbDatenbank = DBEngine.Workspaces(0).Databases(0)
'Technologische Werte aus Tabelle Toleranz übernehmen
' Update Tolerance Information
sql = "SELECT * FROM Toleranz WHERE Toleranz = " & Toleranz
Set dtToleranz = dbDatenbank.OpenRecordset(sql)
MEMT = dtToleranz![Tol_Tragstab_Min_Masche] 'Bearing Bar Mesh
MEMF = dtToleranz![Tol_Füllstab_Min_Masche] 'Cross Bar Mesh
Rand1_2 = dtToleranz![Tol_Rand1+2] 'Banding Thickness for sides 1 & 2
Rand3_4 = dtToleranz![Tol_Rand3+4] 'Banding Thinckness for sides 3 & 4
Toleranz_Füllstab = dtToleranz![Tol_Füllstab] 'Cross Bar
Toleranz_Tragstab = dtToleranz![Tol_Tragstab] 'Bearing Bar
dtToleranz.Close
'Einfassungswerte aus Tabelle Einfassung übernehmen
' Update Banding Information
sql = "SELECT * FROM Einfassung WHERE Einfassung = " & Einfassung
Set dtEinfassung = dbDatenbank.OpenRecordset(sql)
Einfassungsdicke = dtEinfassung![Einf_Dicke] '
Einfassungsmaterial = dtEinfassung![Einf_Materialbezeichnung]
txtEinfassung_Einfassung.Value = dtEinfassung![Einf_Bezeichnung]
txtFüllstab_Einfassung = dtEinfassung![Einf_Bezeichnung]
txtTragstab_Einfassung = dtEinfassung![Einf_Bezeichnung]
If Einfassungsdicke = 0 Then
txt_hide_banding.Visible = True
txt_hide_banding = "NO BANDING"
End If
dtEinfassung.Close
'Unterscheidung Artikeltyp/Rosttyp
' Case statement based on the part type, keyed off of the first digit of the machine part number
Artikeltyp = Left(Rosttyp, 1)
Select Case Artikeltyp
Case "1"
lblPW1.Visible = False
lblPW2.Visible = True
lblPW3.Visible = True
'Berechnung P-Rost
' Calc BB MFG Length
Tragstablänge = Tragstab - Einfassungsdicke - Einfassungsdicke - Toleranz_Tragstab
Tragstablänge_Inch = Tragstablänge / Inch
' Calc CB MFG Length
Füllstablänge = Füllstab - Toleranz_Füllstab
Füllstablänge_Inch = Füllstablänge / Inch
' MaterialbezeichnungEF1 = Einfassungsmaterial
MaterialbezeichnungEF2 = Einfassungsmaterial
' Calc CB Banding Length
EinfassungslängeFS = Füllstab - Rand3_4
EinfassungslängeFS_Inch = EinfassungslängeFS / Inch
' Calc BB Banding Length
EinfassungslängeTS = Tragstab - Rand1_2
EinfassungslängeTS_Inch = EinfassungslängeTS / Inch
' Calc Total Banding Qty (Qty * 2)
EinfassungsanzahlTS = Bestellmenge * 2
EinfassungsanzahlFS = Bestellmenge * 2
'******************************************************************
'FIX
' Add Comments for LARGE BB pieces (over 1800mm)
' If (Tragstablänge > 1800) Or (Füllstablänge > 1800) Then
' lblcommentsBand = "OVER 1800mm, ENTER BANDING MANUALLY in 2 PIECES. " & lblcommentsBand
' Else
' lblcommentsBand = Comments
' End If
'*******************************************************************
TA_Anzahl = Füllstab / Tragstabteiler
Komma = InStr(1, TA_Anzahl, ".", vbTextCompare)
If Komma <> 0 Then
TA = CInt(Left(TA_Anzahl, (Komma - 1)))
Else
TA = CInt(TA_Anzahl)
End If
Komma = 0
FA_Anzahl = Tragstab / Füllstabteiler
Komma = InStr(1, FA_Anzahl, ".", vbTextCompare)
If Komma <> 0 Then
FA = CInt(Left(FA_Anzahl, (Komma - 1)))
Else
FA = CInt(FA_Anzahl)
End If
TEM = (Tragstablänge - ((FA - 1) * Füllstabteiler)) / 2
If TEM < MEMT Then
FA = FA - 1
TEM = (Tragstablänge - ((FA - 1) * Füllstabteiler)) / 2
If TEM < MEMT Then
FA = FA - 1
TEM = (Tragstablänge - ((FA - 1) * Füllstabteiler)) / 2
End If
End If
If TEM < ((Füllstabteiler / 2) + (Füllstabteiler * 0.05)) Then
FA = FA - 1
TEM = (Tragstablänge - ((FA - 1) * Füllstabteiler)) / 2
End If
FEM = (Füllstablänge - ((TA - 1) * Tragstabteiler)) / 2
If FEM < MEMF Then
TA = TA - 1
FEM = (Füllstablänge - ((TA - 1) * Tragstabteiler)) / 2
If FEM < MEMF Then
TA = TA - 1
FEM = (Füllstablänge - ((TA - 1) * Tragstabteiler)) / 2
End If
End If
If FEM < ((Tragstabteiler / 2) + (Tragstabteiler * 0.05)) Then
TA = TA - 1
FEM = (Füllstablänge - ((TA - 1) * Tragstabteiler)) / 2
End If
Füllstabendmasche = Format(FEM, "###0.00")
Füllstabendmasche_Inch = Füllstabendmasche / Inch
füllstabendmasche_1 = Format(FEM, "###0.00")
Füllstabendmasche_1_Inch = füllstabendmasche_1 / Inch
'********************************************************************
'Unterschiedliche Einblendung zwischen SP- und P-Rosten
[Report_Fertigungszettel]![lbl_ef_lob1].Visible = False
[Report_Fertigungszettel]![Einfassungslänge].Visible = False
[Report_Fertigungszettel]![MaterialbezeichnungTS].Visible = False
[Report_Fertigungszettel]![lbl_ef_quantity1].Visible = False
[Report_Fertigungszettel]![Einfassungsanzahl].Visible = False
'Ausblendung Stückzahlen bei T-Einfassung - Wunsch von Hr. Johann wegen Verwirrung bei Produktion Ohio
If Toleranz = 1 Then
[Report_Fertigungszettel]![EinfassungsanzahlTS].Visible = False
[Report_Fertigungszettel]![EinfassungsanzahlFS].Visible = False
[Report_Fertigungszettel]![lbl_ef_quantity2].Visible = False
[Report_Fertigungszettel]![lbl_ef_quantity3].Visible = False
End If
'********************************************************************
Case 2
lblPW1.Visible = True
lblPW2.Visible = False
lblPW3.Visible = False
'Entscheidungskriterium für Mattenfertigung
'Adjournment-criterion for mat-production
If Einfassungsdicke = 0 Then
T = Tragstab - Rand3_4
Else
T = Tragstab - (Tragstabdicke * 2) - Rand3_4
End If
'Berechnung SP-Rost
'Calculation SP-Rost
'TA-Anzahl = ((CrossBar - BearingBarThickness) / BearingBarPitch
TA_Anzahl = ((Füllstab - Tragstabdicke) / Tragstabteiler)
Komma = InStr(1, TA_Anzahl, ".", vbTextCompare)
If Komma <> 0 Then
'NumberOfBearingBars = CInt(Left(TA_Anzahl, (Komma - 1))) + 2
TA = CInt(Left(TA_Anzahl, (Komma - 1))) + 2
Else
'NumberOfBearingBars = CInt(TA_Anzahl) + 2
TA = CInt(TA_Anzahl) + 2
End If
'CrossBarNumber = MFG BearingBar Length \ CrossBarPitch
FA = T \ Füllstabteiler
'CrossBarNumber = (CrossBarNumber \ 2) * 2
FA = (FA \ 2) * 2
'BearingBarMesh = (MFGBearingBarLength - (CrossBarNumber-1)*CrossBarPitch)/2
TEM = (T - (FA - 1) * Füllstabteiler) / 2
'If BearingBarMesh >= (CrossBarPitch + BearingBarMeshTolerance) Then
If TEM >= (Füllstabteiler + MEMT) Then
'CrossBarNumber = CrosbarNumber + 2
FA = FA + 2
'BearingBarMesh = (MFBearingBarLength - (CrossBarNumber - 1) * CrossBarPitch) / 2
TEM = (T - (FA - 1) * Füllstabteiler) / 2
End If
'If BearingBarMesh < BearingBarMeshTolerance then
If TEM < MEMT Then
'CrossBarNumber = CrossBarNumber - 2
FA = FA - 2
'BearingBarMesh = (MFGBearingBarLength - (CrossBarNumber - 1) * CrossBarPitch) /2
TEM = (T - (FA - 1) * Füllstabteiler) / 2
End If
Select Case Tragstabteiler 'BearingBarPitch
Case 30.15
'CrossBarMesh = CrossBarLength - (NumberofBearingBars - 2) * BearingBarPitch - Side3&4 banding thickness - (2 * Bearing Bar Thickness)
FEM = Füllstab - (TA - 2) * Tragstabteiler - Rand3_4 - (2 * Tragstabdicke)
'If CrossBarMesh < 16.6 AND CrosbarMesh > = 1 then
If FEM < 16.6 And FEM >= 1 Then
'CrossBarMesh = CrosbarMesh + 15.07
FEM = FEM + 15.07
'CrossBarMesh plus value = 15
ZFEM = 15.07
End If
'If CrossBarMesh < 1 then
If FEM < 1 Then
'CrossBarMesh = CrossBarMesh + 30.15
FEM = FEM + 30.15
'NumberOfBearingBarRods = NumberOfBearingBarRods - 1
TA = TA - 1
End If
Case 30.16
'CrossBarMesh = CrossBarLength - (NumberofBearingBars - 2) * BearingBarPitch - Side3&4 banding thickness - (2 * Bearing Bar Thickness)
FEM = Füllstab - (TA - 2) * Tragstabteiler - Rand3_4 - (2 * Tragstabdicke)
'If CrossBarMesh < 16.6 AND CrosbarMesh > = 1 then
If FEM < 16.6 And FEM >= 1 Then
'CrossBarMesh = CrosbarMesh + 15.07
FEM = FEM + 15.07
'CrossBarMesh plus value = 15
ZFEM = 15.07
End If
'If CrossBarMesh < 1 then
If FEM < 1 Then
'CrossBarMesh = CrossBarMesh + 30.15
FEM = FEM + 30.15
'NumberOfBearingBarRods = NumberOfBearingBarRods - 1
TA = TA - 1
End If
Case Else
'CrossBarMesh = 9999
FEM = 9999
TA = TA - 1
End Select
' Calc CB MFG Length
'CrossBarLength = CrossBar + CrossBarTolerance
Füllstablänge = Füllstab + Toleranz_Füllstab
'CrosBarLength = CrossBarLength / 25.4
Füllstablänge_Inch = Füllstablänge / Inch
' Calc BB MFG Length
'BearingBarLength = (BearingBar - Side3&4BandThickness - (2 * BandingThickness)
Tragstablänge = (Tragstab - Rand3_4 - (2 * Einfassungsdicke))
Tragstablänge_Inch = Tragstablänge / Inch
MaterialbezeichnungEF2 = Einfassungsmaterial
' Calc CB Banding Length
EinfassungslängeFS = Füllstab - Rand3_4
EinfassungslängeFS_Inch = EinfassungslängeFS / Inch
' Calc BB Banding Length
EinfassungslängeTS = Tragstab - Rand1_2
EinfassungslängeTS_Inch = EinfassungslängeTS / Inch
' Calc Total Banding Qty (Qty * 2)
EinfassungsanzahlTS = Bestellmenge * 2
EinfassungsanzahlFS = Bestellmenge * 2
'INCH Anpassung nicht vergessen
Füllstabendmasche = Format(FEM, "###0.00") '& " : " & Format(ZFEM, "###0.00")
füllstabendmasche_1 = Format(FEM, "###0.00") '& " : " & Format(ZFEM, "###0.00")
Füllstabendmasche_Inch = Format(FEM / Inch, "###0.00") & " : " & ZFEM
Füllstabendmasche_1_Inch = Format(FEM / Inch, "###0.00") & " : " & ZFEM
'Bei Mattenfertigung wird Einfassunsbereich nicht angedruckt
If Einfassungsdicke = 0 Then
[Report_Fertigungszettel]![Jahreszahl].Visible = False
[Report_Fertigungszettel]![AuftragsNr].Visible = False
[Report_Fertigungszettel]![Kunde].Visible = False
[Report_Fertigungszettel]![txtEinfassung_Einfassung].Visible = False
[Report_Fertigungszettel]![Position].Visible = False
[Report_Fertigungszettel]![txtTragstab].Visible = False
[Report_Fertigungszettel]![txtFüllstab].Visible = False
[Report_Fertigungszettel]![Rostbezeichnung].Visible = False
[Report_Fertigungszettel]![Bestellmenge].Visible = False
[Report_Fertigungszettel]![Einfassungslänge].Visible = False
[Report_Fertigungszettel]![Einfassungsanzahl].Visible = False
[Report_Fertigungszettel]![MaterialbezeichnungTS].Visible = False
Else
Einfassungslänge = Füllstab
Einfassungslänge_Inch = Einfassungslänge / Inch
Einfassungsanzahl = Bestellmenge * 2
End If
Case Else
FEM = 9999
TEM = 9999
End Select
'********************************************************************
'Berichtsfelder zuweisen - Bereich Füllstab
Füllstabanzahl = FA * Bestellmenge & " / " & FA
'Berichtsfelder zuweisen - Bereich Tragstab
'Report-fields assign - area filling-rod
Tragstabanzahl = TA * Bestellmenge & " / " & TA
Tragstabendmasche = Format(TEM, "###0.00")
Tragstabendmasche_Inch = Tragstabendmasche / Inch
Klinkung = FA
'Zeichnungskästchen anzeigen
'Show Drawing Boxes
If Zeichnung = -1 Then
lblZeichnungBearing = "Review Traveler / DWG"
lblZeichnungCross = "Review Traveler / DWG"
lblZeichnungBand = "Review Traveler / DWG"
Else
lblZeichnungBearing = ""
lblZeichnungCross = ""
lblZeichnungBand = ""
End If
'Barcode links ausfüllen
'Fill Barcode Links
If Len(Position) <> 3 Then
Auffüllen = 3 - Len(Position)
PositionsNr = String(Auffüllen, "0") & Position
End If
'********************************************************************
'Daten Fertigungszettel in Datenbank PPS-Daten.mdb schreiben
'Write the records to the PPS-Daten-IN db
sql = "SELECT * FROM [PPS-Daten-IN] where Projekt = " & AuftragsNr & " and Artikel = """ & Mark & """"
Set dtPPSDaten = dbDatenbank.OpenRecordset(sql)
If dtPPSDaten.RecordCount = 0 Then
' Get the Seq number
Dim db As Database
Dim rs, ros As Recordset
Dim dbSQL, rosSQL As String
Set db = DBEngine.Workspaces(0).Databases(0)
dbSQL = "SELECT SeqNum FROM [SEQNum] where ID = 1"
Set rs = db.OpenRecordset(dbSQL)
rosSQL = "SELECT HöheTS, DickeFS FROM Rostbezeichnung WHERE Rostbezeichnung = """ & Rostbezeichnung & """"
Set ros = db.OpenRecordset(rosSQL)
With rs
.Edit
!SeqNum = !SeqNum + 1
.Update
End With
OpSeqBand.Caption = rs!SeqNum
OpSeqCB.Caption = rs!SeqNum
OpSeqBB.Caption = rs!SeqNum
With dtPPSDaten
.AddNew
!Projekt = AuftragsNr
!Bez_Rost = Rostbezeichnung
!Artikel = Mark
!Arttext1 = rs!SeqNum
!Rost_TS = Tragstab
!Rost_FS = Füllstab
!Stück_S0 = Bestellmenge
!Bez_Komp_1 = Einfassungsmaterial
!Länge_1 = EinfassungslängeTS
!Länge_2 = EinfassungslängeTS
!Länge_3 = EinfassungslängeFS
!Länge_4 = EinfassungslängeFS
!Bez_Komp_5 = MaterialbezeichnungTS
!Länge_TS = Tragstablänge
!Endma_TS = Format(TEM, "###0.00")
!Stück_TS = TA
!Teilung_TS = Tragstabteiler
!Bez_Komp_6 = MaterialbezeichnungFS
!Länge_FS = Füllstablänge
!Endma_FS = Format(FEM, "###0.00")
!Stück_FS = FA
!Teilung_FS = Füllstabteiler
!Eintragsdatum = Forms![Fertigungskopf]![Datum]
!Höhe_TS = ros!HöheTS
!Stärke_FS = ros!DickeFS
ProdAuftrag = !Prodauftr
.Update
End With
dtPPSDaten.Close
rs.Close
Else
' The to take into account the looping issue. When this is the 2nd time through on the same
' Mark #, then get the Machine # for the report
dtPPSDaten.Close
sql = "SELECT Prodauftr, Arttext1 FROM [PPS-Daten-IN] where Projekt = " & AuftragsNr & " and Artikel = """ & Mark & """"
Set dtPPSDaten = dbDatenbank.OpenRecordset(sql)
ProdAuftrag = dtPPSDaten.Prodauftr
OpSeqBand.Caption = dtPPSDaten.Arttext1
OpSeqCB.Caption = dtPPSDaten.Arttext1
OpSeqBB.Caption = dtPPSDaten.Arttext1
dtPPSDaten.Close
End If
'End If
'********************************************************************
'Barcode rechts ausfüllen (nachgelagert wegen Produktionsauftragsnummer aus Tabelle PPS-Daten-IN)
If Len(Trim(ProdAuftrag)) <> 9 Then
Auffüllen = 9 - Len(Trim(ProdAuftrag))
Barcode_ProdAuftrag = String(Auffüllen, "0") & ProdAuftrag
Barcode_PA_Edgebanding = "*" & Barcode_ProdAuftrag & "*"
Barcode_PA_crossbar = "*" & Barcode_ProdAuftrag & "*"
Barcode_PA_Bearingbar = "*" & Barcode_ProdAuftrag & "*"
Else
Barcode_PA_Edgebanding = "*" & Barcode_ProdAuftrag & "*"
Barcode_PA_crossbar = "*" & Barcode_ProdAuftrag & "*"
Barcode_PA_Bearingbar = "*" & Barcode_ProdAuftrag & "*"
End If
'------------------------------------------------------------------
Me.Label444.Visible
Me.Label445.Visible
Exit_Detailbereich_Format:
Exit Sub
Err_Detailbereich_Format:
MsgBox Err.Description
Resume Exit_Detailbereich_Format
End Sub
If you scroll to the very end you will see to lines that make labels 444 and 445 visible. If i comment this lines out, as they are the only ones I added, the report will work correctly.
Oh and it does not matter where the code is placed as the end result is always the same, no output.
Does anyone have any idea as to why this is happening?? I have hit the wall on this one.
Thanks to any suggestions I can get.
The correct syntax for making a control visible is
Me.Label444.Visible = True
Me.Label445.Visible = True
The visible property is expecting a Boolean value so you have to set it to either true or false. This might solve the problem.
I have a complicated problem that I have been working on for weeks with no Success, any and all help is much appreciated.
I have 2 pairs of longitude and latitude co-ords,
A1:A11418 = lat1
B1:B11418 = long1
C1:C11418 = lat1/long1 Corresponding ID to be returned
D1:D10248 = lat2
E1:E10248 = long2
F1:F10248 = Return column for ID
Columns D, E are in a completely different order than A, B.
I need to match lat/long1 against lat/long2 and compare the distance between and if it is <= the desired distance, output only the ID with the least distance from column C to F.
Private Sub CommandButton1_click()
Dim ID As Double
Dim Dist, Results, Pre_rslt As Variant
Dim lat1, long1, lat2, long2 As Range
Dim i As Integer, j As Integer
j_DO:
Do While j <= 11418 '## lat/long1 (Col D, E) Counter
j = j + 1
i_DO:
Do While i <= 10248 '## lat/long2 (Col A, B) Counter
i = i + 1
Set lat1 = Range("A2").Offset(i)
Set long1 = Range("B2").Offset(i)
Set lat2 = Range("D2").Offset(j)
Set long2 = Range("E2").Offset(j)
If IsEmpty(Range("A2").Offset(i).Value) = True Or IsEmpty(Range("B2").Offset(i).Value) = True Then
i = i + 1
End If
earth_radius = 6371 '## GCD START
PI = 3.14159265
deg2rad = PI / 180
dLat = deg2rad * (lat2 - lat1)
dLon = deg2rad * (long2 - long1)
a = Sin(dLat / 2) * Sin(dLat / 2) + Cos(deg2rad * lat1) * Cos(deg2rad * lat2) * Sin(dLon / 2) * Sin(dLon / 2)
c = 2 * WorksheetFunction.Asin(Sqr(a))
d = earth_radius * c
Dist = d '## GCD END
If Dist <= 1 Then '## Result filtering
Results = ID
Cells(j, 6) = Results
ID = Range("B2").Offset(i, 1)
i = 0
GoTo j_DO
ElseIf i >= 10248 And Results <> ID Then
i = 0
GoTo j_DO
ElseIf IsEmpty(Range("F2").Offset(j).Value) = True Then
GoTo i_DO
End If
Loop
Loop
End Sub
As it is now, it will return random results and I cannot figure out how to get it to only return the closest result.
P.S Please forgive my code/explanation I Have only been using VBA for 2-3 weeks and am still very new, thank-you for your help in advance.
Changed up your code formatting. This works for me.
Sub CommandButton2_click()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim ID, Results As String
Dim Dist, dist_min As Double
Dim lat1, long1, lat2, long2, dLat, dLong As Double
Dim data1r, data2r As Integer
earth_radius = 6371 'km
Pi = 3.14159265
deg2rad = Pi / 180
For data2r = 2 To 75 '2nd set of lat/lon rows
dist_min = 2 * earth_radius * Pi 'furthest point on earth
Results = ""
For data1r = 2 To 100 '1st set of lat/lon rows
lat1 = Cells(data1r, 1)
long1 = Cells(data1r, 2)
ID = Cells(data1r, 3)
lat2 = Cells(data2r, 4)
long2 = Cells(data2r, 5)
lat1 = lat1 * deg2rad
long1 = long1 * deg2rad
lat2 = lat2 * deg2rad
long2 = long2 * deg2rad
dLat = Abs(lat2 - lat1)
dLong = Abs(long2 - long1)
a = Sin(dLat / 2) ^ 2 + Cos(lat1) * Cos(lat2) * (Sin(dLong) ^ 2)
c = 2 * WorksheetFunction.Atan2(a ^ 0.5, (1 - a) ^ 0.5)
d = earth_radius * c
Dist = d '## GCD END
If Dist < dist_min Then
Results = ID
dist_min = Dist
lat = lat1
lon = long1
End If
Next data1r
Cells(data2r, 6) = Results
Next data2r
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
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