@mark1russell7/packing 0.2.10 → 0.2.11
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.map +1 -1
- package/build/index.js +80 -25
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/maxrects.ts +67 -17
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.11",
|
|
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/maxrects.ts
CHANGED
|
@@ -90,9 +90,14 @@ function inside(a: Box, b: Box): boolean {
|
|
|
90
90
|
function cut(free: Box[], b: Box, minW: number, minH: number): { free: Box[]; hits: number } {
|
|
91
91
|
const kept: Box[] = [];
|
|
92
92
|
const parts: Box[] = [];
|
|
93
|
+
// The side of the box at each part: 0 left, 1 right, 2 top, 3 bottom
|
|
94
|
+
const sides: number[] = [];
|
|
93
95
|
let hits = 0;
|
|
94
|
-
const add = (x0: number, y0: number, x1: number, y1: number): void => {
|
|
95
|
-
if (x1 - x0 >= minW && y1 - y0 >= minH)
|
|
96
|
+
const add = (side: number, x0: number, y0: number, x1: number, y1: number): void => {
|
|
97
|
+
if (x1 - x0 >= minW && y1 - y0 >= minH) {
|
|
98
|
+
parts.push({ x0, y0, x1, y1 });
|
|
99
|
+
sides.push(side);
|
|
100
|
+
}
|
|
96
101
|
};
|
|
97
102
|
for (const f of free) {
|
|
98
103
|
if (b.x0 >= f.x1 || b.x1 <= f.x0 || b.y0 >= f.y1 || b.y1 <= f.y0) {
|
|
@@ -100,28 +105,67 @@ function cut(free: Box[], b: Box, minW: number, minH: number): { free: Box[]; hi
|
|
|
100
105
|
continue;
|
|
101
106
|
}
|
|
102
107
|
hits++;
|
|
103
|
-
if (b.x0 > f.x0) add(f.x0, f.y0, b.x0, f.y1);
|
|
104
|
-
if (b.x1 < f.x1) add(b.x1, f.y0, f.x1, f.y1);
|
|
105
|
-
if (b.y0 > f.y0) add(f.x0, f.y0, f.x1, b.y0);
|
|
106
|
-
if (b.y1 < f.y1) add(f.x0, b.y1, f.x1, f.y1);
|
|
108
|
+
if (b.x0 > f.x0) add(0, f.x0, f.y0, b.x0, f.y1);
|
|
109
|
+
if (b.x1 < f.x1) add(1, b.x1, f.y0, f.x1, f.y1);
|
|
110
|
+
if (b.y0 > f.y0) add(2, f.x0, f.y0, f.x1, b.y0);
|
|
111
|
+
if (b.y1 < f.y1) add(3, f.x0, b.y1, f.x1, f.y1);
|
|
107
112
|
}
|
|
113
|
+
if (parts.length === 0) return { free: kept, hits };
|
|
108
114
|
// Each part is in the free rectangle that it comes from. Thus a kept rectangle is never in a part.
|
|
109
|
-
|
|
115
|
+
// A part spans the box on one axis and touches the box on the other axis. Thus a part of one side is not in a part of
|
|
116
|
+
// another side. A kept rectangle with the part in it has an edge on the same side of the box.
|
|
117
|
+
const near: Box[][] = [[], [], [], []];
|
|
118
|
+
for (const k of kept) {
|
|
119
|
+
if (k.x1 === b.x0) near[0]!.push(k);
|
|
120
|
+
if (k.x0 === b.x1) near[1]!.push(k);
|
|
121
|
+
if (k.y1 === b.y0) near[2]!.push(k);
|
|
122
|
+
if (k.y0 === b.y1) near[3]!.push(k);
|
|
123
|
+
}
|
|
124
|
+
// In the order of `byExtent`, a part comes after each part that holds it. Of two equal parts, the first part stays.
|
|
125
|
+
// Thus a part goes when a part that stays before it holds it.
|
|
126
|
+
const order = parts.map((_, i) => i).sort((i, j) => byExtent(parts[i]!, parts[j]!) || i - j);
|
|
127
|
+
const gone = new Uint8Array(parts.length);
|
|
128
|
+
const stay: Box[][] = [[], [], [], []];
|
|
129
|
+
for (const i of order) {
|
|
130
|
+
const p = parts[i]!;
|
|
131
|
+
const own = stay[sides[i]!]!;
|
|
132
|
+
let drop = false;
|
|
133
|
+
for (const q of own) {
|
|
134
|
+
if (inside(p, q)) {
|
|
135
|
+
drop = true;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (drop) gone[i] = 1;
|
|
140
|
+
else own.push(p);
|
|
141
|
+
}
|
|
110
142
|
for (let i = 0; i < parts.length; i++) {
|
|
143
|
+
if (gone[i] === 1) continue;
|
|
111
144
|
const p = parts[i]!;
|
|
112
145
|
let drop = false;
|
|
113
|
-
for (
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
146
|
+
for (const k of near[sides[i]!]!) {
|
|
147
|
+
if (inside(p, k)) {
|
|
148
|
+
drop = true;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
118
151
|
}
|
|
119
|
-
for (let k = 0; k < count && !drop; k++) if (inside(p, kept[k]!)) drop = true;
|
|
120
152
|
if (!drop) kept.push(p);
|
|
121
153
|
}
|
|
122
154
|
return { free: kept, hits };
|
|
123
155
|
}
|
|
124
156
|
|
|
157
|
+
/**
|
|
158
|
+
* This function orders two boxes. The smaller `x0` comes first, then the smaller `y0`, then the larger `x1`, then the
|
|
159
|
+
* larger `y1`. Thus a box comes before each other box in it.
|
|
160
|
+
*/
|
|
161
|
+
function byExtent(a: Box, b: Box): number {
|
|
162
|
+
if (a.x0 !== b.x0) return a.x0 < b.x0 ? -1 : 1;
|
|
163
|
+
if (a.y0 !== b.y0) return a.y0 < b.y0 ? -1 : 1;
|
|
164
|
+
if (a.x1 !== b.x1) return a.x1 > b.x1 ? -1 : 1;
|
|
165
|
+
if (a.y1 !== b.y1) return a.y1 > b.y1 ? -1 : 1;
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
125
169
|
/** The edges of the boxes on one axis: for each coordinate, the flat list of the ranges on the other axis. */
|
|
126
170
|
type Edges = Map<number, number[]>;
|
|
127
171
|
|
|
@@ -315,10 +359,16 @@ export function runMaxRects<T>(rects: Rect<T>[], outer: Rect<T>, options: MaxRec
|
|
|
315
359
|
s2 = f.y0 + bh;
|
|
316
360
|
break;
|
|
317
361
|
}
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (
|
|
362
|
+
const x = f.x0;
|
|
363
|
+
const y = f.y0;
|
|
364
|
+
// A choice object comes only for a better place
|
|
365
|
+
if (best === undefined || s1 < best.s1 || (s1 === best.s1 && (s2 < best.s2 || (s2 === best.s2 && (y < best.y || (y === best.y && x < best.x)))))) {
|
|
366
|
+
best = { index: i, turn: t === 1, x, y, w: bw, h: bh, s1, s2 };
|
|
367
|
+
}
|
|
368
|
+
// Only the move count of a rule other than `bl` reads the place of `bl`
|
|
369
|
+
if (rule === "bl") continue;
|
|
370
|
+
const ly = y + bh;
|
|
371
|
+
if (low === undefined || ly < low.s1 || (ly === low.s1 && x < low.s2)) low = { index: i, turn: t === 1, x, y, w: bw, h: bh, s1: ly, s2: x };
|
|
322
372
|
}
|
|
323
373
|
}
|
|
324
374
|
if (best === undefined || !check || space.fits({ x: best.x + ox, y: best.y + oy, w: best.w, h: best.h })) break;
|