@mark1russell7/packing 0.2.10 → 0.2.12

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.js CHANGED
@@ -369,27 +369,38 @@ function readFrame(input, frame, trace, moved = [], turns) {
369
369
  };
370
370
  }
371
371
  /**
372
- * This function gives the size of the container after a packing. A fixed side keeps its size. A free side goes to the
373
- * far edge of the items with their margins and the inset, or of the obstacles.
372
+ * This function gives the far edges of a packing from the corner of the container. It reads the items with their margins,
373
+ * the inset and the obstacles. With `turn`, a placement with `turn: 90` reads the turned item.
374
374
  */
375
- function sizeOf(input, frame, placements, moved, turns) {
375
+ function extentOf(input, placements, moved, origin = containerBounds(input), inset = input.container.inset ?? 0, turn) {
376
376
  const items = new Map(input.items.map((i) => [i.id, i]));
377
377
  let right = 0;
378
378
  let bottom = 0;
379
379
  for (const p of placements) {
380
380
  const given = items.get(p.id);
381
- const item = p.turn === 90 && turns !== void 0 ? turns.turn(given) : given;
381
+ const item = p.turn === 90 && turn !== void 0 ? turn(given) : given;
382
382
  const m = marginOf(item);
383
- right = Math.max(right, p.x + item.w + m.right + frame.inset - frame.bounds.x);
384
- bottom = Math.max(bottom, p.y + item.h + m.bottom + frame.inset - frame.bounds.y);
383
+ right = Math.max(right, p.x + item.w + m.right + inset - origin.x);
384
+ bottom = Math.max(bottom, p.y + item.h + m.bottom + inset - origin.y);
385
385
  }
386
386
  const movedById = new Map(moved.map((m) => [m.id, m]));
387
387
  for (const o of input.obstacles ?? []) {
388
388
  const b = boundsOf(o.region);
389
389
  const at = movedById.get(o.id) ?? b;
390
- right = Math.max(right, at.x + b.w - frame.bounds.x);
391
- bottom = Math.max(bottom, at.y + b.h - frame.bounds.y);
390
+ right = Math.max(right, at.x + b.w - origin.x);
391
+ bottom = Math.max(bottom, at.y + b.h - origin.y);
392
392
  }
393
+ return {
394
+ right,
395
+ bottom
396
+ };
397
+ }
398
+ /**
399
+ * This function gives the size of the container after a packing. A fixed side keeps its size. A free side goes to the
400
+ * far edge of the items with their margins and the inset, or of the obstacles.
401
+ */
402
+ function sizeOf(input, frame, placements, moved, turns) {
403
+ const { right, bottom } = extentOf(input, placements, moved, frame.bounds, frame.inset, turns?.turn);
393
404
  return {
394
405
  w: Number.isFinite(frame.bounds.w) ? frame.bounds.w : right,
395
406
  h: Number.isFinite(frame.bounds.h) ? frame.bounds.h : bottom
@@ -868,14 +879,18 @@ function inside(a, b) {
868
879
  function cut(free, b, minW, minH) {
869
880
  const kept = [];
870
881
  const parts = [];
882
+ const sides = [];
871
883
  let hits = 0;
872
- const add = (x0, y0, x1, y1) => {
873
- if (x1 - x0 >= minW && y1 - y0 >= minH) parts.push({
874
- x0,
875
- y0,
876
- x1,
877
- y1
878
- });
884
+ const add = (side, x0, y0, x1, y1) => {
885
+ if (x1 - x0 >= minW && y1 - y0 >= minH) {
886
+ parts.push({
887
+ x0,
888
+ y0,
889
+ x1,
890
+ y1
891
+ });
892
+ sides.push(side);
893
+ }
879
894
  };
880
895
  for (const f of free) {
881
896
  if (b.x0 >= f.x1 || b.x1 <= f.x0 || b.y0 >= f.y1 || b.y1 <= f.y0) {
@@ -883,21 +898,54 @@ function cut(free, b, minW, minH) {
883
898
  continue;
884
899
  }
885
900
  hits++;
886
- if (b.x0 > f.x0) add(f.x0, f.y0, b.x0, f.y1);
887
- if (b.x1 < f.x1) add(b.x1, f.y0, f.x1, f.y1);
888
- if (b.y0 > f.y0) add(f.x0, f.y0, f.x1, b.y0);
889
- if (b.y1 < f.y1) add(f.x0, b.y1, f.x1, f.y1);
901
+ if (b.x0 > f.x0) add(0, f.x0, f.y0, b.x0, f.y1);
902
+ if (b.x1 < f.x1) add(1, b.x1, f.y0, f.x1, f.y1);
903
+ if (b.y0 > f.y0) add(2, f.x0, f.y0, f.x1, b.y0);
904
+ if (b.y1 < f.y1) add(3, f.x0, b.y1, f.x1, f.y1);
905
+ }
906
+ if (parts.length === 0) return {
907
+ free: kept,
908
+ hits
909
+ };
910
+ const near = [
911
+ [],
912
+ [],
913
+ [],
914
+ []
915
+ ];
916
+ for (const k of kept) {
917
+ if (k.x1 === b.x0) near[0].push(k);
918
+ if (k.x0 === b.x1) near[1].push(k);
919
+ if (k.y1 === b.y0) near[2].push(k);
920
+ if (k.y0 === b.y1) near[3].push(k);
921
+ }
922
+ const order = parts.map((_, i) => i).sort((i, j) => byExtent(parts[i], parts[j]) || i - j);
923
+ const gone = new Uint8Array(parts.length);
924
+ const stay = [
925
+ [],
926
+ [],
927
+ [],
928
+ []
929
+ ];
930
+ for (const i of order) {
931
+ const p = parts[i];
932
+ const own = stay[sides[i]];
933
+ let drop = false;
934
+ for (const q of own) if (inside(p, q)) {
935
+ drop = true;
936
+ break;
937
+ }
938
+ if (drop) gone[i] = 1;
939
+ else own.push(p);
890
940
  }
891
- const count = kept.length;
892
941
  for (let i = 0; i < parts.length; i++) {
942
+ if (gone[i] === 1) continue;
893
943
  const p = parts[i];
894
944
  let drop = false;
895
- for (let j = 0; j < parts.length && !drop; j++) {
896
- if (j === i) continue;
897
- const q = parts[j];
898
- if (inside(p, q) && (j < i || !inside(q, p))) drop = true;
945
+ for (const k of near[sides[i]]) if (inside(p, k)) {
946
+ drop = true;
947
+ break;
899
948
  }
900
- for (let k = 0; k < count && !drop; k++) if (inside(p, kept[k])) drop = true;
901
949
  if (!drop) kept.push(p);
902
950
  }
903
951
  return {
@@ -905,6 +953,17 @@ function cut(free, b, minW, minH) {
905
953
  hits
906
954
  };
907
955
  }
956
+ /**
957
+ * This function orders two boxes. The smaller `x0` comes first, then the smaller `y0`, then the larger `x1`, then the
958
+ * larger `y1`. Thus a box comes before each other box in it.
959
+ */
960
+ function byExtent(a, b) {
961
+ if (a.x0 !== b.x0) return a.x0 < b.x0 ? -1 : 1;
962
+ if (a.y0 !== b.y0) return a.y0 < b.y0 ? -1 : 1;
963
+ if (a.x1 !== b.x1) return a.x1 > b.x1 ? -1 : 1;
964
+ if (a.y1 !== b.y1) return a.y1 > b.y1 ? -1 : 1;
965
+ return 0;
966
+ }
908
967
  /** This function adds a range to the edges at a coordinate. */
909
968
  function addEdge(edges, at, a, b) {
910
969
  const list = edges.get(at);
@@ -1109,22 +1168,29 @@ function runMaxRects(rects, outer, options = {}) {
1109
1168
  s1 = -contact(f.x0, f.y0, bw, bh);
1110
1169
  s2 = f.y0 + bh;
1111
1170
  }
1112
- const c = {
1171
+ const x = f.x0;
1172
+ const y = f.y0;
1173
+ if (best === void 0 || s1 < best.s1 || s1 === best.s1 && (s2 < best.s2 || s2 === best.s2 && (y < best.y || y === best.y && x < best.x))) best = {
1113
1174
  index: i,
1114
1175
  turn: t === 1,
1115
- x: f.x0,
1116
- y: f.y0,
1176
+ x,
1177
+ y,
1117
1178
  w: bw,
1118
1179
  h: bh,
1119
1180
  s1,
1120
1181
  s2
1121
1182
  };
1122
- if (best === void 0 || s1 < best.s1 || s1 === best.s1 && (s2 < best.s2 || s2 === best.s2 && (c.y < best.y || c.y === best.y && c.x < best.x))) best = c;
1123
- const ly = f.y0 + bh;
1124
- if (low === void 0 || ly < low.s1 || ly === low.s1 && f.x0 < low.s2) low = {
1125
- ...c,
1183
+ if (rule === "bl") continue;
1184
+ const ly = y + bh;
1185
+ if (low === void 0 || ly < low.s1 || ly === low.s1 && x < low.s2) low = {
1186
+ index: i,
1187
+ turn: t === 1,
1188
+ x,
1189
+ y,
1190
+ w: bw,
1191
+ h: bh,
1126
1192
  s1: ly,
1127
- s2: f.x0
1193
+ s2: x
1128
1194
  };
1129
1195
  }
1130
1196
  }
@@ -3575,6 +3641,346 @@ function toRects(output, rects, outer) {
3575
3641
  }
3576
3642
  return outer;
3577
3643
  }
3644
+ // src/check.ts
3645
+ /** This function tells if the insides of two boxes overlap by more than a tolerance on each axis. */
3646
+ function overlap(a, b, tolerance) {
3647
+ return Math.min(a.r, b.r) - Math.max(a.l, b.l) > tolerance && Math.min(a.b, b.b) - Math.max(a.t, b.t) > tolerance;
3648
+ }
3649
+ /** This function gives the box made smaller by a tolerance on each side, as a rectangle. */
3650
+ function inner(box, tolerance) {
3651
+ return {
3652
+ x: box.l + tolerance,
3653
+ y: box.t + tolerance,
3654
+ w: box.r - box.l - 2 * tolerance,
3655
+ h: box.b - box.t - 2 * tolerance
3656
+ };
3657
+ }
3658
+ /**
3659
+ * This function counts the breaks of the 4 hard rules of the packer for an output.
3660
+ *
3661
+ * - `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`.
3662
+ * - `in-container`: each item with its margin that goes out of a fixed side by more than `tolerance.overlap`. The inset of
3663
+ * the container makes each fixed side smaller (decision 3 of `docs/PACKER.md`).
3664
+ * - `locked-kept`: each locked item that moved by more than `RECORD_EPS`, or that has no placement.
3665
+ * - `complete`: each item with no placement that is not in the overflow. A locked item breaks `locked-kept` in its place.
3666
+ *
3667
+ * A locked item with no placement stays at its input position for `no-overlap` and `in-container`. A locked item with no
3668
+ * `x` or no `y` in the input is a bad input. The rules skip it, as `packHard` of the contract does. `checkInput` finds it.
3669
+ *
3670
+ * The test of an obstacle is exact: a circle, an ellipse and a polygon with holes count only their insides, as in `packHard`.
3671
+ * The rules read a shape of the container or of an item as its bounding box (decision 5). The bounding box of an item
3672
+ * shape is relative to the position of the item. The result has no tolerance in it.
3673
+ *
3674
+ * With `options.exactContainer`, a shaped container with the size `fixed` also uses its true outline for `in-container`.
3675
+ * A box then counts when `rectInRegion` of the contract tells that it is not in the shape, as in `packHard`.
3676
+ * For that test, the box grows by the inset and becomes smaller by `tolerance.overlap` on each side. The default is `false`.
3677
+ */
3678
+ function checkHard(input, output, tolerance = DEFAULT_TOLERANCE, options = {}) {
3679
+ const tol = tolerance.overlap;
3680
+ const placements = /* @__PURE__ */ new Map();
3681
+ for (const p of output.placements) if (!placements.has(p.id)) placements.set(p.id, p);
3682
+ const overflow = new Set(output.overflow);
3683
+ let lockedKept = 0;
3684
+ let complete = 0;
3685
+ const boxes = [];
3686
+ for (const item of input.items) {
3687
+ if (item.locked === true && (item.x === void 0 || item.y === void 0)) continue;
3688
+ const lockedAt = item.locked === true ? {
3689
+ x: item.x,
3690
+ y: item.y
3691
+ } : void 0;
3692
+ let p = placements.get(item.id);
3693
+ if (p === void 0) {
3694
+ if (lockedAt === void 0) {
3695
+ if (!overflow.has(item.id)) complete += 1;
3696
+ continue;
3697
+ }
3698
+ lockedKept += 1;
3699
+ p = lockedAt;
3700
+ } else if (lockedAt !== void 0) {
3701
+ if (Math.abs(p.x - lockedAt.x) > RECORD_EPS || Math.abs(p.y - lockedAt.y) > RECORD_EPS) lockedKept += 1;
3702
+ }
3703
+ const placed = p.turn === 90 ? turnItem(item) : item;
3704
+ const m = marginOf(placed);
3705
+ const area = placed.shape === void 0 ? {
3706
+ x: 0,
3707
+ y: 0,
3708
+ w: placed.w,
3709
+ h: placed.h
3710
+ } : boundsOf(placed.shape);
3711
+ const l = p.x + area.x;
3712
+ const t = p.y + area.y;
3713
+ boxes.push({
3714
+ l: l - m.left,
3715
+ t: t - m.top,
3716
+ r: l + area.w + m.right,
3717
+ b: t + area.h + m.bottom
3718
+ });
3719
+ }
3720
+ const moved = new Map(output.moved.map((m) => [m.id, m]));
3721
+ const obstacles = (input.obstacles ?? []).map((o) => {
3722
+ const at = moved.get(o.id);
3723
+ if (at === void 0) return o.region;
3724
+ const b = boundsOf(o.region);
3725
+ return translate(o.region, at.x - b.x, at.y - b.y);
3726
+ });
3727
+ let overlaps = 0;
3728
+ for (const [i, a] of boxes.entries()) {
3729
+ for (let j = i + 1; j < boxes.length; j++) if (overlap(a, boxes[j], tol)) overlaps += 1;
3730
+ if (obstacles.length === 0) continue;
3731
+ const rect = inner(a, tol);
3732
+ for (const region of obstacles) if (regionHitsRect(region, rect)) overlaps += 1;
3733
+ }
3734
+ const { container } = input;
3735
+ const shape = container.shape === void 0 ? void 0 : boundsOf(container.shape);
3736
+ const x = shape?.x ?? 0;
3737
+ const y = shape?.y ?? 0;
3738
+ const w = shape?.w ?? container.w ?? Infinity;
3739
+ const h = shape?.h ?? container.h ?? Infinity;
3740
+ const inset = container.inset ?? 0;
3741
+ const fixedW = container.fixed === "size" || container.fixed === "width";
3742
+ const fixedH = container.fixed === "size" || container.fixed === "height";
3743
+ const exact = options.exactContainer === true && container.fixed === "size" ? container.shape : void 0;
3744
+ let outside = 0;
3745
+ for (const box of boxes) {
3746
+ const outX = fixedW && (box.l < x + inset - tol || box.r > x + w - inset + tol);
3747
+ const outY = fixedH && (box.t < y + inset - tol || box.b > y + h - inset + tol);
3748
+ if (outX || outY) outside += 1;
3749
+ else if (exact !== void 0 && !rectInRegion(exact, inner({
3750
+ l: box.l - inset,
3751
+ t: box.t - inset,
3752
+ r: box.r + inset,
3753
+ b: box.b + inset
3754
+ }, tol))) outside += 1;
3755
+ }
3756
+ return {
3757
+ "no-overlap": overlaps,
3758
+ "in-container": outside,
3759
+ "locked-kept": lockedKept,
3760
+ complete
3761
+ };
3762
+ }
3763
+ // src/select-key.ts
3764
+ /** This function gives the count of the hard breaks of an output: the sum of `checkHard` with `exactContainer`. */
3765
+ function hardBreaks(input, output) {
3766
+ return Object.values(checkHard(input, output, void 0, { exactContainer: true })).reduce((sum, n) => sum + n, 0);
3767
+ }
3768
+ /** This function gives the level of a measure id with its spec of `MEASURE_SPECS`. It throws a `RangeError` for an unknown id. */
3769
+ function measureLevel(id) {
3770
+ const spec = MEASURE_SPECS[id];
3771
+ if (spec === void 0 || id === "hard") throw new RangeError(`A level of a key is a measure of MEASURE_SPECS, not "${id}".`);
3772
+ return {
3773
+ id,
3774
+ spec
3775
+ };
3776
+ }
3777
+ /**
3778
+ * This function makes the key of a pick: `hardBreaks`, then the measures of `levels` in order, then `outputHash`.
3779
+ *
3780
+ * - The levels read the measures after `roundMeasures`, as a record keeps them. A replay of the records gives the same pick.
3781
+ * - The measures of one output are computed one time.
3782
+ * - `view` gives the input of the measures. The default is the input. The hard breaks always read the input.
3783
+ */
3784
+ function measureKey(levels, view = (input) => input) {
3785
+ const cache = /* @__PURE__ */ new WeakMap();
3786
+ const measured = (input, output) => {
3787
+ let m = cache.get(output);
3788
+ if (m === void 0) {
3789
+ m = roundMeasures(measure(view(input), output));
3790
+ cache.set(output, m);
3791
+ }
3792
+ return m;
3793
+ };
3794
+ return {
3795
+ hard: hardBreaks,
3796
+ levels: levels.map((level) => ({
3797
+ id: level.id,
3798
+ spec: level.spec,
3799
+ value: (input, output) => measured(input, output)[level.id]
3800
+ })),
3801
+ hash: outputHash
3802
+ };
3803
+ }
3804
+ // src/aspect-fit.ts
3805
+ /** The id of the trace stage of `aspectFit`. Its measure `move.aspectPick` is 1 when the pick is not the output of the wrapped strategy. */
3806
+ const ASPECT_FIT_STAGE = "aspect-fit";
3807
+ /**
3808
+ * The factors of the widths of `aspectFit`. Each width is a factor times the square root of the area of the first output
3809
+ * times the target aspect. The steps are approximately 5% of the width, thus approximately 0.1 of the aspect error.
3810
+ */
3811
+ const ASPECT_FIT_FACTORS = Object.freeze([
3812
+ .86,
3813
+ .9,
3814
+ .95,
3815
+ 1,
3816
+ 1.05,
3817
+ 1.1,
3818
+ 1.16
3819
+ ]);
3820
+ /**
3821
+ * The levels of the pick of `aspectFit`, in order. Level 0 is `hard`.
3822
+ *
3823
+ * - `hard`: the count of hard breaks of `checkHard` with `exactContainer`. Lower is better, with the minimum effect 0.
3824
+ * - `pack.overflow.excessArea`: the overflow area above its bound. Lower is better.
3825
+ * - `pack.aspectError`: the distance from the target aspect. Lower is better, with the minimum effect 0.01.
3826
+ * - `pack.area`: the area of the hull. Lower is better, with the minimum effect of 1% of the area.
3827
+ */
3828
+ const ASPECT_FIT_LEVELS = Object.freeze([
3829
+ {
3830
+ id: "hard",
3831
+ spec: {
3832
+ direction: "lower",
3833
+ minEffect: 0
3834
+ }
3835
+ },
3836
+ {
3837
+ id: "pack.overflow.excessArea",
3838
+ spec: MEASURE_SPECS["pack.overflow.excessArea"]
3839
+ },
3840
+ {
3841
+ id: "pack.aspectError",
3842
+ spec: MEASURE_SPECS["pack.aspectError"]
3843
+ },
3844
+ {
3845
+ id: "pack.area",
3846
+ spec: MEASURE_SPECS["pack.area"]
3847
+ }
3848
+ ]);
3849
+ /** This function gives a shape with the origin of `shape` and the width `w`. A shape that is not a rectangle keeps the holes of its polygon. */
3850
+ function stripOf(shape, w) {
3851
+ const b = boundsOf$1(shape);
3852
+ if (shape.kind === "rect") return {
3853
+ kind: "rect",
3854
+ x: b.x,
3855
+ y: b.y,
3856
+ w,
3857
+ h: b.h
3858
+ };
3859
+ const points = [
3860
+ [b.x, b.y],
3861
+ [b.x + w, b.y],
3862
+ [b.x + w, b.y + b.h],
3863
+ [b.x, b.y + b.h]
3864
+ ];
3865
+ return shape.kind === "polygon" && shape.holes !== void 0 ? {
3866
+ kind: "polygon",
3867
+ points,
3868
+ holes: shape.holes
3869
+ } : {
3870
+ kind: "polygon",
3871
+ points
3872
+ };
3873
+ }
3874
+ /** This function gives an input with the fixed width `w` and a free height. A shape becomes a strip of the width `w`. */
3875
+ function atWidth(input, w) {
3876
+ const c = input.container;
3877
+ return {
3878
+ ...input,
3879
+ container: {
3880
+ ...c,
3881
+ fixed: "width",
3882
+ w,
3883
+ ...c.shape === void 0 ? {} : { shape: stripOf(c.shape, w) }
3884
+ }
3885
+ };
3886
+ }
3887
+ /** This function gives the widths of `aspectFit` for an input and the first output. Each width is narrower than a fixed width. */
3888
+ function widthsOf(input, first, target) {
3889
+ const area = measure(input, first)["pack.area"];
3890
+ if (!(area > 0)) return [];
3891
+ const inset = input.container.inset ?? 0;
3892
+ const fixed = input.container.fixed === "width" ? containerBounds(input).w : Infinity;
3893
+ let widest = 0;
3894
+ for (const item of input.items) {
3895
+ const m = marginOf(item);
3896
+ widest = Math.max(widest, item.w + m.left + m.right);
3897
+ }
3898
+ const ideal = Math.sqrt(area * target);
3899
+ const out = [];
3900
+ for (const f of ASPECT_FIT_FACTORS) {
3901
+ const w = Math.max(Math.round(f * ideal + 2 * inset), Math.ceil(widest + 2 * inset));
3902
+ if (w < fixed && !out.includes(w)) out.push(w);
3903
+ }
3904
+ return out;
3905
+ }
3906
+ /** This function packs an input with a free height or a free size near the target aspect. */
3907
+ function fit(input, target, inner, options, context) {
3908
+ const fresh = () => createContext(context.seed, {
3909
+ ...context.budget === void 0 ? {} : { budget: context.budget },
3910
+ ...context.previous === void 0 ? {} : { previous: context.previous }
3911
+ });
3912
+ const first = inner.pack(input, options, fresh());
3913
+ const widths = widthsOf(input, first, target);
3914
+ if (widths.length === 0) return first;
3915
+ const origin = containerBounds(input);
3916
+ const inset = input.container.inset ?? 0;
3917
+ const free = input.container.fixed === "none";
3918
+ const sized = (out) => {
3919
+ const w = free ? extentOf(input, out.placements, out.moved, origin, inset, turnItem).right : origin.w;
3920
+ return {
3921
+ ...out,
3922
+ size: {
3923
+ w,
3924
+ h: out.size.h
3925
+ }
3926
+ };
3927
+ };
3928
+ const members = [{
3929
+ id: inner.id,
3930
+ run: () => first
3931
+ }, ...widths.map((w) => ({
3932
+ id: `${inner.id}@${w}`,
3933
+ run: (x) => sized(inner.pack(atWidth(x, w), options, fresh()))
3934
+ }))];
3935
+ const view = (x) => x.container.aspect === target ? x : {
3936
+ ...x,
3937
+ container: {
3938
+ ...x.container,
3939
+ aspect: target
3940
+ }
3941
+ };
3942
+ const got = portfolio(ASPECT_FIT_STAGE, members, measureKey(ASPECT_FIT_LEVELS.slice(1), view)).pick(input);
3943
+ const stage = {
3944
+ id: ASPECT_FIT_STAGE,
3945
+ measures: { "move.aspectPick": pickMoves(got.trace).pick }
3946
+ };
3947
+ return {
3948
+ ...got.output,
3949
+ trace: [...got.output.trace ?? [], stage]
3950
+ };
3951
+ }
3952
+ /**
3953
+ * This function wraps a strategy. For a free-size container, it packs at some widths near the target aspect and keeps the best output.
3954
+ *
3955
+ * - The target is `targetAspect`, else `aspect` of the container, else 1.
3956
+ * - A container with the size `fixed` goes to the wrapped strategy without a change.
3957
+ * - The first member is the output of the wrapped strategy. Each other member packs at one fixed width of `ASPECT_FIT_FACTORS`.
3958
+ * - The key is `ASPECT_FIT_LEVELS`, then `outputHash`. The aspect error reads the target. `selectBest` of the contract picks the winner.
3959
+ * - A fixed height goes through the turn of the axes, with the target `1 / target`.
3960
+ * - The function throws a `RangeError` for a target that is not a number above 0.
3961
+ */
3962
+ function aspectFit(inner, options = {}) {
3963
+ const given = options.targetAspect;
3964
+ if (given !== void 0 && !(Number.isFinite(given) && given > 0)) throw new RangeError(`The target aspect of aspectFit is a number above 0, not ${String(given)}.`);
3965
+ return {
3966
+ id: `aspect-fit(${inner.id})`,
3967
+ family: inner.family,
3968
+ catalog: [],
3969
+ supports: inner.supports,
3970
+ defaults: inner.defaults,
3971
+ deterministic: inner.deterministic,
3972
+ pack(input, o, context) {
3973
+ const c = input.container;
3974
+ if (c.fixed === "size") return inner.pack(input, o, context);
3975
+ const target = given ?? c.aspect ?? 1;
3976
+ if (c.fixed === "height") return viaAxes(input, context.previous, (turned, previous) => fit(turned, 1 / target, inner, o, {
3977
+ ...context,
3978
+ ...previous === void 0 ? {} : { previous }
3979
+ }));
3980
+ return fit(input, target, inner, o, context);
3981
+ }
3982
+ };
3983
+ }
3578
3984
  // src/portfolio-sets.ts
3579
3985
  /** The table of the per-cell portfolio: the sets of 1, 2 and 3 members of each cell, with their training scores. The run 40652567756 gives it. */
3580
3986
  const PORTFOLIO_SETS = {
@@ -5796,125 +6202,6 @@ function portfolioDefaultMembers(input, k, table = PORTFOLIO_SETS) {
5796
6202
  const count = portfolioSetOf(input, table).sets.defaultMembers ?? 3;
5797
6203
  return k === void 0 ? count : Math.min(count, k);
5798
6204
  }
5799
- // src/check.ts
5800
- /** This function tells if the insides of two boxes overlap by more than a tolerance on each axis. */
5801
- function overlap(a, b, tolerance) {
5802
- return Math.min(a.r, b.r) - Math.max(a.l, b.l) > tolerance && Math.min(a.b, b.b) - Math.max(a.t, b.t) > tolerance;
5803
- }
5804
- /** This function gives the box made smaller by a tolerance on each side, as a rectangle. */
5805
- function inner(box, tolerance) {
5806
- return {
5807
- x: box.l + tolerance,
5808
- y: box.t + tolerance,
5809
- w: box.r - box.l - 2 * tolerance,
5810
- h: box.b - box.t - 2 * tolerance
5811
- };
5812
- }
5813
- /**
5814
- * This function counts the breaks of the 4 hard rules of the packer for an output.
5815
- *
5816
- * - `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`.
5817
- * - `in-container`: each item with its margin that goes out of a fixed side by more than `tolerance.overlap`. The inset of
5818
- * the container makes each fixed side smaller (decision 3 of `docs/PACKER.md`).
5819
- * - `locked-kept`: each locked item that moved by more than `RECORD_EPS`, or that has no placement.
5820
- * - `complete`: each item with no placement that is not in the overflow. A locked item breaks `locked-kept` in its place.
5821
- *
5822
- * A locked item with no placement stays at its input position for `no-overlap` and `in-container`. A locked item with no
5823
- * `x` or no `y` in the input is a bad input. The rules skip it, as `packHard` of the contract does. `checkInput` finds it.
5824
- *
5825
- * The test of an obstacle is exact: a circle, an ellipse and a polygon with holes count only their insides, as in `packHard`.
5826
- * The rules read a shape of the container or of an item as its bounding box (decision 5). The bounding box of an item
5827
- * shape is relative to the position of the item. The result has no tolerance in it.
5828
- *
5829
- * With `options.exactContainer`, a shaped container with the size `fixed` also uses its true outline for `in-container`.
5830
- * A box then counts when `rectInRegion` of the contract tells that it is not in the shape, as in `packHard`.
5831
- * For that test, the box grows by the inset and becomes smaller by `tolerance.overlap` on each side. The default is `false`.
5832
- */
5833
- function checkHard(input, output, tolerance = DEFAULT_TOLERANCE, options = {}) {
5834
- const tol = tolerance.overlap;
5835
- const placements = /* @__PURE__ */ new Map();
5836
- for (const p of output.placements) if (!placements.has(p.id)) placements.set(p.id, p);
5837
- const overflow = new Set(output.overflow);
5838
- let lockedKept = 0;
5839
- let complete = 0;
5840
- const boxes = [];
5841
- for (const item of input.items) {
5842
- if (item.locked === true && (item.x === void 0 || item.y === void 0)) continue;
5843
- const lockedAt = item.locked === true ? {
5844
- x: item.x,
5845
- y: item.y
5846
- } : void 0;
5847
- let p = placements.get(item.id);
5848
- if (p === void 0) {
5849
- if (lockedAt === void 0) {
5850
- if (!overflow.has(item.id)) complete += 1;
5851
- continue;
5852
- }
5853
- lockedKept += 1;
5854
- p = lockedAt;
5855
- } else if (lockedAt !== void 0) {
5856
- if (Math.abs(p.x - lockedAt.x) > RECORD_EPS || Math.abs(p.y - lockedAt.y) > RECORD_EPS) lockedKept += 1;
5857
- }
5858
- const placed = p.turn === 90 ? turnItem(item) : item;
5859
- const m = marginOf(placed);
5860
- const area = placed.shape === void 0 ? {
5861
- x: 0,
5862
- y: 0,
5863
- w: placed.w,
5864
- h: placed.h
5865
- } : boundsOf(placed.shape);
5866
- const l = p.x + area.x;
5867
- const t = p.y + area.y;
5868
- boxes.push({
5869
- l: l - m.left,
5870
- t: t - m.top,
5871
- r: l + area.w + m.right,
5872
- b: t + area.h + m.bottom
5873
- });
5874
- }
5875
- const moved = new Map(output.moved.map((m) => [m.id, m]));
5876
- const obstacles = (input.obstacles ?? []).map((o) => {
5877
- const at = moved.get(o.id);
5878
- if (at === void 0) return o.region;
5879
- const b = boundsOf(o.region);
5880
- return translate(o.region, at.x - b.x, at.y - b.y);
5881
- });
5882
- let overlaps = 0;
5883
- for (const [i, a] of boxes.entries()) {
5884
- for (let j = i + 1; j < boxes.length; j++) if (overlap(a, boxes[j], tol)) overlaps += 1;
5885
- if (obstacles.length === 0) continue;
5886
- const rect = inner(a, tol);
5887
- for (const region of obstacles) if (regionHitsRect(region, rect)) overlaps += 1;
5888
- }
5889
- const { container } = input;
5890
- const shape = container.shape === void 0 ? void 0 : boundsOf(container.shape);
5891
- const x = shape?.x ?? 0;
5892
- const y = shape?.y ?? 0;
5893
- const w = shape?.w ?? container.w ?? Infinity;
5894
- const h = shape?.h ?? container.h ?? Infinity;
5895
- const inset = container.inset ?? 0;
5896
- const fixedW = container.fixed === "size" || container.fixed === "width";
5897
- const fixedH = container.fixed === "size" || container.fixed === "height";
5898
- const exact = options.exactContainer === true && container.fixed === "size" ? container.shape : void 0;
5899
- let outside = 0;
5900
- for (const box of boxes) {
5901
- const outX = fixedW && (box.l < x + inset - tol || box.r > x + w - inset + tol);
5902
- const outY = fixedH && (box.t < y + inset - tol || box.b > y + h - inset + tol);
5903
- if (outX || outY) outside += 1;
5904
- else if (exact !== void 0 && !rectInRegion(exact, inner({
5905
- l: box.l - inset,
5906
- t: box.t - inset,
5907
- r: box.r + inset,
5908
- b: box.b + inset
5909
- }, tol))) outside += 1;
5910
- }
5911
- return {
5912
- "no-overlap": overlaps,
5913
- "in-container": outside,
5914
- "locked-kept": lockedKept,
5915
- complete
5916
- };
5917
- }
5918
6205
  // src/portfolio.ts
5919
6206
  /** The id of the trace stage of `packPortfolio`. Its measure `move.pick` is 1 when the pick is not member 0. */
5920
6207
  const PORTFOLIO_STAGE = "portfolio";
@@ -5942,10 +6229,6 @@ const PORTFOLIO_LEVELS = Object.freeze([
5942
6229
  spec: MEASURE_SPECS["pack.fill"]
5943
6230
  }
5944
6231
  ]);
5945
- /** This function gives the count of the hard breaks of an output: the sum of `checkHard` with `exactContainer`. */
5946
- function hardBreaks(input, output) {
5947
- return Object.values(checkHard(input, output, void 0, { exactContainer: true })).reduce((sum, n) => sum + n, 0);
5948
- }
5949
6232
  /**
5950
6233
  * This function makes an opt-in portfolio of strategies of the registry with `portfolio` of the contract.
5951
6234
  * Its `pick(input)` packs the input with each member and gives the output of the winner, its id and the trace.
@@ -5953,10 +6236,11 @@ function hardBreaks(input, output) {
5953
6236
  * - A member is a strategy id, or a `PortfolioEntry` with options. The members run in the given order.
5954
6237
  * - `members` is a list, or a function of the input that gives the list. The strategy `portfolio` uses the function form.
5955
6238
  * - The key is the hard breaks, then `pack.overflow.excessArea`, then `pack.fill`. `PORTFOLIO_LEVELS` gives the specs.
6239
+ * The option `key` gives other measures after the hard breaks.
5956
6240
  * - The key reads the measures after `roundMeasures`, as a record keeps them. A replay of the records gives the same pick.
5957
6241
  * - The hash of the key is `outputHash`. `selectBest` of the contract picks the winner.
5958
6242
  * - The output of the winner gets the trace stage `portfolio` with `move.pick` of `pickMoves`.
5959
- * - The function throws a `RangeError` for an unknown strategy, an empty list and two members with the same id.
6243
+ * - The function throws a `RangeError` for an unknown strategy, an empty list, two members with the same id and an unknown measure of `key`.
5960
6244
  * With the function form, `pick` throws this error for the list of an input.
5961
6245
  */
5962
6246
  function packPortfolio(members, options = {}) {
@@ -5973,24 +6257,7 @@ function packPortfolio(members, options = {}) {
5973
6257
  };
5974
6258
  });
5975
6259
  const list = typeof members === "function" ? (input) => toMembers(members(input)) : toMembers(members);
5976
- const cache = /* @__PURE__ */ new WeakMap();
5977
- const measured = (input, output) => {
5978
- let m = cache.get(output);
5979
- if (m === void 0) {
5980
- m = roundMeasures(measure(input, output));
5981
- cache.set(output, m);
5982
- }
5983
- return m;
5984
- };
5985
- const key = {
5986
- hard: hardBreaks,
5987
- levels: PORTFOLIO_LEVELS.slice(1).map((level) => ({
5988
- id: level.id,
5989
- spec: level.spec,
5990
- value: (input, output) => measured(input, output)[level.id]
5991
- })),
5992
- hash: outputHash
5993
- };
6260
+ const key = measureKey(options.key === void 0 ? PORTFOLIO_LEVELS.slice(1) : options.key.map(measureLevel));
5994
6261
  const limits = {
5995
6262
  ...options.budgetMs !== void 0 ? { budgetMs: options.budgetMs } : {},
5996
6263
  ...options.maxMembers !== void 0 ? { maxMembers: options.maxMembers } : {},
@@ -6044,7 +6311,8 @@ function unionSupports() {
6044
6311
  * The default output thus does not depend on the clock.
6045
6312
  * - A member that does not support the cell of an input throws, and it does not win. When each member throws, the strategy throws the first error.
6046
6313
  * - The option `budgetMs` goes to `portfolio` of the contract. The first member always runs.
6047
- * - The strategy throws a `RangeError` for a `k` other than 2 or 3.
6314
+ * - With `members`, each input gets these members, and `maxMembers` has no default. The id of the portfolio stays `portfolio`.
6315
+ * - The strategy throws a `RangeError` for a `k` other than 2 or 3, and for `k` with `members`.
6048
6316
  */
6049
6317
  const portfolioStrategy = {
6050
6318
  id: "portfolio",
@@ -6054,15 +6322,17 @@ const portfolioStrategy = {
6054
6322
  defaults: {},
6055
6323
  deterministic: true,
6056
6324
  pack(input, options, context) {
6057
- const { k, maxMembers, budgetMs } = options;
6325
+ const { k, maxMembers, budgetMs, members, key } = options;
6058
6326
  if (k !== void 0 && k !== 2 && k !== 3) throw new RangeError(`The option k of the strategy portfolio is 2 or 3, not ${String(k)}.`);
6327
+ if (k !== void 0 && members !== void 0) throw new RangeError("The strategy portfolio takes the option k or the option members, not the two.");
6059
6328
  const size = k ?? 3;
6060
- const count = maxMembers ?? (k === void 0 ? portfolioDefaultMembers(input) : void 0);
6329
+ const count = maxMembers ?? (k === void 0 && members === void 0 ? portfolioDefaultMembers(input) : void 0);
6061
6330
  const limits = {
6062
6331
  ...count === void 0 ? {} : { maxMembers: count },
6063
- ...budgetMs === void 0 ? {} : { budgetMs }
6332
+ ...budgetMs === void 0 ? {} : { budgetMs },
6333
+ ...key === void 0 ? {} : { key }
6064
6334
  };
6065
- return packPortfolio((x) => portfolioMembers(x, size), {
6335
+ return packPortfolio(members ?? ((x) => portfolioMembers(x, size)), {
6066
6336
  id: k === void 0 ? "portfolio" : `portfolio-k${k}`,
6067
6337
  seed: context.seed,
6068
6338
  ...limits
@@ -6078,11 +6348,42 @@ const strategies = Object.freeze([
6078
6348
  push,
6079
6349
  maxrects
6080
6350
  ]);
6351
+ /** This function gives the union of the supports of some strategies. */
6352
+ function unionOf(list) {
6353
+ const out = {};
6354
+ for (const s of list) for (const [dim, values] of Object.entries(s.supports)) {
6355
+ const own = out[dim] ??= [];
6356
+ for (const v of values) if (!own.includes(v)) own.push(v);
6357
+ }
6358
+ return out;
6359
+ }
6360
+ /**
6361
+ * The strategy `aspect-fit`: `aspectFit` of roadmap item k62 around the strategy of the option `strategy`. Its supports are
6362
+ * the union of the supports of the single strategies. A cell that the wrapped strategy does not support throws its error.
6363
+ * The strategy throws a `RangeError` without the option `strategy`.
6364
+ */
6365
+ const aspectFitStrategy = {
6366
+ id: "aspect-fit",
6367
+ family: "aspect-fit",
6368
+ catalog: [],
6369
+ supports: unionOf(strategies),
6370
+ defaults: {},
6371
+ deterministic: true,
6372
+ pack(input, options, context) {
6373
+ if (options.strategy === void 0) throw new RangeError("The strategy aspect-fit needs the option strategy.");
6374
+ const inner = strategy(options.strategy);
6375
+ const merged = {
6376
+ ...inner.defaults,
6377
+ ...options.options
6378
+ };
6379
+ return aspectFit(inner, options.targetAspect === void 0 ? {} : { targetAspect: options.targetAspect }).pack(input, merged, context);
6380
+ }
6381
+ };
6081
6382
  /**
6082
6383
  * The composite strategies of the registry: each one packs an input with other strategies. They are not in `strategies`.
6083
- * Since 0.2.7, the list has `portfolio` of roadmap item k20.
6384
+ * Since 0.2.7, the list has `portfolio` of roadmap item k20. Roadmap item k62 adds `aspect-fit`.
6084
6385
  */
6085
- const composites = Object.freeze([portfolioStrategy]);
6386
+ const composites = Object.freeze([portfolioStrategy, aspectFitStrategy]);
6086
6387
  /** This function gives the strategy with an id, also a composite strategy. It throws a `RangeError` for an id that the registry does not have. */
6087
6388
  function strategy(id) {
6088
6389
  const found = strategies.find((s) => s.id === id) ?? composites.find((s) => s.id === id);
@@ -6669,19 +6970,67 @@ const rectGrid = defineGenerator({
6669
6970
  },
6670
6971
  family: (p) => `rect-grid.${p.containerSize}.${p.containerShape}.${p.obstacles}.${p.sizes}.n${p.count}`,
6671
6972
  cell: (p) => cellWith(p.containerSize, p.containerShape, p.obstacles, p.sizes, p.count),
6973
+ build: gridOf
6974
+ });
6975
+ /** This function builds an input of `rect-grid`: the container, then the obstacles, then the boxes. */
6976
+ function gridOf(seed, p) {
6977
+ let r = stepRandom(seed, "container");
6978
+ const w = int(r, 600, 1e3);
6979
+ const h = int(r, 400, 800);
6980
+ const { region, frame } = shapeOf(r, p.containerShape, w, h);
6981
+ const container = containerOf(p.containerSize, w, h, region);
6982
+ r = stepRandom(seed, "obstacles");
6983
+ const obstacles = obstaclesOf(r, p.obstacles, frame);
6984
+ r = stepRandom(seed, "boxes");
6985
+ return {
6986
+ container,
6987
+ obstacles,
6988
+ boxes: boxesOf(r, p.sizes, p.count, frame)
6989
+ };
6990
+ }
6991
+ /**
6992
+ * The generator `rect-grid`, version 2: the inputs of version 1 with the parameter `order`. With `soft`, each box gets a
6993
+ * rank, a random permutation of the box indexes. With `free`, the boxes have no rank.
6994
+ */
6995
+ const rectGridV2 = defineGenerator({
6996
+ name: "rect-grid",
6997
+ version: 2,
6998
+ set: "corpus-v1",
6999
+ defaults: {
7000
+ containerSize: "fixed",
7001
+ containerShape: "rect",
7002
+ obstacles: "none",
7003
+ sizes: "uniform",
7004
+ order: "free",
7005
+ count: 100
7006
+ },
7007
+ values: {
7008
+ containerSize: CONTAINER_SIZES,
7009
+ containerShape: CONTAINER_SHAPES,
7010
+ obstacles: OBSTACLE_LAYOUTS,
7011
+ sizes: SIZE_DISTRIBUTIONS,
7012
+ order: ["free", "soft"]
7013
+ },
7014
+ family: (p) => `rect-grid.${p.containerSize}.${p.containerShape}.${p.obstacles}.${p.sizes}.${p.order}.n${p.count}`,
7015
+ cell: (p) => ({
7016
+ ...cellWith(p.containerSize, p.containerShape, p.obstacles, p.sizes, p.count),
7017
+ order: p.order
7018
+ }),
6672
7019
  build(seed, p) {
6673
- let r = stepRandom(seed, "container");
6674
- const w = int(r, 600, 1e3);
6675
- const h = int(r, 400, 800);
6676
- const { region, frame } = shapeOf(r, p.containerShape, w, h);
6677
- const container = containerOf(p.containerSize, w, h, region);
6678
- r = stepRandom(seed, "obstacles");
6679
- const obstacles = obstaclesOf(r, p.obstacles, frame);
6680
- r = stepRandom(seed, "boxes");
7020
+ const built = gridOf(seed, p);
7021
+ if (p.order === "free") return built;
7022
+ const r = stepRandom(seed, "order");
7023
+ const ranks = built.boxes.map((_, i) => i);
7024
+ for (let i = ranks.length - 1; i > 0; i--) {
7025
+ const j = Math.floor(r() * (i + 1));
7026
+ [ranks[i], ranks[j]] = [ranks[j], ranks[i]];
7027
+ }
6681
7028
  return {
6682
- container,
6683
- obstacles,
6684
- boxes: boxesOf(r, p.sizes, p.count, frame)
7029
+ ...built,
7030
+ boxes: built.boxes.map((b, i) => ({
7031
+ ...b,
7032
+ rank: ranks[i]
7033
+ }))
6685
7034
  };
6686
7035
  }
6687
7036
  });
@@ -6961,16 +7310,23 @@ const edgeCases = defineGenerator({
6961
7310
  const generators = Object.freeze([
6962
7311
  rectScattered,
6963
7312
  rectGrid,
7313
+ rectGridV2,
6964
7314
  gallery,
6965
7315
  dashboard,
6966
7316
  cards,
6967
7317
  wordCloud,
6968
7318
  edgeCases
6969
7319
  ]);
6970
- /** This function gives the generator with a name. It throws a `RangeError` for a name that the package does not have. */
6971
- function generator(name) {
6972
- const found = generators.find((g) => g.name === name);
6973
- if (found === void 0) throw new RangeError(`The package has no generator "${name}".`);
7320
+ /**
7321
+ * This function gives the generator with a name and a version. Without a version, it gives the first generator of the name
7322
+ * in `generators`: version 1 of `rect-grid`. It throws a `RangeError` for a name or a version that the package does not have.
7323
+ */
7324
+ function generator(name, version) {
7325
+ const named = generators.filter((g) => g.name === name);
7326
+ if (named.length === 0) throw new RangeError(`The package has no generator "${name}".`);
7327
+ if (version === void 0) return named[0];
7328
+ const found = named.find((g) => g.version === version);
7329
+ if (found === void 0) throw new RangeError(`The generator "${name}" has the versions ${named.map((g) => g.version).join(", ")}, not ${version}.`);
6974
7330
  return found;
6975
7331
  }
6976
7332
  // src/corpus.ts
@@ -6988,8 +7344,7 @@ function corpus(name, specs) {
6988
7344
  const inputs = [];
6989
7345
  const ids = /* @__PURE__ */ new Set();
6990
7346
  for (const spec of specs) {
6991
- const gen = generator(spec.generator);
6992
- if (spec.version !== void 0 && spec.version !== gen.version) throw new RangeError(`The generator "${gen.name}" has the version ${gen.version}, not ${spec.version}.`);
7347
+ const gen = generator(spec.generator, spec.version);
6993
7348
  const params = gen.withDefaults(spec.params);
6994
7349
  const cell = gen.cell(params);
6995
7350
  manifest.specs.push({
@@ -7015,6 +7370,6 @@ function corpus(name, specs) {
7015
7370
  inputs
7016
7371
  };
7017
7372
  }
7018
- export { DEFAULT_TOLERANCE, EPS, MAXRECTS_BANDS, MAXRECTS_RULES, MEASURE_SPECS, PORTFOLIO_FRAME_MS, PORTFOLIO_LEVELS, PORTFOLIO_SETS, PORTFOLIO_STAGE, RECORD_EPS, Rect, SHELF_FITS, SORT_ORDERS, SPREAD_THRESHOLDS, THIN_INPUTS, UnsupportedCell, Vector, aspectError, assertSupported, canonicalJson, cards, cellKey, cellOf, checkHard, checkInput, compareRects, composites, corpus, countCell, createContext, dashboard, edgeCases, fnv1a32, freeSpace, fromRects, gallery, generator, generators, hardBreaks, hashInput, isDeterministic, logSweep, lowestWindow, marginOf, maxrects, measure, measureChange, medianRatio, mulberry32, outputHash, pack, packFree, packMaxRects, packPortfolio, packRows, packShelf, packSkyline, packWith, packingMetrics, pairedRatio, portfolioCell, portfolioDefaultMembers, portfolioMembers, portfolioSetOf, portfolioStrategy, push, pushCascade, quantile, rectGrid, rectScattered, round3, roundMeasures, rows, shelf, skyline, spreadBand, spreadOf, strategies, strategy, supportsCell, tailShare, toPackInput, toRects, turnItem, turnPoint, turnPort, turnRegion, unsupportedOf, wordCloud };
7373
+ export { ASPECT_FIT_FACTORS, ASPECT_FIT_LEVELS, ASPECT_FIT_STAGE, DEFAULT_TOLERANCE, EPS, MAXRECTS_BANDS, MAXRECTS_RULES, MEASURE_SPECS, PORTFOLIO_FRAME_MS, PORTFOLIO_LEVELS, PORTFOLIO_SETS, PORTFOLIO_STAGE, RECORD_EPS, Rect, SHELF_FITS, SORT_ORDERS, SPREAD_THRESHOLDS, THIN_INPUTS, UnsupportedCell, Vector, aspectError, aspectFit, aspectFitStrategy, assertSupported, canonicalJson, cards, cellKey, cellOf, checkHard, checkInput, compareRects, composites, corpus, countCell, createContext, dashboard, edgeCases, fnv1a32, freeSpace, fromRects, gallery, generator, generators, hardBreaks, hashInput, isDeterministic, logSweep, lowestWindow, marginOf, maxrects, measure, measureChange, medianRatio, mulberry32, outputHash, pack, packFree, packMaxRects, packPortfolio, packRows, packShelf, packSkyline, packWith, packingMetrics, pairedRatio, portfolioCell, portfolioDefaultMembers, portfolioMembers, portfolioSetOf, portfolioStrategy, push, pushCascade, quantile, rectGrid, rectGridV2, rectScattered, round3, roundMeasures, rows, shelf, skyline, spreadBand, spreadOf, strategies, strategy, supportsCell, tailShare, toPackInput, toRects, turnItem, turnPoint, turnPort, turnRegion, unsupportedOf, wordCloud };
7019
7374
 
7020
7375
  //# sourceMappingURL=index.js.map