I have the following algorithm for my d3.js charts which detects colliding value labels in my charts. This operation has to be done once all labels are rendered, and hence the reason why I use d3.selectAll()
.
Right now, what it does is, I select all the value labels on the div. For each value label, I get their coordinates to generate two points of x
and y
coordinates of the corners of the rectangle. More info can be found in this SO answer.
I then check for collisions by checking all points previously added to the valueLabelPositions
array. So this operation is like O(n^2)
, which is super slow. I also checked the JS call stack timeline, and it takes over 2000 milliseconds for graphs with 4 axis. Here's an example of the graph with collision detection on labels:
This is the code I wrote.
removeOverlappingLabelText: ->
valueLabelPositions = []
d3.selectAll('.value-labels text')[0].forEach (t) ->
currentBox = t.getBoundingClientRect()
# Check this for deeper summary: https://stackoverflow.com/a/32088787/1913389
# _________________ x2, y2
# | |
# | |
# | |
# | |
# x1, y1 |_________________|
# Position of bottom-left coordinate of current rect
x1 = currentBox.left
y1 = currentBox.top
# Position of top-right coordinate of current rect
x2 = currentBox.right
y2 = currentBox.bottom
for i in valueLabelPositions
# Second rect
x3 = i.left
y3 = i.top
x4 = i.right
y4 = i.bottom
if(x3 > x2 || y3 > y2 || x1 > x4 || y1 > y4)
# No overlaps
else
# Overlap found, so remove
t.remove()
valueLabelPositions.push(currentBox)
I am curious how I can optimize this so it's faster than n^2
?