@mark1russell7/packing 0.2.2 → 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 +4 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +101 -16
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters.ts +7 -4
- package/src/free.ts +3 -0
- package/src/measures.ts +24 -3
- package/src/moves.ts +33 -0
- package/src/pack.ts +15 -3
- package/src/shelf.ts +3 -0
- package/src/skyline.ts +34 -7
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/packing",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"description": "Rectangle packing: shelf, guillotine and skyline strategies, push cascades and measures of a layout.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"rectangle-packing",
|
package/src/adapters.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { EPS, type PackInput, type PackOutput } from "./contract.ts";
|
|
4
4
|
import { boundsOf, buildFrame, now, outerOf, readFrame, rectsOf, viaAxes, type Entry } from "./frame.ts";
|
|
5
5
|
import { packFree } from "./free.ts";
|
|
6
|
+
import { clearMoves, moveCount, MOVES } from "./moves.ts";
|
|
6
7
|
import type { SortOrder } from "./order.ts";
|
|
7
8
|
import { pack } from "./pack.ts";
|
|
8
9
|
import { pushCascade } from "./push.ts";
|
|
@@ -59,9 +60,10 @@ function packFixedWidth(id: "rows" | "skyline" | "shelf", input: PackInput, opti
|
|
|
59
60
|
const start = now();
|
|
60
61
|
const frame = buildFrame(input);
|
|
61
62
|
const rects = rectsOf(frame);
|
|
63
|
+
clearMoves();
|
|
62
64
|
if (frame.w > 0) pack(rects, outerOf(frame), { strategy: id, sortOrder: options.sortOrder, space: spaceOf(input, frame) });
|
|
63
65
|
const output = readFrame(input, frame, []);
|
|
64
|
-
return { ...output, trace: [{ id, ms: now() - start, measures: { "pack.overflow": output.overflow.length } }] };
|
|
66
|
+
return { ...output, trace: [{ id, ms: now() - start, measures: { "pack.overflow": output.overflow.length, [MOVES[id]]: moveCount(MOVES[id]) } }] };
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/** This function makes the strategy of one function of `pack`. */
|
|
@@ -150,7 +152,7 @@ export const logSweep: SizeSearch = {
|
|
|
150
152
|
...output,
|
|
151
153
|
trace: [
|
|
152
154
|
{ id: "log-sweep", ms, measures: { "pack.search.widths": result.tried.length, "pack.search.ms": ms } },
|
|
153
|
-
{ id, ms: ms / runs, measures: { "pack.overflow": output.overflow.length } },
|
|
155
|
+
{ id, ms: ms / runs, measures: { "pack.overflow": output.overflow.length, [MOVES[id]]: moveCount(MOVES[id]) } },
|
|
154
156
|
],
|
|
155
157
|
};
|
|
156
158
|
},
|
|
@@ -191,8 +193,9 @@ function pushFixedWidth(input: PackInput, options: PushStrategyOptions, previous
|
|
|
191
193
|
// The cascade: each item in the order of its rank pushes the others, and then it stays
|
|
192
194
|
const inPlay = frame.entries.filter((e) => !pending.includes(e)).map((e) => e.rect);
|
|
193
195
|
const maxShoves = options.maxShovesPerItem * Math.max(1, inPlay.length);
|
|
196
|
+
let cascaded = 0;
|
|
194
197
|
for (const e of positioned) {
|
|
195
|
-
pushCascade(e.rect, inPlay, { gap: 0, maxShoves });
|
|
198
|
+
cascaded += pushCascade(e.rect, inPlay, { gap: 0, maxShoves }).length;
|
|
196
199
|
e.rect.locked = true;
|
|
197
200
|
}
|
|
198
201
|
|
|
@@ -234,7 +237,7 @@ function pushFixedWidth(input: PackInput, options: PushStrategyOptions, previous
|
|
|
234
237
|
return {
|
|
235
238
|
...output,
|
|
236
239
|
trace: [
|
|
237
|
-
{ id: "push", ms: pushMs, measures: { "pack.push.moved": moved.length } },
|
|
240
|
+
{ id: "push", ms: pushMs, measures: { "pack.push.moved": moved.length, [MOVES.push]: cascaded } },
|
|
238
241
|
{ id: "skyline", ms: now() - skylineStart, measures: { "pack.push.replaced": pending.length, "pack.overflow": output.overflow.length } },
|
|
239
242
|
],
|
|
240
243
|
};
|
package/src/free.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearMoves } from "./moves.ts";
|
|
1
2
|
import { pack, type PackLayoutOptions } from "./pack.ts";
|
|
2
3
|
import { Rect } from "./rect.ts";
|
|
3
4
|
|
|
@@ -85,6 +86,8 @@ export function packFree<T>(rects: Rect<T>[], outer: Rect<T> = new Rect(), optio
|
|
|
85
86
|
|
|
86
87
|
const tried: FreeWidthTry[] = [];
|
|
87
88
|
const run = (width: number): FreeWidthTry => {
|
|
89
|
+
// The counts of the moves come from the last run: the run with the best width
|
|
90
|
+
clearMoves();
|
|
88
91
|
Rect.resetPacked(rects);
|
|
89
92
|
const box = new Rect<T>();
|
|
90
93
|
box.position.setTo(outer.position);
|
package/src/measures.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type { Direction } from "@mark1russell7/layout-contract";
|
|
|
13
13
|
export type MeasureFamily = "density" | "aspect" | "order" | "raggedness" | "overflow" | "stability" | "determinism" | "speed";
|
|
14
14
|
|
|
15
15
|
/** The id of a measure of the packer. The ids are those of `docs/FORMAT.md`, and `highways` uses the same ids. */
|
|
16
|
-
export type MeasureId = "pack.area" | "pack.fill" | "pack.whitespace" | "pack.aspect" | "pack.aspectError" | "pack.order.inversions" | "pack.order.breaks" | "pack.raggedness" | "pack.overflow" | "pack.overflow.rate" | "pack.overflow.area" | "pack.overflow.forced" | "pack.overflow.forcedArea" | "pack.overflow.excess" | "pack.stability.moved" | "pack.stability.mean" | "pack.stability.q95" | "pack.deterministic" | "pack.search.widths" | "pack.search.ms";
|
|
16
|
+
export type MeasureId = "pack.area" | "pack.fill" | "pack.whitespace" | "pack.aspect" | "pack.aspectError" | "pack.order.inversions" | "pack.order.breaks" | "pack.raggedness" | "pack.overflow" | "pack.overflow.rate" | "pack.overflow.area" | "pack.overflow.forced" | "pack.overflow.forcedArea" | "pack.overflow.excess" | "pack.overflow.forcedAreaMin" | "pack.overflow.excessArea" | "pack.stability.moved" | "pack.stability.mean" | "pack.stability.q95" | "pack.deterministic" | "pack.search.widths" | "pack.search.ms";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* The facts of one measure: its family, its unit, its direction and its range.
|
|
@@ -53,6 +53,8 @@ export interface LayoutMeasures {
|
|
|
53
53
|
"pack.overflow.forced": number;
|
|
54
54
|
"pack.overflow.forcedArea": number;
|
|
55
55
|
"pack.overflow.excess": number;
|
|
56
|
+
"pack.overflow.forcedAreaMin": number;
|
|
57
|
+
"pack.overflow.excessArea": number;
|
|
56
58
|
"pack.search.widths": number;
|
|
57
59
|
"pack.search.ms": number;
|
|
58
60
|
}
|
|
@@ -111,6 +113,8 @@ export const MEASURE_SPECS: Readonly<Record<MeasureId, MeasureSpec>> = Object.fr
|
|
|
111
113
|
"pack.overflow.forced": spec("pack.overflow.forced", "overflow", "count", "lower", 0, Infinity, 0),
|
|
112
114
|
"pack.overflow.forcedArea": spec("pack.overflow.forcedArea", "overflow", "share", "lower", 0, 1, 0.005),
|
|
113
115
|
"pack.overflow.excess": spec("pack.overflow.excess", "overflow", "count", "lower", 0, Infinity, 0),
|
|
116
|
+
"pack.overflow.forcedAreaMin": spec("pack.overflow.forcedAreaMin", "overflow", "share", "lower", 0, 1, 0.005),
|
|
117
|
+
"pack.overflow.excessArea": spec("pack.overflow.excessArea", "overflow", "share", "lower", 0, 1, 0.005),
|
|
114
118
|
"pack.stability.moved": spec("pack.stability.moved", "stability", "share", "lower", 0, 1, 0.02, { floor: 0, tol: 0.02 }),
|
|
115
119
|
"pack.stability.mean": spec("pack.stability.mean", "stability", "container widths", "lower", 0, Infinity, 0.005, { floor: 0, tol: 0.005 }),
|
|
116
120
|
"pack.stability.q95": spec("pack.stability.q95", "stability", "container widths", "lower", 0, Infinity, 0.01, { floor: 0, tol: 0.01 }),
|
|
@@ -205,6 +209,7 @@ function traceSum(output: PackOutput, id: string): number {
|
|
|
205
209
|
* - The time `pack.search.ms` comes from the trace. It is not part of the determinism rule.
|
|
206
210
|
* - The overflow measures count each id once. `pack.overflow.forced` is the lower bound of `overflowBound`.
|
|
207
211
|
* - `pack.overflow.excess` is the overflow less the forced overflow. A container with no fixed side gives 0 for both.
|
|
212
|
+
* - `pack.overflow.forcedAreaMin` is a lower bound of the overflow area. `pack.overflow.excessArea` is the overflow area above it.
|
|
208
213
|
*
|
|
209
214
|
* @example
|
|
210
215
|
* ```ts
|
|
@@ -257,6 +262,8 @@ export function measure(input: PackInput, output: PackOutput, options: MeasureOp
|
|
|
257
262
|
if (unplaced.has(item.id)) unplacedArea += a;
|
|
258
263
|
}
|
|
259
264
|
const bound = overflowBound(input);
|
|
265
|
+
const overflowArea = itemArea > 0 ? unplacedArea / itemArea : 0;
|
|
266
|
+
const forcedAreaMin = itemArea > 0 ? Math.min(1, bound.area / itemArea) : 0;
|
|
260
267
|
|
|
261
268
|
return {
|
|
262
269
|
"pack.area": hullArea,
|
|
@@ -269,15 +276,29 @@ export function measure(input: PackInput, output: PackOutput, options: MeasureOp
|
|
|
269
276
|
"pack.raggedness": hull.w > 0 ? spread / hull.w : 0,
|
|
270
277
|
"pack.overflow": unplaced.size,
|
|
271
278
|
"pack.overflow.rate": input.items.length > 0 ? unplaced.size / input.items.length : 0,
|
|
272
|
-
"pack.overflow.area":
|
|
279
|
+
"pack.overflow.area": overflowArea,
|
|
273
280
|
"pack.overflow.forced": bound.forced,
|
|
274
|
-
"pack.overflow.forcedArea":
|
|
281
|
+
"pack.overflow.forcedArea": forcedAreaMin,
|
|
275
282
|
"pack.overflow.excess": Math.max(0, unplaced.size - bound.forced),
|
|
283
|
+
"pack.overflow.forcedAreaMin": forcedAreaMin,
|
|
284
|
+
"pack.overflow.excessArea": excessAreaOf(overflowArea, forcedAreaMin),
|
|
276
285
|
"pack.search.widths": traceSum(output, "pack.search.widths"),
|
|
277
286
|
"pack.search.ms": traceSum(output, "pack.search.ms"),
|
|
278
287
|
};
|
|
279
288
|
}
|
|
280
289
|
|
|
290
|
+
/** The float noise of `pack.overflow.excessArea`. A difference below 0 by this value or less becomes 0. */
|
|
291
|
+
const AREA_NOISE = 1e-9;
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* This function gives the overflow area above its lower bound. A value from `-AREA_NOISE` to 0 becomes 0. A value below
|
|
295
|
+
* `-AREA_NOISE` stays, because it shows a bug of the bound or of the packer.
|
|
296
|
+
*/
|
|
297
|
+
function excessAreaOf(overflowArea: number, forcedAreaMin: number): number {
|
|
298
|
+
const excess = overflowArea - forcedAreaMin;
|
|
299
|
+
return excess < 0 && excess >= -AREA_NOISE ? 0 : excess;
|
|
300
|
+
}
|
|
301
|
+
|
|
281
302
|
/**
|
|
282
303
|
* This function gives the stability measures of a change pair: the run on the base input and the run on the changed
|
|
283
304
|
* input. The items match by id. The values are not rounded.
|
package/src/moves.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// The counts of the characteristic move of each strategy (roadmap item k59, `docs/BENCH.md`, "Activation"). The
|
|
2
|
+
// packers count their move here, and the adapters write the count in `trace.measures`. The counts do not change a layout.
|
|
3
|
+
// The module is internal: the package root does not export it.
|
|
4
|
+
|
|
5
|
+
/** The id of the characteristic move of each strategy. The trace of each strategy has its id, also with the count 0. */
|
|
6
|
+
export const MOVES: Readonly<{ rows: "move.gapFill"; shelf: "move.stack"; skyline: "move.lowerWindow"; push: "move.cascade" }> = Object.freeze({
|
|
7
|
+
/** A rectangle that the gap fill of `packRow` places in a gap of a row: under a shorter neighbor, or at the row break. */
|
|
8
|
+
rows: "move.gapFill",
|
|
9
|
+
/** A rectangle that `packShelf` puts on an open shelf with a taller rectangle. The strip above it stays empty. */
|
|
10
|
+
shelf: "move.stack",
|
|
11
|
+
/** A rectangle that the skyline puts in a lower window, not in the leftmost window where it fits. */
|
|
12
|
+
skyline: "move.lowerWindow",
|
|
13
|
+
/** A rectangle that a push cascade moves. */
|
|
14
|
+
push: "move.cascade",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const counts = new Map<string, number>();
|
|
18
|
+
|
|
19
|
+
/** This function adds `n` to the count of a move. */
|
|
20
|
+
export function countMove(id: string, n = 1): void {
|
|
21
|
+
if (n > 0) counts.set(id, (counts.get(id) ?? 0) + n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** This function sets the count of a move to 0. Without an id, it sets each count to 0. */
|
|
25
|
+
export function clearMoves(id?: string): void {
|
|
26
|
+
if (id === undefined) counts.clear();
|
|
27
|
+
else counts.delete(id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** This function gives the count of a move since the last `clearMoves`. */
|
|
31
|
+
export function moveCount(id: string): number {
|
|
32
|
+
return counts.get(id) ?? 0;
|
|
33
|
+
}
|
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;
|