@graphty/graph-io 0.3.48 → 0.3.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{approximations-C2riR4NT.js → approximations-BRXsll0T.js} +2 -2
- package/dist/chunks/{approximations-C2riR4NT.js.map → approximations-BRXsll0T.js.map} +1 -1
- package/dist/chunks/bends-BGXjv_U4.js +129 -0
- package/dist/chunks/bends-BGXjv_U4.js.map +1 -0
- package/dist/chunks/{block-Cu22ewuS.js → block-yl0f_qLO.js} +931 -164
- package/dist/chunks/block-yl0f_qLO.js.map +1 -0
- package/dist/chunks/choose-CczZJaX_.js +717 -0
- package/dist/chunks/choose-CczZJaX_.js.map +1 -0
- package/dist/chunks/{codes-CV_Bhcyi.js → codes-BE2xir2k.js} +6 -2
- package/dist/chunks/codes-BE2xir2k.js.map +1 -0
- package/dist/chunks/{constants-D0LqcsLJ.js → constants-CKkYLPtt.js} +2 -2
- package/dist/chunks/{constants-D0LqcsLJ.js.map → constants-CKkYLPtt.js.map} +1 -1
- package/dist/chunks/{context-cgmmOxZ2.js → context-DizmppHy.js} +5 -4
- package/dist/chunks/{context-cgmmOxZ2.js.map → context-DizmppHy.js.map} +1 -1
- package/dist/chunks/{convert-BcQ8_jMo.js → convert-Ct2zelbQ.js} +7 -2
- package/dist/chunks/convert-Ct2zelbQ.js.map +1 -0
- package/dist/chunks/{credentials-BiQsnMTb.js → credentials-CmvJXYeH.js} +67 -16
- package/dist/chunks/credentials-CmvJXYeH.js.map +1 -0
- package/dist/chunks/{cx-export-BEeF6ymo.js → cx-export-Bt6NoqTQ.js} +3 -3
- package/dist/chunks/{cx-export-BEeF6ymo.js.map → cx-export-Bt6NoqTQ.js.map} +1 -1
- package/dist/chunks/descriptor-B28U25x4.js +60 -0
- package/dist/chunks/descriptor-B28U25x4.js.map +1 -0
- package/dist/chunks/{descriptor-BJ-W0Jd-.js → descriptor-Dhez_Cew.js} +11 -2
- package/dist/chunks/descriptor-Dhez_Cew.js.map +1 -0
- package/dist/chunks/{emit-CD8xnY8s.js → emit-7Jkpg0yd.js} +1788 -133
- package/dist/chunks/emit-7Jkpg0yd.js.map +1 -0
- package/dist/chunks/{escape-DH2Egt-l.js → escape-CuCIzNMB.js} +8 -8
- package/dist/chunks/{escape-DH2Egt-l.js.map → escape-CuCIzNMB.js.map} +1 -1
- package/dist/chunks/{export-CZ7VRm4I.js → export-Cha4dRFz.js} +45 -387
- package/dist/chunks/export-Cha4dRFz.js.map +1 -0
- package/dist/chunks/{exporter-ChjL5fmP.js → exporter-1Ajud15J.js} +9 -8
- package/dist/chunks/{exporter-ChjL5fmP.js.map → exporter-1Ajud15J.js.map} +1 -1
- package/dist/chunks/{exporter-BIdaoIgh.js → exporter-B6yvL3wy.js} +7 -7
- package/dist/chunks/{exporter-BIdaoIgh.js.map → exporter-B6yvL3wy.js.map} +1 -1
- package/dist/chunks/{exporter-Dcw7Ldtq.js → exporter-BP6otMO6.js} +6 -6
- package/dist/chunks/{exporter-Dcw7Ldtq.js.map → exporter-BP6otMO6.js.map} +1 -1
- package/dist/chunks/{exporter-DtVU80Fa.js → exporter-CA5q8ROP.js} +9 -8
- package/dist/chunks/{exporter-DtVU80Fa.js.map → exporter-CA5q8ROP.js.map} +1 -1
- package/dist/chunks/{exporter-bxxjOrDZ.js → exporter-CQQZFJ2v.js} +1284 -156
- package/dist/chunks/exporter-CQQZFJ2v.js.map +1 -0
- package/dist/chunks/format-BXiAb-1h.js +408 -0
- package/dist/chunks/format-BXiAb-1h.js.map +1 -0
- package/dist/chunks/{generic-CP6YD34u.js → generic-Dc21MAdg.js} +387 -31
- package/dist/chunks/generic-Dc21MAdg.js.map +1 -0
- package/dist/chunks/{importer-B87_fmVx.js → importer-C6NVpCXp.js} +327 -84
- package/dist/chunks/importer-C6NVpCXp.js.map +1 -0
- package/dist/chunks/{importer-DPZDN40-.js → importer-CKFe4q54.js} +25 -17
- package/dist/chunks/importer-CKFe4q54.js.map +1 -0
- package/dist/chunks/{importer-Bji2MeWr.js → importer-CKbUbsJA.js} +13 -10
- package/dist/chunks/{importer-Bji2MeWr.js.map → importer-CKbUbsJA.js.map} +1 -1
- package/dist/chunks/{importer-CB_zV5_L.js → importer-ClCsdgTJ.js} +208 -1104
- package/dist/chunks/importer-ClCsdgTJ.js.map +1 -0
- package/dist/chunks/{importer-e3huMZbm.js → importer-CyVZTrf0.js} +14 -10
- package/dist/chunks/importer-CyVZTrf0.js.map +1 -0
- package/dist/chunks/{importer-CVT-JyDE.js → importer-DgG4v1Ms.js} +1537 -355
- package/dist/chunks/importer-DgG4v1Ms.js.map +1 -0
- package/dist/chunks/{importer-Cyy6UseH.js → importer-asFUrIsj.js} +66 -13
- package/dist/chunks/importer-asFUrIsj.js.map +1 -0
- package/dist/chunks/{input-Ccg9I22A.js → input-CAxt_aV-.js} +4 -4
- package/dist/chunks/{input-Ccg9I22A.js.map → input-CAxt_aV-.js.map} +1 -1
- package/dist/chunks/{json-elements-By172SVq.js → json-elements-Cq76WjnZ.js} +4 -4
- package/dist/chunks/{json-elements-By172SVq.js.map → json-elements-Cq76WjnZ.js.map} +1 -1
- package/dist/chunks/{lexicon-u2SI5xcx.js → lexicon-IVRq2Uoo.js} +282 -74
- package/dist/chunks/lexicon-IVRq2Uoo.js.map +1 -0
- package/dist/chunks/{ontology-export-BwgMyuF0.js → ontology-export-OMo91mb0.js} +4 -4
- package/dist/chunks/{ontology-export-BwgMyuF0.js.map → ontology-export-OMo91mb0.js.map} +1 -1
- package/dist/chunks/{records-Cgvxz-Vx.js → records-NJiYvekn.js} +2 -2
- package/dist/chunks/{records-Cgvxz-Vx.js.map → records-NJiYvekn.js.map} +1 -1
- package/dist/chunks/{report-CgdsL9Hb.js → report-CwMfYa9J.js} +23 -23
- package/dist/chunks/{report-CgdsL9Hb.js.map → report-CwMfYa9J.js.map} +1 -1
- package/dist/chunks/round-trip-Cy3_SHRZ.js +79 -0
- package/dist/chunks/round-trip-Cy3_SHRZ.js.map +1 -0
- package/dist/chunks/{sigv4-LkAzsLAu.js → sigv4-BXwVbza3.js} +5 -5
- package/dist/chunks/sigv4-BXwVbza3.js.map +1 -0
- package/dist/chunks/{styles-CaABMoO1.js → styles-CAVmBLgp.js} +1237 -138
- package/dist/chunks/styles-CAVmBLgp.js.map +1 -0
- package/dist/chunks/styles-CPczw1Jz.js +1928 -0
- package/dist/chunks/styles-CPczw1Jz.js.map +1 -0
- package/dist/chunks/styles-boK6Ln54.js +2847 -0
- package/dist/chunks/styles-boK6Ln54.js.map +1 -0
- package/dist/chunks/{styles-yed-Cs1nmrjn.js → styles-yed-XeFBQ6VS.js} +506 -175
- package/dist/chunks/styles-yed-XeFBQ6VS.js.map +1 -0
- package/dist/chunks/{text-BuKQVMz2.js → text-ystE2bYi.js} +2 -2
- package/dist/chunks/{text-BuKQVMz2.js.map → text-ystE2bYi.js.map} +1 -1
- package/dist/csv.js +7 -7
- package/dist/cx.js +2 -2
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +8 -4
- package/dist/dot.js.map +1 -1
- package/dist/gexf.js +42 -12
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +848 -194
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +230 -193
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +3 -3
- package/dist/json.js +3 -2
- package/dist/neo4j.js +6 -6
- package/dist/obo.js +1 -1
- package/dist/pajek.js +1 -1
- package/dist/positions.js +45 -3
- package/dist/positions.js.map +1 -1
- package/dist/sources-cypher.js +141 -23
- package/dist/sources-cypher.js.map +1 -1
- package/dist/sources-ndex.js +2 -2
- package/dist/sources-rest.js +32 -6
- package/dist/sources-rest.js.map +1 -1
- package/dist/sources-sparql.d.ts +1 -0
- package/dist/sources-sparql.js +439 -0
- package/dist/sources-sparql.js.map +1 -0
- package/dist/src/formats/cx/importer.d.ts.map +1 -1
- package/dist/src/formats/cx/importer.js +5 -1
- package/dist/src/formats/cx/importer.js.map +1 -1
- package/dist/src/formats/cx/styles.d.ts +24 -1
- package/dist/src/formats/cx/styles.d.ts.map +1 -1
- package/dist/src/formats/cx/styles.js +117 -11
- package/dist/src/formats/cx/styles.js.map +1 -1
- package/dist/src/formats/cx2/importer.d.ts.map +1 -1
- package/dist/src/formats/cx2/importer.js +1 -0
- package/dist/src/formats/cx2/importer.js.map +1 -1
- package/dist/src/formats/cx2/styles.d.ts.map +1 -1
- package/dist/src/formats/cx2/styles.js +3 -1
- package/dist/src/formats/cx2/styles.js.map +1 -1
- package/dist/src/formats/cys/importer.d.ts.map +1 -1
- package/dist/src/formats/cys/importer.js +1 -0
- package/dist/src/formats/cys/importer.js.map +1 -1
- package/dist/src/formats/cys/styles.d.ts +2 -0
- package/dist/src/formats/cys/styles.d.ts.map +1 -1
- package/dist/src/formats/cys/styles.js +15 -7
- package/dist/src/formats/cys/styles.js.map +1 -1
- package/dist/src/formats/dot/drawing.d.ts +98 -0
- package/dist/src/formats/dot/drawing.d.ts.map +1 -0
- package/dist/src/formats/dot/drawing.js +734 -0
- package/dist/src/formats/dot/drawing.js.map +1 -0
- package/dist/src/formats/dot/exporter.d.ts.map +1 -1
- package/dist/src/formats/dot/exporter.js +34 -2
- package/dist/src/formats/dot/exporter.js.map +1 -1
- package/dist/src/formats/dot/importer.d.ts +6 -1
- package/dist/src/formats/dot/importer.d.ts.map +1 -1
- package/dist/src/formats/dot/importer.js +170 -33
- package/dist/src/formats/dot/importer.js.map +1 -1
- package/dist/src/formats/dot/index.d.ts +3 -1
- package/dist/src/formats/dot/index.d.ts.map +1 -1
- package/dist/src/formats/dot/index.js +2 -1
- package/dist/src/formats/dot/index.js.map +1 -1
- package/dist/src/formats/dot/label-places.d.ts +41 -0
- package/dist/src/formats/dot/label-places.d.ts.map +1 -0
- package/dist/src/formats/dot/label-places.js +185 -0
- package/dist/src/formats/dot/label-places.js.map +1 -0
- package/dist/src/formats/dot/names.d.ts +13 -0
- package/dist/src/formats/dot/names.d.ts.map +1 -1
- package/dist/src/formats/dot/names.js +13 -0
- package/dist/src/formats/dot/names.js.map +1 -1
- package/dist/src/formats/dot/record.d.ts +50 -0
- package/dist/src/formats/dot/record.d.ts.map +1 -0
- package/dist/src/formats/dot/record.js +275 -0
- package/dist/src/formats/dot/record.js.map +1 -0
- package/dist/src/formats/dot/styles.d.ts +72 -2
- package/dist/src/formats/dot/styles.d.ts.map +1 -1
- package/dist/src/formats/dot/styles.js +1500 -119
- package/dist/src/formats/dot/styles.js.map +1 -1
- package/dist/src/formats/gexf/dialog-metrics.d.ts +15 -0
- package/dist/src/formats/gexf/dialog-metrics.d.ts.map +1 -0
- package/dist/src/formats/gexf/dialog-metrics.js +59 -0
- package/dist/src/formats/gexf/dialog-metrics.js.map +1 -0
- package/dist/src/formats/gexf/importer.d.ts +7 -0
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +41 -5
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gexf/preview.d.ts +77 -0
- package/dist/src/formats/gexf/preview.d.ts.map +1 -0
- package/dist/src/formats/gexf/preview.js +191 -0
- package/dist/src/formats/gexf/preview.js.map +1 -0
- package/dist/src/formats/gexf/schema.d.ts.map +1 -1
- package/dist/src/formats/gexf/schema.js +2 -1
- package/dist/src/formats/gexf/schema.js.map +1 -1
- package/dist/src/formats/gexf/styles.d.ts +58 -31
- package/dist/src/formats/gexf/styles.d.ts.map +1 -1
- package/dist/src/formats/gexf/styles.js +848 -225
- package/dist/src/formats/gexf/styles.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts +5 -2
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +258 -71
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/index.d.ts +1 -1
- package/dist/src/formats/gml/index.d.ts.map +1 -1
- package/dist/src/formats/gml/index.js +1 -1
- package/dist/src/formats/gml/index.js.map +1 -1
- package/dist/src/formats/gml/positions.d.ts +7 -0
- package/dist/src/formats/gml/positions.d.ts.map +1 -1
- package/dist/src/formats/gml/positions.js +29 -7
- package/dist/src/formats/gml/positions.js.map +1 -1
- package/dist/src/formats/gml/styles.d.ts.map +1 -1
- package/dist/src/formats/gml/styles.js +754 -102
- package/dist/src/formats/gml/styles.js.map +1 -1
- package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -1
- package/dist/src/formats/graphml/styles-plain.js +6 -10
- package/dist/src/formats/graphml/styles-plain.js.map +1 -1
- package/dist/src/formats/graphml/styles-yed.d.ts +21 -15
- package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -1
- package/dist/src/formats/graphml/styles-yed.js +695 -164
- package/dist/src/formats/graphml/styles-yed.js.map +1 -1
- package/dist/src/formats/graphml/styles-yfiles3.d.ts +1 -1
- package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -1
- package/dist/src/formats/graphml/styles-yfiles3.js +431 -64
- package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts +3 -3
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +16 -6
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts +3 -2
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +39 -5
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +13 -2
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +42 -6
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/index.d.ts +1 -1
- package/dist/src/formats/json/index.d.ts.map +1 -1
- package/dist/src/formats/json/index.js +1 -1
- package/dist/src/formats/json/index.js.map +1 -1
- package/dist/src/formats/json/positions.d.ts +5 -2
- package/dist/src/formats/json/positions.d.ts.map +1 -1
- package/dist/src/formats/json/positions.js +20 -4
- package/dist/src/formats/json/positions.js.map +1 -1
- package/dist/src/formats/json/styles.d.ts +12 -1
- package/dist/src/formats/json/styles.d.ts.map +1 -1
- package/dist/src/formats/json/styles.js +1843 -136
- package/dist/src/formats/json/styles.js.map +1 -1
- package/dist/src/formats/pajek/draw.d.ts +104 -0
- package/dist/src/formats/pajek/draw.d.ts.map +1 -0
- package/dist/src/formats/pajek/draw.js +382 -0
- package/dist/src/formats/pajek/draw.js.map +1 -0
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +70 -51
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts +9 -0
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +233 -102
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/index.d.ts +1 -0
- package/dist/src/formats/pajek/index.d.ts.map +1 -1
- package/dist/src/formats/pajek/index.js.map +1 -1
- package/dist/src/formats/pajek/styles.d.ts +15 -7
- package/dist/src/formats/pajek/styles.d.ts.map +1 -1
- package/dist/src/formats/pajek/styles.js +1395 -114
- package/dist/src/formats/pajek/styles.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts +10 -1
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +26 -2
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +1 -0
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +65 -5
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/styles.d.ts +23 -3
- package/dist/src/formats/xgmml/styles.d.ts.map +1 -1
- package/dist/src/formats/xgmml/styles.js +277 -60
- package/dist/src/formats/xgmml/styles.js.map +1 -1
- package/dist/src/index.d.ts +6 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/positions/convert.d.ts.map +1 -1
- package/dist/src/positions/convert.js +11 -1
- package/dist/src/positions/convert.js.map +1 -1
- package/dist/src/positions/extract.d.ts.map +1 -1
- package/dist/src/positions/extract.js +53 -1
- package/dist/src/positions/extract.js.map +1 -1
- package/dist/src/positions/index.d.ts +1 -1
- package/dist/src/positions/index.d.ts.map +1 -1
- package/dist/src/positions/types.d.ts +59 -0
- package/dist/src/positions/types.d.ts.map +1 -1
- package/dist/src/sources/builtins.d.ts +1 -1
- package/dist/src/sources/builtins.d.ts.map +1 -1
- package/dist/src/sources/builtins.js +8 -2
- package/dist/src/sources/builtins.js.map +1 -1
- package/dist/src/sources/context.d.ts.map +1 -1
- package/dist/src/sources/context.js +1 -0
- package/dist/src/sources/context.js.map +1 -1
- package/dist/src/sources/credentials.d.ts +50 -1
- package/dist/src/sources/credentials.d.ts.map +1 -1
- package/dist/src/sources/credentials.js +69 -0
- package/dist/src/sources/credentials.js.map +1 -1
- package/dist/src/sources/cypher/descriptor.d.ts.map +1 -1
- package/dist/src/sources/cypher/descriptor.js +5 -0
- package/dist/src/sources/cypher/descriptor.js.map +1 -1
- package/dist/src/sources/cypher/index.d.ts.map +1 -1
- package/dist/src/sources/cypher/index.js +125 -18
- package/dist/src/sources/cypher/index.js.map +1 -1
- package/dist/src/sources/cypher/params.d.ts +1 -1
- package/dist/src/sources/cypher/params.d.ts.map +1 -1
- package/dist/src/sources/cypher/query-builder.d.ts +17 -0
- package/dist/src/sources/cypher/query-builder.d.ts.map +1 -1
- package/dist/src/sources/cypher/query-builder.js +31 -0
- package/dist/src/sources/cypher/query-builder.js.map +1 -1
- package/dist/src/sources/ids.d.ts +5 -0
- package/dist/src/sources/ids.d.ts.map +1 -1
- package/dist/src/sources/ids.js +5 -0
- package/dist/src/sources/ids.js.map +1 -1
- package/dist/src/sources/params.d.ts +3 -0
- package/dist/src/sources/params.d.ts.map +1 -1
- package/dist/src/sources/rest/descriptor.d.ts.map +1 -1
- package/dist/src/sources/rest/descriptor.js +9 -0
- package/dist/src/sources/rest/descriptor.js.map +1 -1
- package/dist/src/sources/rest/openalex.d.ts +4 -2
- package/dist/src/sources/rest/openalex.d.ts.map +1 -1
- package/dist/src/sources/rest/openalex.js +4 -2
- package/dist/src/sources/rest/openalex.js.map +1 -1
- package/dist/src/sources/rest/source.d.ts.map +1 -1
- package/dist/src/sources/rest/source.js +38 -0
- package/dist/src/sources/rest/source.js.map +1 -1
- package/dist/src/sources/run.d.ts +2 -0
- package/dist/src/sources/run.d.ts.map +1 -1
- package/dist/src/sources/run.js +30 -3
- package/dist/src/sources/run.js.map +1 -1
- package/dist/src/sources/sigv4.d.ts +5 -3
- package/dist/src/sources/sigv4.d.ts.map +1 -1
- package/dist/src/sources/sigv4.js +6 -4
- package/dist/src/sources/sigv4.js.map +1 -1
- package/dist/src/sources/sparql/descriptor.d.ts +10 -0
- package/dist/src/sources/sparql/descriptor.d.ts.map +1 -0
- package/dist/src/sources/sparql/descriptor.js +64 -0
- package/dist/src/sources/sparql/descriptor.js.map +1 -0
- package/dist/src/sources/sparql/index.d.ts +28 -0
- package/dist/src/sources/sparql/index.d.ts.map +1 -0
- package/dist/src/sources/sparql/index.js +327 -0
- package/dist/src/sources/sparql/index.js.map +1 -0
- package/dist/src/sources/sparql/ntriples.d.ts +69 -0
- package/dist/src/sources/sparql/ntriples.d.ts.map +1 -0
- package/dist/src/sources/sparql/ntriples.js +180 -0
- package/dist/src/sources/sparql/ntriples.js.map +1 -0
- package/dist/src/sources/sparql/params.d.ts +27 -0
- package/dist/src/sources/sparql/params.d.ts.map +1 -0
- package/dist/src/sources/sparql/params.js +5 -0
- package/dist/src/sources/sparql/params.js.map +1 -0
- package/dist/src/sources/sparql/query.d.ts +73 -0
- package/dist/src/sources/sparql/query.d.ts.map +1 -0
- package/dist/src/sources/sparql/query.js +164 -0
- package/dist/src/sources/sparql/query.js.map +1 -0
- package/dist/src/sources/types.d.ts +60 -0
- package/dist/src/sources/types.d.ts.map +1 -1
- package/dist/src/styles/annotations.d.ts +61 -5
- package/dist/src/styles/annotations.d.ts.map +1 -1
- package/dist/src/styles/annotations.js +130 -22
- package/dist/src/styles/annotations.js.map +1 -1
- package/dist/src/styles/canon/bends.d.ts +46 -0
- package/dist/src/styles/canon/bends.d.ts.map +1 -1
- package/dist/src/styles/canon/bends.js +123 -0
- package/dist/src/styles/canon/bends.js.map +1 -1
- package/dist/src/styles/canon/color-tables.d.ts +6 -1
- package/dist/src/styles/canon/color-tables.d.ts.map +1 -1
- package/dist/src/styles/canon/color-tables.js +10 -4
- package/dist/src/styles/canon/color-tables.js.map +1 -1
- package/dist/src/styles/canon/color.d.ts +3 -1
- package/dist/src/styles/canon/color.d.ts.map +1 -1
- package/dist/src/styles/canon/color.js +103 -3
- package/dist/src/styles/canon/color.js.map +1 -1
- package/dist/src/styles/canon/enums.d.ts +2 -2
- package/dist/src/styles/canon/enums.d.ts.map +1 -1
- package/dist/src/styles/canon/enums.js +89 -7
- package/dist/src/styles/canon/enums.js.map +1 -1
- package/dist/src/styles/canon/graphviz.d.ts +12 -3
- package/dist/src/styles/canon/graphviz.d.ts.map +1 -1
- package/dist/src/styles/canon/graphviz.js +57 -9
- package/dist/src/styles/canon/graphviz.js.map +1 -1
- package/dist/src/styles/canon/number.d.ts +8 -0
- package/dist/src/styles/canon/number.d.ts.map +1 -1
- package/dist/src/styles/canon/number.js +18 -0
- package/dist/src/styles/canon/number.js.map +1 -1
- package/dist/src/styles/canon/text.d.ts +29 -2
- package/dist/src/styles/canon/text.d.ts.map +1 -1
- package/dist/src/styles/canon/text.js +156 -16
- package/dist/src/styles/canon/text.js.map +1 -1
- package/dist/src/styles/codes.d.ts +2 -0
- package/dist/src/styles/codes.d.ts.map +1 -1
- package/dist/src/styles/codes.js +2 -0
- package/dist/src/styles/codes.js.map +1 -1
- package/dist/src/styles/cyjs/cascade.d.ts.map +1 -1
- package/dist/src/styles/cyjs/cascade.js +1139 -79
- package/dist/src/styles/cyjs/cascade.js.map +1 -1
- package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -1
- package/dist/src/styles/cyjs/defaults-table.js +4 -0
- package/dist/src/styles/cyjs/defaults-table.js.map +1 -1
- package/dist/src/styles/cyjs/emit.d.ts.map +1 -1
- package/dist/src/styles/cyjs/emit.js +232 -10
- package/dist/src/styles/cyjs/emit.js.map +1 -1
- package/dist/src/styles/cyjs/face.d.ts +20 -0
- package/dist/src/styles/cyjs/face.d.ts.map +1 -0
- package/dist/src/styles/cyjs/face.js +332 -0
- package/dist/src/styles/cyjs/face.js.map +1 -0
- package/dist/src/styles/cyjs/mappers.d.ts +44 -1
- package/dist/src/styles/cyjs/mappers.d.ts.map +1 -1
- package/dist/src/styles/cyjs/mappers.js +475 -26
- package/dist/src/styles/cyjs/mappers.js.map +1 -1
- package/dist/src/styles/cyjs/routes.d.ts +53 -0
- package/dist/src/styles/cyjs/routes.d.ts.map +1 -0
- package/dist/src/styles/cyjs/routes.js +360 -0
- package/dist/src/styles/cyjs/routes.js.map +1 -0
- package/dist/src/styles/cytoscape/charts.d.ts +23 -0
- package/dist/src/styles/cytoscape/charts.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/charts.js +560 -0
- package/dist/src/styles/cytoscape/charts.js.map +1 -0
- package/dist/src/styles/cytoscape/emit.d.ts.map +1 -1
- package/dist/src/styles/cytoscape/emit.js +156 -48
- package/dist/src/styles/cytoscape/emit.js.map +1 -1
- package/dist/src/styles/cytoscape/lexicon.d.ts +91 -3
- package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -1
- package/dist/src/styles/cytoscape/lexicon.js +387 -77
- package/dist/src/styles/cytoscape/lexicon.js.map +1 -1
- package/dist/src/styles/cytoscape/mappings.d.ts +30 -0
- package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -1
- package/dist/src/styles/cytoscape/mappings.js +92 -0
- package/dist/src/styles/cytoscape/mappings.js.map +1 -1
- package/dist/src/styles/cytoscape/resolve.d.ts +42 -2
- package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -1
- package/dist/src/styles/cytoscape/resolve.js +623 -17
- package/dist/src/styles/cytoscape/resolve.js.map +1 -1
- package/dist/src/styles/defaults.d.ts +46 -26
- package/dist/src/styles/defaults.d.ts.map +1 -1
- package/dist/src/styles/defaults.js +308 -71
- package/dist/src/styles/defaults.js.map +1 -1
- package/dist/src/styles/index.d.ts +4 -3
- package/dist/src/styles/index.d.ts.map +1 -1
- package/dist/src/styles/index.js +3 -2
- package/dist/src/styles/index.js.map +1 -1
- package/dist/src/styles/internal.d.ts +1 -0
- package/dist/src/styles/internal.d.ts.map +1 -1
- package/dist/src/styles/internal.js +1 -0
- package/dist/src/styles/internal.js.map +1 -1
- package/dist/src/styles/io/generic.d.ts.map +1 -1
- package/dist/src/styles/io/generic.js +56 -16
- package/dist/src/styles/io/generic.js.map +1 -1
- package/dist/src/styles/io/round-trip.d.ts +56 -0
- package/dist/src/styles/io/round-trip.d.ts.map +1 -0
- package/dist/src/styles/io/round-trip.js +138 -0
- package/dist/src/styles/io/round-trip.js.map +1 -0
- package/dist/src/styles/io/writer.d.ts.map +1 -1
- package/dist/src/styles/io/writer.js +6 -1
- package/dist/src/styles/io/writer.js.map +1 -1
- package/dist/src/styles/notes.d.ts.map +1 -1
- package/dist/src/styles/notes.js +1 -0
- package/dist/src/styles/notes.js.map +1 -1
- package/dist/src/styles/plan/features.d.ts +1 -1
- package/dist/src/styles/plan/features.d.ts.map +1 -1
- package/dist/src/styles/plan/features.js +22 -2
- package/dist/src/styles/plan/features.js.map +1 -1
- package/dist/src/styles/plan/index.d.ts +1 -1
- package/dist/src/styles/plan/index.d.ts.map +1 -1
- package/dist/src/styles/plan/index.js +1 -1
- package/dist/src/styles/plan/index.js.map +1 -1
- package/dist/src/styles/plan/plan.d.ts.map +1 -1
- package/dist/src/styles/plan/plan.js +61 -4
- package/dist/src/styles/plan/plan.js.map +1 -1
- package/dist/src/styles/plan/resolve.d.ts +11 -0
- package/dist/src/styles/plan/resolve.d.ts.map +1 -1
- package/dist/src/styles/plan/resolve.js +40 -4
- package/dist/src/styles/plan/resolve.js.map +1 -1
- package/dist/src/styles/slots.d.ts +14 -0
- package/dist/src/styles/slots.d.ts.map +1 -0
- package/dist/src/styles/slots.js +20 -0
- package/dist/src/styles/slots.js.map +1 -0
- package/dist/src/styles/types.d.ts +687 -6
- package/dist/src/styles/types.d.ts.map +1 -1
- package/dist/src/styles/vocabulary.d.ts +3 -2
- package/dist/src/styles/vocabulary.d.ts.map +1 -1
- package/dist/src/styles/vocabulary.js +224 -0
- package/dist/src/styles/vocabulary.js.map +1 -1
- package/dist/styles.js +30 -239
- package/dist/styles.js.map +1 -1
- package/dist/xgmml.js +3 -3
- package/package.json +27 -7
- package/src/formats/cx/importer.ts +6 -1
- package/src/formats/cx/styles.ts +143 -9
- package/src/formats/cx2/importer.ts +1 -0
- package/src/formats/cx2/styles.ts +3 -1
- package/src/formats/cys/importer.ts +1 -0
- package/src/formats/cys/styles.ts +27 -5
- package/src/formats/dot/drawing.ts +799 -0
- package/src/formats/dot/exporter.ts +37 -1
- package/src/formats/dot/importer.ts +192 -36
- package/src/formats/dot/index.ts +3 -1
- package/src/formats/dot/label-places.ts +241 -0
- package/src/formats/dot/names.ts +15 -0
- package/src/formats/dot/record.ts +336 -0
- package/src/formats/dot/styles.ts +1702 -149
- package/src/formats/gexf/dialog-metrics.ts +61 -0
- package/src/formats/gexf/importer.ts +50 -5
- package/src/formats/gexf/preview.ts +217 -0
- package/src/formats/gexf/schema.ts +2 -1
- package/src/formats/gexf/styles.ts +1099 -245
- package/src/formats/gml/importer.ts +291 -76
- package/src/formats/gml/index.ts +1 -1
- package/src/formats/gml/positions.ts +29 -7
- package/src/formats/gml/styles.ts +852 -110
- package/src/formats/graphml/styles-plain.ts +6 -15
- package/src/formats/graphml/styles-yed.ts +765 -176
- package/src/formats/graphml/styles-yfiles3.ts +487 -63
- package/src/formats/graphml/yfiles.ts +19 -6
- package/src/formats/json/dialect.ts +41 -5
- package/src/formats/json/importer.ts +59 -7
- package/src/formats/json/index.ts +1 -0
- package/src/formats/json/positions.ts +22 -5
- package/src/formats/json/styles.ts +2123 -171
- package/src/formats/pajek/draw.ts +469 -0
- package/src/formats/pajek/exporter.ts +81 -49
- package/src/formats/pajek/importer.ts +278 -103
- package/src/formats/pajek/index.ts +1 -0
- package/src/formats/pajek/styles.ts +1506 -133
- package/src/formats/pajek/syntax.ts +27 -2
- package/src/formats/xgmml/emit.ts +73 -5
- package/src/formats/xgmml/styles.ts +330 -71
- package/src/index.ts +12 -1
- package/src/positions/convert.ts +12 -1
- package/src/positions/extract.ts +55 -1
- package/src/positions/index.ts +1 -0
- package/src/positions/types.ts +60 -0
- package/src/sources/builtins.ts +8 -1
- package/src/sources/context.ts +1 -0
- package/src/sources/credentials.ts +90 -1
- package/src/sources/cypher/descriptor.ts +7 -1
- package/src/sources/cypher/index.ts +149 -18
- package/src/sources/cypher/params.ts +1 -1
- package/src/sources/cypher/query-builder.ts +33 -0
- package/src/sources/ids.ts +6 -0
- package/src/sources/params.ts +3 -0
- package/src/sources/rest/descriptor.ts +9 -0
- package/src/sources/rest/openalex.ts +4 -2
- package/src/sources/rest/source.ts +41 -0
- package/src/sources/run.ts +35 -3
- package/src/sources/sigv4.ts +6 -4
- package/src/sources/sparql/descriptor.ts +67 -0
- package/src/sources/sparql/index.ts +371 -0
- package/src/sources/sparql/ntriples.ts +219 -0
- package/src/sources/sparql/params.ts +28 -0
- package/src/sources/sparql/query.ts +216 -0
- package/src/sources/types.ts +68 -0
- package/src/styles/annotations.ts +201 -27
- package/src/styles/canon/bends.ts +137 -0
- package/src/styles/canon/color-tables.ts +10 -4
- package/src/styles/canon/color.ts +105 -3
- package/src/styles/canon/enums.ts +107 -8
- package/src/styles/canon/graphviz.ts +71 -10
- package/src/styles/canon/number.ts +19 -0
- package/src/styles/canon/text.ts +187 -16
- package/src/styles/codes.ts +2 -0
- package/src/styles/cyjs/cascade.ts +1303 -84
- package/src/styles/cyjs/defaults-table.ts +4 -0
- package/src/styles/cyjs/emit.ts +268 -14
- package/src/styles/cyjs/face.ts +372 -0
- package/src/styles/cyjs/mappers.ts +506 -27
- package/src/styles/cyjs/routes.ts +432 -0
- package/src/styles/cytoscape/charts.ts +684 -0
- package/src/styles/cytoscape/emit.ts +170 -48
- package/src/styles/cytoscape/lexicon.ts +431 -78
- package/src/styles/cytoscape/mappings.ts +108 -0
- package/src/styles/cytoscape/resolve.ts +741 -17
- package/src/styles/defaults.ts +417 -108
- package/src/styles/index.ts +5 -0
- package/src/styles/internal.ts +1 -0
- package/src/styles/io/generic.ts +79 -23
- package/src/styles/io/round-trip.ts +202 -0
- package/src/styles/io/writer.ts +6 -1
- package/src/styles/notes.ts +1 -0
- package/src/styles/plan/features.ts +23 -2
- package/src/styles/plan/index.ts +1 -1
- package/src/styles/plan/plan.ts +69 -5
- package/src/styles/plan/resolve.ts +47 -4
- package/src/styles/slots.ts +25 -0
- package/src/styles/types.ts +698 -5
- package/src/styles/vocabulary.ts +232 -2
- package/dist/chunks/block-Cu22ewuS.js.map +0 -1
- package/dist/chunks/choose-Cdx6Enl2.js +0 -57
- package/dist/chunks/choose-Cdx6Enl2.js.map +0 -1
- package/dist/chunks/codes-CV_Bhcyi.js.map +0 -1
- package/dist/chunks/convert-BcQ8_jMo.js.map +0 -1
- package/dist/chunks/credentials-BiQsnMTb.js.map +0 -1
- package/dist/chunks/descriptor-BJ-W0Jd-.js.map +0 -1
- package/dist/chunks/emit-CD8xnY8s.js.map +0 -1
- package/dist/chunks/export-CZ7VRm4I.js.map +0 -1
- package/dist/chunks/exporter-bxxjOrDZ.js.map +0 -1
- package/dist/chunks/format-BMS-1Nxb.js +0 -64
- package/dist/chunks/format-BMS-1Nxb.js.map +0 -1
- package/dist/chunks/generic-CP6YD34u.js.map +0 -1
- package/dist/chunks/importer-B87_fmVx.js.map +0 -1
- package/dist/chunks/importer-CB_zV5_L.js.map +0 -1
- package/dist/chunks/importer-CVT-JyDE.js.map +0 -1
- package/dist/chunks/importer-Cyy6UseH.js.map +0 -1
- package/dist/chunks/importer-DPZDN40-.js.map +0 -1
- package/dist/chunks/importer-e3huMZbm.js.map +0 -1
- package/dist/chunks/lexicon-u2SI5xcx.js.map +0 -1
- package/dist/chunks/sigv4-LkAzsLAu.js.map +0 -1
- package/dist/chunks/styles-CaABMoO1.js.map +0 -1
- package/dist/chunks/styles-P_gmFAOi.js +0 -770
- package/dist/chunks/styles-P_gmFAOi.js.map +0 -1
- package/dist/chunks/styles-yed-Cs1nmrjn.js.map +0 -1
- package/dist/chunks/writer-DvHTl36J.js +0 -449
- package/dist/chunks/writer-DvHTl36J.js.map +0 -1
|
@@ -11,16 +11,26 @@ import type { GraphSnapshot } from "@graphty/graph-format";
|
|
|
11
11
|
|
|
12
12
|
import { toFilePositions, toNeutral } from "../../positions/convert.js";
|
|
13
13
|
import type { PositionColumnExtra } from "../../positions/types.js";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
type BendPoint,
|
|
16
|
+
bendsToPoints,
|
|
17
|
+
canonBends,
|
|
18
|
+
canonNodeOffsets,
|
|
19
|
+
flattenCubic,
|
|
20
|
+
flattenQuadSpline,
|
|
21
|
+
nodeOffsetsToPoints,
|
|
22
|
+
} from "../../styles/canon/bends.js";
|
|
15
23
|
import { canonColor, colorOpacity } from "../../styles/canon/color.js";
|
|
16
24
|
import { canonEnum } from "../../styles/canon/enums.js";
|
|
17
|
-
import { canonNumber } from "../../styles/canon/number.js";
|
|
25
|
+
import { canonNumber, canonSigned } from "../../styles/canon/number.js";
|
|
18
26
|
import { type CanonSite, MAX_STYLE_VALUE_LENGTH, noteAt } from "../../styles/canon/site.js";
|
|
19
27
|
import { canonText } from "../../styles/canon/text.js";
|
|
20
28
|
import { STYLE_ISSUE } from "../../styles/codes.js";
|
|
29
|
+
import { JAVA_MONO, JAVA_SANS, JAVA_SERIF, javaFamily } from "../../styles/cytoscape/lexicon.js";
|
|
21
30
|
import { StyleApproximations } from "../../styles/io/approximations.js";
|
|
31
|
+
import { elementValues, takeFromBlock } from "../../styles/io/round-trip.js";
|
|
22
32
|
import type { ExporterStyleInput } from "../../styles/io/writer.js";
|
|
23
|
-
import
|
|
33
|
+
import { StyleNotes } from "../../styles/notes.js";
|
|
24
34
|
import { plainCell } from "../../styles/plan/resolve.js";
|
|
25
35
|
import type { StyleTarget, StyleValue, StyleWorkBudget, StyleWriteCapabilities } from "../../styles/types.js";
|
|
26
36
|
import type { LossNote } from "../../types.js";
|
|
@@ -235,36 +245,183 @@ function activeRealizer(proxy: unknown): Realizer | null {
|
|
|
235
245
|
return all[Number.isInteger(active) && active >= 0 && active < all.length ? active : 0] ?? null;
|
|
236
246
|
}
|
|
237
247
|
|
|
248
|
+
/** A shape a yEd configuration draws: a neutral shape, with its corners where it is a `polygon`. */
|
|
249
|
+
export interface YedOutline {
|
|
250
|
+
readonly shape: string;
|
|
251
|
+
/** The polygon's corners as neutral `shapePoints` (`x,y` fractions of the half sizes, y up). */
|
|
252
|
+
readonly points?: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
238
255
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
256
|
+
* Points along an elliptical arc, as `x,y` text: center (cx, cy), radii (rx, ry), from angle `from` to `to` (radians,
|
|
257
|
+
* counter-clockwise, y up), both ends included.
|
|
258
|
+
* @param cx - center x
|
|
259
|
+
* @param cy - center y
|
|
260
|
+
* @param rx - x radius
|
|
261
|
+
* @param ry - y radius
|
|
262
|
+
* @param from - first angle
|
|
263
|
+
* @param to - last angle
|
|
264
|
+
* @param steps - segments
|
|
265
|
+
* @returns the points
|
|
241
266
|
*/
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
267
|
+
function arcPoints(cx: number, cy: number, rx: number, ry: number, from: number, to: number, steps = 12): string[] {
|
|
268
|
+
return Array.from({ length: steps + 1 }, (_, i) => {
|
|
269
|
+
const t = from + ((to - from) * i) / steps;
|
|
270
|
+
return pointText(cx + rx * Math.cos(t), cy + ry * Math.sin(t));
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Points along a wave y = y0 + amp * sin(pi * x), x from `x0` to `x1`.
|
|
276
|
+
* @param y0 - the wave's middle
|
|
277
|
+
* @param amp - its height
|
|
278
|
+
* @param x0 - first x
|
|
279
|
+
* @param x1 - last x
|
|
280
|
+
* @returns the points
|
|
281
|
+
*/
|
|
282
|
+
function wavePoints(y0: number, amp: number, x0: number, x1: number): string[] {
|
|
283
|
+
const steps = 16;
|
|
284
|
+
return Array.from({ length: steps + 1 }, (_, i) => {
|
|
285
|
+
const x = x0 + ((x1 - x0) * i) / steps;
|
|
286
|
+
return pointText(x, y0 + amp * Math.sin(Math.PI * x));
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* One corner as `x,y`, rounded to three decimals.
|
|
292
|
+
* @param x - x
|
|
293
|
+
* @param y - y
|
|
294
|
+
* @returns the text
|
|
295
|
+
*/
|
|
296
|
+
function pointText(x: number, y: number): string {
|
|
297
|
+
const r = (v: number): string => String(Math.round(v * 1000) / 1000 + 0);
|
|
298
|
+
return `${r(x)},${r(y)}`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const HALF_PI = Math.PI / 2;
|
|
251
302
|
|
|
252
|
-
|
|
303
|
+
/** A cloud: a circle of bumps, stretched to the box. */
|
|
304
|
+
const CLOUD = Array.from({ length: 72 }, (_, i) => {
|
|
305
|
+
const t = (2 * Math.PI * i) / 72;
|
|
306
|
+
const r = 0.86 + 0.14 * Math.abs(Math.cos(4 * t));
|
|
307
|
+
return pointText(r * Math.cos(t), r * Math.sin(t));
|
|
308
|
+
}).join(" ");
|
|
309
|
+
|
|
310
|
+
/** A stadium: a box whose ends are half circles (a pill). */
|
|
311
|
+
const STADIUM = [
|
|
312
|
+
...arcPoints(0.5, 0, 0.5, 1, -HALF_PI, HALF_PI),
|
|
313
|
+
...arcPoints(-0.5, 0, 0.5, 1, HALF_PI, 3 * HALF_PI),
|
|
314
|
+
].join(" ");
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The shape each configuration of yEd's GenericNode palettes draws (flowchart, entity relationship, BPMN, SBGN), by
|
|
318
|
+
* its name after `com.yworks.` without a `.withShadow` suffix, lowercase. Matched whole, before the words below:
|
|
319
|
+
* "data" is a parallelogram, but "storedData" and "directData" are not. A shape no neutral one names is the polygon
|
|
320
|
+
* yEd draws it as (curves as short segments); the lines some draw inside (predefined process, internal storage) are
|
|
321
|
+
* not drawn. Left out until a drawing by yEd itself shows them (ISSUES.md): the BPMN artifact, whose `type` property
|
|
322
|
+
* picks among a data object, a data store, a message and others, and the SBGN nucleic acid feature and perturbing
|
|
323
|
+
* agent, which draw.io, the stand-in, draws as plain boxes.
|
|
324
|
+
*/
|
|
325
|
+
const YED_CONFIGURATIONS: ReadonlyMap<string, YedOutline> = new Map<string, YedOutline>([
|
|
326
|
+
["flowchart.process", { shape: "rectangle" }],
|
|
327
|
+
["flowchart.predefinedprocess", { shape: "rectangle" }],
|
|
328
|
+
["flowchart.internalstorage", { shape: "rectangle" }],
|
|
329
|
+
["flowchart.decision", { shape: "diamond" }],
|
|
330
|
+
["flowchart.terminator", { shape: "round-rectangle" }],
|
|
331
|
+
["flowchart.start1", { shape: "ellipse" }],
|
|
332
|
+
["flowchart.start2", { shape: "ellipse" }],
|
|
333
|
+
["flowchart.onpagereference", { shape: "ellipse" }],
|
|
334
|
+
["flowchart.sequentialdata", { shape: "ellipse" }],
|
|
335
|
+
["flowchart.data", { shape: "parallelogram" }],
|
|
336
|
+
["flowchart.preparation", { shape: "hexagon" }],
|
|
337
|
+
["flowchart.database", { shape: "other:database" }],
|
|
338
|
+
["flowchart.looplimit", { shape: "polygon", points: "-1,-1 1,-1 1,0.5 0.8,1 -0.8,1 -1,0.5" }],
|
|
339
|
+
["flowchart.looplimitend", { shape: "polygon", points: "-1,1 -1,-0.5 -0.8,-1 0.8,-1 1,-0.5 1,1" }],
|
|
340
|
+
["flowchart.manualinput", { shape: "polygon", points: "-1,-1 1,-1 1,1 -1,0.4" }],
|
|
341
|
+
["flowchart.card", { shape: "polygon", points: "-1,-1 1,-1 1,1 -0.6,1 -1,0.6" }],
|
|
342
|
+
["flowchart.manualoperation", { shape: "polygon", points: "-1,1 -0.7,-1 0.7,-1 1,1" }],
|
|
343
|
+
["flowchart.offpagereference", { shape: "polygon", points: "-1,1 -1,0 0,-1 1,0 1,1" }],
|
|
344
|
+
["flowchart.usermessage", { shape: "polygon", points: "-1,1 -1,-1 0.6,-1 1,0 0.6,1" }],
|
|
345
|
+
["flowchart.networkmessage", { shape: "polygon", points: "-1,1 -0.6,0 -1,-1 1,-1 1,1" }],
|
|
346
|
+
["flowchart.document", { shape: "polygon", points: ["-1,1", ...wavePoints(-0.75, 0.2, -1, 1), "1,1"].join(" ") }],
|
|
347
|
+
[
|
|
348
|
+
"flowchart.papertype",
|
|
349
|
+
{
|
|
350
|
+
shape: "polygon",
|
|
351
|
+
points: [...wavePoints(-0.8, 0.2, -1, 1), ...wavePoints(0.8, 0.2, 1, -1)].join(" "),
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
[
|
|
355
|
+
"flowchart.delay",
|
|
356
|
+
{ shape: "polygon", points: ["-1,-1", ...arcPoints(0, 0, 1, 1, -HALF_PI, HALF_PI), "-1,1"].join(" ") },
|
|
357
|
+
],
|
|
358
|
+
[
|
|
359
|
+
"flowchart.display",
|
|
360
|
+
{
|
|
361
|
+
shape: "polygon",
|
|
362
|
+
points: ["-1,0", "-0.6,-1", ...arcPoints(0.6, 0, 0.4, 1, -HALF_PI, HALF_PI), "-0.6,1"].join(" "),
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
[
|
|
366
|
+
"flowchart.storeddata",
|
|
367
|
+
{
|
|
368
|
+
shape: "polygon",
|
|
369
|
+
points: [
|
|
370
|
+
...arcPoints(1, 0, -0.2, 1, -HALF_PI, HALF_PI),
|
|
371
|
+
...arcPoints(-0.8, 0, 0.2, 1, HALF_PI, 3 * HALF_PI),
|
|
372
|
+
].join(" "),
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
[
|
|
376
|
+
"flowchart.directdata",
|
|
377
|
+
{
|
|
378
|
+
shape: "polygon",
|
|
379
|
+
points: [
|
|
380
|
+
...arcPoints(0.8, 0, 0.2, 1, -HALF_PI, HALF_PI),
|
|
381
|
+
...arcPoints(-0.8, 0, 0.2, 1, HALF_PI, 3 * HALF_PI),
|
|
382
|
+
].join(" "),
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
["flowchart.cloud", { shape: "polygon", points: CLOUD }],
|
|
386
|
+
["entityrelationship.relationship", { shape: "diamond" }],
|
|
387
|
+
["entityrelationship.attribute", { shape: "ellipse" }],
|
|
388
|
+
["bpmn.conversation", { shape: "hexagon" }],
|
|
389
|
+
["sbgn.unspecifiedentity", { shape: "ellipse" }],
|
|
390
|
+
["sbgn.simplechemical", { shape: "ellipse" }],
|
|
391
|
+
["sbgn.emptyset", { shape: "ellipse" }],
|
|
392
|
+
["sbgn.operator", { shape: "ellipse" }],
|
|
393
|
+
["sbgn.macromolecule", { shape: "round-rectangle" }],
|
|
394
|
+
["sbgn.phenotype", { shape: "hexagon" }],
|
|
395
|
+
["sbgn.process", { shape: "rectangle" }],
|
|
396
|
+
["sbgn.submap", { shape: "rectangle" }],
|
|
397
|
+
["sbgn.unitofinformation", { shape: "rectangle" }],
|
|
398
|
+
["sbgn.statevariable", { shape: "polygon", points: STADIUM }],
|
|
399
|
+
["sbgn.tag", { shape: "polygon", points: "-1,1 -1,-1 0.6,-1 1,0 0.6,1" }],
|
|
400
|
+
]);
|
|
253
401
|
|
|
254
402
|
/**
|
|
255
|
-
* The shape a yEd
|
|
403
|
+
* The shape a yEd GenericNode configuration draws: a GraphML GenericNode's `configuration`, or a yEd GML node's
|
|
256
404
|
* `customconfiguration`.
|
|
257
405
|
* @param configuration - the configuration
|
|
258
|
-
* @returns the
|
|
406
|
+
* @returns the shape, with its corners where it is a polygon, or null for a configuration not known here
|
|
259
407
|
*/
|
|
260
|
-
export function
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
408
|
+
export function configurationOutline(configuration: string): YedOutline | null {
|
|
409
|
+
const key = configuration
|
|
410
|
+
.slice(0, MAX_STYLE_VALUE_LENGTH)
|
|
411
|
+
.toLowerCase()
|
|
412
|
+
.replace(/^com\.yworks\./, "")
|
|
413
|
+
.replace(/\.withshadow$/, "");
|
|
414
|
+
return YED_CONFIGURATIONS.get(key) ?? null;
|
|
264
415
|
}
|
|
265
416
|
|
|
266
417
|
/** GenericNode configurations, by a word they contain, to the shape they draw; the first match wins. */
|
|
267
418
|
const GENERIC_SHAPES: readonly (readonly [string, string])[] = [
|
|
419
|
+
// yEd's BPMN and entity-relationship palettes: a task is a rounded box, an event a circle, a
|
|
420
|
+
// gateway a diamond, an attribute an ellipse
|
|
421
|
+
["bpmn.activity", "round-rectangle"],
|
|
422
|
+
["bpmn.event", "ellipse"],
|
|
423
|
+
["bpmn.gateway", "diamond"],
|
|
424
|
+
["entityrelationship.attribute", "ellipse"],
|
|
268
425
|
["ellipse", "ellipse"],
|
|
269
426
|
["circle", "ellipse"],
|
|
270
427
|
["oval", "ellipse"],
|
|
@@ -338,8 +495,9 @@ function readColor(raw: unknown, property: string, opacity: string | null, put:
|
|
|
338
495
|
* @param node - whether they are node labels (which have a position)
|
|
339
496
|
* @param put - receives the values
|
|
340
497
|
* @param at - the sites
|
|
498
|
+
* @param geometry - a node label's node `y:Geometry`, which places the label inside or outside the node
|
|
341
499
|
*/
|
|
342
|
-
function readLabels(labels: readonly unknown[], node: boolean, put: PutValue, at: SiteOf): void {
|
|
500
|
+
function readLabels(labels: readonly unknown[], node: boolean, put: PutValue, at: SiteOf, geometry?: unknown): void {
|
|
343
501
|
let label: unknown = undefined;
|
|
344
502
|
let text = "";
|
|
345
503
|
let extra = 0;
|
|
@@ -362,20 +520,33 @@ function readLabels(labels: readonly unknown[], node: boolean, put: PutValue, at
|
|
|
362
520
|
noteAt(at("label"), STYLE_ISSUE.EXTRA_LABELS, `${String(extra + 1)} labels`);
|
|
363
521
|
}
|
|
364
522
|
put("label", canonText(text, at("label")));
|
|
365
|
-
readLabelLook(label, node, put, at);
|
|
523
|
+
readLabelLook(label, node, put, at, geometry);
|
|
366
524
|
}
|
|
367
525
|
|
|
368
526
|
/**
|
|
369
|
-
* Reads a label's look: its color, font and, for a node label, its position.
|
|
527
|
+
* Reads a label's look: its color, background box and its border, font and, for a node label, its position.
|
|
370
528
|
* @param label - the label element
|
|
371
529
|
* @param node - whether it is a node label
|
|
372
530
|
* @param put - receives the values
|
|
373
531
|
* @param at - the sites
|
|
532
|
+
* @param geometry - a node label's node `y:Geometry`
|
|
374
533
|
*/
|
|
375
|
-
function readLabelLook(label: unknown, node: boolean, put: PutValue, at: SiteOf): void {
|
|
534
|
+
function readLabelLook(label: unknown, node: boolean, put: PutValue, at: SiteOf, geometry?: unknown): void {
|
|
376
535
|
readColor(treeAttr(label, "textColor"), "labelColor", null, put, at);
|
|
536
|
+
// yEd draws a box behind the words in `backgroundColor` unless `hasBackgroundColor` is false
|
|
537
|
+
if (treeAttr(label, "hasBackgroundColor") !== "false") {
|
|
538
|
+
readColor(treeAttr(label, "backgroundColor"), "labelBackground", null, put, at);
|
|
539
|
+
}
|
|
540
|
+
// and a one-pixel line round that box in `lineColor` unless `hasLineColor` is false
|
|
541
|
+
if (treeAttr(label, "hasLineColor") !== "false" && treeAttr(label, "lineColor") !== undefined) {
|
|
542
|
+
readColor(treeAttr(label, "lineColor"), "labelBorderColor", null, put, at);
|
|
543
|
+
put("labelBorderWidth", 1);
|
|
544
|
+
}
|
|
377
545
|
put("labelSize", canonNumber(treeAttr(label, "fontSize"), at("labelSize")));
|
|
378
|
-
|
|
546
|
+
// yEd is a Java program: its logical fonts (Dialog, SansSerif, Monospaced, ...) are drawn in the faces Java gives
|
|
547
|
+
// them, and a font it does not have in Dialog's
|
|
548
|
+
const font = canonText(treeAttr(label, "fontFamily"), at("labelFont"));
|
|
549
|
+
put("labelFont", font === null ? null : javaFamily(font));
|
|
379
550
|
const style = treeAttr(label, "fontStyle");
|
|
380
551
|
if (style !== undefined) {
|
|
381
552
|
const known = FONT_STYLES.get(style.trim().toLowerCase());
|
|
@@ -386,39 +557,156 @@ function readLabelLook(label: unknown, node: boolean, put: PutValue, at: SiteOf)
|
|
|
386
557
|
put("labelItalic", known[1]);
|
|
387
558
|
}
|
|
388
559
|
}
|
|
560
|
+
readRotation(label, put, at);
|
|
389
561
|
if (node) {
|
|
390
|
-
|
|
562
|
+
const position = nodeLabelPosition(label, geometry, at);
|
|
563
|
+
put("labelPosition", position);
|
|
564
|
+
readInsideInset(label, geometry, position, put);
|
|
565
|
+
readOutsideOffset(label, geometry, position, put);
|
|
391
566
|
}
|
|
392
567
|
}
|
|
393
568
|
|
|
394
569
|
/**
|
|
395
|
-
* Reads a
|
|
396
|
-
* `
|
|
570
|
+
* Reads a label's `rotationAngle`: yEd is a Java2D program, whose angles turn clockwise on screen (y down), as the
|
|
571
|
+
* neutral `labelRotation` does.
|
|
397
572
|
* @param label - the label element
|
|
398
573
|
* @param put - receives the values
|
|
399
574
|
* @param at - the sites
|
|
400
575
|
*/
|
|
401
|
-
function
|
|
402
|
-
const
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
576
|
+
function readRotation(label: unknown, put: PutValue, at: SiteOf): void {
|
|
577
|
+
const angle = canonSigned(treeAttr(label, "rotationAngle"), at("labelRotation"));
|
|
578
|
+
if (angle === null) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const turn = ((angle % 360) + 360) % 360;
|
|
582
|
+
put("labelRotation", turn === 0 ? null : turn);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* How far from where its position puts it a label outside its node, or placed freely, is drawn: yEd writes every
|
|
587
|
+
* label's box as it draws it, so an outside label keeps its model's distance from the node (4 by default) and a
|
|
588
|
+
* free label its offset. Across the side it is beside, the label is moved by the gap between the node and its box;
|
|
589
|
+
* along that side (and on both axes for a free label over the node), by how far its box's middle is from the node's.
|
|
590
|
+
* A turned label's box is not its words' box, so it is left where its position puts it.
|
|
591
|
+
* @param label - the label element
|
|
592
|
+
* @param geometry - the node's `y:Geometry`
|
|
593
|
+
* @param position - where the label is placed
|
|
594
|
+
* @param put - receives the values
|
|
595
|
+
*/
|
|
596
|
+
function readOutsideOffset(label: unknown, geometry: unknown, position: string | null, put: PutValue): void {
|
|
597
|
+
const model = treeAttr(label, "modelName");
|
|
598
|
+
const free = position === "center" && model !== undefined && model !== "internal";
|
|
599
|
+
if (position === null || position.startsWith("inside-") || (position === "center" && !free)) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (Number(treeAttr(label, "rotationAngle") ?? 0) % 360 !== 0) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const num = (o: unknown, k: string): number => Number(treeAttr(o, k) ?? Number.NaN);
|
|
606
|
+
const [x, y, w, h] = ["x", "y", "width", "height"].map((k) => num(label, k));
|
|
607
|
+
const [nw, nh] = [num(geometry, "width"), num(geometry, "height")];
|
|
608
|
+
if (![x, y, w, h, nw, nh].every(Number.isFinite)) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const signed = (value: number): number | null => {
|
|
612
|
+
const r = Math.round(value * 1000) / 1000;
|
|
613
|
+
return r === 0 ? null : r;
|
|
614
|
+
};
|
|
615
|
+
// across a side: the gap between the node and the box; along it: the box's middle against the node's
|
|
616
|
+
const offset = (lo: number, size: number, extent: number, before: boolean, after: boolean): number | null => {
|
|
617
|
+
if (before) {
|
|
618
|
+
return signed(lo + size);
|
|
408
619
|
}
|
|
409
|
-
|
|
620
|
+
return after ? signed(lo - extent) : signed(lo + size / 2 - extent / 2);
|
|
621
|
+
};
|
|
622
|
+
put("labelOffsetX", offset(x, w, nw, position.includes("left"), position.includes("right")));
|
|
623
|
+
put("labelOffsetY", offset(y, h, nh, position.includes("top"), position.includes("bottom")));
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* How far in from the sides it is drawn against yEd puts a label inside its node (its internal model's insets, 4 by
|
|
628
|
+
* default), from the label's stored box, for a label sized to its words: the label is moved that far right of a left side, left of a right side, down
|
|
629
|
+
* from the top and up from the bottom.
|
|
630
|
+
* @param label - the label element
|
|
631
|
+
* @param geometry - the node's `y:Geometry`
|
|
632
|
+
* @param position - where the label is placed
|
|
633
|
+
* @param put - receives the values
|
|
634
|
+
*/
|
|
635
|
+
function readInsideInset(label: unknown, geometry: unknown, position: string | null, put: PutValue): void {
|
|
636
|
+
// a label sized to its node (a group's header band) has a box that is not its words'
|
|
637
|
+
const sized = treeAttr(label, "autoSizePolicy")?.startsWith("node_") === true;
|
|
638
|
+
if (position?.startsWith("inside-") !== true || sized) {
|
|
410
639
|
return;
|
|
411
640
|
}
|
|
641
|
+
const num = (o: unknown, k: string): number => Number(treeAttr(o, k) ?? Number.NaN);
|
|
642
|
+
const [x, y, w, h] = ["x", "y", "width", "height"].map((k) => num(label, k));
|
|
643
|
+
const [nw, nh] = [num(geometry, "width"), num(geometry, "height")];
|
|
644
|
+
const inset = (value: number): number | null =>
|
|
645
|
+
Number.isFinite(value) && value > 0 ? Math.round(value * 1000) / 1000 : null;
|
|
646
|
+
if (position.includes("left")) {
|
|
647
|
+
put("labelOffsetX", inset(x));
|
|
648
|
+
} else if (position.includes("right")) {
|
|
649
|
+
const gap = inset(nw - x - w);
|
|
650
|
+
put("labelOffsetX", gap === null ? null : -gap);
|
|
651
|
+
}
|
|
652
|
+
if (position.includes("top")) {
|
|
653
|
+
put("labelOffsetY", inset(y));
|
|
654
|
+
} else if (position.includes("bottom")) {
|
|
655
|
+
const gap = inset(nh - y - h);
|
|
656
|
+
put("labelOffsetY", gap === null ? null : -gap);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Where yEd draws a node label: from the label's box, which every model yEd writes stores relative to the node's
|
|
662
|
+
* top-left corner (a label entirely beside the node is outside it on that side), else from its model. A label inside
|
|
663
|
+
* the node is placed by its model: yEd's `internal` model puts it against a side or in a corner (`t`, `bl`, ...), or
|
|
664
|
+
* in the middle (`c`); any other model's label inside the node is in its middle.
|
|
665
|
+
* @param label - the label element
|
|
666
|
+
* @param geometry - the node's `y:Geometry`
|
|
667
|
+
* @param at - the sites
|
|
668
|
+
* @returns the position, or null
|
|
669
|
+
*/
|
|
670
|
+
function nodeLabelPosition(label: unknown, geometry: unknown, at: SiteOf): string | null {
|
|
671
|
+
const outside = boxPosition(label, geometry);
|
|
672
|
+
if (outside !== null && outside !== "center") {
|
|
673
|
+
return outside;
|
|
674
|
+
}
|
|
412
675
|
const position = treeAttr(label, "modelPosition");
|
|
413
|
-
|
|
414
|
-
|
|
676
|
+
const model = treeAttr(label, "modelName");
|
|
677
|
+
if (model === "internal") {
|
|
678
|
+
return insidePosition(label, position);
|
|
679
|
+
}
|
|
680
|
+
if (model === "custom") {
|
|
681
|
+
return customPosition(label, outside, at);
|
|
682
|
+
}
|
|
683
|
+
if (outside !== null || position === undefined) {
|
|
684
|
+
return outside;
|
|
415
685
|
}
|
|
416
|
-
const
|
|
417
|
-
const known = model === "internal" && key === "t" ? "inside-top" : YED_POSITIONS.get(key);
|
|
686
|
+
const known = YED_POSITIONS.get(position.trim().toLowerCase());
|
|
418
687
|
if (known === undefined) {
|
|
419
688
|
noteAt(at("labelPosition"), STYLE_ISSUE.UNKNOWN_VALUE, position.slice(0, MAX_STYLE_VALUE_LENGTH));
|
|
420
689
|
}
|
|
421
|
-
|
|
690
|
+
return known ?? null;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Where a SmartNodeLabelModel (`custom`) label is drawn: where its stored box puts it, as for any other model, and
|
|
695
|
+
* where its ratios put it when the file stores no box (a label hung from the node's top edge into it is a header
|
|
696
|
+
* along its top). A box beside the node is handled before this.
|
|
697
|
+
* @param label - the label element
|
|
698
|
+
* @param outside - the box's position against the node, `center` or null
|
|
699
|
+
* @param at - the sites
|
|
700
|
+
* @returns the position, or null
|
|
701
|
+
*/
|
|
702
|
+
function customPosition(label: unknown, outside: string | null, at: SiteOf): string | null {
|
|
703
|
+
const smart = treeChild(treeChild(label, "ModelParameter"), "SmartNodeLabelModelParameter");
|
|
704
|
+
if (smart === undefined) {
|
|
705
|
+
noteAt(at("labelPosition"), STYLE_ISSUE.UNKNOWN_VALUE, "custom");
|
|
706
|
+
return outside;
|
|
707
|
+
}
|
|
708
|
+
// the stored box is where yEd drew the label; the ratios place it only when the file stores no box
|
|
709
|
+
return outside ?? smartPosition(smart);
|
|
422
710
|
}
|
|
423
711
|
|
|
424
712
|
/**
|
|
@@ -460,6 +748,63 @@ function sideOf(ratio: number, low: string, high: string): string {
|
|
|
460
748
|
return ratio > 0 ? high : "";
|
|
461
749
|
}
|
|
462
750
|
|
|
751
|
+
/**
|
|
752
|
+
* Where a label's box lies against its node: beside a side or a corner, or `center` when it overlaps the node.
|
|
753
|
+
* @param label - the label element
|
|
754
|
+
* @param geometry - the node's `y:Geometry`
|
|
755
|
+
* @returns the position, or null when the label or the node has no box
|
|
756
|
+
*/
|
|
757
|
+
function boxPosition(label: unknown, geometry: unknown): string | null {
|
|
758
|
+
const num = (o: unknown, k: string): number => Number(treeAttr(o, k) ?? Number.NaN);
|
|
759
|
+
const [x, y, w, h] = ["x", "y", "width", "height"].map((k) => num(label, k));
|
|
760
|
+
const [nw, nh] = [num(geometry, "width"), num(geometry, "height")];
|
|
761
|
+
if (![x, y, w, h, nw, nh].every(Number.isFinite)) {
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
const side = (lo: number, size: number, extent: number): -1 | 0 | 1 => {
|
|
765
|
+
if (lo + size <= 0) {
|
|
766
|
+
return -1;
|
|
767
|
+
}
|
|
768
|
+
return lo >= extent ? 1 : 0;
|
|
769
|
+
};
|
|
770
|
+
return BOX_POSITIONS[side(y, h, nh) + 1][side(x, w, nw) + 1];
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Where yEd's internal model draws a label: in the middle, or inside the node against the side or in the corner its
|
|
775
|
+
* `modelPosition` names. A label as wide as its node (a group's header) is drawn against the side its `alignment`
|
|
776
|
+
* names, so a right-aligned header along the top is in the top-right corner.
|
|
777
|
+
* @param label - the label element
|
|
778
|
+
* @param position - its `modelPosition`
|
|
779
|
+
* @returns the position
|
|
780
|
+
*/
|
|
781
|
+
function insidePosition(label: unknown, position: string | undefined): string {
|
|
782
|
+
const inside = INTERNAL_POSITIONS.get((position ?? "c").trim().toLowerCase()) ?? "center";
|
|
783
|
+
const align = treeAttr(label, "alignment");
|
|
784
|
+
const across = treeAttr(label, "autoSizePolicy") === "node_width" && (align === "left" || align === "right");
|
|
785
|
+
return across && (inside === "inside-top" || inside === "inside-bottom") ? `${inside}-${align}` : inside;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/** yEd's internal label model positions, as the neutral position inside the node. */
|
|
789
|
+
const INTERNAL_POSITIONS: ReadonlyMap<string, string> = new Map([
|
|
790
|
+
["c", "center"],
|
|
791
|
+
["t", "inside-top"],
|
|
792
|
+
["b", "inside-bottom"],
|
|
793
|
+
["l", "inside-left"],
|
|
794
|
+
["r", "inside-right"],
|
|
795
|
+
["tl", "inside-top-left"],
|
|
796
|
+
["tr", "inside-top-right"],
|
|
797
|
+
["bl", "inside-bottom-left"],
|
|
798
|
+
["br", "inside-bottom-right"],
|
|
799
|
+
]);
|
|
800
|
+
|
|
801
|
+
/** A label's position by the side of the node it lies on: rows above, level, below; columns left, level, right. */
|
|
802
|
+
const BOX_POSITIONS: readonly (readonly string[])[] = [
|
|
803
|
+
["top-left", "top", "top-right"],
|
|
804
|
+
["left", "center", "right"],
|
|
805
|
+
["bottom-left", "bottom", "bottom-right"],
|
|
806
|
+
];
|
|
807
|
+
|
|
463
808
|
/**
|
|
464
809
|
* The shape of a node realizer: its `y:Shape` where it has one, a GenericNode's configuration, or the realizer
|
|
465
810
|
* type itself for one that draws an image (SVG, image, table, UML).
|
|
@@ -475,11 +820,15 @@ function realizerShape(real: Realizer, at: SiteOf): string | null {
|
|
|
475
820
|
if (real.type === "ShapeNode" || real.type === "GroupNode" || real.type === "GenericGroupNode") {
|
|
476
821
|
return null;
|
|
477
822
|
}
|
|
823
|
+
if (real.type === "TableNode") {
|
|
824
|
+
// a yEd table or pool is a rectangle of bands
|
|
825
|
+
return canonEnum("shape", "rectangle", "yed", at("shape"));
|
|
826
|
+
}
|
|
478
827
|
if (real.type === "GenericNode") {
|
|
479
828
|
const conf = treeAttr(real.r, "configuration") ?? "";
|
|
480
|
-
const
|
|
481
|
-
if (
|
|
482
|
-
return
|
|
829
|
+
const known = configurationOutline(conf);
|
|
830
|
+
if (known !== null) {
|
|
831
|
+
return known.shape;
|
|
483
832
|
}
|
|
484
833
|
const lower = conf.slice(0, MAX_STYLE_VALUE_LENGTH).toLowerCase();
|
|
485
834
|
for (const [word, value] of GENERIC_SHAPES) {
|
|
@@ -497,27 +846,176 @@ function realizerShape(real: Realizer, at: SiteOf): string | null {
|
|
|
497
846
|
return `other:${real.type}`;
|
|
498
847
|
}
|
|
499
848
|
|
|
849
|
+
/** yEd's gradient: from the fill color at a node's top-left corner to `color2` at its bottom-right one. */
|
|
850
|
+
const YED_GRADIENT_ANGLE = 45;
|
|
851
|
+
|
|
500
852
|
/**
|
|
501
|
-
* Reads
|
|
853
|
+
* Reads a node realizer's fill: its color, and a gradient into `color2`. yEd paints no fill where the fill has no
|
|
854
|
+
* color or is transparent.
|
|
502
855
|
* @param real - the realizer
|
|
503
856
|
* @param put - receives the values
|
|
504
857
|
* @param at - the sites
|
|
505
858
|
*/
|
|
506
|
-
function
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
859
|
+
function readFill(real: Realizer, put: PutValue, at: SiteOf): void {
|
|
860
|
+
const fill = treeChild(real.r, "Fill");
|
|
861
|
+
if (fill === undefined) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
if (treeAttr(fill, "hasColor") === "false" || treeAttr(fill, "transparent") === "true") {
|
|
865
|
+
noteAt(at("fill"), STYLE_ISSUE.NO_FILL, "transparent");
|
|
866
|
+
put("fillOpacity", 0);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
readColor(treeAttr(fill, "color"), "fill", "fillOpacity", put, at);
|
|
870
|
+
const second = canonColor(treeAttr(fill, "color2"), "yed", at("fillGradient"));
|
|
871
|
+
if (second !== null) {
|
|
872
|
+
put("fillGradient", second.color);
|
|
873
|
+
put("fillGradientAngle", YED_GRADIENT_ANGLE);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* A `y:TableNode`'s `yed.table.*` style property, as a color.
|
|
879
|
+
* @param real - the table realizer
|
|
880
|
+
* @param name - the property's name
|
|
881
|
+
* @param at - the sites
|
|
882
|
+
* @returns the `#rrggbb` color, or null when the table does not set it
|
|
883
|
+
*/
|
|
884
|
+
function tableColor(real: Realizer, name: string, at: SiteOf): string | null {
|
|
885
|
+
const props = treeChildren(treeChild(real.r, "StyleProperties"), "Property");
|
|
886
|
+
const prop = props.find((p) => treeAttr(p, "name") === name);
|
|
887
|
+
return prop === undefined ? null : (canonColor(treeAttr(prop, "value"), "yed", at("stripeColors"))?.color ?? null);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Reads a `y:TableNode` (a yEd table or BPMN pool) as bands over the node: its header (the table's top inset, or
|
|
892
|
+
* its left one for columns side by side) in `yed.table.header.color.main`, then its rows top to bottom (or its
|
|
893
|
+
* columns left to right) in `yed.table.lane.color.main` and `.alternating` by turns, then the opposite inset. A
|
|
894
|
+
* color the table does not set is the node's fill. The lines between the lanes and the row and column headers
|
|
895
|
+
* (`yed.table.header.color.alternating`) are not drawn.
|
|
896
|
+
* @param real - the realizer
|
|
897
|
+
* @param put - receives the values
|
|
898
|
+
* @param at - the sites
|
|
899
|
+
*/
|
|
900
|
+
function readTable(real: Realizer, put: PutValue, at: SiteOf): void {
|
|
901
|
+
const table = treeChild(real.r, "Table");
|
|
902
|
+
const rows = treeChildren(treeChild(table, "Rows"), "Row");
|
|
903
|
+
const columns = treeChildren(treeChild(table, "Columns"), "Column");
|
|
904
|
+
const across = columns.length > 1 && rows.length <= 1;
|
|
905
|
+
const lanes = (across ? columns : rows).map((l) => Number(treeAttr(l, across ? "width" : "height")));
|
|
906
|
+
const insets = treeChild(table, "Insets");
|
|
907
|
+
const inset = (side: string): number => Math.max(0, Number(treeAttr(insets, side) ?? 0) || 0);
|
|
908
|
+
const [head, tail] = across ? [inset("left"), inset("right")] : [inset("top"), inset("bottom")];
|
|
909
|
+
const sizes = [head, ...lanes, tail];
|
|
910
|
+
const total = sizes.reduce((a, b) => a + b, 0);
|
|
911
|
+
if (lanes.length === 0 || !sizes.every(Number.isFinite) || total <= 0) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
const fill =
|
|
915
|
+
canonColor(treeAttr(treeChild(real.r, "Fill"), "color"), "yed", at("stripeColors"))?.color ?? "#ffffff";
|
|
916
|
+
const main = tableColor(real, "yed.table.lane.color.main", at) ?? fill;
|
|
917
|
+
const alternating = tableColor(real, "yed.table.lane.color.alternating", at) ?? main;
|
|
918
|
+
const header = tableColor(real, "yed.table.header.color.main", at) ?? fill;
|
|
919
|
+
const bands = (
|
|
920
|
+
[[head, header], ...lanes.map((size, i) => [size, i % 2 === 0 ? main : alternating]), [tail, fill]] as [
|
|
921
|
+
number,
|
|
922
|
+
string,
|
|
923
|
+
][]
|
|
924
|
+
).filter(([size]) => size > 0);
|
|
925
|
+
put("stripeColors", bands.map(([, color]) => color).join(" "));
|
|
926
|
+
put("stripeSizes", bands.map(([size]) => String(Number((size / total).toFixed(6)))).join(" "));
|
|
927
|
+
if (across) {
|
|
928
|
+
put("stripeDirection", "right");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* yEd's shared pictures (`<y:Resources>` on the graph): each resource's id to the picture as a URL, an SVG's text as
|
|
934
|
+
* a `data:image/svg+xml` URL and a `java.awt.image.BufferedImage` (base64 PNG) as a `data:image/png` one.
|
|
935
|
+
* @param snapshot - the graph
|
|
936
|
+
* @returns the pictures by id
|
|
937
|
+
*/
|
|
938
|
+
function yedResources(snapshot: GraphSnapshot): ReadonlyMap<string, string> {
|
|
939
|
+
const out = new Map<string, string>();
|
|
940
|
+
for (const col of snapshot.graph) {
|
|
941
|
+
if (col.dtype !== "json" || col.meta.origin?.namespace !== "yfiles") {
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
addYedResources(plainCell(snapshot.graph.value(col.meta.name, 0)), out);
|
|
945
|
+
}
|
|
946
|
+
return out;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Adds the pictures of one yFiles graph value's `<y:Resources>`; an id already there keeps its picture.
|
|
951
|
+
* @param root - the graph's yFiles value
|
|
952
|
+
* @param out - the pictures by id, added to
|
|
953
|
+
*/
|
|
954
|
+
function addYedResources(root: unknown, out: Map<string, string>): void {
|
|
955
|
+
for (const resources of treeChildren(root, "Resources")) {
|
|
956
|
+
for (const r of treeChildren(resources, "Resource")) {
|
|
957
|
+
const id = treeAttr(r, "id");
|
|
958
|
+
const url = id === undefined || out.has(id) ? null : yedResourceUrl(r);
|
|
959
|
+
if (id !== undefined && url !== null) {
|
|
960
|
+
out.set(id, url);
|
|
518
961
|
}
|
|
519
962
|
}
|
|
520
963
|
}
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* One `<y:Resource>` as a picture URL: a `java.awt.image.BufferedImage` (base64 PNG) as a `data:image/png` URL and
|
|
968
|
+
* an SVG's text as a `data:image/svg+xml` one.
|
|
969
|
+
* @param r - the resource element
|
|
970
|
+
* @returns the URL, or null for an empty resource or one that is no picture
|
|
971
|
+
*/
|
|
972
|
+
function yedResourceUrl(r: unknown): string | null {
|
|
973
|
+
const text = treeText(r)?.trim() ?? "";
|
|
974
|
+
if (text === "") {
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
const type = treeAttr(r, "type") ?? "";
|
|
978
|
+
if (type.endsWith("BufferedImage")) {
|
|
979
|
+
return `data:image/png;base64,${text.replaceAll(/\s+/g, "")}`;
|
|
980
|
+
}
|
|
981
|
+
if (text.includes("<svg")) {
|
|
982
|
+
return `data:image/svg+xml;utf8,${encodeURIComponent(text)}`;
|
|
983
|
+
}
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* The picture an image or SVG node realizer draws (`y:Image refid`, `y:SVGModel/y:SVGContent refid`), stretched over
|
|
989
|
+
* the node's box, as yEd draws `y:ImageNode` and `y:SVGNode`. The realizer's own fill and border are kept beneath it:
|
|
990
|
+
* whether yEd paints them under the picture waits for a drawing by yEd itself (ISSUES.md).
|
|
991
|
+
* @param real - the realizer
|
|
992
|
+
* @param resources - the graph's shared pictures
|
|
993
|
+
* @param put - receives the values
|
|
994
|
+
*/
|
|
995
|
+
function readPicture(real: Realizer, resources: ReadonlyMap<string, string>, put: PutValue): void {
|
|
996
|
+
const ref =
|
|
997
|
+
real.type === "ImageNode"
|
|
998
|
+
? treeAttr(treeChild(real.r, "Image"), "refid")
|
|
999
|
+
: treeAttr(treeChild(treeChild(real.r, "SVGModel"), "SVGContent"), "refid");
|
|
1000
|
+
const src = ref === undefined ? undefined : resources.get(ref);
|
|
1001
|
+
if (src === undefined) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
put("image", src);
|
|
1005
|
+
put("imageFit", "fill");
|
|
1006
|
+
put("imageClip", "none");
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Reads one node realizer.
|
|
1011
|
+
* @param real - the realizer
|
|
1012
|
+
* @param put - receives the values
|
|
1013
|
+
* @param at - the sites
|
|
1014
|
+
* @param resources - the graph's shared pictures, for image and SVG nodes
|
|
1015
|
+
*/
|
|
1016
|
+
function readNode(real: Realizer, put: PutValue, at: SiteOf, resources?: ReadonlyMap<string, string>): void {
|
|
1017
|
+
const { r } = real;
|
|
1018
|
+
readFill(real, put, at);
|
|
521
1019
|
const border = treeChild(r, "BorderStyle");
|
|
522
1020
|
if (border !== undefined) {
|
|
523
1021
|
if (treeAttr(border, "hasColor") === "false") {
|
|
@@ -532,7 +1030,18 @@ function readNode(real: Realizer, put: PutValue, at: SiteOf): void {
|
|
|
532
1030
|
put("width", canonNumber(treeAttr(geometry, "width"), at("width")));
|
|
533
1031
|
put("height", canonNumber(treeAttr(geometry, "height"), at("height")));
|
|
534
1032
|
put("shape", realizerShape(real, at));
|
|
535
|
-
|
|
1033
|
+
const points =
|
|
1034
|
+
real.type === "GenericNode" ? configurationOutline(treeAttr(r, "configuration") ?? "")?.points : undefined;
|
|
1035
|
+
if (points !== undefined) {
|
|
1036
|
+
put("shapePoints", points);
|
|
1037
|
+
}
|
|
1038
|
+
if (real.type === "TableNode") {
|
|
1039
|
+
readTable(real, put, at);
|
|
1040
|
+
}
|
|
1041
|
+
if (resources !== undefined && (real.type === "ImageNode" || real.type === "SVGNode")) {
|
|
1042
|
+
readPicture(real, resources, put);
|
|
1043
|
+
}
|
|
1044
|
+
readLabels(treeChildren(r, "NodeLabel"), true, put, at, geometry);
|
|
536
1045
|
}
|
|
537
1046
|
|
|
538
1047
|
/**
|
|
@@ -555,12 +1064,21 @@ function readEdge(real: Realizer, put: PutValue, at: SiteOf): void {
|
|
|
555
1064
|
put("targetArrow", canonEnum("arrow", treeAttr(arrows, "target"), "yed", at("targetArrow")));
|
|
556
1065
|
}
|
|
557
1066
|
put("curved", edgeCurved(real));
|
|
1067
|
+
if (real.type === "ArcEdge") {
|
|
1068
|
+
// the bend yEd saves is the arc's apex, `y:Arc height` from the line between the ends (every yEd-saved arc
|
|
1069
|
+
// in the corpus: scala-collection-mutable, Animatron__player__flow), so the arc is the circle through it
|
|
1070
|
+
put("bendCurve", "arc");
|
|
1071
|
+
}
|
|
558
1072
|
readLabels(treeChildren(r, "EdgeLabel"), false, put, at);
|
|
559
1073
|
}
|
|
560
1074
|
|
|
561
1075
|
/**
|
|
562
|
-
* Whether
|
|
563
|
-
*
|
|
1076
|
+
* Whether an edge is drawn curved through its bends: an arc always; a spline only through its control points (with
|
|
1077
|
+
* none it is a straight line); a polyline, and a generic edge (a framed or styled polyline), only when it smooths
|
|
1078
|
+
* bends it has. A quad curve edge never, nor a Bezier edge of whole cubic pieces (two control points, five, ...): its
|
|
1079
|
+
* curve is flattened into points on it (`readEdgeRoute`), drawn straight from one to the next, as a Graphviz spline
|
|
1080
|
+
* is; rounding their corners cut inside the curve (a quad curve of straightness 1, whose points are its control
|
|
1081
|
+
* polygon, was drawn bowed).
|
|
564
1082
|
* @param real - the edge realizer
|
|
565
1083
|
* @returns true when curved, null when a polyline does not say whether it smooths
|
|
566
1084
|
*/
|
|
@@ -568,8 +1086,16 @@ function edgeCurved(real: Realizer): boolean | null {
|
|
|
568
1086
|
if (real.type === "ArcEdge") {
|
|
569
1087
|
return true;
|
|
570
1088
|
}
|
|
571
|
-
const
|
|
572
|
-
if (real.type
|
|
1089
|
+
const points = treeChildren(treeChild(real.r, "Path"), "Point").length;
|
|
1090
|
+
if (real.type === "QuadCurveEdge") {
|
|
1091
|
+
return false;
|
|
1092
|
+
}
|
|
1093
|
+
if (real.type === "BezierEdge") {
|
|
1094
|
+
// a count of control points no run of cubic pieces takes is not flattened: its corners are rounded
|
|
1095
|
+
return points > 0 && (points + 1) % 3 !== 0;
|
|
1096
|
+
}
|
|
1097
|
+
const bent = points > 0;
|
|
1098
|
+
if (real.type !== "PolyLineEdge" && real.type !== "GenericEdge") {
|
|
573
1099
|
return bent;
|
|
574
1100
|
}
|
|
575
1101
|
const smoothed = treeAttr(treeChild(real.r, "BendStyle"), "smoothed");
|
|
@@ -577,23 +1103,52 @@ function edgeCurved(real: Realizer): boolean | null {
|
|
|
577
1103
|
}
|
|
578
1104
|
|
|
579
1105
|
/**
|
|
580
|
-
* An edge's
|
|
581
|
-
*
|
|
1106
|
+
* An edge's route from its `y:Path`: the ports its ends attach at (`sx sy`, `tx ty`: offsets from the node centers),
|
|
1107
|
+
* relative to each node's size, and its bends, relative to the centers of its two nodes (a self-loop's to its node's
|
|
1108
|
+
* size). yEd draws a polyline, an arc or a spline through the points; a Bezier or quad curve edge takes them as
|
|
1109
|
+
* control points its curve does not pass through, so its curve is flattened into the points it does pass through.
|
|
582
1110
|
* @param real - the edge realizer
|
|
583
|
-
* @param
|
|
1111
|
+
* @param nodes - each node's center x and y, width and height
|
|
584
1112
|
* @param source - the source node's index
|
|
585
1113
|
* @param target - the target node's index
|
|
586
|
-
* @
|
|
1114
|
+
* @param put - receives the values
|
|
587
1115
|
*/
|
|
588
|
-
function
|
|
589
|
-
const
|
|
1116
|
+
function readEdgeRoute(real: Realizer, nodes: Float64Array, source: number, target: number, put: PutValue): void {
|
|
1117
|
+
const path = treeChild(real.r, "Path");
|
|
1118
|
+
const at = (k: number): BendPoint => [nodes[4 * k], nodes[4 * k + 1]];
|
|
1119
|
+
const size = (k: number): BendPoint => [nodes[4 * k + 2], nodes[4 * k + 3]];
|
|
1120
|
+
const offset = (key: string): number => Number(treeAttr(path, key) ?? 0) || 0;
|
|
1121
|
+
const ends: readonly [BendPoint, BendPoint] = [
|
|
1122
|
+
[at(source)[0] + offset("sx"), at(source)[1] + offset("sy")],
|
|
1123
|
+
[at(target)[0] + offset("tx"), at(target)[1] + offset("ty")],
|
|
1124
|
+
];
|
|
1125
|
+
const port = (k: number, end: BendPoint): string | null =>
|
|
1126
|
+
end[0] === at(k)[0] && end[1] === at(k)[1] ? null : canonNodeOffsets(at(k), size(k), [end], true);
|
|
1127
|
+
put("sourcePort", port(source, ends[0]));
|
|
1128
|
+
put("targetPort", port(target, ends[1]));
|
|
1129
|
+
let points = treeChildren(path, "Point").map((p): BendPoint => [
|
|
590
1130
|
Number(treeAttr(p, "x")),
|
|
591
1131
|
Number(treeAttr(p, "y")),
|
|
592
1132
|
]);
|
|
593
|
-
|
|
594
|
-
|
|
1133
|
+
if (points.length > 0 && (real.type === "BezierEdge" || real.type === "QuadCurveEdge")) {
|
|
1134
|
+
const controls = [ends[0], ...points, ends[1]];
|
|
1135
|
+
const curve =
|
|
1136
|
+
real.type === "BezierEdge"
|
|
1137
|
+
? flattenCubic(controls)
|
|
1138
|
+
: flattenQuadSpline(controls, Number(treeAttr(real.r, "straightness") ?? YED_STRAIGHTNESS));
|
|
1139
|
+
points = (curve ?? controls).slice(1, -1);
|
|
1140
|
+
}
|
|
1141
|
+
put(
|
|
1142
|
+
"bends",
|
|
1143
|
+
source === target
|
|
1144
|
+
? canonNodeOffsets(at(source), size(source), points, true)
|
|
1145
|
+
: canonBends(at(source), at(target), points, true),
|
|
1146
|
+
);
|
|
595
1147
|
}
|
|
596
1148
|
|
|
1149
|
+
/** yEd's quad curve straightness when a file gives none. */
|
|
1150
|
+
const YED_STRAIGHTNESS = 0.1;
|
|
1151
|
+
|
|
597
1152
|
/** Canonical values per property, by element index. */
|
|
598
1153
|
export type ValueColumns = Record<string, (StyleValue | null)[]>;
|
|
599
1154
|
|
|
@@ -635,8 +1190,9 @@ export interface YedRead {
|
|
|
635
1190
|
*/
|
|
636
1191
|
export function readYedStyles(snapshot: GraphSnapshot, notes: StyleNotes, budget: StyleWorkBudget): YedRead | null {
|
|
637
1192
|
let found = false;
|
|
638
|
-
|
|
639
|
-
|
|
1193
|
+
const resources = yedResources(snapshot);
|
|
1194
|
+
// each node's center, width and height, read with the node, for its edges' routes; NaN without yEd graphics
|
|
1195
|
+
const boxes = new Float64Array(4 * snapshot.nodeCount).fill(Number.NaN);
|
|
640
1196
|
const read = (target: StyleTarget): ValueColumns => {
|
|
641
1197
|
const table = target === "node" ? snapshot.nodes : snapshot.edges;
|
|
642
1198
|
const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
|
|
@@ -661,13 +1217,16 @@ export function readYedStyles(snapshot: GraphSnapshot, notes: StyleNotes, budget
|
|
|
661
1217
|
out.put(i, property, value);
|
|
662
1218
|
};
|
|
663
1219
|
if (target === "node") {
|
|
664
|
-
readNode(real, put, at);
|
|
1220
|
+
readNode(real, put, at, resources);
|
|
665
1221
|
const geometry = treeChild(real.r, "Geometry");
|
|
666
|
-
|
|
667
|
-
|
|
1222
|
+
const [w, h] = [Number(treeAttr(geometry, "width")), Number(treeAttr(geometry, "height"))];
|
|
1223
|
+
boxes.set(
|
|
1224
|
+
[Number(treeAttr(geometry, "x")) + w / 2, Number(treeAttr(geometry, "y")) + h / 2, w, h],
|
|
1225
|
+
4 * i,
|
|
1226
|
+
);
|
|
668
1227
|
} else {
|
|
669
1228
|
readEdge(real, put, at);
|
|
670
|
-
|
|
1229
|
+
readEdgeRoute(real, boxes, snapshot.edgeSource(i), snapshot.edgeTarget(i), put);
|
|
671
1230
|
}
|
|
672
1231
|
}
|
|
673
1232
|
return out.values;
|
|
@@ -689,6 +1248,8 @@ export const GRAPHML_STYLE_CAPABILITIES: StyleWriteCapabilities = Object.freeze(
|
|
|
689
1248
|
node: Object.freeze([
|
|
690
1249
|
"fill",
|
|
691
1250
|
"fillOpacity",
|
|
1251
|
+
"fillGradient",
|
|
1252
|
+
"fillGradientAngle",
|
|
692
1253
|
"borderColor",
|
|
693
1254
|
"borderWidth",
|
|
694
1255
|
"borderStyle",
|
|
@@ -712,6 +1273,8 @@ export const GRAPHML_STYLE_CAPABILITIES: StyleWriteCapabilities = Object.freeze(
|
|
|
712
1273
|
"targetArrow",
|
|
713
1274
|
"curved",
|
|
714
1275
|
"bends",
|
|
1276
|
+
"sourcePort",
|
|
1277
|
+
"targetPort",
|
|
715
1278
|
"label",
|
|
716
1279
|
"labelColor",
|
|
717
1280
|
"labelSize",
|
|
@@ -753,9 +1316,9 @@ export const YED_LINES: ReadonlyMap<string, string> = new Map([
|
|
|
753
1316
|
/** yEd's arrows, by the neutral arrow each draws exactly. */
|
|
754
1317
|
export const YED_ARROWS: ReadonlyMap<string, string> = new Map([
|
|
755
1318
|
["none", "none"],
|
|
756
|
-
["triangle", "
|
|
1319
|
+
["triangle", "delta"],
|
|
757
1320
|
["triangle-open", "white_delta"],
|
|
758
|
-
["vee", "
|
|
1321
|
+
["vee", "standard"],
|
|
759
1322
|
["tee", "t_shape"],
|
|
760
1323
|
["diamond", "diamond"],
|
|
761
1324
|
["diamond-open", "white_diamond"],
|
|
@@ -765,21 +1328,21 @@ export const YED_ARROWS: ReadonlyMap<string, string> = new Map([
|
|
|
765
1328
|
["crow", "crows_foot_many"],
|
|
766
1329
|
]);
|
|
767
1330
|
|
|
768
|
-
/**
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
["top",
|
|
776
|
-
["bottom",
|
|
777
|
-
["left",
|
|
778
|
-
["right",
|
|
779
|
-
["top-left",
|
|
780
|
-
["top-right",
|
|
781
|
-
["bottom-left",
|
|
782
|
-
["bottom-right",
|
|
1331
|
+
/** yEd's internal label model positions by neutral position: the center, and against a side or in a corner inside. */
|
|
1332
|
+
const INTERNAL_MODEL_POSITIONS: ReadonlyMap<string, string> = new Map(
|
|
1333
|
+
[...INTERNAL_POSITIONS].map(([model, position]) => [position, model]),
|
|
1334
|
+
);
|
|
1335
|
+
|
|
1336
|
+
/** yEd's eight-position label model positions by neutral position, all outside the node. */
|
|
1337
|
+
const YED_MODEL_POSITIONS: ReadonlyMap<string, string> = new Map([
|
|
1338
|
+
["top", "n"],
|
|
1339
|
+
["bottom", "s"],
|
|
1340
|
+
["left", "w"],
|
|
1341
|
+
["right", "e"],
|
|
1342
|
+
["top-left", "nw"],
|
|
1343
|
+
["top-right", "ne"],
|
|
1344
|
+
["bottom-left", "sw"],
|
|
1345
|
+
["bottom-right", "se"],
|
|
783
1346
|
]);
|
|
784
1347
|
|
|
785
1348
|
/**
|
|
@@ -847,22 +1410,32 @@ export function yedSpelled(
|
|
|
847
1410
|
/** One element's value of a property. */
|
|
848
1411
|
type GetValue = (property: string) => StyleValue | null;
|
|
849
1412
|
|
|
1413
|
+
/** The Java logical font each family the reader gives one is written back as. */
|
|
1414
|
+
const JAVA_LOGICAL: Readonly<Record<string, string>> = {
|
|
1415
|
+
[JAVA_SANS]: "Dialog",
|
|
1416
|
+
[JAVA_SERIF]: "Serif",
|
|
1417
|
+
[JAVA_MONO]: "Monospaced",
|
|
1418
|
+
};
|
|
1419
|
+
|
|
850
1420
|
/**
|
|
851
|
-
*
|
|
1421
|
+
* A CSS family as the one font name yEd reads: a Java logical font's family as that font, else the list's first name.
|
|
1422
|
+
* @param family - the CSS family or family list
|
|
1423
|
+
* @returns the font name
|
|
1424
|
+
*/
|
|
1425
|
+
export function yedFontName(family: string): string {
|
|
1426
|
+
return Object.hasOwn(JAVA_LOGICAL, family) ? JAVA_LOGICAL[family] : family.split(",")[0].trim();
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/**
|
|
1430
|
+
* A label's font attributes: family, size and style.
|
|
852
1431
|
* @param get - the element's values
|
|
853
|
-
* @
|
|
854
|
-
* @param approximate - records approximations
|
|
855
|
-
* @returns the label element, or null when the element has no label text
|
|
1432
|
+
* @returns the label element holding them
|
|
856
1433
|
*/
|
|
857
|
-
function
|
|
858
|
-
const text = get("label");
|
|
859
|
-
if (typeof text !== "string" || text.trim().length === 0) {
|
|
860
|
-
return null;
|
|
861
|
-
}
|
|
1434
|
+
function labelFontTree(get: GetValue): Tree {
|
|
862
1435
|
const label: Tree = {};
|
|
863
1436
|
const font = get("labelFont");
|
|
864
1437
|
if (typeof font === "string") {
|
|
865
|
-
label["@_fontFamily"] = font;
|
|
1438
|
+
label["@_fontFamily"] = yedFontName(font);
|
|
866
1439
|
}
|
|
867
1440
|
const size = get("labelSize");
|
|
868
1441
|
if (typeof size === "number") {
|
|
@@ -874,18 +1447,36 @@ function labelTree(get: GetValue, node: boolean, approximate: Approximate): Tree
|
|
|
874
1447
|
const style = (weight === "bold" ? "bold" : "") + (italic === true ? "italic" : "");
|
|
875
1448
|
label["@_fontStyle"] = style.length === 0 ? "plain" : style;
|
|
876
1449
|
}
|
|
1450
|
+
return label;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* The label attributes and text of one element.
|
|
1455
|
+
* @param get - the element's values
|
|
1456
|
+
* @param node - whether it is a node label (which has a position)
|
|
1457
|
+
* @param approximate - records approximations
|
|
1458
|
+
* @returns the label element, or null when the element has no label text
|
|
1459
|
+
*/
|
|
1460
|
+
function labelTree(get: GetValue, node: boolean, approximate: Approximate): Tree | null {
|
|
1461
|
+
const text = get("label");
|
|
1462
|
+
if (typeof text !== "string" || text.trim().length === 0) {
|
|
1463
|
+
return null;
|
|
1464
|
+
}
|
|
1465
|
+
const label = labelFontTree(get);
|
|
877
1466
|
const color = get("labelColor");
|
|
878
1467
|
if (typeof color === "string") {
|
|
879
1468
|
label["@_textColor"] = color.toUpperCase();
|
|
880
1469
|
}
|
|
881
1470
|
const position = node ? get("labelPosition") : null;
|
|
882
1471
|
if (typeof position === "string") {
|
|
883
|
-
const
|
|
884
|
-
|
|
1472
|
+
const inside = INTERNAL_MODEL_POSITIONS.get(position);
|
|
1473
|
+
const outside = YED_MODEL_POSITIONS.get(position);
|
|
1474
|
+
if (inside === undefined && outside === undefined) {
|
|
885
1475
|
approximate("labelPosition", position);
|
|
886
1476
|
}
|
|
887
|
-
|
|
888
|
-
label["@
|
|
1477
|
+
// the center and the inside positions in yEd's internal model, the outside ones in its eight-position one
|
|
1478
|
+
label["@_modelName"] = outside === undefined ? "internal" : "eight_pos";
|
|
1479
|
+
label["@_modelPosition"] = outside ?? inside ?? "c";
|
|
889
1480
|
}
|
|
890
1481
|
label["#text"] = text;
|
|
891
1482
|
return label;
|
|
@@ -905,6 +1496,14 @@ function shapeNodeTree(get: GetValue, at: readonly [string, string] | null, appr
|
|
|
905
1496
|
const fillOpacity = get("fillOpacity");
|
|
906
1497
|
if (typeof fill === "string") {
|
|
907
1498
|
node["y:Fill"] = { "@_color": yedColor(fill, fillOpacity), "@_transparent": "false" };
|
|
1499
|
+
const second = get("fillGradient");
|
|
1500
|
+
if (typeof second === "string") {
|
|
1501
|
+
(node["y:Fill"] as Tree)["@_color2"] = second.toUpperCase();
|
|
1502
|
+
const angle = get("fillGradientAngle");
|
|
1503
|
+
if (angle !== YED_GRADIENT_ANGLE) {
|
|
1504
|
+
approximate("fillGradientAngle", angle ?? 0);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
908
1507
|
} else if (fillOpacity === 0) {
|
|
909
1508
|
node["y:Fill"] = { "@_hasColor": "false", "@_transparent": "false" };
|
|
910
1509
|
}
|
|
@@ -1004,7 +1603,7 @@ function polyLineEdgeTree(get: GetValue, path: unknown, approximate: Approximate
|
|
|
1004
1603
|
const target = get("targetArrow");
|
|
1005
1604
|
if (source !== null || target !== null) {
|
|
1006
1605
|
const arrow = (property: string, value: StyleValue | null): string =>
|
|
1007
|
-
typeof value === "string" ? yedSpelled(YED_ARROWS, value, "
|
|
1606
|
+
typeof value === "string" ? yedSpelled(YED_ARROWS, value, "delta", property, approximate) : "none";
|
|
1008
1607
|
edge["y:Arrows"] = { "@_source": arrow("sourceArrow", source), "@_target": arrow("targetArrow", target) };
|
|
1009
1608
|
}
|
|
1010
1609
|
const label = labelTree(get, false, approximate);
|
|
@@ -1018,59 +1617,25 @@ function polyLineEdgeTree(get: GetValue, path: unknown, approximate: Approximate
|
|
|
1018
1617
|
return { "y:PolyLineEdge": edge };
|
|
1019
1618
|
}
|
|
1020
1619
|
|
|
1021
|
-
/**
|
|
1022
|
-
* Each element's values of the properties the writer writes, as a getter per element.
|
|
1023
|
-
* @param picture - each element's value of a property
|
|
1024
|
-
* @param target - nodes or edges
|
|
1025
|
-
* @returns the getter
|
|
1026
|
-
*/
|
|
1027
|
-
function getter(
|
|
1028
|
-
picture: (target: StyleTarget, property: string) => readonly (StyleValue | null)[],
|
|
1029
|
-
target: StyleTarget,
|
|
1030
|
-
): (i: number) => GetValue {
|
|
1031
|
-
const columns = new Map<string, readonly (StyleValue | null)[]>();
|
|
1032
|
-
for (const p of GRAPHML_STYLE_CAPABILITIES[target]) {
|
|
1033
|
-
columns.set(p, picture(target, p));
|
|
1034
|
-
}
|
|
1035
|
-
return (i) => (property) => columns.get(property)?.[i] ?? null;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
1620
|
/**
|
|
1039
1621
|
* For a file graphty wrote: where a value read from the yEd graphics is what the writer writes for the block's own
|
|
1040
1622
|
* value (a pentagon written as a hexagon, an opacity rounded to yEd's 255 steps), the block's exact value is the
|
|
1041
1623
|
* truth, so a re-import gives back the same rules; a value another program changed since stays the truth.
|
|
1624
|
+
* @param snapshot - the graph
|
|
1625
|
+
* @param text - the block's text
|
|
1042
1626
|
* @param read - the values the reader read, changed in place
|
|
1043
|
-
* @param picture - each element's value of a property under the block's rules
|
|
1044
|
-
* @param counts - the node and edge counts
|
|
1045
|
-
* @param counts.node - the node count
|
|
1046
|
-
* @param counts.edge - the edge count
|
|
1047
|
-
* @param notes - a scratch note list for reading back what the writer writes
|
|
1048
1627
|
*/
|
|
1049
|
-
export function exactFromBlock(
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
const values = read[target];
|
|
1057
|
-
const get = getter(picture, target);
|
|
1058
|
-
for (let i = 0; i < counts[target]; i++) {
|
|
1059
|
-
const want = get(i);
|
|
1628
|
+
export function exactFromBlock(snapshot: GraphSnapshot, text: string, read: YedRead): void {
|
|
1629
|
+
const notes = new StyleNotes();
|
|
1630
|
+
takeFromBlock(snapshot, text, "graphml", (target) => ({
|
|
1631
|
+
values: read[target],
|
|
1632
|
+
properties: GRAPHML_STYLE_CAPABILITIES[target],
|
|
1633
|
+
writes: GRAPHML_STYLE_CAPABILITIES[target],
|
|
1634
|
+
readBack: (_i, want) => {
|
|
1060
1635
|
const back = writtenAndReadBack(target, want, notes);
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
for (const property of GRAPHML_STYLE_CAPABILITIES[target]) {
|
|
1065
|
-
const native = values[property]?.[i] ?? null;
|
|
1066
|
-
const w = want(property);
|
|
1067
|
-
if (native !== w && native === (back.get(property) ?? null)) {
|
|
1068
|
-
values[property] ??= new Array<StyleValue | null>(counts[target]).fill(null);
|
|
1069
|
-
values[property][i] = w;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1636
|
+
return back === null ? null : (property) => back.get(property) ?? null;
|
|
1637
|
+
},
|
|
1638
|
+
}));
|
|
1074
1639
|
}
|
|
1075
1640
|
|
|
1076
1641
|
/**
|
|
@@ -1182,25 +1747,44 @@ function keptPath(table: GraphSnapshot["edges"], columns: readonly string[], e:
|
|
|
1182
1747
|
return undefined;
|
|
1183
1748
|
}
|
|
1184
1749
|
|
|
1750
|
+
/** A written node's center and its width and height. */
|
|
1751
|
+
type WrittenBox = readonly [center: BendPoint, size: BendPoint];
|
|
1752
|
+
|
|
1185
1753
|
/**
|
|
1186
|
-
* An edge's `y:Path` with its points placed from its `bends` value between the written node centers
|
|
1187
|
-
*
|
|
1188
|
-
* had.
|
|
1754
|
+
* An edge's `y:Path` with its points placed from its `bends` value between the written node centers (a self-loop's
|
|
1755
|
+
* around its node) and its ports from its `sourcePort` and `targetPort`, so they read back as written; a port with no
|
|
1756
|
+
* value keeps the one of the path it had. Without a bend or port value, or without both centers, the path it had.
|
|
1189
1757
|
* @param kept - the path from the graphics the edge had, or undefined
|
|
1190
|
-
* @param
|
|
1191
|
-
* @param ends - the written
|
|
1758
|
+
* @param get - the edge's values
|
|
1759
|
+
* @param ends - the written boxes of its source and target
|
|
1760
|
+
* @param loop - whether the edge is a self-loop
|
|
1192
1761
|
* @returns the path, or undefined
|
|
1193
1762
|
*/
|
|
1194
|
-
function
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1763
|
+
function pathWithRoute(kept: unknown, get: GetValue, ends: readonly [WrittenBox, WrittenBox], loop: boolean): unknown {
|
|
1764
|
+
const bends = get("bends");
|
|
1765
|
+
const [[c0, size0], [c1]] = ends;
|
|
1766
|
+
let points: BendPoint[] | null = null;
|
|
1767
|
+
if (typeof bends === "string") {
|
|
1768
|
+
points = loop ? nodeOffsetsToPoints(c0, size0, bends, true) : bendsToPoints(c0, c1, bends, true);
|
|
1769
|
+
}
|
|
1770
|
+
const ports = (["sourcePort", "targetPort"] as const).map((property, k) => {
|
|
1771
|
+
const value = get(property);
|
|
1772
|
+
const [center, size] = ends[k];
|
|
1773
|
+
const at = typeof value === "string" ? nodeOffsetsToPoints(center, size, value, true) : null;
|
|
1774
|
+
return at === null ? null : [at[0][0] - center[0], at[0][1] - center[1]];
|
|
1775
|
+
});
|
|
1776
|
+
if (points === null && ports.every((p) => p === null)) {
|
|
1197
1777
|
return kept;
|
|
1198
1778
|
}
|
|
1199
1779
|
const path: Tree = {};
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1780
|
+
["sx", "sy", "tx", "ty"].forEach((key, k) => {
|
|
1781
|
+
const port = ports[k >> 1];
|
|
1782
|
+
path[`@_${key}`] = port === null ? (treeAttr(kept, key) ?? "0.0") : String(port[k & 1]);
|
|
1783
|
+
});
|
|
1784
|
+
const written =
|
|
1785
|
+
points ??
|
|
1786
|
+
treeChildren(kept, "Point").map((q): BendPoint => [Number(treeAttr(q, "x")), Number(treeAttr(q, "y"))]);
|
|
1787
|
+
path["y:Point"] = written.map(([x, y]) => ({ "@_x": String(x), "@_y": String(y) }));
|
|
1204
1788
|
return path;
|
|
1205
1789
|
}
|
|
1206
1790
|
|
|
@@ -1246,27 +1830,32 @@ export function graphmlStyledExport(
|
|
|
1246
1830
|
input.picture(target, property as never);
|
|
1247
1831
|
const nodeColumns = treeColumns(snapshot.nodes);
|
|
1248
1832
|
const edgeColumns = treeColumns(snapshot.edges);
|
|
1249
|
-
const getNode =
|
|
1250
|
-
const getEdge =
|
|
1833
|
+
const getNode = elementValues(picture, "node", GRAPHML_STYLE_CAPABILITIES.node);
|
|
1834
|
+
const getEdge = elementValues(picture, "edge", GRAPHML_STYLE_CAPABILITIES.edge);
|
|
1251
1835
|
const nodeTrees: (Tree | null)[] = [];
|
|
1252
1836
|
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
1253
1837
|
const get = getNode(i);
|
|
1254
1838
|
const at = keptPosition(snapshot.nodes, nodeColumns, i) ?? columnPosition(snapshot, i, get);
|
|
1255
1839
|
nodeTrees.push(shapeNodeTree(get, at, approximateFor("node")));
|
|
1256
1840
|
}
|
|
1257
|
-
const
|
|
1841
|
+
const boxes = nodeTrees.map((tree): WrittenBox => {
|
|
1258
1842
|
const geometry = treeChild(treeChild(tree, "ShapeNode"), "Geometry");
|
|
1259
1843
|
const at = (k: string): number => Number(treeAttr(geometry, k) ?? Number.NaN);
|
|
1260
|
-
return [
|
|
1844
|
+
return [
|
|
1845
|
+
[at("x") + at("width") / 2, at("y") + at("height") / 2],
|
|
1846
|
+
[at("width"), at("height")],
|
|
1847
|
+
];
|
|
1261
1848
|
});
|
|
1262
1849
|
const edgeTrees: (Tree | null)[] = [];
|
|
1263
1850
|
for (let e = 0; e < snapshot.edgeCount; e++) {
|
|
1264
1851
|
const get = getEdge(e);
|
|
1265
|
-
const
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1852
|
+
const [source, target] = [snapshot.edgeSource(e), snapshot.edgeTarget(e)];
|
|
1853
|
+
const path = pathWithRoute(
|
|
1854
|
+
keptPath(snapshot.edges, edgeColumns, e),
|
|
1855
|
+
get,
|
|
1856
|
+
[boxes[source], boxes[target]],
|
|
1857
|
+
source === target,
|
|
1858
|
+
);
|
|
1270
1859
|
edgeTrees.push(polyLineEdgeTree(get, path, approximateFor("edge")));
|
|
1271
1860
|
}
|
|
1272
1861
|
const out = snapshot.withColumns();
|