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 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 understand that the code looks rude and not optimized, but I canβt spend more time on this project. I left a few notes to help read it if someone decides to continue this project.
Hope this helps.
Best regards, Schadenfreude.