(item, placed, bpCols)
| 66 | } |
| 67 | |
| 68 | function placeGreedy(item, placed, bpCols) { |
| 69 | const clamped = clampItemToGrid(item, bpCols); |
| 70 | const maxX = Math.max(0, bpCols - clamped.w); |
| 71 | |
| 72 | // Scan from top row down to the item's current row |
| 73 | for (let y = 0; y <= clamped.y; y += 1) { |
| 74 | for (let x = 0; x <= maxX; x += 1) { |
| 75 | const test = { ...clamped, x, y }; |
| 76 | const collides = placed.some((p) => rectanglesOverlap(test, p)); |
| 77 | if (!collides) { |
| 78 | return test; |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // Fallback: keep y, slide leftmost then move up if possible |
| 84 | const leftX = findLeftmostX(clamped, placed, bpCols); |
| 85 | const positioned = { ...clamped, x: leftX }; |
| 86 | return moveUp(positioned, placed); |
| 87 | } |
| 88 | |
| 89 | function stretchTrailingGaps(items, bpCols) { |
| 90 | const cloned = cloneItems(items); |
no test coverage detected