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