Exemple #1
0
def arrange_ring(radii, colors, R0, rmax):
    clear_zeros(radii)
    for D in colors.values():
        clear_zeros(D)
    Rc = R0 + rmax
    thetasum = sum(2 * r * cnt / Rc for r, cnt in radii.items())
    if thetasum > 2 * pi:
        return False, None
    # Probably possible to put all the discs in one ring
    # First attempt: arrange the discs with equally spaced centers
    n = sum(radii.values())
    thetadiff = (2 * pi / n)
    dist = Rc * (2 - 2 * thetadiff.cos()
                 ).sqrt()  # distance between centers in equal space case
    if len(radii) == 1:
        r = next(iter(radii))
        placed = [r for i in range(n)]
    else:
        invcnt = {r: QQ(1) / radii[r] for r in radii}
        # We assign radii greedily, by keeping track of the radius with the highest
        # proportion not yet placed.  We start by alternating between big and small
        # and smaller circles.
        srad = sorted(radii, reverse=True)
        srad = [srad[(-1)**i * ((i + 1) // 2)] for i in range(len(srad))]
        placed = list(srad)
        remaining = [(QQ(-1) + invcnt[srad[i]], i) for i in range(len(srad))]
        #print("Initial", remaining)
        heapq.heapify(remaining)
        cur = heapq.heappop(remaining)
        while len(placed) < n:
            #print(cur, remaining)
            placed.append(srad[cur[1]])
            cur = heapq.heappushpop(remaining,
                                    (cur[0] + invcnt[srad[cur[1]]], cur[1]))
    #print("radii", radii)
    #print("colors", colors)
    #print(placed)
    # Now assign colors in a similar way
    color_placed = {}
    for r in radii:
        col = next(iter(colors[r]))
        if len(colors[r]) == 1:
            color_placed[r] = [col for i in range(radii[r])]
        else:
            invcnt = {c: QQ(1) / colors[r][c] for c in colors[r]}
            color_placed[r] = [col]
            remaining = [(QQ(-1), c) for c in colors[r] if c != col]
            cur = (QQ(-1), col)
            heapq.heapify(remaining)
            while len(color_placed[r]) < radii[r]:
                cur = heapq.heappushpop(remaining,
                                        (cur[0] + invcnt[cur[1]], cur[1]))
                color_placed[r].append(cur[1])
    #print(color_placed)
    if len(radii) == 1 or len(radii) == 2 and len(set(radii.values())) == 2:
        # There will be two circles of max radii adjacent
        equal_centers = (2 * rmax < dist + eps)
    else:
        equal_centers = True
        for i in range(len(placed)):
            if placed[i] + placed[(i + 1) % len(placed)] > dist + eps:
                equal_centers = False
                break
    if not equal_centers:
        theta_diffs = []
        for i, r in enumerate(placed):
            nextr = placed[(i + 1) % len(placed)]
            theta_diffs.append((1 - (r + nextr)**2 / (2 * Rc**2)).arccos())
        thetasum = sum(theta_diffs)
        if thetasum > 2 * pi + eps:
            return False, None
        thetaspace = (2 * pi - thetasum) / n
    theta = RR(0)
    circles = []
    for i, r in enumerate(placed):
        c = color_placed[r].pop(0)
        circles.append((Rc * theta.cos(), Rc * theta.sin(), r, c))
        if not equal_centers:
            thetadiff = theta_diffs[i] + thetaspace
            #nextr = placed[(i+1)%len(placed)]
            #thetadiff = (r + nextr)/Rc + thetaspace
            ## d^2 = 2Rc^2(1-cos(thetadiff))
            #if 2 * Rc**2 * (1 - thetadiff.cos()) > (r + nextr)**2 + eps:
            #    # need to use more than one ring
            #    print(f"{i}/{len(placed)}", thetaspace, thetadiff, 2 * Rc**2 * (1 - thetadiff.cos()), (r + nextr)**2)
            #    return False, None
        theta += thetadiff
    return circles, Rc + rmax