@graphty/graph-io 0.3.47 → 0.3.49

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 (770) hide show
  1. package/dist/chunks/{approximations-wWjCRzKj.js → approximations-Bzn3L-je.js} +2 -2
  2. package/dist/chunks/{approximations-wWjCRzKj.js.map → approximations-Bzn3L-je.js.map} +1 -1
  3. package/dist/chunks/bends-BGXjv_U4.js +129 -0
  4. package/dist/chunks/bends-BGXjv_U4.js.map +1 -0
  5. package/dist/chunks/{block-DiGq58c3.js → block-iptrlMBb.js} +947 -134
  6. package/dist/chunks/block-iptrlMBb.js.map +1 -0
  7. package/dist/chunks/choose-CczZJaX_.js +717 -0
  8. package/dist/chunks/choose-CczZJaX_.js.map +1 -0
  9. package/dist/chunks/codes-BE2xir2k.js +157 -0
  10. package/dist/chunks/codes-BE2xir2k.js.map +1 -0
  11. package/dist/chunks/{constants-BKwqelYD.js → constants-CKkYLPtt.js} +2 -2
  12. package/dist/chunks/{constants-BKwqelYD.js.map → constants-CKkYLPtt.js.map} +1 -1
  13. package/dist/chunks/context-C3p0RQxR.js +664 -0
  14. package/dist/chunks/context-C3p0RQxR.js.map +1 -0
  15. package/dist/chunks/{convert-BcQ8_jMo.js → convert-Ct2zelbQ.js} +7 -2
  16. package/dist/chunks/convert-Ct2zelbQ.js.map +1 -0
  17. package/dist/chunks/credentials-DXHx7nwR.js +349 -0
  18. package/dist/chunks/credentials-DXHx7nwR.js.map +1 -0
  19. package/dist/chunks/cx-export-Bt6NoqTQ.js +259 -0
  20. package/dist/chunks/cx-export-Bt6NoqTQ.js.map +1 -0
  21. package/dist/chunks/descriptor-B28U25x4.js +60 -0
  22. package/dist/chunks/descriptor-B28U25x4.js.map +1 -0
  23. package/dist/chunks/descriptor-C-YDhtsn.js +48 -0
  24. package/dist/chunks/descriptor-C-YDhtsn.js.map +1 -0
  25. package/dist/chunks/descriptor-DLA5kINx.js +142 -0
  26. package/dist/chunks/descriptor-DLA5kINx.js.map +1 -0
  27. package/dist/chunks/{emit-CwU8cUmG.js → emit-BwHQkZle.js} +1690 -103
  28. package/dist/chunks/emit-BwHQkZle.js.map +1 -0
  29. package/dist/chunks/{escape-CgzxiYPg.js → escape-CuCIzNMB.js} +12 -12
  30. package/dist/chunks/{escape-CgzxiYPg.js.map → escape-CuCIzNMB.js.map} +1 -1
  31. package/dist/chunks/{export-BvqAXCKh.js → export-Cha4dRFz.js} +48 -1780
  32. package/dist/chunks/export-Cha4dRFz.js.map +1 -0
  33. package/dist/chunks/{exporter--Mk9gXCJ.js → exporter-BP6otMO6.js} +6 -4
  34. package/dist/chunks/{exporter--Mk9gXCJ.js.map → exporter-BP6otMO6.js.map} +1 -1
  35. package/dist/chunks/{exporter-CrDWIyiq.js → exporter-D2CETOMK.js} +1222 -154
  36. package/dist/chunks/exporter-D2CETOMK.js.map +1 -0
  37. package/dist/chunks/{exporter-DndFlGSa.js → exporter-D9yMtocA.js} +7 -6
  38. package/dist/chunks/{exporter-DndFlGSa.js.map → exporter-D9yMtocA.js.map} +1 -1
  39. package/dist/chunks/exporter-_jrBTnos.js +1009 -0
  40. package/dist/chunks/exporter-_jrBTnos.js.map +1 -0
  41. package/dist/chunks/{exporter-CJgfhF-l.js → exporter-nDfxIK5D.js} +9 -7
  42. package/dist/chunks/{exporter-CJgfhF-l.js.map → exporter-nDfxIK5D.js.map} +1 -1
  43. package/dist/chunks/format-BXiAb-1h.js +408 -0
  44. package/dist/chunks/format-BXiAb-1h.js.map +1 -0
  45. package/dist/chunks/{generic-Dq1ntt9q.js → generic-ymWCETAJ.js} +387 -31
  46. package/dist/chunks/generic-ymWCETAJ.js.map +1 -0
  47. package/dist/chunks/{importer-CMmeMWlC.js → importer-B92x1xY5.js} +15 -263
  48. package/dist/chunks/importer-B92x1xY5.js.map +1 -0
  49. package/dist/chunks/{importer-DiZRmOQG.js → importer-CT9mJ1nn.js} +328 -90
  50. package/dist/chunks/importer-CT9mJ1nn.js.map +1 -0
  51. package/dist/chunks/{importer-COTK9Tsl.js → importer-DaLTjbds.js} +116 -1099
  52. package/dist/chunks/importer-DaLTjbds.js.map +1 -0
  53. package/dist/chunks/{importer-iFYPEKaJ.js → importer-GREKMdIf.js} +28 -16
  54. package/dist/chunks/importer-GREKMdIf.js.map +1 -0
  55. package/dist/chunks/{importer-BSvfrX_h.js → importer-PqaaKKnY.js} +23 -1016
  56. package/dist/chunks/importer-PqaaKKnY.js.map +1 -0
  57. package/dist/chunks/{importer-yh_aBCU6.js → importer-Vn5QvmmJ.js} +66 -12
  58. package/dist/chunks/importer-Vn5QvmmJ.js.map +1 -0
  59. package/dist/chunks/{importer-e6LGpqHB.js → importer-vRq4h3j5.js} +1470 -354
  60. package/dist/chunks/importer-vRq4h3j5.js.map +1 -0
  61. package/dist/chunks/input-CAxt_aV-.js +1397 -0
  62. package/dist/chunks/input-CAxt_aV-.js.map +1 -0
  63. package/dist/chunks/{json-elements-DJZRZ6jd.js → json-elements-Cq76WjnZ.js} +4 -4
  64. package/dist/chunks/{json-elements-DJZRZ6jd.js.map → json-elements-Cq76WjnZ.js.map} +1 -1
  65. package/dist/chunks/{lexicon-CT69Kd87.js → lexicon-Clf4QIyl.js} +299 -77
  66. package/dist/chunks/lexicon-Clf4QIyl.js.map +1 -0
  67. package/dist/chunks/{ontology-export-G4MqRdux.js → ontology-export-OMo91mb0.js} +4 -3
  68. package/dist/chunks/{ontology-export-G4MqRdux.js.map → ontology-export-OMo91mb0.js.map} +1 -1
  69. package/dist/chunks/{records-CiaWZrFH.js → records-NJiYvekn.js} +2 -2
  70. package/dist/chunks/{records-CiaWZrFH.js.map → records-NJiYvekn.js.map} +1 -1
  71. package/dist/chunks/{format-BpQBx8MZ.js → report-CwMfYa9J.js} +70 -127
  72. package/dist/chunks/report-CwMfYa9J.js.map +1 -0
  73. package/dist/chunks/round-trip-Zm3_ivgq.js +79 -0
  74. package/dist/chunks/round-trip-Zm3_ivgq.js.map +1 -0
  75. package/dist/chunks/sigv4-EokFic1V.js +247 -0
  76. package/dist/chunks/sigv4-EokFic1V.js.map +1 -0
  77. package/dist/chunks/{styles-BQnsga7p.js → styles-CCs214_3.js} +1300 -168
  78. package/dist/chunks/styles-CCs214_3.js.map +1 -0
  79. package/dist/chunks/styles-Cm62yyQX.js +2812 -0
  80. package/dist/chunks/styles-Cm62yyQX.js.map +1 -0
  81. package/dist/chunks/styles-DPQQIig4.js +1928 -0
  82. package/dist/chunks/styles-DPQQIig4.js.map +1 -0
  83. package/dist/chunks/styles-yed-UMFdNqze.js +1213 -0
  84. package/dist/chunks/styles-yed-UMFdNqze.js.map +1 -0
  85. package/dist/chunks/{text-CzJNFOS9.js → text-ystE2bYi.js} +2 -2
  86. package/dist/chunks/{text-CzJNFOS9.js.map → text-ystE2bYi.js.map} +1 -1
  87. package/dist/csv.js +7 -5
  88. package/dist/csv.js.map +1 -1
  89. package/dist/cx.js +5 -4
  90. package/dist/cx.js.map +1 -1
  91. package/dist/cx2.js +2 -2
  92. package/dist/cys.js +1 -1
  93. package/dist/dot.js +5 -4
  94. package/dist/dot.js.map +1 -1
  95. package/dist/gexf.js +42 -10
  96. package/dist/gexf.js.map +1 -1
  97. package/dist/gml.js +850 -193
  98. package/dist/gml.js.map +1 -1
  99. package/dist/graph-io.js +1189 -220
  100. package/dist/graph-io.js.map +1 -1
  101. package/dist/graphml.js +3 -3
  102. package/dist/json.js +3 -2
  103. package/dist/neo4j.js +6 -4
  104. package/dist/neo4j.js.map +1 -1
  105. package/dist/obo.js +1 -1
  106. package/dist/pajek.js +1 -1
  107. package/dist/positions.js +45 -3
  108. package/dist/positions.js.map +1 -1
  109. package/dist/sources-cypher.d.ts +1 -0
  110. package/dist/sources-cypher.js +1351 -0
  111. package/dist/sources-cypher.js.map +1 -0
  112. package/dist/sources-ndex.d.ts +1 -0
  113. package/dist/sources-ndex.js +278 -0
  114. package/dist/sources-ndex.js.map +1 -0
  115. package/dist/sources-rest.d.ts +1 -0
  116. package/dist/sources-rest.js +1377 -0
  117. package/dist/sources-rest.js.map +1 -0
  118. package/dist/sources-sparql.d.ts +1 -0
  119. package/dist/sources-sparql.js +405 -0
  120. package/dist/sources-sparql.js.map +1 -0
  121. package/dist/src/common/report.d.ts +6 -3
  122. package/dist/src/common/report.d.ts.map +1 -1
  123. package/dist/src/common/report.js +15 -9
  124. package/dist/src/common/report.js.map +1 -1
  125. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  126. package/dist/src/formats/cx/importer.js +5 -1
  127. package/dist/src/formats/cx/importer.js.map +1 -1
  128. package/dist/src/formats/cx/styles.d.ts +24 -1
  129. package/dist/src/formats/cx/styles.d.ts.map +1 -1
  130. package/dist/src/formats/cx/styles.js +118 -11
  131. package/dist/src/formats/cx/styles.js.map +1 -1
  132. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  133. package/dist/src/formats/cx2/importer.js +1 -0
  134. package/dist/src/formats/cx2/importer.js.map +1 -1
  135. package/dist/src/formats/cx2/styles.d.ts.map +1 -1
  136. package/dist/src/formats/cx2/styles.js +3 -1
  137. package/dist/src/formats/cx2/styles.js.map +1 -1
  138. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  139. package/dist/src/formats/cys/importer.js +1 -0
  140. package/dist/src/formats/cys/importer.js.map +1 -1
  141. package/dist/src/formats/cys/styles.d.ts +2 -0
  142. package/dist/src/formats/cys/styles.d.ts.map +1 -1
  143. package/dist/src/formats/cys/styles.js +15 -7
  144. package/dist/src/formats/cys/styles.js.map +1 -1
  145. package/dist/src/formats/dot/drawing.d.ts +98 -0
  146. package/dist/src/formats/dot/drawing.d.ts.map +1 -0
  147. package/dist/src/formats/dot/drawing.js +734 -0
  148. package/dist/src/formats/dot/drawing.js.map +1 -0
  149. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  150. package/dist/src/formats/dot/importer.js +108 -31
  151. package/dist/src/formats/dot/importer.js.map +1 -1
  152. package/dist/src/formats/dot/label-places.d.ts +34 -0
  153. package/dist/src/formats/dot/label-places.d.ts.map +1 -0
  154. package/dist/src/formats/dot/label-places.js +179 -0
  155. package/dist/src/formats/dot/label-places.js.map +1 -0
  156. package/dist/src/formats/dot/record.d.ts +50 -0
  157. package/dist/src/formats/dot/record.d.ts.map +1 -0
  158. package/dist/src/formats/dot/record.js +275 -0
  159. package/dist/src/formats/dot/record.js.map +1 -0
  160. package/dist/src/formats/dot/styles.d.ts +62 -2
  161. package/dist/src/formats/dot/styles.d.ts.map +1 -1
  162. package/dist/src/formats/dot/styles.js +1457 -120
  163. package/dist/src/formats/dot/styles.js.map +1 -1
  164. package/dist/src/formats/gexf/dialog-metrics.d.ts +15 -0
  165. package/dist/src/formats/gexf/dialog-metrics.d.ts.map +1 -0
  166. package/dist/src/formats/gexf/dialog-metrics.js +59 -0
  167. package/dist/src/formats/gexf/dialog-metrics.js.map +1 -0
  168. package/dist/src/formats/gexf/importer.d.ts +7 -0
  169. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  170. package/dist/src/formats/gexf/importer.js +41 -5
  171. package/dist/src/formats/gexf/importer.js.map +1 -1
  172. package/dist/src/formats/gexf/preview.d.ts +77 -0
  173. package/dist/src/formats/gexf/preview.d.ts.map +1 -0
  174. package/dist/src/formats/gexf/preview.js +191 -0
  175. package/dist/src/formats/gexf/preview.js.map +1 -0
  176. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  177. package/dist/src/formats/gexf/schema.js +2 -1
  178. package/dist/src/formats/gexf/schema.js.map +1 -1
  179. package/dist/src/formats/gexf/styles.d.ts +60 -16
  180. package/dist/src/formats/gexf/styles.d.ts.map +1 -1
  181. package/dist/src/formats/gexf/styles.js +883 -131
  182. package/dist/src/formats/gexf/styles.js.map +1 -1
  183. package/dist/src/formats/gml/importer.d.ts +5 -2
  184. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  185. package/dist/src/formats/gml/importer.js +258 -71
  186. package/dist/src/formats/gml/importer.js.map +1 -1
  187. package/dist/src/formats/gml/index.d.ts +1 -1
  188. package/dist/src/formats/gml/index.d.ts.map +1 -1
  189. package/dist/src/formats/gml/index.js +1 -1
  190. package/dist/src/formats/gml/index.js.map +1 -1
  191. package/dist/src/formats/gml/positions.d.ts +7 -0
  192. package/dist/src/formats/gml/positions.d.ts.map +1 -1
  193. package/dist/src/formats/gml/positions.js +29 -7
  194. package/dist/src/formats/gml/positions.js.map +1 -1
  195. package/dist/src/formats/gml/styles.d.ts.map +1 -1
  196. package/dist/src/formats/gml/styles.js +756 -103
  197. package/dist/src/formats/gml/styles.js.map +1 -1
  198. package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -1
  199. package/dist/src/formats/graphml/styles-plain.js +8 -12
  200. package/dist/src/formats/graphml/styles-plain.js.map +1 -1
  201. package/dist/src/formats/graphml/styles-yed.d.ts +21 -15
  202. package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -1
  203. package/dist/src/formats/graphml/styles-yed.js +749 -148
  204. package/dist/src/formats/graphml/styles-yed.js.map +1 -1
  205. package/dist/src/formats/graphml/styles-yfiles3.d.ts +1 -1
  206. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -1
  207. package/dist/src/formats/graphml/styles-yfiles3.js +429 -64
  208. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -1
  209. package/dist/src/formats/graphml/yfiles.d.ts +3 -3
  210. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  211. package/dist/src/formats/graphml/yfiles.js +16 -6
  212. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  213. package/dist/src/formats/json/dialect.d.ts +3 -2
  214. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  215. package/dist/src/formats/json/dialect.js +39 -5
  216. package/dist/src/formats/json/dialect.js.map +1 -1
  217. package/dist/src/formats/json/importer.d.ts +13 -2
  218. package/dist/src/formats/json/importer.d.ts.map +1 -1
  219. package/dist/src/formats/json/importer.js +42 -6
  220. package/dist/src/formats/json/importer.js.map +1 -1
  221. package/dist/src/formats/json/index.d.ts +1 -1
  222. package/dist/src/formats/json/index.d.ts.map +1 -1
  223. package/dist/src/formats/json/index.js +1 -1
  224. package/dist/src/formats/json/index.js.map +1 -1
  225. package/dist/src/formats/json/positions.d.ts +5 -2
  226. package/dist/src/formats/json/positions.d.ts.map +1 -1
  227. package/dist/src/formats/json/positions.js +20 -4
  228. package/dist/src/formats/json/positions.js.map +1 -1
  229. package/dist/src/formats/json/styles.d.ts +12 -1
  230. package/dist/src/formats/json/styles.d.ts.map +1 -1
  231. package/dist/src/formats/json/styles.js +1755 -136
  232. package/dist/src/formats/json/styles.js.map +1 -1
  233. package/dist/src/formats/pajek/draw.d.ts +96 -0
  234. package/dist/src/formats/pajek/draw.d.ts.map +1 -0
  235. package/dist/src/formats/pajek/draw.js +372 -0
  236. package/dist/src/formats/pajek/draw.js.map +1 -0
  237. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  238. package/dist/src/formats/pajek/exporter.js +70 -51
  239. package/dist/src/formats/pajek/exporter.js.map +1 -1
  240. package/dist/src/formats/pajek/importer.d.ts +9 -0
  241. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  242. package/dist/src/formats/pajek/importer.js +233 -102
  243. package/dist/src/formats/pajek/importer.js.map +1 -1
  244. package/dist/src/formats/pajek/index.d.ts +1 -0
  245. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  246. package/dist/src/formats/pajek/index.js.map +1 -1
  247. package/dist/src/formats/pajek/styles.d.ts +15 -7
  248. package/dist/src/formats/pajek/styles.d.ts.map +1 -1
  249. package/dist/src/formats/pajek/styles.js +1279 -114
  250. package/dist/src/formats/pajek/styles.js.map +1 -1
  251. package/dist/src/formats/pajek/syntax.d.ts +10 -1
  252. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  253. package/dist/src/formats/pajek/syntax.js +26 -2
  254. package/dist/src/formats/pajek/syntax.js.map +1 -1
  255. package/dist/src/formats/xgmml/emit.d.ts +1 -0
  256. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  257. package/dist/src/formats/xgmml/emit.js +65 -5
  258. package/dist/src/formats/xgmml/emit.js.map +1 -1
  259. package/dist/src/formats/xgmml/styles.d.ts +23 -3
  260. package/dist/src/formats/xgmml/styles.d.ts.map +1 -1
  261. package/dist/src/formats/xgmml/styles.js +278 -60
  262. package/dist/src/formats/xgmml/styles.js.map +1 -1
  263. package/dist/src/index.d.ts +10 -2
  264. package/dist/src/index.d.ts.map +1 -1
  265. package/dist/src/index.js +11 -2
  266. package/dist/src/index.js.map +1 -1
  267. package/dist/src/positions/convert.d.ts.map +1 -1
  268. package/dist/src/positions/convert.js +11 -1
  269. package/dist/src/positions/convert.js.map +1 -1
  270. package/dist/src/positions/extract.d.ts.map +1 -1
  271. package/dist/src/positions/extract.js +53 -1
  272. package/dist/src/positions/extract.js.map +1 -1
  273. package/dist/src/positions/index.d.ts +1 -1
  274. package/dist/src/positions/index.d.ts.map +1 -1
  275. package/dist/src/positions/types.d.ts +59 -0
  276. package/dist/src/positions/types.d.ts.map +1 -1
  277. package/dist/src/registry.d.ts +173 -1
  278. package/dist/src/registry.d.ts.map +1 -1
  279. package/dist/src/registry.js +196 -1
  280. package/dist/src/registry.js.map +1 -1
  281. package/dist/src/sources/builtins.d.ts +11 -0
  282. package/dist/src/sources/builtins.d.ts.map +1 -0
  283. package/dist/src/sources/builtins.js +53 -0
  284. package/dist/src/sources/builtins.js.map +1 -0
  285. package/dist/src/sources/codes.d.ts +230 -0
  286. package/dist/src/sources/codes.d.ts.map +1 -0
  287. package/dist/src/sources/codes.js +58 -0
  288. package/dist/src/sources/codes.js.map +1 -0
  289. package/dist/src/sources/context.d.ts +112 -0
  290. package/dist/src/sources/context.d.ts.map +1 -0
  291. package/dist/src/sources/context.js +740 -0
  292. package/dist/src/sources/context.js.map +1 -0
  293. package/dist/src/sources/credentials.d.ts +179 -0
  294. package/dist/src/sources/credentials.d.ts.map +1 -0
  295. package/dist/src/sources/credentials.js +301 -0
  296. package/dist/src/sources/credentials.js.map +1 -0
  297. package/dist/src/sources/cypher/descriptor.d.ts +21 -0
  298. package/dist/src/sources/cypher/descriptor.d.ts.map +1 -0
  299. package/dist/src/sources/cypher/descriptor.js +124 -0
  300. package/dist/src/sources/cypher/descriptor.js.map +1 -0
  301. package/dist/src/sources/cypher/flatten.d.ts +72 -0
  302. package/dist/src/sources/cypher/flatten.d.ts.map +1 -0
  303. package/dist/src/sources/cypher/flatten.js +279 -0
  304. package/dist/src/sources/cypher/flatten.js.map +1 -0
  305. package/dist/src/sources/cypher/index.d.ts +33 -0
  306. package/dist/src/sources/cypher/index.d.ts.map +1 -0
  307. package/dist/src/sources/cypher/index.js +511 -0
  308. package/dist/src/sources/cypher/index.js.map +1 -0
  309. package/dist/src/sources/cypher/params.d.ts +40 -0
  310. package/dist/src/sources/cypher/params.d.ts.map +1 -0
  311. package/dist/src/sources/cypher/params.js +6 -0
  312. package/dist/src/sources/cypher/params.js.map +1 -0
  313. package/dist/src/sources/cypher/profiles.d.ts +44 -0
  314. package/dist/src/sources/cypher/profiles.d.ts.map +1 -0
  315. package/dist/src/sources/cypher/profiles.js +61 -0
  316. package/dist/src/sources/cypher/profiles.js.map +1 -0
  317. package/dist/src/sources/cypher/query-builder.d.ts +123 -0
  318. package/dist/src/sources/cypher/query-builder.d.ts.map +1 -0
  319. package/dist/src/sources/cypher/query-builder.js +267 -0
  320. package/dist/src/sources/cypher/query-builder.js.map +1 -0
  321. package/dist/src/sources/cypher/transports/bolt.d.ts +21 -0
  322. package/dist/src/sources/cypher/transports/bolt.d.ts.map +1 -0
  323. package/dist/src/sources/cypher/transports/bolt.js +185 -0
  324. package/dist/src/sources/cypher/transports/bolt.js.map +1 -0
  325. package/dist/src/sources/cypher/transports/neptune-analytics.d.ts +15 -0
  326. package/dist/src/sources/cypher/transports/neptune-analytics.d.ts.map +1 -0
  327. package/dist/src/sources/cypher/transports/neptune-analytics.js +38 -0
  328. package/dist/src/sources/cypher/transports/neptune-analytics.js.map +1 -0
  329. package/dist/src/sources/cypher/transports/neptune-https.d.ts +31 -0
  330. package/dist/src/sources/cypher/transports/neptune-https.d.ts.map +1 -0
  331. package/dist/src/sources/cypher/transports/neptune-https.js +104 -0
  332. package/dist/src/sources/cypher/transports/neptune-https.js.map +1 -0
  333. package/dist/src/sources/cypher/transports/query-api.d.ts +14 -0
  334. package/dist/src/sources/cypher/transports/query-api.d.ts.map +1 -0
  335. package/dist/src/sources/cypher/transports/query-api.js +80 -0
  336. package/dist/src/sources/cypher/transports/query-api.js.map +1 -0
  337. package/dist/src/sources/cypher/transports/transport.d.ts +78 -0
  338. package/dist/src/sources/cypher/transports/transport.d.ts.map +1 -0
  339. package/dist/src/sources/cypher/transports/transport.js +77 -0
  340. package/dist/src/sources/cypher/transports/transport.js.map +1 -0
  341. package/dist/src/sources/cypher/types-map.d.ts +91 -0
  342. package/dist/src/sources/cypher/types-map.d.ts.map +1 -0
  343. package/dist/src/sources/cypher/types-map.js +306 -0
  344. package/dist/src/sources/cypher/types-map.js.map +1 -0
  345. package/dist/src/sources/define.d.ts +47 -0
  346. package/dist/src/sources/define.d.ts.map +1 -0
  347. package/dist/src/sources/define.js +173 -0
  348. package/dist/src/sources/define.js.map +1 -0
  349. package/dist/src/sources/fail.d.ts +33 -0
  350. package/dist/src/sources/fail.d.ts.map +1 -0
  351. package/dist/src/sources/fail.js +49 -0
  352. package/dist/src/sources/fail.js.map +1 -0
  353. package/dist/src/sources/ids.d.ts +69 -0
  354. package/dist/src/sources/ids.d.ts.map +1 -0
  355. package/dist/src/sources/ids.js +69 -0
  356. package/dist/src/sources/ids.js.map +1 -0
  357. package/dist/src/sources/ndex/client.d.ts +31 -0
  358. package/dist/src/sources/ndex/client.d.ts.map +1 -0
  359. package/dist/src/sources/ndex/client.js +90 -0
  360. package/dist/src/sources/ndex/client.js.map +1 -0
  361. package/dist/src/sources/ndex/descriptor.d.ts +10 -0
  362. package/dist/src/sources/ndex/descriptor.d.ts.map +1 -0
  363. package/dist/src/sources/ndex/descriptor.js +55 -0
  364. package/dist/src/sources/ndex/descriptor.js.map +1 -0
  365. package/dist/src/sources/ndex/expand.d.ts +20 -0
  366. package/dist/src/sources/ndex/expand.d.ts.map +1 -0
  367. package/dist/src/sources/ndex/expand.js +87 -0
  368. package/dist/src/sources/ndex/expand.js.map +1 -0
  369. package/dist/src/sources/ndex/index.d.ts +25 -0
  370. package/dist/src/sources/ndex/index.d.ts.map +1 -0
  371. package/dist/src/sources/ndex/index.js +191 -0
  372. package/dist/src/sources/ndex/index.js.map +1 -0
  373. package/dist/src/sources/ndex/params.d.ts +17 -0
  374. package/dist/src/sources/ndex/params.d.ts.map +1 -0
  375. package/dist/src/sources/ndex/params.js +6 -0
  376. package/dist/src/sources/ndex/params.js.map +1 -0
  377. package/dist/src/sources/ndex/share-url.d.ts +40 -0
  378. package/dist/src/sources/ndex/share-url.d.ts.map +1 -0
  379. package/dist/src/sources/ndex/share-url.js +64 -0
  380. package/dist/src/sources/ndex/share-url.js.map +1 -0
  381. package/dist/src/sources/params.d.ts +31 -0
  382. package/dist/src/sources/params.d.ts.map +1 -0
  383. package/dist/src/sources/params.js +6 -0
  384. package/dist/src/sources/params.js.map +1 -0
  385. package/dist/src/sources/registry.d.ts +118 -0
  386. package/dist/src/sources/registry.d.ts.map +1 -0
  387. package/dist/src/sources/registry.js +193 -0
  388. package/dist/src/sources/registry.js.map +1 -0
  389. package/dist/src/sources/rest/descriptor.d.ts +16 -0
  390. package/dist/src/sources/rest/descriptor.d.ts.map +1 -0
  391. package/dist/src/sources/rest/descriptor.js +76 -0
  392. package/dist/src/sources/rest/descriptor.js.map +1 -0
  393. package/dist/src/sources/rest/index.d.ts +14 -0
  394. package/dist/src/sources/rest/index.d.ts.map +1 -0
  395. package/dist/src/sources/rest/index.js +13 -0
  396. package/dist/src/sources/rest/index.js.map +1 -0
  397. package/dist/src/sources/rest/mapping.d.ts +30 -0
  398. package/dist/src/sources/rest/mapping.d.ts.map +1 -0
  399. package/dist/src/sources/rest/mapping.js +362 -0
  400. package/dist/src/sources/rest/mapping.js.map +1 -0
  401. package/dist/src/sources/rest/openalex.d.ts +27 -0
  402. package/dist/src/sources/rest/openalex.d.ts.map +1 -0
  403. package/dist/src/sources/rest/openalex.js +269 -0
  404. package/dist/src/sources/rest/openalex.js.map +1 -0
  405. package/dist/src/sources/rest/paging.d.ts +53 -0
  406. package/dist/src/sources/rest/paging.d.ts.map +1 -0
  407. package/dist/src/sources/rest/paging.js +138 -0
  408. package/dist/src/sources/rest/paging.js.map +1 -0
  409. package/dist/src/sources/rest/params.d.ts +216 -0
  410. package/dist/src/sources/rest/params.d.ts.map +1 -0
  411. package/dist/src/sources/rest/params.js +7 -0
  412. package/dist/src/sources/rest/params.js.map +1 -0
  413. package/dist/src/sources/rest/records.d.ts +41 -0
  414. package/dist/src/sources/rest/records.d.ts.map +1 -0
  415. package/dist/src/sources/rest/records.js +240 -0
  416. package/dist/src/sources/rest/records.js.map +1 -0
  417. package/dist/src/sources/rest/samples/deps-dev.d.ts +10 -0
  418. package/dist/src/sources/rest/samples/deps-dev.d.ts.map +1 -0
  419. package/dist/src/sources/rest/samples/deps-dev.js +47 -0
  420. package/dist/src/sources/rest/samples/deps-dev.js.map +1 -0
  421. package/dist/src/sources/rest/samples/string.d.ts +9 -0
  422. package/dist/src/sources/rest/samples/string.d.ts.map +1 -0
  423. package/dist/src/sources/rest/samples/string.js +42 -0
  424. package/dist/src/sources/rest/samples/string.js.map +1 -0
  425. package/dist/src/sources/rest/selectors.d.ts +41 -0
  426. package/dist/src/sources/rest/selectors.d.ts.map +1 -0
  427. package/dist/src/sources/rest/selectors.js +81 -0
  428. package/dist/src/sources/rest/selectors.js.map +1 -0
  429. package/dist/src/sources/rest/source.d.ts +67 -0
  430. package/dist/src/sources/rest/source.d.ts.map +1 -0
  431. package/dist/src/sources/rest/source.js +650 -0
  432. package/dist/src/sources/rest/source.js.map +1 -0
  433. package/dist/src/sources/rest/templates.d.ts +38 -0
  434. package/dist/src/sources/rest/templates.d.ts.map +1 -0
  435. package/dist/src/sources/rest/templates.js +125 -0
  436. package/dist/src/sources/rest/templates.js.map +1 -0
  437. package/dist/src/sources/run.d.ts +93 -0
  438. package/dist/src/sources/run.d.ts.map +1 -0
  439. package/dist/src/sources/run.js +673 -0
  440. package/dist/src/sources/run.js.map +1 -0
  441. package/dist/src/sources/sha256.d.ts +12 -0
  442. package/dist/src/sources/sha256.d.ts.map +1 -0
  443. package/dist/src/sources/sha256.js +95 -0
  444. package/dist/src/sources/sha256.js.map +1 -0
  445. package/dist/src/sources/sigv4.d.ts +26 -0
  446. package/dist/src/sources/sigv4.d.ts.map +1 -0
  447. package/dist/src/sources/sigv4.js +184 -0
  448. package/dist/src/sources/sigv4.js.map +1 -0
  449. package/dist/src/sources/sparql/descriptor.d.ts +10 -0
  450. package/dist/src/sources/sparql/descriptor.d.ts.map +1 -0
  451. package/dist/src/sources/sparql/descriptor.js +64 -0
  452. package/dist/src/sources/sparql/descriptor.js.map +1 -0
  453. package/dist/src/sources/sparql/index.d.ts +28 -0
  454. package/dist/src/sources/sparql/index.d.ts.map +1 -0
  455. package/dist/src/sources/sparql/index.js +314 -0
  456. package/dist/src/sources/sparql/index.js.map +1 -0
  457. package/dist/src/sources/sparql/ntriples.d.ts +69 -0
  458. package/dist/src/sources/sparql/ntriples.d.ts.map +1 -0
  459. package/dist/src/sources/sparql/ntriples.js +180 -0
  460. package/dist/src/sources/sparql/ntriples.js.map +1 -0
  461. package/dist/src/sources/sparql/params.d.ts +27 -0
  462. package/dist/src/sources/sparql/params.d.ts.map +1 -0
  463. package/dist/src/sources/sparql/params.js +5 -0
  464. package/dist/src/sources/sparql/params.js.map +1 -0
  465. package/dist/src/sources/sparql/query.d.ts +72 -0
  466. package/dist/src/sources/sparql/query.d.ts.map +1 -0
  467. package/dist/src/sources/sparql/query.js +113 -0
  468. package/dist/src/sources/sparql/query.js.map +1 -0
  469. package/dist/src/sources/testing.d.ts +96 -0
  470. package/dist/src/sources/testing.d.ts.map +1 -0
  471. package/dist/src/sources/testing.js +138 -0
  472. package/dist/src/sources/testing.js.map +1 -0
  473. package/dist/src/sources/types.d.ts +696 -0
  474. package/dist/src/sources/types.d.ts.map +1 -0
  475. package/dist/src/sources/types.js +10 -0
  476. package/dist/src/sources/types.js.map +1 -0
  477. package/dist/src/styles/annotations.d.ts +61 -5
  478. package/dist/src/styles/annotations.d.ts.map +1 -1
  479. package/dist/src/styles/annotations.js +130 -22
  480. package/dist/src/styles/annotations.js.map +1 -1
  481. package/dist/src/styles/canon/bends.d.ts +46 -0
  482. package/dist/src/styles/canon/bends.d.ts.map +1 -1
  483. package/dist/src/styles/canon/bends.js +123 -0
  484. package/dist/src/styles/canon/bends.js.map +1 -1
  485. package/dist/src/styles/canon/color-tables.d.ts +6 -1
  486. package/dist/src/styles/canon/color-tables.d.ts.map +1 -1
  487. package/dist/src/styles/canon/color-tables.js +10 -4
  488. package/dist/src/styles/canon/color-tables.js.map +1 -1
  489. package/dist/src/styles/canon/color.d.ts +3 -1
  490. package/dist/src/styles/canon/color.d.ts.map +1 -1
  491. package/dist/src/styles/canon/color.js +103 -3
  492. package/dist/src/styles/canon/color.js.map +1 -1
  493. package/dist/src/styles/canon/enums.d.ts +2 -2
  494. package/dist/src/styles/canon/enums.d.ts.map +1 -1
  495. package/dist/src/styles/canon/enums.js +82 -7
  496. package/dist/src/styles/canon/enums.js.map +1 -1
  497. package/dist/src/styles/canon/graphviz.d.ts +12 -3
  498. package/dist/src/styles/canon/graphviz.d.ts.map +1 -1
  499. package/dist/src/styles/canon/graphviz.js +57 -9
  500. package/dist/src/styles/canon/graphviz.js.map +1 -1
  501. package/dist/src/styles/canon/number.d.ts +8 -0
  502. package/dist/src/styles/canon/number.d.ts.map +1 -1
  503. package/dist/src/styles/canon/number.js +18 -0
  504. package/dist/src/styles/canon/number.js.map +1 -1
  505. package/dist/src/styles/canon/text.d.ts +29 -2
  506. package/dist/src/styles/canon/text.d.ts.map +1 -1
  507. package/dist/src/styles/canon/text.js +156 -16
  508. package/dist/src/styles/canon/text.js.map +1 -1
  509. package/dist/src/styles/codes.d.ts +2 -0
  510. package/dist/src/styles/codes.d.ts.map +1 -1
  511. package/dist/src/styles/codes.js +2 -0
  512. package/dist/src/styles/codes.js.map +1 -1
  513. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -1
  514. package/dist/src/styles/cyjs/cascade.js +1037 -35
  515. package/dist/src/styles/cyjs/cascade.js.map +1 -1
  516. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -1
  517. package/dist/src/styles/cyjs/defaults-table.js +5 -0
  518. package/dist/src/styles/cyjs/defaults-table.js.map +1 -1
  519. package/dist/src/styles/cyjs/emit.d.ts.map +1 -1
  520. package/dist/src/styles/cyjs/emit.js +216 -12
  521. package/dist/src/styles/cyjs/emit.js.map +1 -1
  522. package/dist/src/styles/cyjs/face.d.ts +20 -0
  523. package/dist/src/styles/cyjs/face.d.ts.map +1 -0
  524. package/dist/src/styles/cyjs/face.js +332 -0
  525. package/dist/src/styles/cyjs/face.js.map +1 -0
  526. package/dist/src/styles/cyjs/mappers.d.ts +48 -2
  527. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -1
  528. package/dist/src/styles/cyjs/mappers.js +463 -26
  529. package/dist/src/styles/cyjs/mappers.js.map +1 -1
  530. package/dist/src/styles/cyjs/routes.d.ts +53 -0
  531. package/dist/src/styles/cyjs/routes.d.ts.map +1 -0
  532. package/dist/src/styles/cyjs/routes.js +360 -0
  533. package/dist/src/styles/cyjs/routes.js.map +1 -0
  534. package/dist/src/styles/cytoscape/charts.d.ts +23 -0
  535. package/dist/src/styles/cytoscape/charts.d.ts.map +1 -0
  536. package/dist/src/styles/cytoscape/charts.js +560 -0
  537. package/dist/src/styles/cytoscape/charts.js.map +1 -0
  538. package/dist/src/styles/cytoscape/emit.d.ts.map +1 -1
  539. package/dist/src/styles/cytoscape/emit.js +204 -44
  540. package/dist/src/styles/cytoscape/emit.js.map +1 -1
  541. package/dist/src/styles/cytoscape/lexicon.d.ts +97 -3
  542. package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -1
  543. package/dist/src/styles/cytoscape/lexicon.js +413 -79
  544. package/dist/src/styles/cytoscape/lexicon.js.map +1 -1
  545. package/dist/src/styles/cytoscape/mappings.d.ts +30 -0
  546. package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -1
  547. package/dist/src/styles/cytoscape/mappings.js +92 -0
  548. package/dist/src/styles/cytoscape/mappings.js.map +1 -1
  549. package/dist/src/styles/cytoscape/resolve.d.ts +46 -3
  550. package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -1
  551. package/dist/src/styles/cytoscape/resolve.js +632 -22
  552. package/dist/src/styles/cytoscape/resolve.js.map +1 -1
  553. package/dist/src/styles/cytoscape/translate.d.ts.map +1 -1
  554. package/dist/src/styles/cytoscape/translate.js +10 -10
  555. package/dist/src/styles/cytoscape/translate.js.map +1 -1
  556. package/dist/src/styles/defaults.d.ts +50 -0
  557. package/dist/src/styles/defaults.d.ts.map +1 -1
  558. package/dist/src/styles/defaults.js +315 -37
  559. package/dist/src/styles/defaults.js.map +1 -1
  560. package/dist/src/styles/index.d.ts +4 -3
  561. package/dist/src/styles/index.d.ts.map +1 -1
  562. package/dist/src/styles/index.js +3 -2
  563. package/dist/src/styles/index.js.map +1 -1
  564. package/dist/src/styles/internal.d.ts +1 -0
  565. package/dist/src/styles/internal.d.ts.map +1 -1
  566. package/dist/src/styles/internal.js +1 -0
  567. package/dist/src/styles/internal.js.map +1 -1
  568. package/dist/src/styles/io/block.d.ts.map +1 -1
  569. package/dist/src/styles/io/block.js +9 -0
  570. package/dist/src/styles/io/block.js.map +1 -1
  571. package/dist/src/styles/io/generic.d.ts.map +1 -1
  572. package/dist/src/styles/io/generic.js +56 -16
  573. package/dist/src/styles/io/generic.js.map +1 -1
  574. package/dist/src/styles/io/round-trip.d.ts +56 -0
  575. package/dist/src/styles/io/round-trip.d.ts.map +1 -0
  576. package/dist/src/styles/io/round-trip.js +138 -0
  577. package/dist/src/styles/io/round-trip.js.map +1 -0
  578. package/dist/src/styles/io/writer.d.ts.map +1 -1
  579. package/dist/src/styles/io/writer.js +13 -4
  580. package/dist/src/styles/io/writer.js.map +1 -1
  581. package/dist/src/styles/notes.d.ts.map +1 -1
  582. package/dist/src/styles/notes.js +1 -0
  583. package/dist/src/styles/notes.js.map +1 -1
  584. package/dist/src/styles/plan/features.d.ts +1 -1
  585. package/dist/src/styles/plan/features.d.ts.map +1 -1
  586. package/dist/src/styles/plan/features.js +22 -2
  587. package/dist/src/styles/plan/features.js.map +1 -1
  588. package/dist/src/styles/plan/index.d.ts +1 -1
  589. package/dist/src/styles/plan/index.d.ts.map +1 -1
  590. package/dist/src/styles/plan/index.js +1 -1
  591. package/dist/src/styles/plan/index.js.map +1 -1
  592. package/dist/src/styles/plan/plan.d.ts.map +1 -1
  593. package/dist/src/styles/plan/plan.js +61 -4
  594. package/dist/src/styles/plan/plan.js.map +1 -1
  595. package/dist/src/styles/plan/resolve.d.ts +13 -1
  596. package/dist/src/styles/plan/resolve.d.ts.map +1 -1
  597. package/dist/src/styles/plan/resolve.js +53 -6
  598. package/dist/src/styles/plan/resolve.js.map +1 -1
  599. package/dist/src/styles/slots.d.ts +14 -0
  600. package/dist/src/styles/slots.d.ts.map +1 -0
  601. package/dist/src/styles/slots.js +20 -0
  602. package/dist/src/styles/slots.js.map +1 -0
  603. package/dist/src/styles/types.d.ts +638 -5
  604. package/dist/src/styles/types.d.ts.map +1 -1
  605. package/dist/src/styles/vocabulary.d.ts +3 -2
  606. package/dist/src/styles/vocabulary.d.ts.map +1 -1
  607. package/dist/src/styles/vocabulary.js +218 -1
  608. package/dist/src/styles/vocabulary.js.map +1 -1
  609. package/dist/src/types.d.ts +19 -5
  610. package/dist/src/types.d.ts.map +1 -1
  611. package/dist/src/types.js.map +1 -1
  612. package/dist/styles.js +29 -238
  613. package/dist/styles.js.map +1 -1
  614. package/dist/xgmml.js +3 -3
  615. package/docs/samples/people.json +6 -0
  616. package/package.json +53 -8
  617. package/src/common/report.ts +27 -7
  618. package/src/formats/cx/importer.ts +6 -1
  619. package/src/formats/cx/styles.ts +144 -9
  620. package/src/formats/cx2/importer.ts +1 -0
  621. package/src/formats/cx2/styles.ts +3 -1
  622. package/src/formats/cys/importer.ts +1 -0
  623. package/src/formats/cys/styles.ts +27 -5
  624. package/src/formats/dot/drawing.ts +799 -0
  625. package/src/formats/dot/importer.ts +120 -35
  626. package/src/formats/dot/label-places.ts +228 -0
  627. package/src/formats/dot/record.ts +336 -0
  628. package/src/formats/dot/styles.ts +1657 -154
  629. package/src/formats/gexf/dialog-metrics.ts +61 -0
  630. package/src/formats/gexf/importer.ts +50 -5
  631. package/src/formats/gexf/preview.ts +217 -0
  632. package/src/formats/gexf/schema.ts +2 -1
  633. package/src/formats/gexf/styles.ts +1144 -142
  634. package/src/formats/gml/importer.ts +291 -76
  635. package/src/formats/gml/index.ts +1 -1
  636. package/src/formats/gml/positions.ts +29 -7
  637. package/src/formats/gml/styles.ts +854 -111
  638. package/src/formats/graphml/styles-plain.ts +8 -23
  639. package/src/formats/graphml/styles-yed.ts +821 -159
  640. package/src/formats/graphml/styles-yfiles3.ts +485 -63
  641. package/src/formats/graphml/yfiles.ts +19 -6
  642. package/src/formats/json/dialect.ts +41 -5
  643. package/src/formats/json/importer.ts +59 -7
  644. package/src/formats/json/index.ts +1 -0
  645. package/src/formats/json/positions.ts +22 -5
  646. package/src/formats/json/styles.ts +2041 -185
  647. package/src/formats/pajek/draw.ts +458 -0
  648. package/src/formats/pajek/exporter.ts +81 -49
  649. package/src/formats/pajek/importer.ts +278 -103
  650. package/src/formats/pajek/index.ts +1 -0
  651. package/src/formats/pajek/styles.ts +1382 -133
  652. package/src/formats/pajek/syntax.ts +27 -2
  653. package/src/formats/xgmml/emit.ts +73 -5
  654. package/src/formats/xgmml/styles.ts +331 -71
  655. package/src/index.ts +94 -0
  656. package/src/positions/convert.ts +12 -1
  657. package/src/positions/extract.ts +55 -1
  658. package/src/positions/index.ts +1 -0
  659. package/src/positions/types.ts +60 -0
  660. package/src/registry.ts +326 -1
  661. package/src/sources/builtins.ts +63 -0
  662. package/src/sources/codes.ts +235 -0
  663. package/src/sources/context.ts +894 -0
  664. package/src/sources/credentials.ts +409 -0
  665. package/src/sources/cypher/descriptor.ts +131 -0
  666. package/src/sources/cypher/flatten.ts +323 -0
  667. package/src/sources/cypher/index.ts +628 -0
  668. package/src/sources/cypher/params.ts +41 -0
  669. package/src/sources/cypher/profiles.ts +78 -0
  670. package/src/sources/cypher/query-builder.ts +327 -0
  671. package/src/sources/cypher/transports/bolt.ts +228 -0
  672. package/src/sources/cypher/transports/neptune-analytics.ts +41 -0
  673. package/src/sources/cypher/transports/neptune-https.ts +122 -0
  674. package/src/sources/cypher/transports/query-api.ts +100 -0
  675. package/src/sources/cypher/transports/transport.ts +139 -0
  676. package/src/sources/cypher/types-map.ts +418 -0
  677. package/src/sources/define.ts +195 -0
  678. package/src/sources/fail.ts +70 -0
  679. package/src/sources/ids.ts +80 -0
  680. package/src/sources/ndex/client.ts +102 -0
  681. package/src/sources/ndex/descriptor.ts +64 -0
  682. package/src/sources/ndex/expand.ts +100 -0
  683. package/src/sources/ndex/index.ts +235 -0
  684. package/src/sources/ndex/params.ts +17 -0
  685. package/src/sources/ndex/share-url.ts +84 -0
  686. package/src/sources/params.ts +32 -0
  687. package/src/sources/registry.ts +325 -0
  688. package/src/sources/rest/descriptor.ts +79 -0
  689. package/src/sources/rest/index.ts +22 -0
  690. package/src/sources/rest/mapping.ts +401 -0
  691. package/src/sources/rest/openalex.ts +288 -0
  692. package/src/sources/rest/paging.ts +180 -0
  693. package/src/sources/rest/params.ts +243 -0
  694. package/src/sources/rest/records.ts +289 -0
  695. package/src/sources/rest/samples/deps-dev.ts +49 -0
  696. package/src/sources/rest/samples/string.ts +44 -0
  697. package/src/sources/rest/selectors.ts +91 -0
  698. package/src/sources/rest/source.ts +776 -0
  699. package/src/sources/rest/templates.ts +151 -0
  700. package/src/sources/run.ts +793 -0
  701. package/src/sources/sha256.ts +99 -0
  702. package/src/sources/sigv4.ts +209 -0
  703. package/src/sources/sparql/descriptor.ts +67 -0
  704. package/src/sources/sparql/index.ts +356 -0
  705. package/src/sources/sparql/ntriples.ts +219 -0
  706. package/src/sources/sparql/params.ts +28 -0
  707. package/src/sources/sparql/query.ts +162 -0
  708. package/src/sources/testing.ts +210 -0
  709. package/src/sources/types.ts +776 -0
  710. package/src/styles/annotations.ts +201 -27
  711. package/src/styles/canon/bends.ts +137 -0
  712. package/src/styles/canon/color-tables.ts +10 -4
  713. package/src/styles/canon/color.ts +105 -3
  714. package/src/styles/canon/enums.ts +99 -8
  715. package/src/styles/canon/graphviz.ts +71 -10
  716. package/src/styles/canon/number.ts +19 -0
  717. package/src/styles/canon/text.ts +187 -16
  718. package/src/styles/codes.ts +2 -0
  719. package/src/styles/cyjs/cascade.ts +1193 -38
  720. package/src/styles/cyjs/defaults-table.ts +5 -0
  721. package/src/styles/cyjs/emit.ts +247 -16
  722. package/src/styles/cyjs/face.ts +372 -0
  723. package/src/styles/cyjs/mappers.ts +498 -28
  724. package/src/styles/cyjs/routes.ts +432 -0
  725. package/src/styles/cytoscape/charts.ts +684 -0
  726. package/src/styles/cytoscape/emit.ts +220 -44
  727. package/src/styles/cytoscape/lexicon.ts +459 -80
  728. package/src/styles/cytoscape/mappings.ts +108 -0
  729. package/src/styles/cytoscape/resolve.ts +763 -23
  730. package/src/styles/cytoscape/translate.ts +11 -11
  731. package/src/styles/defaults.ts +436 -38
  732. package/src/styles/index.ts +5 -0
  733. package/src/styles/internal.ts +1 -0
  734. package/src/styles/io/block.ts +10 -0
  735. package/src/styles/io/generic.ts +79 -23
  736. package/src/styles/io/round-trip.ts +202 -0
  737. package/src/styles/io/writer.ts +14 -4
  738. package/src/styles/notes.ts +1 -0
  739. package/src/styles/plan/features.ts +23 -2
  740. package/src/styles/plan/index.ts +1 -1
  741. package/src/styles/plan/plan.ts +69 -5
  742. package/src/styles/plan/resolve.ts +61 -6
  743. package/src/styles/slots.ts +25 -0
  744. package/src/styles/types.ts +658 -4
  745. package/src/styles/vocabulary.ts +226 -3
  746. package/src/types.ts +19 -5
  747. package/dist/chunks/block-DiGq58c3.js.map +0 -1
  748. package/dist/chunks/choose--jM0yYCo.js +0 -57
  749. package/dist/chunks/choose--jM0yYCo.js.map +0 -1
  750. package/dist/chunks/convert-BcQ8_jMo.js.map +0 -1
  751. package/dist/chunks/emit-CwU8cUmG.js.map +0 -1
  752. package/dist/chunks/export-BvqAXCKh.js.map +0 -1
  753. package/dist/chunks/exporter-CrDWIyiq.js.map +0 -1
  754. package/dist/chunks/format-BpQBx8MZ.js.map +0 -1
  755. package/dist/chunks/generic-Dq1ntt9q.js.map +0 -1
  756. package/dist/chunks/importer-BSvfrX_h.js.map +0 -1
  757. package/dist/chunks/importer-CMmeMWlC.js.map +0 -1
  758. package/dist/chunks/importer-COTK9Tsl.js.map +0 -1
  759. package/dist/chunks/importer-DiZRmOQG.js.map +0 -1
  760. package/dist/chunks/importer-e6LGpqHB.js.map +0 -1
  761. package/dist/chunks/importer-iFYPEKaJ.js.map +0 -1
  762. package/dist/chunks/importer-yh_aBCU6.js.map +0 -1
  763. package/dist/chunks/lexicon-CT69Kd87.js.map +0 -1
  764. package/dist/chunks/styles-BQnsga7p.js.map +0 -1
  765. package/dist/chunks/styles-D3fRphrO.js +0 -690
  766. package/dist/chunks/styles-D3fRphrO.js.map +0 -1
  767. package/dist/chunks/styles-yed-BF5x3_Y5.js +0 -842
  768. package/dist/chunks/styles-yed-BF5x3_Y5.js.map +0 -1
  769. package/dist/chunks/writer-DMKvthQE.js +0 -448
  770. package/dist/chunks/writer-DMKvthQE.js.map +0 -1
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Pajek styles: each vertex's shape keyword and drawing parameters (`ic`, `bc`, `bw`, `x_fact`, `y_fact`, `lc`, `fos`)
3
- * and each line's `c`, `w` and `p`, read the way Pajek draws them, with Pajek's stated defaults beneath (an ellipse,
4
- * black solid lines, an arrow at the target of every arc). Colors are Pajek's: the dvips names, `GrayNN`,
5
- * `RGBrrggbb`, `RGB(r,g,b)` and the CMYK forms. `x_fact` and `y_fact` are factors of Pajek's normal vertex size, so the
6
- * lengths are relative.
3
+ * and each line's `c`, `w` and `p`, read the way Pajek 6.02 draws them (parameters carried from line to line, `CARRIED`),
4
+ * with Pajek's own defaults beneath (a #ffd9bc ellipse with a #660000 border, grey arcs with an arrow at the target,
5
+ * teal edges). Colors are Pajek's: the dvips names and the ten Pajek adds, `GrayNN`, `RGBrrggbb`, `RGB(r,g,b)` and the
6
+ * CMYK forms. Lengths are in PostScript points, the unit of Pajek's EPS and SVG export, where the drawing's [0,1] square
7
+ * is [0,500] (`PAJEK_POINTS_PER_UNIT`): a vertex is Pajek's normal size 20 times its `x_fact` and `y_fact`,
8
+ * `bw` and `fos` are points as written, an arc is `w` wide and an edge twice `w`.
7
9
  *
8
10
  * Writing: the same parameters from what graphty draws, leaving out what Pajek draws by default; a color that is a
9
11
  * dvips name is written by name, any other as `RGBrrggbb`, so colors are exact. The `graphty.styles` block, with the
@@ -13,18 +15,21 @@
13
15
 
14
16
  import type { GraphSnapshot } from "@graphty/graph-format";
15
17
 
18
+ import { explicitWeights } from "../../common/weights.js";
16
19
  import { canonColor, colorOpacity } from "../../styles/canon/color.js";
17
20
  import { DVIPS_COLORS } from "../../styles/canon/color-tables.js";
18
21
  import { canonEnum } from "../../styles/canon/enums.js";
19
22
  import { canonNumber } from "../../styles/canon/number.js";
20
- import type { CanonSite } from "../../styles/canon/site.js";
23
+ import { type CanonSite, noteAt } from "../../styles/canon/site.js";
21
24
  import { canonText } from "../../styles/canon/text.js";
25
+ import { STYLE_ISSUE } from "../../styles/codes.js";
26
+ import { toolDefaults } from "../../styles/defaults.js";
22
27
  import { StyleApproximations } from "../../styles/io/approximations.js";
23
- import { blockStyles, parseStyleBlock, styleBlockLosses, writeStyleBlock } from "../../styles/io/block.js";
28
+ import { styleBlockLosses, writeStyleBlock } from "../../styles/io/block.js";
29
+ import { elementValues, takeFromBlock } from "../../styles/io/round-trip.js";
24
30
  import type { ExporterStyleInput } from "../../styles/io/writer.js";
25
31
  import { StyleNotes } from "../../styles/notes.js";
26
- import { styleFingerprint } from "../../styles/plan/features.js";
27
- import { plainCell, resolveStyles } from "../../styles/plan/resolve.js";
32
+ import { plainCell } from "../../styles/plan/resolve.js";
28
33
  import type {
29
34
  ImporterStyles,
30
35
  StyleColumn,
@@ -35,15 +40,19 @@ import type {
35
40
  StyleWriteCapabilities,
36
41
  } from "../../styles/types.js";
37
42
  import type { LossNote } from "../../types.js";
43
+ import { graphvizFontName, postscriptFont } from "../dot/drawing.js";
38
44
  import { type ValueColumns, valueColumns } from "../graphml/styles-yed.js";
39
- import { LABEL_COLUMN, PAJEK_STYLE_COMMENT, SHAPE_COLUMN } from "./syntax.js";
45
+ import { drawLines, drawsLineLabels, drawVertices, type PajekDraw } from "./draw.js";
46
+ import { LABEL_COLUMN, PAJEK_STYLE_COMMENT, SHAPE_COLUMN, VALUE_COLUMN } from "./syntax.js";
40
47
 
41
48
  /**
42
- * What the Pajek writer holds natively: the vertex label, the vertex shape keyword and drawing parameters, the line
49
+ * What the Pajek writer holds natively: the vertex label, the vertex shape keyword (a polygon only as Pajek's diamond,
50
+ * `shapePoints` with its `q`) and drawing parameters, the line
43
51
  * parameters, and the arrows Pajek draws on arcs. The `graphty.styles` block is a comment line the writer places itself.
44
52
  * @category Built-in formats
45
53
  */
46
54
  export const PAJEK_STYLE_CAPABILITIES: StyleWriteCapabilities = Object.freeze({
55
+ tool: "pajek",
47
56
  node: Object.freeze([
48
57
  "label",
49
58
  "fill",
@@ -52,26 +61,139 @@ export const PAJEK_STYLE_CAPABILITIES: StyleWriteCapabilities = Object.freeze({
52
61
  "borderColor",
53
62
  "borderWidth",
54
63
  "shape",
64
+ "shapePoints",
55
65
  "width",
56
66
  "height",
57
67
  "labelColor",
58
68
  "labelSize",
69
+ "labelFont",
70
+ "labelPosition",
71
+ "labelOffsetX",
72
+ "labelOffsetY",
73
+ "labelShiftX",
74
+ ] as const),
75
+ edge: Object.freeze([
76
+ "color",
77
+ "opacity",
78
+ "width",
79
+ "lineStyle",
80
+ "sourceArrow",
81
+ "targetArrow",
82
+ "arrowPosition",
83
+ "label",
84
+ "labelColor",
85
+ "labelSize",
86
+ "labelFont",
59
87
  ] as const),
60
- edge: Object.freeze(["color", "opacity", "width", "lineStyle", "sourceArrow", "targetArrow"] as const),
61
88
  block: "native",
62
89
  });
63
90
 
64
91
  /**
65
- * Pajek's default vertex size, in the units of a border width `bw`: `x_fact` and `y_fact` are factors of it, so a
66
- * border width is read as a factor of it too (`bw 1`, Pajek's default, is a fifth of a normal vertex).
92
+ * Pajek's normal vertex size, in points: what `x_fact` and `y_fact` multiply (the manual, p. 89, names `x_fact` and
93
+ * `y_fact` "magnification" without stating the value).
94
+ * Measured in Pajek 6.02's own SVG export (`test/fidelity/oracles/pajek-*.svg`): a vertex with no size parameter is
95
+ * drawn 20 across, and `x_fact 1.54` 30.8, on the drawing's [0,500] square.
96
+ */
97
+ const PAJEK_VERTEX_SIZE = 20;
98
+
99
+ /**
100
+ * How far past its vertex size Pajek draws its man and woman figures, across and up: Pajek 6.02's SVG export draws
101
+ * them as paths that reach beyond the vertex's own square, a man 2.5 times its size across and 6.49 times its size
102
+ * tall, a woman (her skirt and arms wider) 3 times across and 6.48 tall (`pajek-specimen-node-shapes`, vertices 9 and
103
+ * 10 at `x_fact 0.5`: the man's path spans x -12.51..12.51 and y -32.74..32.13, the woman's -15.02..15.02 and
104
+ * -32.99..31.79, around a size of 10).
67
105
  */
68
- const VERTEX_SIZE = 5;
106
+ const PAJEK_FIGURES: ReadonlyMap<string, readonly [number, number]> = new Map([
107
+ ["other:man", [2.502, 6.487]],
108
+ ["other:woman", [3.003, 6.478]],
109
+ ]);
110
+
111
+ /**
112
+ * Points per coordinate unit: "When Postscript picture is obtained, the square [0,1]x[0,1] is transformed to
113
+ * [0,500]x[0,500]" (Pajek, "Pajek to EPS", http://mrvar.fdv.uni-lj.si/pajek/DrawEPS.htm).
114
+ */
115
+ export const PAJEK_POINTS_PER_UNIT = 500;
69
116
 
70
117
  /** The vertex parameters read as styles. */
71
- const NODE_KEYS: readonly string[] = ["ic", "bc", "bw", "x_fact", "y_fact", "lc", "fos"];
118
+ const NODE_KEYS: readonly string[] = [
119
+ "ic",
120
+ "bc",
121
+ "bw",
122
+ "s_size",
123
+ "x_fact",
124
+ "y_fact",
125
+ "phi",
126
+ "q",
127
+ "lc",
128
+ "fos",
129
+ "font",
130
+ "lr",
131
+ "lphi",
132
+ "la",
133
+ ];
72
134
 
73
135
  /** The line parameters read as styles. */
74
- const EDGE_KEYS: readonly string[] = ["c", "w", "p"];
136
+ const EDGE_KEYS: readonly string[] = ["c", "w", "p", "s", "ap", "l", "lc", "fos", "font"];
137
+
138
+ /**
139
+ * What Pajek carries from one line of a section to the next. A line (arc or edge) that does not set one of these
140
+ * parameters is drawn with the value the line before it set. A vertex line that sets no parameter and names no shape
141
+ * is drawn exactly as the vertex line before it; a vertex line that sets any starts again from Pajek's defaults.
142
+ * Measured in Pajek 6.02's own SVG export: `test/fidelity/oracles/pajek-kdorola.svg` (vertices 2 to 59, which set
143
+ * nothing, drawn with vertex 1's `lr 11 lphi 285 fos 9`, and 61 on as vertex 60's `box`), `pajek-1crn.svg` (vertex 3
144
+ * drawn as vertex 2), `pajek-synth-lines.svg` (arc 3, which sets `w 3` but no `c`, drawn in arc 2's `c Blue`), and
145
+ * the generated `shapes-and-lines.net` (vertex 5 at the default size after vertex 4's `x_fact 8`). A line's label `l`
146
+ * is its own; its label's color, size and font are not carried here, as they only show with a label.
147
+ */
148
+ const CARRIED: Readonly<Record<StyleTarget, readonly string[]>> = {
149
+ node: [SHAPE_COLUMN, ...NODE_KEYS],
150
+ edge: ["c", "w", "p", "s", "ap"],
151
+ };
152
+
153
+ /**
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).
157
+ */
158
+ const CARRIED_LINE: readonly string[] = CARRIED.edge.filter((key) => key !== "p");
159
+
160
+ /** The width Pajek draws an edge (undirected) at for each unit of `w`; an arc is drawn `w` wide. */
161
+ const EDGE_WIDTH_PER_W = 2;
162
+
163
+ /** The color and width Pajek draws an arc with when it sets no `c` or `w` (an edge's are the tool defaults). */
164
+ const ARC_DEFAULTS = { color: "#999999", width: 1 } as const;
165
+
166
+ /**
167
+ * A getter that gives an element the parameters an element before it set, as Pajek reads them: for a line each
168
+ * parameter it leaves out, for a vertex all of them when it sets none (`CARRIED`).
169
+ * @param keys - the parameters carried
170
+ * @param whole - true for vertices: carried only to an element that sets none, and then all together
171
+ * @returns for each element in order, its getter
172
+ */
173
+ function carriedParams(keys: readonly string[], whole: boolean): (get: Param) => Param {
174
+ let last = new Map<string, unknown>();
175
+ return (get) => {
176
+ const own = new Map<string, unknown>();
177
+ for (const key of keys) {
178
+ const v = get(key);
179
+ if (v !== null) {
180
+ own.set(key, v);
181
+ }
182
+ }
183
+ if (whole) {
184
+ if (own.size > 0) {
185
+ last = own;
186
+ return get;
187
+ }
188
+ } else {
189
+ for (const [key, v] of own) {
190
+ last.set(key, v);
191
+ }
192
+ }
193
+ const carried = last;
194
+ return (key) => get(key) ?? (keys.includes(key) ? (carried.get(key) ?? null) : null);
195
+ };
196
+ }
75
197
 
76
198
  /** The style column marking arcs, which Pajek draws with an arrow at the target. */
77
199
  const DIRECTED_KEY = "directed";
@@ -81,6 +203,8 @@ const READ: Readonly<Record<StyleTarget, readonly string[]>> = {
81
203
  node: [
82
204
  "label",
83
205
  "shape",
206
+ "shapePoints",
207
+ "shapePointsStretch",
84
208
  "fill",
85
209
  "fillOpacity",
86
210
  "borderColor",
@@ -89,8 +213,25 @@ const READ: Readonly<Record<StyleTarget, readonly string[]>> = {
89
213
  "height",
90
214
  "labelColor",
91
215
  "labelSize",
216
+ "labelFont",
217
+ "labelPosition",
218
+ "labelOffsetX",
219
+ "labelOffsetY",
220
+ "labelShiftX",
221
+ "labelRotation",
222
+ ],
223
+ edge: [
224
+ "color",
225
+ "opacity",
226
+ "width",
227
+ "lineStyle",
228
+ "arrowSize",
229
+ "arrowPosition",
230
+ "label",
231
+ "labelColor",
232
+ "labelSize",
233
+ "labelFont",
92
234
  ],
93
- edge: ["color", "opacity", "width", "lineStyle"],
94
235
  };
95
236
 
96
237
  /** A parameter's value on one element, or null. */
@@ -116,6 +257,13 @@ function readColor(raw: unknown, property: string, opacity: string | null, put:
116
257
  }
117
258
  const c = canonColor(raw, "dvips", at(property));
118
259
  if (c === null) {
260
+ // a name Pajek does not know, or a known one in another case (`red`), fills a vertex white and draws a line
261
+ // black (`pajek-specimen-node-fills-borders-p3` `ic-unknown-name`, `ic-lowercase-name`); ponytail: measured
262
+ // on names in `ic` and `c` only, so a border or label color and an unreadable RGB / CMYK form keep the default
263
+ const unknown = UNKNOWN_COLORS.get(property);
264
+ if (unknown !== undefined && typeof raw === "string" && /^[A-Za-z]\w*$/.test(raw.trim())) {
265
+ put(property, unknown);
266
+ }
119
267
  return;
120
268
  }
121
269
  put(property, c.color);
@@ -125,6 +273,12 @@ function readColor(raw: unknown, property: string, opacity: string | null, put:
125
273
  }
126
274
  }
127
275
 
276
+ /** What Pajek draws for a color it cannot read, by property. */
277
+ const UNKNOWN_COLORS: ReadonlyMap<string, StyleValue> = new Map([
278
+ ["fill", "#ffffff"],
279
+ ["color", "#000000"],
280
+ ]);
281
+
128
282
  /**
129
283
  * Reads a length or factor.
130
284
  * @param raw - the parameter's value, or null
@@ -143,42 +297,742 @@ function readNumber(raw: unknown, property: string, put: Put, at: At): void {
143
297
  * @param get - the vertex's parameters
144
298
  * @param put - receives the values
145
299
  * @param at - the sites
300
+ * @param place - the vertex's place in the file's coordinates, or null when it has none
146
301
  */
147
- function readVertex(get: Param, put: Put, at: At): void {
302
+ function readVertex(get: Param, put: Put, at: At, place: Place | null = null): void {
148
303
  // an empty label draws nothing
149
304
  const label = get(LABEL_COLUMN);
150
305
  if (label !== null && label !== "") {
151
306
  put("label", canonText(label, at("label")));
152
307
  }
153
- const shape = get(SHAPE_COLUMN);
154
- if (shape !== null) {
155
- put("shape", canonEnum("shape", shape, "dvips", at("shape")));
308
+ const raw = get(SHAPE_COLUMN);
309
+ const shape = raw === null ? null : canonEnum("shape", raw, "dvips", at("shape"));
310
+ if (raw !== null) {
311
+ put("shape", shape);
156
312
  }
157
313
  readColor(get("ic"), "fill", "fillOpacity", put, at);
158
314
  readColor(get("bc"), "borderColor", null, put, at);
159
- const bw = get("bw");
160
- if (bw !== null) {
161
- const width = canonNumber(bw, at("borderWidth"));
162
- put("borderWidth", width === null ? null : width / VERTEX_SIZE);
163
- }
164
- readNumber(get("x_fact"), "width", put, at);
165
- readNumber(get("y_fact"), "height", put, at);
315
+ readNumber(get("bw"), "borderWidth", put, at);
316
+ readSize(get, put, at, PAJEK_FIGURES.get(String(shape)));
317
+ readOutline(get, put, shape);
166
318
  readColor(get("lc"), "labelColor", null, put, at);
167
319
  readNumber(get("fos"), "labelSize", put, at);
320
+ readFont(get("font"), put, at);
321
+ readLabelPosition(get, put, at);
322
+ readLabelAngle(get("la"), put, at, place);
323
+ if (shape === "none") {
324
+ // an `empty` vertex's label is drawn white at size 1 on its center, whatever the line sets
325
+ // (`pajek-synth-vertices` `empty *`, `pajek-lin-rel4` v226 to v233)
326
+ for (const [property, value] of EMPTY_LABEL) {
327
+ put(property, value);
328
+ }
329
+ }
330
+ }
331
+
332
+ /**
333
+ * The corners Pajek 6.02's SVG export draws a box, a triangle and a house with, as `shapePoints` (fractions of the half
334
+ * width and height, y up): `pajek-specimen-node-shapes`, `shape-box`, `shape-triangle`, `shape-house`.
335
+ */
336
+ const PAJEK_OUTLINES: ReadonlyMap<string, readonly (readonly [number, number])[]> = new Map([
337
+ [
338
+ "rectangle",
339
+ [
340
+ [-1, 1],
341
+ [1, 1],
342
+ [1, -1],
343
+ [-1, -1],
344
+ ],
345
+ ],
346
+ [
347
+ "triangle",
348
+ [
349
+ [1, -1],
350
+ [0, 1],
351
+ [-1, -1],
352
+ ],
353
+ ],
354
+ [
355
+ "other:house",
356
+ [
357
+ [-0.7, -1],
358
+ [0.7, -1],
359
+ [0.7, 0.3],
360
+ [1, 0.3],
361
+ [1, 0.4],
362
+ [0, 1],
363
+ [-1, 0.4],
364
+ [-1, 0.3],
365
+ [-0.7, 0.3],
366
+ ],
367
+ ],
368
+ ]);
369
+
370
+ /** A diamond's `q` where the line sets none (Pajek's shipped SHAPES.CFG). */
371
+ const PAJEK_DIAMOND_Q = 0.001;
372
+
373
+ /**
374
+ * A vertex's outline where Pajek draws one no named shape is: a diamond is Pajek's six-cornered one, its top and bottom
375
+ * cut `q` of its half width wide (`pajek-specimen-node-shapes`: `q 0.001` by default, `q 0.5` a hexagon, `q 1` a
376
+ * square, `q 2` a bow tie twice as wide), and `phi` turns a box, a triangle, a house or a diamond `phi` degrees
377
+ * counterclockwise about its center (`phi-30-box`, `phi-90-triangle`: Pajek draws the shape at its size, then turns
378
+ * it). ponytail: the corners are fractions of the size the line gives (`x_fact`, `y_fact`); a turned shape whose width
379
+ * and height the Draw window's Size of Vertices or vectors scale apart is turned in the wrong box. A turned ellipse,
380
+ * man or woman is drawn unturned: an outline of corners would draw an ellipse as a polygon.
381
+ * @param get - the vertex's parameters
382
+ * @param put - receives the values
383
+ * @param shape - its shape
384
+ */
385
+ function readOutline(get: Param, put: Put, shape: StyleValue | null): void {
386
+ const phi = numberOrNull(get("phi")) ?? 0;
387
+ const q = numberOrNull(get("q")) ?? PAJEK_DIAMOND_Q;
388
+ const diamond: readonly (readonly [number, number])[] = [
389
+ [-1, 0],
390
+ [-q, 1],
391
+ [q, 1],
392
+ [1, 0],
393
+ [q, -1],
394
+ [-q, -1],
395
+ ];
396
+ const corners = shape === "diamond" ? diamond : PAJEK_OUTLINES.get(String(shape));
397
+ if (corners === undefined || (shape !== "diamond" && phi % 360 === 0)) {
398
+ return;
399
+ }
400
+ // turned in the drawn box, x_fact by y_fact, and back to fractions of it
401
+ const [w, h] = [numberOrNull(get("x_fact")) || 1, numberOrNull(get("y_fact")) || 1];
402
+ const [c, s] = [Math.cos((phi * Math.PI) / 180), Math.sin((phi * Math.PI) / 180)];
403
+ const round = (v: number): string => String(Number(v.toFixed(4)) + 0);
404
+ const points = corners.map(([x, y]) => {
405
+ const [px, py] = [x * w, y * h];
406
+ return `${round((px * c - py * s) / w)},${round((px * s + py * c) / h)}`;
407
+ });
408
+ put("shape", "polygon");
409
+ put("shapePoints", points.join(" "));
410
+ put("shapePointsStretch", false);
411
+ }
412
+
413
+ /** How Pajek 6.02's SVG export draws an `empty` vertex's label. */
414
+ const EMPTY_LABEL: readonly (readonly [string, StyleValue])[] = [
415
+ ["labelSize", 1],
416
+ ["labelColor", "#ffffff"],
417
+ ["labelPosition", "center"],
418
+ ["labelOffsetX", 0],
419
+ ["labelOffsetY", 0],
420
+ ["labelShiftX", 0],
421
+ ];
422
+
423
+ /** A vertex's place in the file's own coordinates, 0..1 across and down. */
424
+ type Place = readonly [x: number, y: number];
425
+
426
+ /**
427
+ * A label's `font`: Pajek writes it into its SVG export as the family, as given, so a PostScript name (`Courier-Bold`,
428
+ * `Helvetica-Oblique`) is drawn in its family, with a generic one after it, and upright, as the pinned fonts draw it
429
+ * (`pajek-specimen-labels` `font-courier-bold` monospace, `font-helvetica-oblique` sans-serif and upright); any other
430
+ * name is the family as written. Pajek draws every label bold whatever the name says.
431
+ * @param raw - the parameter's value, or null
432
+ * @param put - receives the values
433
+ * @param at - the sites
434
+ */
435
+ function readFont(raw: unknown, put: Put, at: At): void {
436
+ if (raw === null || raw === "") {
437
+ return;
438
+ }
439
+ const ps = typeof raw === "string" ? postscriptFont(raw.trim()) : null;
440
+ if (ps === null) {
441
+ readText(raw, "labelFont", put, at);
442
+ return;
443
+ }
444
+ put("labelFont", ps.family);
445
+ }
446
+
447
+ /**
448
+ * A vertex label's angle `la` (manual p. 90), degrees counterclockwise, as a clockwise rotation: Pajek 6.02's SVG export
449
+ * turns the label by `rotate(-la)` (`pajek-specimen-labels`, `la-30` .. `la-270`). An angle of 360 or more turns the
450
+ * label away from the middle of the file's square, (0.5, 0.5) in its own coordinates, and then `la - 360` further
451
+ * (`la-360` at (0.4533, 0.3706) is drawn at -109.83, the direction from the middle; `la-400` at (0.5467, 0.3706) at
452
+ * -110.17, that direction, 70.17, and 40 more); without the vertex's place it is left unread.
453
+ * @param raw - the parameter's value, or null
454
+ * @param put - receives the value
455
+ * @param at - the sites
456
+ * @param place - the vertex's place in the file's coordinates, or null
457
+ */
458
+ function readLabelAngle(raw: unknown, put: Put, at: At, place: Place | null): void {
459
+ let a = raw === null ? null : canonNumber(raw, at("labelRotation"));
460
+ if (a !== null && a >= 360) {
461
+ a = place === null ? null : (Math.atan2(0.5 - place[1], place[0] - 0.5) * 180) / Math.PI + a - 360;
462
+ }
463
+ if (a !== null) {
464
+ put("labelRotation", Number(((((360 - a) % 360) + 360) % 360).toPrecision(12)));
465
+ }
466
+ }
467
+
468
+ /**
469
+ * A vertex's width and height in points: the normal size times its `x_fact` and `y_fact`, each 1 when unset; a vertex
470
+ * that sets neither is left to the default beneath. The manual (p. 89) calls `s_size` "default size", but Pajek 6.02
471
+ * draws a vertex the same size whatever its `s_size` (`pajek-specimen-node-sizes-opacity`: `s_size` 0.5, 2 and 3 all
472
+ * 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.
474
+ * @param get - the vertex's parameters
475
+ * @param put - receives the values
476
+ * @param at - the sites
477
+ * @param figure - a figure's width and height over its size, or undefined for another shape
478
+ */
479
+ function readSize(get: Param, put: Put, at: At, figure?: readonly [number, number]): void {
480
+ for (const [key, property, k] of [
481
+ ["x_fact", "width", 0],
482
+ ["y_fact", "height", 1],
483
+ ] as const) {
484
+ const f = get(key);
485
+ if (f === null && figure === undefined) {
486
+ continue;
487
+ }
488
+ const factor = f === null ? 1 : canonNumber(f, at(property));
489
+ const over = figure?.[k] ?? 1;
490
+ put(property, factor === null ? null : Number((PAJEK_VERTEX_SIZE * factor * over).toPrecision(15)));
491
+ }
492
+ }
493
+
494
+ /**
495
+ * A text parameter (a PostScript font name).
496
+ * @param raw - the parameter's value, or null
497
+ * @param property - the property
498
+ * @param put - receives the value
499
+ * @param at - the sites
500
+ */
501
+ function readText(raw: unknown, property: string, put: Put, at: At): void {
502
+ if (raw !== null && raw !== "") {
503
+ put(property, canonText(raw, at(property)));
504
+ }
505
+ }
506
+
507
+ /** The `lphi` written for each side graphty puts a label on, and the side written for each Pajek has no angle for. */
508
+ const SIDE_ANGLES: ReadonlyMap<string, number> = new Map([
509
+ ["right", 0],
510
+ ["top-right", 90],
511
+ ["left", 180],
512
+ ["bottom-right", 285],
513
+ ]);
514
+ const NEAREST_SIDES: ReadonlyMap<string, string> = new Map([
515
+ ["top", "top-right"],
516
+ ["top-left", "top-right"],
517
+ ["bottom", "bottom-right"],
518
+ ["bottom-left", "bottom-right"],
519
+ ]);
520
+
521
+ /**
522
+ * A vertex's label place from Pajek's polar label parameters (manual p. 90): `lr` is the distance of the label from
523
+ * the vertex center, 0 centering it, and `lphi` its angle in degrees counterclockwise. Pajek 6.02's SVG export starts
524
+ * the text at that point, its baseline through it (`text-anchor` start at y = the point), so the label's left edge is
525
+ * at the point's x (`labelShiftX` 0.5) and its middle `PAJEK_BASELINE_TO_MIDDLE` of its size above the point's y
526
+ * (`pajek-specimen-labels` `lr-*`, `lphi-*`; every corpus label). A vertex that sets none of `lr`, `lphi` and `fos`
527
+ * is left to the default beneath, the same place at the default size.
528
+ * @param get - the vertex's parameters
529
+ * @param put - receives the values
530
+ * @param at - the sites
531
+ */
532
+ function readLabelPosition(get: Param, put: Put, at: At): void {
533
+ const [lr, lphi, fos] = [get("lr"), get("lphi"), get("fos")];
534
+ if (lr === null && lphi === null && fos === null) {
535
+ return;
536
+ }
537
+ const radius = lr === null ? PAJEK_LABEL_RADIUS : canonNumber(lr, at("labelPosition"));
538
+ const angle = lphi === null ? PAJEK_LABEL_ANGLE : canonNumber(lphi, at("labelPosition"));
539
+ const size = fos === null ? PAJEK_LABEL_SIZE : canonNumber(fos, at("labelSize"));
540
+ if (radius === null || angle === null || size === null) {
541
+ return;
542
+ }
543
+ const [x, y] = labelOffset(radius, angle, size);
544
+ put("labelPosition", "center");
545
+ put("labelOffsetX", x);
546
+ put("labelOffsetY", y);
547
+ put("labelShiftX", radius === 0 ? 0 : 0.5);
548
+ }
549
+
550
+ /**
551
+ * Where Pajek's polar label parameters put a label, from the vertex center, y down: the middle of its left edge, or
552
+ * its middle when `lr` is 0, which centers it.
553
+ * @param radius - `lr`
554
+ * @param angle - `lphi`, degrees counterclockwise
555
+ * @param size - the label's size
556
+ * @returns the offset across and down
557
+ */
558
+ function labelOffset(radius: number, angle: number, size: number): [number, number] {
559
+ if (radius === 0) {
560
+ return [0, 0];
561
+ }
562
+ const t = (angle * Math.PI) / 180;
563
+ const round = (v: number): number => Number(v.toFixed(9)) + 0;
564
+ return [round(radius * Math.cos(t)), round(-radius * Math.sin(t) - PAJEK_BASELINE_TO_MIDDLE * size)];
168
565
  }
169
566
 
170
567
  /**
171
- * One line's style: its color, width and pattern.
568
+ * The label radius and angle Pajek uses when a vertex sets none: 20 and 285, below and to the right of the vertex.
569
+ * Measured in Pajek 6.02's own SVG export (`test/fidelity/oracles/pajek-football.svg`, `pajek-specimen-labels`
570
+ * `default`: every such label starts 20 from its vertex at 285 degrees); the size is the default label size.
571
+ */
572
+ const PAJEK_LABEL_RADIUS = 20;
573
+ const PAJEK_LABEL_ANGLE = 285;
574
+ const PAJEK_LABEL_SIZE = 15;
575
+
576
+ /**
577
+ * How far above its baseline a label's middle is, per unit of its size: half the ascent less the descent of Arial
578
+ * (Liberation Sans: 0.905 and 0.212), the face Pajek draws its labels in by default.
579
+ */
580
+ const PAJEK_BASELINE_TO_MIDDLE = 0.3465;
581
+
582
+ /**
583
+ * One line's style: its color, width and pattern. Pajek draws an edge (undirected) twice as wide as an arc of the
584
+ * same `w`.
172
585
  * @param get - the line's parameters
173
586
  * @param put - receives the values
174
587
  * @param at - the sites
588
+ * @param directed - whether the line is an arc
589
+ * @param labels - whether its label is drawn (`drawsLineLabels`)
590
+ * @param run - the line from its source's center to its target's, in drawn units, y down, or null when unplaced
591
+ * @param loop - for a loop on a placed node, receives its control points; null for any other line
175
592
  */
176
- function readLine(get: Param, put: Put, at: At): void {
593
+ function readLine(
594
+ get: Param,
595
+ put: Put,
596
+ at: At,
597
+ directed: boolean,
598
+ labels = true,
599
+ run: Run | null = null,
600
+ loop: ((controls: LoopControls) => void) | null = null,
601
+ ): void {
177
602
  readColor(get("c"), "color", "opacity", put, at);
178
- readNumber(get("w"), "width", put, at);
603
+ readNumber(
604
+ get("w"),
605
+ "width",
606
+ (property, value) => {
607
+ put(property, typeof value === "number" && !directed ? value * EDGE_WIDTH_PER_W : value);
608
+ },
609
+ at,
610
+ );
179
611
  const p = get("p");
180
- if (p !== null) {
181
- put("lineStyle", canonEnum("lineStyle", p, "dvips", at("lineStyle")));
612
+ if (p === "Dots") {
613
+ put("lineStyle", pajekDash(get, directed));
614
+ } else if (p === "Solid") {
615
+ put("lineStyle", "solid");
616
+ } else if (p !== null) {
617
+ // Pajek knows Solid and Dots, case for case; any other pattern (`Dashed`, `dots`) draws a solid line
618
+ noteAt(at("lineStyle"), STYLE_ISSUE.UNKNOWN_VALUE, typeof p === "string" ? p : JSON.stringify(p));
619
+ put("lineStyle", "solid");
620
+ }
621
+ // 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
+ readNumber(get("s"), "arrowSize", put, at);
623
+ readArrowPosition(get("ap"), put, at);
624
+ // 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
+ const line = loop === null ? get : loopParam(get);
626
+ const controls = loop === null ? null : readLoop(line, put, loop);
627
+ if (labels) {
628
+ readText(get("l"), "label", put, at);
629
+ if (controls === null) {
630
+ readLineLabelPlace(get, put, at, run);
631
+ } else {
632
+ readLineLabelPlace(line, put, at, [PAJEK_POINTS_PER_UNIT, 0], loopTop(controls));
633
+ }
634
+ }
635
+ if (loop === null) {
636
+ readBezier(get, put, run);
637
+ }
638
+ readColor(get("lc"), "labelColor", null, put, at);
639
+ readNumber(get("fos"), "labelSize", put, at);
640
+ readFont(get("font"), put, at);
641
+ }
642
+
643
+ /** A line from its source's center to its target's, in drawn units, y down. */
644
+ type Run = readonly [dx: number, dy: number];
645
+
646
+ /** A loop's two Bezier control points, each from its node's center in drawn units, y down. */
647
+ type LoopControls = readonly [readonly [number, number], readonly [number, number]];
648
+
649
+ /**
650
+ * The route parameters Pajek 6.02's SVG export draws a loop with where the line sets none (`pajek-specimen-edges-lines-p2`
651
+ * `loop` and `loop-arc`, `pajek-specimen-edge-labels` `on-loop`: a cubic from the node's center up and back, its control
652
+ * points 74.9 out at 70 and 110 degrees up from the right).
653
+ */
654
+ const LOOP_DEFAULTS: Readonly<Record<string, number>> = { a1: -70, k1: 0.3, a2: -70, k2: 0.3 };
655
+
656
+ /**
657
+ * A loop's route parameters: the line's own, Pajek's loop defaults for any it leaves out.
658
+ * @param get - the line's parameters
659
+ * @returns the parameters with the defaults
660
+ */
661
+ function loopParam(get: Param): Param {
662
+ return (key) => get(key) ?? (key in LOOP_DEFAULTS ? LOOP_DEFAULTS[key] : null);
663
+ }
664
+
665
+ /**
666
+ * A loop's route (manual p. 90: "if arc is a loop (arc to itself) bezier loop will be drawn"), as Pajek 6.02's SVG
667
+ * export draws it: the Bezier route of a line PAJEK_POINTS_PER_UNIT long running right, both its ends on the node
668
+ * (`a1 -130 k1 0.6 a2 -130 k2 0.6`: control points 150 out at 130 and 50 degrees up from the right, measured on
669
+ * `pajek-specimen-edges-lines-p2` `loop-bezier`). Its bends are written once the nodes' sizes are known
670
+ * (`layLoops`). ponytail: measured in one Draw window (1200 x 900) and one vertex size; whether Pajek scales a loop
671
+ * with either was not measured.
672
+ * @param get - the line's parameters, with the loop defaults
673
+ * @param put - receives the values
674
+ * @param loop - receives the control points
675
+ * @returns the control points, or null when the line's parameters give none
676
+ */
677
+ function readLoop(get: Param, put: Put, loop: (controls: LoopControls) => void): LoopControls | null {
678
+ const run: Run = [PAJEK_POINTS_PER_UNIT, 0];
679
+ const controls = bezierControls(get, run);
680
+ if (controls === null) {
681
+ return null;
682
+ }
683
+ const [[a1, l1], [a2, l2]] = controls;
684
+ const L = PAJEK_POINTS_PER_UNIT;
685
+ // along the line is right, left of it up: the second point is measured back from the line's far end, the node
686
+ const [c1, c2]: LoopControls = [
687
+ [a1 * L, -l1 * L],
688
+ [(a2 - 1) * L, -l2 * L],
689
+ ];
690
+ loop([c1, c2]);
691
+ put("curved", true);
692
+ put("bendCurve", "bezier");
693
+ // an arc's head is drawn where the loop comes back to its node's center, under the node
694
+ // (`pajek-specimen-edges-lines-p2` `loop-arc`, `s 1 a A`: no head shows)
695
+ put("targetArrow", "none");
696
+ return [c1, c2];
697
+ }
698
+
699
+ /**
700
+ * The way a loop runs at its top, where its label is drawn (`lp` 0.5): from its first control point to its second
701
+ * (`pajek-specimen-edge-labels` `on-loop`: 179.97). ponytail: a label elsewhere on the loop (`lp`) is turned as at
702
+ * its top.
703
+ * @param controls - the loop's control points
704
+ * @returns the direction, in degrees clockwise on screen
705
+ */
706
+ function loopTop(controls: LoopControls): number {
707
+ const [c1, c2] = controls;
708
+ return (Math.atan2(c2[1] - c1[1], c2[0] - c1[0]) * 180) / Math.PI;
709
+ }
710
+
711
+ /**
712
+ * Writes each loop's control points as its bends, fractions of its node's half width and half height, y up, once the
713
+ * nodes are sized, and draws it round whatever the Draw window does to the other lines.
714
+ * @param snapshot - the graph
715
+ * @param loops - each loop's control points by edge
716
+ * @param node - the node values, sized
717
+ * @param edge - the edge values; changed in place
718
+ */
719
+ function layLoops(
720
+ snapshot: GraphSnapshot,
721
+ loops: ReadonlyMap<number, LoopControls>,
722
+ node: ValueColumns,
723
+ edge: ValueColumns,
724
+ ): void {
725
+ const fraction = (x: number): string => String(Math.round(x * 1e4) / 1e4 || 0);
726
+ for (const [e, controls] of loops) {
727
+ const v = snapshot.edgeSource(e);
728
+ const half = (property: string): number => {
729
+ const size = node[property]?.[v];
730
+ return (typeof size === "number" && size > 0 ? size : PAJEK_VERTEX_SIZE) / 2;
731
+ };
732
+ const [hw, hh] = [half("width"), half("height")];
733
+ const column = (property: string): (StyleValue | null)[] =>
734
+ (edge[property] ??= new Array<StyleValue | null>(snapshot.edgeCount).fill(null));
735
+ column("bends")[e] = controls.map(([x, y]) => `${fraction(x / hw)},${fraction(-y / hh)}`).join(" ");
736
+ // Export Line Default / Straight Lines straightens every other line; a loop is still drawn round
737
+ // (`pajek-specimen-edges-lines-p4`)
738
+ column("curved")[e] = true;
739
+ column("bendCurve")[e] = "bezier";
740
+ }
741
+ }
742
+
743
+ /**
744
+ * Where Pajek 6.02's SVG export draws a line's label (`pajek-specimen-edge-labels`): its middle at `lp` from the
745
+ * terminal vertex, a fraction of the line up to 1 (0.5 by default, 0.2 near the target) and a distance beyond it
746
+ * (`lp 20` 20 from the target), moved by `lr` at `lphi` degrees counterclockwise (`lr 12 lphi 90` 12 above), its
747
+ * baseline through that point, and turned along the line and `la` degrees counterclockwise from it (`la 45` drawn
748
+ * rotate(-45) on a level line; `la 405` as `la 45`). graphty hangs a line's label on the line's middle, so the place is
749
+ * an offset from there, the words' middle above the baseline in the label's own turned frame.
750
+ * @param get - the line's parameters
751
+ * @param put - receives the values
752
+ * @param at - the sites
753
+ * @param run - the line, or null when unplaced
754
+ * @param direction - the line's direction at the label, in degrees clockwise on screen, where `run` does not give it
755
+ * (a loop)
756
+ */
757
+ function readLineLabelPlace(get: Param, put: Put, at: At, run: Run | null, direction?: number): void {
758
+ const length = run === null ? 0 : Math.hypot(run[0], run[1]);
759
+ if (run === null || length === 0) {
760
+ return;
761
+ }
762
+ const n = (key: string, fallback: number, property: string): number | null => {
763
+ const v = get(key);
764
+ return v === null ? fallback : canonNumber(v, at(property));
765
+ };
766
+ const [lp, lr, lphi, la] = [
767
+ n("lp", 0.5, "labelOffsetX"),
768
+ n("lr", 0, "labelOffsetX"),
769
+ n("lphi", 0, "labelOffsetX"),
770
+ n("la", 0, "labelRotation"),
771
+ ];
772
+ const size = n("fos", PAJEK_LABEL_SIZE, "labelSize");
773
+ if (lp === null || lr === null || lphi === null || la === null || size === null) {
774
+ return;
775
+ }
776
+ // from the line's middle to the point lp from its target, then out by lr at lphi
777
+ const along = 0.5 - (lp <= 1 ? lp : lp / length);
778
+ const t = (lphi * Math.PI) / 180;
779
+ const round = (v: number): number => Number(v.toFixed(9)) + 0;
780
+ const turn = (((direction ?? lineDirection(get, run, along + 0.5)) - la) % 360) + 360;
781
+ // the baseline runs through the point; the words' middle is above it in the label's own turned frame
782
+ const up = (turn * Math.PI) / 180;
783
+ const rise = PAJEK_BASELINE_TO_MIDDLE * size;
784
+ put("labelPosition", "center");
785
+ put("labelOffsetX", round(along * run[0] + lr * Math.cos(t) + rise * Math.sin(up)));
786
+ put("labelOffsetY", round(along * run[1] - lr * Math.sin(t) - rise * Math.cos(up)));
787
+ put("labelRotation", round(turn % 360));
788
+ }
789
+
790
+ /**
791
+ * A line's Bezier route (manual p. 90: `a1`, `k1` at its source, `a2`, `k2` at its target), as Pajek 6.02's SVG export
792
+ * draws it (`pajek-specimen-edges-lines-p2`, `pajek-specimen-edge-labels`): one cubic Bezier curve whose first control
793
+ * point is `k1` / 2 of the line's length from the source, turned `a1` degrees clockwise on screen from the line, and
794
+ * whose second is `k2` / 2 of it back from the target, turned `a2` degrees counterclockwise (`a1 40 k1 1.5 a2 -40 k2
795
+ * 1.5` an S, `a2 40` a bow). A negative `k1` is one of Pajek's oval, circular arc or double routes, which its SVG export
796
+ * draws straight. A loop is `readLoop`'s. ponytail: a line that sets only some of the four reads the
797
+ * others as 0 and 1; Pajek's values for them were not measured.
798
+ * @param get - the line's parameters
799
+ * @param put - receives the values
800
+ * @param run - the line, or null when unplaced
801
+ */
802
+ function readBezier(get: Param, put: Put, run: Run | null): void {
803
+ const controls = bezierControls(get, run);
804
+ if (controls === null) {
805
+ return;
806
+ }
807
+ const fraction = (x: number): string => String(Math.round(x * 1e4) / 1e4 || 0);
808
+ put("curved", true);
809
+ put("bendCurve", "bezier");
810
+ put("bends", controls.map(([along, across]) => `${fraction(along)},${fraction(across)}`).join(" "));
811
+ }
812
+
813
+ /**
814
+ * 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
+ * center. It places the arrowhead as on a straight arc, its tip 1.5 short of where the straight line meets the
816
+ * target's outline, turns it `-a2` degrees about that point, and runs the curve from the source's center to the
817
+ * arrowhead's back, its control points measured on that shorter line (`pajek-specimen-edges-lines-p2`
818
+ * `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.
823
+ * @param get - the line's parameters
824
+ * @param put - receives the values
825
+ * @param run - the line from its source's center to its target's, in drawn units, y down
826
+ * @param target - the target vertex's width and height
827
+ */
828
+ function readArcEnd(get: Param, put: Put, run: Run, target: readonly [number, number]): void {
829
+ const ap = numberOrNull(get("ap")) ?? 0;
830
+ const length = Math.hypot(run[0], run[1]);
831
+ if (ap !== 0 || length === 0 || bezierControls(get, run) === null) {
832
+ return;
833
+ }
834
+ const [ux, uy] = [run[0] / length, run[1] / length];
835
+ // the straight line meets the outline r from the target's center; the arrowhead's back is 1.5 + 5 s before it
836
+ const r = 1 / Math.hypot(ux / (target[0] / 2), uy / (target[1] / 2));
837
+ const back = 1.5 + 5 * (numberOrNull(get("s")) ?? 1);
838
+ const turn = (-(numberOrNull(get("a2")) ?? 0) * Math.PI) / 180;
839
+ const [c, s] = [Math.cos(turn), Math.sin(turn)];
840
+ // the arrowhead's back, from the source's center
841
+ const end: Run = [run[0] - r * ux - back * (ux * c - uy * s), run[1] - r * uy - back * (ux * s + uy * c)];
842
+ const controls = bezierControls(get, end);
843
+ if (controls === null) {
844
+ return;
845
+ }
846
+ // each control point on screen from the source's center, then along and to the left of the line to the target
847
+ const L2 = length * length;
848
+ const fraction = (x: number): string => String(Math.round(x * 1e4) / 1e4 || 0);
849
+ const bends = controls.map(([along, left]) => {
850
+ const [x, y] = [along * end[0] + left * end[1], along * end[1] - left * end[0]];
851
+ return `${fraction((x * run[0] + y * run[1]) / L2)},${fraction((x * run[1] - y * run[0]) / L2)}`;
852
+ });
853
+ put("bends", bends.join(" "));
854
+ }
855
+
856
+ /**
857
+ * A line's two Bezier control points (`readBezier`), each `[along, across]` as fractions of the line from its source:
858
+ * along it, and to its left on screen.
859
+ * @param get - the line's parameters
860
+ * @param run - the line, or null when unplaced
861
+ * @returns the points, or null for a line Pajek draws straight or a loop
862
+ */
863
+ function bezierControls(get: Param, run: Run | null): [[number, number], [number, number]] | null {
864
+ const [a1, k1, a2, k2] = ["a1", "k1", "a2", "k2"].map((key) => numberOrNull(get(key)));
865
+ if ((a1 === null && k1 === null && a2 === null && k2 === null) || run === null || (run[0] === 0 && run[1] === 0)) {
866
+ return null;
867
+ }
868
+ const [s, t] = [k1 ?? 1, k2 ?? 1];
869
+ if (s < 0 || t < 0) {
870
+ return null;
871
+ }
872
+ const rad = Math.PI / 180;
873
+ const [u, v] = [(a1 ?? 0) * rad, (a2 ?? 0) * rad];
874
+ return [
875
+ [(s / 2) * Math.cos(u), (-s / 2) * Math.sin(u)],
876
+ [1 - (t / 2) * Math.cos(v), (-t / 2) * Math.sin(v)],
877
+ ];
878
+ }
879
+
880
+ /**
881
+ * A line's direction on screen, in degrees clockwise, where its label is drawn: along a straight line, or along its
882
+ * Bezier curve (`bezierControls`) at the label's place, `along` of the way from the source.
883
+ * @param get - the line's parameters
884
+ * @param run - the line
885
+ * @param along - the label's place, 0 at the source and 1 at the target
886
+ * @returns the direction
887
+ */
888
+ function lineDirection(get: Param, run: Run, along: number): number {
889
+ const controls = bezierControls(get, run);
890
+ let [dx, dy] = run;
891
+ if (controls !== null) {
892
+ // the cubic's tangent at `along`, in the line's own frame (along, left), then on screen
893
+ const t = Math.min(1, Math.max(0, along));
894
+ const p = [[0, 0], ...controls, [1, 0]];
895
+ const d = (k: 0 | 1): number =>
896
+ 3 * (1 - t) ** 2 * (p[1][k] - p[0][k]) +
897
+ 6 * (1 - t) * t * (p[2][k] - p[1][k]) +
898
+ 3 * t ** 2 * (p[3][k] - p[2][k]);
899
+ const [a, l] = [d(0), d(1)];
900
+ // left of (dx, dy) on screen, y down, is (dy, -dx)
901
+ [dx, dy] = [a * run[0] + l * run[1], a * run[1] - l * run[0]];
902
+ }
903
+ return (Math.atan2(dy, dx) * 180) / Math.PI;
904
+ }
905
+
906
+ /**
907
+ * What a line's value draws (manual 5.3 p. 90): a line of value 0 is not drawn, and a negative one is drawn as Dots
908
+ * unless the line sets a pattern.
909
+ * @param value - the line's value, or null
910
+ * @param get - the line's parameters
911
+ * @param put - receives the values
912
+ * @param directed - whether the line is an arc
913
+ */
914
+ function readLineValue(value: number | null, get: Param, put: Put, directed = true): void {
915
+ if (value === 0) {
916
+ put("visible", false);
917
+ } else if (value !== null && value < 0 && get("p") === null) {
918
+ // drawn as Dots are (`pajek-specimen-edges-lines` `value-negative`)
919
+ put("lineStyle", pajekDash(get, directed));
920
+ }
921
+ }
922
+
923
+ /** The dash and the gap of Pajek 6.02's Dots, in px whatever the line's width (`stroke-dasharray:5`). */
924
+ const PAJEK_DASH = 5;
925
+
926
+ /**
927
+ * How a line drawn as Pajek's Dots looks: Pajek 6.02's SVG export draws it `stroke-dasharray:5`, dashes and gaps 5 px
928
+ * long whatever the line's width, so a line 10/3 px wide or wider, its dashes no longer than one and a half widths,
929
+ * shows dots and a thinner one dashes (`pajek-specimen-edges-lines` `p-dots` at 1.5: dashes; `pajek-synth-lines`
930
+ * arc `12 13` at w 5 and edge `1 8` at w 3, drawn 6 wide: dots).
931
+ * @param get - the line's parameters
932
+ * @param directed - whether the line is an arc, drawn `w` wide (an edge twice `w`)
933
+ * @returns the line style
934
+ */
935
+ function pajekDash(get: Param, directed: boolean): "dashed" | "dotted" {
936
+ const w = numberOrNull(get("w"));
937
+ // without a `w`, Pajek's arc is 1 wide and its edge 2
938
+ const width = (w ?? 1) * (directed ? 1 : EDGE_WIDTH_PER_W);
939
+ return PAJEK_DASH <= 1.5 * Math.max(1, width) ? "dotted" : "dashed";
940
+ }
941
+
942
+ /**
943
+ * A line's hooks `h1` and `h2` (manual p. 90): the points of its source's and its target's outline it runs between,
944
+ * numbered by Pajek's shipped SHAPES.CFG for the vertex's shape. An ellipse's hook k is at k degrees counterclockwise
945
+ * from the right (CIRC `10 1`), a box's runs round its border in 400 steps from the middle of its right side,
946
+ * counterclockwise (CART), and a cross, which Pajek draws as an ellipse, has an ellipse's; `pajek-specimen-edges-lines-p2`:
947
+ * `h1 90 h2 270` runs from the top of the source to the bottom of the target, `h1 1 h2 1` ends on the far side of the
948
+ * target, `h1 1` on a box leaves at its right side. 0 (the center) and -1 (the nearest hook) are where graphty runs a
949
+ * line anyway. ponytail: the hooks of the other shapes (a diamond's 360 polar radii, a triangle's, a figure's) are
950
+ * not read; Pajek's box hook 100 was measured 0.22 of the half width left of the top's middle, where SHAPES.CFG puts
951
+ * it.
952
+ * @param get - the line's parameters
953
+ * @param put - receives the values
954
+ * @param source - the source vertex's shape keyword, or null
955
+ * @param target - the target vertex's shape keyword, or null
956
+ */
957
+ function readHooks(get: Param, put: Put, source: unknown, target: unknown): void {
958
+ for (const [key, property, shape] of [
959
+ ["h1", "sourcePort", source],
960
+ ["h2", "targetPort", target],
961
+ ] as const) {
962
+ const hook = numberOrNull(get(key));
963
+ const port = hook === null ? null : hookPort(shape, hook);
964
+ if (port !== null) {
965
+ const round = (v: number): string => String(Number(v.toFixed(4)) + 0);
966
+ put(property, `${round(port[0])},${round(port[1])}`);
967
+ }
968
+ }
969
+ }
970
+
971
+ /**
972
+ * One hook's point on its vertex (`readHooks`), as fractions of the half width and height, y up.
973
+ * @param shape - the vertex's shape keyword, null for Pajek's default ellipse
974
+ * @param hook - the hook's number
975
+ * @returns the point, or null where Pajek's hook is not read
976
+ */
977
+ function hookPort(shape: unknown, hook: number): readonly [number, number] | null {
978
+ if (!Number.isInteger(hook) || hook < 1) {
979
+ return null;
980
+ }
981
+ const keyword = typeof shape === "string" ? shape.toLowerCase() : "ellipse";
982
+ // Pajek 6.02 draws a cross as an ellipse, with an ellipse's hooks (`h-cross-1-3`: hook 3 at 3 degrees)
983
+ if (keyword === "ellipse" || keyword === "cross") {
984
+ const a = (hook * Math.PI) / 180;
985
+ return [Math.cos(a), Math.sin(a)];
986
+ }
987
+ if (keyword !== "box" || hook > 400) {
988
+ return null;
989
+ }
990
+ // 0.02 of the half size a step: up the right side's top half, across the top, down the left, across the
991
+ // bottom and up the right side's bottom half
992
+ const d = hook * 0.02;
993
+ if (hook <= 50) {
994
+ return [1, d];
995
+ }
996
+ if (hook <= 150) {
997
+ return [2 - d, 1];
998
+ }
999
+ if (hook <= 250) {
1000
+ return [-1, 4 - d];
1001
+ }
1002
+ return hook <= 350 ? [d - 6, -1] : [1, d - 8];
1003
+ }
1004
+
1005
+ /**
1006
+ * A cell's number: a number as is, a numeric text parsed, anything else null.
1007
+ * @param raw - the cell
1008
+ * @returns the number, or null
1009
+ */
1010
+ function numberOrNull(raw: unknown): number | null {
1011
+ if (typeof raw === "string" && raw.trim() !== "") {
1012
+ raw = Number(raw);
1013
+ }
1014
+ return typeof raw === "number" && Number.isFinite(raw) ? raw : null;
1015
+ }
1016
+
1017
+ /**
1018
+ * 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.
1020
+ * @param raw - the parameter's value, or null
1021
+ * @param put - receives the value
1022
+ * @param at - the sites
1023
+ */
1024
+ function readArrowPosition(raw: unknown, put: Put, at: At): void {
1025
+ if (raw === null) {
1026
+ return;
1027
+ }
1028
+ const ap = canonNumber(raw, at("arrowPosition"));
1029
+ if (ap !== null && ap > 1) {
1030
+ noteAt(at("arrowPosition"), STYLE_ISSUE.NOT_CARRIED, `ap ${String(ap)}`);
1031
+ return;
1032
+ }
1033
+ // 0 is Pajek's own place for the arrow, at the line's end, which a line restates after one that moved it
1034
+ if (ap !== 0) {
1035
+ put("arrowPosition", ap);
182
1036
  }
183
1037
  }
184
1038
 
@@ -189,9 +1043,21 @@ function readLine(get: Param, put: Put, at: At): void {
189
1043
  * @returns the getter
190
1044
  */
191
1045
  function cells(table: GraphSnapshot["nodes"], i: number): Param {
192
- return (key) => (table.has(key) ? plainCell(table.value(key, i)) : null);
1046
+ return (key) => {
1047
+ const v = table.has(key) ? plainCell(table.value(key, i)) : null;
1048
+ // a color written as numbers (`c 0.700 0.700 0.700`) is no color to Pajek 6.02: the line keeps the color it
1049
+ // carries from the line before, or Pajek's default (`pajek-1crn`, `pajek-specimen-edges-lines`
1050
+ // `c-three-fractions`). ponytail: measured on lines; vertex colors are read the same way
1051
+ return COLOR_PARAMS.has(key) && typeof v === "string" && NUMERIC_COLOR.test(v) ? null : v;
1052
+ };
193
1053
  }
194
1054
 
1055
+ /** The parameters that hold a color. */
1056
+ const COLOR_PARAMS: ReadonlySet<string> = new Set(["ic", "bc", "lc", "c"]);
1057
+
1058
+ /** A color written as numbers rather than a name or an RGB / CMYK form. */
1059
+ const NUMERIC_COLOR = /^[\d.]+(?: [\d.]+)*$/;
1060
+
195
1061
  /**
196
1062
  * Whether each edge is an arc (directed).
197
1063
  * @param snapshot - the graph
@@ -219,41 +1085,90 @@ function styleColumns(table: GraphSnapshot["nodes"], keys: readonly string[]): s
219
1085
  }
220
1086
 
221
1087
  /**
222
- * The styles of a Pajek file, read the way Pajek draws them, with Pajek's stated defaults beneath: a labeled ellipse
223
- * with a black border and a black centered label, black solid lines, and an arrow at the target of every arc. A file graphty wrote gives back its `graphty.styles` block.
1088
+ * The styles of a Pajek file, read the way Pajek draws them, with Pajek's own defaults beneath: a labeled #ffd9bc
1089
+ * ellipse with a #660000 border, teal edges, and grey arcs with an arrow at the target. A file graphty wrote gives back its `graphty.styles` block.
224
1090
  * @param snapshot - the graph the import built
225
1091
  * @param context - the work budget and the block text the import kept
226
1092
  * @returns the styles
227
1093
  */
228
1094
  export function pajekStyles(snapshot: GraphSnapshot, context: StyleReadContext): ImporterStyles {
229
1095
  const notes = new StyleNotes();
1096
+ const { block, draw, voided } = keptOf(context.kept);
1097
+ const labels = drawsLineLabels(draw);
1098
+ const place = places(snapshot);
1099
+ // Pajek stretches the file's 0..1 to its Draw window one axis at a time, 500 x 500 without one
1100
+ const window = draw ?? { width: PAJEK_POINTS_PER_UNIT, height: PAJEK_POINTS_PER_UNIT };
1101
+ const loops = new Map<number, LoopControls>();
1102
+ const runOf = (e: number): Run | null => {
1103
+ const [s, t] = [place(snapshot.edgeSource(e)), place(snapshot.edgeTarget(e))];
1104
+ return s === null || t === null ? null : [(t[0] - s[0]) * window.width, (t[1] - s[1]) * window.height];
1105
+ };
230
1106
  const read = (target: StyleTarget): ValueColumns => {
231
1107
  const table = target === "node" ? snapshot.nodes : snapshot.edges;
232
1108
  const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
233
1109
  const out = valueColumns(n);
1110
+ const carry = carriedParams(target === "node" ? CARRIED.node : CARRIED_LINE, target === "node");
234
1111
  for (let i = 0; i < n; i++) {
235
1112
  context.budget.spend(target, 1);
236
1113
  const at: At = (property) => ({ notes, target, property, element: i, budget: context.budget });
237
- (target === "node" ? readVertex : readLine)(
238
- cells(table, i),
239
- (property, value) => {
240
- out.put(i, property, value);
241
- },
242
- at,
243
- );
1114
+ const put: Put = (property, value) => {
1115
+ out.put(i, property, value);
1116
+ };
1117
+ const own = cells(table, i);
1118
+ const get = target === "node" && voided.has(i) ? voidedParams(own, keywords[i - 1] ?? null) : carry(own);
1119
+ if (target === "node") {
1120
+ readVertex(get, put, at, place(i));
1121
+ keywords[i] = get(SHAPE_COLUMN);
1122
+ } else {
1123
+ readEdge(i, get, put, at);
1124
+ }
244
1125
  }
245
1126
  return out.values;
246
1127
  };
1128
+ const readEdge = (i: number, get: Param, put: Put, at: At): void => {
1129
+ // a loop on a placed node: an unplaced one is laid out by graphty, which draws its own loop, and a
1130
+ // file graphty wrote keeps its routes in its block
1131
+ const run = runOf(i);
1132
+ const loop =
1133
+ run !== null && block === null && snapshot.edgeSource(i) === snapshot.edgeTarget(i)
1134
+ ? (controls: LoopControls): void => {
1135
+ loops.set(i, controls);
1136
+ }
1137
+ : 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)));
1141
+ }
1142
+ readLineValue(lineValue(i), get, put, directed[i]);
1143
+ readHooks(get, put, keywords[snapshot.edgeSource(i)], keywords[snapshot.edgeTarget(i)]);
1144
+ };
1145
+ const directed = directedEdges(snapshot);
1146
+ const keywords: unknown[] = [];
1147
+ const weights = explicitWeights(snapshot);
1148
+ // the line's value: its weight, or the `value` column when the import kept the value as an attribute
1149
+ const lineValue = (e: number): number | null =>
1150
+ weights.isExplicit(e) ? weights.value(e) : numberOrNull(cells(snapshot.edges, e)(VALUE_COLUMN));
247
1151
  const node = read("node");
1152
+ // a vertex's width and height as the file draws it, for where an arc's arrowhead meets it
1153
+ const targetSize = (v: number): readonly [number, number] => {
1154
+ const size = (property: string): number => {
1155
+ const s = node[property]?.[v];
1156
+ return typeof s === "number" && s > 0 ? s : PAJEK_VERTEX_SIZE;
1157
+ };
1158
+ return [size("width"), size("height")];
1159
+ };
248
1160
  const edge = read("edge");
249
- const block = typeof context.kept === "string" ? context.kept : null;
1161
+ if (draw !== null) {
1162
+ drawVertices(draw, snapshot, node);
1163
+ drawLines(draw, snapshot, node, edge);
1164
+ }
1165
+ layLoops(snapshot, loops, node, edge);
250
1166
  if (block !== null) {
251
- exactFromBlock(snapshot, block, { node, edge });
1167
+ exactFromBlock(snapshot, block, { node, edge }, labels);
252
1168
  }
253
- // Pajek's default that is a rule: an arc has an arrow at its target, an edge none
1169
+ // Pajek's defaults that are a rule: an arc has an arrow at its target and is drawn grey and thinner, an edge not
254
1170
  const rules: StyleRule[] = [];
255
1171
  const columns = new Map<string, StyleColumn>();
256
- const directed = directedEdges(snapshot);
257
1172
  if (directed.some(Boolean)) {
258
1173
  columns.set(
259
1174
  DIRECTED_KEY,
@@ -265,14 +1180,14 @@ export function pajekStyles(snapshot: GraphSnapshot, context: StyleReadContext):
265
1180
  role: "group",
266
1181
  origin: "tool",
267
1182
  where: { has: { style: DIRECTED_KEY } },
268
- set: { targetArrow: "triangle" },
1183
+ set: { targetArrow: "triangle", color: ARC_DEFAULTS.color, width: ARC_DEFAULTS.width },
269
1184
  });
270
1185
  }
271
1186
  return {
272
1187
  canonical: true,
273
1188
  dialect: "dvips",
274
1189
  tool: "pajek",
275
- units: "relative",
1190
+ units: "px",
276
1191
  node: { values: node },
277
1192
  edge: { values: edge, rules, columns },
278
1193
  notes: notes.list(),
@@ -284,6 +1199,67 @@ export function pajekStyles(snapshot: GraphSnapshot, context: StyleReadContext):
284
1199
  };
285
1200
  }
286
1201
 
1202
+ /**
1203
+ * Each vertex's place in the file's own coordinates, as the position column holds them.
1204
+ * @param snapshot - the graph
1205
+ * @returns a getter of a vertex's place, null when it has none
1206
+ */
1207
+ function places(snapshot: GraphSnapshot): (i: number) => Place | null {
1208
+ const col = snapshot.nodes.byRole("position");
1209
+ return (i) => {
1210
+ const v: unknown = col === null || col === undefined ? null : col.value(i);
1211
+ if (v === null || typeof v !== "object" || !("length" in v)) {
1212
+ return null;
1213
+ }
1214
+ const [x, y] = v as ArrayLike<unknown> as [unknown, unknown];
1215
+ return typeof x === "number" && typeof y === "number" ? [x, y] : null;
1216
+ };
1217
+ }
1218
+
1219
+ /**
1220
+ * What the import kept for the styles: the `graphty.styles` block, the Draw window of the `pajekDraw` option, and the
1221
+ * vertices whose line Pajek draws with none of its parameters.
1222
+ * @param kept - the kept value: the block's text, or `{ block, pajekDraw, voided }`
1223
+ * @returns the block and the draw settings, each null when absent, and the voided vertices
1224
+ */
1225
+ function keptOf(kept: unknown): {
1226
+ readonly block: string | null;
1227
+ readonly draw: PajekDraw | null;
1228
+ readonly voided: ReadonlySet<number>;
1229
+ } {
1230
+ if (typeof kept === "string") {
1231
+ return { block: kept, draw: null, voided: new Set() };
1232
+ }
1233
+ if (typeof kept === "object" && kept !== null) {
1234
+ const { block, pajekDraw, voided } = kept as { block?: unknown; pajekDraw?: PajekDraw; voided?: number[] };
1235
+ return {
1236
+ block: typeof block === "string" ? block : null,
1237
+ draw: pajekDraw ?? null,
1238
+ voided: new Set(voided ?? []),
1239
+ };
1240
+ }
1241
+ return { block: null, draw: null, voided: new Set() };
1242
+ }
1243
+
1244
+ /**
1245
+ * The parameters Pajek 6.02 draws a voided vertex line with (`VOIDING_KEYS` in the importer): its label, and none of
1246
+ * its own parameters, so the vertex is Pajek's default size and colors, in the shape of the vertex line before it
1247
+ * (`pajek-specimen-node-sizes-opacity` `s-size-no-shape`, after a box: a box 20 across in #ffd9bc). ponytail: probes
1248
+ * showed the shape is not always the line before's (after `box ic Green` a voided line drew an ellipse in one file and
1249
+ * the triangle of two lines before in another); the rule Pajek follows was not found.
1250
+ * @param own - the line's own parameters
1251
+ * @param before - the shape keyword of the vertex line before it, or null
1252
+ * @returns the getter
1253
+ */
1254
+ function voidedParams(own: Param, before: unknown): Param {
1255
+ return (key) => {
1256
+ if (key === LABEL_COLUMN) {
1257
+ return own(key);
1258
+ }
1259
+ return key === SHAPE_COLUMN ? before : null;
1260
+ };
1261
+ }
1262
+
287
1263
  // ============================================================ writing
288
1264
 
289
1265
  /** One element's value of a property. */
@@ -360,13 +1336,62 @@ function shapeKeyword(get: Get, approximate: Approximate): string {
360
1336
  if (keyword !== undefined) {
361
1337
  return keyword;
362
1338
  }
1339
+ if (shape === "polygon" && diamondQ(get) !== null) {
1340
+ return "diamond";
1341
+ }
363
1342
  approximate("shape", shape);
1343
+ const points = get("shapePoints");
1344
+ if (shape === "polygon" && points !== null) {
1345
+ approximate("shapePoints", points);
1346
+ }
364
1347
  return NEAREST_SHAPES.get(shape) ?? "ellipse";
365
1348
  }
366
1349
 
367
1350
  /**
368
- * A vertex's parameters from its values, leaving out what Pajek draws by default: an ellipse with a black border 1
369
- * wide and a black label.
1351
+ * The `q` of a polygon that is Pajek's unturned diamond (`readOutline`), or null for any other.
1352
+ * @param get - the vertex's values
1353
+ * @returns the diamond's `q`, or null
1354
+ */
1355
+ function diamondQ(get: Get): number | null {
1356
+ const points = get("shapePoints");
1357
+ const corners =
1358
+ typeof points === "string"
1359
+ ? points
1360
+ .trim()
1361
+ .split(/\s+/)
1362
+ .map((p) => p.split(",").map(Number))
1363
+ : [];
1364
+ if (corners.length !== 6) {
1365
+ return null;
1366
+ }
1367
+ const q = corners[2][0];
1368
+ const want = [
1369
+ [-1, 0],
1370
+ [-q, 1],
1371
+ [q, 1],
1372
+ [1, 0],
1373
+ [q, -1],
1374
+ [-q, -1],
1375
+ ];
1376
+ return want.every(([x, y], k) => corners[k][0] === x && corners[k][1] === y) ? q : null;
1377
+ }
1378
+
1379
+ /**
1380
+ * What Pajek draws by default for a property (`styles/defaults.ts`), which the writer leaves out.
1381
+ * @param target - nodes or edges
1382
+ * @param property - the property
1383
+ * @returns the default, or undefined
1384
+ */
1385
+ function pajekDefault(target: StyleTarget, property: string): StyleValue | undefined {
1386
+ const values: Readonly<Record<string, { readonly value: unknown } | undefined>> | undefined =
1387
+ toolDefaults("pajek")?.[target];
1388
+ return values?.[property]?.value as StyleValue | undefined;
1389
+ }
1390
+
1391
+ /**
1392
+ * A vertex's parameters from its values, leaving out the ellipse and the black border 1 wide Pajek draws by default;
1393
+ * the inverse of `readVertex`: `x_fact` and `y_fact` are the width and height over the normal size, the other lengths
1394
+ * points as they are.
370
1395
  * @param get - the vertex's values
371
1396
  * @param approximate - records approximations
372
1397
  * @returns the parameters, the shape keyword under `shape`
@@ -378,43 +1403,148 @@ function vertexParams(get: Get, approximate: Approximate): Map<string, Written>
378
1403
  if (keyword !== "ellipse") {
379
1404
  out.set(SHAPE_COLUMN, keyword);
380
1405
  }
1406
+ const q = diamondQ(get);
1407
+ if (q !== null && q !== PAJEK_DIAMOND_Q) {
1408
+ out.set("q", q);
1409
+ }
1410
+ vertexColorParams(get, out);
1411
+ vertexSizeParams(get, out);
1412
+ const bw = get("borderWidth");
1413
+ if (typeof bw === "number" && bw !== pajekDefault("node", "borderWidth")) {
1414
+ out.set("bw", bw);
1415
+ }
1416
+ fontParam(get, out, pajekDefault("node", "labelFont"));
1417
+ labelPositionParams(get, out, approximate);
1418
+ return out;
1419
+ }
1420
+
1421
+ /**
1422
+ * A vertex's colors as parameters (`ic`, `bc`, `lc`), leaving out Pajek's defaults.
1423
+ * @param get - the vertex's values
1424
+ * @param out - receives the parameters
1425
+ */
1426
+ function vertexColorParams(get: Get, out: Map<string, Written>): void {
381
1427
  const colors = [
382
1428
  ["fill", "ic"],
383
1429
  ["borderColor", "bc"],
384
1430
  ["labelColor", "lc"],
385
1431
  ] as const;
386
- const numbers = [
387
- ["width", "x_fact"],
388
- ["height", "y_fact"],
389
- ["labelSize", "fos"],
390
- ] as const;
391
1432
  for (const [property, key] of colors) {
392
1433
  const v = get(property);
393
- if (typeof v === "string" && (property === "fill" || v !== "#000000")) {
1434
+ if (typeof v === "string" && v !== pajekDefault("node", property)) {
394
1435
  out.set(key, pajekColorText(v));
395
1436
  }
396
1437
  }
397
- for (const [property, key] of numbers) {
1438
+ }
1439
+
1440
+ /**
1441
+ * A vertex's sizes as parameters (`x_fact`, `y_fact`, `fos`), leaving out Pajek's defaults.
1442
+ * @param get - the vertex's values
1443
+ * @param out - receives the parameters
1444
+ */
1445
+ function vertexSizeParams(get: Get, out: Map<string, Written>): void {
1446
+ const numbers = [
1447
+ ["width", "x_fact", PAJEK_VERTEX_SIZE],
1448
+ ["height", "y_fact", PAJEK_VERTEX_SIZE],
1449
+ ["labelSize", "fos", 1],
1450
+ ] as const;
1451
+ // a man's or a woman's box is the figure's, which reaches past the size its x_fact and y_fact give (readSize)
1452
+ const figure = PAJEK_FIGURES.get(String(get("shape")));
1453
+ for (const [property, key, per] of numbers) {
398
1454
  const v = get(property);
399
- if (typeof v === "number") {
400
- out.set(key, v);
1455
+ let over = 1;
1456
+ if (figure !== undefined && key !== "fos") {
1457
+ over = figure[key === "x_fact" ? 0 : 1];
1458
+ }
1459
+ if (typeof v === "number" && (over !== 1 || v !== pajekDefault("node", property))) {
1460
+ out.set(key, Number((v / (per * over)).toPrecision(15)));
401
1461
  }
402
1462
  }
403
- const bw = get("borderWidth");
404
- if (typeof bw === "number") {
405
- const written = Number((bw * VERTEX_SIZE).toPrecision(15));
406
- if (written === 1) {
407
- out.delete("bw");
408
- } else {
409
- out.set("bw", written);
1463
+ }
1464
+
1465
+ /**
1466
+ * A label's `font`, the inverse of `readFont`: a PostScript family with a generic one after it is written by its
1467
+ * PostScript name (`Courier, monospace` as `Courier`), any other family as it is; Pajek draws every label bold and
1468
+ * upright, so neither the weight nor the slant is written.
1469
+ * @param get - the element's values
1470
+ * @param out - the parameters, changed in place
1471
+ * @param skip - the font left out, Pajek's default, or undefined
1472
+ */
1473
+ function fontParam(get: Get, out: Map<string, Written>, skip?: StyleValue): void {
1474
+ const font = get("labelFont");
1475
+ const name = typeof font === "string" && font !== "" ? graphvizFontName(font, false, false) : null;
1476
+ if (name !== null && name !== skip) {
1477
+ out.set("font", name);
1478
+ }
1479
+ }
1480
+
1481
+ /**
1482
+ * A label position as Pajek's polar label parameters, leaving out the default (20 from the center at 285 degrees): a
1483
+ * label centered with its left edge's middle offset from the center is `lr` and `lphi` (the inverse of
1484
+ * `readLabelPosition`), one centered on the vertex `lr 0`, and a side its angle `lphi`; a side Pajek cannot put a
1485
+ * label on is written as the nearest it can, and noted.
1486
+ * @param get - the vertex's values
1487
+ * @param out - the parameters, changed in place
1488
+ * @param approximate - records approximations
1489
+ */
1490
+ function labelPositionParams(get: Get, out: Map<string, Written>, approximate: Approximate): void {
1491
+ const position = get("labelPosition");
1492
+ if (typeof position !== "string") {
1493
+ return;
1494
+ }
1495
+ if (position === "center") {
1496
+ const polar = polarOf(get);
1497
+ if (polar === null) {
1498
+ out.set("lr", 0);
1499
+ } else if (polar[0] !== PAJEK_LABEL_RADIUS || polar[1] !== PAJEK_LABEL_ANGLE) {
1500
+ out.set("lr", polar[0]);
1501
+ out.set("lphi", polar[1]);
410
1502
  }
1503
+ return;
411
1504
  }
412
- return out;
1505
+ const nearest = NEAREST_SIDES.get(position);
1506
+ if (nearest !== undefined) {
1507
+ approximate("labelPosition", position);
1508
+ }
1509
+ const angle = SIDE_ANGLES.get(nearest ?? position);
1510
+ if (angle !== undefined && angle !== PAJEK_LABEL_ANGLE) {
1511
+ out.set("lphi", angle);
1512
+ }
1513
+ }
1514
+
1515
+ /**
1516
+ * The `lr` and `lphi` that put a centered label where its offsets say (`labelOffset`, inverted), 6 places each.
1517
+ * @param get - the vertex's values
1518
+ * @returns `[lr, lphi]`, or null for a label on the vertex's center (no offset, no shift)
1519
+ */
1520
+ function polarOf(get: Get): [number, number] | null {
1521
+ const [x, y, shift] = [get("labelOffsetX"), get("labelOffsetY"), get("labelShiftX")];
1522
+ if (typeof x !== "number" || typeof y !== "number" || shift !== 0.5) {
1523
+ return null;
1524
+ }
1525
+ const size = get("labelSize");
1526
+ const up = y + PAJEK_BASELINE_TO_MIDDLE * (typeof size === "number" ? size : PAJEK_LABEL_SIZE);
1527
+ const round = (v: number): number => Number(v.toFixed(6)) + 0;
1528
+ const lr = round(Math.hypot(x, up));
1529
+ return lr === 0 ? null : [lr, round(((((Math.atan2(-up, x) * 180) / Math.PI) % 360) + 360) % 360)];
413
1530
  }
414
1531
 
415
1532
  /**
416
- * A line's parameters from its values, leaving out Pajek's black solid line. Arrows are Pajek's own: an arc has one
417
- * at its target and an edge none, so any other arrow is noted.
1533
+ * A line's color and width when it sets neither: an arc's, or an edge's (the tool defaults).
1534
+ * @param directed - whether the line is an arc
1535
+ * @returns the color and width
1536
+ */
1537
+ function lineDefaults(directed: boolean): {
1538
+ readonly color: StyleValue | undefined;
1539
+ readonly width: StyleValue | undefined;
1540
+ } {
1541
+ return directed ? ARC_DEFAULTS : { color: pajekDefault("edge", "color"), width: pajekDefault("edge", "width") };
1542
+ }
1543
+
1544
+ /**
1545
+ * A line's parameters from its values, leaving out the solid line Pajek draws by default (grey and 1 wide for an arc,
1546
+ * teal and 2 wide for an edge; `w` is half an edge's width). Arrows are Pajek's own: an arc has one at its target and
1547
+ * an edge none, so any other arrow is noted.
418
1548
  * @param get - the line's values
419
1549
  * @param directed - whether the line is an arc
420
1550
  * @param approximate - records approximations
@@ -423,18 +1553,24 @@ function vertexParams(get: Get, approximate: Approximate): Map<string, Written>
423
1553
  function lineParams(get: Get, directed: boolean, approximate: Approximate): Map<string, Written> {
424
1554
  const out = new Map<string, Written>();
425
1555
  opaque(get, ["opacity"], approximate);
1556
+ const defaults = lineDefaults(directed);
426
1557
  const color = get("color");
427
- if (typeof color === "string" && color !== "#000000") {
1558
+ if (typeof color === "string" && color !== defaults.color) {
428
1559
  out.set("c", pajekColorText(color));
429
1560
  }
430
1561
  const width = get("width");
431
- if (typeof width === "number") {
432
- out.set("w", width);
1562
+ if (typeof width === "number" && width !== defaults.width) {
1563
+ out.set("w", directed ? width : Number((width / EDGE_WIDTH_PER_W).toPrecision(15)));
433
1564
  }
434
1565
  const lineStyle = get("lineStyle");
435
- if (typeof lineStyle === "string" && lineStyle !== "solid" && pajekDots(lineStyle, approximate)) {
1566
+ if (typeof lineStyle === "string" && lineStyle !== "solid" && pajekDots(lineStyle, out, directed, approximate)) {
436
1567
  out.set("p", "Dots");
437
1568
  }
1569
+ const position = get("arrowPosition");
1570
+ if (typeof position === "number" && position > 0) {
1571
+ out.set("ap", position);
1572
+ }
1573
+ lineLabelParams(get, out, approximate);
438
1574
  const target = get("targetArrow");
439
1575
  if (target !== null && target !== (directed ? "triangle" : "none")) {
440
1576
  approximate("targetArrow", target);
@@ -447,84 +1583,100 @@ function lineParams(get: Get, directed: boolean, approximate: Approximate): Map<
447
1583
  }
448
1584
 
449
1585
  /**
450
- * Whether a line other than a solid one is written as Pajek's Dots: Pajek draws Solid and Dots only; a dashed kind is
451
- * nearest to Dots, any other line to Solid.
452
- * @param lineStyle - the line style
1586
+ * A line's label parameters: its text `l` (a line break or a double quote written as Pajek can hold it, noted), its
1587
+ * color `lc`, size `fos` and font `font`.
1588
+ * @param get - the line's values
1589
+ * @param out - the parameters, changed in place
453
1590
  * @param approximate - records approximations
454
- * @returns true for Dots
455
1591
  */
456
- function pajekDots(lineStyle: string, approximate: Approximate): boolean {
457
- if (lineStyle === "dotted") {
458
- return true;
1592
+ function lineLabelParams(get: Get, out: Map<string, Written>, approximate: Approximate): void {
1593
+ const label = get("label");
1594
+ if (typeof label === "string" && label !== "") {
1595
+ const text = pajekLabelText(label);
1596
+ if (text !== label) {
1597
+ approximate("label", label);
1598
+ }
1599
+ out.set("l", text);
459
1600
  }
460
- approximate("lineStyle", lineStyle);
461
- return lineStyle === "dashed" || lineStyle === "dash-dot" || lineStyle === "long-dash";
1601
+ const labelColor = get("labelColor");
1602
+ if (typeof labelColor === "string") {
1603
+ out.set("lc", pajekColorText(labelColor));
1604
+ }
1605
+ const labelSize = get("labelSize");
1606
+ if (typeof labelSize === "number") {
1607
+ out.set("fos", labelSize);
1608
+ }
1609
+ fontParam(get, out);
462
1610
  }
463
1611
 
464
1612
  /**
465
- * Each element's values of the properties the writer writes, as a getter per element.
466
- * @param picture - each element's value of a property
467
- * @param target - nodes or edges
468
- * @returns the getter
1613
+ * Whether a line other than a solid one is written as Pajek's Dots: Pajek draws Solid and Dots only, and its Dots
1614
+ * shows dots on a wide line and dashes on a thin one (`pajekDash`); the other of the two and any other dashed kind is
1615
+ * nearest to Dots, any other line to Solid.
1616
+ * @param lineStyle - the line style
1617
+ * @param params - the line's parameters written so far, its `w` among them
1618
+ * @param directed - whether the line is an arc
1619
+ * @param approximate - records approximations
1620
+ * @returns true for Dots
469
1621
  */
470
- function getter(
471
- picture: (target: StyleTarget, property: string) => readonly (StyleValue | null)[],
472
- target: StyleTarget,
473
- ): (i: number) => Get {
474
- const columns = new Map<string, readonly (StyleValue | null)[]>();
475
- for (const p of PAJEK_STYLE_CAPABILITIES[target]) {
476
- columns.set(p, picture(target, p));
1622
+ function pajekDots(
1623
+ lineStyle: string,
1624
+ params: ReadonlyMap<string, Written>,
1625
+ directed: boolean,
1626
+ approximate: Approximate,
1627
+ ): boolean {
1628
+ if (lineStyle === pajekDash((key) => params.get(key) ?? null, directed)) {
1629
+ return true;
477
1630
  }
478
- return (i) => (property) => columns.get(property)?.[i] ?? null;
1631
+ approximate("lineStyle", lineStyle);
1632
+ return ["dashed", "dotted", "dash-dot", "long-dash"].includes(lineStyle);
479
1633
  }
480
1634
 
481
1635
  /**
482
1636
  * For a file graphty wrote: where a value read from the parameters is what the writer writes for the block's own
483
- * value (a hexagon written as an ellipse, a dashed line as Dots), the block's exact value is the truth, so a re-import
1637
+ * value (a hexagon written as an ellipse, a dotted line as Dots), the block's exact value is the truth, so a re-import
484
1638
  * gives back the same rules; a value another program changed since stays the truth.
485
1639
  * @param snapshot - the graph
486
1640
  * @param text - the block's text
487
1641
  * @param read - the values read, changed in place
488
1642
  * @param read.node - the node values
489
1643
  * @param read.edge - the edge values
1644
+ * @param labels - whether the lines' labels are drawn
490
1645
  */
491
1646
  function exactFromBlock(
492
1647
  snapshot: GraphSnapshot,
493
1648
  text: string,
494
1649
  read: { readonly node: ValueColumns; readonly edge: ValueColumns },
1650
+ labels: boolean,
495
1651
  ): void {
496
- const parsed = parseStyleBlock(text, styleFingerprint(snapshot));
497
- const ex = parsed.ok ? blockStyles(parsed.block, snapshot, "pajek") : null;
498
- if (ex === null) {
499
- return;
500
- }
501
1652
  const notes = new StyleNotes();
502
1653
  const directed = directedEdges(snapshot);
503
- for (const target of ["node", "edge"] as const) {
504
- const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
505
- const get = getter((t, p) => resolveStyles(ex, t, p as never, snapshot), target);
506
- const values = read[target];
507
- for (let i = 0; i < n; i++) {
508
- const want = get(i);
509
- const back = writtenAndReadBack(target, want, directed[i], notes);
510
- for (const property of READ[target]) {
511
- const native = values[property]?.[i] ?? null;
512
- const w = want(property);
513
- if (w !== null && native !== w && native === (back.get(property) ?? null)) {
514
- values[property] ??= new Array<StyleValue | null>(n).fill(null);
515
- values[property][i] = w;
516
- }
517
- }
518
- }
519
- }
1654
+ takeFromBlock(snapshot, text, "pajek", (target) => ({
1655
+ values: read[target],
1656
+ properties: READ[target],
1657
+ writes: PAJEK_STYLE_CAPABILITIES[target],
1658
+ readBack: (i, want) => {
1659
+ const back = writtenAndReadBack(target, want, directed[i], notes, labels);
1660
+ return (property) => back.get(property) ?? null;
1661
+ },
1662
+ // a label the block places by a side clears the offsets its `lphi` reads back as
1663
+ take: (property, wanted) => wanted !== null || LABEL_PLACE.has(property) || OUTLINE.has(property),
1664
+ }));
520
1665
  }
521
1666
 
1667
+ /** The outline a shape written as `diamond` reads back as, which the block's own shape clears. */
1668
+ const OUTLINE: ReadonlySet<string> = new Set(["shapePoints", "shapePointsStretch"]);
1669
+
1670
+ /** The label place properties a side written as `lphi` reads back as. */
1671
+ const LABEL_PLACE: ReadonlySet<string> = new Set(["labelOffsetX", "labelOffsetY", "labelShiftX"]);
1672
+
522
1673
  /**
523
1674
  * An element's values written as the writer writes them and read back as the reader reads them.
524
1675
  * @param target - nodes or edges
525
1676
  * @param want - its values
526
1677
  * @param directed - for a line, whether it is an arc
527
1678
  * @param notes - a scratch for the notes the round trip makes
1679
+ * @param labels - whether the lines' labels are drawn
528
1680
  * @returns the values read back, by property
529
1681
  */
530
1682
  function writtenAndReadBack(
@@ -532,6 +1684,7 @@ function writtenAndReadBack(
532
1684
  want: Get,
533
1685
  directed: boolean,
534
1686
  notes: StyleNotes,
1687
+ labels: boolean,
535
1688
  ): Map<string, StyleValue> {
536
1689
  const ignore: Approximate = () => undefined;
537
1690
  const params = target === "node" ? vertexParams(want, ignore) : lineParams(want, directed, ignore);
@@ -540,15 +1693,27 @@ function writtenAndReadBack(
540
1693
  if (typeof label === "string") {
541
1694
  params.set(LABEL_COLUMN, pajekLabelText(label));
542
1695
  }
543
- (target === "node" ? readVertex : readLine)(
544
- (key) => params.get(key) ?? null,
545
- (property, value) => {
546
- if (value !== null) {
547
- back.set(property, value);
548
- }
549
- },
550
- (property) => ({ notes, target, property }),
551
- );
1696
+ // what the writer restates so that nothing is carried to this element (`restateCarried`)
1697
+ if (target === "node" && [...params.keys()].every((key) => key === LABEL_COLUMN)) {
1698
+ params.set(SHAPE_COLUMN, "ellipse");
1699
+ }
1700
+ for (const key of target === "edge" ? CARRIED.edge : []) {
1701
+ if (!params.has(key)) {
1702
+ params.set(key, lineRestated(key, directed));
1703
+ }
1704
+ }
1705
+ const get: Param = (key) => params.get(key) ?? null;
1706
+ const put: Put = (property, value) => {
1707
+ if (value !== null) {
1708
+ back.set(property, value);
1709
+ }
1710
+ };
1711
+ const at: At = (property) => ({ notes, target, property });
1712
+ if (target === "node") {
1713
+ readVertex(get, put, at);
1714
+ } else {
1715
+ readLine(get, put, at, directed, labels);
1716
+ }
552
1717
  return back;
553
1718
  }
554
1719
 
@@ -585,15 +1750,16 @@ export function pajekStyledExport(
585
1750
  const approximate: Approximate = (property, value) => {
586
1751
  approximated.add(target, property, value);
587
1752
  };
588
- const get = getter((t, p) => input.picture(t, p as never), target);
1753
+ const get = elementValues((t, p) => input.picture(t, p as never), target, PAJEK_STYLE_CAPABILITIES[target]);
589
1754
  const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
590
1755
  const params = (i: number): Map<string, Written> =>
591
1756
  target === "node" ? vertexParams(get(i), approximate) : lineParams(get(i), directed[i], approximate);
592
- replaceParamColumns(
593
- table,
594
- target === "node" ? [SHAPE_COLUMN, ...NODE_KEYS] : EDGE_KEYS,
595
- paramColumns(n, params),
596
- );
1757
+ const columns = paramColumns(n, params);
1758
+ if (target === "edge") {
1759
+ keepLineLabels(snapshot, columns);
1760
+ }
1761
+ restateCarried(target, columns, directed);
1762
+ replaceParamColumns(table, target === "node" ? [SHAPE_COLUMN, ...NODE_KEYS] : EDGE_KEYS, columns);
597
1763
  }
598
1764
  out.nodes.set(
599
1765
  LABEL_COLUMN,
@@ -609,6 +1775,30 @@ export function pajekStyledExport(
609
1775
  return { snapshot: out, comment: PAJEK_STYLE_COMMENT + block, notes };
610
1776
  }
611
1777
 
1778
+ /**
1779
+ * Keeps each line's own label `l` where graphty draws none: Pajek draws no line label by default (`drawsLineLabels`),
1780
+ * so a label the file holds is not drawn, and writing it back keeps it for a Draw window that marks lines with labels.
1781
+ * @param snapshot - the graph handed to the exporter
1782
+ * @param columns - the written edge columns, by key, changed in place
1783
+ */
1784
+ function keepLineLabels(snapshot: GraphSnapshot, columns: Map<string, (Written | null)[]>): void {
1785
+ if (!snapshot.edges.has("l")) {
1786
+ return;
1787
+ }
1788
+ const own = (e: number): unknown => plainCell(snapshot.edges.value("l", e));
1789
+ let col = columns.get("l");
1790
+ for (let e = 0; e < snapshot.edgeCount; e++) {
1791
+ const v = own(e);
1792
+ if (typeof v === "string" && v !== "" && (col?.[e] ?? null) === null) {
1793
+ col ??= new Array<Written | null>(snapshot.edgeCount).fill(null);
1794
+ col[e] = pajekLabelText(v);
1795
+ }
1796
+ }
1797
+ if (col !== undefined) {
1798
+ columns.set("l", col);
1799
+ }
1800
+ }
1801
+
612
1802
  /**
613
1803
  * Each vertex's label as graphty draws it; a vertex graphty draws without one gets an empty label, which Pajek draws
614
1804
  * as nothing (with no label, Pajek writes and draws the vertex's id).
@@ -664,6 +1854,65 @@ function replaceParamColumns(
664
1854
  }
665
1855
  }
666
1856
 
1857
+ /**
1858
+ * Keeps Pajek from drawing an element with the parameters of the one before it (`CARRIED`): a line writes Pajek's own
1859
+ * default for each parameter another line writes and it leaves out, and a vertex that writes no parameter while
1860
+ * another does writes its shape, `ellipse`, which starts it again from Pajek's defaults.
1861
+ * @param target - nodes or edges
1862
+ * @param columns - the written columns, by key, changed in place
1863
+ * @param directed - for each line, whether it is an arc
1864
+ */
1865
+ function restateCarried(
1866
+ target: StyleTarget,
1867
+ columns: Map<string, (Written | null)[]>,
1868
+ directed: readonly boolean[],
1869
+ ): void {
1870
+ if (target === "node") {
1871
+ const all = [...columns.values()];
1872
+ if (all.length === 0) {
1873
+ return;
1874
+ }
1875
+ const n = all[0].length;
1876
+ const shape = columns.get(SHAPE_COLUMN) ?? new Array<Written | null>(n).fill(null);
1877
+ for (let i = 0; i < n; i++) {
1878
+ if (all.every((col) => col[i] === null)) {
1879
+ shape[i] = "ellipse";
1880
+ }
1881
+ }
1882
+ columns.set(SHAPE_COLUMN, shape);
1883
+ return;
1884
+ }
1885
+ for (const key of CARRIED.edge) {
1886
+ const col = columns.get(key);
1887
+ if (col === undefined) {
1888
+ continue;
1889
+ }
1890
+ for (let i = 0; i < col.length; i++) {
1891
+ col[i] ??= lineRestated(key, directed[i]);
1892
+ }
1893
+ }
1894
+ }
1895
+
1896
+ /**
1897
+ * What a line writes for a carried parameter it leaves at Pajek's default.
1898
+ * @param key - the parameter
1899
+ * @param directed - whether the line is an arc
1900
+ * @returns the value written
1901
+ */
1902
+ function lineRestated(key: string, directed: boolean): Written {
1903
+ const defaults = lineDefaults(directed);
1904
+ switch (key) {
1905
+ case "c":
1906
+ return pajekColorText(String(defaults.color));
1907
+ case "w":
1908
+ return directed ? Number(defaults.width) : Number(defaults.width) / EDGE_WIDTH_PER_W;
1909
+ case "p":
1910
+ return "Solid";
1911
+ default:
1912
+ return 0;
1913
+ }
1914
+ }
1915
+
667
1916
  /**
668
1917
  * Every element's parameters, as columns.
669
1918
  * @param n - the element count