@mark1russell7/packing 0.1.7 → 0.2.0
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/README.md +1 -1
- package/build/index.d.ts +46 -219
- package/build/index.d.ts.map +1 -1
- package/build/index.js +166 -279
- package/build/index.js.map +1 -1
- package/package.json +5 -2
- package/src/adapters.ts +16 -13
- package/src/check.ts +34 -27
- package/src/contract.ts +30 -192
- package/src/convert.ts +11 -15
- package/src/corpus.ts +3 -3
- package/src/frame.ts +37 -50
- package/src/generators.ts +11 -12
- package/src/index.ts +7 -3
- package/src/layout-input.ts +34 -43
- package/src/measures.ts +12 -15
- package/src/strategy.ts +3 -3
package/src/generators.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// container, then the obstacles, then the boxes. Each step has its own random numbers: `mulberry32` of
|
|
3
3
|
// `fnv1a32("<seed>:<step>")`. Thus a packing input and a routing input with the same seed get the same obstacles.
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { Item, LayoutContainer, Obstacle, Region } from "./contract.ts";
|
|
6
6
|
import {
|
|
7
7
|
countCell,
|
|
8
8
|
fnv1a32,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
round3,
|
|
11
11
|
type CorpusCell,
|
|
12
12
|
type GeneratorParams,
|
|
13
|
-
type
|
|
13
|
+
type GeneratedInput,
|
|
14
14
|
type ObstacleLayout,
|
|
15
15
|
type SizeDistribution,
|
|
16
16
|
} from "./layout-input.ts";
|
|
@@ -36,11 +36,11 @@ export interface CorpusGenerator<P extends GeneratorParams = GeneratorParams> {
|
|
|
36
36
|
/** This method gives the cell of each input of the parameters. The cell does not depend on the seed. */
|
|
37
37
|
cell(params?: Partial<P>): CorpusCell;
|
|
38
38
|
/** This method gives the input file of a seed. The `id` is `<set>/<family>/s<seed>`. */
|
|
39
|
-
generate(seed: number, params?: Partial<P>, set?: string):
|
|
39
|
+
generate(seed: number, params?: Partial<P>, set?: string): GeneratedInput;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/** The parts of a file that a generator makes. */
|
|
43
|
-
type Parts = Pick<
|
|
43
|
+
type Parts = Pick<GeneratedInput, "container" | "boxes"> & { obstacles: Obstacle[] };
|
|
44
44
|
|
|
45
45
|
/** The data of a generator, before `defineGenerator` adds the common steps. */
|
|
46
46
|
interface GeneratorSpec<P extends GeneratorParams> {
|
|
@@ -153,12 +153,12 @@ function defineGenerator<P extends GeneratorParams>(spec: GeneratorSpec<P>): Cor
|
|
|
153
153
|
values: spec.values,
|
|
154
154
|
withDefaults,
|
|
155
155
|
cell: (params?: Partial<P>): CorpusCell => spec.cell(withDefaults(params)),
|
|
156
|
-
generate(seed: number, params?: Partial<P>, set: string = spec.set):
|
|
156
|
+
generate(seed: number, params?: Partial<P>, set: string = spec.set): GeneratedInput {
|
|
157
157
|
if (!Number.isInteger(seed) || seed < 0) throw new RangeError(`The seed is a whole number from 0. It is ${seed}.`);
|
|
158
158
|
const full = withDefaults(params);
|
|
159
159
|
const family = spec.family(full);
|
|
160
160
|
const { container, obstacles, boxes } = spec.build(seed, full);
|
|
161
|
-
const file: Omit<
|
|
161
|
+
const file: Omit<GeneratedInput, "hash"> = roundNumbers({
|
|
162
162
|
schema: "layout-input/1" as const,
|
|
163
163
|
id: `${set}/${family}/s${seed}`,
|
|
164
164
|
set,
|
|
@@ -195,7 +195,7 @@ export const rectScattered: CorpusGenerator<RectScatteredParams> = defineGenerat
|
|
|
195
195
|
let r = stepRandom(seed, "container");
|
|
196
196
|
const w = int(r, 600, 1000);
|
|
197
197
|
const h = int(r, 400, 800);
|
|
198
|
-
const container:
|
|
198
|
+
const container: LayoutContainer = { w, h, fixed: "size" };
|
|
199
199
|
|
|
200
200
|
r = stepRandom(seed, "obstacles");
|
|
201
201
|
const n = int(r, 3, 8);
|
|
@@ -232,8 +232,8 @@ const OBSTACLE_LAYOUTS = ["none", "center", "scattered", "wall", "corridors", "t
|
|
|
232
232
|
const SIZE_DISTRIBUTIONS = ["identical", "nearIdentical", "uniform", "heavyTail", "bimodal", "extremeAspect"] as const;
|
|
233
233
|
|
|
234
234
|
/** This function gives the container of a size value for the nominal rectangle `w` by `h`, with an optional shape. */
|
|
235
|
-
function containerOf(size: DimensionValue<"containerSize">, w: number, h: number, shape?: Region):
|
|
236
|
-
const containers: Record<DimensionValue<"containerSize">,
|
|
235
|
+
function containerOf(size: DimensionValue<"containerSize">, w: number, h: number, shape?: Region): LayoutContainer {
|
|
236
|
+
const containers: Record<DimensionValue<"containerSize">, LayoutContainer> = {
|
|
237
237
|
fixed: { w, h, fixed: "size" },
|
|
238
238
|
freeHeight: { w, fixed: "width" },
|
|
239
239
|
freeWidth: { h, fixed: "height" },
|
|
@@ -241,8 +241,7 @@ function containerOf(size: DimensionValue<"containerSize">, w: number, h: number
|
|
|
241
241
|
freeArea: { fixed: "none", aspectWeight: 0 },
|
|
242
242
|
};
|
|
243
243
|
const container = containers[size];
|
|
244
|
-
|
|
245
|
-
return container;
|
|
244
|
+
return shape === undefined ? container : { ...container, shape };
|
|
246
245
|
}
|
|
247
246
|
|
|
248
247
|
/** This function gives the 4 corners of a rectangle, clockwise from the top left. */
|
|
@@ -625,7 +624,7 @@ export const edgeCases: CorpusGenerator<EdgeCaseParams> = defineGenerator<EdgeCa
|
|
|
625
624
|
w = int(r, 600, 1000);
|
|
626
625
|
h = int(r, 400, 800);
|
|
627
626
|
}
|
|
628
|
-
const container:
|
|
627
|
+
const container: LayoutContainer = { w, h, fixed: "size" };
|
|
629
628
|
|
|
630
629
|
r = stepRandom(seed, "obstacles");
|
|
631
630
|
const obstacles: Obstacle[] = [];
|
package/src/index.ts
CHANGED
|
@@ -29,13 +29,14 @@ export {
|
|
|
29
29
|
DEFAULT_TOLERANCE,
|
|
30
30
|
EPS,
|
|
31
31
|
RECORD_EPS,
|
|
32
|
+
checkInput,
|
|
32
33
|
marginOf,
|
|
33
34
|
turnItem,
|
|
34
35
|
turnPoint,
|
|
35
36
|
turnPort,
|
|
36
37
|
turnRegion,
|
|
37
|
-
type Container,
|
|
38
38
|
type Item,
|
|
39
|
+
type LayoutContainer,
|
|
39
40
|
type Margin,
|
|
40
41
|
type Obstacle,
|
|
41
42
|
type PackInput,
|
|
@@ -43,6 +44,8 @@ export {
|
|
|
43
44
|
type Placement,
|
|
44
45
|
type Port,
|
|
45
46
|
type Region,
|
|
47
|
+
type Side,
|
|
48
|
+
type Sides,
|
|
46
49
|
type StageTrace,
|
|
47
50
|
type Tolerance,
|
|
48
51
|
} from "./contract.ts";
|
|
@@ -65,7 +68,7 @@ export {
|
|
|
65
68
|
export { logSweep, push, rows, shelf, skyline, type LayoutStrategyOptions, type PushStrategyOptions } from "./adapters.ts";
|
|
66
69
|
export { fromRects, toRects } from "./convert.ts";
|
|
67
70
|
export { packWith, strategies, strategy } from "./registry.ts";
|
|
68
|
-
export { checkHard, type
|
|
71
|
+
export { checkHard, type PackHard } from "./check.ts";
|
|
69
72
|
export {
|
|
70
73
|
canonicalJson,
|
|
71
74
|
countCell,
|
|
@@ -75,9 +78,10 @@ export {
|
|
|
75
78
|
toPackInput,
|
|
76
79
|
type CorpusCell,
|
|
77
80
|
type CountScale,
|
|
78
|
-
type
|
|
81
|
+
type GeneratedInput,
|
|
79
82
|
type GeneratorParams,
|
|
80
83
|
type GeneratorStamp,
|
|
84
|
+
type LayoutEdge,
|
|
81
85
|
type LayoutInput,
|
|
82
86
|
type ObstacleLayout,
|
|
83
87
|
type SizeDistribution,
|
package/src/layout-input.ts
CHANGED
|
@@ -1,52 +1,39 @@
|
|
|
1
1
|
// The input file `layout-input/1` of `docs/FORMAT.md`: its types, its numbers, its canonical form and its hash.
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { LayoutContainer, LayoutEdge, LayoutGenerator, LayoutInput } from "@mark1russell7/layout-contract";
|
|
4
|
+
import type { PackInput } from "./contract.ts";
|
|
4
5
|
import { sha256Hex, utf8 } from "./sha256.ts";
|
|
5
6
|
import type { Cell } from "./strategy.ts";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
export interface Edge {
|
|
9
|
-
id: string;
|
|
10
|
-
source: string;
|
|
11
|
-
target: string;
|
|
12
|
-
sourcePort?: string;
|
|
13
|
-
targetPort?: string;
|
|
14
|
-
/** The free data of the edge. The packer does not read it. */
|
|
15
|
-
data?: Readonly<Record<string, unknown>>;
|
|
16
|
-
}
|
|
8
|
+
export type { LayoutEdge, LayoutInput } from "@mark1russell7/layout-contract";
|
|
17
9
|
|
|
18
10
|
/** The parameters of a generator: a name and a string or a number for each parameter. */
|
|
19
11
|
export type GeneratorParams = Record<string, string | number>;
|
|
20
12
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The stamp of a generated input: the generator, its version, the full parameters and the seed.
|
|
15
|
+
* It is `LayoutGenerator` of the contract with a string or a number for each parameter.
|
|
16
|
+
*/
|
|
17
|
+
export interface GeneratorStamp extends LayoutGenerator {
|
|
18
|
+
readonly params: Readonly<GeneratorParams>;
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
/**
|
|
22
|
+
* A generated input file `layout-input/1`. It is a `LayoutInput` with each field that a generator writes.
|
|
23
|
+
* `docs/FORMAT.md` gives each field.
|
|
24
|
+
*/
|
|
25
|
+
export interface GeneratedInput extends LayoutInput {
|
|
26
|
+
readonly set: string;
|
|
27
|
+
readonly family: string;
|
|
28
|
+
readonly seed: number;
|
|
36
29
|
/** The origin: `made-up`, `published:<set>` or `known-best:<construction>`. */
|
|
37
|
-
source: string;
|
|
30
|
+
readonly source: string;
|
|
38
31
|
/** The generator of a generated input. A hand-made or published input has `null`. */
|
|
39
|
-
generator: GeneratorStamp | null;
|
|
40
|
-
container:
|
|
41
|
-
|
|
42
|
-
gap?: number;
|
|
43
|
-
boxes: Item[];
|
|
44
|
-
edges: Edge[];
|
|
45
|
-
base?: string;
|
|
46
|
-
change?: string;
|
|
47
|
-
step?: number;
|
|
32
|
+
readonly generator: GeneratorStamp | null;
|
|
33
|
+
readonly container: LayoutContainer;
|
|
34
|
+
readonly edges: readonly LayoutEdge[];
|
|
48
35
|
/** The first 16 hex characters of the SHA-256 of the canonical form of the other fields. */
|
|
49
|
-
hash: string;
|
|
36
|
+
readonly hash: string;
|
|
50
37
|
}
|
|
51
38
|
|
|
52
39
|
/** The layout of the obstacles (`docs/research/TAXONOMY.md`, dimension 3). */
|
|
@@ -98,9 +85,8 @@ export function canonicalJson(value: unknown): string {
|
|
|
98
85
|
}
|
|
99
86
|
|
|
100
87
|
/** This function gives the hash of an input file: the first 16 hex characters of the SHA-256 of its canonical form without `hash`. */
|
|
101
|
-
export function hashInput(file:
|
|
102
|
-
const
|
|
103
|
-
delete rest.hash;
|
|
88
|
+
export function hashInput(file: LayoutInput): string {
|
|
89
|
+
const { hash: _hash, ...rest } = file;
|
|
104
90
|
return sha256Hex(canonicalJson(rest)).slice(0, 16);
|
|
105
91
|
}
|
|
106
92
|
|
|
@@ -113,10 +99,15 @@ export function countCell(count: number): CountScale {
|
|
|
113
99
|
return "10k";
|
|
114
100
|
}
|
|
115
101
|
|
|
116
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* This function gives the packing input of a file. The boxes become the items, and the edges go.
|
|
104
|
+
* A file without a container gets `{ fixed: "none" }`, so the packer picks the size. It is `toPackInput` of the contract.
|
|
105
|
+
*/
|
|
117
106
|
export function toPackInput(file: Pick<LayoutInput, "container" | "boxes" | "obstacles" | "gap">): PackInput {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
107
|
+
return {
|
|
108
|
+
container: file.container ?? { fixed: "none" },
|
|
109
|
+
items: file.boxes,
|
|
110
|
+
...(file.obstacles === undefined ? {} : { obstacles: file.obstacles }),
|
|
111
|
+
...(file.gap === undefined ? {} : { gap: file.gap }),
|
|
112
|
+
};
|
|
122
113
|
}
|
package/src/measures.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// The algebra of measures (roadmap item k8). `docs/MEASURES.md` defines each measure. This file computes them.
|
|
2
2
|
|
|
3
|
+
import type { MeasureSpec as ContractMeasureSpec, Direction } from "@mark1russell7/layout-contract";
|
|
3
4
|
import { RECORD_EPS, type PackInput, type PackOutput } from "./contract.ts";
|
|
4
5
|
import { aspectError } from "./free.ts";
|
|
5
6
|
import { round3 } from "./layout-input.ts";
|
|
6
7
|
|
|
7
|
-
/** The direction of a measure: `lower` when a lower value is better, `higher` when a higher value is better. */
|
|
8
|
-
export type Direction
|
|
8
|
+
/** The direction of a measure comes from the contract: `lower` when a lower value is better, `higher` when a higher value is better. */
|
|
9
|
+
export type { Direction } from "@mark1russell7/layout-contract";
|
|
9
10
|
|
|
10
11
|
/** The family of a measure. Each family has a section in `docs/MEASURES.md`. */
|
|
11
12
|
export type MeasureFamily = "density" | "aspect" | "order" | "raggedness" | "overflow" | "stability" | "determinism" | "speed";
|
|
@@ -13,21 +14,17 @@ export type MeasureFamily = "density" | "aspect" | "order" | "raggedness" | "ove
|
|
|
13
14
|
/** The id of a measure of the packer. The ids are those of `docs/FORMAT.md`, and `highways` uses the same ids. */
|
|
14
15
|
export type MeasureId = "pack.area" | "pack.fill" | "pack.whitespace" | "pack.aspect" | "pack.aspectError" | "pack.order.inversions" | "pack.order.breaks" | "pack.raggedness" | "pack.overflow" | "pack.stability.moved" | "pack.stability.mean" | "pack.stability.q95" | "pack.deterministic" | "pack.search.widths" | "pack.search.ms";
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/** The lowest value. */
|
|
23
|
-
min: number;
|
|
24
|
-
/** The highest value. The value `Infinity` tells that the measure has no upper limit. */
|
|
25
|
-
max: number;
|
|
17
|
+
/**
|
|
18
|
+
* The facts of one measure: its family, its unit, its direction and its range.
|
|
19
|
+
* It is `MeasureSpec` of the contract for a packer measure id, with a family.
|
|
20
|
+
*/
|
|
21
|
+
export interface MeasureSpec extends ContractMeasureSpec<MeasureId> {
|
|
22
|
+
readonly family: MeasureFamily;
|
|
26
23
|
/**
|
|
27
24
|
* The floor is the best value of a guard measure. The floor rule of `docs/PROMOTION.md` of `highways` reads it. Each
|
|
28
25
|
* guard of the packer has the floor 0. A measure without a floor has no floor rule.
|
|
29
26
|
*/
|
|
30
|
-
floor?: number;
|
|
27
|
+
readonly floor?: number;
|
|
31
28
|
/**
|
|
32
29
|
* The tolerance is the absolute tolerance at the floor. A candidate within the tolerance ties with a default at the floor.
|
|
33
30
|
*
|
|
@@ -36,7 +33,7 @@ export interface MeasureSpec {
|
|
|
36
33
|
* - `pack.stability.mean` has 0.005 container widths, and `pack.stability.q95` has 0.01. The default often moves no item.
|
|
37
34
|
* - `pack.stability.moved` has 0.02: 2% of the items that stay. On a small change, the default often moves no item.
|
|
38
35
|
*/
|
|
39
|
-
tol?: number;
|
|
36
|
+
readonly tol?: number;
|
|
40
37
|
}
|
|
41
38
|
|
|
42
39
|
/** The measures of one layout, by id. `measure` gives them. The values are not rounded. */
|
|
@@ -175,7 +172,7 @@ export function readsBefore(a: Pick<Box, "x" | "y" | "h">, b: Pick<Box, "x" | "y
|
|
|
175
172
|
/** This function sums one measure over the stages of a trace. A trace without the measure gives 0. */
|
|
176
173
|
function traceSum(output: PackOutput, id: string): number {
|
|
177
174
|
let sum = 0;
|
|
178
|
-
for (const stage of output.trace) sum += stage.measures[id] ?? 0;
|
|
175
|
+
for (const stage of output.trace ?? []) sum += stage.measures?.[id] ?? 0;
|
|
179
176
|
return sum;
|
|
180
177
|
}
|
|
181
178
|
|
package/src/strategy.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LayoutContainer, PackInput, PackOutput, Region } from "./contract.ts";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* For each dimension of the taxonomy, the values that a strategy supports. `docs/research/TAXONOMY.md` gives the values.
|
|
@@ -108,7 +108,7 @@ export function createContext(seed = 0, more: Omit<PackContext, "seed" | "rng">
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/** This function tells if a polygon ring is convex. A ring with fewer than 3 points is convex. */
|
|
111
|
-
function isConvex(points: readonly [number, number][]): boolean {
|
|
111
|
+
function isConvex(points: readonly (readonly [number, number])[]): boolean {
|
|
112
112
|
let sign = 0;
|
|
113
113
|
const n = points.length;
|
|
114
114
|
for (let i = 0; i < n; i++) {
|
|
@@ -163,7 +163,7 @@ export function cellOf(input: PackInput): Cell {
|
|
|
163
163
|
const { container, items } = input;
|
|
164
164
|
const obstacles = input.obstacles ?? [];
|
|
165
165
|
|
|
166
|
-
const sizes: Record<
|
|
166
|
+
const sizes: Record<LayoutContainer["fixed"], DimensionValue<"containerSize">> = {
|
|
167
167
|
size: "fixed",
|
|
168
168
|
width: "freeHeight",
|
|
169
169
|
height: "freeWidth",
|