195 lines
5.3 KiB
Plaintext
195 lines
5.3 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "54ae86a4",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from itertools import product, zip_longest, count\n",
|
|
"import numpy as np\n",
|
|
"from dataclasses import dataclass\n",
|
|
"from matplotlib import pyplot as plt"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "e4756abb",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"@dataclass\n",
|
|
"class Rectangle:\n",
|
|
" x: int\n",
|
|
" y: int\n",
|
|
" w: int\n",
|
|
" h: int\n",
|
|
"\n",
|
|
"# Beware, axis 0 is vertical, axis 1 horizontal\n",
|
|
"\n",
|
|
"def measure(rectangles):\n",
|
|
" w, h = 0, 0\n",
|
|
" for rect in rectangles:\n",
|
|
" w = max(w, rect.x + rect.w)\n",
|
|
" h = max(h, rect.y + rect.h)\n",
|
|
" return w, h\n",
|
|
"\n",
|
|
"def check_overlap(rectangles):\n",
|
|
" w, h = measure(rectangles)\n",
|
|
" image = np.zeros((h, w), dtype=int)\n",
|
|
" for i, rect in enumerate(rectangles, start=1):\n",
|
|
" if rect.x < 0 or rect.y < 0:\n",
|
|
" raise ValueError(\"Rectangle placed out of bound\", rect)\n",
|
|
" for x, y in product(range(rect.w), range(rect.h)):\n",
|
|
" if image[rect.y + y, rect.x + x]:\n",
|
|
" raise ValueError(f\"Overlap at {rect.x+x}, {rect.y+y}\", rectangles[i - 1], rectangles[image[rect.y + y, rect.x + x] - 1])\n",
|
|
" image[rect.y + y, rect.x + x] = i\n",
|
|
"\n",
|
|
"def show_rectangles(rectangles):\n",
|
|
" w, h = measure(rectangles)\n",
|
|
" image = np.zeros((h, w), dtype=int)\n",
|
|
" for i, rect in enumerate(rectangles, start=1):\n",
|
|
" for x, y in product(range(rect.w), range(rect.h)):\n",
|
|
" image[rect.y + y, rect.x + x] = i\n",
|
|
" plt.imshow(image, aspect='equal')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "fd8c423e",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"r1 = Rectangle(0, 0, 10, 10)\n",
|
|
"r2 = Rectangle(0, 10, 10, 10)\n",
|
|
"r3 = Rectangle(10, 0, 10, 20)\n",
|
|
"show_rectangles([r1, r2, r3])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "1c7bfa44",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"r1 = Rectangle(0, 0, 10, 10)\n",
|
|
"r2 = Rectangle(0, 0, 5, 5)\n",
|
|
"try:\n",
|
|
" check_overlap([r1, r2])\n",
|
|
"except ValueError as err:\n",
|
|
" print(err)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "ec3d4881",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def repack(to_pack):\n",
|
|
" to_pack.sort(key=lambda r: r.w)\n",
|
|
" total_height = sum(r.h for r in to_pack)\n",
|
|
" first_half = []\n",
|
|
" second_half = []\n",
|
|
" position = 0\n",
|
|
" for rect in to_pack:\n",
|
|
" if position < total_height // 2:\n",
|
|
" first_half.append(rect)\n",
|
|
" else:\n",
|
|
" second_half.append(rect)\n",
|
|
" position += rect.h\n",
|
|
" total_width = to_pack[-1].w + to_pack[-2].w\n",
|
|
" for shift in range(0, 1000):\n",
|
|
" y = 0\n",
|
|
" for left in first_half:\n",
|
|
" left.y = y\n",
|
|
" left.x = 0\n",
|
|
" y += left.h\n",
|
|
" for right in second_half:\n",
|
|
" right.y = y - right.h\n",
|
|
" right.x = shift - right.w\n",
|
|
" y -= right.h\n",
|
|
" try:\n",
|
|
" check_overlap(to_pack)\n",
|
|
" return\n",
|
|
" except ValueError:\n",
|
|
" pass"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "90689e9a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"to_pack = [\n",
|
|
" Rectangle(0, 0, 8, 10),\n",
|
|
" Rectangle(0, 0, 5, 6),\n",
|
|
" Rectangle(0, 0, 5, 4),\n",
|
|
" Rectangle(0, 0, 2, 5),\n",
|
|
"]\n",
|
|
"repack(to_pack)\n",
|
|
"show_rectangles(to_pack)\n",
|
|
"check_overlap(to_pack)\n",
|
|
"w, h = measure(to_pack)\n",
|
|
"print(\"score (lower is better)\", w * h)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "b3778f5b",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"to_pack = [\n",
|
|
" Rectangle(0, 0, 8, 10),\n",
|
|
" Rectangle(0, 0, 5, 6),\n",
|
|
" Rectangle(0, 0, 5, 4),\n",
|
|
" Rectangle(0, 0, 2, 5),\n",
|
|
" Rectangle(0, 0, 8, 16),\n",
|
|
" Rectangle(0, 0, 5, 5),\n",
|
|
" Rectangle(0, 0, 5, 2),\n",
|
|
" Rectangle(0, 0, 2, 3),\n",
|
|
" Rectangle(0, 0, 12, 10),\n",
|
|
" Rectangle(0, 0, 4, 6),\n",
|
|
" Rectangle(0, 0, 4, 4),\n",
|
|
" Rectangle(0, 0, 1, 5),\n",
|
|
"]\n",
|
|
"repack(to_pack)\n",
|
|
"show_rectangles(to_pack)\n",
|
|
"check_overlap(to_pack)\n",
|
|
"w, h = measure(to_pack)\n",
|
|
"print(\"score (lower is better)\", w * h)"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.9.4"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|