@skygraph/core 0.4.0 → 0.5.1
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 +48 -0
- package/dist/{chunk-KP75DEA4.js → chunk-7FA2TBSZ.js} +206 -7
- package/dist/chunk-7FA2TBSZ.js.map +1 -0
- package/dist/graph.cjs +210 -6
- package/dist/graph.cjs.map +1 -1
- package/dist/graph.d.cts +131 -2
- package/dist/graph.d.ts +131 -2
- package/dist/graph.js +11 -1
- package/dist/index.cjs +210 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-KP75DEA4.js.map +0 -1
package/dist/graph.cjs
CHANGED
|
@@ -22,12 +22,17 @@ var graph_exports = {};
|
|
|
22
22
|
__export(graph_exports, {
|
|
23
23
|
aabbFromOBB: () => aabbFromOBB,
|
|
24
24
|
createGraph: () => createGraph,
|
|
25
|
+
floatingAnchor: () => floatingAnchor,
|
|
25
26
|
getBezierPath: () => getBezierPath,
|
|
27
|
+
getEdgePosition: () => getEdgePosition,
|
|
28
|
+
getNodeIntersection: () => getNodeIntersection,
|
|
29
|
+
inferSide: () => inferSide,
|
|
26
30
|
nearestSide: () => nearestSide,
|
|
27
31
|
obbContainsPoint: () => obbContainsPoint,
|
|
28
32
|
obbCorners: () => obbCorners,
|
|
29
33
|
pointsToPath: () => pointsToPath,
|
|
30
34
|
pointsToRoundedPath: () => pointsToRoundedPath,
|
|
35
|
+
resolveEdgeEndpoint: () => resolveEdgeEndpoint,
|
|
31
36
|
routeOrthogonal: () => routeOrthogonal
|
|
32
37
|
});
|
|
33
38
|
module.exports = __toCommonJS(graph_exports);
|
|
@@ -660,22 +665,27 @@ function createGraph(core, options) {
|
|
|
660
665
|
function routeOrthogonal(start, end, options) {
|
|
661
666
|
const opts = typeof options === "string" ? { preferred: options } : options ?? {};
|
|
662
667
|
const gridSize = opts.gridSize ?? 10;
|
|
663
|
-
const stubLength = opts.stubLength ?? Math.max(
|
|
668
|
+
const stubLength = opts.stubLength ?? Math.max(20, gridSize);
|
|
669
|
+
const stepPosition = opts.stepPosition ?? 0.5;
|
|
664
670
|
let sExit = null;
|
|
665
671
|
let sStub = null;
|
|
666
672
|
let sCorner = null;
|
|
673
|
+
let sSide = null;
|
|
667
674
|
if (opts.sourceBounds) {
|
|
668
|
-
const exitInfo = exitOnNearestSide(opts.sourceBounds, end);
|
|
675
|
+
const exitInfo = exitOnNearestSide(opts.sourceBounds, end, start);
|
|
669
676
|
sExit = exitInfo.point;
|
|
677
|
+
sSide = exitInfo.side;
|
|
670
678
|
sStub = extrudePoint(exitInfo.point, exitInfo.side, stubLength);
|
|
671
679
|
sCorner = orthoBridge(start, sExit, exitInfo.side);
|
|
672
680
|
}
|
|
673
681
|
let tExit = null;
|
|
674
682
|
let tStub = null;
|
|
675
683
|
let tCorner = null;
|
|
684
|
+
let tSide = null;
|
|
676
685
|
if (opts.targetBounds) {
|
|
677
|
-
const enterInfo = exitOnNearestSide(opts.targetBounds, start);
|
|
686
|
+
const enterInfo = exitOnNearestSide(opts.targetBounds, start, end);
|
|
678
687
|
tExit = enterInfo.point;
|
|
688
|
+
tSide = enterInfo.side;
|
|
679
689
|
tStub = extrudePoint(enterInfo.point, enterInfo.side, stubLength);
|
|
680
690
|
tCorner = orthoBridge(end, tExit, enterInfo.side);
|
|
681
691
|
}
|
|
@@ -685,8 +695,12 @@ function routeOrthogonal(start, end, options) {
|
|
|
685
695
|
const filteredObstacles = obstacles.length > 0 && (opts.sourceBounds || opts.targetBounds) ? obstacles.filter((o) => o !== opts.sourceBounds && o !== opts.targetBounds) : obstacles;
|
|
686
696
|
let core;
|
|
687
697
|
if (filteredObstacles.length === 0) {
|
|
688
|
-
|
|
689
|
-
|
|
698
|
+
if (sSide && tSide) {
|
|
699
|
+
core = smoothStepRoute(coreStart, sSide, coreEnd, tSide, stepPosition);
|
|
700
|
+
} else {
|
|
701
|
+
const snap2 = opts.snap ?? gridSize;
|
|
702
|
+
core = lRoute(coreStart, coreEnd, opts.preferred ?? "auto", snap2);
|
|
703
|
+
}
|
|
690
704
|
} else {
|
|
691
705
|
const inflate = opts.inflate ?? 0;
|
|
692
706
|
const maxNodes = opts.maxNodes ?? 5e3;
|
|
@@ -710,6 +724,68 @@ function routeOrthogonal(start, end, options) {
|
|
|
710
724
|
if (!pointsEqual(end, points[points.length - 1])) pushIfDistinct(points, end);
|
|
711
725
|
return compressCollinear(points);
|
|
712
726
|
}
|
|
727
|
+
function smoothStepRoute(coreStart, sSide, coreEnd, tSide, stepPosition) {
|
|
728
|
+
const [sx, sy] = coreStart;
|
|
729
|
+
const [tx, ty] = coreEnd;
|
|
730
|
+
const sAxis = sideAxis(sSide);
|
|
731
|
+
const tAxis = sideAxis(tSide);
|
|
732
|
+
const sDir = sideDir(sSide);
|
|
733
|
+
const tDir = sideDir(tSide);
|
|
734
|
+
if (sAxis === tAxis) {
|
|
735
|
+
if (sDir + tDir === 0) {
|
|
736
|
+
if (sAxis === "x") {
|
|
737
|
+
const cx = sx + (tx - sx) * stepPosition;
|
|
738
|
+
return [
|
|
739
|
+
[sx, sy],
|
|
740
|
+
[cx, sy],
|
|
741
|
+
[cx, ty],
|
|
742
|
+
[tx, ty]
|
|
743
|
+
];
|
|
744
|
+
}
|
|
745
|
+
const cy = sy + (ty - sy) * stepPosition;
|
|
746
|
+
return [
|
|
747
|
+
[sx, sy],
|
|
748
|
+
[sx, cy],
|
|
749
|
+
[tx, cy],
|
|
750
|
+
[tx, ty]
|
|
751
|
+
];
|
|
752
|
+
}
|
|
753
|
+
if (sAxis === "x") {
|
|
754
|
+
const extX = sDir > 0 ? Math.max(sx, tx) : Math.min(sx, tx);
|
|
755
|
+
return [
|
|
756
|
+
[sx, sy],
|
|
757
|
+
[extX, sy],
|
|
758
|
+
[extX, ty],
|
|
759
|
+
[tx, ty]
|
|
760
|
+
];
|
|
761
|
+
}
|
|
762
|
+
const extY = sDir > 0 ? Math.max(sy, ty) : Math.min(sy, ty);
|
|
763
|
+
return [
|
|
764
|
+
[sx, sy],
|
|
765
|
+
[sx, extY],
|
|
766
|
+
[tx, extY],
|
|
767
|
+
[tx, ty]
|
|
768
|
+
];
|
|
769
|
+
}
|
|
770
|
+
if (sAxis === "x") {
|
|
771
|
+
return [
|
|
772
|
+
[sx, sy],
|
|
773
|
+
[tx, sy],
|
|
774
|
+
[tx, ty]
|
|
775
|
+
];
|
|
776
|
+
}
|
|
777
|
+
return [
|
|
778
|
+
[sx, sy],
|
|
779
|
+
[sx, ty],
|
|
780
|
+
[tx, ty]
|
|
781
|
+
];
|
|
782
|
+
}
|
|
783
|
+
function sideAxis(s) {
|
|
784
|
+
return s === "left" || s === "right" ? "x" : "y";
|
|
785
|
+
}
|
|
786
|
+
function sideDir(s) {
|
|
787
|
+
return s === "right" || s === "bottom" ? 1 : -1;
|
|
788
|
+
}
|
|
713
789
|
function pointsToPath(points) {
|
|
714
790
|
if (points.length === 0) return "";
|
|
715
791
|
const [head, ...rest] = points;
|
|
@@ -769,6 +845,31 @@ function getBezierPath(opts) {
|
|
|
769
845
|
const [c2x, c2y] = controlPoint(opts.targetSide, tx, ty, sx, sy, curvature);
|
|
770
846
|
return `M ${sx} ${sy} C ${c1x} ${c1y} ${c2x} ${c2y} ${tx} ${ty}`;
|
|
771
847
|
}
|
|
848
|
+
function floatingAnchor(box, opposite) {
|
|
849
|
+
const cx = box.x + box.w / 2;
|
|
850
|
+
const cy = box.y + box.h / 2;
|
|
851
|
+
const dx = opposite[0] - cx;
|
|
852
|
+
const dy = opposite[1] - cy;
|
|
853
|
+
if (dx === 0 && dy === 0) {
|
|
854
|
+
return { point: [box.x + box.w, cy], side: "right" };
|
|
855
|
+
}
|
|
856
|
+
const hw = box.w / 2;
|
|
857
|
+
const hh = box.h / 2;
|
|
858
|
+
const tx = dx === 0 ? Infinity : hw / Math.abs(dx);
|
|
859
|
+
const ty = dy === 0 ? Infinity : hh / Math.abs(dy);
|
|
860
|
+
if (tx < ty) {
|
|
861
|
+
const sign2 = dx > 0 ? 1 : -1;
|
|
862
|
+
return {
|
|
863
|
+
point: [cx + sign2 * hw, cy + sign2 * (hw / Math.abs(dx)) * dy],
|
|
864
|
+
side: sign2 > 0 ? "right" : "left"
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
const sign = dy > 0 ? 1 : -1;
|
|
868
|
+
return {
|
|
869
|
+
point: [cx + sign * (hh / Math.abs(dy)) * dx, cy + sign * hh],
|
|
870
|
+
side: sign > 0 ? "bottom" : "top"
|
|
871
|
+
};
|
|
872
|
+
}
|
|
772
873
|
function nearestSide(box, target) {
|
|
773
874
|
const cx = box.x + box.w / 2;
|
|
774
875
|
const cy = box.y + box.h / 2;
|
|
@@ -777,9 +878,107 @@ function nearestSide(box, target) {
|
|
|
777
878
|
if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? "right" : "left";
|
|
778
879
|
return dy >= 0 ? "bottom" : "top";
|
|
779
880
|
}
|
|
780
|
-
function
|
|
881
|
+
function getNodeIntersection(intersectionBox, targetCenter) {
|
|
882
|
+
const w = intersectionBox.w / 2;
|
|
883
|
+
const h = intersectionBox.h / 2;
|
|
884
|
+
const x2 = intersectionBox.x + w;
|
|
885
|
+
const y2 = intersectionBox.y + h;
|
|
886
|
+
if (w === 0 || h === 0) return [x2, y2];
|
|
887
|
+
const x1 = targetCenter[0];
|
|
888
|
+
const y1 = targetCenter[1];
|
|
889
|
+
const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
|
|
890
|
+
const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
|
|
891
|
+
const denom = Math.abs(xx1) + Math.abs(yy1);
|
|
892
|
+
if (denom === 0) {
|
|
893
|
+
return [intersectionBox.x + intersectionBox.w, y2];
|
|
894
|
+
}
|
|
895
|
+
const a = 1 / denom;
|
|
896
|
+
const xx3 = a * xx1;
|
|
897
|
+
const yy3 = a * yy1;
|
|
898
|
+
const x = w * (xx3 + yy3) + x2;
|
|
899
|
+
const y = h * (-xx3 + yy3) + y2;
|
|
900
|
+
return [x, y];
|
|
901
|
+
}
|
|
902
|
+
function getEdgePosition(box, intersectionPoint) {
|
|
903
|
+
const nx = Math.round(box.x);
|
|
904
|
+
const ny = Math.round(box.y);
|
|
905
|
+
const px = Math.round(intersectionPoint[0]);
|
|
906
|
+
const py = Math.round(intersectionPoint[1]);
|
|
907
|
+
if (px <= nx + 1) return "left";
|
|
908
|
+
if (px >= nx + box.w - 1) return "right";
|
|
909
|
+
if (py <= ny + 1) return "top";
|
|
910
|
+
if (py >= ny + box.h - 1) return "bottom";
|
|
911
|
+
return "top";
|
|
912
|
+
}
|
|
913
|
+
function resolveEdgeEndpoint(sourceBox, targetBox, padding = 0) {
|
|
914
|
+
const targetCenter = [targetBox.x + targetBox.w / 2, targetBox.y + targetBox.h / 2];
|
|
915
|
+
const raw = getNodeIntersection(sourceBox, targetCenter);
|
|
916
|
+
const side = getEdgePosition(sourceBox, raw);
|
|
917
|
+
if (padding === 0) return { point: raw, side };
|
|
918
|
+
let point = raw;
|
|
919
|
+
switch (side) {
|
|
920
|
+
case "right":
|
|
921
|
+
point = [raw[0] + padding, raw[1]];
|
|
922
|
+
break;
|
|
923
|
+
case "left":
|
|
924
|
+
point = [raw[0] - padding, raw[1]];
|
|
925
|
+
break;
|
|
926
|
+
case "bottom":
|
|
927
|
+
point = [raw[0], raw[1] + padding];
|
|
928
|
+
break;
|
|
929
|
+
case "top":
|
|
930
|
+
point = [raw[0], raw[1] - padding];
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
return { point, side };
|
|
934
|
+
}
|
|
935
|
+
function inferSide(box, anchor, opposite, tolerance = 1) {
|
|
936
|
+
const onLeft = Math.abs(anchor[0] - box.x) <= tolerance;
|
|
937
|
+
const onRight = Math.abs(anchor[0] - (box.x + box.w)) <= tolerance;
|
|
938
|
+
const onTop = Math.abs(anchor[1] - box.y) <= tolerance;
|
|
939
|
+
const onBottom = Math.abs(anchor[1] - (box.y + box.h)) <= tolerance;
|
|
940
|
+
const horizontalHit = onLeft || onRight;
|
|
941
|
+
const verticalHit = onTop || onBottom;
|
|
942
|
+
if (horizontalHit && verticalHit) {
|
|
943
|
+
const cx = box.x + box.w / 2;
|
|
944
|
+
const cy = box.y + box.h / 2;
|
|
945
|
+
const dx = opposite[0] - cx;
|
|
946
|
+
const dy = opposite[1] - cy;
|
|
947
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
948
|
+
return { side: onRight ? "right" : "left", confident: true };
|
|
949
|
+
}
|
|
950
|
+
return { side: onBottom ? "bottom" : "top", confident: true };
|
|
951
|
+
}
|
|
952
|
+
if (onLeft) return { side: "left", confident: true };
|
|
953
|
+
if (onRight) return { side: "right", confident: true };
|
|
954
|
+
if (onTop) return { side: "top", confident: true };
|
|
955
|
+
if (onBottom) return { side: "bottom", confident: true };
|
|
956
|
+
return { side: nearestSide(box, opposite), confident: false };
|
|
957
|
+
}
|
|
958
|
+
function exitOnNearestSide(box, target, anchor) {
|
|
781
959
|
const cx = box.x + box.w / 2;
|
|
782
960
|
const cy = box.y + box.h / 2;
|
|
961
|
+
if (anchor) {
|
|
962
|
+
const tolerance = 1;
|
|
963
|
+
const onLeft = Math.abs(anchor[0] - box.x) <= tolerance;
|
|
964
|
+
const onRight = Math.abs(anchor[0] - (box.x + box.w)) <= tolerance;
|
|
965
|
+
const onTop = Math.abs(anchor[1] - box.y) <= tolerance;
|
|
966
|
+
const onBottom = Math.abs(anchor[1] - (box.y + box.h)) <= tolerance;
|
|
967
|
+
const horizontalHit = onLeft || onRight;
|
|
968
|
+
const verticalHit = onTop || onBottom;
|
|
969
|
+
if (horizontalHit && verticalHit) {
|
|
970
|
+
const dx2 = target[0] - cx;
|
|
971
|
+
const dy2 = target[1] - cy;
|
|
972
|
+
if (Math.abs(dx2) >= Math.abs(dy2)) {
|
|
973
|
+
return onRight ? { point: [box.x + box.w, cy], side: "right" } : { point: [box.x, cy], side: "left" };
|
|
974
|
+
}
|
|
975
|
+
return onBottom ? { point: [cx, box.y + box.h], side: "bottom" } : { point: [cx, box.y], side: "top" };
|
|
976
|
+
}
|
|
977
|
+
if (onRight) return { point: [box.x + box.w, cy], side: "right" };
|
|
978
|
+
if (onLeft) return { point: [box.x, cy], side: "left" };
|
|
979
|
+
if (onBottom) return { point: [cx, box.y + box.h], side: "bottom" };
|
|
980
|
+
if (onTop) return { point: [cx, box.y], side: "top" };
|
|
981
|
+
}
|
|
783
982
|
const dx = target[0] - cx;
|
|
784
983
|
const dy = target[1] - cy;
|
|
785
984
|
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
@@ -1054,12 +1253,17 @@ function obbContainsPoint(obb, p) {
|
|
|
1054
1253
|
0 && (module.exports = {
|
|
1055
1254
|
aabbFromOBB,
|
|
1056
1255
|
createGraph,
|
|
1256
|
+
floatingAnchor,
|
|
1057
1257
|
getBezierPath,
|
|
1258
|
+
getEdgePosition,
|
|
1259
|
+
getNodeIntersection,
|
|
1260
|
+
inferSide,
|
|
1058
1261
|
nearestSide,
|
|
1059
1262
|
obbContainsPoint,
|
|
1060
1263
|
obbCorners,
|
|
1061
1264
|
pointsToPath,
|
|
1062
1265
|
pointsToRoundedPath,
|
|
1266
|
+
resolveEdgeEndpoint,
|
|
1063
1267
|
routeOrthogonal
|
|
1064
1268
|
});
|
|
1065
1269
|
//# sourceMappingURL=graph.cjs.map
|