@graphty/graph-io 0.3.42 → 0.3.44
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-3Q4TsdTh.js +40 -0
- package/dist/chunks/approximations-3Q4TsdTh.js.map +1 -0
- package/dist/chunks/block-JQiwViEt.js +2777 -0
- package/dist/chunks/block-JQiwViEt.js.map +1 -0
- package/dist/chunks/{choose-DvpbdRuz.js → choose-BemygfH8.js} +3 -2
- package/dist/chunks/{choose-DvpbdRuz.js.map → choose-BemygfH8.js.map} +1 -1
- package/dist/chunks/{constants-fT3E8JYY.js → constants-BFUR_CtU.js} +6 -2
- package/dist/chunks/constants-BFUR_CtU.js.map +1 -0
- package/dist/chunks/convert-BcQ8_jMo.js +59 -0
- package/dist/chunks/convert-BcQ8_jMo.js.map +1 -0
- package/dist/chunks/emit-DCwFLts5.js +3456 -0
- package/dist/chunks/emit-DCwFLts5.js.map +1 -0
- package/dist/chunks/{escape-B62ZGM_a.js → escape-B1rMftd9.js} +43 -20
- package/dist/chunks/escape-B1rMftd9.js.map +1 -0
- package/dist/chunks/{export-rKLeGQMo.js → export-D7IGzTG4.js} +196 -792
- package/dist/chunks/export-D7IGzTG4.js.map +1 -0
- package/dist/chunks/{exporter-BWwEuOPT.js → exporter-BtnqYuuj.js} +22 -12
- package/dist/chunks/exporter-BtnqYuuj.js.map +1 -0
- package/dist/chunks/{exporter-yLxKBvLE.js → exporter-ChPyhBOv.js} +4 -3
- package/dist/chunks/{exporter-yLxKBvLE.js.map → exporter-ChPyhBOv.js.map} +1 -1
- package/dist/chunks/{exporter-8hLNemFk.js → exporter-KEPxYUXD.js} +133 -74
- package/dist/chunks/exporter-KEPxYUXD.js.map +1 -0
- package/dist/chunks/{exporter-CpRyHZ3H.js → exporter-quPUCBiC.js} +1807 -288
- package/dist/chunks/exporter-quPUCBiC.js.map +1 -0
- package/dist/chunks/format-GDmO6m6Q.js +676 -0
- package/dist/chunks/format-GDmO6m6Q.js.map +1 -0
- package/dist/chunks/generic-BL06jrJf.js +1347 -0
- package/dist/chunks/generic-BL06jrJf.js.map +1 -0
- package/dist/chunks/{importer-WrGRZb0N.js → importer-B7r2owVf.js} +449 -102
- package/dist/chunks/importer-B7r2owVf.js.map +1 -0
- package/dist/chunks/{importer-Ht7LNg8L.js → importer-BFmOP96W.js} +116 -296
- package/dist/chunks/importer-BFmOP96W.js.map +1 -0
- package/dist/chunks/{importer-BbNb7eNg.js → importer-C2nzGhdV.js} +1390 -129
- package/dist/chunks/importer-C2nzGhdV.js.map +1 -0
- package/dist/chunks/{importer-Dz7tI8Sa.js → importer-CbX2_rNF.js} +617 -115
- package/dist/chunks/importer-CbX2_rNF.js.map +1 -0
- package/dist/chunks/{importer-D794XUTe.js → importer-CdzeN6X4.js} +31 -8
- package/dist/chunks/importer-CdzeN6X4.js.map +1 -0
- package/dist/chunks/{importer-xZ-3suY1.js → importer-ChKNC_GN.js} +791 -135
- package/dist/chunks/importer-ChKNC_GN.js.map +1 -0
- package/dist/chunks/{importer-C98dpSH4.js → importer-dwoYpSqb.js} +141 -82
- package/dist/chunks/importer-dwoYpSqb.js.map +1 -0
- package/dist/chunks/index-B9fdNpTA.js +4310 -0
- package/dist/chunks/index-B9fdNpTA.js.map +1 -0
- package/dist/chunks/{json-elements-DKSqpNW1.js → json-elements-C4FwgnH5.js} +123 -32
- package/dist/chunks/json-elements-C4FwgnH5.js.map +1 -0
- package/dist/chunks/lexicon-B-knUi-l.js +508 -0
- package/dist/chunks/lexicon-B-knUi-l.js.map +1 -0
- package/dist/chunks/{ontology-export-CtRXjg_i.js → ontology-export-BSpbY5FL.js} +3 -2
- package/dist/chunks/{ontology-export-CtRXjg_i.js.map → ontology-export-BSpbY5FL.js.map} +1 -1
- package/dist/chunks/{records-Cu4bxYOm.js → records-Nwz_rbNt.js} +2 -2
- package/dist/chunks/{records-Cu4bxYOm.js.map → records-Nwz_rbNt.js.map} +1 -1
- package/dist/chunks/styles-Brt9JMfs.js +690 -0
- package/dist/chunks/styles-Brt9JMfs.js.map +1 -0
- package/dist/chunks/styles-_N-C8S-U.js +2102 -0
- package/dist/chunks/styles-_N-C8S-U.js.map +1 -0
- package/dist/chunks/styles-yed-DAfcIkf7.js +842 -0
- package/dist/chunks/styles-yed-DAfcIkf7.js.map +1 -0
- package/dist/chunks/{text-DHOJXnED.js → text-CgzMqHQb.js} +2 -2
- package/dist/chunks/{text-DHOJXnED.js.map → text-CgzMqHQb.js.map} +1 -1
- package/dist/chunks/units-XMZFIFe1.js +19 -0
- package/dist/chunks/units-XMZFIFe1.js.map +1 -0
- package/dist/csv.js +5 -4
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +1 -1
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +5 -2
- package/dist/dot.js.map +1 -1
- package/dist/gexf.js +295 -414
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +8 -3031
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +354 -240
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +4 -2
- package/dist/graphml.js.map +1 -1
- package/dist/json.js +1 -1
- package/dist/neo4j.js +4 -3
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +1 -1
- package/dist/pajek.js +4 -2
- package/dist/positions.d.ts +1 -0
- package/dist/positions.js +427 -0
- package/dist/positions.js.map +1 -0
- package/dist/src/common/codes.d.ts +6 -0
- package/dist/src/common/codes.d.ts.map +1 -1
- package/dist/src/common/codes.js +6 -0
- package/dist/src/common/codes.js.map +1 -1
- package/dist/src/common/escape.d.ts.map +1 -1
- package/dist/src/common/escape.js +4 -2
- package/dist/src/common/escape.js.map +1 -1
- package/dist/src/common/export.d.ts +10 -0
- package/dist/src/common/export.d.ts.map +1 -1
- package/dist/src/common/export.js +123 -100
- package/dist/src/common/export.js.map +1 -1
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +8 -1
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/json-elements.d.ts +12 -9
- package/dist/src/common/json-elements.d.ts.map +1 -1
- package/dist/src/common/json-elements.js +160 -37
- package/dist/src/common/json-elements.js.map +1 -1
- package/dist/src/common/notes.d.ts +42 -0
- package/dist/src/common/notes.d.ts.map +1 -0
- package/dist/src/common/notes.js +72 -0
- package/dist/src/common/notes.js.map +1 -0
- package/dist/src/common/report.d.ts +18 -0
- package/dist/src/common/report.d.ts.map +1 -1
- package/dist/src/common/report.js +36 -1
- package/dist/src/common/report.js.map +1 -1
- package/dist/src/common/units.d.ts +24 -0
- package/dist/src/common/units.d.ts.map +1 -0
- package/dist/src/common/units.js +26 -0
- package/dist/src/common/units.js.map +1 -0
- package/dist/src/common/xml.d.ts +18 -0
- package/dist/src/common/xml.d.ts.map +1 -1
- package/dist/src/common/xml.js +42 -5
- package/dist/src/common/xml.js.map +1 -1
- package/dist/src/formats/cx/exporter.d.ts +6 -0
- package/dist/src/formats/cx/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx/exporter.js +71 -8
- package/dist/src/formats/cx/exporter.js.map +1 -1
- package/dist/src/formats/cx/importer.d.ts +5 -2
- package/dist/src/formats/cx/importer.d.ts.map +1 -1
- package/dist/src/formats/cx/importer.js +102 -72
- package/dist/src/formats/cx/importer.js.map +1 -1
- package/dist/src/formats/cx/styles.d.ts +102 -0
- package/dist/src/formats/cx/styles.d.ts.map +1 -0
- package/dist/src/formats/cx/styles.js +225 -0
- package/dist/src/formats/cx/styles.js.map +1 -0
- package/dist/src/formats/cx2/exporter.d.ts +5 -0
- package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx2/exporter.js +181 -71
- package/dist/src/formats/cx2/exporter.js.map +1 -1
- package/dist/src/formats/cx2/importer.d.ts +4 -1
- package/dist/src/formats/cx2/importer.d.ts.map +1 -1
- package/dist/src/formats/cx2/importer.js +19 -1
- package/dist/src/formats/cx2/importer.js.map +1 -1
- package/dist/src/formats/cx2/styles.d.ts +18 -0
- package/dist/src/formats/cx2/styles.d.ts.map +1 -0
- package/dist/src/formats/cx2/styles.js +24 -0
- package/dist/src/formats/cx2/styles.js.map +1 -0
- package/dist/src/formats/cys/constants.d.ts +2 -0
- package/dist/src/formats/cys/constants.d.ts.map +1 -1
- package/dist/src/formats/cys/constants.js +2 -0
- package/dist/src/formats/cys/constants.js.map +1 -1
- package/dist/src/formats/cys/exporter.d.ts +6 -2
- package/dist/src/formats/cys/exporter.d.ts.map +1 -1
- package/dist/src/formats/cys/exporter.js +215 -53
- package/dist/src/formats/cys/exporter.js.map +1 -1
- package/dist/src/formats/cys/importer.d.ts +2 -1
- package/dist/src/formats/cys/importer.d.ts.map +1 -1
- package/dist/src/formats/cys/importer.js +119 -6
- package/dist/src/formats/cys/importer.js.map +1 -1
- package/dist/src/formats/cys/session.d.ts +2 -0
- package/dist/src/formats/cys/session.d.ts.map +1 -1
- package/dist/src/formats/cys/session.js +5 -0
- package/dist/src/formats/cys/session.js.map +1 -1
- package/dist/src/formats/cys/styles.d.ts +38 -0
- package/dist/src/formats/cys/styles.d.ts.map +1 -0
- package/dist/src/formats/cys/styles.js +213 -0
- package/dist/src/formats/cys/styles.js.map +1 -0
- package/dist/src/formats/cys/tables.d.ts +8 -3
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +88 -34
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/exporter.d.ts +2 -0
- package/dist/src/formats/dot/exporter.d.ts.map +1 -1
- package/dist/src/formats/dot/exporter.js +195 -81
- package/dist/src/formats/dot/exporter.js.map +1 -1
- package/dist/src/formats/dot/importer.d.ts.map +1 -1
- package/dist/src/formats/dot/importer.js +100 -20
- package/dist/src/formats/dot/importer.js.map +1 -1
- package/dist/src/formats/dot/index.d.ts +1 -0
- package/dist/src/formats/dot/index.d.ts.map +1 -1
- package/dist/src/formats/dot/index.js +1 -0
- package/dist/src/formats/dot/index.js.map +1 -1
- package/dist/src/formats/dot/positions.d.ts +14 -0
- package/dist/src/formats/dot/positions.d.ts.map +1 -0
- package/dist/src/formats/dot/positions.js +70 -0
- package/dist/src/formats/dot/positions.js.map +1 -0
- package/dist/src/formats/dot/styles.d.ts +82 -0
- package/dist/src/formats/dot/styles.d.ts.map +1 -0
- package/dist/src/formats/dot/styles.js +1484 -0
- package/dist/src/formats/dot/styles.js.map +1 -0
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +23 -7
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts +8 -1
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +251 -131
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gexf/index.d.ts +8 -0
- package/dist/src/formats/gexf/index.d.ts.map +1 -1
- package/dist/src/formats/gexf/index.js +10 -2
- package/dist/src/formats/gexf/index.js.map +1 -1
- package/dist/src/formats/gexf/schema.d.ts +5 -0
- package/dist/src/formats/gexf/schema.d.ts.map +1 -1
- package/dist/src/formats/gexf/schema.js +5 -0
- package/dist/src/formats/gexf/schema.js.map +1 -1
- package/dist/src/formats/gexf/styles.d.ts +74 -0
- package/dist/src/formats/gexf/styles.d.ts.map +1 -0
- package/dist/src/formats/gexf/styles.js +604 -0
- package/dist/src/formats/gexf/styles.js.map +1 -0
- package/dist/src/formats/gml/exporter.d.ts +1 -5
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +19 -8
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +4 -0
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/index.d.ts +2 -1
- package/dist/src/formats/gml/index.d.ts.map +1 -1
- package/dist/src/formats/gml/index.js +2 -1
- package/dist/src/formats/gml/index.js.map +1 -1
- package/dist/src/formats/gml/positions.d.ts +14 -0
- package/dist/src/formats/gml/positions.d.ts.map +1 -0
- package/dist/src/formats/gml/positions.js +46 -0
- package/dist/src/formats/gml/positions.js.map +1 -0
- package/dist/src/formats/gml/styles.d.ts +59 -0
- package/dist/src/formats/gml/styles.d.ts.map +1 -0
- package/dist/src/formats/gml/styles.js +1056 -0
- package/dist/src/formats/gml/styles.js.map +1 -0
- package/dist/src/formats/gml/syntax.d.ts +11 -0
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +151 -62
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/constants.d.ts +4 -0
- package/dist/src/formats/graphml/constants.d.ts.map +1 -1
- package/dist/src/formats/graphml/constants.js +5 -1
- package/dist/src/formats/graphml/constants.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +133 -65
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts +4 -1
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +100 -44
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/index.d.ts +1 -0
- package/dist/src/formats/graphml/index.d.ts.map +1 -1
- package/dist/src/formats/graphml/index.js +1 -0
- package/dist/src/formats/graphml/index.js.map +1 -1
- package/dist/src/formats/graphml/styles-plain.d.ts +19 -0
- package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-plain.js +187 -0
- package/dist/src/formats/graphml/styles-plain.js.map +1 -0
- package/dist/src/formats/graphml/styles-yed.d.ts +179 -0
- package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-yed.js +1086 -0
- package/dist/src/formats/graphml/styles-yed.js.map +1 -0
- package/dist/src/formats/graphml/styles-yfiles3.d.ts +19 -0
- package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -0
- package/dist/src/formats/graphml/styles-yfiles3.js +508 -0
- package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -0
- package/dist/src/formats/graphml/tree.d.ts.map +1 -1
- package/dist/src/formats/graphml/tree.js +34 -19
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts +16 -2
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +83 -12
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts +5 -0
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +68 -25
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +22 -3
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +441 -265
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/positions.d.ts +16 -0
- package/dist/src/formats/json/positions.d.ts.map +1 -0
- package/dist/src/formats/json/positions.js +44 -0
- package/dist/src/formats/json/positions.js.map +1 -0
- package/dist/src/formats/json/styles.d.ts +102 -0
- package/dist/src/formats/json/styles.d.ts.map +1 -0
- package/dist/src/formats/json/styles.js +1753 -0
- package/dist/src/formats/json/styles.js.map +1 -0
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +2 -1
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts +2 -0
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +142 -76
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts +4 -0
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +60 -6
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/index.d.ts +2 -0
- package/dist/src/formats/pajek/index.d.ts.map +1 -1
- package/dist/src/formats/pajek/index.js +2 -0
- package/dist/src/formats/pajek/index.js.map +1 -1
- package/dist/src/formats/pajek/styles.d.ts +50 -0
- package/dist/src/formats/pajek/styles.d.ts.map +1 -0
- package/dist/src/formats/pajek/styles.js +569 -0
- package/dist/src/formats/pajek/styles.js.map +1 -0
- package/dist/src/formats/pajek/syntax.d.ts +6 -0
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +6 -0
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
- package/dist/src/formats/xgmml/columns.js +34 -9
- package/dist/src/formats/xgmml/columns.js.map +1 -1
- package/dist/src/formats/xgmml/constants.d.ts +2 -0
- package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
- package/dist/src/formats/xgmml/constants.js +3 -1
- package/dist/src/formats/xgmml/constants.js.map +1 -1
- package/dist/src/formats/xgmml/document.d.ts +12 -8
- package/dist/src/formats/xgmml/document.d.ts.map +1 -1
- package/dist/src/formats/xgmml/document.js +56 -13
- package/dist/src/formats/xgmml/document.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +12 -6
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts +3 -1
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +22 -9
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts +2 -1
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +31 -24
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/formats/xgmml/styles.d.ts +104 -0
- package/dist/src/formats/xgmml/styles.d.ts.map +1 -0
- package/dist/src/formats/xgmml/styles.js +310 -0
- package/dist/src/formats/xgmml/styles.js.map +1 -0
- package/dist/src/index.d.ts +9 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -7
- package/dist/src/index.js.map +1 -1
- package/dist/src/positions/codes.d.ts +31 -0
- package/dist/src/positions/codes.d.ts.map +1 -0
- package/dist/src/positions/codes.js +26 -0
- package/dist/src/positions/codes.js.map +1 -0
- package/dist/src/positions/convert.d.ts +58 -0
- package/dist/src/positions/convert.d.ts.map +1 -0
- package/dist/src/positions/convert.js +107 -0
- package/dist/src/positions/convert.js.map +1 -0
- package/dist/src/positions/extract.d.ts +27 -0
- package/dist/src/positions/extract.d.ts.map +1 -0
- package/dist/src/positions/extract.js +526 -0
- package/dist/src/positions/extract.js.map +1 -0
- package/dist/src/positions/index.d.ts +10 -0
- package/dist/src/positions/index.d.ts.map +1 -0
- package/dist/src/positions/index.js +9 -0
- package/dist/src/positions/index.js.map +1 -0
- package/dist/src/positions/types.d.ts +208 -0
- package/dist/src/positions/types.d.ts.map +1 -0
- package/dist/src/positions/types.js +10 -0
- package/dist/src/positions/types.js.map +1 -0
- package/dist/src/registry.d.ts +30 -0
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +130 -9
- package/dist/src/registry.js.map +1 -1
- package/dist/src/styles/annotations.d.ts +132 -0
- package/dist/src/styles/annotations.d.ts.map +1 -0
- package/dist/src/styles/annotations.js +320 -0
- package/dist/src/styles/annotations.js.map +1 -0
- package/dist/src/styles/budget.d.ts +45 -0
- package/dist/src/styles/budget.d.ts.map +1 -0
- package/dist/src/styles/budget.js +58 -0
- package/dist/src/styles/budget.js.map +1 -0
- package/dist/src/styles/canon/bends.d.ts +32 -0
- package/dist/src/styles/canon/bends.d.ts.map +1 -0
- package/dist/src/styles/canon/bends.js +74 -0
- package/dist/src/styles/canon/bends.js.map +1 -0
- package/dist/src/styles/canon/color-tables.d.ts +22 -0
- package/dist/src/styles/canon/color-tables.d.ts.map +1 -0
- package/dist/src/styles/canon/color-tables.js +369 -0
- package/dist/src/styles/canon/color-tables.js.map +1 -0
- package/dist/src/styles/canon/color.d.ts +66 -0
- package/dist/src/styles/canon/color.d.ts.map +1 -0
- package/dist/src/styles/canon/color.js +548 -0
- package/dist/src/styles/canon/color.js.map +1 -0
- package/dist/src/styles/canon/enums.d.ts +25 -0
- package/dist/src/styles/canon/enums.d.ts.map +1 -0
- package/dist/src/styles/canon/enums.js +187 -0
- package/dist/src/styles/canon/enums.js.map +1 -0
- package/dist/src/styles/canon/graphviz.d.ts +48 -0
- package/dist/src/styles/canon/graphviz.d.ts.map +1 -0
- package/dist/src/styles/canon/graphviz.js +160 -0
- package/dist/src/styles/canon/graphviz.js.map +1 -0
- package/dist/src/styles/canon/index.d.ts +13 -0
- package/dist/src/styles/canon/index.d.ts.map +1 -0
- package/dist/src/styles/canon/index.js +13 -0
- package/dist/src/styles/canon/index.js.map +1 -0
- package/dist/src/styles/canon/number.d.ts +59 -0
- package/dist/src/styles/canon/number.d.ts.map +1 -0
- package/dist/src/styles/canon/number.js +142 -0
- package/dist/src/styles/canon/number.js.map +1 -0
- package/dist/src/styles/canon/site.d.ts +51 -0
- package/dist/src/styles/canon/site.d.ts.map +1 -0
- package/dist/src/styles/canon/site.js +63 -0
- package/dist/src/styles/canon/site.js.map +1 -0
- package/dist/src/styles/canon/text.d.ts +61 -0
- package/dist/src/styles/canon/text.d.ts.map +1 -0
- package/dist/src/styles/canon/text.js +199 -0
- package/dist/src/styles/canon/text.js.map +1 -0
- package/dist/src/styles/codes.d.ts +95 -0
- package/dist/src/styles/codes.d.ts.map +1 -0
- package/dist/src/styles/codes.js +97 -0
- package/dist/src/styles/codes.js.map +1 -0
- package/dist/src/styles/cyjs/cascade.d.ts +134 -0
- package/dist/src/styles/cyjs/cascade.d.ts.map +1 -0
- package/dist/src/styles/cyjs/cascade.js +2252 -0
- package/dist/src/styles/cyjs/cascade.js.map +1 -0
- package/dist/src/styles/cyjs/defaults-table.d.ts +25 -0
- package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -0
- package/dist/src/styles/cyjs/defaults-table.js +76 -0
- package/dist/src/styles/cyjs/defaults-table.js.map +1 -0
- package/dist/src/styles/cyjs/emit.d.ts +82 -0
- package/dist/src/styles/cyjs/emit.d.ts.map +1 -0
- package/dist/src/styles/cyjs/emit.js +790 -0
- package/dist/src/styles/cyjs/emit.js.map +1 -0
- package/dist/src/styles/cyjs/index.d.ts +13 -0
- package/dist/src/styles/cyjs/index.d.ts.map +1 -0
- package/dist/src/styles/cyjs/index.js +13 -0
- package/dist/src/styles/cyjs/index.js.map +1 -0
- package/dist/src/styles/cyjs/live.d.ts +46 -0
- package/dist/src/styles/cyjs/live.d.ts.map +1 -0
- package/dist/src/styles/cyjs/live.js +108 -0
- package/dist/src/styles/cyjs/live.js.map +1 -0
- package/dist/src/styles/cyjs/mappers.d.ts +147 -0
- package/dist/src/styles/cyjs/mappers.d.ts.map +1 -0
- package/dist/src/styles/cyjs/mappers.js +744 -0
- package/dist/src/styles/cyjs/mappers.js.map +1 -0
- package/dist/src/styles/cyjs/selector.d.ts +113 -0
- package/dist/src/styles/cyjs/selector.d.ts.map +1 -0
- package/dist/src/styles/cyjs/selector.js +611 -0
- package/dist/src/styles/cyjs/selector.js.map +1 -0
- package/dist/src/styles/cyjs/stylesheet-text.d.ts +37 -0
- package/dist/src/styles/cyjs/stylesheet-text.d.ts.map +1 -0
- package/dist/src/styles/cyjs/stylesheet-text.js +181 -0
- package/dist/src/styles/cyjs/stylesheet-text.js.map +1 -0
- package/dist/src/styles/cytoscape/emit.d.ts +125 -0
- package/dist/src/styles/cytoscape/emit.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/emit.js +1025 -0
- package/dist/src/styles/cytoscape/emit.js.map +1 -0
- package/dist/src/styles/cytoscape/index.d.ts +10 -0
- package/dist/src/styles/cytoscape/index.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/index.js +10 -0
- package/dist/src/styles/cytoscape/index.js.map +1 -0
- package/dist/src/styles/cytoscape/lexicon.d.ts +49 -0
- package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/lexicon.js +603 -0
- package/dist/src/styles/cytoscape/lexicon.js.map +1 -0
- package/dist/src/styles/cytoscape/mappings.d.ts +171 -0
- package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/mappings.js +460 -0
- package/dist/src/styles/cytoscape/mappings.js.map +1 -0
- package/dist/src/styles/cytoscape/resolve.d.ts +137 -0
- package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/resolve.js +381 -0
- package/dist/src/styles/cytoscape/resolve.js.map +1 -0
- package/dist/src/styles/cytoscape/translate.d.ts +50 -0
- package/dist/src/styles/cytoscape/translate.d.ts.map +1 -0
- package/dist/src/styles/cytoscape/translate.js +156 -0
- package/dist/src/styles/cytoscape/translate.js.map +1 -0
- package/dist/src/styles/defaults.d.ts +90 -0
- package/dist/src/styles/defaults.d.ts.map +1 -0
- package/dist/src/styles/defaults.js +414 -0
- package/dist/src/styles/defaults.js.map +1 -0
- package/dist/src/styles/index.d.ts +15 -0
- package/dist/src/styles/index.d.ts.map +1 -0
- package/dist/src/styles/index.js +14 -0
- package/dist/src/styles/index.js.map +1 -0
- package/dist/src/styles/internal.d.ts +17 -0
- package/dist/src/styles/internal.d.ts.map +1 -0
- package/dist/src/styles/internal.js +16 -0
- package/dist/src/styles/internal.js.map +1 -0
- package/dist/src/styles/io/approximations.d.ts +24 -0
- package/dist/src/styles/io/approximations.d.ts.map +1 -0
- package/dist/src/styles/io/approximations.js +51 -0
- package/dist/src/styles/io/approximations.js.map +1 -0
- package/dist/src/styles/io/block.d.ts +166 -0
- package/dist/src/styles/io/block.d.ts.map +1 -0
- package/dist/src/styles/io/block.js +930 -0
- package/dist/src/styles/io/block.js.map +1 -0
- package/dist/src/styles/io/generic.d.ts +88 -0
- package/dist/src/styles/io/generic.d.ts.map +1 -0
- package/dist/src/styles/io/generic.js +516 -0
- package/dist/src/styles/io/generic.js.map +1 -0
- package/dist/src/styles/io/index.d.ts +8 -0
- package/dist/src/styles/io/index.d.ts.map +1 -0
- package/dist/src/styles/io/index.js +7 -0
- package/dist/src/styles/io/index.js.map +1 -0
- package/dist/src/styles/io/writer.d.ts +80 -0
- package/dist/src/styles/io/writer.d.ts.map +1 -0
- package/dist/src/styles/io/writer.js +596 -0
- package/dist/src/styles/io/writer.js.map +1 -0
- package/dist/src/styles/notes.d.ts +47 -0
- package/dist/src/styles/notes.d.ts.map +1 -0
- package/dist/src/styles/notes.js +140 -0
- package/dist/src/styles/notes.js.map +1 -0
- package/dist/src/styles/plan/classes.d.ts +31 -0
- package/dist/src/styles/plan/classes.d.ts.map +1 -0
- package/dist/src/styles/plan/classes.js +139 -0
- package/dist/src/styles/plan/classes.js.map +1 -0
- package/dist/src/styles/plan/continuous.d.ts +32 -0
- package/dist/src/styles/plan/continuous.d.ts.map +1 -0
- package/dist/src/styles/plan/continuous.js +165 -0
- package/dist/src/styles/plan/continuous.js.map +1 -0
- package/dist/src/styles/plan/discrete.d.ts +39 -0
- package/dist/src/styles/plan/discrete.d.ts.map +1 -0
- package/dist/src/styles/plan/discrete.js +127 -0
- package/dist/src/styles/plan/discrete.js.map +1 -0
- package/dist/src/styles/plan/families.d.ts +31 -0
- package/dist/src/styles/plan/families.d.ts.map +1 -0
- package/dist/src/styles/plan/families.js +95 -0
- package/dist/src/styles/plan/families.js.map +1 -0
- package/dist/src/styles/plan/features.d.ts +58 -0
- package/dist/src/styles/plan/features.d.ts.map +1 -0
- package/dist/src/styles/plan/features.js +185 -0
- package/dist/src/styles/plan/features.js.map +1 -0
- package/dist/src/styles/plan/index.d.ts +7 -0
- package/dist/src/styles/plan/index.d.ts.map +1 -0
- package/dist/src/styles/plan/index.js +7 -0
- package/dist/src/styles/plan/index.js.map +1 -0
- package/dist/src/styles/plan/plan.d.ts +71 -0
- package/dist/src/styles/plan/plan.d.ts.map +1 -0
- package/dist/src/styles/plan/plan.js +893 -0
- package/dist/src/styles/plan/plan.js.map +1 -0
- package/dist/src/styles/plan/resolve.d.ts +137 -0
- package/dist/src/styles/plan/resolve.d.ts.map +1 -0
- package/dist/src/styles/plan/resolve.js +587 -0
- package/dist/src/styles/plan/resolve.js.map +1 -0
- package/dist/src/styles/types.d.ts +702 -0
- package/dist/src/styles/types.d.ts.map +1 -0
- package/dist/src/styles/types.js +10 -0
- package/dist/src/styles/types.js.map +1 -0
- package/dist/src/styles/vocabulary.d.ts +41 -0
- package/dist/src/styles/vocabulary.d.ts.map +1 -0
- package/dist/src/styles/vocabulary.js +140 -0
- package/dist/src/styles/vocabulary.js.map +1 -0
- package/dist/src/types.d.ts +64 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/styles.d.ts +1 -0
- package/dist/styles.js +322 -0
- package/dist/styles.js.map +1 -0
- package/dist/xgmml.js +5 -4
- package/dist/xgmml.js.map +1 -1
- package/package.json +13 -2
- package/src/common/codes.ts +7 -0
- package/src/common/escape.ts +5 -2
- package/src/common/export.ts +179 -140
- package/src/common/input.ts +8 -4
- package/src/common/json-elements.ts +169 -38
- package/src/common/notes.ts +94 -0
- package/src/common/report.ts +41 -1
- package/src/common/units.ts +42 -0
- package/src/common/xml.ts +45 -5
- package/src/formats/cx/exporter.ts +95 -11
- package/src/formats/cx/importer.ts +126 -90
- package/src/formats/cx/styles.ts +304 -0
- package/src/formats/cx2/exporter.ts +234 -77
- package/src/formats/cx2/importer.ts +19 -1
- package/src/formats/cx2/styles.ts +29 -0
- package/src/formats/cys/constants.ts +3 -0
- package/src/formats/cys/exporter.ts +246 -60
- package/src/formats/cys/importer.ts +125 -9
- package/src/formats/cys/session.ts +6 -0
- package/src/formats/cys/styles.ts +254 -0
- package/src/formats/cys/tables.ts +117 -48
- package/src/formats/dot/exporter.ts +262 -126
- package/src/formats/dot/importer.ts +111 -20
- package/src/formats/dot/index.ts +1 -0
- package/src/formats/dot/positions.ts +77 -0
- package/src/formats/dot/styles.ts +1741 -0
- package/src/formats/gexf/exporter.ts +30 -13
- package/src/formats/gexf/importer.ts +308 -158
- package/src/formats/gexf/index.ts +10 -0
- package/src/formats/gexf/schema.ts +5 -0
- package/src/formats/gexf/styles.ts +719 -0
- package/src/formats/gml/exporter.ts +33 -14
- package/src/formats/gml/importer.ts +4 -0
- package/src/formats/gml/index.ts +2 -1
- package/src/formats/gml/positions.ts +52 -0
- package/src/formats/gml/styles.ts +1248 -0
- package/src/formats/gml/syntax.ts +173 -62
- package/src/formats/graphml/constants.ts +5 -0
- package/src/formats/graphml/exporter.ts +163 -71
- package/src/formats/graphml/importer.ts +119 -45
- package/src/formats/graphml/index.ts +1 -0
- package/src/formats/graphml/styles-plain.ts +236 -0
- package/src/formats/graphml/styles-yed.ts +1221 -0
- package/src/formats/graphml/styles-yfiles3.ts +573 -0
- package/src/formats/graphml/tree.ts +36 -20
- package/src/formats/graphml/yfiles.ts +89 -13
- package/src/formats/json/exporter.ts +76 -26
- package/src/formats/json/importer.ts +543 -323
- package/src/formats/json/positions.ts +50 -0
- package/src/formats/json/styles.ts +2082 -0
- package/src/formats/neo4j/exporter.ts +2 -1
- package/src/formats/pajek/exporter.ts +195 -106
- package/src/formats/pajek/importer.ts +78 -9
- package/src/formats/pajek/index.ts +2 -0
- package/src/formats/pajek/styles.ts +685 -0
- package/src/formats/pajek/syntax.ts +7 -0
- package/src/formats/xgmml/columns.ts +39 -9
- package/src/formats/xgmml/constants.ts +3 -0
- package/src/formats/xgmml/document.ts +54 -12
- package/src/formats/xgmml/emit.ts +11 -6
- package/src/formats/xgmml/exporter.ts +32 -13
- package/src/formats/xgmml/importer.ts +43 -29
- package/src/formats/xgmml/styles.ts +400 -0
- package/src/index.ts +16 -0
- package/src/positions/codes.ts +32 -0
- package/src/positions/convert.ts +123 -0
- package/src/positions/extract.ts +619 -0
- package/src/positions/index.ts +23 -0
- package/src/positions/types.ts +221 -0
- package/src/registry.ts +179 -8
- package/src/styles/annotations.ts +478 -0
- package/src/styles/budget.ts +60 -0
- package/src/styles/canon/bends.ts +84 -0
- package/src/styles/canon/color-tables.ts +386 -0
- package/src/styles/canon/color.ts +624 -0
- package/src/styles/canon/enums.ts +211 -0
- package/src/styles/canon/graphviz.ts +189 -0
- package/src/styles/canon/index.ts +13 -0
- package/src/styles/canon/number.ts +152 -0
- package/src/styles/canon/site.ts +87 -0
- package/src/styles/canon/text.ts +221 -0
- package/src/styles/codes.ts +104 -0
- package/src/styles/cyjs/cascade.ts +2670 -0
- package/src/styles/cyjs/defaults-table.ts +82 -0
- package/src/styles/cyjs/emit.ts +951 -0
- package/src/styles/cyjs/index.ts +37 -0
- package/src/styles/cyjs/live.ts +147 -0
- package/src/styles/cyjs/mappers.ts +879 -0
- package/src/styles/cyjs/selector.ts +778 -0
- package/src/styles/cyjs/stylesheet-text.ts +214 -0
- package/src/styles/cytoscape/emit.ts +1264 -0
- package/src/styles/cytoscape/index.ts +40 -0
- package/src/styles/cytoscape/lexicon.ts +656 -0
- package/src/styles/cytoscape/mappings.ts +603 -0
- package/src/styles/cytoscape/resolve.ts +521 -0
- package/src/styles/cytoscape/translate.ts +237 -0
- package/src/styles/defaults.ts +504 -0
- package/src/styles/index.ts +57 -0
- package/src/styles/internal.ts +17 -0
- package/src/styles/io/approximations.ts +47 -0
- package/src/styles/io/block.ts +1138 -0
- package/src/styles/io/generic.ts +687 -0
- package/src/styles/io/index.ts +18 -0
- package/src/styles/io/writer.ts +745 -0
- package/src/styles/notes.ts +165 -0
- package/src/styles/plan/classes.ts +166 -0
- package/src/styles/plan/continuous.ts +218 -0
- package/src/styles/plan/discrete.ts +165 -0
- package/src/styles/plan/families.ts +115 -0
- package/src/styles/plan/features.ts +234 -0
- package/src/styles/plan/index.ts +7 -0
- package/src/styles/plan/plan.ts +1129 -0
- package/src/styles/plan/resolve.ts +685 -0
- package/src/styles/types.ts +814 -0
- package/src/styles/vocabulary.ts +172 -0
- package/src/types.ts +76 -1
- package/dist/chunks/constants-fT3E8JYY.js.map +0 -1
- package/dist/chunks/escape-B62ZGM_a.js.map +0 -1
- package/dist/chunks/export-rKLeGQMo.js.map +0 -1
- package/dist/chunks/exporter-8hLNemFk.js.map +0 -1
- package/dist/chunks/exporter-BWwEuOPT.js.map +0 -1
- package/dist/chunks/exporter-CpRyHZ3H.js.map +0 -1
- package/dist/chunks/importer-BbNb7eNg.js.map +0 -1
- package/dist/chunks/importer-C98dpSH4.js.map +0 -1
- package/dist/chunks/importer-D794XUTe.js.map +0 -1
- package/dist/chunks/importer-Dz7tI8Sa.js.map +0 -1
- package/dist/chunks/importer-Ht7LNg8L.js.map +0 -1
- package/dist/chunks/importer-WrGRZb0N.js.map +0 -1
- package/dist/chunks/importer-xZ-3suY1.js.map +0 -1
- package/dist/chunks/json-elements-DKSqpNW1.js.map +0 -1
|
@@ -0,0 +1,1221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* yEd styles (yFiles for Java 2.x GraphML): reading every realizer a yEd graphics tree holds (`y:ShapeNode`,
|
|
3
|
+
* `y:GenericNode`, `y:GroupNode`, `y:SVGNode`, `y:PolyLineEdge`, `y:BezierEdge`, ...) into the neutral style
|
|
4
|
+
* vocabulary, and writing a graph's styles back as `y:ShapeNode` and `y:PolyLineEdge` graphics. The trees are the
|
|
5
|
+
* json columns the GraphML importer keeps (tree.ts), never the typed `yfiles.*` columns read from them.
|
|
6
|
+
*
|
|
7
|
+
* The tree helpers here are shared with the yFiles 3 reader.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { GraphSnapshot } from "@graphty/graph-format";
|
|
11
|
+
|
|
12
|
+
import { toFilePositions, toNeutral } from "../../positions/convert.js";
|
|
13
|
+
import type { PositionColumnExtra } from "../../positions/types.js";
|
|
14
|
+
import { type BendPoint, bendsToPoints, canonBends } from "../../styles/canon/bends.js";
|
|
15
|
+
import { canonColor, colorOpacity } from "../../styles/canon/color.js";
|
|
16
|
+
import { canonEnum } from "../../styles/canon/enums.js";
|
|
17
|
+
import { canonNumber } from "../../styles/canon/number.js";
|
|
18
|
+
import { type CanonSite, MAX_STYLE_VALUE_LENGTH, noteAt } from "../../styles/canon/site.js";
|
|
19
|
+
import { canonText } from "../../styles/canon/text.js";
|
|
20
|
+
import { STYLE_ISSUE } from "../../styles/codes.js";
|
|
21
|
+
import { StyleApproximations } from "../../styles/io/approximations.js";
|
|
22
|
+
import type { ExporterStyleInput } from "../../styles/io/writer.js";
|
|
23
|
+
import type { StyleNotes } from "../../styles/notes.js";
|
|
24
|
+
import { plainCell } from "../../styles/plan/resolve.js";
|
|
25
|
+
import type { StyleTarget, StyleValue, StyleWorkBudget, StyleWriteCapabilities } from "../../styles/types.js";
|
|
26
|
+
import type { LossNote } from "../../types.js";
|
|
27
|
+
|
|
28
|
+
// ============================================================ the kept trees
|
|
29
|
+
|
|
30
|
+
/** A tree object as tree.ts builds it: `@_<name>` attributes, `#text`, and child elements by name. */
|
|
31
|
+
type Tree = Record<string, unknown>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whether a value is a tree object.
|
|
35
|
+
* @param value - the value
|
|
36
|
+
* @returns true for a non-array object
|
|
37
|
+
*/
|
|
38
|
+
export function isTree(value: unknown): value is Tree {
|
|
39
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The local part of an element name: `y:ShapeNode` is `ShapeNode`, whatever the prefix.
|
|
44
|
+
* @param name - the name as written
|
|
45
|
+
* @returns the local name
|
|
46
|
+
*/
|
|
47
|
+
export function localOf(name: string): string {
|
|
48
|
+
const colon = name.indexOf(":");
|
|
49
|
+
return colon < 0 ? name : name.slice(colon + 1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The child elements of a tree node with a local name, in file order.
|
|
54
|
+
* @param node - the tree node
|
|
55
|
+
* @param local - the local name
|
|
56
|
+
* @returns the children
|
|
57
|
+
*/
|
|
58
|
+
export function treeChildren(node: unknown, local: string): unknown[] {
|
|
59
|
+
const out: unknown[] = [];
|
|
60
|
+
if (!isTree(node)) {
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
for (const key of Object.keys(node)) {
|
|
64
|
+
if (key.startsWith("@_") || key === "#text" || localOf(key) !== local) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const value = node[key];
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
for (const item of value) {
|
|
70
|
+
out.push(item);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
out.push(value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The first child element of a tree node with a local name.
|
|
81
|
+
* @param node - the tree node
|
|
82
|
+
* @param local - the local name
|
|
83
|
+
* @returns the child, or undefined
|
|
84
|
+
*/
|
|
85
|
+
export function treeChild(node: unknown, local: string): unknown {
|
|
86
|
+
if (!isTree(node)) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
for (const key of Object.keys(node)) {
|
|
90
|
+
if (!key.startsWith("@_") && key !== "#text" && localOf(key) === local) {
|
|
91
|
+
const value = node[key];
|
|
92
|
+
return Array.isArray(value) ? value[0] : value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* An attribute of a tree node.
|
|
100
|
+
* @param node - the tree node
|
|
101
|
+
* @param name - the attribute name as written
|
|
102
|
+
* @returns its text, or undefined
|
|
103
|
+
*/
|
|
104
|
+
export function treeAttr(node: unknown, name: string): string | undefined {
|
|
105
|
+
if (!isTree(node) || !Object.hasOwn(node, `@_${name}`)) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const value = node[`@_${name}`];
|
|
109
|
+
return typeof value === "string" ? value : undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The text of an element: the element itself when it has neither attributes nor children, else its `#text`.
|
|
114
|
+
* @param node - the tree node
|
|
115
|
+
* @returns the text, or null
|
|
116
|
+
*/
|
|
117
|
+
export function treeText(node: unknown): string | null {
|
|
118
|
+
if (typeof node === "string") {
|
|
119
|
+
return node;
|
|
120
|
+
}
|
|
121
|
+
if (isTree(node) && Object.hasOwn(node, "#text")) {
|
|
122
|
+
const text = node["#text"];
|
|
123
|
+
return typeof text === "string" ? text : null;
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The json columns of a table holding yFiles trees.
|
|
130
|
+
* @param table - the node, edge or graph table
|
|
131
|
+
* @returns their names
|
|
132
|
+
*/
|
|
133
|
+
function treeColumns(table: GraphSnapshot["nodes"]): string[] {
|
|
134
|
+
const out: string[] = [];
|
|
135
|
+
for (const col of table) {
|
|
136
|
+
if (col.dtype === "json" && col.meta.origin?.namespace === "yfiles") {
|
|
137
|
+
out.push(col.meta.name);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Every column of a table the GraphML importer made from yFiles keys: the trees and the typed `yfiles.*` columns
|
|
145
|
+
* read from them. They hold the styles themselves, so the planner never explains a style with them.
|
|
146
|
+
* @param table - the node or edge table
|
|
147
|
+
* @returns their names
|
|
148
|
+
*/
|
|
149
|
+
export function yfilesColumns(table: GraphSnapshot["nodes"]): string[] {
|
|
150
|
+
const out: string[] = [];
|
|
151
|
+
for (const col of table) {
|
|
152
|
+
if (col.meta.origin?.namespace === "yfiles") {
|
|
153
|
+
out.push(col.meta.name);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ============================================================ reading
|
|
160
|
+
|
|
161
|
+
/** One element's canonical value of a property; null leaves it unset. */
|
|
162
|
+
export type PutValue = (property: string, value: StyleValue | null) => void;
|
|
163
|
+
|
|
164
|
+
/** Where a value of an element was read, by property. */
|
|
165
|
+
export type SiteOf = (property: string) => CanonSite;
|
|
166
|
+
|
|
167
|
+
/** The yEd node realizers, by local name. */
|
|
168
|
+
const NODE_REALIZERS: ReadonlySet<string> = new Set([
|
|
169
|
+
"ShapeNode",
|
|
170
|
+
"GenericNode",
|
|
171
|
+
"GroupNode",
|
|
172
|
+
"GenericGroupNode",
|
|
173
|
+
"SVGNode",
|
|
174
|
+
"ImageNode",
|
|
175
|
+
"TableNode",
|
|
176
|
+
"UMLClassNode",
|
|
177
|
+
"UMLNoteNode",
|
|
178
|
+
"ProxyAutoBoundsNode",
|
|
179
|
+
]);
|
|
180
|
+
|
|
181
|
+
/** The yEd edge realizers, by local name. */
|
|
182
|
+
const EDGE_REALIZERS: ReadonlySet<string> = new Set([
|
|
183
|
+
"PolyLineEdge",
|
|
184
|
+
"ArcEdge",
|
|
185
|
+
"BezierEdge",
|
|
186
|
+
"QuadCurveEdge",
|
|
187
|
+
"SplineEdge",
|
|
188
|
+
"GenericEdge",
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
/** A realizer: its local name and its element. */
|
|
192
|
+
interface Realizer {
|
|
193
|
+
readonly type: string;
|
|
194
|
+
readonly r: unknown;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The realizer a graphics tree holds; a `y:ProxyAutoBoundsNode` (a folder or group) gives its active realizer.
|
|
199
|
+
* @param tree - the `<data>` content
|
|
200
|
+
* @param names - the realizer names of the target
|
|
201
|
+
* @returns the realizer, or null
|
|
202
|
+
*/
|
|
203
|
+
function realizerOf(tree: unknown, names: ReadonlySet<string>): Realizer | null {
|
|
204
|
+
if (!isTree(tree)) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
for (const key of Object.keys(tree)) {
|
|
208
|
+
const type = localOf(key);
|
|
209
|
+
if (key.startsWith("@_") || !names.has(type)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const value = tree[key];
|
|
213
|
+
const r = Array.isArray(value) ? value[0] : value;
|
|
214
|
+
return type === "ProxyAutoBoundsNode" ? activeRealizer(r) : { type, r };
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The active realizer of a `y:ProxyAutoBoundsNode`, else its first.
|
|
221
|
+
* @param proxy - the proxy element
|
|
222
|
+
* @returns the realizer, or null when it has none
|
|
223
|
+
*/
|
|
224
|
+
function activeRealizer(proxy: unknown): Realizer | null {
|
|
225
|
+
const realizers = treeChild(proxy, "Realizers");
|
|
226
|
+
const all: Realizer[] = [];
|
|
227
|
+
if (isTree(realizers)) {
|
|
228
|
+
for (const k of Object.keys(realizers).filter((key) => !key.startsWith("@_") && key !== "#text")) {
|
|
229
|
+
for (const item of treeChildren(realizers, localOf(k))) {
|
|
230
|
+
all.push({ type: localOf(k), r: item });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const active = Number(treeAttr(realizers, "active") ?? "0");
|
|
235
|
+
return all[Number.isInteger(active) && active >= 0 && active < all.length ? active : 0] ?? null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* yEd's flowchart palette (`com.yworks.flowchart.<name>`), by name, to the shape each draws. Matched whole, before
|
|
240
|
+
* the words below: "data" is a parallelogram, but "storedData" and "directData" are not.
|
|
241
|
+
*/
|
|
242
|
+
const FLOWCHART_SHAPES: ReadonlyMap<string, string> = new Map([
|
|
243
|
+
["process", "rectangle"],
|
|
244
|
+
["decision", "diamond"],
|
|
245
|
+
["terminator", "round-rectangle"],
|
|
246
|
+
["start1", "ellipse"],
|
|
247
|
+
["start2", "ellipse"],
|
|
248
|
+
["data", "parallelogram"],
|
|
249
|
+
["preparation", "hexagon"],
|
|
250
|
+
]);
|
|
251
|
+
|
|
252
|
+
const FLOWCHART_PREFIX = "com.yworks.flowchart.";
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The shape a yEd flowchart configuration draws: a GraphML GenericNode's `configuration`, or a yEd GML node's
|
|
256
|
+
* `customconfiguration`.
|
|
257
|
+
* @param configuration - the configuration
|
|
258
|
+
* @returns the neutral shape, or null for a configuration that is not one of the flowchart shapes known here
|
|
259
|
+
*/
|
|
260
|
+
export function flowchartShape(configuration: string): string | null {
|
|
261
|
+
return configuration.startsWith(FLOWCHART_PREFIX)
|
|
262
|
+
? (FLOWCHART_SHAPES.get(configuration.slice(FLOWCHART_PREFIX.length)) ?? null)
|
|
263
|
+
: null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** GenericNode configurations, by a word they contain, to the shape they draw; the first match wins. */
|
|
267
|
+
const GENERIC_SHAPES: readonly (readonly [string, string])[] = [
|
|
268
|
+
["ellipse", "ellipse"],
|
|
269
|
+
["circle", "ellipse"],
|
|
270
|
+
["oval", "ellipse"],
|
|
271
|
+
["diamond", "diamond"],
|
|
272
|
+
["decision", "diamond"],
|
|
273
|
+
["hexagon", "hexagon"],
|
|
274
|
+
["octagon", "octagon"],
|
|
275
|
+
["triangle", "triangle"],
|
|
276
|
+
["rect", "rectangle"],
|
|
277
|
+
["process", "rectangle"],
|
|
278
|
+
["box", "rectangle"],
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
/** yEd's label positions (`modelPosition` of the internal, sides and eight-position models). */
|
|
282
|
+
const YED_POSITIONS: ReadonlyMap<string, string> = new Map([
|
|
283
|
+
["c", "center"],
|
|
284
|
+
["t", "top"],
|
|
285
|
+
["b", "bottom"],
|
|
286
|
+
["l", "left"],
|
|
287
|
+
["r", "right"],
|
|
288
|
+
["tl", "top-left"],
|
|
289
|
+
["tr", "top-right"],
|
|
290
|
+
["bl", "bottom-left"],
|
|
291
|
+
["br", "bottom-right"],
|
|
292
|
+
["n", "top"],
|
|
293
|
+
["s", "bottom"],
|
|
294
|
+
["e", "right"],
|
|
295
|
+
["w", "left"],
|
|
296
|
+
["nw", "top-left"],
|
|
297
|
+
["ne", "top-right"],
|
|
298
|
+
["sw", "bottom-left"],
|
|
299
|
+
["se", "bottom-right"],
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
/** yEd's font styles, as label weight and italic. */
|
|
303
|
+
export const FONT_STYLES: ReadonlyMap<string, readonly [string, boolean]> = new Map([
|
|
304
|
+
["plain", ["normal", false]],
|
|
305
|
+
["bold", ["bold", false]],
|
|
306
|
+
["italic", ["normal", true]],
|
|
307
|
+
["bolditalic", ["bold", true]],
|
|
308
|
+
]);
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Reads a color into a property and its alpha into the property's opacity (or notes the alpha dropped).
|
|
312
|
+
* @param raw - the attribute
|
|
313
|
+
* @param property - the color property
|
|
314
|
+
* @param opacity - its opacity property, or null when it has none
|
|
315
|
+
* @param put - receives the values
|
|
316
|
+
* @param at - the sites
|
|
317
|
+
*/
|
|
318
|
+
function readColor(raw: unknown, property: string, opacity: string | null, put: PutValue, at: SiteOf): void {
|
|
319
|
+
const c = canonColor(raw, "yed", at(property));
|
|
320
|
+
if (c === null) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
put(property, c.color);
|
|
324
|
+
const alpha = colorOpacity(c, null, at(property), opacity !== null);
|
|
325
|
+
if (opacity !== null) {
|
|
326
|
+
put(opacity, alpha);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Reads an element's label: the first visible label with text (yEd icon nodes put an empty, `hasText="false"`
|
|
332
|
+
* label first); further visible labels are noted for the element.
|
|
333
|
+
* @param labels - the label elements, in file order
|
|
334
|
+
* @param node - whether they are node labels (which have a position)
|
|
335
|
+
* @param put - receives the values
|
|
336
|
+
* @param at - the sites
|
|
337
|
+
*/
|
|
338
|
+
function readLabels(labels: readonly unknown[], node: boolean, put: PutValue, at: SiteOf): void {
|
|
339
|
+
let label: unknown = undefined;
|
|
340
|
+
let text = "";
|
|
341
|
+
let extra = 0;
|
|
342
|
+
for (const l of labels) {
|
|
343
|
+
const t = treeText(l)?.trim() ?? "";
|
|
344
|
+
if (t.length === 0 || treeAttr(l, "visible") === "false" || treeAttr(l, "hasText") === "false") {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (label === undefined) {
|
|
348
|
+
label = l;
|
|
349
|
+
text = t;
|
|
350
|
+
} else {
|
|
351
|
+
extra++;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (label === undefined) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (extra > 0) {
|
|
358
|
+
noteAt(at("label"), STYLE_ISSUE.EXTRA_LABELS, `${String(extra + 1)} labels`);
|
|
359
|
+
}
|
|
360
|
+
put("label", canonText(text, at("label")));
|
|
361
|
+
readLabelLook(label, node, put, at);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Reads a label's look: its color, font and, for a node label, its position.
|
|
366
|
+
* @param label - the label element
|
|
367
|
+
* @param node - whether it is a node label
|
|
368
|
+
* @param put - receives the values
|
|
369
|
+
* @param at - the sites
|
|
370
|
+
*/
|
|
371
|
+
function readLabelLook(label: unknown, node: boolean, put: PutValue, at: SiteOf): void {
|
|
372
|
+
readColor(treeAttr(label, "textColor"), "labelColor", null, put, at);
|
|
373
|
+
put("labelSize", canonNumber(treeAttr(label, "fontSize"), at("labelSize")));
|
|
374
|
+
put("labelFont", canonText(treeAttr(label, "fontFamily"), at("labelFont")));
|
|
375
|
+
const style = treeAttr(label, "fontStyle");
|
|
376
|
+
if (style !== undefined) {
|
|
377
|
+
const known = FONT_STYLES.get(style.trim().toLowerCase());
|
|
378
|
+
if (known === undefined) {
|
|
379
|
+
noteAt(at("labelWeight"), STYLE_ISSUE.UNKNOWN_VALUE, style.slice(0, MAX_STYLE_VALUE_LENGTH));
|
|
380
|
+
} else {
|
|
381
|
+
put("labelWeight", known[0]);
|
|
382
|
+
put("labelItalic", known[1]);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const position = node ? treeAttr(label, "modelPosition") : undefined;
|
|
386
|
+
if (position !== undefined) {
|
|
387
|
+
const known = YED_POSITIONS.get(position.trim().toLowerCase());
|
|
388
|
+
if (known === undefined) {
|
|
389
|
+
noteAt(at("labelPosition"), STYLE_ISSUE.UNKNOWN_VALUE, position.slice(0, MAX_STYLE_VALUE_LENGTH));
|
|
390
|
+
}
|
|
391
|
+
put("labelPosition", known ?? null);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* The shape of a node realizer: its `y:Shape` where it has one, a GenericNode's configuration, or the realizer
|
|
397
|
+
* type itself for one that draws an image (SVG, image, table, UML).
|
|
398
|
+
* @param real - the realizer
|
|
399
|
+
* @param at - the sites
|
|
400
|
+
* @returns the shape, or null
|
|
401
|
+
*/
|
|
402
|
+
function realizerShape(real: Realizer, at: SiteOf): string | null {
|
|
403
|
+
const shape = treeChild(real.r, "Shape");
|
|
404
|
+
if (shape !== undefined) {
|
|
405
|
+
return canonEnum("shape", treeAttr(shape, "type"), "yed", at("shape"));
|
|
406
|
+
}
|
|
407
|
+
if (real.type === "ShapeNode" || real.type === "GroupNode" || real.type === "GenericGroupNode") {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
if (real.type === "GenericNode") {
|
|
411
|
+
const conf = treeAttr(real.r, "configuration") ?? "";
|
|
412
|
+
const flowchart = flowchartShape(conf);
|
|
413
|
+
if (flowchart !== null) {
|
|
414
|
+
return flowchart;
|
|
415
|
+
}
|
|
416
|
+
const lower = conf.slice(0, MAX_STYLE_VALUE_LENGTH).toLowerCase();
|
|
417
|
+
for (const [word, value] of GENERIC_SHAPES) {
|
|
418
|
+
if (lower.includes(word)) {
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (lower.length === 0) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
noteAt(at("shape"), STYLE_ISSUE.UNKNOWN_VALUE, lower);
|
|
426
|
+
return `other:${lower}`;
|
|
427
|
+
}
|
|
428
|
+
noteAt(at("shape"), STYLE_ISSUE.UNKNOWN_VALUE, real.type);
|
|
429
|
+
return `other:${real.type}`;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Reads one node realizer.
|
|
434
|
+
* @param real - the realizer
|
|
435
|
+
* @param put - receives the values
|
|
436
|
+
* @param at - the sites
|
|
437
|
+
*/
|
|
438
|
+
function readNode(real: Realizer, put: PutValue, at: SiteOf): void {
|
|
439
|
+
const { r } = real;
|
|
440
|
+
const fill = treeChild(r, "Fill");
|
|
441
|
+
if (fill !== undefined) {
|
|
442
|
+
if (treeAttr(fill, "hasColor") === "false" || treeAttr(fill, "transparent") === "true") {
|
|
443
|
+
noteAt(at("fill"), STYLE_ISSUE.NO_FILL, "transparent");
|
|
444
|
+
put("fillOpacity", 0);
|
|
445
|
+
} else {
|
|
446
|
+
readColor(treeAttr(fill, "color"), "fill", "fillOpacity", put, at);
|
|
447
|
+
const second = treeAttr(fill, "color2");
|
|
448
|
+
if (second !== undefined) {
|
|
449
|
+
noteAt(at("fill"), STYLE_ISSUE.GRADIENT, second);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const border = treeChild(r, "BorderStyle");
|
|
454
|
+
if (border !== undefined) {
|
|
455
|
+
if (treeAttr(border, "hasColor") === "false") {
|
|
456
|
+
put("borderWidth", 0);
|
|
457
|
+
} else {
|
|
458
|
+
readColor(treeAttr(border, "color"), "borderColor", null, put, at);
|
|
459
|
+
put("borderWidth", canonNumber(treeAttr(border, "width"), at("borderWidth")));
|
|
460
|
+
}
|
|
461
|
+
put("borderStyle", canonEnum("borderStyle", treeAttr(border, "type"), "yed", at("borderStyle")));
|
|
462
|
+
}
|
|
463
|
+
const geometry = treeChild(r, "Geometry");
|
|
464
|
+
put("width", canonNumber(treeAttr(geometry, "width"), at("width")));
|
|
465
|
+
put("height", canonNumber(treeAttr(geometry, "height"), at("height")));
|
|
466
|
+
put("shape", realizerShape(real, at));
|
|
467
|
+
readLabels(treeChildren(r, "NodeLabel"), true, put, at);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Reads one edge realizer.
|
|
472
|
+
* @param real - the realizer
|
|
473
|
+
* @param put - receives the values
|
|
474
|
+
* @param at - the sites
|
|
475
|
+
*/
|
|
476
|
+
function readEdge(real: Realizer, put: PutValue, at: SiteOf): void {
|
|
477
|
+
const { r } = real;
|
|
478
|
+
const line = treeChild(r, "LineStyle");
|
|
479
|
+
if (line !== undefined) {
|
|
480
|
+
readColor(treeAttr(line, "color"), "color", "opacity", put, at);
|
|
481
|
+
put("width", canonNumber(treeAttr(line, "width"), at("width")));
|
|
482
|
+
put("lineStyle", canonEnum("lineStyle", treeAttr(line, "type"), "yed", at("lineStyle")));
|
|
483
|
+
}
|
|
484
|
+
const arrows = treeChild(r, "Arrows");
|
|
485
|
+
if (arrows !== undefined) {
|
|
486
|
+
put("sourceArrow", canonEnum("arrow", treeAttr(arrows, "source"), "yed", at("sourceArrow")));
|
|
487
|
+
put("targetArrow", canonEnum("arrow", treeAttr(arrows, "target"), "yed", at("targetArrow")));
|
|
488
|
+
}
|
|
489
|
+
put("curved", edgeCurved(real));
|
|
490
|
+
readLabels(treeChildren(r, "EdgeLabel"), false, put, at);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Whether yEd draws an edge curved: an arc always; a Bezier, quad curve, spline or generic edge only through its
|
|
495
|
+
* control points (with none it is a straight line); a polyline only when it smooths bends it has.
|
|
496
|
+
* @param real - the edge realizer
|
|
497
|
+
* @returns true when curved, null when a polyline does not say whether it smooths
|
|
498
|
+
*/
|
|
499
|
+
function edgeCurved(real: Realizer): boolean | null {
|
|
500
|
+
if (real.type === "ArcEdge") {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
const bent = treeChildren(treeChild(real.r, "Path"), "Point").length > 0;
|
|
504
|
+
if (real.type !== "PolyLineEdge") {
|
|
505
|
+
return bent;
|
|
506
|
+
}
|
|
507
|
+
const smoothed = treeAttr(treeChild(real.r, "BendStyle"), "smoothed");
|
|
508
|
+
return smoothed === undefined ? null : smoothed === "true" && bent;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* An edge's bends: the points of its `y:Path`, relative to the centers of its two nodes. yEd draws a polyline through
|
|
513
|
+
* them; a curve type (Bezier, quad curve, spline) passes near them as its control points.
|
|
514
|
+
* @param real - the edge realizer
|
|
515
|
+
* @param centers - each node's center, x and y
|
|
516
|
+
* @param source - the source node's index
|
|
517
|
+
* @param target - the target node's index
|
|
518
|
+
* @returns the `bends` value, or null without bends
|
|
519
|
+
*/
|
|
520
|
+
function edgeBends(real: Realizer, centers: Float64Array, source: number, target: number): string | null {
|
|
521
|
+
const points = treeChildren(treeChild(real.r, "Path"), "Point").map((p): BendPoint => [
|
|
522
|
+
Number(treeAttr(p, "x")),
|
|
523
|
+
Number(treeAttr(p, "y")),
|
|
524
|
+
]);
|
|
525
|
+
const at = (k: number): BendPoint => [centers[2 * k], centers[2 * k + 1]];
|
|
526
|
+
return canonBends(at(source), at(target), points, true);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** Canonical values per property, by element index. */
|
|
530
|
+
export type ValueColumns = Record<string, (StyleValue | null)[]>;
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* A collector of canonical values for one target.
|
|
534
|
+
* @param n - the element count
|
|
535
|
+
* @returns the values and a setter for element i
|
|
536
|
+
*/
|
|
537
|
+
export function valueColumns(n: number): {
|
|
538
|
+
readonly values: ValueColumns;
|
|
539
|
+
put(i: number, property: string, value: StyleValue | null): void;
|
|
540
|
+
} {
|
|
541
|
+
const values: ValueColumns = Object.create(null) as ValueColumns;
|
|
542
|
+
return {
|
|
543
|
+
values,
|
|
544
|
+
put(i, property, value) {
|
|
545
|
+
if (value !== null) {
|
|
546
|
+
values[property] ??= new Array<StyleValue | null>(n).fill(null);
|
|
547
|
+
values[property][i] = value;
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/** What the yEd reader read. */
|
|
554
|
+
export interface YedRead {
|
|
555
|
+
/** Node values. */
|
|
556
|
+
readonly node: ValueColumns;
|
|
557
|
+
/** Edge values. */
|
|
558
|
+
readonly edge: ValueColumns;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* The yEd styles of a graph: each element's realizer read into canonical values.
|
|
563
|
+
* @param snapshot - the graph
|
|
564
|
+
* @param notes - where problems are noted
|
|
565
|
+
* @param budget - the work budget
|
|
566
|
+
* @returns the values, or null when no element has a yEd realizer
|
|
567
|
+
*/
|
|
568
|
+
export function readYedStyles(snapshot: GraphSnapshot, notes: StyleNotes, budget: StyleWorkBudget): YedRead | null {
|
|
569
|
+
let found = false;
|
|
570
|
+
// each node's center, read with the node, for its edges' bends; NaN for a node without yEd graphics
|
|
571
|
+
const centers = new Float64Array(2 * snapshot.nodeCount).fill(Number.NaN);
|
|
572
|
+
const read = (target: StyleTarget): ValueColumns => {
|
|
573
|
+
const table = target === "node" ? snapshot.nodes : snapshot.edges;
|
|
574
|
+
const n = target === "node" ? snapshot.nodeCount : snapshot.edgeCount;
|
|
575
|
+
const names = target === "node" ? NODE_REALIZERS : EDGE_REALIZERS;
|
|
576
|
+
const columns = treeColumns(table);
|
|
577
|
+
const out = valueColumns(n);
|
|
578
|
+
if (columns.length === 0) {
|
|
579
|
+
return out.values;
|
|
580
|
+
}
|
|
581
|
+
for (let i = 0; i < n; i++) {
|
|
582
|
+
budget.spend(target, columns.length);
|
|
583
|
+
let real: Realizer | null = null;
|
|
584
|
+
for (let c = 0; c < columns.length && real === null; c++) {
|
|
585
|
+
real = realizerOf(plainCell(table.value(columns[c], i)), names);
|
|
586
|
+
}
|
|
587
|
+
if (real === null) {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
found = true;
|
|
591
|
+
const at: SiteOf = (property) => ({ notes, target, property, element: i, budget });
|
|
592
|
+
const put: PutValue = (property, value) => {
|
|
593
|
+
out.put(i, property, value);
|
|
594
|
+
};
|
|
595
|
+
if (target === "node") {
|
|
596
|
+
readNode(real, put, at);
|
|
597
|
+
const geometry = treeChild(real.r, "Geometry");
|
|
598
|
+
centers[2 * i] = Number(treeAttr(geometry, "x")) + Number(treeAttr(geometry, "width")) / 2;
|
|
599
|
+
centers[2 * i + 1] = Number(treeAttr(geometry, "y")) + Number(treeAttr(geometry, "height")) / 2;
|
|
600
|
+
} else {
|
|
601
|
+
readEdge(real, put, at);
|
|
602
|
+
put("bends", edgeBends(real, centers, snapshot.edgeSource(i), snapshot.edgeTarget(i)));
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return out.values;
|
|
606
|
+
};
|
|
607
|
+
const node = read("node");
|
|
608
|
+
const edge = read("edge");
|
|
609
|
+
return found ? { node, edge } : null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ============================================================ writing
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* What the GraphML writer holds natively: yEd `y:ShapeNode` and `y:PolyLineEdge` graphics. The `graphty.styles`
|
|
616
|
+
* block is a graph `<data>` under its own key, and the per-element values its rules read are keys of their own.
|
|
617
|
+
* @category Built-in formats
|
|
618
|
+
*/
|
|
619
|
+
export const GRAPHML_STYLE_CAPABILITIES: StyleWriteCapabilities = Object.freeze({
|
|
620
|
+
node: Object.freeze([
|
|
621
|
+
"fill",
|
|
622
|
+
"fillOpacity",
|
|
623
|
+
"borderColor",
|
|
624
|
+
"borderWidth",
|
|
625
|
+
"borderStyle",
|
|
626
|
+
"shape",
|
|
627
|
+
"width",
|
|
628
|
+
"height",
|
|
629
|
+
"label",
|
|
630
|
+
"labelColor",
|
|
631
|
+
"labelSize",
|
|
632
|
+
"labelFont",
|
|
633
|
+
"labelWeight",
|
|
634
|
+
"labelItalic",
|
|
635
|
+
"labelPosition",
|
|
636
|
+
] as const),
|
|
637
|
+
edge: Object.freeze([
|
|
638
|
+
"color",
|
|
639
|
+
"opacity",
|
|
640
|
+
"width",
|
|
641
|
+
"lineStyle",
|
|
642
|
+
"sourceArrow",
|
|
643
|
+
"targetArrow",
|
|
644
|
+
"curved",
|
|
645
|
+
"bends",
|
|
646
|
+
"label",
|
|
647
|
+
"labelColor",
|
|
648
|
+
"labelSize",
|
|
649
|
+
"labelFont",
|
|
650
|
+
"labelWeight",
|
|
651
|
+
"labelItalic",
|
|
652
|
+
] as const),
|
|
653
|
+
block: "attributes",
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
/** A written value and whether it is the exact value. */
|
|
657
|
+
interface Written {
|
|
658
|
+
readonly value: string;
|
|
659
|
+
readonly exact: boolean;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** yEd's ShapeNode types, by the neutral shape each draws exactly. */
|
|
663
|
+
export const YED_SHAPES: ReadonlyMap<string, string> = new Map([
|
|
664
|
+
["ellipse", "ellipse"],
|
|
665
|
+
["rectangle", "rectangle"],
|
|
666
|
+
["round-rectangle", "roundrectangle"],
|
|
667
|
+
["triangle", "triangle"],
|
|
668
|
+
["diamond", "diamond"],
|
|
669
|
+
["hexagon", "hexagon"],
|
|
670
|
+
["octagon", "octagon"],
|
|
671
|
+
["parallelogram", "parallelogram"],
|
|
672
|
+
["trapezoid", "trapezoid"],
|
|
673
|
+
["star", "star5"],
|
|
674
|
+
]);
|
|
675
|
+
|
|
676
|
+
/** yEd's line types, by the neutral line style each draws exactly. */
|
|
677
|
+
export const YED_LINES: ReadonlyMap<string, string> = new Map([
|
|
678
|
+
["solid", "line"],
|
|
679
|
+
["dashed", "dashed"],
|
|
680
|
+
["dotted", "dotted"],
|
|
681
|
+
["dash-dot", "dashed_dotted"],
|
|
682
|
+
]);
|
|
683
|
+
|
|
684
|
+
/** yEd's arrows, by the neutral arrow each draws exactly. */
|
|
685
|
+
export const YED_ARROWS: ReadonlyMap<string, string> = new Map([
|
|
686
|
+
["none", "none"],
|
|
687
|
+
["triangle", "standard"],
|
|
688
|
+
["triangle-open", "white_delta"],
|
|
689
|
+
["vee", "concave"],
|
|
690
|
+
["tee", "t_shape"],
|
|
691
|
+
["diamond", "diamond"],
|
|
692
|
+
["diamond-open", "white_diamond"],
|
|
693
|
+
["circle", "circle"],
|
|
694
|
+
["circle-open", "white_circle"],
|
|
695
|
+
["square", "skewed_dash"],
|
|
696
|
+
["crow", "crows_foot_many"],
|
|
697
|
+
]);
|
|
698
|
+
|
|
699
|
+
/** yEd's label positions in the internal model, by neutral position. */
|
|
700
|
+
const YED_MODEL_POSITIONS: ReadonlyMap<string, string> = new Map([
|
|
701
|
+
["center", "c"],
|
|
702
|
+
["top", "t"],
|
|
703
|
+
["bottom", "b"],
|
|
704
|
+
["left", "l"],
|
|
705
|
+
["right", "r"],
|
|
706
|
+
["top-left", "tl"],
|
|
707
|
+
["top-right", "tr"],
|
|
708
|
+
["bottom-left", "bl"],
|
|
709
|
+
["bottom-right", "br"],
|
|
710
|
+
]);
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* A value of a table, or the nearest value the table has.
|
|
714
|
+
* @param table - neutral value to yEd value
|
|
715
|
+
* @param value - the neutral value
|
|
716
|
+
* @param nearest - the yEd value written when the table has none
|
|
717
|
+
* @returns the written value
|
|
718
|
+
*/
|
|
719
|
+
export function yedValue(table: ReadonlyMap<string, string>, value: string, nearest: string): Written {
|
|
720
|
+
const exact = table.get(value);
|
|
721
|
+
if (exact !== undefined) {
|
|
722
|
+
return { value: exact, exact: true };
|
|
723
|
+
}
|
|
724
|
+
// a value the reader kept as other:<raw> goes back as written
|
|
725
|
+
return value.startsWith("other:") ? { value: value.slice(6), exact: true } : { value: nearest, exact: false };
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* A color with an alpha from an opacity, as yEd writes it: `#RRGGBB`, or `#RRGGBBAA` with the alpha last.
|
|
730
|
+
* @param color - the canonical color
|
|
731
|
+
* @param opacity - the opacity, or null
|
|
732
|
+
* @returns the text
|
|
733
|
+
*/
|
|
734
|
+
export function yedColor(color: string, opacity: StyleValue | null): string {
|
|
735
|
+
const hex = color.toUpperCase();
|
|
736
|
+
if (typeof opacity !== "number" || opacity >= 1) {
|
|
737
|
+
return hex;
|
|
738
|
+
}
|
|
739
|
+
return (
|
|
740
|
+
hex +
|
|
741
|
+
Math.round(Math.max(0, opacity) * 255)
|
|
742
|
+
.toString(16)
|
|
743
|
+
.toUpperCase()
|
|
744
|
+
.padStart(2, "0")
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/** Records a value written as the nearest one yEd has. */
|
|
749
|
+
type Approximate = (property: string, value: StyleValue) => void;
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* A value in yEd's spelling, recorded as approximated when yEd has no exact one.
|
|
753
|
+
* @param table - yEd's spellings
|
|
754
|
+
* @param value - the neutral value
|
|
755
|
+
* @param nearest - the spelling written when there is no exact one
|
|
756
|
+
* @param property - the property, for the approximation note
|
|
757
|
+
* @param approximate - records approximations
|
|
758
|
+
* @returns the spelling
|
|
759
|
+
*/
|
|
760
|
+
export function yedSpelled(
|
|
761
|
+
table: ReadonlyMap<string, string>,
|
|
762
|
+
value: string,
|
|
763
|
+
nearest: string,
|
|
764
|
+
property: string,
|
|
765
|
+
approximate: Approximate,
|
|
766
|
+
): string {
|
|
767
|
+
const w = yedValue(table, value, nearest);
|
|
768
|
+
if (!w.exact) {
|
|
769
|
+
approximate(property, value);
|
|
770
|
+
}
|
|
771
|
+
return w.value;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/** One element's value of a property. */
|
|
775
|
+
type GetValue = (property: string) => StyleValue | null;
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* The label attributes and text of one element.
|
|
779
|
+
* @param get - the element's values
|
|
780
|
+
* @param node - whether it is a node label (which has a position)
|
|
781
|
+
* @param approximate - records approximations
|
|
782
|
+
* @returns the label element, or null when the element has no label text
|
|
783
|
+
*/
|
|
784
|
+
function labelTree(get: GetValue, node: boolean, approximate: Approximate): Tree | null {
|
|
785
|
+
const text = get("label");
|
|
786
|
+
if (typeof text !== "string" || text.trim().length === 0) {
|
|
787
|
+
return null;
|
|
788
|
+
}
|
|
789
|
+
const label: Tree = {};
|
|
790
|
+
const font = get("labelFont");
|
|
791
|
+
if (typeof font === "string") {
|
|
792
|
+
label["@_fontFamily"] = font;
|
|
793
|
+
}
|
|
794
|
+
const size = get("labelSize");
|
|
795
|
+
if (typeof size === "number") {
|
|
796
|
+
label["@_fontSize"] = String(size);
|
|
797
|
+
}
|
|
798
|
+
const weight = get("labelWeight");
|
|
799
|
+
const italic = get("labelItalic");
|
|
800
|
+
if (weight !== null || italic !== null) {
|
|
801
|
+
const style = (weight === "bold" ? "bold" : "") + (italic === true ? "italic" : "");
|
|
802
|
+
label["@_fontStyle"] = style.length === 0 ? "plain" : style;
|
|
803
|
+
}
|
|
804
|
+
const color = get("labelColor");
|
|
805
|
+
if (typeof color === "string") {
|
|
806
|
+
label["@_textColor"] = color.toUpperCase();
|
|
807
|
+
}
|
|
808
|
+
const position = node ? get("labelPosition") : null;
|
|
809
|
+
if (typeof position === "string") {
|
|
810
|
+
const written = YED_MODEL_POSITIONS.get(position);
|
|
811
|
+
if (written === undefined) {
|
|
812
|
+
approximate("labelPosition", position);
|
|
813
|
+
}
|
|
814
|
+
label["@_modelName"] = "internal";
|
|
815
|
+
label["@_modelPosition"] = written ?? "c";
|
|
816
|
+
}
|
|
817
|
+
label["#text"] = text;
|
|
818
|
+
return label;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* A node's `y:ShapeNode` from its values.
|
|
823
|
+
* @param get - the node's values
|
|
824
|
+
* @param at - the node's position from the graphics it had, or null
|
|
825
|
+
* @param approximate - records approximations
|
|
826
|
+
* @returns the graphics tree
|
|
827
|
+
*/
|
|
828
|
+
function shapeNodeTree(get: GetValue, at: readonly [string, string] | null, approximate: Approximate): Tree {
|
|
829
|
+
const node: Tree = {};
|
|
830
|
+
node["y:Geometry"] = geometryTree(get, at);
|
|
831
|
+
const fill = get("fill");
|
|
832
|
+
const fillOpacity = get("fillOpacity");
|
|
833
|
+
if (typeof fill === "string") {
|
|
834
|
+
node["y:Fill"] = { "@_color": yedColor(fill, fillOpacity), "@_transparent": "false" };
|
|
835
|
+
} else if (fillOpacity === 0) {
|
|
836
|
+
node["y:Fill"] = { "@_hasColor": "false", "@_transparent": "false" };
|
|
837
|
+
}
|
|
838
|
+
const border = lineTree(get, ["borderColor", "borderWidth", "borderStyle"], approximate);
|
|
839
|
+
if (border !== null) {
|
|
840
|
+
node["y:BorderStyle"] = border;
|
|
841
|
+
}
|
|
842
|
+
const label = labelTree(get, true, approximate);
|
|
843
|
+
if (label !== null) {
|
|
844
|
+
node["y:NodeLabel"] = label;
|
|
845
|
+
}
|
|
846
|
+
const shape = get("shape");
|
|
847
|
+
if (typeof shape === "string") {
|
|
848
|
+
node["y:Shape"] = {
|
|
849
|
+
"@_type": yedSpelled(YED_SHAPES, shape, shape === "vee" ? "triangle" : "rectangle", "shape", approximate),
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
return { "y:ShapeNode": node };
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* A node's `y:Geometry`: its size, and its position from the graphics it had.
|
|
857
|
+
* @param get - the node's values
|
|
858
|
+
* @param at - the node's position, or null
|
|
859
|
+
* @returns the geometry
|
|
860
|
+
*/
|
|
861
|
+
function geometryTree(get: GetValue, at: readonly [string, string] | null): Tree {
|
|
862
|
+
const geometry: Tree = {};
|
|
863
|
+
const height = get("height");
|
|
864
|
+
const width = get("width");
|
|
865
|
+
if (typeof height === "number") {
|
|
866
|
+
geometry["@_height"] = String(height);
|
|
867
|
+
}
|
|
868
|
+
if (typeof width === "number") {
|
|
869
|
+
geometry["@_width"] = String(width);
|
|
870
|
+
}
|
|
871
|
+
if (at !== null) {
|
|
872
|
+
geometry["@_x"] = at[0];
|
|
873
|
+
geometry["@_y"] = at[1];
|
|
874
|
+
}
|
|
875
|
+
return geometry;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* A `y:BorderStyle` or `y:LineStyle`: a color, a width and a line type. A node's border color is written opaque; an
|
|
880
|
+
* edge's color carries its opacity.
|
|
881
|
+
* @param get - the element's values
|
|
882
|
+
* @param properties - the color, width and style properties
|
|
883
|
+
* @param approximate - records approximations
|
|
884
|
+
* @param opacity - the opacity of the color, for an edge's line
|
|
885
|
+
* @returns the element, or null when none of the three is set
|
|
886
|
+
*/
|
|
887
|
+
function lineTree(
|
|
888
|
+
get: GetValue,
|
|
889
|
+
properties: readonly [string, string, string],
|
|
890
|
+
approximate: Approximate,
|
|
891
|
+
opacity?: StyleValue | null,
|
|
892
|
+
): Tree | null {
|
|
893
|
+
const [colorKey, widthKey, styleKey] = properties;
|
|
894
|
+
const color = get(colorKey);
|
|
895
|
+
const width = get(widthKey);
|
|
896
|
+
const style = get(styleKey);
|
|
897
|
+
if (color === null && width === null && style === null) {
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
const line: Tree = {};
|
|
901
|
+
if (typeof color === "string") {
|
|
902
|
+
line["@_color"] = opacity === undefined ? color.toUpperCase() : yedColor(color, opacity);
|
|
903
|
+
}
|
|
904
|
+
if (typeof style === "string") {
|
|
905
|
+
const nearest = styleKey === "lineStyle" && style === "long-dash" ? "dashed" : "line";
|
|
906
|
+
line["@_type"] = yedSpelled(YED_LINES, style, nearest, styleKey, approximate);
|
|
907
|
+
}
|
|
908
|
+
if (typeof width === "number") {
|
|
909
|
+
line["@_width"] = String(width);
|
|
910
|
+
}
|
|
911
|
+
return line;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* An edge's `y:PolyLineEdge` from its values.
|
|
916
|
+
* @param get - the edge's values
|
|
917
|
+
* @param path - the `y:Path` of the graphics it had, or undefined
|
|
918
|
+
* @param approximate - records approximations
|
|
919
|
+
* @returns the graphics tree
|
|
920
|
+
*/
|
|
921
|
+
function polyLineEdgeTree(get: GetValue, path: unknown, approximate: Approximate): Tree {
|
|
922
|
+
const edge: Tree = {};
|
|
923
|
+
if (path !== undefined) {
|
|
924
|
+
edge["y:Path"] = path;
|
|
925
|
+
}
|
|
926
|
+
const line = lineTree(get, ["color", "width", "lineStyle"], approximate, get("opacity"));
|
|
927
|
+
if (line !== null) {
|
|
928
|
+
edge["y:LineStyle"] = line;
|
|
929
|
+
}
|
|
930
|
+
const source = get("sourceArrow");
|
|
931
|
+
const target = get("targetArrow");
|
|
932
|
+
if (source !== null || target !== null) {
|
|
933
|
+
const arrow = (property: string, value: StyleValue | null): string =>
|
|
934
|
+
typeof value === "string" ? yedSpelled(YED_ARROWS, value, "standard", property, approximate) : "none";
|
|
935
|
+
edge["y:Arrows"] = { "@_source": arrow("sourceArrow", source), "@_target": arrow("targetArrow", target) };
|
|
936
|
+
}
|
|
937
|
+
const label = labelTree(get, false, approximate);
|
|
938
|
+
if (label !== null) {
|
|
939
|
+
edge["y:EdgeLabel"] = label;
|
|
940
|
+
}
|
|
941
|
+
const curved = get("curved");
|
|
942
|
+
if (typeof curved === "boolean") {
|
|
943
|
+
edge["y:BendStyle"] = { "@_smoothed": String(curved) };
|
|
944
|
+
}
|
|
945
|
+
return { "y:PolyLineEdge": edge };
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Each element's values of the properties the writer writes, as a getter per element.
|
|
950
|
+
* @param picture - each element's value of a property
|
|
951
|
+
* @param target - nodes or edges
|
|
952
|
+
* @returns the getter
|
|
953
|
+
*/
|
|
954
|
+
function getter(
|
|
955
|
+
picture: (target: StyleTarget, property: string) => readonly (StyleValue | null)[],
|
|
956
|
+
target: StyleTarget,
|
|
957
|
+
): (i: number) => GetValue {
|
|
958
|
+
const columns = new Map<string, readonly (StyleValue | null)[]>();
|
|
959
|
+
for (const p of GRAPHML_STYLE_CAPABILITIES[target]) {
|
|
960
|
+
columns.set(p, picture(target, p));
|
|
961
|
+
}
|
|
962
|
+
return (i) => (property) => columns.get(property)?.[i] ?? null;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* For a file graphty wrote: where a value read from the yEd graphics is what the writer writes for the block's own
|
|
967
|
+
* value (a pentagon written as a hexagon, an opacity rounded to yEd's 255 steps), the block's exact value is the
|
|
968
|
+
* truth, so a re-import gives back the same rules; a value another program changed since stays the truth.
|
|
969
|
+
* @param read - the values the reader read, changed in place
|
|
970
|
+
* @param picture - each element's value of a property under the block's rules
|
|
971
|
+
* @param counts - the node and edge counts
|
|
972
|
+
* @param counts.node - the node count
|
|
973
|
+
* @param counts.edge - the edge count
|
|
974
|
+
* @param notes - a scratch note list for reading back what the writer writes
|
|
975
|
+
*/
|
|
976
|
+
export function exactFromBlock(
|
|
977
|
+
read: YedRead,
|
|
978
|
+
picture: (target: StyleTarget, property: string) => readonly (StyleValue | null)[],
|
|
979
|
+
counts: { readonly node: number; readonly edge: number },
|
|
980
|
+
notes: StyleNotes,
|
|
981
|
+
): void {
|
|
982
|
+
for (const target of ["node", "edge"] as const) {
|
|
983
|
+
const values = read[target];
|
|
984
|
+
const get = getter(picture, target);
|
|
985
|
+
for (let i = 0; i < counts[target]; i++) {
|
|
986
|
+
const want = get(i);
|
|
987
|
+
const back = writtenAndReadBack(target, want, notes);
|
|
988
|
+
if (back === null) {
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
for (const property of GRAPHML_STYLE_CAPABILITIES[target]) {
|
|
992
|
+
const native = values[property]?.[i] ?? null;
|
|
993
|
+
const w = want(property);
|
|
994
|
+
if (native !== w && native === (back.get(property) ?? null)) {
|
|
995
|
+
values[property] ??= new Array<StyleValue | null>(counts[target]).fill(null);
|
|
996
|
+
values[property][i] = w;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* An element's values written as the writer writes them and read back as the reader reads them.
|
|
1005
|
+
* @param target - nodes or edges
|
|
1006
|
+
* @param want - its values
|
|
1007
|
+
* @param notes - a scratch for the notes the round trip makes
|
|
1008
|
+
* @returns the values read back, by property, or null when the written graphics hold no realizer
|
|
1009
|
+
*/
|
|
1010
|
+
function writtenAndReadBack(target: StyleTarget, want: GetValue, notes: StyleNotes): Map<string, StyleValue> | null {
|
|
1011
|
+
const ignore: Approximate = () => undefined;
|
|
1012
|
+
const tree = target === "node" ? shapeNodeTree(want, null, ignore) : polyLineEdgeTree(want, undefined, ignore);
|
|
1013
|
+
const real = realizerOf(tree, target === "node" ? NODE_REALIZERS : EDGE_REALIZERS);
|
|
1014
|
+
if (real === null) {
|
|
1015
|
+
return null;
|
|
1016
|
+
}
|
|
1017
|
+
const back = new Map<string, StyleValue>();
|
|
1018
|
+
const at: SiteOf = (property) => ({ notes, target, property });
|
|
1019
|
+
const put: PutValue = (property, value) => {
|
|
1020
|
+
if (value !== null) {
|
|
1021
|
+
back.set(property, value);
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
if (target === "node") {
|
|
1025
|
+
readNode(real, put, at);
|
|
1026
|
+
} else {
|
|
1027
|
+
readEdge(real, put, at);
|
|
1028
|
+
}
|
|
1029
|
+
return back;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/** A GraphML export with styles worked out. */
|
|
1033
|
+
interface GraphmlStyledExport {
|
|
1034
|
+
/** The graph to write: the yFiles drawing columns replaced by yEd graphics written from the styles. */
|
|
1035
|
+
readonly snapshot: GraphSnapshot;
|
|
1036
|
+
/** W_STYLE_APPROXIMATED for every value written as the nearest one yEd has. */
|
|
1037
|
+
readonly notes: readonly LossNote[];
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/** The yFiles key type of the written node and edge graphics. */
|
|
1041
|
+
const GRAPHICS_TYPES: Readonly<Record<StyleTarget, string>> = { node: "nodegraphics", edge: "edgegraphics" };
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* A node's position from the graphics it had: a yEd realizer's `y:Geometry` x and y, or a yFiles 3 `y:RectD`'s.
|
|
1045
|
+
* @param table - the node table
|
|
1046
|
+
* @param columns - its tree columns
|
|
1047
|
+
* @param i - the node
|
|
1048
|
+
* @returns the x and y as written, or null
|
|
1049
|
+
*/
|
|
1050
|
+
function keptPosition(table: GraphSnapshot["nodes"], columns: readonly string[], i: number): [string, string] | null {
|
|
1051
|
+
for (const name of columns) {
|
|
1052
|
+
const tree = plainCell(table.value(name, i));
|
|
1053
|
+
const real = realizerOf(tree, NODE_REALIZERS);
|
|
1054
|
+
const geometry = real === null ? treeChild(tree, "RectD") : treeChild(real.r, "Geometry");
|
|
1055
|
+
const x = treeAttr(geometry, real === null ? "X" : "x");
|
|
1056
|
+
const y = treeAttr(geometry, real === null ? "Y" : "y");
|
|
1057
|
+
if (x !== undefined && y !== undefined) {
|
|
1058
|
+
return [x, y];
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* A node's `y:Geometry` x and y from the graph's position column, for a node with no yEd graphics of its own: the
|
|
1066
|
+
* column's coordinate read in its declared spelling (center, y up when it declares none), written as yEd places a
|
|
1067
|
+
* box, by its top-left corner with y growing downward.
|
|
1068
|
+
* @param snapshot - the graph
|
|
1069
|
+
* @param i - the node
|
|
1070
|
+
* @param get - the node's values (its drawn width and height)
|
|
1071
|
+
* @returns the x and y to write, or null when the node has no finite position
|
|
1072
|
+
*/
|
|
1073
|
+
function columnPosition(snapshot: GraphSnapshot, i: number, get: GetValue): [string, string] | null {
|
|
1074
|
+
const column = snapshot.nodes.byRole("position");
|
|
1075
|
+
if (column === null || column.meta.components < 2 || !column.isSet(i)) {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
const v = column.value(i) as ArrayLike<number>;
|
|
1079
|
+
const width = get("width");
|
|
1080
|
+
const height = get("height");
|
|
1081
|
+
const size: [number, number] = [typeof width === "number" ? width : 0, typeof height === "number" ? height : 0];
|
|
1082
|
+
const p: [number, number, number] = [v[0], v[1], 0];
|
|
1083
|
+
toNeutral(p, column.meta.extra ?? {}, size, false);
|
|
1084
|
+
const [x, y] = toFilePositions(Float64Array.from(p), YED_SPELLING, {
|
|
1085
|
+
width: Float64Array.of(size[0]),
|
|
1086
|
+
height: Float64Array.of(size[1]),
|
|
1087
|
+
});
|
|
1088
|
+
return Number.isFinite(x) && Number.isFinite(y) ? [String(x), String(y)] : null;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/** How yEd spells a node's place: its box's top-left corner, y growing downward. */
|
|
1092
|
+
const YED_SPELLING: PositionColumnExtra = { yAxis: "down", anchor: "top-left" };
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* An edge's `y:Path` (its bends) from the yEd graphics it had.
|
|
1096
|
+
* @param table - the edge table
|
|
1097
|
+
* @param columns - its tree columns
|
|
1098
|
+
* @param e - the edge
|
|
1099
|
+
* @returns the path, or undefined
|
|
1100
|
+
*/
|
|
1101
|
+
function keptPath(table: GraphSnapshot["edges"], columns: readonly string[], e: number): unknown {
|
|
1102
|
+
for (const name of columns) {
|
|
1103
|
+
const real = realizerOf(plainCell(table.value(name, e)), EDGE_REALIZERS);
|
|
1104
|
+
const path = real === null ? undefined : treeChild(real.r, "Path");
|
|
1105
|
+
if (path !== undefined) {
|
|
1106
|
+
return path;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return undefined;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* An edge's `y:Path` with its points placed from its `bends` value between the written node centers, so the bends
|
|
1114
|
+
* read back as written; the ports of the path it had are kept. Without a value, or without both centers, the path it
|
|
1115
|
+
* had.
|
|
1116
|
+
* @param kept - the path from the graphics the edge had, or undefined
|
|
1117
|
+
* @param bends - the edge's `bends` value
|
|
1118
|
+
* @param ends - the written centers of its source and target
|
|
1119
|
+
* @returns the path, or undefined
|
|
1120
|
+
*/
|
|
1121
|
+
function pathWithBends(kept: unknown, bends: StyleValue | null, ends: readonly [BendPoint, BendPoint]): unknown {
|
|
1122
|
+
const points = typeof bends === "string" ? bendsToPoints(ends[0], ends[1], bends, true) : null;
|
|
1123
|
+
if (points === null) {
|
|
1124
|
+
return kept;
|
|
1125
|
+
}
|
|
1126
|
+
const path: Tree = {};
|
|
1127
|
+
for (const port of ["sx", "sy", "tx", "ty"]) {
|
|
1128
|
+
path[`@_${port}`] = treeAttr(kept, port) ?? "0.0";
|
|
1129
|
+
}
|
|
1130
|
+
path["y:Point"] = points.map(([x, y]) => ({ "@_x": String(x), "@_y": String(y) }));
|
|
1131
|
+
return path;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* A column name not yet taken in a table.
|
|
1136
|
+
* @param table - the table
|
|
1137
|
+
* @param base - the preferred name
|
|
1138
|
+
* @returns the name
|
|
1139
|
+
*/
|
|
1140
|
+
function freeName(table: GraphSnapshot["nodes"], base: string): string {
|
|
1141
|
+
let name = base;
|
|
1142
|
+
let k = 2;
|
|
1143
|
+
while (table.has(name)) {
|
|
1144
|
+
name = `${base}#${String(k)}`;
|
|
1145
|
+
k++;
|
|
1146
|
+
}
|
|
1147
|
+
return name;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Works out a GraphML export with styles: every node and edge column made from yFiles keys (the graphics trees,
|
|
1152
|
+
* the typed `yfiles.*` columns and yFiles 3's drawing state) is replaced by yEd graphics written from what graphty
|
|
1153
|
+
* draws, keeping each node's position and each edge's bends from the graphics it had; graph data from yFiles 3
|
|
1154
|
+
* keys goes too, since its `y:` elements are not yEd's.
|
|
1155
|
+
* @param snapshot - the graph handed to the exporter
|
|
1156
|
+
* @param input - the styles, or undefined when none are written
|
|
1157
|
+
* @returns the styled export, or null without styles
|
|
1158
|
+
*/
|
|
1159
|
+
export function graphmlStyledExport(
|
|
1160
|
+
snapshot: GraphSnapshot,
|
|
1161
|
+
input: ExporterStyleInput | undefined,
|
|
1162
|
+
): GraphmlStyledExport | null {
|
|
1163
|
+
if (input === undefined) {
|
|
1164
|
+
return null;
|
|
1165
|
+
}
|
|
1166
|
+
const approximated = new StyleApproximations();
|
|
1167
|
+
const approximateFor =
|
|
1168
|
+
(target: StyleTarget): Approximate =>
|
|
1169
|
+
(property, value) => {
|
|
1170
|
+
approximated.add(target, property, value);
|
|
1171
|
+
};
|
|
1172
|
+
const picture = (target: StyleTarget, property: string): readonly (StyleValue | null)[] =>
|
|
1173
|
+
input.picture(target, property as never);
|
|
1174
|
+
const nodeColumns = treeColumns(snapshot.nodes);
|
|
1175
|
+
const edgeColumns = treeColumns(snapshot.edges);
|
|
1176
|
+
const getNode = getter(picture, "node");
|
|
1177
|
+
const getEdge = getter(picture, "edge");
|
|
1178
|
+
const nodeTrees: (Tree | null)[] = [];
|
|
1179
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
1180
|
+
const get = getNode(i);
|
|
1181
|
+
const at = keptPosition(snapshot.nodes, nodeColumns, i) ?? columnPosition(snapshot, i, get);
|
|
1182
|
+
nodeTrees.push(shapeNodeTree(get, at, approximateFor("node")));
|
|
1183
|
+
}
|
|
1184
|
+
const centers = nodeTrees.map((tree): BendPoint => {
|
|
1185
|
+
const geometry = treeChild(treeChild(tree, "ShapeNode"), "Geometry");
|
|
1186
|
+
const at = (k: string): number => Number(treeAttr(geometry, k) ?? Number.NaN);
|
|
1187
|
+
return [at("x") + at("width") / 2, at("y") + at("height") / 2];
|
|
1188
|
+
});
|
|
1189
|
+
const edgeTrees: (Tree | null)[] = [];
|
|
1190
|
+
for (let e = 0; e < snapshot.edgeCount; e++) {
|
|
1191
|
+
const get = getEdge(e);
|
|
1192
|
+
const ends: readonly [BendPoint, BendPoint] = [
|
|
1193
|
+
centers[snapshot.edgeSource(e)],
|
|
1194
|
+
centers[snapshot.edgeTarget(e)],
|
|
1195
|
+
];
|
|
1196
|
+
const path = pathWithBends(keptPath(snapshot.edges, edgeColumns, e), get("bends"), ends);
|
|
1197
|
+
edgeTrees.push(polyLineEdgeTree(get, path, approximateFor("edge")));
|
|
1198
|
+
}
|
|
1199
|
+
const out = snapshot.withColumns();
|
|
1200
|
+
for (const [table, trees, target] of [
|
|
1201
|
+
[out.nodes, nodeTrees, "node"],
|
|
1202
|
+
[out.edges, edgeTrees, "edge"],
|
|
1203
|
+
] as const) {
|
|
1204
|
+
for (const name of yfilesColumns(table)) {
|
|
1205
|
+
table.remove(name);
|
|
1206
|
+
}
|
|
1207
|
+
const name = freeName(table, GRAPHICS_TYPES[target]);
|
|
1208
|
+
table.set(name, trees, {
|
|
1209
|
+
dtype: "json",
|
|
1210
|
+
nullable: true,
|
|
1211
|
+
origin: { format: "graphml", id: null, title: null, type: GRAPHICS_TYPES[target], namespace: "yfiles" },
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
const yfiles3 = [...out.graph].filter(
|
|
1215
|
+
(col) => col.meta.origin?.namespace === "yfiles" && (col.meta.origin.type ?? "").includes("://"),
|
|
1216
|
+
);
|
|
1217
|
+
for (const col of yfiles3) {
|
|
1218
|
+
out.graph.remove(col.meta.name);
|
|
1219
|
+
}
|
|
1220
|
+
return { snapshot: out, notes: approximated.notes("yEd") };
|
|
1221
|
+
}
|