@mark1russell7/packing 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.d.ts +24 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +476 -34
- package/build/index.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters.ts +7 -4
- package/src/check.ts +14 -3
- package/src/free.ts +3 -0
- package/src/index.ts +1 -1
- package/src/measures.ts +78 -19
- package/src/moves.ts +33 -0
- package/src/overflow.ts +332 -0
- package/src/pack.ts +15 -3
- package/src/shelf.ts +3 -0
- package/src/skyline.ts +34 -7
package/src/pack.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearMoves, countMove, MOVES } from "./moves.ts";
|
|
1
2
|
import { compareRects, type SortOrder } from "./order.ts";
|
|
2
3
|
import { Rect } from "./rect.ts";
|
|
3
4
|
import { packRowsInSpace, packShelf } from "./shelf.ts";
|
|
@@ -25,10 +26,20 @@ export interface PackLayoutOptions extends PackOptions {
|
|
|
25
26
|
strategy?: "rows" | "skyline" | "shelf";
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
/** The depth of the calls of `packInto`. Only the outer gap fill counts its rectangles as moves. */
|
|
30
|
+
let fillDepth = 0;
|
|
31
|
+
|
|
28
32
|
function packInto<T>(rects: Rect<T>[], region: Rect<T>, offset: Vector, order: SortOrder): void {
|
|
29
33
|
const unpacked = Rect.unpacked(rects);
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
fillDepth++;
|
|
35
|
+
try {
|
|
36
|
+
pack(unpacked, region, { sortOrder: order });
|
|
37
|
+
} finally {
|
|
38
|
+
fillDepth--;
|
|
39
|
+
}
|
|
40
|
+
const placed = Rect.packed(unpacked);
|
|
41
|
+
if (fillDepth === 0) countMove(MOVES.rows, placed.length);
|
|
42
|
+
for (const r of placed) r.translateBy(offset);
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
function packRow<T>(rects: Rect<T>[], outer: Rect<T>, order: SortOrder): boolean {
|
|
@@ -100,7 +111,8 @@ export function packRows<T>(rects: Rect<T>[], outer: Rect<T>, options: PackOptio
|
|
|
100
111
|
const space = options.space;
|
|
101
112
|
if (space !== undefined && (!space.plain || rects.some((r) => r.locked))) return packRowsInSpace(rects, outer, order, space);
|
|
102
113
|
for (const r of rects) if (r.locked) r.wasPacked = true;
|
|
103
|
-
|
|
114
|
+
// A row that does not fit grows the height and starts again, thus the gap fills of the try before do not count
|
|
115
|
+
while (!packRow(rects, outer, order)) if (fillDepth === 0) clearMoves(MOVES.rows);
|
|
104
116
|
return outer;
|
|
105
117
|
}
|
|
106
118
|
|
package/src/shelf.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { countMove, MOVES } from "./moves.ts";
|
|
1
2
|
import { compareRects, type SortOrder } from "./order.ts";
|
|
2
3
|
import type { Rect } from "./rect.ts";
|
|
3
4
|
import { firstFit, type FreeSpace, type FreeSpan } from "./space.ts";
|
|
@@ -111,6 +112,8 @@ export function packShelf<T>(rects: Rect<T>[], outer: Rect<T>, options: ShelfOpt
|
|
|
111
112
|
|
|
112
113
|
if (outer.fixedSize && rowTop + h > outer.size.y) continue;
|
|
113
114
|
|
|
115
|
+
// The move of a shelf: the rectangle joins an open shelf that a taller rectangle made
|
|
116
|
+
if (!rowEmpty && rowTop + h < rowBottom) countMove(MOVES.shelf);
|
|
114
117
|
rect.position.set(x + outer.position.x, rowTop + outer.position.y);
|
|
115
118
|
rect.wasPacked = true;
|
|
116
119
|
rowEmpty = false;
|
package/src/skyline.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { countMove, MOVES } from "./moves.ts";
|
|
1
2
|
import { compareRects, type SortOrder } from "./order.ts";
|
|
2
3
|
import type { Rect } from "./rect.ts";
|
|
3
4
|
import type { FreeSpace } from "./space.ts";
|
|
@@ -11,6 +12,19 @@ import type { FreeSpace } from "./space.ts";
|
|
|
11
12
|
* `limit[x, x + rw)`. A second deque keeps that smallest value. No valid window gives the height `Infinity`.
|
|
12
13
|
*/
|
|
13
14
|
export function lowestWindow(skyline: ArrayLike<number>, width: number, rw: number, limit?: ArrayLike<number>, rh = 0): { x: number; y: number } {
|
|
15
|
+
const { x, y } = findWindow(skyline, width, rw, limit, rh);
|
|
16
|
+
return { x, y };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The lowest window of `findWindow`, and `first`: the x of the leftmost valid window, or `-1` */
|
|
20
|
+
interface Window {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
first: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** This function is `lowestWindow`, and it also gives the x of the leftmost valid window. */
|
|
27
|
+
function findWindow(skyline: ArrayLike<number>, width: number, rw: number, limit?: ArrayLike<number>, rh = 0): Window {
|
|
14
28
|
if (limit !== undefined) return lowestLimitedWindow(skyline, limit, width, rw, rh);
|
|
15
29
|
const deque = new Int32Array(width);
|
|
16
30
|
let head = 0;
|
|
@@ -29,11 +43,16 @@ export function lowestWindow(skyline: ArrayLike<number>, width: number, rw: numb
|
|
|
29
43
|
}
|
|
30
44
|
}
|
|
31
45
|
}
|
|
32
|
-
return { x: bestX, y: bestY };
|
|
46
|
+
return { x: bestX, y: bestY, first: width >= rw ? 0 : -1 };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** This function counts the move of the skyline: a window that is lower than the leftmost valid window. */
|
|
50
|
+
function countWindow(w: Window): void {
|
|
51
|
+
if (Number.isFinite(w.y) && w.first >= 0 && w.x !== w.first) countMove(MOVES.skyline);
|
|
33
52
|
}
|
|
34
53
|
|
|
35
54
|
/** This function is `lowestWindow` with a limit for each cell. */
|
|
36
|
-
function lowestLimitedWindow(skyline: ArrayLike<number>, limit: ArrayLike<number>, width: number, rw: number, rh: number):
|
|
55
|
+
function lowestLimitedWindow(skyline: ArrayLike<number>, limit: ArrayLike<number>, width: number, rw: number, rh: number): Window {
|
|
37
56
|
const high = new Int32Array(width);
|
|
38
57
|
const low = new Int32Array(width);
|
|
39
58
|
let hh = 0;
|
|
@@ -42,6 +61,7 @@ function lowestLimitedWindow(skyline: ArrayLike<number>, limit: ArrayLike<number
|
|
|
42
61
|
let lt = 0;
|
|
43
62
|
let bestX = 0;
|
|
44
63
|
let bestY = Infinity;
|
|
64
|
+
let first = -1;
|
|
45
65
|
for (let i = 0; i < width; i++) {
|
|
46
66
|
while (ht > hh && skyline[high[ht - 1]!]! <= skyline[i]!) ht--;
|
|
47
67
|
high[ht++] = i;
|
|
@@ -51,13 +71,15 @@ function lowestLimitedWindow(skyline: ArrayLike<number>, limit: ArrayLike<number
|
|
|
51
71
|
if (low[lh]! <= i - rw) lh++;
|
|
52
72
|
if (i >= rw - 1) {
|
|
53
73
|
const max = skyline[high[hh]!]!;
|
|
54
|
-
|
|
74
|
+
const valid = max + rh <= limit[low[lh]!]!;
|
|
75
|
+
if (valid && first < 0) first = i - rw + 1;
|
|
76
|
+
if (max < bestY && valid) {
|
|
55
77
|
bestY = max;
|
|
56
78
|
bestX = i - rw + 1;
|
|
57
79
|
}
|
|
58
80
|
}
|
|
59
81
|
}
|
|
60
|
-
return { x: bestX, y: bestY };
|
|
82
|
+
return { x: bestX, y: bestY, first };
|
|
61
83
|
}
|
|
62
84
|
|
|
63
85
|
/** The options of `packSkyline` */
|
|
@@ -116,8 +138,10 @@ export function packSkyline<T>(rects: Rect<T>[], outer: Rect<T>, options: Skylin
|
|
|
116
138
|
y = 0;
|
|
117
139
|
for (let i = 0; i < width; i++) if (skyline[i]! > y) y = skyline[i]!;
|
|
118
140
|
} else {
|
|
119
|
-
|
|
141
|
+
const found = findWindow(skyline, width, rw);
|
|
142
|
+
({ x, y } = found);
|
|
120
143
|
if (outer.fixedSize && y + rh > outer.size.y) continue;
|
|
144
|
+
countWindow(found);
|
|
121
145
|
}
|
|
122
146
|
|
|
123
147
|
rect.position.set(x + outer.position.x, y + outer.position.y);
|
|
@@ -179,7 +203,8 @@ function packSkylineInSpace<T>(rects: Rect<T>[], outer: Rect<T>, order: SortOrde
|
|
|
179
203
|
outer.size.y = Math.max(outer.size.y, y + rh);
|
|
180
204
|
continue;
|
|
181
205
|
}
|
|
182
|
-
|
|
206
|
+
let found = findWindow(skyline, width, rw, limit, rh);
|
|
207
|
+
({ x, y } = found);
|
|
183
208
|
let sky: Float64Array = skyline;
|
|
184
209
|
let lim: Float64Array = limit;
|
|
185
210
|
if (!Number.isFinite(y)) {
|
|
@@ -189,10 +214,12 @@ function packSkylineInSpace<T>(rects: Rect<T>[], outer: Rect<T>, order: SortOrde
|
|
|
189
214
|
for (let i = 0; i < width; i++) {
|
|
190
215
|
while (Number.isFinite(lim[i]!) && lim[i]! - sky[i]! < rh && lim[i]! < end) [sky[i], lim[i]] = rangeOf(i, lim[i]!);
|
|
191
216
|
}
|
|
192
|
-
|
|
217
|
+
found = findWindow(sky, width, rw, lim, rh);
|
|
218
|
+
({ x, y } = found);
|
|
193
219
|
if (!Number.isFinite(y)) continue;
|
|
194
220
|
}
|
|
195
221
|
|
|
222
|
+
countWindow(found);
|
|
196
223
|
rect.position.set(x + ox, y + oy);
|
|
197
224
|
rect.wasPacked = true;
|
|
198
225
|
const top = y + rh;
|