@mark1russell7/packing 0.2.12 → 0.2.13

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/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.12",
4
+ "version": "0.2.13",
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
@@ -89,19 +89,41 @@ function inside(a: Box, b: Box): boolean {
89
89
  */
90
90
  function cut(free: Box[], b: Box, minW: number, minH: number): { free: Box[]; hits: number } {
91
91
  const kept: Box[] = [];
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[] = [];
92
+ // 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
93
+ // another side. A kept rectangle with the part in it has an edge on the same side of the box.
94
+ const near: Box[][] = [[], [], [], []];
95
+ // For each side of the box (0 left, 1 right, 2 top, 3 bottom), the parts that stay so far. Each part has five numbers
96
+ // in the list: its four edges and its index in the order of creation.
97
+ const stay: number[][] = [[], [], [], []];
98
+ let made = 0;
95
99
  let hits = 0;
100
+ // A part stays when no other part holds more than it, and no equal part comes before it. This rule does not use an order.
101
+ // Thus a new part goes when a part that stays holds it. Else it removes each part that it holds.
96
102
  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);
103
+ if (x1 - x0 < minW || y1 - y0 < minH) return;
104
+ const own = stay[side]!;
105
+ for (let k = 0; k < own.length; ) {
106
+ const qx0 = own[k]!;
107
+ const qy0 = own[k + 1]!;
108
+ const qx1 = own[k + 2]!;
109
+ const qy1 = own[k + 3]!;
110
+ if (x0 >= qx0 && y0 >= qy0 && x1 <= qx1 && y1 <= qy1) return;
111
+ if (qx0 >= x0 && qy0 >= y0 && qx1 <= x1 && qy1 <= y1) {
112
+ // The last part of the list takes the place of the removed part
113
+ const last = own.length - 5;
114
+ if (k !== last) for (let n = 0; n < 5; n++) own[k + n] = own[last + n]!;
115
+ own.length = last;
116
+ } else k += 5;
100
117
  }
118
+ own.push(x0, y0, x1, y1, made++);
101
119
  };
102
120
  for (const f of free) {
103
121
  if (b.x0 >= f.x1 || b.x1 <= f.x0 || b.y0 >= f.y1 || b.y1 <= f.y0) {
104
122
  kept.push(f);
123
+ if (f.x1 === b.x0) near[0]!.push(f);
124
+ if (f.x0 === b.x1) near[1]!.push(f);
125
+ if (f.y1 === b.y0) near[2]!.push(f);
126
+ if (f.y0 === b.y1) near[3]!.push(f);
105
127
  continue;
106
128
  }
107
129
  hits++;
@@ -110,62 +132,28 @@ function cut(free: Box[], b: Box, minW: number, minH: number): { free: Box[]; hi
110
132
  if (b.y0 > f.y0) add(2, f.x0, f.y0, f.x1, b.y0);
111
133
  if (b.y1 < f.y1) add(3, f.x0, b.y1, f.x1, f.y1);
112
134
  }
113
- if (parts.length === 0) return { free: kept, hits };
114
- // Each part is in the free rectangle that it comes from. Thus a kept rectangle is never in a part.
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
- }
142
- for (let i = 0; i < parts.length; i++) {
143
- if (gone[i] === 1) continue;
144
- const p = parts[i]!;
145
- let drop = false;
146
- for (const k of near[sides[i]!]!) {
147
- if (inside(p, k)) {
148
- drop = true;
149
- break;
135
+ if (made === 0) return { free: kept, hits };
136
+ // Each part is in the free rectangle that it comes from. Thus no kept rectangle is in a part.
137
+ // The parts that stay go to the list in the order of creation.
138
+ const out: (Box | undefined)[] = new Array<Box | undefined>(made).fill(undefined);
139
+ for (let side = 0; side < 4; side++) {
140
+ const own = stay[side]!;
141
+ for (let k = 0; k < own.length; k += 5) {
142
+ const p: Box = { x0: own[k]!, y0: own[k + 1]!, x1: own[k + 2]!, y1: own[k + 3]! };
143
+ let drop = false;
144
+ for (const q of near[side]!) {
145
+ if (inside(p, q)) {
146
+ drop = true;
147
+ break;
148
+ }
150
149
  }
150
+ if (!drop) out[own[k + 4]!] = p;
151
151
  }
152
- if (!drop) kept.push(p);
153
152
  }
153
+ for (const p of out) if (p !== undefined) kept.push(p);
154
154
  return { free: kept, hits };
155
155
  }
156
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
-
169
157
  /** The edges of the boxes on one axis: for each coordinate, the flat list of the ranges on the other axis. */
170
158
  type Edges = Map<number, number[]>;
171
159