capdag 0.187.479 → 0.189.485
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/cap-fab-renderer.js +100 -2
- package/package.json +1 -1
package/cap-fab-renderer.js
CHANGED
|
@@ -379,8 +379,12 @@ function layoutForMode(mode) {
|
|
|
379
379
|
};
|
|
380
380
|
if (mode === 'browse') {
|
|
381
381
|
return Object.assign({}, base, {
|
|
382
|
-
'elk.layered.spacing.nodeNodeBetweenLayers':
|
|
383
|
-
'elk.spacing.
|
|
382
|
+
'elk.layered.spacing.nodeNodeBetweenLayers': 220,
|
|
383
|
+
'elk.layered.spacing.edgeEdgeBetweenLayers': 44,
|
|
384
|
+
'elk.layered.spacing.edgeNodeBetweenLayers': 52,
|
|
385
|
+
'elk.spacing.edgeEdge': 34,
|
|
386
|
+
'elk.spacing.edgeNode': 42,
|
|
387
|
+
'elk.spacing.nodeNode': 118,
|
|
384
388
|
});
|
|
385
389
|
}
|
|
386
390
|
if (mode === 'strand') {
|
|
@@ -547,6 +551,15 @@ function buildStylesheet() {
|
|
|
547
551
|
'transition-duration': '0.2s',
|
|
548
552
|
},
|
|
549
553
|
},
|
|
554
|
+
{
|
|
555
|
+
selector: 'edge.crowded-edge',
|
|
556
|
+
style: {
|
|
557
|
+
'curve-style': 'unbundled-bezier',
|
|
558
|
+
'control-point-step-size': 'data(controlPointStepSize)',
|
|
559
|
+
'control-point-distances': 'data(controlPointDistances)',
|
|
560
|
+
'control-point-weights': 'data(controlPointWeights)',
|
|
561
|
+
},
|
|
562
|
+
},
|
|
550
563
|
{
|
|
551
564
|
selector: 'edge.highlighted',
|
|
552
565
|
style: { 'width': 2.5, 'z-index': 999 },
|
|
@@ -690,6 +703,12 @@ function assertArray(value, path) {
|
|
|
690
703
|
}
|
|
691
704
|
}
|
|
692
705
|
|
|
706
|
+
function assertObject(value, path) {
|
|
707
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
708
|
+
throw new Error(`CapFabRenderer: ${path} must be an object`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
693
712
|
function validateBrowseData(data) {
|
|
694
713
|
assertArray(data, 'browse mode data');
|
|
695
714
|
data.forEach((cap, idx) => {
|
|
@@ -961,6 +980,80 @@ function edgeHueColor(edgeIdx) {
|
|
|
961
980
|
return `hsl(${hue}, 60%, 55%)`;
|
|
962
981
|
}
|
|
963
982
|
|
|
983
|
+
function centeredOrdinal(index, total) {
|
|
984
|
+
if (!Number.isInteger(index) || !Number.isInteger(total) || total <= 0) {
|
|
985
|
+
throw new Error('CapFabRenderer: centeredOrdinal requires integer index/total');
|
|
986
|
+
}
|
|
987
|
+
return index - ((total - 1) / 2);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function crowdOffsets(index, total, step, maxAbs) {
|
|
991
|
+
if (total <= 1) return 0;
|
|
992
|
+
const raw = centeredOrdinal(index, total) * step;
|
|
993
|
+
if (maxAbs === undefined) return raw;
|
|
994
|
+
return Math.max(-maxAbs, Math.min(maxAbs, raw));
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function annotateCrowdedBrowseEdges(edges) {
|
|
998
|
+
const bySource = new Map();
|
|
999
|
+
const byTarget = new Map();
|
|
1000
|
+
|
|
1001
|
+
for (const edge of edges) {
|
|
1002
|
+
if (!bySource.has(edge.source)) bySource.set(edge.source, []);
|
|
1003
|
+
bySource.get(edge.source).push(edge);
|
|
1004
|
+
if (!byTarget.has(edge.target)) byTarget.set(edge.target, []);
|
|
1005
|
+
byTarget.get(edge.target).push(edge);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const stableSort = (a, b) => {
|
|
1009
|
+
const targetCmp = a.target.localeCompare(b.target);
|
|
1010
|
+
if (targetCmp !== 0) return targetCmp;
|
|
1011
|
+
const titleCmp = a.title.localeCompare(b.title);
|
|
1012
|
+
if (titleCmp !== 0) return titleCmp;
|
|
1013
|
+
return a.id.localeCompare(b.id);
|
|
1014
|
+
};
|
|
1015
|
+
const reverseStableSort = (a, b) => {
|
|
1016
|
+
const sourceCmp = a.source.localeCompare(b.source);
|
|
1017
|
+
if (sourceCmp !== 0) return sourceCmp;
|
|
1018
|
+
const titleCmp = a.title.localeCompare(b.title);
|
|
1019
|
+
if (titleCmp !== 0) return titleCmp;
|
|
1020
|
+
return a.id.localeCompare(b.id);
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
for (const group of bySource.values()) group.sort(stableSort);
|
|
1024
|
+
for (const group of byTarget.values()) group.sort(reverseStableSort);
|
|
1025
|
+
|
|
1026
|
+
const sourceIndex = new Map();
|
|
1027
|
+
const targetIndex = new Map();
|
|
1028
|
+
for (const group of bySource.values()) {
|
|
1029
|
+
group.forEach((edge, idx) => sourceIndex.set(edge.id, idx));
|
|
1030
|
+
}
|
|
1031
|
+
for (const group of byTarget.values()) {
|
|
1032
|
+
group.forEach((edge, idx) => targetIndex.set(edge.id, idx));
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
for (const edge of edges) {
|
|
1036
|
+
const sourceGroup = bySource.get(edge.source) || [edge];
|
|
1037
|
+
const targetGroup = byTarget.get(edge.target) || [edge];
|
|
1038
|
+
const sourceCount = sourceGroup.length;
|
|
1039
|
+
const targetCount = targetGroup.length;
|
|
1040
|
+
const crowdCount = Math.max(sourceCount, targetCount);
|
|
1041
|
+
|
|
1042
|
+
if (crowdCount <= 2) {
|
|
1043
|
+
edge.crowdedClass = '';
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const sourceOffset = crowdOffsets(sourceIndex.get(edge.id), sourceCount, 18, 64);
|
|
1048
|
+
const targetOffset = crowdOffsets(targetIndex.get(edge.id), targetCount, 18, 64);
|
|
1049
|
+
|
|
1050
|
+
edge.crowdedClass = 'crowded-edge';
|
|
1051
|
+
edge.controlPointDistances = `${sourceOffset} ${targetOffset}`;
|
|
1052
|
+
edge.controlPointWeights = '0.22 0.78';
|
|
1053
|
+
edge.controlPointStepSize = 56;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
964
1057
|
// --------- Browse mode builder ----------------------------------------------
|
|
965
1058
|
|
|
966
1059
|
function buildBrowseGraphData(capabilities) {
|
|
@@ -1014,6 +1107,7 @@ function buildBrowseGraphData(capabilities) {
|
|
|
1014
1107
|
edges.forEach((edge, i) => {
|
|
1015
1108
|
edge.color = edgeHueColor(i);
|
|
1016
1109
|
});
|
|
1110
|
+
annotateCrowdedBrowseEdges(edges);
|
|
1017
1111
|
|
|
1018
1112
|
const nodes = Array.from(nodesMap.values());
|
|
1019
1113
|
for (const node of nodes) {
|
|
@@ -1061,7 +1155,11 @@ function browseCytoscapeElements(built) {
|
|
|
1061
1155
|
fullUrn: edge.capability.urn,
|
|
1062
1156
|
capFabEdgeIndex: edge.capFabEdgeIndex,
|
|
1063
1157
|
color: edge.color,
|
|
1158
|
+
controlPointStepSize: edge.controlPointStepSize || 56,
|
|
1159
|
+
controlPointDistances: edge.controlPointDistances || '0 0',
|
|
1160
|
+
controlPointWeights: edge.controlPointWeights || '0.22 0.78',
|
|
1064
1161
|
},
|
|
1162
|
+
classes: edge.crowdedClass || '',
|
|
1065
1163
|
};
|
|
1066
1164
|
});
|
|
1067
1165
|
return nodeElements.concat(edgeElements);
|
package/package.json
CHANGED