Skip to main content
Corrected output.
Source Link
Jollywatt
  • 321
  • 1
  • 2
  • 9

This gives [['red', [0[12.5, 1]]13.8]], ['red'['blue', [3[0.0, 4]]2.0]], ['yellow'['green', [4[2.0, 6]]3.5]], ['green', [12[10.0, 15]]12.0]], ['cyan'['yellow', [6[3.5, 12]]6.7]], ['blue'['orange', [1[6.7, 3]]]10.0]]], which is correct.

This gives [['red', [0, 1]], ['red', [3, 4]], ['yellow', [4, 6]], ['green', [12, 15]], ['cyan', [6, 12]], ['blue', [1, 3]]], which is correct.

This gives [['red', [12.5, 13.8]], ['blue', [0.0, 2.0]], ['green', [2.0, 3.5]], ['green', [10.0, 12.0]], ['yellow', [3.5, 6.7]], ['orange', [6.7, 10.0]]], which is correct.

Source Link
Jollywatt
  • 321
  • 1
  • 2
  • 9

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.