@weasel-js/guides 1.9.3 → 1.10.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/dist/chunk-LOFNN7EQ.js +45 -0
- package/dist/chunk-LOFNN7EQ.js.map +1 -0
- package/dist/{chunk-5BCCIJY3.js → chunk-TLFMAZ6M.js} +11 -13
- package/dist/chunk-TLFMAZ6M.js.map +1 -0
- package/dist/index.d.ts +276 -70
- package/dist/index.js +500 -53
- package/dist/index.js.map +1 -1
- package/dist/insert.d.ts +1 -1
- package/dist/insert.js +12 -31
- package/dist/insert.js.map +1 -1
- package/dist/move.d.ts +1 -1
- package/dist/move.js +2 -2
- package/dist/resize.d.ts +1 -1
- package/dist/resize.js +10 -32
- package/dist/resize.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-5BCCIJY3.js.map +0 -1
- package/dist/chunk-GEDAPTOM.js +0 -48
- package/dist/chunk-GEDAPTOM.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { pointToGridCell } from './chunk-
|
|
2
|
-
export { gridSnapStrategy, pointToGridCell } from './chunk-
|
|
1
|
+
import { pointToGridCell } from './chunk-TLFMAZ6M.js';
|
|
2
|
+
export { gridSnapStrategy, pointToGridCell } from './chunk-TLFMAZ6M.js';
|
|
3
3
|
export { pointSnapToGrid } from './chunk-B25KFI3I.js';
|
|
4
|
-
|
|
4
|
+
import { guideOffsetOn } from './chunk-LOFNN7EQ.js';
|
|
5
|
+
export { DEFAULT_GUIDE_TOLERANCE_PX, guideSnapStrategy } from './chunk-LOFNN7EQ.js';
|
|
5
6
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
6
|
-
import { useLatest,
|
|
7
|
+
import { useLatest, resolveAxes, pxExtent, textCommandFromRuns, AUTO_POSE_DESCRIPTOR, visualBoundsViaDescriptor, rectFromPlane, translatePoseViaDescriptor, unionBounds, fixedCornerOf, createTransformOp, resolveStrokeWidth, screenToWorld, rectToPlane, screenTolerance } from '@weasel-js/core';
|
|
7
8
|
import { rectPath, PATH_M, PATH_L } from '@weasel-js/geom';
|
|
8
9
|
|
|
9
10
|
// src/grid/roundToCell.ts
|
|
@@ -52,15 +53,15 @@ function useGridCellHover(opts) {
|
|
|
52
53
|
return { cell, getCell };
|
|
53
54
|
}
|
|
54
55
|
var DEFAULT_LINE = {
|
|
55
|
-
paint: {
|
|
56
|
+
paint: { kind: "solid", color: "rgba(255, 255, 255, 0.08)" },
|
|
56
57
|
width: 1
|
|
57
58
|
};
|
|
58
59
|
var DEFAULT_ACCENT = {
|
|
59
|
-
paint: {
|
|
60
|
+
paint: { kind: "solid", color: "rgba(255, 255, 255, 0.16)" },
|
|
60
61
|
width: 1
|
|
61
62
|
};
|
|
62
63
|
var DEFAULT_SUB = {
|
|
63
|
-
paint: {
|
|
64
|
+
paint: { kind: "solid", color: "rgba(255, 255, 255, 0.04)" },
|
|
64
65
|
width: 1
|
|
65
66
|
};
|
|
66
67
|
function createGridLayer(opts) {
|
|
@@ -74,7 +75,7 @@ function createGridLayer(opts) {
|
|
|
74
75
|
const b = opts.bounds();
|
|
75
76
|
if (b.width <= 0 || b.height <= 0) return [];
|
|
76
77
|
const { accentEvery, subdivisions } = opts;
|
|
77
|
-
const spacing =
|
|
78
|
+
const spacing = resolveAxes(opts.spacing, opts.unitSystem);
|
|
78
79
|
const x0 = b.x;
|
|
79
80
|
const y0 = b.y;
|
|
80
81
|
const x1 = b.x + b.width;
|
|
@@ -88,46 +89,47 @@ function createGridLayer(opts) {
|
|
|
88
89
|
coords: new Float32Array([sx, sy, ex, ey]),
|
|
89
90
|
fillRule: "nonzero"
|
|
90
91
|
};
|
|
91
|
-
const
|
|
92
|
+
const across2 = sx === ex ? px.x : px.y;
|
|
92
93
|
children.push({
|
|
93
94
|
kind: "path",
|
|
94
95
|
path,
|
|
95
|
-
stroke: { ...s, width: resolveStrokeWidth(s.width ?? 1, 1) *
|
|
96
|
+
stroke: { ...s, width: resolveStrokeWidth(s.width ?? 1, 1) * across2 }
|
|
96
97
|
});
|
|
97
98
|
};
|
|
98
99
|
if (subdivisions && subdivisions > 1) {
|
|
99
|
-
const
|
|
100
|
-
for (let x = x0; x <= x1 + 1e-9; x +=
|
|
101
|
-
const k = Math.round((x - x0) /
|
|
100
|
+
const stepX = spacing.x / subdivisions;
|
|
101
|
+
for (let x = x0; x <= x1 + 1e-9; x += stepX) {
|
|
102
|
+
const k = Math.round((x - x0) / stepX);
|
|
102
103
|
if (k % subdivisions === 0) continue;
|
|
103
104
|
pushSegment(x, y0, x, y1, sub);
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
const stepY = spacing.y / subdivisions;
|
|
107
|
+
for (let y = y0; y <= y1 + 1e-9; y += stepY) {
|
|
108
|
+
const k = Math.round((y - y0) / stepY);
|
|
107
109
|
if (k % subdivisions === 0) continue;
|
|
108
110
|
pushSegment(x0, y, x1, y, sub);
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
let idx = 0;
|
|
112
|
-
for (let x = x0; x <= x1 + 1e-9; x += spacing) {
|
|
114
|
+
for (let x = x0; x <= x1 + 1e-9; x += spacing.x) {
|
|
113
115
|
const isAccent = !!accentEvery && accentEvery > 0 && idx % accentEvery === 0;
|
|
114
116
|
if (!isAccent) pushSegment(x, y0, x, y1, line);
|
|
115
117
|
idx++;
|
|
116
118
|
}
|
|
117
119
|
idx = 0;
|
|
118
|
-
for (let y = y0; y <= y1 + 1e-9; y += spacing) {
|
|
120
|
+
for (let y = y0; y <= y1 + 1e-9; y += spacing.y) {
|
|
119
121
|
const isAccent = !!accentEvery && accentEvery > 0 && idx % accentEvery === 0;
|
|
120
122
|
if (!isAccent) pushSegment(x0, y, x1, y, line);
|
|
121
123
|
idx++;
|
|
122
124
|
}
|
|
123
125
|
if (accentEvery && accentEvery > 0) {
|
|
124
126
|
idx = 0;
|
|
125
|
-
for (let x = x0; x <= x1 + 1e-9; x += spacing) {
|
|
127
|
+
for (let x = x0; x <= x1 + 1e-9; x += spacing.x) {
|
|
126
128
|
if (idx % accentEvery === 0) pushSegment(x, y0, x, y1, accent);
|
|
127
129
|
idx++;
|
|
128
130
|
}
|
|
129
131
|
idx = 0;
|
|
130
|
-
for (let y = y0; y <= y1 + 1e-9; y += spacing) {
|
|
132
|
+
for (let y = y0; y <= y1 + 1e-9; y += spacing.y) {
|
|
131
133
|
if (idx % accentEvery === 0) pushSegment(x0, y, x1, y, accent);
|
|
132
134
|
idx++;
|
|
133
135
|
}
|
|
@@ -136,7 +138,7 @@ function createGridLayer(opts) {
|
|
|
136
138
|
}
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
|
-
var DEFAULT_FILL = {
|
|
141
|
+
var DEFAULT_FILL = { paint: { kind: "solid", color: "rgba(127,176,105,0.15)" } };
|
|
140
142
|
function createCellHighlightLayer(opts) {
|
|
141
143
|
const fill = opts.fill ?? DEFAULT_FILL;
|
|
142
144
|
return {
|
|
@@ -145,14 +147,14 @@ function createCellHighlightLayer(opts) {
|
|
|
145
147
|
draw: () => {
|
|
146
148
|
const cell = opts.getCell();
|
|
147
149
|
if (!cell) return [];
|
|
148
|
-
const spacing =
|
|
150
|
+
const spacing = resolveAxes(opts.spacing, opts.unitSystem);
|
|
149
151
|
const o = opts.origin ? opts.origin() : { x: 0, y: 0 };
|
|
150
|
-
const x = o.x + cell.col * spacing;
|
|
151
|
-
const y = o.y + cell.row * spacing;
|
|
152
|
+
const x = o.x + cell.col * spacing.x;
|
|
153
|
+
const y = o.y + cell.row * spacing.y;
|
|
152
154
|
const children = [
|
|
153
155
|
{
|
|
154
156
|
kind: "path",
|
|
155
|
-
path: { kind: "rect", x, y, width: spacing, height: spacing },
|
|
157
|
+
path: { kind: "rect", x, y, width: spacing.x, height: spacing.y },
|
|
156
158
|
fill
|
|
157
159
|
}
|
|
158
160
|
];
|
|
@@ -213,8 +215,8 @@ function createGuidesLayer(opts) {
|
|
|
213
215
|
const ticks = opts.ticks ?? 6;
|
|
214
216
|
const spanned = (opts.extent ?? "span") === "span";
|
|
215
217
|
const format = opts.gapLabels === false ? null : typeof opts.gapLabels === "function" ? opts.gapLabels : (n) => String(Math.round(n));
|
|
216
|
-
const stroke = { paint: {
|
|
217
|
-
const gapStroke = { paint: {
|
|
218
|
+
const stroke = { paint: { kind: "solid", color }, width: lineWidth };
|
|
219
|
+
const gapStroke = { paint: { kind: "solid", color: gapColor }, width: lineWidth };
|
|
218
220
|
return {
|
|
219
221
|
id: opts.id ?? "guides",
|
|
220
222
|
label: opts.label ?? "Guides",
|
|
@@ -248,11 +250,11 @@ function createGuidesLayer(opts) {
|
|
|
248
250
|
const w = text.length * LABEL_CHAR_W + LABEL_PAD_X * 2;
|
|
249
251
|
const left = (x0 + x1) / 2 - w / 2;
|
|
250
252
|
const top = (y0 + y1) / 2 - LABEL_H / 2;
|
|
251
|
-
out.push({ kind: "path", path: rectPath(left, top, w, LABEL_H), fill: {
|
|
253
|
+
out.push({ kind: "path", path: rectPath(left, top, w, LABEL_H), fill: { paint: { kind: "solid", color: gapColor } } });
|
|
252
254
|
out.push(textCommandFromRuns(
|
|
253
255
|
left + LABEL_PAD_X,
|
|
254
256
|
top + 2,
|
|
255
|
-
[{ text, fill: {
|
|
257
|
+
[{ text, fill: { paint: { kind: "solid", color: "#fff" } } }],
|
|
256
258
|
LABEL_FONT
|
|
257
259
|
));
|
|
258
260
|
}
|
|
@@ -291,7 +293,11 @@ function deriveAlignmentGuides(targets, opts = {}) {
|
|
|
291
293
|
add("y", b.y + b.height / 2, b);
|
|
292
294
|
}
|
|
293
295
|
};
|
|
294
|
-
|
|
296
|
+
targets.forEach((t, i) => {
|
|
297
|
+
const box = visualBoundsViaDescriptor(t, d);
|
|
298
|
+
const plane = opts.planeOf?.(t, i);
|
|
299
|
+
emit(plane ? rectFromPlane(plane, box) : box);
|
|
300
|
+
});
|
|
295
301
|
if (opts.page) emit(opts.page);
|
|
296
302
|
return [...seenX.values(), ...seenY.values()];
|
|
297
303
|
}
|
|
@@ -460,6 +466,39 @@ function applyEdges(b, dx, dy, moving) {
|
|
|
460
466
|
} else if (moving.y === "max") height += dy;
|
|
461
467
|
return { x, y, width, height };
|
|
462
468
|
}
|
|
469
|
+
var across = (axis) => axis === "x" ? "y" : "x";
|
|
470
|
+
var into = (m, axis, v) => v * m.scale[axis] + m.offset[axis];
|
|
471
|
+
var outOf = (m, axis, v) => (v - m.offset[axis]) / m.scale[axis];
|
|
472
|
+
function range(r, map) {
|
|
473
|
+
const a = map(r.min);
|
|
474
|
+
const b = map(r.max);
|
|
475
|
+
return { ...r, min: Math.min(a, b), max: Math.max(a, b) };
|
|
476
|
+
}
|
|
477
|
+
function guideToPlane(g, m) {
|
|
478
|
+
const span = g.span && range(g.span, (v) => into(m, across(g.axis), v));
|
|
479
|
+
return { ...g, offset: guideOffsetOn(g, m), ...span ? { span } : {} };
|
|
480
|
+
}
|
|
481
|
+
function guideFromPlane(g, m) {
|
|
482
|
+
const span = g.span && range(g.span, (v) => outOf(m, across(g.axis), v));
|
|
483
|
+
return { ...g, offset: outOf(m, g.axis, g.offset), ...span ? { span } : {} };
|
|
484
|
+
}
|
|
485
|
+
function gapFromPlane(g, m) {
|
|
486
|
+
return { ...range(g, (v) => outOf(m, g.axis, v)), at: outOf(m, across(g.axis), g.at) };
|
|
487
|
+
}
|
|
488
|
+
function alignmentOnPlane(args, plane) {
|
|
489
|
+
if (!plane) return args;
|
|
490
|
+
const spacing = args.getSpacingTargets;
|
|
491
|
+
const setGaps = args.setActiveGaps;
|
|
492
|
+
return {
|
|
493
|
+
...args,
|
|
494
|
+
getCandidates: () => args.getCandidates().map((g) => guideToPlane(g, plane)),
|
|
495
|
+
setActiveGuides: (guides) => args.setActiveGuides(guides.map((g) => guideFromPlane(g, plane))),
|
|
496
|
+
...spacing ? { getSpacingTargets: () => spacing().map((b) => rectToPlane(plane, b)) } : {},
|
|
497
|
+
...setGaps ? { setActiveGaps: (gaps) => setGaps(gaps.map((g) => gapFromPlane(g, plane))) } : {}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/guides/alignment/behaviors.ts
|
|
463
502
|
var activeList = (m) => [m.activeX, m.activeY].filter((g) => g !== null);
|
|
464
503
|
var EXACT = { x: 1e-6, y: 1e-6 };
|
|
465
504
|
function resolve(b, args, tol, anchors, edges) {
|
|
@@ -491,10 +530,11 @@ function publish(args, r) {
|
|
|
491
530
|
function worldTol(base, ctx) {
|
|
492
531
|
return screenTolerance(base.tolerance ?? 6, ctx);
|
|
493
532
|
}
|
|
494
|
-
function alignMoveBehavior(
|
|
495
|
-
const d =
|
|
533
|
+
function alignMoveBehavior(given) {
|
|
534
|
+
const d = given.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
496
535
|
return {
|
|
497
536
|
onMove(ctx, transform) {
|
|
537
|
+
const args = alignmentOnPlane(given, ctx.plane);
|
|
498
538
|
if (args.bypassKey && ctx.modifiers[args.bypassKey]) {
|
|
499
539
|
publish(args, null);
|
|
500
540
|
return;
|
|
@@ -517,17 +557,18 @@ function alignMoveBehavior(args) {
|
|
|
517
557
|
return { transform: { kind: "translate", dx: transform.dx + r.dx, dy: transform.dy + r.dy } };
|
|
518
558
|
},
|
|
519
559
|
onEnd() {
|
|
520
|
-
publish(
|
|
560
|
+
publish(given, null);
|
|
521
561
|
},
|
|
522
562
|
onCancel() {
|
|
523
|
-
publish(
|
|
563
|
+
publish(given, null);
|
|
524
564
|
}
|
|
525
565
|
};
|
|
526
566
|
}
|
|
527
|
-
function alignInsertBehavior(
|
|
567
|
+
function alignInsertBehavior(given) {
|
|
528
568
|
const pointAnchors = { x: ["min"], y: ["min"] };
|
|
529
569
|
return {
|
|
530
570
|
onMove(ctx, { current }) {
|
|
571
|
+
const args = alignmentOnPlane(given, ctx.plane);
|
|
531
572
|
if (args.bypassKey && ctx.modifiers[args.bypassKey]) {
|
|
532
573
|
args.setActiveGuides([]);
|
|
533
574
|
return;
|
|
@@ -542,17 +583,18 @@ function alignInsertBehavior(args) {
|
|
|
542
583
|
return { current: { x: current.x + m.dx, y: current.y + m.dy } };
|
|
543
584
|
},
|
|
544
585
|
onEnd() {
|
|
545
|
-
|
|
586
|
+
given.setActiveGuides([]);
|
|
546
587
|
},
|
|
547
588
|
onCancel() {
|
|
548
|
-
|
|
589
|
+
given.setActiveGuides([]);
|
|
549
590
|
}
|
|
550
591
|
};
|
|
551
592
|
}
|
|
552
|
-
function alignResizeBehavior(
|
|
553
|
-
const d =
|
|
593
|
+
function alignResizeBehavior(given) {
|
|
594
|
+
const d = given.poseDescriptor ?? AUTO_POSE_DESCRIPTOR;
|
|
554
595
|
return {
|
|
555
596
|
onMove(ctx, { pose, anchor }) {
|
|
597
|
+
const args = alignmentOnPlane(given, ctx.plane);
|
|
556
598
|
if (args.bypassKey && ctx.modifiers[args.bypassKey]) {
|
|
557
599
|
publish(args, null);
|
|
558
600
|
return;
|
|
@@ -579,10 +621,10 @@ function alignResizeBehavior(args) {
|
|
|
579
621
|
return { pose: { ...pose, x, y, width, height } };
|
|
580
622
|
},
|
|
581
623
|
onEnd() {
|
|
582
|
-
publish(
|
|
624
|
+
publish(given, null);
|
|
583
625
|
},
|
|
584
626
|
onCancel() {
|
|
585
|
-
publish(
|
|
627
|
+
publish(given, null);
|
|
586
628
|
}
|
|
587
629
|
};
|
|
588
630
|
}
|
|
@@ -627,8 +669,8 @@ function matchRotatedCorners(pose, anchor, originBox, rotation, candidates, tol)
|
|
|
627
669
|
const dir = along === "x" ? toWorld(1, 0) : toWorld(0, 1);
|
|
628
670
|
const edge = movingEdge(along)[0];
|
|
629
671
|
let best = null;
|
|
630
|
-
for (const
|
|
631
|
-
const c = along === "x" ? drawn(edge,
|
|
672
|
+
for (const across3 of movingEdge(along === "x" ? "y" : "x")) {
|
|
673
|
+
const c = along === "x" ? drawn(edge, across3) : drawn(across3, edge);
|
|
632
674
|
for (const g of candidates) {
|
|
633
675
|
const reach = g.axis === "x" ? dir.x : dir.y;
|
|
634
676
|
if (Math.abs(reach) < 1e-9) continue;
|
|
@@ -638,8 +680,8 @@ function matchRotatedCorners(pose, anchor, originBox, rotation, candidates, tol)
|
|
|
638
680
|
}
|
|
639
681
|
}
|
|
640
682
|
if (best === null) return null;
|
|
641
|
-
const
|
|
642
|
-
const guides = [stretchSpan(best.guide,
|
|
683
|
+
const across2 = best.guide.axis === "x" ? best.at.y + best.t * dir.y : best.at.x + best.t * dir.x;
|
|
684
|
+
const guides = [stretchSpan(best.guide, across2, across2)];
|
|
643
685
|
return along === "x" ? { lx: best.t, ly: 0, guides, gaps: [] } : { lx: 0, ly: best.t, guides, gaps: [] };
|
|
644
686
|
}
|
|
645
687
|
|
|
@@ -705,6 +747,41 @@ function cellAt() {
|
|
|
705
747
|
}
|
|
706
748
|
};
|
|
707
749
|
}
|
|
750
|
+
|
|
751
|
+
// src/layout/perContainer.ts
|
|
752
|
+
function perContainer(factory, opts) {
|
|
753
|
+
const max = opts.max ?? 256;
|
|
754
|
+
const built = /* @__PURE__ */ new Map();
|
|
755
|
+
const of = (container) => {
|
|
756
|
+
let strategy = built.get(container.id);
|
|
757
|
+
if (strategy) built.delete(container.id);
|
|
758
|
+
else strategy = factory(container);
|
|
759
|
+
built.set(container.id, strategy);
|
|
760
|
+
if (built.size > max) built.delete(built.keys().next().value);
|
|
761
|
+
return strategy;
|
|
762
|
+
};
|
|
763
|
+
return {
|
|
764
|
+
snap: opts.snap,
|
|
765
|
+
contains: opts.contains,
|
|
766
|
+
forget: (containerId) => void built.delete(containerId),
|
|
767
|
+
childPoses: (container, children) => of(container).childPoses(container, children),
|
|
768
|
+
getDropTargets: (container, children, dragged) => of(container).getDropTargets(container, children, dragged),
|
|
769
|
+
reflowPoses: (container, children, dragged, target) => of(container).reflowPoses(container, children, dragged, target),
|
|
770
|
+
commitDrop: (container, children, dragged, target) => of(container).commitDrop(container, children, dragged, target),
|
|
771
|
+
dropRegion: (container) => of(container).dropRegion?.(container) ?? null,
|
|
772
|
+
acceptsDrop: (container, dragged) => of(container).acceptsDrop?.(container, dragged) ?? true,
|
|
773
|
+
arrive(container, children, arrivals) {
|
|
774
|
+
const s = of(container);
|
|
775
|
+
return s.arrive ? s.arrive(container, children, arrivals) : { poses: s.childPoses(container, children) };
|
|
776
|
+
},
|
|
777
|
+
depart(container, children, departed) {
|
|
778
|
+
const s = of(container);
|
|
779
|
+
return s.depart ? s.depart(container, children, departed) : { poses: s.childPoses(container, children) };
|
|
780
|
+
},
|
|
781
|
+
contentExtent: (container, children) => of(container).contentExtent?.(container, children) ?? container.bounds,
|
|
782
|
+
releaseDrop: (container, children, dragged) => of(container).releaseDrop?.(container, children, dragged) ?? null
|
|
783
|
+
};
|
|
784
|
+
}
|
|
708
785
|
function freeform(opts = {}) {
|
|
709
786
|
const snap = opts.snap ?? none();
|
|
710
787
|
return {
|
|
@@ -798,7 +875,7 @@ function tileGrid(opts) {
|
|
|
798
875
|
if (fromIdx === null || fromIdx === idx) return null;
|
|
799
876
|
return { occupant: occupant.id, from: occupant.pose, newPose: cellToPose(grid.rectAt(fromIdx), occupant.pose) };
|
|
800
877
|
}
|
|
801
|
-
function
|
|
878
|
+
function pack2(from, into2, children) {
|
|
802
879
|
const ordered = children.map((c) => ({ c, cell: from.indexOf(c.pose) })).sort((a, b) => {
|
|
803
880
|
if (a.cell !== b.cell) {
|
|
804
881
|
if (a.cell === null) return 1;
|
|
@@ -809,7 +886,7 @@ function tileGrid(opts) {
|
|
|
809
886
|
});
|
|
810
887
|
const out = /* @__PURE__ */ new Map();
|
|
811
888
|
for (let i = 0; i < ordered.length; i++) {
|
|
812
|
-
out.set(ordered[i].c.id, cellToPose(
|
|
889
|
+
out.set(ordered[i].c.id, cellToPose(into2.rectAt(i), ordered[i].c.pose));
|
|
813
890
|
}
|
|
814
891
|
return out;
|
|
815
892
|
}
|
|
@@ -817,7 +894,7 @@ function tileGrid(opts) {
|
|
|
817
894
|
snap,
|
|
818
895
|
childPoses(container, children) {
|
|
819
896
|
const grid = gridOf(container.bounds, children.length);
|
|
820
|
-
return
|
|
897
|
+
return pack2(grid, grid, children);
|
|
821
898
|
},
|
|
822
899
|
getDropTargets(container, children, dragged) {
|
|
823
900
|
const grid = gridOf(container.bounds, heldBy(container, children, dragged));
|
|
@@ -884,7 +961,7 @@ function tileGrid(opts) {
|
|
|
884
961
|
const n = children.length;
|
|
885
962
|
if (overflow === "reject" && n > capacity) return null;
|
|
886
963
|
const grid = gridOf(container.bounds, n - arrivals.size);
|
|
887
|
-
const
|
|
964
|
+
const slots2 = overflow === "reject" ? capacity : linesFor(n) * perLine;
|
|
888
965
|
const taken = /* @__PURE__ */ new Set();
|
|
889
966
|
for (const c of children) {
|
|
890
967
|
if (arrivals.has(c.id)) continue;
|
|
@@ -896,13 +973,13 @@ function tileGrid(opts) {
|
|
|
896
973
|
if (!arrivals.has(c.id)) continue;
|
|
897
974
|
const own = grid.indexOf(c.pose);
|
|
898
975
|
let idx;
|
|
899
|
-
if (own !== null && own <
|
|
976
|
+
if (own !== null && own < slots2 && !taken.has(own)) {
|
|
900
977
|
idx = own;
|
|
901
978
|
} else {
|
|
902
979
|
const at = centerOf(c.pose);
|
|
903
980
|
let best = -1;
|
|
904
981
|
let bestD = Infinity;
|
|
905
|
-
for (let i = 0; i <
|
|
982
|
+
for (let i = 0; i < slots2; i++) {
|
|
906
983
|
if (taken.has(i)) continue;
|
|
907
984
|
const r = grid.rectAt(i);
|
|
908
985
|
const dx = r.x + r.width / 2 - at.x;
|
|
@@ -929,10 +1006,10 @@ function tileGrid(opts) {
|
|
|
929
1006
|
const before = gridOf(container.bounds, n + departed.size);
|
|
930
1007
|
const lines = linesFor(n);
|
|
931
1008
|
if (overflow !== "grow" || lines === linesFor(n + departed.size)) {
|
|
932
|
-
return { poses:
|
|
1009
|
+
return { poses: pack2(before, before, children) };
|
|
933
1010
|
}
|
|
934
1011
|
const bounds = before.boundsFor(lines);
|
|
935
|
-
return { poses:
|
|
1012
|
+
return { poses: pack2(before, gridOf(bounds, n), children), bounds };
|
|
936
1013
|
},
|
|
937
1014
|
contentExtent(container, children) {
|
|
938
1015
|
const grid = gridOf(container.bounds, children.length);
|
|
@@ -1009,7 +1086,377 @@ function snapPoint(opts) {
|
|
|
1009
1086
|
}
|
|
1010
1087
|
};
|
|
1011
1088
|
}
|
|
1089
|
+
function defaultCenterOf2(pose) {
|
|
1090
|
+
const p = pose;
|
|
1091
|
+
return { x: (p.x ?? 0) + (p.width ?? 0) / 2, y: (p.y ?? 0) + (p.height ?? 0) / 2 };
|
|
1092
|
+
}
|
|
1093
|
+
var withAt = (list, index, item) => [
|
|
1094
|
+
...list.slice(0, index),
|
|
1095
|
+
item,
|
|
1096
|
+
...list.slice(index)
|
|
1097
|
+
];
|
|
1098
|
+
function othersOf(seq, container, children, dragged) {
|
|
1099
|
+
const held = children.filter((c) => c.id !== dragged.id);
|
|
1100
|
+
const count = held.length + (dragged.sourceContainerId === container.id ? 1 : 0);
|
|
1101
|
+
return seq.order(container, held, count);
|
|
1102
|
+
}
|
|
1103
|
+
function nearestIndex(seq, container, sequence, child) {
|
|
1104
|
+
const at = seq.centerOf(child.pose);
|
|
1105
|
+
let best = null;
|
|
1106
|
+
let bestD = Infinity;
|
|
1107
|
+
for (let i = 0; i <= sequence.length; i++) {
|
|
1108
|
+
const pose = seq.place(container, withAt(sequence, i, child))?.get(child.id);
|
|
1109
|
+
if (pose === void 0) continue;
|
|
1110
|
+
const p = seq.centerOf(pose);
|
|
1111
|
+
const d = (p.x - at.x) ** 2 + (p.y - at.y) ** 2;
|
|
1112
|
+
if (d < bestD) {
|
|
1113
|
+
bestD = d;
|
|
1114
|
+
best = i;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return best;
|
|
1118
|
+
}
|
|
1119
|
+
function sequenceLayout(seq) {
|
|
1120
|
+
const { centerOf } = seq;
|
|
1121
|
+
const moved = (a, b) => {
|
|
1122
|
+
const p = centerOf(a);
|
|
1123
|
+
const q = centerOf(b);
|
|
1124
|
+
return p.x !== q.x || p.y !== q.y;
|
|
1125
|
+
};
|
|
1126
|
+
const arranged = (container, sequence, poses) => {
|
|
1127
|
+
const bounds = seq.resize?.(container, sequence);
|
|
1128
|
+
return bounds ? { poses, bounds } : { poses };
|
|
1129
|
+
};
|
|
1130
|
+
const shifted = (container, children, dragged, target) => {
|
|
1131
|
+
if (!target) return /* @__PURE__ */ new Map();
|
|
1132
|
+
const others = othersOf(seq, container, children, dragged);
|
|
1133
|
+
const self = { id: dragged.id, pose: dragged.pose };
|
|
1134
|
+
const poses = seq.place(container, withAt(others, target.meta.index, self)) ?? /* @__PURE__ */ new Map();
|
|
1135
|
+
poses.delete(dragged.id);
|
|
1136
|
+
return poses;
|
|
1137
|
+
};
|
|
1138
|
+
return {
|
|
1139
|
+
snap: seq.snap,
|
|
1140
|
+
childPoses(container, children) {
|
|
1141
|
+
const sequence = seq.order(container, children, children.length);
|
|
1142
|
+
return seq.place(container, sequence) ?? new Map(children.map((c) => [c.id, c.pose]));
|
|
1143
|
+
},
|
|
1144
|
+
getDropTargets(container, children, dragged) {
|
|
1145
|
+
const others = othersOf(seq, container, children, dragged);
|
|
1146
|
+
const self = { id: dragged.id, pose: dragged.pose };
|
|
1147
|
+
const targets = [];
|
|
1148
|
+
for (let index = 0; index <= others.length; index++) {
|
|
1149
|
+
const pose = seq.place(container, withAt(others, index, self))?.get(dragged.id);
|
|
1150
|
+
if (pose === void 0) continue;
|
|
1151
|
+
targets.push({ pose, origin: centerOf(pose), meta: { index } });
|
|
1152
|
+
}
|
|
1153
|
+
return targets;
|
|
1154
|
+
},
|
|
1155
|
+
reflowPoses: shifted,
|
|
1156
|
+
commitDrop(container, children, dragged, target) {
|
|
1157
|
+
const ops = [];
|
|
1158
|
+
if (target) {
|
|
1159
|
+
const poses = shifted(container, children, dragged, target);
|
|
1160
|
+
for (const c of children) {
|
|
1161
|
+
const to = poses.get(c.id);
|
|
1162
|
+
if (to !== void 0 && moved(c.pose, to)) {
|
|
1163
|
+
ops.push(createTransformOp({ id: c.id, from: c.pose, to, label: seq.labels.shift }));
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
ops.push(
|
|
1168
|
+
createTransformOp({
|
|
1169
|
+
id: dragged.id,
|
|
1170
|
+
from: dragged.originPose,
|
|
1171
|
+
to: target?.pose ?? dragged.pose,
|
|
1172
|
+
label: seq.labels.drop
|
|
1173
|
+
})
|
|
1174
|
+
);
|
|
1175
|
+
return ops;
|
|
1176
|
+
},
|
|
1177
|
+
arrive(container, children, arrivals) {
|
|
1178
|
+
const held = children.filter((c) => !arrivals.has(c.id));
|
|
1179
|
+
let sequence = seq.order(container, held, held.length);
|
|
1180
|
+
for (const c of children) {
|
|
1181
|
+
if (!arrivals.has(c.id)) continue;
|
|
1182
|
+
const index = nearestIndex(seq, container, sequence, c);
|
|
1183
|
+
if (index === null) return null;
|
|
1184
|
+
sequence = withAt(sequence, index, c);
|
|
1185
|
+
}
|
|
1186
|
+
const poses = seq.place(container, sequence);
|
|
1187
|
+
return poses && arranged(container, sequence, poses);
|
|
1188
|
+
},
|
|
1189
|
+
depart(container, children, departed) {
|
|
1190
|
+
const sequence = seq.order(container, children, children.length + departed.size);
|
|
1191
|
+
const poses = seq.place(container, sequence) ?? /* @__PURE__ */ new Map();
|
|
1192
|
+
return arranged(container, sequence, poses);
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// src/layout/strategies/slots.ts
|
|
1198
|
+
function defaultSlotToPose(slot, pose) {
|
|
1199
|
+
const { width = 0, height = 0 } = pose;
|
|
1200
|
+
return { ...pose, x: slot.x - width / 2, y: slot.y - height / 2 };
|
|
1201
|
+
}
|
|
1202
|
+
function nearestSlot(at, places) {
|
|
1203
|
+
let best = -1;
|
|
1204
|
+
let bestD = Infinity;
|
|
1205
|
+
for (let i = 0; i < places.length; i++) {
|
|
1206
|
+
const d = (places[i].x - at.x) ** 2 + (places[i].y - at.y) ** 2;
|
|
1207
|
+
if (d < bestD) {
|
|
1208
|
+
bestD = d;
|
|
1209
|
+
best = i;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return best;
|
|
1213
|
+
}
|
|
1214
|
+
function slots(opts) {
|
|
1215
|
+
const { arrangement } = opts;
|
|
1216
|
+
const slotToPose = opts.slotToPose ?? defaultSlotToPose;
|
|
1217
|
+
const centerOf = opts.centerOf ?? defaultCenterOf2;
|
|
1218
|
+
return sequenceLayout({
|
|
1219
|
+
snap: opts.snap ?? nearest(),
|
|
1220
|
+
centerOf,
|
|
1221
|
+
labels: { drop: "Slot drop", shift: "Slot shift" },
|
|
1222
|
+
// Ties break by id, so a container nobody has arranged yet still has an order.
|
|
1223
|
+
order(container, children, count) {
|
|
1224
|
+
const places = arrangement(container.bounds, count);
|
|
1225
|
+
return children.map((c) => ({ c, slot: nearestSlot(centerOf(c.pose), places) })).sort((a, b) => a.slot - b.slot || (a.c.id < b.c.id ? -1 : a.c.id > b.c.id ? 1 : 0)).map((e) => e.c);
|
|
1226
|
+
},
|
|
1227
|
+
place(container, sequence) {
|
|
1228
|
+
const places = arrangement(container.bounds, sequence.length);
|
|
1229
|
+
if (places.length < sequence.length) return null;
|
|
1230
|
+
return new Map(sequence.map((c, i) => [c.id, slotToPose(places[i], c.pose)]));
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// src/layout/strategies/pack.ts
|
|
1236
|
+
function pack(opts = {}) {
|
|
1237
|
+
const gap = opts.gap ?? 0;
|
|
1238
|
+
const overflow = opts.overflow ?? "reject";
|
|
1239
|
+
const minHeight = opts.minHeight ?? 0;
|
|
1240
|
+
const rectOf2 = opts.rectOf ?? ((pose) => pose);
|
|
1241
|
+
const moveTo = opts.moveTo ?? ((pose, at) => ({ ...pose, ...at }));
|
|
1242
|
+
const readingOrder = (children) => children.map((c) => ({ c, r: rectOf2(c.pose) })).sort((a, b) => a.r.y - b.r.y || a.r.x - b.r.x || (a.c.id < b.c.id ? -1 : a.c.id > b.c.id ? 1 : 0)).map((e) => e.c);
|
|
1243
|
+
function shelve(bounds, sequence) {
|
|
1244
|
+
const poses = /* @__PURE__ */ new Map();
|
|
1245
|
+
let x = 0;
|
|
1246
|
+
let y = 0;
|
|
1247
|
+
let shelf = 0;
|
|
1248
|
+
for (const c of sequence) {
|
|
1249
|
+
const { width, height } = rectOf2(c.pose);
|
|
1250
|
+
if (width > bounds.width) return null;
|
|
1251
|
+
if (x > 0 && x + width > bounds.width) {
|
|
1252
|
+
y += shelf + gap;
|
|
1253
|
+
x = 0;
|
|
1254
|
+
shelf = 0;
|
|
1255
|
+
}
|
|
1256
|
+
poses.set(c.id, moveTo(c.pose, { x: bounds.x + x, y: bounds.y + y }));
|
|
1257
|
+
x += width + gap;
|
|
1258
|
+
shelf = Math.max(shelf, height);
|
|
1259
|
+
}
|
|
1260
|
+
return { poses, height: y + shelf };
|
|
1261
|
+
}
|
|
1262
|
+
const layout = sequenceLayout({
|
|
1263
|
+
snap: opts.snap ?? nearest(),
|
|
1264
|
+
labels: { drop: "Pack drop", shift: "Pack shift" },
|
|
1265
|
+
centerOf(pose) {
|
|
1266
|
+
const r = rectOf2(pose);
|
|
1267
|
+
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
1268
|
+
},
|
|
1269
|
+
order: (_container, children) => readingOrder(children),
|
|
1270
|
+
place(container, sequence) {
|
|
1271
|
+
const shelved = shelve(container.bounds, sequence);
|
|
1272
|
+
if (!shelved) return null;
|
|
1273
|
+
return overflow === "reject" && shelved.height > container.bounds.height ? null : shelved.poses;
|
|
1274
|
+
},
|
|
1275
|
+
resize(container, sequence) {
|
|
1276
|
+
if (overflow !== "grow") return void 0;
|
|
1277
|
+
const height = Math.max(minHeight, shelve(container.bounds, sequence)?.height ?? 0);
|
|
1278
|
+
return height === container.bounds.height ? void 0 : { ...container.bounds, height };
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
return {
|
|
1282
|
+
...layout,
|
|
1283
|
+
contentExtent(container, children) {
|
|
1284
|
+
const reach = shelve(container.bounds, readingOrder(children))?.height ?? 0;
|
|
1285
|
+
return { ...container.bounds, height: Math.max(container.bounds.height, reach) };
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
function overlaps(a, b) {
|
|
1290
|
+
const [outer, inner] = a.depth <= b.depth ? [a, b] : [b, a];
|
|
1291
|
+
const shift = inner.depth - outer.depth;
|
|
1292
|
+
return inner.col >> shift === outer.col && inner.row >> shift === outer.row;
|
|
1293
|
+
}
|
|
1294
|
+
function rectOf(bounds, cell) {
|
|
1295
|
+
const width = bounds.width / 2 ** cell.depth;
|
|
1296
|
+
const height = bounds.height / 2 ** cell.depth;
|
|
1297
|
+
return { x: bounds.x + cell.col * width, y: bounds.y + cell.row * height, width, height };
|
|
1298
|
+
}
|
|
1299
|
+
function freeCell(bounds, taken, depth, at) {
|
|
1300
|
+
let best = null;
|
|
1301
|
+
let bestD = Infinity;
|
|
1302
|
+
for (const cell of freeCells(taken, depth)) {
|
|
1303
|
+
const r = rectOf(bounds, cell);
|
|
1304
|
+
const d = (r.x + r.width / 2 - at.x) ** 2 + (r.y + r.height / 2 - at.y) ** 2;
|
|
1305
|
+
if (d < bestD) {
|
|
1306
|
+
bestD = d;
|
|
1307
|
+
best = cell;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return best;
|
|
1311
|
+
}
|
|
1312
|
+
function freeCells(taken, depth) {
|
|
1313
|
+
const out = [];
|
|
1314
|
+
const n = 2 ** depth;
|
|
1315
|
+
for (let row = 0; row < n; row++) {
|
|
1316
|
+
for (let col = 0; col < n; col++) {
|
|
1317
|
+
const cell = { depth, col, row };
|
|
1318
|
+
if (!taken.some((t) => overlaps(t, cell))) out.push(cell);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return out;
|
|
1322
|
+
}
|
|
1323
|
+
function defaultCellToPose(cell, pose) {
|
|
1324
|
+
const { width = 0, height = 0 } = pose;
|
|
1325
|
+
return {
|
|
1326
|
+
...pose,
|
|
1327
|
+
x: cell.x + (cell.width - width) / 2,
|
|
1328
|
+
y: cell.y + (cell.height - height) / 2
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
function quadtree(opts = {}) {
|
|
1332
|
+
const maxDepth = opts.maxDepth ?? 4;
|
|
1333
|
+
const snap = opts.snap ?? containedThenNearest();
|
|
1334
|
+
const cellToPose = opts.cellToPose ?? defaultCellToPose;
|
|
1335
|
+
const centerOf = opts.centerOf ?? defaultCenterOf2;
|
|
1336
|
+
const sizeOf = opts.sizeOf ?? ((pose) => {
|
|
1337
|
+
const { width = 0, height = 0 } = pose;
|
|
1338
|
+
return Math.max(width, height);
|
|
1339
|
+
});
|
|
1340
|
+
function depthOf(bounds, pose) {
|
|
1341
|
+
const size = sizeOf(pose);
|
|
1342
|
+
const side = Math.min(bounds.width, bounds.height);
|
|
1343
|
+
let depth = 0;
|
|
1344
|
+
while (depth < maxDepth && side / 2 ** (depth + 1) >= size) depth++;
|
|
1345
|
+
return depth;
|
|
1346
|
+
}
|
|
1347
|
+
function assign(bounds, children, held = []) {
|
|
1348
|
+
const taken = [...held];
|
|
1349
|
+
const cells = /* @__PURE__ */ new Map();
|
|
1350
|
+
const bySize = children.map((c) => ({ c, depth: depthOf(bounds, c.pose) })).sort((a, b) => a.depth - b.depth || (a.c.id < b.c.id ? -1 : a.c.id > b.c.id ? 1 : 0));
|
|
1351
|
+
for (const { c, depth } of bySize) {
|
|
1352
|
+
const cell = freeCell(bounds, taken, depth, centerOf(c.pose));
|
|
1353
|
+
if (!cell) continue;
|
|
1354
|
+
taken.push(cell);
|
|
1355
|
+
cells.set(c.id, cell);
|
|
1356
|
+
}
|
|
1357
|
+
return { cells, taken };
|
|
1358
|
+
}
|
|
1359
|
+
return {
|
|
1360
|
+
snap,
|
|
1361
|
+
childPoses(container, children) {
|
|
1362
|
+
const { cells } = assign(container.bounds, children);
|
|
1363
|
+
return new Map(
|
|
1364
|
+
children.map((c) => {
|
|
1365
|
+
const cell = cells.get(c.id);
|
|
1366
|
+
return [c.id, cell ? cellToPose(rectOf(container.bounds, cell), c.pose) : c.pose];
|
|
1367
|
+
})
|
|
1368
|
+
);
|
|
1369
|
+
},
|
|
1370
|
+
getDropTargets(container, children, dragged) {
|
|
1371
|
+
const { taken } = assign(container.bounds, children.filter((c) => c.id !== dragged.id));
|
|
1372
|
+
return freeCells(taken, depthOf(container.bounds, dragged.pose)).map((cell) => {
|
|
1373
|
+
const hitBounds = rectOf(container.bounds, cell);
|
|
1374
|
+
return {
|
|
1375
|
+
pose: cellToPose(hitBounds, dragged.pose),
|
|
1376
|
+
origin: { x: hitBounds.x + hitBounds.width / 2, y: hitBounds.y + hitBounds.height / 2 },
|
|
1377
|
+
hitBounds
|
|
1378
|
+
};
|
|
1379
|
+
});
|
|
1380
|
+
},
|
|
1381
|
+
reflowPoses: () => /* @__PURE__ */ new Map(),
|
|
1382
|
+
commitDrop: (_container, _children, dragged, target) => [
|
|
1383
|
+
createTransformOp({
|
|
1384
|
+
id: dragged.id,
|
|
1385
|
+
from: dragged.originPose,
|
|
1386
|
+
to: target?.pose ?? dragged.pose,
|
|
1387
|
+
label: "Quadtree drop"
|
|
1388
|
+
})
|
|
1389
|
+
],
|
|
1390
|
+
arrive(container, children, arrivals) {
|
|
1391
|
+
const { taken } = assign(container.bounds, children.filter((c) => !arrivals.has(c.id)));
|
|
1392
|
+
const joining = children.filter((c) => arrivals.has(c.id));
|
|
1393
|
+
const { cells } = assign(container.bounds, joining, taken);
|
|
1394
|
+
if (cells.size < joining.length) return null;
|
|
1395
|
+
return {
|
|
1396
|
+
poses: new Map(joining.map((c) => [c.id, cellToPose(rectOf(container.bounds, cells.get(c.id)), c.pose)]))
|
|
1397
|
+
};
|
|
1398
|
+
},
|
|
1399
|
+
cells(container, children) {
|
|
1400
|
+
const { cells } = assign(container.bounds, children);
|
|
1401
|
+
return [...cells].map(([id, cell]) => ({ id, depth: cell.depth, rect: rectOf(container.bounds, cell) })).sort((a, b) => a.depth - b.depth || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// src/layout/strategies/arrangements.ts
|
|
1407
|
+
function rowSlots(opts = {}) {
|
|
1408
|
+
const alongX = opts.axis !== "y";
|
|
1409
|
+
return (bounds, count) => {
|
|
1410
|
+
const length = alongX ? bounds.width : bounds.height;
|
|
1411
|
+
const pitch = opts.pitch ?? length / Math.max(count, 1);
|
|
1412
|
+
const first = length / 2 - pitch * (count - 1) / 2;
|
|
1413
|
+
const out = [];
|
|
1414
|
+
for (let i = 0; i < count; i++) {
|
|
1415
|
+
const along = first + i * pitch;
|
|
1416
|
+
out.push(
|
|
1417
|
+
alongX ? { x: bounds.x + along, y: bounds.y + bounds.height / 2 } : { x: bounds.x + bounds.width / 2, y: bounds.y + along }
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
return out;
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
function gridSlots(opts) {
|
|
1424
|
+
return (bounds, count) => {
|
|
1425
|
+
const rows = Math.max(opts.rows ?? 1, Math.ceil(count / opts.cols));
|
|
1426
|
+
const cw = bounds.width / opts.cols;
|
|
1427
|
+
const ch = bounds.height / rows;
|
|
1428
|
+
const out = [];
|
|
1429
|
+
for (let i = 0; i < count; i++) {
|
|
1430
|
+
out.push({
|
|
1431
|
+
x: bounds.x + (i % opts.cols + 0.5) * cw,
|
|
1432
|
+
y: bounds.y + (Math.floor(i / opts.cols) + 0.5) * ch
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
return out;
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
function ringSlots(opts = {}) {
|
|
1439
|
+
const start = opts.start ?? -Math.PI / 2;
|
|
1440
|
+
const sweep = opts.sweep ?? Math.PI * 2;
|
|
1441
|
+
const inset = opts.inset ?? 0;
|
|
1442
|
+
const closed = Math.abs(sweep) >= Math.PI * 2;
|
|
1443
|
+
return (bounds, count) => {
|
|
1444
|
+
const rx = bounds.width / 2 - inset;
|
|
1445
|
+
const ry = bounds.height / 2 - inset;
|
|
1446
|
+
const steps = closed ? count : Math.max(count - 1, 1);
|
|
1447
|
+
const out = [];
|
|
1448
|
+
for (let i = 0; i < count; i++) {
|
|
1449
|
+
const angle = start + sweep * i / steps;
|
|
1450
|
+
out.push({
|
|
1451
|
+
x: bounds.x + bounds.width / 2 + rx * Math.cos(angle),
|
|
1452
|
+
y: bounds.y + bounds.height / 2 + ry * Math.sin(angle),
|
|
1453
|
+
angle
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
return out;
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1012
1459
|
|
|
1013
|
-
export { MOVE_ANCHORS, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGuidesLayer, deriveAlignmentGuides, freeform, matchAlignment, matchSpacing, measureGaps, nearest, nearestWithin, none, roundToCell, snapPoint, tileGrid, useGridCellHover, useGuides };
|
|
1460
|
+
export { MOVE_ANCHORS, alignInsertBehavior, alignMoveBehavior, alignResizeBehavior, cellAt, containedThenNearest, createCellHighlightLayer, createGridLayer, createGuidesLayer, deriveAlignmentGuides, freeform, gridSlots, matchAlignment, matchSpacing, measureGaps, nearest, nearestWithin, none, pack, perContainer, quadtree, ringSlots, roundToCell, rowSlots, sequenceLayout, slots, snapPoint, tileGrid, useGridCellHover, useGuides };
|
|
1014
1461
|
//# sourceMappingURL=index.js.map
|
|
1015
1462
|
//# sourceMappingURL=index.js.map
|