@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.
Files changed (208) hide show
  1. package/dist/chunks/{approximations-Bzn3L-je.js → approximations-BRXsll0T.js} +2 -2
  2. package/dist/chunks/{approximations-Bzn3L-je.js.map → approximations-BRXsll0T.js.map} +1 -1
  3. package/dist/chunks/{block-iptrlMBb.js → block-yl0f_qLO.js} +27 -2
  4. package/dist/chunks/block-yl0f_qLO.js.map +1 -0
  5. package/dist/chunks/{context-C3p0RQxR.js → context-DizmppHy.js} +2 -2
  6. package/dist/chunks/{context-C3p0RQxR.js.map → context-DizmppHy.js.map} +1 -1
  7. package/dist/chunks/{credentials-DXHx7nwR.js → credentials-CmvJXYeH.js} +58 -9
  8. package/dist/chunks/credentials-CmvJXYeH.js.map +1 -0
  9. package/dist/chunks/{descriptor-DLA5kINx.js → descriptor-Dhez_Cew.js} +5 -1
  10. package/dist/chunks/{descriptor-DLA5kINx.js.map → descriptor-Dhez_Cew.js.map} +1 -1
  11. package/dist/chunks/{emit-BwHQkZle.js → emit-7Jkpg0yd.js} +132 -4
  12. package/dist/chunks/emit-7Jkpg0yd.js.map +1 -0
  13. package/dist/chunks/{exporter-nDfxIK5D.js → exporter-1Ajud15J.js} +4 -4
  14. package/dist/chunks/{exporter-nDfxIK5D.js.map → exporter-1Ajud15J.js.map} +1 -1
  15. package/dist/chunks/{exporter-D9yMtocA.js → exporter-B6yvL3wy.js} +3 -3
  16. package/dist/chunks/{exporter-D9yMtocA.js.map → exporter-B6yvL3wy.js.map} +1 -1
  17. package/dist/chunks/{exporter-_jrBTnos.js → exporter-CA5q8ROP.js} +4 -4
  18. package/dist/chunks/{exporter-_jrBTnos.js.map → exporter-CA5q8ROP.js.map} +1 -1
  19. package/dist/chunks/{exporter-D2CETOMK.js → exporter-CQQZFJ2v.js} +72 -10
  20. package/dist/chunks/exporter-CQQZFJ2v.js.map +1 -0
  21. package/dist/chunks/{generic-ymWCETAJ.js → generic-Dc21MAdg.js} +3 -3
  22. package/dist/chunks/{generic-ymWCETAJ.js.map → generic-Dc21MAdg.js.map} +1 -1
  23. package/dist/chunks/{importer-CT9mJ1nn.js → importer-C6NVpCXp.js} +7 -6
  24. package/dist/chunks/importer-C6NVpCXp.js.map +1 -0
  25. package/dist/chunks/{importer-GREKMdIf.js → importer-CKFe4q54.js} +4 -4
  26. package/dist/chunks/{importer-GREKMdIf.js.map → importer-CKFe4q54.js.map} +1 -1
  27. package/dist/chunks/{importer-PqaaKKnY.js → importer-CKbUbsJA.js} +4 -4
  28. package/dist/chunks/{importer-PqaaKKnY.js.map → importer-CKbUbsJA.js.map} +1 -1
  29. package/dist/chunks/{importer-DaLTjbds.js → importer-ClCsdgTJ.js} +97 -7
  30. package/dist/chunks/importer-ClCsdgTJ.js.map +1 -0
  31. package/dist/chunks/{importer-B92x1xY5.js → importer-CyVZTrf0.js} +3 -3
  32. package/dist/chunks/{importer-B92x1xY5.js.map → importer-CyVZTrf0.js.map} +1 -1
  33. package/dist/chunks/{importer-vRq4h3j5.js → importer-DgG4v1Ms.js} +89 -20
  34. package/dist/chunks/importer-DgG4v1Ms.js.map +1 -0
  35. package/dist/chunks/{importer-Vn5QvmmJ.js → importer-asFUrIsj.js} +2 -2
  36. package/dist/chunks/{importer-Vn5QvmmJ.js.map → importer-asFUrIsj.js.map} +1 -1
  37. package/dist/chunks/{lexicon-Clf4QIyl.js → lexicon-IVRq2Uoo.js} +2 -2
  38. package/dist/chunks/{lexicon-Clf4QIyl.js.map → lexicon-IVRq2Uoo.js.map} +1 -1
  39. package/dist/chunks/{round-trip-Zm3_ivgq.js → round-trip-Cy3_SHRZ.js} +2 -2
  40. package/dist/chunks/{round-trip-Zm3_ivgq.js.map → round-trip-Cy3_SHRZ.js.map} +1 -1
  41. package/dist/chunks/{sigv4-EokFic1V.js → sigv4-BXwVbza3.js} +3 -3
  42. package/dist/chunks/{sigv4-EokFic1V.js.map → sigv4-BXwVbza3.js.map} +1 -1
  43. package/dist/chunks/{styles-CCs214_3.js → styles-CAVmBLgp.js} +4 -4
  44. package/dist/chunks/{styles-CCs214_3.js.map → styles-CAVmBLgp.js.map} +1 -1
  45. package/dist/chunks/{styles-DPQQIig4.js → styles-CPczw1Jz.js} +4 -4
  46. package/dist/chunks/{styles-DPQQIig4.js.map → styles-CPczw1Jz.js.map} +1 -1
  47. package/dist/chunks/{styles-Cm62yyQX.js → styles-boK6Ln54.js} +65 -30
  48. package/dist/chunks/styles-boK6Ln54.js.map +1 -0
  49. package/dist/chunks/{styles-yed-UMFdNqze.js → styles-yed-XeFBQ6VS.js} +8 -8
  50. package/dist/chunks/{styles-yed-UMFdNqze.js.map → styles-yed-XeFBQ6VS.js.map} +1 -1
  51. package/dist/cx.js +2 -2
  52. package/dist/cx2.js +2 -2
  53. package/dist/cys.js +1 -1
  54. package/dist/dot.js +7 -4
  55. package/dist/gexf.js +2 -2
  56. package/dist/gml.js +8 -8
  57. package/dist/gml.js.map +1 -1
  58. package/dist/graph-io.js +129 -104
  59. package/dist/graph-io.js.map +1 -1
  60. package/dist/graphml.js +2 -2
  61. package/dist/json.js +1 -1
  62. package/dist/pajek.js +1 -1
  63. package/dist/sources-cypher.js +25 -22
  64. package/dist/sources-cypher.js.map +1 -1
  65. package/dist/sources-ndex.js +2 -2
  66. package/dist/sources-rest.js +20 -4
  67. package/dist/sources-rest.js.map +1 -1
  68. package/dist/sources-sparql.js +40 -6
  69. package/dist/sources-sparql.js.map +1 -1
  70. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  71. package/dist/src/formats/dot/exporter.js +34 -2
  72. package/dist/src/formats/dot/exporter.js.map +1 -1
  73. package/dist/src/formats/dot/importer.d.ts +6 -1
  74. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  75. package/dist/src/formats/dot/importer.js +65 -5
  76. package/dist/src/formats/dot/importer.js.map +1 -1
  77. package/dist/src/formats/dot/index.d.ts +3 -1
  78. package/dist/src/formats/dot/index.d.ts.map +1 -1
  79. package/dist/src/formats/dot/index.js +2 -1
  80. package/dist/src/formats/dot/index.js.map +1 -1
  81. package/dist/src/formats/dot/label-places.d.ts +7 -0
  82. package/dist/src/formats/dot/label-places.d.ts.map +1 -1
  83. package/dist/src/formats/dot/label-places.js +9 -3
  84. package/dist/src/formats/dot/label-places.js.map +1 -1
  85. package/dist/src/formats/dot/names.d.ts +13 -0
  86. package/dist/src/formats/dot/names.d.ts.map +1 -1
  87. package/dist/src/formats/dot/names.js +13 -0
  88. package/dist/src/formats/dot/names.js.map +1 -1
  89. package/dist/src/formats/dot/styles.d.ts +10 -0
  90. package/dist/src/formats/dot/styles.d.ts.map +1 -1
  91. package/dist/src/formats/dot/styles.js +49 -3
  92. package/dist/src/formats/dot/styles.js.map +1 -1
  93. package/dist/src/formats/gml/styles.js +1 -1
  94. package/dist/src/formats/gml/styles.js.map +1 -1
  95. package/dist/src/formats/graphml/styles-yed.js +3 -3
  96. package/dist/src/formats/graphml/styles-yed.js.map +1 -1
  97. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -1
  98. package/dist/src/formats/graphml/styles-yfiles3.js +6 -4
  99. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -1
  100. package/dist/src/formats/json/styles.d.ts.map +1 -1
  101. package/dist/src/formats/json/styles.js +94 -6
  102. package/dist/src/formats/json/styles.js.map +1 -1
  103. package/dist/src/formats/pajek/draw.d.ts +8 -0
  104. package/dist/src/formats/pajek/draw.d.ts.map +1 -1
  105. package/dist/src/formats/pajek/draw.js +10 -0
  106. package/dist/src/formats/pajek/draw.js.map +1 -1
  107. package/dist/src/formats/pajek/styles.d.ts.map +1 -1
  108. package/dist/src/formats/pajek/styles.js +142 -25
  109. package/dist/src/formats/pajek/styles.js.map +1 -1
  110. package/dist/src/index.d.ts +3 -3
  111. package/dist/src/index.d.ts.map +1 -1
  112. package/dist/src/index.js +2 -2
  113. package/dist/src/index.js.map +1 -1
  114. package/dist/src/sources/credentials.d.ts +50 -1
  115. package/dist/src/sources/credentials.d.ts.map +1 -1
  116. package/dist/src/sources/credentials.js +69 -0
  117. package/dist/src/sources/credentials.js.map +1 -1
  118. package/dist/src/sources/cypher/index.d.ts.map +1 -1
  119. package/dist/src/sources/cypher/index.js +12 -11
  120. package/dist/src/sources/cypher/index.js.map +1 -1
  121. package/dist/src/sources/cypher/query-builder.d.ts +6 -6
  122. package/dist/src/sources/cypher/query-builder.d.ts.map +1 -1
  123. package/dist/src/sources/cypher/query-builder.js +15 -15
  124. package/dist/src/sources/cypher/query-builder.js.map +1 -1
  125. package/dist/src/sources/rest/descriptor.d.ts.map +1 -1
  126. package/dist/src/sources/rest/descriptor.js +4 -0
  127. package/dist/src/sources/rest/descriptor.js.map +1 -1
  128. package/dist/src/sources/rest/openalex.d.ts +4 -2
  129. package/dist/src/sources/rest/openalex.d.ts.map +1 -1
  130. package/dist/src/sources/rest/openalex.js +4 -2
  131. package/dist/src/sources/rest/openalex.js.map +1 -1
  132. package/dist/src/sources/rest/source.d.ts.map +1 -1
  133. package/dist/src/sources/rest/source.js +23 -0
  134. package/dist/src/sources/rest/source.js.map +1 -1
  135. package/dist/src/sources/run.d.ts +2 -0
  136. package/dist/src/sources/run.d.ts.map +1 -1
  137. package/dist/src/sources/run.js +30 -3
  138. package/dist/src/sources/run.js.map +1 -1
  139. package/dist/src/sources/sparql/index.d.ts.map +1 -1
  140. package/dist/src/sources/sparql/index.js +14 -1
  141. package/dist/src/sources/sparql/index.js.map +1 -1
  142. package/dist/src/sources/sparql/query.d.ts +2 -1
  143. package/dist/src/sources/sparql/query.d.ts.map +1 -1
  144. package/dist/src/sources/sparql/query.js +55 -4
  145. package/dist/src/sources/sparql/query.js.map +1 -1
  146. package/dist/src/sources/types.d.ts +29 -0
  147. package/dist/src/sources/types.d.ts.map +1 -1
  148. package/dist/src/styles/canon/enums.d.ts.map +1 -1
  149. package/dist/src/styles/canon/enums.js +8 -0
  150. package/dist/src/styles/canon/enums.js.map +1 -1
  151. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -1
  152. package/dist/src/styles/cyjs/cascade.js +124 -0
  153. package/dist/src/styles/cyjs/cascade.js.map +1 -1
  154. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -1
  155. package/dist/src/styles/cyjs/defaults-table.js +1 -0
  156. package/dist/src/styles/cyjs/defaults-table.js.map +1 -1
  157. package/dist/src/styles/cyjs/emit.d.ts.map +1 -1
  158. package/dist/src/styles/cyjs/emit.js +30 -1
  159. package/dist/src/styles/cyjs/emit.js.map +1 -1
  160. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -1
  161. package/dist/src/styles/cyjs/mappers.js +14 -0
  162. package/dist/src/styles/cyjs/mappers.js.map +1 -1
  163. package/dist/src/styles/types.d.ts +65 -0
  164. package/dist/src/styles/types.d.ts.map +1 -1
  165. package/dist/src/styles/vocabulary.d.ts.map +1 -1
  166. package/dist/src/styles/vocabulary.js +21 -0
  167. package/dist/src/styles/vocabulary.js.map +1 -1
  168. package/dist/styles.js +6 -6
  169. package/dist/xgmml.js +3 -3
  170. package/package.json +2 -2
  171. package/src/formats/dot/exporter.ts +37 -1
  172. package/src/formats/dot/importer.ts +75 -4
  173. package/src/formats/dot/index.ts +3 -1
  174. package/src/formats/dot/label-places.ts +16 -3
  175. package/src/formats/dot/names.ts +15 -0
  176. package/src/formats/dot/styles.ts +55 -3
  177. package/src/formats/gml/styles.ts +1 -1
  178. package/src/formats/graphml/styles-yed.ts +3 -3
  179. package/src/formats/graphml/styles-yfiles3.ts +6 -4
  180. package/src/formats/json/styles.ts +102 -6
  181. package/src/formats/pajek/draw.ts +11 -0
  182. package/src/formats/pajek/styles.ts +151 -26
  183. package/src/index.ts +7 -0
  184. package/src/sources/credentials.ts +90 -1
  185. package/src/sources/cypher/index.ts +12 -11
  186. package/src/sources/cypher/query-builder.ts +15 -15
  187. package/src/sources/rest/descriptor.ts +4 -0
  188. package/src/sources/rest/openalex.ts +4 -2
  189. package/src/sources/rest/source.ts +25 -0
  190. package/src/sources/run.ts +35 -3
  191. package/src/sources/sparql/index.ts +16 -1
  192. package/src/sources/sparql/query.ts +58 -4
  193. package/src/sources/types.ts +33 -0
  194. package/src/styles/canon/enums.ts +9 -0
  195. package/src/styles/cyjs/cascade.ts +143 -0
  196. package/src/styles/cyjs/defaults-table.ts +1 -0
  197. package/src/styles/cyjs/emit.ts +35 -1
  198. package/src/styles/cyjs/mappers.ts +14 -0
  199. package/src/styles/types.ts +65 -0
  200. package/src/styles/vocabulary.ts +21 -0
  201. package/dist/chunks/block-iptrlMBb.js.map +0 -1
  202. package/dist/chunks/credentials-DXHx7nwR.js.map +0 -1
  203. package/dist/chunks/emit-BwHQkZle.js.map +0 -1
  204. package/dist/chunks/exporter-D2CETOMK.js.map +0 -1
  205. package/dist/chunks/importer-CT9mJ1nn.js.map +0 -1
  206. package/dist/chunks/importer-DaLTjbds.js.map +0 -1
  207. package/dist/chunks/importer-vRq4h3j5.js.map +0 -1
  208. 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", "standard"],
1319
+ ["triangle", "delta"],
1320
1320
  ["triangle-open", "white_delta"],
1321
- ["vee", "concave"],
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, "standard", property, approximate) : "none";
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", "triangle"],
658
- ["STEALTH", "triangle"],
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 edge = visEdges(snapshot, visLayer(options.edges, "edge"), borderOf, geometry, notes, context);
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
- // ponytail: a dynamic curve where physics runs is wherever the simulation left its support node; drawn straight
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
- return { curved: false, bends: points.join(" ") };
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
- snapshot.edgeSource(i) === snapshot.edgeTarget(i) ? (edgeValues[property]?.[i] ?? null) : want(property);
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: { ...VIS_WRITTEN.edge, bends: loop("bends"), bendCurve: loop("bendCurve"), curved: loop("curved") },
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`, which the writer still restates
155
- * (`pajek-synth-lines`: edge `3 10` after `1 8 ... p Dots` and arc `5 6 -1` after a `p Solid` are drawn as if no line
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 factor = f === null ? 1 : canonNumber(f, at(property));
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). graphty draws the route to the target's
820
- * center and stops it at the outline, so the curve's control points are written in the frame of that line.
821
- * ponytail: the outline is the ellipse of the vertex's width and height, as the file sizes it (before the Draw
822
- * window's Size of Vertices or vectors), whatever its shape.
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]): void {
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 = 1.5 + 5 * (numberOrNull(get("s")) ?? 1);
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, which the vocabulary does not hold, so it is noted and not read.
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
- noteAt(at("arrowPosition"), STYLE_ISSUE.NOT_CARRIED, `ap ${String(ap)}`);
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
- readLine(get, put, at, directed[i], labels, run, loop);
1139
- if (directed[i] && loop === null && run !== null) {
1140
- readArcEnd(get, put, run, targetSize(snapshot.edgeTarget(i)));
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
- const position = get("arrowPosition");
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,