@ngroznykh/papirus 0.7.2 → 0.8.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/dist/core/ConnectionManager.d.ts +5 -0
- package/dist/core/ConnectionManager.d.ts.map +1 -1
- package/dist/core/DiagramRenderer.d.ts.map +1 -1
- package/dist/core/EdgeEndpointUpdater.d.ts.map +1 -1
- package/dist/elements/Edge.d.ts.map +1 -1
- package/dist/elements/Node.d.ts +25 -0
- package/dist/elements/Node.d.ts.map +1 -1
- package/dist/elements/composite/CompositeNode.d.ts.map +1 -1
- package/dist/elements/paths/PathStrategy.d.ts +2 -0
- package/dist/elements/paths/PathStrategy.d.ts.map +1 -1
- package/dist/elements/paths/PolylinePathStrategy.d.ts.map +1 -1
- package/dist/elements/paths/index.d.ts +2 -0
- package/dist/elements/paths/index.d.ts.map +1 -1
- package/dist/elements/paths/routeOrthogonalAround.d.ts +35 -0
- package/dist/elements/paths/routeOrthogonalAround.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/papirus.js +739 -38
- package/dist/papirus.js.map +1 -1
- package/dist/utils/resolveContentInset.d.ts +16 -0
- package/dist/utils/resolveContentInset.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/papirus.js
CHANGED
|
@@ -1976,15 +1976,37 @@ var ConnectionManager = class extends EventEmitter {
|
|
|
1976
1976
|
if (toNode) {
|
|
1977
1977
|
const target = this.getTargetPointForReconnect(edge, toNode, "to", snappedPoint);
|
|
1978
1978
|
const toDir = this.getTargetDirForReconnect(edge, "to");
|
|
1979
|
-
|
|
1979
|
+
const sourceId = targetNode?.id ?? edge.from.nodeId;
|
|
1980
|
+
edge.updateEndpoints(snappedPoint, target, snappedDir, toDir, this.buildReconnectRoutingOptions(sourceId, toNode.id));
|
|
1980
1981
|
}
|
|
1981
1982
|
} else if (fromNode) {
|
|
1982
1983
|
const start = this.getTargetPointForReconnect(edge, fromNode, "from", snappedPoint);
|
|
1983
1984
|
const fromDir = this.getTargetDirForReconnect(edge, "from");
|
|
1984
|
-
edge.updateEndpoints(start, snappedPoint, fromDir, snappedDir);
|
|
1985
|
+
edge.updateEndpoints(start, snappedPoint, fromDir, snappedDir, this.buildReconnectRoutingOptions(fromNode.id, targetNode?.id));
|
|
1985
1986
|
}
|
|
1986
1987
|
this.renderer.markDirty();
|
|
1987
1988
|
}
|
|
1989
|
+
/**
|
|
1990
|
+
* Match DiagramRenderer.getNodeObstacles padding and EdgeEndpointUpdater roles
|
|
1991
|
+
* so reconnect preview uses the same polyline routing as updateAll.
|
|
1992
|
+
*/
|
|
1993
|
+
buildReconnectRoutingOptions(sourceNodeId, targetNodeId) {
|
|
1994
|
+
const obstacles = [];
|
|
1995
|
+
for (const node of this.renderer.nodes.values()) {
|
|
1996
|
+
let role = "other";
|
|
1997
|
+
if (node.id === sourceNodeId) role = "source";
|
|
1998
|
+
else if (node.id === targetNodeId) role = "target";
|
|
1999
|
+
obstacles.push({
|
|
2000
|
+
id: node.id,
|
|
2001
|
+
x: node.x - 8,
|
|
2002
|
+
y: node.y - 8,
|
|
2003
|
+
width: node.width + 16,
|
|
2004
|
+
height: node.height + 16,
|
|
2005
|
+
role
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
return { obstacles };
|
|
2009
|
+
}
|
|
1988
2010
|
getTargetPointForReconnect(edge, node, endpoint, fallbackPoint) {
|
|
1989
2011
|
const ep = endpoint === "from" ? edge.from : edge.to;
|
|
1990
2012
|
if (this.attachToOutline && ep.outlineParam !== void 0) return node.getConnectionPointAtOutlineParam(ep.outlineParam);
|
|
@@ -3777,7 +3799,517 @@ var StraightPathStrategy = class {
|
|
|
3777
3799
|
}
|
|
3778
3800
|
};
|
|
3779
3801
|
//#endregion
|
|
3802
|
+
//#region src/elements/paths/routeOrthogonalAround.ts
|
|
3803
|
+
var DEFAULT_MARGIN = 12;
|
|
3804
|
+
var DEFAULT_EXIT = 20;
|
|
3805
|
+
var BEND_PENALTY = .001;
|
|
3806
|
+
function expandRect(r, m) {
|
|
3807
|
+
return {
|
|
3808
|
+
...r,
|
|
3809
|
+
x: r.x - m,
|
|
3810
|
+
y: r.y - m,
|
|
3811
|
+
width: r.width + 2 * m,
|
|
3812
|
+
height: r.height + 2 * m
|
|
3813
|
+
};
|
|
3814
|
+
}
|
|
3815
|
+
function moveByDir$1(p, dir, d) {
|
|
3816
|
+
switch (dir) {
|
|
3817
|
+
case "top": return {
|
|
3818
|
+
x: p.x,
|
|
3819
|
+
y: p.y - d
|
|
3820
|
+
};
|
|
3821
|
+
case "bottom": return {
|
|
3822
|
+
x: p.x,
|
|
3823
|
+
y: p.y + d
|
|
3824
|
+
};
|
|
3825
|
+
case "left": return {
|
|
3826
|
+
x: p.x - d,
|
|
3827
|
+
y: p.y
|
|
3828
|
+
};
|
|
3829
|
+
case "right": return {
|
|
3830
|
+
x: p.x + d,
|
|
3831
|
+
y: p.y
|
|
3832
|
+
};
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
function pointInOpenInterior(p, r) {
|
|
3836
|
+
return p.x > r.x && p.x < r.x + r.width && p.y > r.y && p.y < r.y + r.height;
|
|
3837
|
+
}
|
|
3838
|
+
/** Axis-aligned segment vs open interior of a rect (boundary grazing allowed). */
|
|
3839
|
+
function segmentIntersectsOpenInterior(a, b, r) {
|
|
3840
|
+
if (Math.abs(a.x - b.x) < .001) {
|
|
3841
|
+
const x = a.x;
|
|
3842
|
+
const y1 = Math.min(a.y, b.y);
|
|
3843
|
+
const y2 = Math.max(a.y, b.y);
|
|
3844
|
+
return x > r.x && x < r.x + r.width && y2 > r.y && y1 < r.y + r.height;
|
|
3845
|
+
}
|
|
3846
|
+
if (Math.abs(a.y - b.y) < .001) {
|
|
3847
|
+
const y = a.y;
|
|
3848
|
+
const x1 = Math.min(a.x, b.x);
|
|
3849
|
+
const x2 = Math.max(a.x, b.x);
|
|
3850
|
+
return y > r.y && y < r.y + r.height && x2 > r.x && x1 < r.x + r.width;
|
|
3851
|
+
}
|
|
3852
|
+
return true;
|
|
3853
|
+
}
|
|
3854
|
+
function segmentHitsObstacles(a, b, obstacles) {
|
|
3855
|
+
return obstacles.some((o) => segmentIntersectsOpenInterior(a, b, o));
|
|
3856
|
+
}
|
|
3857
|
+
function manhattan$1(a, b) {
|
|
3858
|
+
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
3859
|
+
}
|
|
3860
|
+
/** Push along dir until outside expanded parent (and at least exitDistance). */
|
|
3861
|
+
function computeStartExit(from, fromDir, parent, margin, exitDistance) {
|
|
3862
|
+
let p = moveByDir$1(from, fromDir, exitDistance);
|
|
3863
|
+
if (!parent) return p;
|
|
3864
|
+
const expanded = expandRect(parent, margin);
|
|
3865
|
+
let guard = 0;
|
|
3866
|
+
while (pointInOpenInterior(p, expanded) && guard++ < 64) p = moveByDir$1(p, fromDir, exitDistance);
|
|
3867
|
+
if (pointInOpenInterior(p, expanded)) {
|
|
3868
|
+
const e = expanded;
|
|
3869
|
+
switch (fromDir) {
|
|
3870
|
+
case "left":
|
|
3871
|
+
p = {
|
|
3872
|
+
x: e.x - 1,
|
|
3873
|
+
y: from.y
|
|
3874
|
+
};
|
|
3875
|
+
break;
|
|
3876
|
+
case "right":
|
|
3877
|
+
p = {
|
|
3878
|
+
x: e.x + e.width + 1,
|
|
3879
|
+
y: from.y
|
|
3880
|
+
};
|
|
3881
|
+
break;
|
|
3882
|
+
case "top":
|
|
3883
|
+
p = {
|
|
3884
|
+
x: from.x,
|
|
3885
|
+
y: e.y - 1
|
|
3886
|
+
};
|
|
3887
|
+
break;
|
|
3888
|
+
case "bottom":
|
|
3889
|
+
p = {
|
|
3890
|
+
x: from.x,
|
|
3891
|
+
y: e.y + e.height + 1
|
|
3892
|
+
};
|
|
3893
|
+
break;
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
return p;
|
|
3897
|
+
}
|
|
3898
|
+
function computeEndEntry(to, toDir, exitDistance) {
|
|
3899
|
+
return moveByDir$1(to, toDir, exitDistance);
|
|
3900
|
+
}
|
|
3901
|
+
/**
|
|
3902
|
+
* When nodes face each other closer than 2×exitDistance, fixed stubs cross
|
|
3903
|
+
* (startExit past endEntry). That draws a U-turn "tail" at the first bend.
|
|
3904
|
+
* Meet both stubs on the mid-gap instead.
|
|
3905
|
+
*/
|
|
3906
|
+
function clampFacingExitEntry(from, to, fromDir, toDir, startExit, endEntry) {
|
|
3907
|
+
if (fromDir === "bottom" && toDir === "top" && to.y > from.y && startExit.y > endEntry.y) {
|
|
3908
|
+
const midY = (from.y + to.y) / 2;
|
|
3909
|
+
return {
|
|
3910
|
+
startExit: {
|
|
3911
|
+
x: startExit.x,
|
|
3912
|
+
y: midY
|
|
3913
|
+
},
|
|
3914
|
+
endEntry: {
|
|
3915
|
+
x: endEntry.x,
|
|
3916
|
+
y: midY
|
|
3917
|
+
}
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
if (fromDir === "top" && toDir === "bottom" && to.y < from.y && startExit.y < endEntry.y) {
|
|
3921
|
+
const midY = (from.y + to.y) / 2;
|
|
3922
|
+
return {
|
|
3923
|
+
startExit: {
|
|
3924
|
+
x: startExit.x,
|
|
3925
|
+
y: midY
|
|
3926
|
+
},
|
|
3927
|
+
endEntry: {
|
|
3928
|
+
x: endEntry.x,
|
|
3929
|
+
y: midY
|
|
3930
|
+
}
|
|
3931
|
+
};
|
|
3932
|
+
}
|
|
3933
|
+
if (fromDir === "right" && toDir === "left" && to.x > from.x && startExit.x > endEntry.x) {
|
|
3934
|
+
const midX = (from.x + to.x) / 2;
|
|
3935
|
+
return {
|
|
3936
|
+
startExit: {
|
|
3937
|
+
x: midX,
|
|
3938
|
+
y: startExit.y
|
|
3939
|
+
},
|
|
3940
|
+
endEntry: {
|
|
3941
|
+
x: midX,
|
|
3942
|
+
y: endEntry.y
|
|
3943
|
+
}
|
|
3944
|
+
};
|
|
3945
|
+
}
|
|
3946
|
+
if (fromDir === "left" && toDir === "right" && to.x < from.x && startExit.x < endEntry.x) {
|
|
3947
|
+
const midX = (from.x + to.x) / 2;
|
|
3948
|
+
return {
|
|
3949
|
+
startExit: {
|
|
3950
|
+
x: midX,
|
|
3951
|
+
y: startExit.y
|
|
3952
|
+
},
|
|
3953
|
+
endEntry: {
|
|
3954
|
+
x: midX,
|
|
3955
|
+
y: endEntry.y
|
|
3956
|
+
}
|
|
3957
|
+
};
|
|
3958
|
+
}
|
|
3959
|
+
return {
|
|
3960
|
+
startExit,
|
|
3961
|
+
endEntry
|
|
3962
|
+
};
|
|
3963
|
+
}
|
|
3964
|
+
function simplifyPath$1(path) {
|
|
3965
|
+
const out = [];
|
|
3966
|
+
for (const p of path) {
|
|
3967
|
+
const prev = out[out.length - 1];
|
|
3968
|
+
if (!prev || Math.abs(prev.x - p.x) > .001 || Math.abs(prev.y - p.y) > .001) out.push(p);
|
|
3969
|
+
}
|
|
3970
|
+
let i = 0;
|
|
3971
|
+
while (i + 2 < out.length) {
|
|
3972
|
+
const a = out[i];
|
|
3973
|
+
const b = out[i + 1];
|
|
3974
|
+
const c = out[i + 2];
|
|
3975
|
+
const colinear = Math.abs(a.x - b.x) < .001 && Math.abs(b.x - c.x) < .001 || Math.abs(a.y - b.y) < .001 && Math.abs(b.y - c.y) < .001;
|
|
3976
|
+
if (Math.abs(a.x - b.x) < .001 && Math.abs(b.x - c.x) < .001 && (a.y < c.y && c.y < b.y || b.y < c.y && c.y < a.y) || Math.abs(a.y - b.y) < .001 && Math.abs(b.y - c.y) < .001 && (a.x < c.x && c.x < b.x || b.x < c.x && c.x < a.x) || colinear && i > 0 && i + 2 < out.length - 1) out.splice(i + 1, 1);
|
|
3977
|
+
else i++;
|
|
3978
|
+
}
|
|
3979
|
+
return out;
|
|
3980
|
+
}
|
|
3981
|
+
function unionBBox(obstacles) {
|
|
3982
|
+
let x1 = Infinity;
|
|
3983
|
+
let y1 = Infinity;
|
|
3984
|
+
let x2 = -Infinity;
|
|
3985
|
+
let y2 = -Infinity;
|
|
3986
|
+
for (const o of obstacles) {
|
|
3987
|
+
x1 = Math.min(x1, o.x);
|
|
3988
|
+
y1 = Math.min(y1, o.y);
|
|
3989
|
+
x2 = Math.max(x2, o.x + o.width);
|
|
3990
|
+
y2 = Math.max(y2, o.y + o.height);
|
|
3991
|
+
}
|
|
3992
|
+
return {
|
|
3993
|
+
x: x1,
|
|
3994
|
+
y: y1,
|
|
3995
|
+
width: x2 - x1,
|
|
3996
|
+
height: y2 - y1
|
|
3997
|
+
};
|
|
3998
|
+
}
|
|
3999
|
+
function collectLatticeCoords(startExit, endEntry, obstacles, parent, target, margin) {
|
|
4000
|
+
const xSet = new Set([startExit.x, endEntry.x]);
|
|
4001
|
+
const ySet = new Set([startExit.y, endEntry.y]);
|
|
4002
|
+
for (const o of obstacles) {
|
|
4003
|
+
xSet.add(o.x);
|
|
4004
|
+
xSet.add(o.x + o.width);
|
|
4005
|
+
ySet.add(o.y);
|
|
4006
|
+
ySet.add(o.y + o.height);
|
|
4007
|
+
xSet.add(o.x - 1);
|
|
4008
|
+
xSet.add(o.x + o.width + 1);
|
|
4009
|
+
ySet.add(o.y - 1);
|
|
4010
|
+
ySet.add(o.y + o.height + 1);
|
|
4011
|
+
}
|
|
4012
|
+
if (parent && target && margin != null) {
|
|
4013
|
+
const ep = expandRect(parent, margin);
|
|
4014
|
+
const et = expandRect(target, margin);
|
|
4015
|
+
const addGapY = (gapY) => {
|
|
4016
|
+
ySet.add(gapY);
|
|
4017
|
+
const left = Math.min(ep.x, et.x, startExit.x, endEntry.x);
|
|
4018
|
+
const right = Math.max(ep.x + ep.width, et.x + et.width, startExit.x, endEntry.x);
|
|
4019
|
+
xSet.add(left);
|
|
4020
|
+
xSet.add(right);
|
|
4021
|
+
xSet.add(startExit.x);
|
|
4022
|
+
xSet.add(endEntry.x);
|
|
4023
|
+
};
|
|
4024
|
+
const addGapX = (gapX) => {
|
|
4025
|
+
xSet.add(gapX);
|
|
4026
|
+
const top = Math.min(ep.y, et.y, startExit.y, endEntry.y);
|
|
4027
|
+
const bottom = Math.max(ep.y + ep.height, et.y + et.height, startExit.y, endEntry.y);
|
|
4028
|
+
ySet.add(top);
|
|
4029
|
+
ySet.add(bottom);
|
|
4030
|
+
ySet.add(startExit.y);
|
|
4031
|
+
ySet.add(endEntry.y);
|
|
4032
|
+
};
|
|
4033
|
+
if (et.y + et.height < ep.y) addGapY((et.y + et.height + ep.y) / 2);
|
|
4034
|
+
if (ep.y + ep.height < et.y) addGapY((ep.y + ep.height + et.y) / 2);
|
|
4035
|
+
if (et.x + et.width < ep.x) addGapX((et.x + et.width + ep.x) / 2);
|
|
4036
|
+
if (ep.x + ep.width < et.x) addGapX((ep.x + ep.width + et.x) / 2);
|
|
4037
|
+
}
|
|
4038
|
+
return {
|
|
4039
|
+
xs: [...xSet].sort((a, b) => a - b),
|
|
4040
|
+
ys: [...ySet].sort((a, b) => a - b)
|
|
4041
|
+
};
|
|
4042
|
+
}
|
|
4043
|
+
function buildCornerGraph(startExit, endEntry, obstacles, parent, target, margin) {
|
|
4044
|
+
const { xs, ys } = collectLatticeCoords(startExit, endEntry, obstacles, parent, target, margin);
|
|
4045
|
+
const nodes = [];
|
|
4046
|
+
const nodeIndex = /* @__PURE__ */ new Map();
|
|
4047
|
+
const nodeKey = (x, y) => `${x}|${y}`;
|
|
4048
|
+
const addNode = (x, y) => {
|
|
4049
|
+
const key = nodeKey(x, y);
|
|
4050
|
+
if (nodeIndex.has(key)) return;
|
|
4051
|
+
const point = {
|
|
4052
|
+
x,
|
|
4053
|
+
y
|
|
4054
|
+
};
|
|
4055
|
+
if (obstacles.some((o) => pointInOpenInterior(point, o))) return;
|
|
4056
|
+
nodeIndex.set(key, nodes.length);
|
|
4057
|
+
nodes.push(point);
|
|
4058
|
+
};
|
|
4059
|
+
for (const x of xs) for (const y of ys) addNode(x, y);
|
|
4060
|
+
addNode(startExit.x, startExit.y);
|
|
4061
|
+
addNode(endEntry.x, endEntry.y);
|
|
4062
|
+
const startIdx = nodeIndex.get(nodeKey(startExit.x, startExit.y));
|
|
4063
|
+
const goalIdx = nodeIndex.get(nodeKey(endEntry.x, endEntry.y));
|
|
4064
|
+
if (startIdx == null || goalIdx == null) return null;
|
|
4065
|
+
const edges = /* @__PURE__ */ new Map();
|
|
4066
|
+
const pushEdge = (a, b, dir, length) => {
|
|
4067
|
+
const list = edges.get(a) ?? [];
|
|
4068
|
+
list.push({
|
|
4069
|
+
to: b,
|
|
4070
|
+
dir,
|
|
4071
|
+
length
|
|
4072
|
+
});
|
|
4073
|
+
edges.set(a, list);
|
|
4074
|
+
};
|
|
4075
|
+
const byX = /* @__PURE__ */ new Map();
|
|
4076
|
+
const byY = /* @__PURE__ */ new Map();
|
|
4077
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
4078
|
+
const node = nodes[i];
|
|
4079
|
+
const xsList = byX.get(node.x) ?? [];
|
|
4080
|
+
xsList.push(i);
|
|
4081
|
+
byX.set(node.x, xsList);
|
|
4082
|
+
const ysList = byY.get(node.y) ?? [];
|
|
4083
|
+
ysList.push(i);
|
|
4084
|
+
byY.set(node.y, ysList);
|
|
4085
|
+
}
|
|
4086
|
+
for (const list of byX.values()) {
|
|
4087
|
+
list.sort((a, b) => nodes[a].y - nodes[b].y);
|
|
4088
|
+
for (let i = 1; i < list.length; i++) {
|
|
4089
|
+
const a = nodes[list[i - 1]];
|
|
4090
|
+
const b = nodes[list[i]];
|
|
4091
|
+
if (!segmentHitsObstacles(a, b, obstacles)) {
|
|
4092
|
+
const length = manhattan$1(a, b);
|
|
4093
|
+
pushEdge(list[i - 1], list[i], "v", length);
|
|
4094
|
+
pushEdge(list[i], list[i - 1], "v", length);
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
for (const list of byY.values()) {
|
|
4099
|
+
list.sort((a, b) => nodes[a].x - nodes[b].x);
|
|
4100
|
+
for (let i = 1; i < list.length; i++) {
|
|
4101
|
+
const a = nodes[list[i - 1]];
|
|
4102
|
+
const b = nodes[list[i]];
|
|
4103
|
+
if (!segmentHitsObstacles(a, b, obstacles)) {
|
|
4104
|
+
const length = manhattan$1(a, b);
|
|
4105
|
+
pushEdge(list[i - 1], list[i], "h", length);
|
|
4106
|
+
pushEdge(list[i], list[i - 1], "h", length);
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
return {
|
|
4111
|
+
nodes,
|
|
4112
|
+
edges,
|
|
4113
|
+
startIdx,
|
|
4114
|
+
goalIdx
|
|
4115
|
+
};
|
|
4116
|
+
}
|
|
4117
|
+
function searchMidPath(nodes, edges, startIdx, goalIdx) {
|
|
4118
|
+
const makeStateKey = (node, dir) => `${node}:${dir}`;
|
|
4119
|
+
const open = [];
|
|
4120
|
+
const best = /* @__PURE__ */ new Map();
|
|
4121
|
+
const prev = /* @__PURE__ */ new Map();
|
|
4122
|
+
const startKey = makeStateKey(startIdx, "s");
|
|
4123
|
+
best.set(startKey, 0);
|
|
4124
|
+
open.push({
|
|
4125
|
+
node: startIdx,
|
|
4126
|
+
dir: "s",
|
|
4127
|
+
g: 0,
|
|
4128
|
+
f: manhattan$1(nodes[startIdx], nodes[goalIdx]),
|
|
4129
|
+
key: startKey
|
|
4130
|
+
});
|
|
4131
|
+
let goalKey = null;
|
|
4132
|
+
while (open.length > 0) {
|
|
4133
|
+
let bestIdx = 0;
|
|
4134
|
+
for (let i = 1; i < open.length; i++) if (open[i].f < open[bestIdx].f) bestIdx = i;
|
|
4135
|
+
const current = open.splice(bestIdx, 1)[0];
|
|
4136
|
+
if (current.node === goalIdx) {
|
|
4137
|
+
goalKey = current.key;
|
|
4138
|
+
break;
|
|
4139
|
+
}
|
|
4140
|
+
for (const edge of edges.get(current.node) ?? []) {
|
|
4141
|
+
const nextDir = edge.dir;
|
|
4142
|
+
let stepCost = edge.length;
|
|
4143
|
+
if (current.dir !== "s" && current.dir !== nextDir) stepCost += BEND_PENALTY;
|
|
4144
|
+
const nextG = current.g + stepCost;
|
|
4145
|
+
const nextKey = makeStateKey(edge.to, nextDir);
|
|
4146
|
+
if (nextG >= (best.get(nextKey) ?? Infinity)) continue;
|
|
4147
|
+
best.set(nextKey, nextG);
|
|
4148
|
+
prev.set(nextKey, current.key);
|
|
4149
|
+
open.push({
|
|
4150
|
+
node: edge.to,
|
|
4151
|
+
dir: nextDir,
|
|
4152
|
+
g: nextG,
|
|
4153
|
+
f: nextG + manhattan$1(nodes[edge.to], nodes[goalIdx]),
|
|
4154
|
+
key: nextKey
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
if (!goalKey) return null;
|
|
4159
|
+
const path = [];
|
|
4160
|
+
let key = goalKey;
|
|
4161
|
+
while (key) {
|
|
4162
|
+
const nodeIdx = Number(key.split(":")[0]);
|
|
4163
|
+
path.unshift(nodes[nodeIdx]);
|
|
4164
|
+
key = prev.get(key);
|
|
4165
|
+
}
|
|
4166
|
+
return path;
|
|
4167
|
+
}
|
|
4168
|
+
function pathIsValid(path, obstacles) {
|
|
4169
|
+
for (let i = 1; i < path.length; i++) if (segmentHitsObstacles(path[i - 1], path[i], obstacles)) return false;
|
|
4170
|
+
return true;
|
|
4171
|
+
}
|
|
4172
|
+
function fallbackOuterRoute(startExit, endEntry, obstacles) {
|
|
4173
|
+
if (obstacles.length === 0) {
|
|
4174
|
+
if (Math.abs(startExit.x - endEntry.x) < .001 || Math.abs(startExit.y - endEntry.y) < .001) return [startExit, endEntry];
|
|
4175
|
+
return [
|
|
4176
|
+
startExit,
|
|
4177
|
+
{
|
|
4178
|
+
x: startExit.x,
|
|
4179
|
+
y: endEntry.y
|
|
4180
|
+
},
|
|
4181
|
+
endEntry
|
|
4182
|
+
];
|
|
4183
|
+
}
|
|
4184
|
+
const u = unionBBox(obstacles);
|
|
4185
|
+
const pad = 1;
|
|
4186
|
+
const top = u.y - pad;
|
|
4187
|
+
const bottom = u.y + u.height + pad;
|
|
4188
|
+
const left = u.x - pad;
|
|
4189
|
+
const right = u.x + u.width + pad;
|
|
4190
|
+
const candidates = [
|
|
4191
|
+
[
|
|
4192
|
+
startExit,
|
|
4193
|
+
{
|
|
4194
|
+
x: startExit.x,
|
|
4195
|
+
y: top
|
|
4196
|
+
},
|
|
4197
|
+
{
|
|
4198
|
+
x: endEntry.x,
|
|
4199
|
+
y: top
|
|
4200
|
+
},
|
|
4201
|
+
endEntry
|
|
4202
|
+
],
|
|
4203
|
+
[
|
|
4204
|
+
startExit,
|
|
4205
|
+
{
|
|
4206
|
+
x: startExit.x,
|
|
4207
|
+
y: bottom
|
|
4208
|
+
},
|
|
4209
|
+
{
|
|
4210
|
+
x: endEntry.x,
|
|
4211
|
+
y: bottom
|
|
4212
|
+
},
|
|
4213
|
+
endEntry
|
|
4214
|
+
],
|
|
4215
|
+
[
|
|
4216
|
+
startExit,
|
|
4217
|
+
{
|
|
4218
|
+
x: left,
|
|
4219
|
+
y: startExit.y
|
|
4220
|
+
},
|
|
4221
|
+
{
|
|
4222
|
+
x: left,
|
|
4223
|
+
y: endEntry.y
|
|
4224
|
+
},
|
|
4225
|
+
endEntry
|
|
4226
|
+
],
|
|
4227
|
+
[
|
|
4228
|
+
startExit,
|
|
4229
|
+
{
|
|
4230
|
+
x: right,
|
|
4231
|
+
y: startExit.y
|
|
4232
|
+
},
|
|
4233
|
+
{
|
|
4234
|
+
x: right,
|
|
4235
|
+
y: endEntry.y
|
|
4236
|
+
},
|
|
4237
|
+
endEntry
|
|
4238
|
+
]
|
|
4239
|
+
];
|
|
4240
|
+
let best = null;
|
|
4241
|
+
let bestLen = Infinity;
|
|
4242
|
+
for (const candidate of candidates) {
|
|
4243
|
+
const simplified = simplifyPath$1(candidate);
|
|
4244
|
+
if (!pathIsValid(simplified, obstacles)) continue;
|
|
4245
|
+
const len = simplified.reduce((sum, p, i) => {
|
|
4246
|
+
if (i === 0) return 0;
|
|
4247
|
+
return sum + manhattan$1(simplified[i - 1], p);
|
|
4248
|
+
}, 0);
|
|
4249
|
+
if (len < bestLen) {
|
|
4250
|
+
bestLen = len;
|
|
4251
|
+
best = simplified;
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
return best ?? [
|
|
4255
|
+
startExit,
|
|
4256
|
+
{
|
|
4257
|
+
x: startExit.x,
|
|
4258
|
+
y: endEntry.y
|
|
4259
|
+
},
|
|
4260
|
+
endEntry
|
|
4261
|
+
];
|
|
4262
|
+
}
|
|
4263
|
+
function routeMidPath(startExit, endEntry, obstacles, parent, target, margin) {
|
|
4264
|
+
if (Math.abs(startExit.x - endEntry.x) < .001 || Math.abs(startExit.y - endEntry.y) < .001) {
|
|
4265
|
+
const direct = [startExit, endEntry];
|
|
4266
|
+
if (pathIsValid(direct, obstacles)) return direct;
|
|
4267
|
+
}
|
|
4268
|
+
const graph = buildCornerGraph(startExit, endEntry, obstacles, parent, target, margin);
|
|
4269
|
+
if (graph) {
|
|
4270
|
+
const mid = searchMidPath(graph.nodes, graph.edges, graph.startIdx, graph.goalIdx);
|
|
4271
|
+
if (mid && pathIsValid(mid, obstacles)) return mid;
|
|
4272
|
+
}
|
|
4273
|
+
return fallbackOuterRoute(startExit, endEntry, obstacles);
|
|
4274
|
+
}
|
|
4275
|
+
function routeOrthogonalAround(input) {
|
|
4276
|
+
const margin = input.margin ?? DEFAULT_MARGIN;
|
|
4277
|
+
const exitDistance = input.exitDistance ?? DEFAULT_EXIT;
|
|
4278
|
+
const exitBlocker = input.parent ?? input.source;
|
|
4279
|
+
const rawStartExit = computeStartExit(input.from, input.fromDir, exitBlocker, margin, exitDistance);
|
|
4280
|
+
const rawEndEntry = computeEndEntry(input.to, input.toDir, exitDistance);
|
|
4281
|
+
const { startExit, endEntry } = clampFacingExitEntry(input.from, input.to, input.fromDir, input.toDir, rawStartExit, rawEndEntry);
|
|
4282
|
+
const obstacles = [];
|
|
4283
|
+
if (input.source) obstacles.push(expandRect(input.source, margin));
|
|
4284
|
+
if (input.parent) obstacles.push(expandRect(input.parent, margin));
|
|
4285
|
+
if (input.target) obstacles.push(expandRect(input.target, margin));
|
|
4286
|
+
const mid = routeMidPath(startExit, endEntry, obstacles, input.parent ?? input.source, input.target, margin).slice(1, -1);
|
|
4287
|
+
return simplifyPath$1([
|
|
4288
|
+
input.from,
|
|
4289
|
+
startExit,
|
|
4290
|
+
...mid,
|
|
4291
|
+
endEntry,
|
|
4292
|
+
input.to
|
|
4293
|
+
]);
|
|
4294
|
+
}
|
|
4295
|
+
//#endregion
|
|
3780
4296
|
//#region src/elements/paths/PolylinePathStrategy.ts
|
|
4297
|
+
function isSide(v) {
|
|
4298
|
+
return v === "top" || v === "right" || v === "bottom" || v === "left";
|
|
4299
|
+
}
|
|
4300
|
+
function resolveRoutingRoles(from, to, obstacles) {
|
|
4301
|
+
const target = obstacles.find((o) => o.role === "target") ?? obstacles.find((o) => {
|
|
4302
|
+
const onVertical = (Math.abs(to.x - o.x) < 1 || Math.abs(to.x - (o.x + o.width)) < 1) && to.y >= o.y - 1 && to.y <= o.y + o.height + 1;
|
|
4303
|
+
const onHorizontal = (Math.abs(to.y - o.y) < 1 || Math.abs(to.y - (o.y + o.height)) < 1) && to.x >= o.x - 1 && to.x <= o.x + o.width + 1;
|
|
4304
|
+
return onVertical || onHorizontal;
|
|
4305
|
+
});
|
|
4306
|
+
const source = obstacles.find((o) => o.role === "source" && o !== target);
|
|
4307
|
+
return {
|
|
4308
|
+
parent: obstacles.find((o) => o !== target && o !== source && o.role !== "source" && from.x >= o.x && from.x <= o.x + o.width && from.y >= o.y && from.y <= o.y + o.height),
|
|
4309
|
+
source,
|
|
4310
|
+
target
|
|
4311
|
+
};
|
|
4312
|
+
}
|
|
3781
4313
|
var MIN_SEGMENT_LENGTH = 20;
|
|
3782
4314
|
var SELF_LOOP_MIN_DISTANCE$1 = 1;
|
|
3783
4315
|
var SELF_LOOP_OFFSET$1 = 42;
|
|
@@ -3855,27 +4387,66 @@ function moveByDir(point, dir, distance) {
|
|
|
3855
4387
|
default: return point;
|
|
3856
4388
|
}
|
|
3857
4389
|
}
|
|
3858
|
-
|
|
4390
|
+
/** Push `point` along `dir` until it clears every obstacle (plus margin). */
|
|
4391
|
+
function moveOutsideObstacles(point, dir, obstacles, minDistance) {
|
|
4392
|
+
if (!dir || obstacles.length === 0) return moveByDir(point, dir, minDistance);
|
|
4393
|
+
const expanded = expandObstacles(obstacles, OBSTACLE_MARGIN);
|
|
4394
|
+
let dist = minDistance;
|
|
4395
|
+
let candidate = moveByDir(point, dir, dist);
|
|
4396
|
+
const step = 16;
|
|
4397
|
+
const maxDist = 4e3;
|
|
4398
|
+
while (dist < maxDist && expanded.some((obstacle) => pointInsideObstacle(candidate, obstacle))) {
|
|
4399
|
+
dist += step;
|
|
4400
|
+
candidate = moveByDir(point, dir, dist);
|
|
4401
|
+
}
|
|
4402
|
+
return candidate;
|
|
4403
|
+
}
|
|
4404
|
+
function inferExitDir(from, to) {
|
|
3859
4405
|
const dx = to.x - from.x;
|
|
3860
4406
|
const dy = to.y - from.y;
|
|
3861
4407
|
if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? "right" : "left";
|
|
3862
4408
|
return dy >= 0 ? "bottom" : "top";
|
|
3863
4409
|
}
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
4410
|
+
/** Outward side at `to` we should approach from (not opposite of fromDir). */
|
|
4411
|
+
function inferApproachDir(from, to) {
|
|
4412
|
+
const dx = from.x - to.x;
|
|
4413
|
+
const dy = from.y - to.y;
|
|
4414
|
+
if (Math.abs(dy) > 1) return dy >= 0 ? "bottom" : "top";
|
|
4415
|
+
if (Math.abs(dx) > 1) return dx >= 0 ? "right" : "left";
|
|
4416
|
+
return dy >= 0 ? "bottom" : "top";
|
|
4417
|
+
}
|
|
4418
|
+
/** Which side of `target` the attachment point `to` lies on (border hit). */
|
|
4419
|
+
function sideOfPointOnRect(to, target) {
|
|
4420
|
+
const onLeft = Math.abs(to.x - target.x) <= 1;
|
|
4421
|
+
const onRight = Math.abs(to.x - (target.x + target.width)) <= 1;
|
|
4422
|
+
const onTop = Math.abs(to.y - target.y) <= 1;
|
|
4423
|
+
const onBottom = Math.abs(to.y - (target.y + target.height)) <= 1;
|
|
4424
|
+
if (onTop && !onLeft && !onRight) return "top";
|
|
4425
|
+
if (onBottom && !onLeft && !onRight) return "bottom";
|
|
4426
|
+
if (onLeft && !onTop && !onBottom) return "left";
|
|
4427
|
+
if (onRight && !onTop && !onBottom) return "right";
|
|
4428
|
+
}
|
|
4429
|
+
/**
|
|
4430
|
+
* Resolve approach side at `to`.
|
|
4431
|
+
* Prefer the side of the target rect where `to` actually sits — never override a
|
|
4432
|
+
* left/right attachment with top/bottom from source geometry (that puts endEntry
|
|
4433
|
+
* inside the target and draws the last bend through the node).
|
|
4434
|
+
*/
|
|
4435
|
+
function resolveApproachDir(from, to, toDir, target) {
|
|
4436
|
+
if (target) {
|
|
4437
|
+
const side = sideOfPointOnRect(to, target);
|
|
4438
|
+
if (side) return side;
|
|
3871
4439
|
}
|
|
4440
|
+
if (toDir && isSide(toDir)) return toDir;
|
|
4441
|
+
return inferApproachDir(from, to);
|
|
3872
4442
|
}
|
|
3873
4443
|
function buildRoutedPolyline(from, to, fromDir, toDir, obstacles) {
|
|
3874
4444
|
if (obstacles.length === 0) return null;
|
|
3875
|
-
const effectiveFromDir = fromDir ??
|
|
3876
|
-
const
|
|
3877
|
-
const
|
|
3878
|
-
const
|
|
4445
|
+
const effectiveFromDir = fromDir ?? inferExitDir(from, to);
|
|
4446
|
+
const { target: routedTarget } = resolveRoutingRoles(from, to, obstacles);
|
|
4447
|
+
const effectiveToDir = resolveApproachDir(from, to, toDir, routedTarget);
|
|
4448
|
+
const startExit = moveOutsideObstacles(from, effectiveFromDir, obstacles, ROUTE_EXIT_DISTANCE);
|
|
4449
|
+
const endEntry = moveOutsideObstacles(to, effectiveToDir, obstacles, ROUTE_EXIT_DISTANCE);
|
|
3879
4450
|
const expanded = expandObstacles(obstacles, OBSTACLE_MARGIN);
|
|
3880
4451
|
const xs = new Set([startExit.x, endEntry.x]);
|
|
3881
4452
|
const ys = new Set([startExit.y, endEntry.y]);
|
|
@@ -4037,10 +4608,6 @@ var PolylinePathStrategy = class {
|
|
|
4037
4608
|
const dist = distance(from, to);
|
|
4038
4609
|
const selfLoop = _options?.selfLoop ?? false;
|
|
4039
4610
|
const obstacles = _options?.obstacles ?? [];
|
|
4040
|
-
if (!selfLoop && dist < OBSTACLE_ROUTING_DISTANCE) {
|
|
4041
|
-
const routed = buildRoutedPolyline(from, to, fromDir, toDir, obstacles);
|
|
4042
|
-
if (routed) return routed;
|
|
4043
|
-
}
|
|
4044
4611
|
if (selfLoop && dist < SELF_LOOP_MIN_DISTANCE$1 && (fromDir || toDir)) switch (fromDir ?? toDir) {
|
|
4045
4612
|
case "bottom": return [
|
|
4046
4613
|
from,
|
|
@@ -4168,6 +4735,25 @@ var PolylinePathStrategy = class {
|
|
|
4168
4735
|
to
|
|
4169
4736
|
];
|
|
4170
4737
|
}
|
|
4738
|
+
if (!selfLoop && isSide(fromDir) && isSide(toDir)) {
|
|
4739
|
+
const { parent, source, target } = resolveRoutingRoles(from, to, obstacles);
|
|
4740
|
+
const approachDir = resolveApproachDir(from, to, toDir, target);
|
|
4741
|
+
if ((parent || source || target) && isSide(approachDir)) return routeOrthogonalAround({
|
|
4742
|
+
from,
|
|
4743
|
+
to,
|
|
4744
|
+
fromDir,
|
|
4745
|
+
toDir: approachDir,
|
|
4746
|
+
parent,
|
|
4747
|
+
source,
|
|
4748
|
+
target,
|
|
4749
|
+
margin: 4,
|
|
4750
|
+
exitDistance: 20
|
|
4751
|
+
});
|
|
4752
|
+
}
|
|
4753
|
+
if (!selfLoop && dist < OBSTACLE_ROUTING_DISTANCE) {
|
|
4754
|
+
const routed = buildRoutedPolyline(from, to, fromDir, toDir, obstacles);
|
|
4755
|
+
if (routed) return routed;
|
|
4756
|
+
}
|
|
4171
4757
|
if (fromDir || toDir) return this.calculateDirectedPath(from, to, fromDir, toDir);
|
|
4172
4758
|
const midX = (from.x + to.x) / 2;
|
|
4173
4759
|
if (Math.abs(to.x - from.x) > Math.abs(to.y - from.y)) return [
|
|
@@ -4248,15 +4834,39 @@ var PolylinePathStrategy = class {
|
|
|
4248
4834
|
y: to.y
|
|
4249
4835
|
});
|
|
4250
4836
|
}
|
|
4251
|
-
} else if (fromVertical && toHorizontal)
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4837
|
+
} else if (fromVertical && toHorizontal) {
|
|
4838
|
+
const exitY = fromDir === "top" ? from.y - MIN_SEGMENT_LENGTH : from.y + MIN_SEGMENT_LENGTH;
|
|
4839
|
+
const entryX = toDir === "left" ? to.x - MIN_SEGMENT_LENGTH : to.x + MIN_SEGMENT_LENGTH;
|
|
4840
|
+
const jogX = (from.x + to.x) / 2;
|
|
4841
|
+
const approachX = Math.abs(jogX - to.x) >= MIN_SEGMENT_LENGTH ? jogX : entryX;
|
|
4842
|
+
points.push({
|
|
4843
|
+
x: from.x,
|
|
4844
|
+
y: exitY
|
|
4845
|
+
});
|
|
4846
|
+
points.push({
|
|
4847
|
+
x: approachX,
|
|
4848
|
+
y: exitY
|
|
4849
|
+
});
|
|
4850
|
+
points.push({
|
|
4851
|
+
x: approachX,
|
|
4852
|
+
y: to.y
|
|
4853
|
+
});
|
|
4854
|
+
} else if (fromHorizontal && toVertical) {
|
|
4855
|
+
const exitX = fromDir === "left" ? from.x - MIN_SEGMENT_LENGTH : from.x + MIN_SEGMENT_LENGTH;
|
|
4856
|
+
const jogY = (from.y + to.y) / 2;
|
|
4857
|
+
points.push({
|
|
4858
|
+
x: exitX,
|
|
4859
|
+
y: from.y
|
|
4860
|
+
});
|
|
4861
|
+
points.push({
|
|
4862
|
+
x: exitX,
|
|
4863
|
+
y: jogY
|
|
4864
|
+
});
|
|
4865
|
+
points.push({
|
|
4866
|
+
x: to.x,
|
|
4867
|
+
y: jogY
|
|
4868
|
+
});
|
|
4869
|
+
} else {
|
|
4260
4870
|
const midX = (from.x + to.x) / 2;
|
|
4261
4871
|
points.push({
|
|
4262
4872
|
x: midX,
|
|
@@ -4878,6 +5488,20 @@ var DEFAULT_EDGE_STYLE = {
|
|
|
4878
5488
|
strokeWidth: 2,
|
|
4879
5489
|
opacity: 1
|
|
4880
5490
|
};
|
|
5491
|
+
/** Compare path options that affect polyline routing (not control-point identity). */
|
|
5492
|
+
function routingOptionsEqual(a, b) {
|
|
5493
|
+
const aObs = a?.obstacles;
|
|
5494
|
+
const bObs = b?.obstacles;
|
|
5495
|
+
if (aObs === bObs) return true;
|
|
5496
|
+
if (!aObs || !bObs) return !aObs && !bObs;
|
|
5497
|
+
if (aObs.length !== bObs.length) return false;
|
|
5498
|
+
for (let i = 0; i < aObs.length; i++) {
|
|
5499
|
+
const left = aObs[i];
|
|
5500
|
+
const right = bObs[i];
|
|
5501
|
+
if (left.id !== right.id || left.role !== right.role || left.x !== right.x || left.y !== right.y || left.width !== right.width || left.height !== right.height) return false;
|
|
5502
|
+
}
|
|
5503
|
+
return true;
|
|
5504
|
+
}
|
|
4881
5505
|
/**
|
|
4882
5506
|
* Connection edge between nodes
|
|
4883
5507
|
*/
|
|
@@ -5223,7 +5847,9 @@ var Edge = class extends Element {
|
|
|
5223
5847
|
* @param toDir Direction at end point (top, right, bottom, left)
|
|
5224
5848
|
*/
|
|
5225
5849
|
updateEndpoints(fromPoint, toPoint, fromDir, toDir, options) {
|
|
5226
|
-
|
|
5850
|
+
const geometryUnchanged = this._path.length > 0 && this._fromPoint.x === fromPoint.x && this._fromPoint.y === fromPoint.y && this._toPoint.x === toPoint.x && this._toPoint.y === toPoint.y && this._fromDir === fromDir && this._toDir === toDir;
|
|
5851
|
+
const routingOptionsChanged = !routingOptionsEqual(this._pathOptions, options);
|
|
5852
|
+
if (geometryUnchanged && !routingOptionsChanged) {
|
|
5227
5853
|
this._pathOptions = options;
|
|
5228
5854
|
return;
|
|
5229
5855
|
}
|
|
@@ -7017,7 +7643,20 @@ var EdgeEndpointUpdater = class {
|
|
|
7017
7643
|
if (includeRoutingObstacles) this.lockAnchors(edge, fromNode, toNode);
|
|
7018
7644
|
const from = this.resolveNodeEndpoint(fromNode, edge.from, toNode.getCenter(), edge.lockAnchors);
|
|
7019
7645
|
const to = this.resolveNodeEndpoint(toNode, edge.to, fromNode.getCenter(), edge.lockAnchors);
|
|
7020
|
-
edge.
|
|
7646
|
+
const fromId = edge.from.nodeId;
|
|
7647
|
+
const toId = edge.to.nodeId;
|
|
7648
|
+
const routingObstacles = obstacles?.map((obstacle) => {
|
|
7649
|
+
if (obstacle.id === fromId) return {
|
|
7650
|
+
...obstacle,
|
|
7651
|
+
role: "source"
|
|
7652
|
+
};
|
|
7653
|
+
if (obstacle.id === toId) return {
|
|
7654
|
+
...obstacle,
|
|
7655
|
+
role: "target"
|
|
7656
|
+
};
|
|
7657
|
+
return obstacle;
|
|
7658
|
+
});
|
|
7659
|
+
edge.updateEndpoints(from.point, to.point, from.direction, to.direction, routingObstacles ? { obstacles: routingObstacles } : void 0);
|
|
7021
7660
|
}
|
|
7022
7661
|
/**
|
|
7023
7662
|
* @returns true when the resolved geometry changed enough to warrant another pass
|
|
@@ -8106,6 +8745,7 @@ var DiagramRenderer = class extends EventEmitter {
|
|
|
8106
8745
|
getNodeObstacles() {
|
|
8107
8746
|
if (this._nodeObstaclesCache !== null) return this._nodeObstaclesCache;
|
|
8108
8747
|
this._nodeObstaclesCache = Array.from(this._nodes.values()).map((node) => ({
|
|
8748
|
+
id: node.id,
|
|
8109
8749
|
x: node.x - 8,
|
|
8110
8750
|
y: node.y - 8,
|
|
8111
8751
|
width: node.width + 16,
|
|
@@ -8926,6 +9566,29 @@ var NodeImage = class {
|
|
|
8926
9566
|
}
|
|
8927
9567
|
};
|
|
8928
9568
|
//#endregion
|
|
9569
|
+
//#region src/utils/resolveContentInset.ts
|
|
9570
|
+
function axisFactor(node, base) {
|
|
9571
|
+
if (base === void 0 || !(base > 0) || !Number.isFinite(base)) return 1;
|
|
9572
|
+
if (!Number.isFinite(node)) return 1;
|
|
9573
|
+
return node / base;
|
|
9574
|
+
}
|
|
9575
|
+
/**
|
|
9576
|
+
* Resolve stored (reference) content inset to absolute px for the current node size.
|
|
9577
|
+
* Vertical sides (top/bottom) scale with height; horizontal (left/right) with width.
|
|
9578
|
+
*/
|
|
9579
|
+
function resolveContentInset(inset, scale, nodeSize, baseSize) {
|
|
9580
|
+
const sx = axisFactor(nodeSize.width, baseSize?.width);
|
|
9581
|
+
const sy = axisFactor(nodeSize.height, baseSize?.height);
|
|
9582
|
+
const s = scale ?? {};
|
|
9583
|
+
const clamp = (v) => Number.isFinite(v) ? Math.max(0, v) : 0;
|
|
9584
|
+
return {
|
|
9585
|
+
top: clamp(s.top ? inset.top * sy : inset.top),
|
|
9586
|
+
bottom: clamp(s.bottom ? inset.bottom * sy : inset.bottom),
|
|
9587
|
+
left: clamp(s.left ? inset.left * sx : inset.left),
|
|
9588
|
+
right: clamp(s.right ? inset.right * sx : inset.right)
|
|
9589
|
+
};
|
|
9590
|
+
}
|
|
9591
|
+
//#endregion
|
|
8929
9592
|
//#region src/elements/Node.ts
|
|
8930
9593
|
var BADGE_SIZE = 15;
|
|
8931
9594
|
var BADGE_OFFSET = 4;
|
|
@@ -8973,6 +9636,8 @@ var Node = class extends Element {
|
|
|
8973
9636
|
this._anchorPoints = this.normalizeAnchorPoints(options.anchorPoints);
|
|
8974
9637
|
this._resizeHandlesEnabled = options.resizeHandlesEnabled ?? true;
|
|
8975
9638
|
this._contentInset = this.normalizeContentInset(options.contentInset);
|
|
9639
|
+
this._contentInsetScale = this.normalizeContentInsetScale(options.contentInsetScale);
|
|
9640
|
+
this._contentInsetBaseSize = this.normalizeContentInsetBaseSize(options.contentInsetBaseSize);
|
|
8976
9641
|
if (options.label !== void 0) if (typeof options.label === "string") this._label = new TextLabel({
|
|
8977
9642
|
text: options.label,
|
|
8978
9643
|
onChange: () => this.markDirty()
|
|
@@ -9209,6 +9874,26 @@ var Node = class extends Element {
|
|
|
9209
9874
|
this.markDirty();
|
|
9210
9875
|
}
|
|
9211
9876
|
/**
|
|
9877
|
+
* Per-side proportional flags for contentInset (reference px vs contentInsetBaseSize).
|
|
9878
|
+
*/
|
|
9879
|
+
get contentInsetScale() {
|
|
9880
|
+
return { ...this._contentInsetScale };
|
|
9881
|
+
}
|
|
9882
|
+
set contentInsetScale(value) {
|
|
9883
|
+
this._contentInsetScale = this.normalizeContentInsetScale(value);
|
|
9884
|
+
this.markDirty();
|
|
9885
|
+
}
|
|
9886
|
+
/**
|
|
9887
|
+
* Base size used when resolving proportional contentInset sides.
|
|
9888
|
+
*/
|
|
9889
|
+
get contentInsetBaseSize() {
|
|
9890
|
+
return this._contentInsetBaseSize ? { ...this._contentInsetBaseSize } : void 0;
|
|
9891
|
+
}
|
|
9892
|
+
set contentInsetBaseSize(value) {
|
|
9893
|
+
this._contentInsetBaseSize = this.normalizeContentInsetBaseSize(value);
|
|
9894
|
+
this.markDirty();
|
|
9895
|
+
}
|
|
9896
|
+
/**
|
|
9212
9897
|
* Set getter for attachToOutline (called by DiagramRenderer when node is added)
|
|
9213
9898
|
*/
|
|
9214
9899
|
setAttachToOutlineGetter(getter) {
|
|
@@ -9632,7 +10317,10 @@ var Node = class extends Element {
|
|
|
9632
10317
|
};
|
|
9633
10318
|
}
|
|
9634
10319
|
getLabelContainerBounds(bounds) {
|
|
9635
|
-
const { top, right, bottom, left } = this._contentInset
|
|
10320
|
+
const { top, right, bottom, left } = resolveContentInset(this._contentInset, this._contentInsetScale, {
|
|
10321
|
+
width: bounds.width,
|
|
10322
|
+
height: bounds.height
|
|
10323
|
+
}, this._contentInsetBaseSize);
|
|
9636
10324
|
return {
|
|
9637
10325
|
x: bounds.x + left,
|
|
9638
10326
|
y: bounds.y + top,
|
|
@@ -9664,6 +10352,25 @@ var Node = class extends Element {
|
|
|
9664
10352
|
left: n(value.left)
|
|
9665
10353
|
};
|
|
9666
10354
|
}
|
|
10355
|
+
normalizeContentInsetScale(value) {
|
|
10356
|
+
if (!value) return {};
|
|
10357
|
+
const out = {};
|
|
10358
|
+
if (value.top === true) out.top = true;
|
|
10359
|
+
if (value.right === true) out.right = true;
|
|
10360
|
+
if (value.bottom === true) out.bottom = true;
|
|
10361
|
+
if (value.left === true) out.left = true;
|
|
10362
|
+
return out;
|
|
10363
|
+
}
|
|
10364
|
+
normalizeContentInsetBaseSize(value) {
|
|
10365
|
+
if (!value) return void 0;
|
|
10366
|
+
const width = Number.isFinite(value.width) ? value.width : 0;
|
|
10367
|
+
const height = Number.isFinite(value.height) ? value.height : 0;
|
|
10368
|
+
if (!(width > 0) && !(height > 0)) return void 0;
|
|
10369
|
+
return {
|
|
10370
|
+
width,
|
|
10371
|
+
height
|
|
10372
|
+
};
|
|
10373
|
+
}
|
|
9667
10374
|
getIconBoxSize() {
|
|
9668
10375
|
if (!this._icon) return;
|
|
9669
10376
|
const { width, height } = this._icon.getSize();
|
|
@@ -11946,13 +12653,7 @@ var CompositeNode = class extends Node {
|
|
|
11946
12653
|
height: h
|
|
11947
12654
|
};
|
|
11948
12655
|
}
|
|
11949
|
-
|
|
11950
|
-
return {
|
|
11951
|
-
x: bounds.x + ci.left,
|
|
11952
|
-
y: bounds.y + ci.top,
|
|
11953
|
-
width: Math.max(0, bounds.width - ci.left - ci.right),
|
|
11954
|
-
height: Math.max(0, bounds.height - ci.top - ci.bottom)
|
|
11955
|
-
};
|
|
12656
|
+
return super.getLabelContainerBounds(bounds);
|
|
11956
12657
|
}
|
|
11957
12658
|
/**
|
|
11958
12659
|
* Get the minimum content size needed for the content tree.
|
|
@@ -13777,6 +14478,6 @@ function applyEdgeStyle(ctx, style) {
|
|
|
13777
14478
|
return baseOpacity * strokeOpacity;
|
|
13778
14479
|
}
|
|
13779
14480
|
//#endregion
|
|
13780
|
-
export { AddNodeCommand, AnimationManager, AutoLayout, AutoRouting, BezierPathStrategy, CContainer, CDivider, CIcon, CShape, CText, ChangeEdgePropertiesCommand, ChangeEditablePolylineControlPointsCommand, ChangeGroupPropertiesCommand, ChangeNodePropertiesCommand, CircleNode, ClipboardManager, CompositeCommand, CompositeNode, ConnectionManager, ContextMenuManager, CustomShapeNode, DARK_THEME, DEFAULT_THEME, DiagramRenderer, DiamondNode, DragManager, Edge, Element, EventEmitter, GridOverlay, Group, GuidesOverlay, HistoryManager, ImageExporter, InputHandler, InteractionManager, MiniMap, MoveNodesCommand, NavigationManager, Node, NodeImage, PolylinePathStrategy, Port, PropertyChangeBatcher, RectangleNode, RemoveNodeCommand, ResizeManager, RulersOverlay, SearchManager, SelectionManager, Serializer, ShapeFactories, StraightPathStrategy, StyleManager, SvgExporter, TextLabel, alignNodes, angle, applyEdgeStyle, applyNodeStyle, bezierPoint, boundsCenter, calculateBezierControlPoints, clamp, clonePoints, container, deserializeCComponent, directionFromAngle, distance, distanceToSegment, distributeNodes, divider, drawRoundedRectPath, expandBounds, flexLayout, generateId, getClosestPointOnPath, getPathPointAt, icon, isCornerPlacement, isEdgeEdgeEndpoint, isNodeEdgeEndpoint, lerp, mergeBounds, normalizeSides, pointInEllipse, pointInRect, rectIntersection, rectUnion, rectsIntersect, renderFillAndStroke, resetIdCounter, resetPortIdCounter, rotatePoint, segmentRectIntersections, shape, snapPointToGrid, snapToGrid, text };
|
|
14481
|
+
export { AddNodeCommand, AnimationManager, AutoLayout, AutoRouting, BezierPathStrategy, CContainer, CDivider, CIcon, CShape, CText, ChangeEdgePropertiesCommand, ChangeEditablePolylineControlPointsCommand, ChangeGroupPropertiesCommand, ChangeNodePropertiesCommand, CircleNode, ClipboardManager, CompositeCommand, CompositeNode, ConnectionManager, ContextMenuManager, CustomShapeNode, DARK_THEME, DEFAULT_THEME, DiagramRenderer, DiamondNode, DragManager, Edge, Element, EventEmitter, GridOverlay, Group, GuidesOverlay, HistoryManager, ImageExporter, InputHandler, InteractionManager, MiniMap, MoveNodesCommand, NavigationManager, Node, NodeImage, PolylinePathStrategy, Port, PropertyChangeBatcher, RectangleNode, RemoveNodeCommand, ResizeManager, RulersOverlay, SearchManager, SelectionManager, Serializer, ShapeFactories, StraightPathStrategy, StyleManager, SvgExporter, TextLabel, alignNodes, angle, applyEdgeStyle, applyNodeStyle, bezierPoint, boundsCenter, calculateBezierControlPoints, clamp, clonePoints, container, deserializeCComponent, directionFromAngle, distance, distanceToSegment, distributeNodes, divider, drawRoundedRectPath, expandBounds, flexLayout, generateId, getClosestPointOnPath, getPathPointAt, icon, isCornerPlacement, isEdgeEdgeEndpoint, isNodeEdgeEndpoint, lerp, mergeBounds, normalizeSides, pointInEllipse, pointInRect, rectIntersection, rectUnion, rectsIntersect, renderFillAndStroke, resetIdCounter, resetPortIdCounter, resolveContentInset, rotatePoint, segmentRectIntersections, shape, snapPointToGrid, snapToGrid, text };
|
|
13781
14482
|
|
|
13782
14483
|
//# sourceMappingURL=papirus.js.map
|