160 lines
3.8 KiB
Python
Executable File
160 lines
3.8 KiB
Python
Executable File
import tkinter as tk
|
|
from positions import *
|
|
|
|
TEXT_SIZE = 14
|
|
PADDING = 40
|
|
OVERLAP_OUTLINE_WIDTH = 4
|
|
SCREEN_WIDTH=1200
|
|
SCREEN_HEIGHT=160
|
|
VSCALE=2
|
|
|
|
def rgb_to_hex(rgb):
|
|
r, g, b = rgb
|
|
return f"#{r:02x}{g:02x}{b:02x}"
|
|
|
|
|
|
def rect_bounds(box):
|
|
return box.get_left(), box.get_top(), box.get_right(), box.get_bottom()
|
|
|
|
|
|
def intersection(a, b):
|
|
ax1, ay1, ax2, ay2 = rect_bounds(a)
|
|
bx1, by1, bx2, by2 = rect_bounds(b)
|
|
|
|
x1 = max(ax1, bx1)
|
|
y1 = max(ay1, by1)
|
|
x2 = min(ax2, bx2)
|
|
y2 = min(ay2, by2)
|
|
|
|
if x1 < x2 and y1 < y2:
|
|
return x1, y1, x2, y2
|
|
|
|
return None
|
|
|
|
|
|
def visualize_packing(items):
|
|
root = tk.Tk()
|
|
root.title("Rectangle Packing Visualizer")
|
|
|
|
canvas_width = SCREEN_WIDTH + PADDING * 2
|
|
canvas_height = SCREEN_HEIGHT*VSCALE + PADDING * 2
|
|
|
|
canvas = tk.Canvas(
|
|
root,
|
|
width=canvas_width,
|
|
height=canvas_height,
|
|
bg="black",
|
|
highlightthickness=0
|
|
)
|
|
canvas.pack()
|
|
|
|
ox = PADDING
|
|
oy = PADDING
|
|
|
|
# Outer perimeter
|
|
canvas.create_rectangle(
|
|
ox,
|
|
oy,
|
|
ox + SCREEN_WIDTH,
|
|
oy + SCREEN_HEIGHT*VSCALE,
|
|
fill="white",
|
|
outline="white"
|
|
)
|
|
|
|
boxes = [box for item in items for box in item.boxes]
|
|
|
|
# Draw boxes
|
|
for box in boxes:
|
|
x1 = ox + box.get_left()
|
|
y1 = oy + box.get_top() * VSCALE
|
|
x2 = ox + box.get_right()
|
|
y2 = oy + box.get_bottom() * VSCALE
|
|
|
|
canvas.create_rectangle(
|
|
x1,
|
|
y1,
|
|
x2,
|
|
y2,
|
|
fill=rgb_to_hex(box.item.color),
|
|
outline="black"
|
|
)
|
|
|
|
# Icon containers draw only vertical separators for simulated icon/dot slots.
|
|
if box.item.is_icon_item and box is box.item.boxes[0]:
|
|
cursor = box.get_left()
|
|
for kind, width in box.item.render_segments()[:-1]:
|
|
cursor += width
|
|
x = ox + cursor
|
|
canvas.create_line(
|
|
x,
|
|
y1,
|
|
x,
|
|
y2,
|
|
fill="black",
|
|
width=2,
|
|
)
|
|
|
|
# Draw overlap regions
|
|
for i in range(len(boxes)):
|
|
for j in range(i + 1, len(boxes)):
|
|
if boxes[i].item is boxes[j].item:
|
|
continue
|
|
overlap = intersection(boxes[i], boxes[j])
|
|
|
|
if overlap:
|
|
x1, y1, x2, y2 = overlap
|
|
y1 *= VSCALE
|
|
y2 *= VSCALE
|
|
|
|
x1 += ox
|
|
y1 += oy
|
|
x2 += ox
|
|
y2 += oy
|
|
|
|
# Red overlap area
|
|
canvas.create_rectangle(
|
|
x1,
|
|
y1,
|
|
x2,
|
|
y2,
|
|
fill="red",
|
|
outline="red"
|
|
)
|
|
|
|
# Thick red circle around overlap
|
|
cx = (x1 + x2) / 2
|
|
cy = (y1 + y2) / 2
|
|
radius = max(x2 - x1, y2 - y1) / 2 + 12
|
|
|
|
canvas.create_oval(
|
|
cx - radius,
|
|
cy - radius,
|
|
cx + radius,
|
|
cy + radius,
|
|
outline="red",
|
|
width=OVERLAP_OUTLINE_WIDTH
|
|
)
|
|
|
|
# Draw item texts
|
|
for item in items:
|
|
x1 = ox + item.get_left()
|
|
y1 = oy + item.get_top() * VSCALE
|
|
x2 = ox + item.get_right()
|
|
y2 = oy + item.get_bottom() * VSCALE
|
|
|
|
canvas.create_text(
|
|
(x1 + x2) / 2,
|
|
(y1 + y2) / 2,
|
|
text=item.rendername(),
|
|
font=("Arial", TEXT_SIZE),
|
|
fill="black"
|
|
)
|
|
|
|
root.mainloop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
containers = position_containers()
|
|
|
|
visualize_packing(containers)
|