@graphty/graph-io 0.3.49 → 0.3.50
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/chunks/{approximations-Bzn3L-je.js → approximations-BRXsll0T.js} +2 -2
- package/dist/chunks/{approximations-Bzn3L-je.js.map → approximations-BRXsll0T.js.map} +1 -1
- package/dist/chunks/{block-iptrlMBb.js → block-yl0f_qLO.js} +27 -2
- package/dist/chunks/block-yl0f_qLO.js.map +1 -0
- package/dist/chunks/{context-C3p0RQxR.js → context-DizmppHy.js} +2 -2
- package/dist/chunks/{context-C3p0RQxR.js.map → context-DizmppHy.js.map} +1 -1
- package/dist/chunks/{credentials-DXHx7nwR.js → credentials-CmvJXYeH.js} +58 -9
- package/dist/chunks/credentials-CmvJXYeH.js.map +1 -0
- package/dist/chunks/{descriptor-DLA5kINx.js → descriptor-Dhez_Cew.js} +5 -1
- package/dist/chunks/{descriptor-DLA5kINx.js.map → descriptor-Dhez_Cew.js.map} +1 -1
- package/dist/chunks/{emit-BwHQkZle.js → emit-7Jkpg0yd.js} +132 -4
- package/dist/chunks/emit-7Jkpg0yd.js.map +1 -0
- package/dist/chunks/{exporter-nDfxIK5D.js → exporter-1Ajud15J.js} +4 -4
- package/dist/chunks/{exporter-nDfxIK5D.js.map → exporter-1Ajud15J.js.map} +1 -1
- package/dist/chunks/{exporter-D9yMtocA.js → exporter-B6yvL3wy.js} +3 -3
- package/dist/chunks/{exporter-D9yMtocA.js.map → exporter-B6yvL3wy.js.map} +1 -1
- package/dist/chunks/{exporter-_jrBTnos.js → exporter-CA5q8ROP.js} +4 -4
- package/dist/chunks/{exporter-_jrBTnos.js.map → exporter-CA5q8ROP.js.map} +1 -1
- package/dist/chunks/{exporter-D2CETOMK.js → exporter-CQQZFJ2v.js} +72 -10
- package/dist/chunks/exporter-CQQZFJ2v.js.map +1 -0
- package/dist/chunks/{generic-ymWCETAJ.js → generic-Dc21MAdg.js} +3 -3
- package/dist/chunks/{generic-ymWCETAJ.js.map → generic-Dc21MAdg.js.map} +1 -1
- package/dist/chunks/{importer-CT9mJ1nn.js → importer-C6NVpCXp.js} +7 -6
- package/dist/chunks/importer-C6NVpCXp.js.map +1 -0
- package/dist/chunks/{importer-GREKMdIf.js → importer-CKFe4q54.js} +4 -4
- package/dist/chunks/{importer-GREKMdIf.js.map → importer-CKFe4q54.js.map} +1 -1
- package/dist/chunks/{importer-PqaaKKnY.js → importer-CKbUbsJA.js} +4 -4
- package/dist/chunks/{importer-PqaaKKnY.js.map → importer-CKbUbsJA.js.map} +1 -1
- package/dist/chunks/{importer-DaLTjbds.js → importer-ClCsdgTJ.js} +97 -7
- package/dist/chunks/importer-ClCsdgTJ.js.map +1 -0
- package/dist/chunks/{importer-B92x1xY5.js → importer-CyVZTrf0.js} +3 -3
- package/dist/chunks/{importer-B92x1xY5.js.map → importer-CyVZTrf0.js.map} +1 -1
- package/dist/chunks/{importer-vRq4h3j5.js → importer-DgG4v1Ms.js} +89 -20
- package/dist/chunks/importer-DgG4v1Ms.js.map +1 -0
- package/dist/chunks/{importer-Vn5QvmmJ.js → importer-asFUrIsj.js} +2 -2
- package/dist/chunks/{importer-Vn5QvmmJ.js.map → importer-asFUrIsj.js.map} +1 -1
- package/dist/chunks/{lexicon-Clf4QIyl.js → lexicon-IVRq2Uoo.js} +2 -2
- package/dist/chunks/{lexicon-Clf4QIyl.js.map → lexicon-IVRq2Uoo.js.map} +1 -1
- package/dist/chunks/{round-trip-Zm3_ivgq.js → round-trip-Cy3_SHRZ.js} +2 -2
- package/dist/chunks/{round-trip-Zm3_ivgq.js.map → round-trip-Cy3_SHRZ.js.map} +1 -1
- package/dist/chunks/{sigv4-EokFic1V.js → sigv4-BXwVbza3.js} +3 -3
- package/dist/chunks/{sigv4-EokFic1V.js.map → sigv4-BXwVbza3.js.map} +1 -1
- package/dist/chunks/{styles-CCs214_3.js → styles-CAVmBLgp.js} +4 -4
- package/dist/chunks/{styles-CCs214_3.js.map → styles-CAVmBLgp.js.map} +1 -1
- package/dist/chunks/{styles-DPQQIig4.js → styles-CPczw1Jz.js} +4 -4
- package/dist/chunks/{styles-DPQQIig4.js.map → styles-CPczw1Jz.js.map} +1 -1
- package/dist/chunks/{styles-Cm62yyQX.js → styles-boK6Ln54.js} +65 -30
- package/dist/chunks/styles-boK6Ln54.js.map +1 -0
- package/dist/chunks/{styles-yed-UMFdNqze.js → styles-yed-XeFBQ6VS.js} +8 -8
- package/dist/chunks/{styles-yed-UMFdNqze.js.map → styles-yed-XeFBQ6VS.js.map} +1 -1
- package/dist/cx.js +2 -2
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +7 -4
- package/dist/gexf.js +2 -2
- package/dist/gml.js +8 -8
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +129 -104
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +2 -2
- package/dist/json.js +1 -1
- package/dist/pajek.js +1 -1
- package/dist/sources-cypher.js +25 -22
- package/dist/sources-cypher.js.map +1 -1
- package/dist/sources-ndex.js +2 -2
- package/dist/sources-rest.js +20 -4
- package/dist/sources-rest.js.map +1 -1
- package/dist/sources-sparql.js +40 -6
- package/dist/sources-sparql.js.map +1 -1
- package/dist/src/formats/dot/exporter.d.ts.map +1 -1
- package/dist/src/formats/dot/exporter.js +34 -2
- package/dist/src/formats/dot/exporter.js.map +1 -1
- package/dist/src/formats/dot/importer.d.ts +6 -1
- package/dist/src/formats/dot/importer.d.ts.map +1 -1
- package/dist/src/formats/dot/importer.js +65 -5
- package/dist/src/formats/dot/importer.js.map +1 -1
- package/dist/src/formats/dot/index.d.ts +3 -1
- package/dist/src/formats/dot/index.d.ts.map +1 -1
- package/dist/src/formats/dot/index.js +2 -1
- package/dist/src/formats/dot/index.js.map +1 -1
- package/dist/src/formats/dot/label-places.d.ts +7 -0
- package/dist/src/formats/dot/label-places.d.ts.map +1 -1
- package/dist/src/formats/dot/label-places.js +9 -3
- package/dist/src/formats/dot/label-places.js.map +1 -1
- package/dist/src/formats/dot/names.d.ts +13 -0
- package/dist/src/formats/dot/names.d.ts.map +1 -1
- package/dist/src/formats/dot/names.js +13 -0
- package/dist/src/formats/dot/names.js.map +1 -1
- package/dist/src/formats/dot/styles.d.ts +10 -0
- package/dist/src/formats/dot/styles.d.ts.map +1 -1
- package/dist/src/formats/dot/styles.js +49 -3
- package/dist/src/formats/dot/styles.js.map +1 -1
- package/dist/src/formats/gml/styles.js +1 -1
- package/dist/src/formats/gml/styles.js.map +1 -1
- package/dist/src/formats/graphml/styles-yed.js +3 -3
- package/dist/src/formats/graphml/styles-yed.js.map +1 -1
- package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -1
- package/dist/src/formats/graphml/styles-yfiles3.js +6 -4
- package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -1
- package/dist/src/formats/json/styles.d.ts.map +1 -1
- package/dist/src/formats/json/styles.js +94 -6
- package/dist/src/formats/json/styles.js.map +1 -1
- package/dist/src/formats/pajek/draw.d.ts +8 -0
- package/dist/src/formats/pajek/draw.d.ts.map +1 -1
- package/dist/src/formats/pajek/draw.js +10 -0
- package/dist/src/formats/pajek/draw.js.map +1 -1
- package/dist/src/formats/pajek/styles.d.ts.map +1 -1
- package/dist/src/formats/pajek/styles.js +142 -25
- package/dist/src/formats/pajek/styles.js.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/sources/credentials.d.ts +50 -1
- package/dist/src/sources/credentials.d.ts.map +1 -1
- package/dist/src/sources/credentials.js +69 -0
- package/dist/src/sources/credentials.js.map +1 -1
- package/dist/src/sources/cypher/index.d.ts.map +1 -1
- package/dist/src/sources/cypher/index.js +12 -11
- package/dist/src/sources/cypher/index.js.map +1 -1
- package/dist/src/sources/cypher/query-builder.d.ts +6 -6
- package/dist/src/sources/cypher/query-builder.d.ts.map +1 -1
- package/dist/src/sources/cypher/query-builder.js +15 -15
- package/dist/src/sources/cypher/query-builder.js.map +1 -1
- package/dist/src/sources/rest/descriptor.d.ts.map +1 -1
- package/dist/src/sources/rest/descriptor.js +4 -0
- package/dist/src/sources/rest/descriptor.js.map +1 -1
- package/dist/src/sources/rest/openalex.d.ts +4 -2
- package/dist/src/sources/rest/openalex.d.ts.map +1 -1
- package/dist/src/sources/rest/openalex.js +4 -2
- package/dist/src/sources/rest/openalex.js.map +1 -1
- package/dist/src/sources/rest/source.d.ts.map +1 -1
- package/dist/src/sources/rest/source.js +23 -0
- package/dist/src/sources/rest/source.js.map +1 -1
- package/dist/src/sources/run.d.ts +2 -0
- package/dist/src/sources/run.d.ts.map +1 -1
- package/dist/src/sources/run.js +30 -3
- package/dist/src/sources/run.js.map +1 -1
- package/dist/src/sources/sparql/index.d.ts.map +1 -1
- package/dist/src/sources/sparql/index.js +14 -1
- package/dist/src/sources/sparql/index.js.map +1 -1
- package/dist/src/sources/sparql/query.d.ts +2 -1
- package/dist/src/sources/sparql/query.d.ts.map +1 -1
- package/dist/src/sources/sparql/query.js +55 -4
- package/dist/src/sources/sparql/query.js.map +1 -1
- package/dist/src/sources/types.d.ts +29 -0
- package/dist/src/sources/types.d.ts.map +1 -1
- package/dist/src/styles/canon/enums.d.ts.map +1 -1
- package/dist/src/styles/canon/enums.js +8 -0
- package/dist/src/styles/canon/enums.js.map +1 -1
- package/dist/src/styles/cyjs/cascade.d.ts.map +1 -1
- package/dist/src/styles/cyjs/cascade.js +124 -0
- package/dist/src/styles/cyjs/cascade.js.map +1 -1
- package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -1
- package/dist/src/styles/cyjs/defaults-table.js +1 -0
- package/dist/src/styles/cyjs/defaults-table.js.map +1 -1
- package/dist/src/styles/cyjs/emit.d.ts.map +1 -1
- package/dist/src/styles/cyjs/emit.js +30 -1
- package/dist/src/styles/cyjs/emit.js.map +1 -1
- package/dist/src/styles/cyjs/mappers.d.ts.map +1 -1
- package/dist/src/styles/cyjs/mappers.js +14 -0
- package/dist/src/styles/cyjs/mappers.js.map +1 -1
- package/dist/src/styles/types.d.ts +65 -0
- package/dist/src/styles/types.d.ts.map +1 -1
- package/dist/src/styles/vocabulary.d.ts.map +1 -1
- package/dist/src/styles/vocabulary.js +21 -0
- package/dist/src/styles/vocabulary.js.map +1 -1
- package/dist/styles.js +6 -6
- package/dist/xgmml.js +3 -3
- package/package.json +2 -2
- package/src/formats/dot/exporter.ts +37 -1
- package/src/formats/dot/importer.ts +75 -4
- package/src/formats/dot/index.ts +3 -1
- package/src/formats/dot/label-places.ts +16 -3
- package/src/formats/dot/names.ts +15 -0
- package/src/formats/dot/styles.ts +55 -3
- package/src/formats/gml/styles.ts +1 -1
- package/src/formats/graphml/styles-yed.ts +3 -3
- package/src/formats/graphml/styles-yfiles3.ts +6 -4
- package/src/formats/json/styles.ts +102 -6
- package/src/formats/pajek/draw.ts +11 -0
- package/src/formats/pajek/styles.ts +151 -26
- package/src/index.ts +7 -0
- package/src/sources/credentials.ts +90 -1
- package/src/sources/cypher/index.ts +12 -11
- package/src/sources/cypher/query-builder.ts +15 -15
- package/src/sources/rest/descriptor.ts +4 -0
- package/src/sources/rest/openalex.ts +4 -2
- package/src/sources/rest/source.ts +25 -0
- package/src/sources/run.ts +35 -3
- package/src/sources/sparql/index.ts +16 -1
- package/src/sources/sparql/query.ts +58 -4
- package/src/sources/types.ts +33 -0
- package/src/styles/canon/enums.ts +9 -0
- package/src/styles/cyjs/cascade.ts +143 -0
- package/src/styles/cyjs/defaults-table.ts +1 -0
- package/src/styles/cyjs/emit.ts +35 -1
- package/src/styles/cyjs/mappers.ts +14 -0
- package/src/styles/types.ts +65 -0
- package/src/styles/vocabulary.ts +21 -0
- package/dist/chunks/block-iptrlMBb.js.map +0 -1
- package/dist/chunks/credentials-DXHx7nwR.js.map +0 -1
- package/dist/chunks/emit-BwHQkZle.js.map +0 -1
- package/dist/chunks/exporter-D2CETOMK.js.map +0 -1
- package/dist/chunks/importer-CT9mJ1nn.js.map +0 -1
- package/dist/chunks/importer-DaLTjbds.js.map +0 -1
- package/dist/chunks/importer-vRq4h3j5.js.map +0 -1
- package/dist/chunks/styles-Cm62yyQX.js.map +0 -1
|
@@ -1316,9 +1316,9 @@ export const YED_LINES: ReadonlyMap<string, string> = new Map([
|
|
|
1316
1316
|
/** yEd's arrows, by the neutral arrow each draws exactly. */
|
|
1317
1317
|
export const YED_ARROWS: ReadonlyMap<string, string> = new Map([
|
|
1318
1318
|
["none", "none"],
|
|
1319
|
-
["triangle", "
|
|
1319
|
+
["triangle", "delta"],
|
|
1320
1320
|
["triangle-open", "white_delta"],
|
|
1321
|
-
["vee", "
|
|
1321
|
+
["vee", "standard"],
|
|
1322
1322
|
["tee", "t_shape"],
|
|
1323
1323
|
["diamond", "diamond"],
|
|
1324
1324
|
["diamond-open", "white_diamond"],
|
|
@@ -1603,7 +1603,7 @@ function polyLineEdgeTree(get: GetValue, path: unknown, approximate: Approximate
|
|
|
1603
1603
|
const target = get("targetArrow");
|
|
1604
1604
|
if (source !== null || target !== null) {
|
|
1605
1605
|
const arrow = (property: string, value: StyleValue | null): string =>
|
|
1606
|
-
typeof value === "string" ? yedSpelled(YED_ARROWS, value, "
|
|
1606
|
+
typeof value === "string" ? yedSpelled(YED_ARROWS, value, "delta", property, approximate) : "none";
|
|
1607
1607
|
edge["y:Arrows"] = { "@_source": arrow("sourceArrow", source), "@_target": arrow("targetArrow", target) };
|
|
1608
1608
|
}
|
|
1609
1609
|
const label = labelTree(get, false, approximate);
|
|
@@ -651,17 +651,19 @@ function readEdgeArrow(
|
|
|
651
651
|
|
|
652
652
|
/**
|
|
653
653
|
* yFiles 3.0 renamed 2.6's DEFAULT arrow to STEALTH and CIRCLE to ELLIPSE
|
|
654
|
-
* (https://docs.yfiles.com/yfiles-html/api/ArrowType/); each is read as its 2.6 name is.
|
|
654
|
+
* (https://docs.yfiles.com/yfiles-html/api/ArrowType/); each is read as its 2.6 name is. STEALTH is "an arrow that is
|
|
655
|
+
* notched on the side opposite its tip": a vee.
|
|
655
656
|
*/
|
|
656
657
|
const RENAMED_ARROWS: ReadonlyMap<string, string> = new Map([
|
|
657
|
-
["DEFAULT", "
|
|
658
|
-
["STEALTH", "
|
|
658
|
+
["DEFAULT", "vee"],
|
|
659
|
+
["STEALTH", "vee"],
|
|
659
660
|
["ELLIPSE", "circle"],
|
|
660
661
|
]);
|
|
661
662
|
|
|
662
|
-
/** The filled arrows whose `{x:Null}` fill draws them as their open outline. */
|
|
663
|
+
/** The filled arrows whose `{x:Null}` fill draws them as their open outline (a stealth's as the open triangle's). */
|
|
663
664
|
const HOLLOW_ARROWS: ReadonlyMap<string, string> = new Map([
|
|
664
665
|
["triangle", "triangle-open"],
|
|
666
|
+
["vee", "triangle-open"],
|
|
665
667
|
["diamond", "diamond-open"],
|
|
666
668
|
["circle", "circle-open"],
|
|
667
669
|
]);
|
|
@@ -785,6 +785,8 @@ interface VisLayer {
|
|
|
785
785
|
selfReference?: unknown;
|
|
786
786
|
selfReferenceSize?: unknown;
|
|
787
787
|
physics?: unknown;
|
|
788
|
+
/** edges: the rest length of its spring (and of its support node's two springs together) */
|
|
789
|
+
length?: unknown;
|
|
788
790
|
image?: unknown;
|
|
789
791
|
brokenImage?: unknown;
|
|
790
792
|
useImageSize?: unknown;
|
|
@@ -1025,6 +1027,7 @@ function visEdgeLayer(o: Record<string, unknown>, set: SetLayer): void {
|
|
|
1025
1027
|
set("selfReference", o.selfReference);
|
|
1026
1028
|
set("selfReferenceSize", o.selfReferenceSize);
|
|
1027
1029
|
set("physics", o.physics);
|
|
1030
|
+
set("length", o.length);
|
|
1028
1031
|
}
|
|
1029
1032
|
|
|
1030
1033
|
/**
|
|
@@ -1809,7 +1812,9 @@ function visStyles(snapshot: GraphSnapshot, src: JsonStyleSource, context: Style
|
|
|
1809
1812
|
const borderOf = (i: number): StyleValue =>
|
|
1810
1813
|
node.values.borderColor?.[i] ?? (toolNode?.borderColor?.value as StyleValue | undefined) ?? "#2b7ce9";
|
|
1811
1814
|
const geometry = visGeometry(snapshot, node.values, shapes);
|
|
1812
|
-
const
|
|
1815
|
+
const support = visSupport(options.physics, options.layout);
|
|
1816
|
+
const edges = visLayer(options.edges, "edge");
|
|
1817
|
+
const edge = visEdges(snapshot, edges, borderOf, { ...geometry, support }, notes, context);
|
|
1813
1818
|
const values = { node: node.values, edge: edge.values };
|
|
1814
1819
|
exactFromBlock(snapshot, src.block, values, visWritten(snapshot, values.edge));
|
|
1815
1820
|
return {
|
|
@@ -2444,6 +2449,8 @@ interface VisGeometry {
|
|
|
2444
2449
|
readonly at: (i: number) => readonly [number, number] | null;
|
|
2445
2450
|
/** A node's half width, half height, border width and vis-network shape, or null for a node sized by its label. */
|
|
2446
2451
|
readonly box: (i: number) => VisBox | null;
|
|
2452
|
+
/** The forces vis-network's physics moves a dynamic edge's support node by, or null where nothing moves it. */
|
|
2453
|
+
readonly support?: Row | null;
|
|
2447
2454
|
}
|
|
2448
2455
|
|
|
2449
2456
|
/** A node's half width, half height, border width and vis-network shape. */
|
|
@@ -2699,15 +2706,75 @@ function visRoute(defaults: VisLayer, own: VisLayer, s: number, t: number, g: Vi
|
|
|
2699
2706
|
return visLoop(defaults, own, g.box(s));
|
|
2700
2707
|
}
|
|
2701
2708
|
const smooth = visSmooth(defaults.smooth, own.smooth);
|
|
2709
|
+
if (smooth.enabled && smooth.type === "dynamic") {
|
|
2710
|
+
return visDynamic(defaults, own, g.support ?? null);
|
|
2711
|
+
}
|
|
2702
2712
|
const [a, b] = [g.at(s), g.at(t)];
|
|
2703
|
-
|
|
2704
|
-
if (!smooth.enabled || smooth.type === "dynamic" || a === null || b === null || (a[0] === b[0] && a[1] === b[1])) {
|
|
2713
|
+
if (!smooth.enabled || a === null || b === null || (a[0] === b[0] && a[1] === b[1])) {
|
|
2705
2714
|
return {};
|
|
2706
2715
|
}
|
|
2707
2716
|
const controls = smooth.type === "cubicBezier" ? visCubicVia(a, b, smooth) : [visStaticVia(a, b, smooth)];
|
|
2708
2717
|
return { curved: true, bendCurve: "bezier", bends: controls.map((p) => alongAcross(a, b, p)).join(" ") };
|
|
2709
2718
|
}
|
|
2710
2719
|
|
|
2720
|
+
/**
|
|
2721
|
+
* vis.js: a dynamic smooth edge, drawn through the support node vis-network's physics places, wherever its nodes
|
|
2722
|
+
* are (they need no position in the file): its forces, with the edge's own `length` as its springs' rest length.
|
|
2723
|
+
* Straight where physics does not run for it (`physics: false` on the network or the edge), as the support node
|
|
2724
|
+
* then stays at the middle.
|
|
2725
|
+
* @param defaults - `options.edges`
|
|
2726
|
+
* @param own - the edge's own keys
|
|
2727
|
+
* @param support - the network's forces, or null
|
|
2728
|
+
* @returns the route's values
|
|
2729
|
+
*/
|
|
2730
|
+
function visDynamic(defaults: VisLayer, own: VisLayer, support: Row | null): Row {
|
|
2731
|
+
if (support === null || own.physics === false || (own.physics === undefined && defaults.physics === false)) {
|
|
2732
|
+
return {};
|
|
2733
|
+
}
|
|
2734
|
+
const length = [own.length, defaults.length].find((v) => typeof v === "number" && Number.isFinite(v) && v >= 0);
|
|
2735
|
+
return { curved: true, ...support, ...(length === undefined ? {} : { supportSpringLength: length as number }) };
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
/** vis-network 9.1.9's physics defaults for the two solvers whose forces a support node is drawn under. */
|
|
2739
|
+
const VIS_SOLVERS: Readonly<Record<string, { model: string; g: number; cg: number; length: number; k: number }>> = {
|
|
2740
|
+
barnesHut: { model: "barnesHut", g: -2000, cg: 0.3, length: 95, k: 0.04 },
|
|
2741
|
+
forceAtlas2Based: { model: "forceAtlas2", g: -50, cg: 0.01, length: 100, k: 0.08 },
|
|
2742
|
+
};
|
|
2743
|
+
|
|
2744
|
+
/**
|
|
2745
|
+
* vis.js: the forces a dynamic edge's support node settles under (9.1.9 BarnesHutSolver,
|
|
2746
|
+
* ForceAtlas2BasedRepulsionSolver, their central gravity solvers and SpringSolver). None where physics is off, where
|
|
2747
|
+
* a hierarchical layout turns every dynamic curve static (`_forceDisableDynamicCurves`), or under the repulsion and
|
|
2748
|
+
* hierarchicalRepulsion solvers (ponytail: drawn straight; their force laws when a file uses them).
|
|
2749
|
+
* @param physics - `options.physics`
|
|
2750
|
+
* @param layout - `options.layout`
|
|
2751
|
+
* @returns the neutral support values, or null
|
|
2752
|
+
*/
|
|
2753
|
+
function visSupport(physics: unknown, layout: unknown): Row | null {
|
|
2754
|
+
const hierarchical = isJsonObject(layout) ? layout.hierarchical : undefined;
|
|
2755
|
+
if (hierarchical === true || (isJsonObject(hierarchical) && hierarchical.enabled !== false)) {
|
|
2756
|
+
return null;
|
|
2757
|
+
}
|
|
2758
|
+
if (physics === false || (isJsonObject(physics) && physics.enabled === false)) {
|
|
2759
|
+
return null;
|
|
2760
|
+
}
|
|
2761
|
+
const o = isJsonObject(physics) ? physics : {};
|
|
2762
|
+
const solver = typeof o.solver === "string" ? o.solver : "barnesHut";
|
|
2763
|
+
const base = VIS_SOLVERS[solver] as (typeof VIS_SOLVERS)[string] | undefined;
|
|
2764
|
+
if (base === undefined) {
|
|
2765
|
+
return null;
|
|
2766
|
+
}
|
|
2767
|
+
const given = isJsonObject(o[solver]) ? o[solver] : {};
|
|
2768
|
+
const num = (v: unknown, fallback: number): number => (typeof v === "number" && Number.isFinite(v) ? v : fallback);
|
|
2769
|
+
return {
|
|
2770
|
+
supportModel: base.model,
|
|
2771
|
+
supportSpringLength: Math.max(0, num(given.springLength, base.length)),
|
|
2772
|
+
supportSpringConstant: Math.max(0, num(given.springConstant, base.k)),
|
|
2773
|
+
supportRepulsion: Math.max(0, -num(given.gravitationalConstant, base.g)),
|
|
2774
|
+
supportCentralGravity: Math.max(0, num(given.centralGravity, base.cg)),
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2711
2778
|
/**
|
|
2712
2779
|
* vis.js: a self-loop, vis-network's circle (9.1.9 `EdgeBase._getCircleData`, `getSelfRefCoordinates`) of radius
|
|
2713
2780
|
* `selfReference.size` (or the older `selfReferenceSize`; 20) centered on the node's outline (visToBorder) along
|
|
@@ -2826,7 +2893,14 @@ function visFrontArc(cx: number, cy: number, radius: number, angle: number, box:
|
|
|
2826
2893
|
const a = start + ((end - start) * k) / steps;
|
|
2827
2894
|
points.push(`${fraction((x + radius * Math.cos(a)) / hw)},${fraction(-(y + radius * Math.sin(a)) / hh)}`);
|
|
2828
2895
|
}
|
|
2829
|
-
|
|
2896
|
+
// the arc's two ends are where vis-network starts and stops it, off the node as often as on it: ports, so no line
|
|
2897
|
+
// is drawn from the node out to them
|
|
2898
|
+
return {
|
|
2899
|
+
curved: false,
|
|
2900
|
+
sourcePort: points[0],
|
|
2901
|
+
bends: points.slice(1, -1).join(" "),
|
|
2902
|
+
targetPort: points.at(-1) ?? points[0],
|
|
2903
|
+
};
|
|
2830
2904
|
}
|
|
2831
2905
|
|
|
2832
2906
|
/**
|
|
@@ -2887,6 +2961,7 @@ const VIS_KEYS: Readonly<Record<StyleTarget, readonly string[]>> = {
|
|
|
2887
2961
|
"selfReference",
|
|
2888
2962
|
"selfReferenceSize",
|
|
2889
2963
|
"physics",
|
|
2964
|
+
"length",
|
|
2890
2965
|
"widthConstraint",
|
|
2891
2966
|
],
|
|
2892
2967
|
};
|
|
@@ -3077,13 +3152,34 @@ const visFitBack =
|
|
|
3077
3152
|
* @returns the readings back
|
|
3078
3153
|
*/
|
|
3079
3154
|
function visWritten(snapshot: GraphSnapshot, edgeValues: Values): Written {
|
|
3155
|
+
const isLoop = (i: number): boolean => snapshot.edgeSource(i) === snapshot.edgeTarget(i);
|
|
3080
3156
|
const loop =
|
|
3081
3157
|
(property: string) =>
|
|
3082
3158
|
(want: (p: string) => StyleValue | null, i: number): StyleValue | null =>
|
|
3083
|
-
|
|
3159
|
+
isLoop(i) ? (edgeValues[property]?.[i] ?? null) : want(property);
|
|
3160
|
+
// the writer writes no `smooth` and no `options.physics`, so every other edge reads back as vis-network's default,
|
|
3161
|
+
// a dynamic curve under the default barnesHut physics
|
|
3162
|
+
const dynamic = visSupport(undefined, undefined) ?? {};
|
|
3163
|
+
const support = (property: string) => (_want: unknown, i: number) =>
|
|
3164
|
+
isLoop(i) ? (edgeValues[property]?.[i] ?? null) : (dynamic[property] ?? null);
|
|
3165
|
+
const supports = Object.fromEntries(Object.keys(dynamic).map((property) => [property, support(property)]));
|
|
3166
|
+
// an edge's own `length`, kept as data, is its springs' rest length again
|
|
3167
|
+
const lengths = cells(snapshot, "edge", "length");
|
|
3168
|
+
supports.supportSpringLength = (want, i) => {
|
|
3169
|
+
const own = lengths?.(i);
|
|
3170
|
+
return typeof own === "number" && Number.isFinite(own) && own >= 0 && !isLoop(i)
|
|
3171
|
+
? own
|
|
3172
|
+
: support("supportSpringLength")(want, i);
|
|
3173
|
+
};
|
|
3084
3174
|
return {
|
|
3085
3175
|
node: VIS_WRITTEN.node,
|
|
3086
|
-
edge: {
|
|
3176
|
+
edge: {
|
|
3177
|
+
...VIS_WRITTEN.edge,
|
|
3178
|
+
...supports,
|
|
3179
|
+
bends: loop("bends"),
|
|
3180
|
+
bendCurve: loop("bendCurve"),
|
|
3181
|
+
curved: (_want, i) => (isLoop(i) ? (edgeValues.curved?.[i] ?? null) : true),
|
|
3182
|
+
},
|
|
3087
3183
|
};
|
|
3088
3184
|
}
|
|
3089
3185
|
|
|
@@ -138,6 +138,17 @@ export function drawsLineLabels(draw: PajekDraw | null): boolean {
|
|
|
138
138
|
return draw !== null && flag(draw, "Form8.withLabels1");
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Whether Pajek draws the arcs it would bow apart (a mutual pair, parallel arcs) straight: under Export Line Default /
|
|
143
|
+
* Straight Lines (`Form10.StraightLinesOnly1`, `pajek-specimen-edges-lines-p4` against `-p2`). A line's own Bezier
|
|
144
|
+
* route stays curved.
|
|
145
|
+
* @param draw - the settings, or null when the import names none (Pajek's defaults)
|
|
146
|
+
* @returns true when they are drawn straight
|
|
147
|
+
*/
|
|
148
|
+
export function drawsStraightLines(draw: PajekDraw | null): boolean {
|
|
149
|
+
return draw !== null && flag(draw, "Form10.StraightLinesOnly1");
|
|
150
|
+
}
|
|
151
|
+
|
|
141
152
|
/**
|
|
142
153
|
* An ini number.
|
|
143
154
|
* @param draw - the settings
|
|
@@ -42,7 +42,7 @@ import type {
|
|
|
42
42
|
import type { LossNote } from "../../types.js";
|
|
43
43
|
import { graphvizFontName, postscriptFont } from "../dot/drawing.js";
|
|
44
44
|
import { type ValueColumns, valueColumns } from "../graphml/styles-yed.js";
|
|
45
|
-
import { drawLines, drawsLineLabels, drawVertices, type PajekDraw } from "./draw.js";
|
|
45
|
+
import { drawLines, drawsLineLabels, drawsStraightLines, drawVertices, type PajekDraw } from "./draw.js";
|
|
46
46
|
import { LABEL_COLUMN, PAJEK_STYLE_COMMENT, SHAPE_COLUMN, VALUE_COLUMN } from "./syntax.js";
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -79,6 +79,7 @@ export const PAJEK_STYLE_CAPABILITIES: StyleWriteCapabilities = Object.freeze({
|
|
|
79
79
|
"lineStyle",
|
|
80
80
|
"sourceArrow",
|
|
81
81
|
"targetArrow",
|
|
82
|
+
"arrowSize",
|
|
82
83
|
"arrowPosition",
|
|
83
84
|
"label",
|
|
84
85
|
"labelColor",
|
|
@@ -108,6 +109,14 @@ const PAJEK_FIGURES: ReadonlyMap<string, readonly [number, number]> = new Map([
|
|
|
108
109
|
["other:woman", [3.003, 6.478]],
|
|
109
110
|
]);
|
|
110
111
|
|
|
112
|
+
/**
|
|
113
|
+
* The factor Pajek draws a man or a woman at along an axis whose `x_fact` or `y_fact` the line leaves unset: 0.3, not
|
|
114
|
+
* the 1 of every other shape (`pajek-synth-vertices`: a man that sets neither, vertices 24, 48 and 81, is drawn 15.02
|
|
115
|
+
* across, 0.3 of the 50.05 of `x_fact 1`, and 30.55 tall from his feet to his head, 0.3 of `y_fact 1`'s 101.83; a woman
|
|
116
|
+
* that sets neither, vertices 9, 33 and 57, 16.30 across, 0.3 of `x_fact 1`'s 54.35).
|
|
117
|
+
*/
|
|
118
|
+
const PAJEK_FIGURE_UNSET_FACTOR = 0.3;
|
|
119
|
+
|
|
111
120
|
/**
|
|
112
121
|
* Points per coordinate unit: "When Postscript picture is obtained, the square [0,1]x[0,1] is transformed to
|
|
113
122
|
* [0,500]x[0,500]" (Pajek, "Pajek to EPS", http://mrvar.fdv.uni-lj.si/pajek/DrawEPS.htm).
|
|
@@ -151,11 +160,12 @@ const CARRIED: Readonly<Record<StyleTarget, readonly string[]>> = {
|
|
|
151
160
|
};
|
|
152
161
|
|
|
153
162
|
/**
|
|
154
|
-
* What Pajek 6.02 carries from line to line as it reads: not the pattern `p
|
|
155
|
-
* (`pajek-synth-lines`: edge `3 10` after `1 8 ... p Dots` and arc `5 6 -1` after a `p Solid`
|
|
156
|
-
* before set one
|
|
163
|
+
* What Pajek 6.02 carries from line to line as it reads: not the pattern `p` nor the arrowhead's place `ap`, which the
|
|
164
|
+
* writer still restates (`pajek-synth-lines`: edge `3 10` after `1 8 ... p Dots` and arc `5 6 -1` after a `p Solid`
|
|
165
|
+
* are drawn as if no line before set one; arcs `11 12` after `10 11 ... ap 0.5` and `18 19` after `17 18 ... ap 0.25`
|
|
166
|
+
* have their arrowheads at their targets).
|
|
157
167
|
*/
|
|
158
|
-
const CARRIED_LINE: readonly string[] = CARRIED.edge.filter((key) => key !== "p");
|
|
168
|
+
const CARRIED_LINE: readonly string[] = CARRIED.edge.filter((key) => key !== "p" && key !== "ap");
|
|
159
169
|
|
|
160
170
|
/** The width Pajek draws an edge (undirected) at for each unit of `w`; an arc is drawn `w` wide. */
|
|
161
171
|
const EDGE_WIDTH_PER_W = 2;
|
|
@@ -470,7 +480,7 @@ function readLabelAngle(raw: unknown, put: Put, at: At, place: Place | null): vo
|
|
|
470
480
|
* that sets neither is left to the default beneath. The manual (p. 89) calls `s_size` "default size", but Pajek 6.02
|
|
471
481
|
* draws a vertex the same size whatever its `s_size` (`pajek-specimen-node-sizes-opacity`: `s_size` 0.5, 2 and 3 all
|
|
472
482
|
* drawn 36 across at `x_fact 1.8`), so it is not read. A man or a woman is the box of the figure Pajek draws for
|
|
473
|
-
* that size (`PAJEK_FIGURES`), set or not.
|
|
483
|
+
* that size (`PAJEK_FIGURES`), set or not, at `PAJEK_FIGURE_UNSET_FACTOR` along an axis the line sets no factor for.
|
|
474
484
|
* @param get - the vertex's parameters
|
|
475
485
|
* @param put - receives the values
|
|
476
486
|
* @param at - the sites
|
|
@@ -485,7 +495,8 @@ function readSize(get: Param, put: Put, at: At, figure?: readonly [number, numbe
|
|
|
485
495
|
if (f === null && figure === undefined) {
|
|
486
496
|
continue;
|
|
487
497
|
}
|
|
488
|
-
const
|
|
498
|
+
const unset = figure === undefined ? 1 : PAJEK_FIGURE_UNSET_FACTOR;
|
|
499
|
+
const factor = f === null ? unset : canonNumber(f, at(property));
|
|
489
500
|
const over = figure?.[k] ?? 1;
|
|
490
501
|
put(property, factor === null ? null : Number((PAJEK_VERTEX_SIZE * factor * over).toPrecision(15)));
|
|
491
502
|
}
|
|
@@ -620,7 +631,7 @@ function readLine(
|
|
|
620
631
|
}
|
|
621
632
|
// an arrowhead is 6 x `s` long and 5 x `s` wide; `s` 1, Pajek's normal size, is 6 long (the pajek tool arrowhead)
|
|
622
633
|
readNumber(get("s"), "arrowSize", put, at);
|
|
623
|
-
readArrowPosition(get("ap"), put, at);
|
|
634
|
+
readArrowPosition(get("ap"), put, at, run);
|
|
624
635
|
// a loop is drawn as the Bezier route of a line PAJEK_POINTS_PER_UNIT long to the right, its two ends on the node
|
|
625
636
|
const line = loop === null ? get : loopParam(get);
|
|
626
637
|
const controls = loop === null ? null : readLoop(line, put, loop);
|
|
@@ -810,38 +821,45 @@ function readBezier(get: Param, put: Put, run: Run | null): void {
|
|
|
810
821
|
put("bends", controls.map(([along, across]) => `${fraction(along)},${fraction(across)}`).join(" "));
|
|
811
822
|
}
|
|
812
823
|
|
|
824
|
+
/** How far back from its target's outline, as the file sizes it, Pajek 6.02 draws a Bezier arc's arrowhead tip. */
|
|
825
|
+
const PAJEK_TIP_BACK = 1.5;
|
|
826
|
+
|
|
813
827
|
/**
|
|
814
828
|
* Where Pajek 6.02's SVG export ends an arc's Bezier route (`readBezier`): at its arrowhead's back, not at its target's
|
|
815
829
|
* center. It places the arrowhead as on a straight arc, its tip 1.5 short of where the straight line meets the
|
|
816
830
|
* target's outline, turns it `-a2` degrees about that point, and runs the curve from the source's center to the
|
|
817
831
|
* arrowhead's back, its control points measured on that shorter line (`pajek-specimen-edges-lines-p2`
|
|
818
832
|
* `route-bezier-opposite-arc`: the arrowhead's back at (347.02, 124.32), 6.5 back from the outline at (352, 128.5),
|
|
819
|
-
* turned 40 degrees; `route-bezier-same-arc` at (571.02, 132.68), turned -40).
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
833
|
+
* turned 40 degrees; `route-bezier-same-arc` at (571.02, 132.68), turned -40). The curve's control points are written
|
|
834
|
+
* in the frame of the line between the two centers, which graphty places bends in, and the route ends at a target port
|
|
835
|
+
* on the arrowhead's tip, 1.5 back from the outline along the turned arrowhead, so graphty's curve runs through the
|
|
836
|
+
* same control points to where Pajek's arrowhead ends: drawn to the target's center instead, the curve bowed 0.135 of
|
|
837
|
+
* its chord where Pajek's bows 0.196 (`pajek-specimen-arrows` `arrow-bezier`). ponytail: the outline is the ellipse
|
|
838
|
+
* of the vertex's width and height, as the file sizes it (before the Draw window's Size of Vertices or vectors),
|
|
839
|
+
* whatever its shape.
|
|
823
840
|
* @param get - the line's parameters
|
|
824
841
|
* @param put - receives the values
|
|
825
842
|
* @param run - the line from its source's center to its target's, in drawn units, y down
|
|
826
843
|
* @param target - the target vertex's width and height
|
|
844
|
+
* @returns whether the arc's route ends at its arrowhead's tip (a target port)
|
|
827
845
|
*/
|
|
828
|
-
function readArcEnd(get: Param, put: Put, run: Run, target: readonly [number, number]):
|
|
846
|
+
function readArcEnd(get: Param, put: Put, run: Run, target: readonly [number, number]): boolean {
|
|
829
847
|
const ap = numberOrNull(get("ap")) ?? 0;
|
|
830
848
|
const length = Math.hypot(run[0], run[1]);
|
|
831
849
|
if (ap !== 0 || length === 0 || bezierControls(get, run) === null) {
|
|
832
|
-
return;
|
|
850
|
+
return false;
|
|
833
851
|
}
|
|
834
852
|
const [ux, uy] = [run[0] / length, run[1] / length];
|
|
835
853
|
// the straight line meets the outline r from the target's center; the arrowhead's back is 1.5 + 5 s before it
|
|
836
854
|
const r = 1 / Math.hypot(ux / (target[0] / 2), uy / (target[1] / 2));
|
|
837
|
-
const back =
|
|
855
|
+
const back = PAJEK_TIP_BACK + 5 * (numberOrNull(get("s")) ?? 1);
|
|
838
856
|
const turn = (-(numberOrNull(get("a2")) ?? 0) * Math.PI) / 180;
|
|
839
857
|
const [c, s] = [Math.cos(turn), Math.sin(turn)];
|
|
840
858
|
// the arrowhead's back, from the source's center
|
|
841
859
|
const end: Run = [run[0] - r * ux - back * (ux * c - uy * s), run[1] - r * uy - back * (ux * s + uy * c)];
|
|
842
860
|
const controls = bezierControls(get, end);
|
|
843
861
|
if (controls === null) {
|
|
844
|
-
return;
|
|
862
|
+
return false;
|
|
845
863
|
}
|
|
846
864
|
// each control point on screen from the source's center, then along and to the left of the line to the target
|
|
847
865
|
const L2 = length * length;
|
|
@@ -851,6 +869,28 @@ function readArcEnd(get: Param, put: Put, run: Run, target: readonly [number, nu
|
|
|
851
869
|
return `${fraction((x * run[0] + y * run[1]) / L2)},${fraction((x * run[1] - y * run[0]) / L2)}`;
|
|
852
870
|
});
|
|
853
871
|
put("bends", bends.join(" "));
|
|
872
|
+
// the tip, from the target's center: 1.5 back from the outline along the turned arrowhead; a port is a fraction of
|
|
873
|
+
// the half width and half height, y up
|
|
874
|
+
const tip: Run = [-r * ux - PAJEK_TIP_BACK * (ux * c - uy * s), -r * uy - PAJEK_TIP_BACK * (ux * s + uy * c)];
|
|
875
|
+
put("targetPort", `${fraction(tip[0] / (target[0] / 2))},${fraction(-tip[1] / (target[1] / 2))}`);
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** How far short of its target's outline, border and all, Pajek 6.02 stops an arc's arrowhead, in px. */
|
|
880
|
+
const PAJEK_TIP_GAP = 1;
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Where an arc's arrowhead stops: PAJEK_TIP_GAP short of its target's outline, whatever the arrowhead's size `s` (6
|
|
884
|
+
* x `s` long), so a share of its length of 1 / (6 `s`) (`pajek-synth-lines`: every arc's tip 1.25 from the center
|
|
885
|
+
* of a vertex 20 across with a border 0.5 wide, `s` 1 to 5; the specimen sheets' 1.5 with a border 1 wide).
|
|
886
|
+
* @param get - the line's parameters
|
|
887
|
+
* @param put - receives the value
|
|
888
|
+
*/
|
|
889
|
+
function readTipGap(get: Param, put: Put): void {
|
|
890
|
+
const s = numberOrNull(get("s")) ?? 1;
|
|
891
|
+
if (s > 0) {
|
|
892
|
+
put("targetArrowGap", Number((PAJEK_TIP_GAP / (6 * s)).toPrecision(6)));
|
|
893
|
+
}
|
|
854
894
|
}
|
|
855
895
|
|
|
856
896
|
/**
|
|
@@ -1016,18 +1056,25 @@ function numberOrNull(raw: unknown): number | null {
|
|
|
1016
1056
|
|
|
1017
1057
|
/**
|
|
1018
1058
|
* Where a line's arrowhead sits, Pajek's `ap` (manual p. 90): a fraction of the line from its end up to 1 (0.5 is the
|
|
1019
|
-
* middle); a value above 1 is a distance from the end,
|
|
1059
|
+
* middle); a value above 1 is a distance from the end in px, the arrowhead's back that far from the target's center
|
|
1060
|
+
* (`pajek-synth-lines` arc `3 4 ... ap 20`), read as its share of a placed line and noted on an unplaced one.
|
|
1020
1061
|
* @param raw - the parameter's value, or null
|
|
1021
1062
|
* @param put - receives the value
|
|
1022
1063
|
* @param at - the sites
|
|
1064
|
+
* @param run - the line from its source's center to its target's, in drawn units, or null when unplaced
|
|
1023
1065
|
*/
|
|
1024
|
-
function readArrowPosition(raw: unknown, put: Put, at: At): void {
|
|
1066
|
+
function readArrowPosition(raw: unknown, put: Put, at: At, run: Run | null = null): void {
|
|
1025
1067
|
if (raw === null) {
|
|
1026
1068
|
return;
|
|
1027
1069
|
}
|
|
1028
1070
|
const ap = canonNumber(raw, at("arrowPosition"));
|
|
1071
|
+
const length = run === null ? 0 : Math.hypot(run[0], run[1]);
|
|
1029
1072
|
if (ap !== null && ap > 1) {
|
|
1030
|
-
|
|
1073
|
+
if (length > 0) {
|
|
1074
|
+
put("arrowPosition", Number(Math.min(1, ap / length).toPrecision(6)));
|
|
1075
|
+
} else {
|
|
1076
|
+
noteAt(at("arrowPosition"), STYLE_ISSUE.NOT_CARRIED, `ap ${String(ap)}`);
|
|
1077
|
+
}
|
|
1031
1078
|
return;
|
|
1032
1079
|
}
|
|
1033
1080
|
// 0 is Pajek's own place for the arrow, at the line's end, which a line restates after one that moved it
|
|
@@ -1074,6 +1121,60 @@ function directedEdges(snapshot: GraphSnapshot): boolean[] {
|
|
|
1074
1121
|
return out;
|
|
1075
1122
|
}
|
|
1076
1123
|
|
|
1124
|
+
/** The Bezier route Pajek 6.02 draws an arc on that shares its two vertices with another arc. */
|
|
1125
|
+
const SHARED_ARC_BOW: Readonly<Record<string, number>> = { a1: 15, k1: 0.25, a2: 15, k2: 0.25 };
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* The arcs Pajek 6.02 bows apart, and to which side: an arc that shares its two vertices with another arc, either way
|
|
1129
|
+
* round, is drawn as `a1 15 k1 0.25 a2 15 k2 0.25` (`pajek-synth-lines`: 20 -> 30 and 30 -> 20, each bowed to the
|
|
1130
|
+
* same side of its own way, so apart; `pajek-iliad` 14 -> 72 and 72 -> 14), and of two that run the same way the first is bowed the
|
|
1131
|
+
* other way, `a1 -15 a2 -15` (`pajek-iliad`: the two arcs 250 -> 123). ponytail: three or more arcs the same way
|
|
1132
|
+
* alternate sides, the last at `a1 15`; Pajek's drawing of them was not measured.
|
|
1133
|
+
* @param snapshot - the graph
|
|
1134
|
+
* @param directed - whether each edge is an arc
|
|
1135
|
+
* @returns each bowed arc's side: 1 for `a1 15 a2 15`, -1 for `a1 -15 a2 -15`
|
|
1136
|
+
*/
|
|
1137
|
+
function sharedArcBows(snapshot: GraphSnapshot, directed: readonly boolean[]): Map<number, 1 | -1> {
|
|
1138
|
+
const byPair = new Map<string, number[]>();
|
|
1139
|
+
for (let e = 0; e < snapshot.edgeCount; e++) {
|
|
1140
|
+
const [u, v] = [snapshot.edgeSource(e), snapshot.edgeTarget(e)];
|
|
1141
|
+
if (directed[e] && u !== v) {
|
|
1142
|
+
const key = u < v ? `${String(u)} ${String(v)}` : `${String(v)} ${String(u)}`;
|
|
1143
|
+
byPair.set(key, [...(byPair.get(key) ?? []), e]);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
const out = new Map<number, 1 | -1>();
|
|
1147
|
+
for (const arcs of byPair.values()) {
|
|
1148
|
+
if (arcs.length < 2) {
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
for (const e of arcs) {
|
|
1152
|
+
const same = arcs.filter((x) => snapshot.edgeSource(x) === snapshot.edgeSource(e));
|
|
1153
|
+
out.set(e, (same.length - 1 - same.indexOf(e)) % 2 === 0 ? 1 : -1);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
return out;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* A bowed arc's parameters (`sharedArcBows`): its own Bezier route when it sets one, else Pajek's bow to its side.
|
|
1161
|
+
* @param get - the line's parameters
|
|
1162
|
+
* @param side - 1 for `a1 15 a2 15`, -1 for `a1 -15 a2 -15`
|
|
1163
|
+
* @returns the parameters
|
|
1164
|
+
*/
|
|
1165
|
+
function bowedParams(get: Param, side: 1 | -1): Param {
|
|
1166
|
+
if (Object.keys(SHARED_ARC_BOW).some((key) => get(key) !== null)) {
|
|
1167
|
+
return get;
|
|
1168
|
+
}
|
|
1169
|
+
return (key) => {
|
|
1170
|
+
const bow = SHARED_ARC_BOW[key] as number | undefined;
|
|
1171
|
+
if (bow === undefined) {
|
|
1172
|
+
return get(key);
|
|
1173
|
+
}
|
|
1174
|
+
return key.startsWith("a") ? side * bow : bow;
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1077
1178
|
/**
|
|
1078
1179
|
* The columns of a table that hold styles.
|
|
1079
1180
|
* @param table - the node or edge table
|
|
@@ -1135,14 +1236,23 @@ export function pajekStyles(snapshot: GraphSnapshot, context: StyleReadContext):
|
|
|
1135
1236
|
loops.set(i, controls);
|
|
1136
1237
|
}
|
|
1137
1238
|
: null;
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1239
|
+
const bow = bows.get(i);
|
|
1240
|
+
const line = bow === undefined ? get : bowedParams(get, bow);
|
|
1241
|
+
readLine(line, put, at, directed[i], labels, run, loop);
|
|
1242
|
+
// an arc's arrowhead stops short of its target: at the port on its tip of a Bezier route, else by its gap (a
|
|
1243
|
+
// file graphty wrote keeps its gaps in its block)
|
|
1244
|
+
const ported = directed[i] && run !== null && readArcEnd(line, put, run, targetSize(snapshot.edgeTarget(i)));
|
|
1245
|
+
if (directed[i] && loop === null && !ported && block === null) {
|
|
1246
|
+
readTipGap(get, put);
|
|
1141
1247
|
}
|
|
1142
1248
|
readLineValue(lineValue(i), get, put, directed[i]);
|
|
1143
1249
|
readHooks(get, put, keywords[snapshot.edgeSource(i)], keywords[snapshot.edgeTarget(i)]);
|
|
1144
1250
|
};
|
|
1145
1251
|
const directed = directedEdges(snapshot);
|
|
1252
|
+
// Straight Lines draws the arcs Pajek would bow apart straight, and a file graphty wrote keeps its routes in its
|
|
1253
|
+
// block
|
|
1254
|
+
const bows =
|
|
1255
|
+
drawsStraightLines(draw) || block !== null ? new Map<number, 1 | -1>() : sharedArcBows(snapshot, directed);
|
|
1146
1256
|
const keywords: unknown[] = [];
|
|
1147
1257
|
const weights = explicitWeights(snapshot);
|
|
1148
1258
|
// the line's value: its weight, or the `value` column when the import kept the value as an attribute
|
|
@@ -1566,10 +1676,7 @@ function lineParams(get: Get, directed: boolean, approximate: Approximate): Map<
|
|
|
1566
1676
|
if (typeof lineStyle === "string" && lineStyle !== "solid" && pajekDots(lineStyle, out, directed, approximate)) {
|
|
1567
1677
|
out.set("p", "Dots");
|
|
1568
1678
|
}
|
|
1569
|
-
|
|
1570
|
-
if (typeof position === "number" && position > 0) {
|
|
1571
|
-
out.set("ap", position);
|
|
1572
|
-
}
|
|
1679
|
+
arrowParams(get, out);
|
|
1573
1680
|
lineLabelParams(get, out, approximate);
|
|
1574
1681
|
const target = get("targetArrow");
|
|
1575
1682
|
if (target !== null && target !== (directed ? "triangle" : "none")) {
|
|
@@ -1582,6 +1689,22 @@ function lineParams(get: Get, directed: boolean, approximate: Approximate): Map<
|
|
|
1582
1689
|
return out;
|
|
1583
1690
|
}
|
|
1584
1691
|
|
|
1692
|
+
/**
|
|
1693
|
+
* A line's arrowhead parameters: its size `s`, which also places a Bezier arc's end (`readArcEnd`), and its place `ap`.
|
|
1694
|
+
* @param get - the line's values
|
|
1695
|
+
* @param out - the parameters, changed in place
|
|
1696
|
+
*/
|
|
1697
|
+
function arrowParams(get: Get, out: Map<string, Written>): void {
|
|
1698
|
+
const arrowSize = get("arrowSize");
|
|
1699
|
+
if (typeof arrowSize === "number" && arrowSize !== 1) {
|
|
1700
|
+
out.set("s", arrowSize);
|
|
1701
|
+
}
|
|
1702
|
+
const position = get("arrowPosition");
|
|
1703
|
+
if (typeof position === "number" && position > 0) {
|
|
1704
|
+
out.set("ap", position);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1585
1708
|
/**
|
|
1586
1709
|
* A line's label parameters: its text `l` (a line break or a double quote written as Pajek can hold it, noted), its
|
|
1587
1710
|
* color `lc`, size `fos` and font `font`.
|
|
@@ -1908,6 +2031,8 @@ function lineRestated(key: string, directed: boolean): Written {
|
|
|
1908
2031
|
return directed ? Number(defaults.width) : Number(defaults.width) / EDGE_WIDTH_PER_W;
|
|
1909
2032
|
case "p":
|
|
1910
2033
|
return "Solid";
|
|
2034
|
+
case "s":
|
|
2035
|
+
return 1;
|
|
1911
2036
|
default:
|
|
1912
2037
|
return 0;
|
|
1913
2038
|
}
|
package/src/index.ts
CHANGED
|
@@ -119,7 +119,9 @@ export {
|
|
|
119
119
|
type AwsSigV4Options,
|
|
120
120
|
type CredentialBind,
|
|
121
121
|
credentials,
|
|
122
|
+
credentialSlots,
|
|
122
123
|
fromStorableCredential,
|
|
124
|
+
type SlotValue,
|
|
123
125
|
type StorableCredential,
|
|
124
126
|
storableCredential,
|
|
125
127
|
} from "./sources/credentials.js";
|
|
@@ -166,6 +168,7 @@ export {
|
|
|
166
168
|
type RetryOptions,
|
|
167
169
|
type SourceBase,
|
|
168
170
|
type SourceContext,
|
|
171
|
+
type SourceCredentialSlot,
|
|
169
172
|
type SourceCursor,
|
|
170
173
|
type SourceDescriptor,
|
|
171
174
|
type SourceFeature,
|
|
@@ -230,6 +233,7 @@ export {
|
|
|
230
233
|
type CysImportOptions,
|
|
231
234
|
} from "./formats/cys/index.js";
|
|
232
235
|
export {
|
|
236
|
+
type BendPoint,
|
|
233
237
|
DOT_ISSUE,
|
|
234
238
|
DOT_LOSS,
|
|
235
239
|
DOT_STYLE_BLOCK_ATTRIBUTE,
|
|
@@ -238,6 +242,9 @@ export {
|
|
|
238
242
|
type DotExportOptions,
|
|
239
243
|
dotImporter,
|
|
240
244
|
type DotImportOptions,
|
|
245
|
+
graphvizPosBends,
|
|
246
|
+
RANK_COLUMN,
|
|
247
|
+
RANK_SET_COLUMN,
|
|
241
248
|
} from "./formats/dot/index.js";
|
|
242
249
|
export {
|
|
243
250
|
GEXF_1_2_CAPABILITIES,
|