@mark1russell7/packing 0.1.6 → 0.1.7
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 +27 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +49 -18
- package/build/index.js.map +1 -1
- package/package.json +2 -2
- package/src/check.ts +16 -9
- package/src/contract.ts +2 -0
- package/src/layout-input.ts +2 -0
- package/src/measures.ts +33 -15
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.1.
|
|
4
|
+
"version": "0.1.7",
|
|
5
5
|
"description": "Rectangle packing: shelf, guillotine and skyline strategies, push cascades and measures of a layout.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"rectangle-packing",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@mark1russell7/cue": "^1.0.2",
|
|
53
|
-
"@mark1russell7/layout-contract": "github:mark1russell7/layout-contract#
|
|
53
|
+
"@mark1russell7/layout-contract": "github:mark1russell7/layout-contract#710ec519027dbc122d111cc5e76a1c18877f4237&path:packages/layout-contract",
|
|
54
54
|
"@types/node": "^26.6.4",
|
|
55
55
|
"tsdown": "^0.23.1",
|
|
56
56
|
"typescript": "^7.0.2",
|
package/src/check.ts
CHANGED
|
@@ -28,8 +28,11 @@ function overlap(a: Box, b: Box, tolerance: number): boolean {
|
|
|
28
28
|
* - `no-overlap`: each pair of items with their margins, and each pair of an item and an obstacle, that overlap by more than `tolerance.overlap`.
|
|
29
29
|
* - `in-container`: each item with its margin that goes out of a fixed side by more than `tolerance.overlap`. The inset of
|
|
30
30
|
* the container makes each fixed side smaller (decision 3 of `docs/PACKER.md`).
|
|
31
|
-
* - `locked-kept`: each locked item that moved by more than `RECORD_EPS`, or that has no
|
|
32
|
-
* - `complete`: each item with no
|
|
31
|
+
* - `locked-kept`: each locked item that moved by more than `RECORD_EPS`, or that has no placement.
|
|
32
|
+
* - `complete`: each item with no placement that is not in the overflow. A locked item breaks `locked-kept` in its place.
|
|
33
|
+
*
|
|
34
|
+
* A locked item with no placement stays at its input position for `no-overlap` and `in-container`. A locked item with no
|
|
35
|
+
* `x` or no `y` in the input is not locked for these rules.
|
|
33
36
|
*
|
|
34
37
|
* The rules read a shape of the container, of an obstacle or of an item as its bounding box (decision 5). The bounding box
|
|
35
38
|
* of an item shape is relative to the position of the item. The result has no tolerance in it.
|
|
@@ -43,14 +46,18 @@ export function checkHard(input: PackInput, output: PackOutput, tolerance: Pick<
|
|
|
43
46
|
|
|
44
47
|
const boxes: Box[] = [];
|
|
45
48
|
for (const item of input.items) {
|
|
46
|
-
const
|
|
49
|
+
const lockedAt = item.locked === true && item.x !== undefined && item.y !== undefined ? { x: item.x, y: item.y } : undefined;
|
|
50
|
+
let p: Pick<PackOutput["placements"][number], "x" | "y" | "turn"> | undefined = placements.get(item.id);
|
|
47
51
|
if (p === undefined) {
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
if (lockedAt === undefined) {
|
|
53
|
+
if (!overflow.has(item.id)) breaks.complete += 1;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// A locked item with no placement breaks locked-kept, and it stays at its input position for the other rules
|
|
57
|
+
breaks["locked-kept"] += 1;
|
|
58
|
+
p = lockedAt;
|
|
59
|
+
} else if (lockedAt !== undefined) {
|
|
60
|
+
if (Math.abs(p.x - lockedAt.x) > RECORD_EPS || Math.abs(p.y - lockedAt.y) > RECORD_EPS) breaks["locked-kept"] += 1;
|
|
54
61
|
}
|
|
55
62
|
const placed: Item = p.turn === 90 ? turnItem(item) : item;
|
|
56
63
|
const m = marginOf(placed);
|
package/src/contract.ts
CHANGED
|
@@ -72,6 +72,8 @@ export interface Item {
|
|
|
72
72
|
margin?: number | Margin;
|
|
73
73
|
/** The ports. The packer keeps them, and turns them with the item. */
|
|
74
74
|
ports?: Port[];
|
|
75
|
+
/** The free data of the item. The packer does not read it, and the turn and the converters keep it. */
|
|
76
|
+
data?: Readonly<Record<string, unknown>>;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
/** The input of a packing: a `layout-input/1` file without `edges`, with `items` for `boxes`. */
|
package/src/layout-input.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface Edge {
|
|
|
11
11
|
target: string;
|
|
12
12
|
sourcePort?: string;
|
|
13
13
|
targetPort?: string;
|
|
14
|
+
/** The free data of the edge. The packer does not read it. */
|
|
15
|
+
data?: Readonly<Record<string, unknown>>;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/** The parameters of a generator: a name and a string or a number for each parameter. */
|
package/src/measures.ts
CHANGED
|
@@ -23,6 +23,20 @@ export interface MeasureSpec {
|
|
|
23
23
|
min: number;
|
|
24
24
|
/** The highest value. The value `Infinity` tells that the measure has no upper limit. */
|
|
25
25
|
max: number;
|
|
26
|
+
/**
|
|
27
|
+
* The floor is the best value of a guard measure. The floor rule of `docs/PROMOTION.md` of `highways` reads it. Each
|
|
28
|
+
* guard of the packer has the floor 0. A measure without a floor has no floor rule.
|
|
29
|
+
*/
|
|
30
|
+
floor?: number;
|
|
31
|
+
/**
|
|
32
|
+
* The tolerance is the absolute tolerance at the floor. A candidate within the tolerance ties with a default at the floor.
|
|
33
|
+
*
|
|
34
|
+
* - `pack.order.inversions` and `pack.overflow` have the tolerance 0. Thus each new inversion and each new unplaced item is a loss.
|
|
35
|
+
* - `pack.aspectError` has 0.01, approximately 1% of the aspect. An exact tiling gives the default an error of 0.
|
|
36
|
+
* - `pack.stability.mean` has 0.005 container widths, and `pack.stability.q95` has 0.01. The default often moves no item.
|
|
37
|
+
* - `pack.stability.moved` has 0.02: 2% of the items that stay. On a small change, the default often moves no item.
|
|
38
|
+
*/
|
|
39
|
+
tol?: number;
|
|
26
40
|
}
|
|
27
41
|
|
|
28
42
|
/** The measures of one layout, by id. `measure` gives them. The values are not rounded. */
|
|
@@ -56,29 +70,33 @@ export interface MeasureOptions {
|
|
|
56
70
|
rowTolerance?: number;
|
|
57
71
|
}
|
|
58
72
|
|
|
59
|
-
const spec = (
|
|
60
|
-
id,
|
|
61
|
-
family,
|
|
62
|
-
unit,
|
|
63
|
-
direction,
|
|
64
|
-
min,
|
|
65
|
-
max,
|
|
66
|
-
}
|
|
73
|
+
const spec = (
|
|
74
|
+
id: MeasureId,
|
|
75
|
+
family: MeasureFamily,
|
|
76
|
+
unit: string,
|
|
77
|
+
direction: Direction,
|
|
78
|
+
min: number,
|
|
79
|
+
max: number,
|
|
80
|
+
guard?: { floor: number; tol: number },
|
|
81
|
+
): MeasureSpec => ({ id, family, unit, direction, min, max, ...guard });
|
|
67
82
|
|
|
68
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* The facts of each measure, by id. `docs/MEASURES.md` gives the full definitions. This record is the source of the floor
|
|
85
|
+
* and the tolerance of each guard of the packer.
|
|
86
|
+
*/
|
|
69
87
|
export const MEASURE_SPECS: Readonly<Record<MeasureId, MeasureSpec>> = Object.freeze({
|
|
70
88
|
"pack.area": spec("pack.area", "density", "square units", "lower", 0, Infinity),
|
|
71
89
|
"pack.fill": spec("pack.fill", "density", "share", "higher", 0, 1),
|
|
72
90
|
"pack.whitespace": spec("pack.whitespace", "density", "share", "lower", 0, 1),
|
|
73
91
|
"pack.aspect": spec("pack.aspect", "aspect", "ratio", "lower", 1, Infinity),
|
|
74
|
-
"pack.aspectError": spec("pack.aspectError", "aspect", "natural log", "lower", 0, Infinity),
|
|
75
|
-
"pack.order.inversions": spec("pack.order.inversions", "order", "share", "lower", 0, 1),
|
|
92
|
+
"pack.aspectError": spec("pack.aspectError", "aspect", "natural log", "lower", 0, Infinity, { floor: 0, tol: 0.01 }),
|
|
93
|
+
"pack.order.inversions": spec("pack.order.inversions", "order", "share", "lower", 0, 1, { floor: 0, tol: 0 }),
|
|
76
94
|
"pack.order.breaks": spec("pack.order.breaks", "order", "count", "lower", 0, Infinity),
|
|
77
95
|
"pack.raggedness": spec("pack.raggedness", "raggedness", "share", "lower", 0, 1),
|
|
78
|
-
"pack.overflow": spec("pack.overflow", "overflow", "count", "lower", 0, Infinity),
|
|
79
|
-
"pack.stability.moved": spec("pack.stability.moved", "stability", "share", "lower", 0, 1),
|
|
80
|
-
"pack.stability.mean": spec("pack.stability.mean", "stability", "container widths", "lower", 0, Infinity),
|
|
81
|
-
"pack.stability.q95": spec("pack.stability.q95", "stability", "container widths", "lower", 0, Infinity),
|
|
96
|
+
"pack.overflow": spec("pack.overflow", "overflow", "count", "lower", 0, Infinity, { floor: 0, tol: 0 }),
|
|
97
|
+
"pack.stability.moved": spec("pack.stability.moved", "stability", "share", "lower", 0, 1, { floor: 0, tol: 0.02 }),
|
|
98
|
+
"pack.stability.mean": spec("pack.stability.mean", "stability", "container widths", "lower", 0, Infinity, { floor: 0, tol: 0.005 }),
|
|
99
|
+
"pack.stability.q95": spec("pack.stability.q95", "stability", "container widths", "lower", 0, Infinity, { floor: 0, tol: 0.01 }),
|
|
82
100
|
"pack.deterministic": spec("pack.deterministic", "determinism", "boolean", "higher", 0, 1),
|
|
83
101
|
"pack.search.widths": spec("pack.search.widths", "speed", "count", "lower", 0, Infinity),
|
|
84
102
|
"pack.search.ms": spec("pack.search.ms", "speed", "milliseconds", "lower", 0, Infinity),
|