This solution seems the simplest. (Or at least, the easiest to grasp)
All that is needed is a function to subtract two ranges. In other words, something that will give this:
A ------ A ------ A ----
B ------- and B ------ and B ---------
= ---- = ---- = --- --
Which is simple enough. Then you can simply iterate through each of the ranges, starting from the lowest, and for each one, subtract from it all the ranges above it, in turn. And there you have it.
Here is an implementation of the range subtractor in Python:
def subtractRanges((As, Ae), (Bs, Be)):
'''SUBTRACTS A FROM B'''
# e.g, A = ------
# B = -----------
# result = -- ---
# Returns list of new range(s)
if As > Be or Bs > Ae: # All of B visible
return [[Bs, Be]]
result = []
if As > Bs: # Beginning of B visible
result.append([Bs, As])
if Ae < Be: # End of B visible
result.append([Ae, Be])
return result
Using this function, the rest can be done like this:
(A 'span' means a range, as 'range' is a Python keyword)
spans = [["red", [12.5, 13.8]],
["blue", [0.0, 5.4]],
["green", [2.0, 12.0]],
["yellow", [3.5, 6.7]],
["orange", [6.7, 10.0]]]
i = 0 # Start at lowest span
while i < len(spans):
for superior in spans[i+1:]: # Iterate through all spans above
result = subtractRanges(superior[1], spans[i][1])
if not result: # If span is completely covered
del spans[i] # Remove it from list
i -= 1 # Compensate for list shifting
break # Skip to next span
else: # If there is at least one resulting span
spans[i][1] = result[0]
if len(result) > 1: # If there are two resulting spans
# Insert another span with the same name
spans.insert(i+1, [spans[i][0], result[1]])
i += 1
print spans
This gives [['red', [0, 1]], ['red', [3, 4]], ['yellow', [4, 6]], ['green', [12, 15]], ['cyan', [6, 12]], ['blue', [1, 3]]]
, which is correct.