From 50ec5d8ef54c47f63e9473df55ce8b80a788a725 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 11 Apr 2024 14:20:03 +0800 Subject: [PATCH] refactor: adapt new d3 force and d3 force 3d layout (#5635) * feat(palettes): add tableau palette * refactor(runtime): remove presetLayout, adapt new force layout * feat(behaviors): support drag element force behaviors * feat: adapt d3 force layout * test: remove webgl dependencies --- .../__tests__/dataset/force-3d.json | 1838 +++++++++++++++++ .../g6-extension-3d/__tests__/demos/index.ts | 1 + .../__tests__/demos/layout-d3-force-3d.ts | 58 + packages/g6/__tests__/demos/index.ts | 2 + .../g6/__tests__/demos/layout-d3-force.ts | 3 + .../__tests__/demos/layout-force-collision.ts | 92 + .../__tests__/demos/layout-force-lattice.ts | 48 + .../behaviors/drag-canvas/default.svg | 308 +-- .../behaviors/hover-element/1-degree-edge.svg | 314 +-- .../behaviors/hover-element/1-degree-node.svg | 392 ++-- .../behaviors/hover-element/2-degree-edge.svg | 392 ++-- .../behaviors/hover-element/2-degree-node.svg | 518 ++--- .../hover-element/after-hover-out.svg | 308 +-- .../behaviors/hover-element/after-hover.svg | 308 +-- .../behaviors/hover-element/default.svg | 308 +-- .../behaviors/hover-element/state.svg | 308 +-- .../behaviors/zoom-canvas/default.svg | 308 +-- .../layouts/d3-force-collision/default.svg | 1011 +++++++++ .../layouts/d3-force-lattice/default.svg | 1772 ++++++++++++++++ .../snapshots/layouts/d3-force/default.svg | 100 +- .../snapshots/plugins/legend/click-again.svg | 308 +-- .../snapshots/plugins/legend/click.svg | 308 +-- .../snapshots/plugins/legend/mouseenter.svg | 308 +-- .../snapshots/plugins/legend/mouseleave.svg | 308 +-- .../snapshots/plugins/legend/normal.svg | 308 +-- .../runtime/graph/graph/after-layout.svg | 8 +- .../runtime/graph/graph/after-rotate-90.svg | 8 +- .../graph/graph/after-translate-node-1.svg | 8 +- .../runtime/graph/graph/after-translate.svg | 8 +- .../runtime/graph/graph/after-zoom-2.svg | 8 +- .../runtime/graph/graph/before-draw.svg | 308 +-- .../unit/layouts/d3-force-collision.spec.ts | 20 + .../unit/layouts/d3-force-lattice.spec.ts | 13 + packages/g6/__tests__/utils/create.ts | 10 +- packages/g6/package.json | 5 +- .../g6/src/behaviors/drag-element-force.ts | 70 + packages/g6/src/behaviors/drag-element.ts | 42 +- packages/g6/src/behaviors/index.ts | 2 + packages/g6/src/layouts/index.ts | 1 + packages/g6/src/layouts/types.ts | 12 +- packages/g6/src/palettes/index.ts | 13 + packages/g6/src/registry/build-in.ts | 7 +- packages/g6/src/runtime/layout.ts | 123 +- 43 files changed, 7756 insertions(+), 2839 deletions(-) create mode 100644 packages/g6-extension-3d/__tests__/dataset/force-3d.json create mode 100644 packages/g6-extension-3d/__tests__/demos/layout-d3-force-3d.ts create mode 100644 packages/g6/__tests__/demos/layout-force-collision.ts create mode 100644 packages/g6/__tests__/demos/layout-force-lattice.ts create mode 100644 packages/g6/__tests__/snapshots/layouts/d3-force-collision/default.svg create mode 100644 packages/g6/__tests__/snapshots/layouts/d3-force-lattice/default.svg create mode 100644 packages/g6/__tests__/unit/layouts/d3-force-collision.spec.ts create mode 100644 packages/g6/__tests__/unit/layouts/d3-force-lattice.spec.ts create mode 100644 packages/g6/src/behaviors/drag-element-force.ts diff --git a/packages/g6-extension-3d/__tests__/dataset/force-3d.json b/packages/g6-extension-3d/__tests__/dataset/force-3d.json new file mode 100644 index 0000000000..3572b89f85 --- /dev/null +++ b/packages/g6-extension-3d/__tests__/dataset/force-3d.json @@ -0,0 +1,1838 @@ +{ + "nodes": [ + { + "id": "Myriel", + "data": { "group": 1 } + }, + { + "id": "Napoleon", + "data": { "group": 1 } + }, + { + "id": "Mlle.Baptistine", + "data": { "group": 1 } + }, + { + "id": "Mme.Magloire", + "data": { "group": 1 } + }, + { + "id": "CountessdeLo", + "data": { "group": 1 } + }, + { + "id": "Geborand", + "data": { "group": 1 } + }, + { + "id": "Champtercier", + "data": { "group": 1 } + }, + { + "id": "Cravatte", + "data": { "group": 1 } + }, + { + "id": "Count", + "data": { "group": 1 } + }, + { + "id": "OldMan", + "data": { "group": 1 } + }, + { + "id": "Labarre", + "data": { "group": 2 } + }, + { + "id": "Valjean", + "data": { "group": 2 } + }, + { + "id": "Marguerite", + "data": { "group": 3 } + }, + { + "id": "Mme.deR", + "data": { "group": 2 } + }, + { + "id": "Isabeau", + "data": { "group": 2 } + }, + { + "id": "Gervais", + "data": { "group": 2 } + }, + { + "id": "Tholomyes", + "data": { "group": 3 } + }, + { + "id": "Listolier", + "data": { "group": 3 } + }, + { + "id": "Fameuil", + "data": { "group": 3 } + }, + { + "id": "Blacheville", + "data": { "group": 3 } + }, + { + "id": "Favourite", + "data": { "group": 3 } + }, + { + "id": "Dahlia", + "data": { "group": 3 } + }, + { + "id": "Zephine", + "data": { "group": 3 } + }, + { + "id": "Fantine", + "data": { "group": 3 } + }, + { + "id": "Mme.Thenardier", + "data": { "group": 4 } + }, + { + "id": "Thenardier", + "data": { "group": 4 } + }, + { + "id": "Cosette", + "data": { "group": 5 } + }, + { + "id": "Javert", + "data": { "group": 4 } + }, + { + "id": "Fauchelevent", + "data": { "group": 0 } + }, + { + "id": "Bamatabois", + "data": { "group": 2 } + }, + { + "id": "Perpetue", + "data": { "group": 3 } + }, + { + "id": "Simplice", + "data": { "group": 2 } + }, + { + "id": "Scaufflaire", + "data": { "group": 2 } + }, + { + "id": "Woman1", + "data": { "group": 2 } + }, + { + "id": "Judge", + "data": { "group": 2 } + }, + { + "id": "Champmathieu", + "data": { "group": 2 } + }, + { + "id": "Brevet", + "data": { "group": 2 } + }, + { + "id": "Chenildieu", + "data": { "group": 2 } + }, + { + "id": "Cochepaille", + "data": { "group": 2 } + }, + { + "id": "Pontmercy", + "data": { "group": 4 } + }, + { + "id": "Boulatruelle", + "data": { "group": 6 } + }, + { + "id": "Eponine", + "data": { "group": 4 } + }, + { + "id": "Anzelma", + "data": { "group": 4 } + }, + { + "id": "Woman2", + "data": { "group": 5 } + }, + { + "id": "MotherInnocent", + "data": { "group": 0 } + }, + { + "id": "Gribier", + "data": { "group": 0 } + }, + { + "id": "Jondrette", + "data": { "group": 7 } + }, + { + "id": "Mme.Burgon", + "data": { "group": 7 } + }, + { + "id": "Gavroche", + "data": { "group": 8 } + }, + { + "id": "Gillenormand", + "data": { "group": 5 } + }, + { + "id": "Magnon", + "data": { "group": 5 } + }, + { + "id": "Mlle.Gillenormand", + "data": { "group": 5 } + }, + { + "id": "Mme.Pontmercy", + "data": { "group": 5 } + }, + { + "id": "Mlle.Vaubois", + "data": { "group": 5 } + }, + { + "id": "Lt.Gillenormand", + "data": { "group": 5 } + }, + { + "id": "Marius", + "data": { "group": 8 } + }, + { + "id": "BaronessT", + "data": { "group": 5 } + }, + { + "id": "Mabeuf", + "data": { "group": 8 } + }, + { + "id": "Enjolras", + "data": { "group": 8 } + }, + { + "id": "Combeferre", + "data": { "group": 8 } + }, + { + "id": "Prouvaire", + "data": { "group": 8 } + }, + { + "id": "Feuilly", + "data": { "group": 8 } + }, + { + "id": "Courfeyrac", + "data": { "group": 8 } + }, + { + "id": "Bahorel", + "data": { "group": 8 } + }, + { + "id": "Bossuet", + "data": { "group": 8 } + }, + { + "id": "Joly", + "data": { "group": 8 } + }, + { + "id": "Grantaire", + "data": { "group": 8 } + }, + { + "id": "MotherPlutarch", + "data": { "group": 9 } + }, + { + "id": "Gueulemer", + "data": { "group": 4 } + }, + { + "id": "Babet", + "data": { "group": 4 } + }, + { + "id": "Claquesous", + "data": { "group": 4 } + }, + { + "id": "Montparnasse", + "data": { "group": 4 } + }, + { + "id": "Toussaint", + "data": { "group": 5 } + }, + { + "id": "Child1", + "data": { "group": 10 } + }, + { + "id": "Child2", + "data": { "group": 10 } + }, + { + "id": "Brujon", + "data": { "group": 4 } + }, + { + "id": "Mme.Hucheloup", + "data": { "group": 8 } + } + ], + "edges": [ + { + "id": "Napoleon-Myriel", + "source": "Napoleon", + "target": "Myriel", + "data": { "value": 1 } + }, + { + "id": "Mlle.Baptistine-Myriel", + "source": "Mlle.Baptistine", + "target": "Myriel", + "data": { "value": 8 } + }, + { + "id": "Mme.Magloire-Myriel", + "source": "Mme.Magloire", + "target": "Myriel", + "data": { "value": 10 } + }, + { + "id": "Mme.Magloire-Mlle.Baptistine", + "source": "Mme.Magloire", + "target": "Mlle.Baptistine", + "data": { "value": 6 } + }, + { + "id": "CountessdeLo-Myriel", + "source": "CountessdeLo", + "target": "Myriel", + "data": { "value": 1 } + }, + { + "id": "Geborand-Myriel", + "source": "Geborand", + "target": "Myriel", + "data": { "value": 1 } + }, + { + "id": "Champtercier-Myriel", + "source": "Champtercier", + "target": "Myriel", + "data": { "value": 1 } + }, + { + "id": "Cravatte-Myriel", + "source": "Cravatte", + "target": "Myriel", + "data": { "value": 1 } + }, + { + "id": "Count-Myriel", + "source": "Count", + "target": "Myriel", + "data": { "value": 2 } + }, + { + "id": "OldMan-Myriel", + "source": "OldMan", + "target": "Myriel", + "data": { "value": 1 } + }, + { + "id": "Valjean-Labarre", + "source": "Valjean", + "target": "Labarre", + "data": { "value": 1 } + }, + { + "id": "Valjean-Mme.Magloire", + "source": "Valjean", + "target": "Mme.Magloire", + "data": { "value": 3 } + }, + { + "id": "Valjean-Mlle.Baptistine", + "source": "Valjean", + "target": "Mlle.Baptistine", + "data": { "value": 3 } + }, + { + "id": "Valjean-Myriel", + "source": "Valjean", + "target": "Myriel", + "data": { "value": 5 } + }, + { + "id": "Marguerite-Valjean", + "source": "Marguerite", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Mme.deR-Valjean", + "source": "Mme.deR", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Isabeau-Valjean", + "source": "Isabeau", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Gervais-Valjean", + "source": "Gervais", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Listolier-Tholomyes", + "source": "Listolier", + "target": "Tholomyes", + "data": { "value": 4 } + }, + { + "id": "Fameuil-Tholomyes", + "source": "Fameuil", + "target": "Tholomyes", + "data": { "value": 4 } + }, + { + "id": "Fameuil-Listolier", + "source": "Fameuil", + "target": "Listolier", + "data": { "value": 4 } + }, + { + "id": "Blacheville-Tholomyes", + "source": "Blacheville", + "target": "Tholomyes", + "data": { "value": 4 } + }, + { + "id": "Blacheville-Listolier", + "source": "Blacheville", + "target": "Listolier", + "data": { "value": 4 } + }, + { + "id": "Blacheville-Fameuil", + "source": "Blacheville", + "target": "Fameuil", + "data": { "value": 4 } + }, + { + "id": "Favourite-Tholomyes", + "source": "Favourite", + "target": "Tholomyes", + "data": { "value": 3 } + }, + { + "id": "Favourite-Listolier", + "source": "Favourite", + "target": "Listolier", + "data": { "value": 3 } + }, + { + "id": "Favourite-Fameuil", + "source": "Favourite", + "target": "Fameuil", + "data": { "value": 3 } + }, + { + "id": "Favourite-Blacheville", + "source": "Favourite", + "target": "Blacheville", + "data": { "value": 4 } + }, + { + "id": "Dahlia-Tholomyes", + "source": "Dahlia", + "target": "Tholomyes", + "data": { "value": 3 } + }, + { + "id": "Dahlia-Listolier", + "source": "Dahlia", + "target": "Listolier", + "data": { "value": 3 } + }, + { + "id": "Dahlia-Fameuil", + "source": "Dahlia", + "target": "Fameuil", + "data": { "value": 3 } + }, + { + "id": "Dahlia-Blacheville", + "source": "Dahlia", + "target": "Blacheville", + "data": { "value": 3 } + }, + { + "id": "Dahlia-Favourite", + "source": "Dahlia", + "target": "Favourite", + "data": { "value": 5 } + }, + { + "id": "Zephine-Tholomyes", + "source": "Zephine", + "target": "Tholomyes", + "data": { "value": 3 } + }, + { + "id": "Zephine-Listolier", + "source": "Zephine", + "target": "Listolier", + "data": { "value": 3 } + }, + { + "id": "Zephine-Fameuil", + "source": "Zephine", + "target": "Fameuil", + "data": { "value": 3 } + }, + { + "id": "Zephine-Blacheville", + "source": "Zephine", + "target": "Blacheville", + "data": { "value": 3 } + }, + { + "id": "Zephine-Favourite", + "source": "Zephine", + "target": "Favourite", + "data": { "value": 4 } + }, + { + "id": "Zephine-Dahlia", + "source": "Zephine", + "target": "Dahlia", + "data": { "value": 4 } + }, + { + "id": "Fantine-Tholomyes", + "source": "Fantine", + "target": "Tholomyes", + "data": { "value": 3 } + }, + { + "id": "Fantine-Listolier", + "source": "Fantine", + "target": "Listolier", + "data": { "value": 3 } + }, + { + "id": "Fantine-Fameuil", + "source": "Fantine", + "target": "Fameuil", + "data": { "value": 3 } + }, + { + "id": "Fantine-Blacheville", + "source": "Fantine", + "target": "Blacheville", + "data": { "value": 3 } + }, + { + "id": "Fantine-Favourite", + "source": "Fantine", + "target": "Favourite", + "data": { "value": 4 } + }, + { + "id": "Fantine-Dahlia", + "source": "Fantine", + "target": "Dahlia", + "data": { "value": 4 } + }, + { + "id": "Fantine-Zephine", + "source": "Fantine", + "target": "Zephine", + "data": { "value": 4 } + }, + { + "id": "Fantine-Marguerite", + "source": "Fantine", + "target": "Marguerite", + "data": { "value": 2 } + }, + { + "id": "Fantine-Valjean", + "source": "Fantine", + "target": "Valjean", + "data": { "value": 9 } + }, + { + "id": "Mme.Thenardier-Fantine", + "source": "Mme.Thenardier", + "target": "Fantine", + "data": { "value": 2 } + }, + { + "id": "Mme.Thenardier-Valjean", + "source": "Mme.Thenardier", + "target": "Valjean", + "data": { "value": 7 } + }, + { + "id": "Thenardier-Mme.Thenardier", + "source": "Thenardier", + "target": "Mme.Thenardier", + "data": { "value": 13 } + }, + { + "id": "Thenardier-Fantine", + "source": "Thenardier", + "target": "Fantine", + "data": { "value": 1 } + }, + { + "id": "Thenardier-Valjean", + "source": "Thenardier", + "target": "Valjean", + "data": { "value": 12 } + }, + { + "id": "Cosette-Mme.Thenardier", + "source": "Cosette", + "target": "Mme.Thenardier", + "data": { "value": 4 } + }, + { + "id": "Cosette-Valjean", + "source": "Cosette", + "target": "Valjean", + "data": { "value": 31 } + }, + { + "id": "Cosette-Tholomyes", + "source": "Cosette", + "target": "Tholomyes", + "data": { "value": 1 } + }, + { + "id": "Cosette-Thenardier", + "source": "Cosette", + "target": "Thenardier", + "data": { "value": 1 } + }, + { + "id": "Javert-Valjean", + "source": "Javert", + "target": "Valjean", + "data": { "value": 17 } + }, + { + "id": "Javert-Fantine", + "source": "Javert", + "target": "Fantine", + "data": { "value": 5 } + }, + { + "id": "Javert-Thenardier", + "source": "Javert", + "target": "Thenardier", + "data": { "value": 5 } + }, + { + "id": "Javert-Mme.Thenardier", + "source": "Javert", + "target": "Mme.Thenardier", + "data": { "value": 1 } + }, + { + "id": "Javert-Cosette", + "source": "Javert", + "target": "Cosette", + "data": { "value": 1 } + }, + { + "id": "Fauchelevent-Valjean", + "source": "Fauchelevent", + "target": "Valjean", + "data": { "value": 8 } + }, + { + "id": "Fauchelevent-Javert", + "source": "Fauchelevent", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Bamatabois-Fantine", + "source": "Bamatabois", + "target": "Fantine", + "data": { "value": 1 } + }, + { + "id": "Bamatabois-Javert", + "source": "Bamatabois", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Bamatabois-Valjean", + "source": "Bamatabois", + "target": "Valjean", + "data": { "value": 2 } + }, + { + "id": "Perpetue-Fantine", + "source": "Perpetue", + "target": "Fantine", + "data": { "value": 1 } + }, + { + "id": "Simplice-Perpetue", + "source": "Simplice", + "target": "Perpetue", + "data": { "value": 2 } + }, + { + "id": "Simplice-Valjean", + "source": "Simplice", + "target": "Valjean", + "data": { "value": 3 } + }, + { + "id": "Simplice-Fantine", + "source": "Simplice", + "target": "Fantine", + "data": { "value": 2 } + }, + { + "id": "Simplice-Javert", + "source": "Simplice", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Scaufflaire-Valjean", + "source": "Scaufflaire", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Woman1-Valjean", + "source": "Woman1", + "target": "Valjean", + "data": { "value": 2 } + }, + { + "id": "Woman1-Javert", + "source": "Woman1", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Judge-Valjean", + "source": "Judge", + "target": "Valjean", + "data": { "value": 3 } + }, + { + "id": "Judge-Bamatabois", + "source": "Judge", + "target": "Bamatabois", + "data": { "value": 2 } + }, + { + "id": "Champmathieu-Valjean", + "source": "Champmathieu", + "target": "Valjean", + "data": { "value": 3 } + }, + { + "id": "Champmathieu-Judge", + "source": "Champmathieu", + "target": "Judge", + "data": { "value": 3 } + }, + { + "id": "Champmathieu-Bamatabois", + "source": "Champmathieu", + "target": "Bamatabois", + "data": { "value": 2 } + }, + { + "id": "Brevet-Judge", + "source": "Brevet", + "target": "Judge", + "data": { "value": 2 } + }, + { + "id": "Brevet-Champmathieu", + "source": "Brevet", + "target": "Champmathieu", + "data": { "value": 2 } + }, + { + "id": "Brevet-Valjean", + "source": "Brevet", + "target": "Valjean", + "data": { "value": 2 } + }, + { + "id": "Brevet-Bamatabois", + "source": "Brevet", + "target": "Bamatabois", + "data": { "value": 1 } + }, + { + "id": "Chenildieu-Judge", + "source": "Chenildieu", + "target": "Judge", + "data": { "value": 2 } + }, + { + "id": "Chenildieu-Champmathieu", + "source": "Chenildieu", + "target": "Champmathieu", + "data": { "value": 2 } + }, + { + "id": "Chenildieu-Brevet", + "source": "Chenildieu", + "target": "Brevet", + "data": { "value": 2 } + }, + { + "id": "Chenildieu-Valjean", + "source": "Chenildieu", + "target": "Valjean", + "data": { "value": 2 } + }, + { + "id": "Chenildieu-Bamatabois", + "source": "Chenildieu", + "target": "Bamatabois", + "data": { "value": 1 } + }, + { + "id": "Cochepaille-Judge", + "source": "Cochepaille", + "target": "Judge", + "data": { "value": 2 } + }, + { + "id": "Cochepaille-Champmathieu", + "source": "Cochepaille", + "target": "Champmathieu", + "data": { "value": 2 } + }, + { + "id": "Cochepaille-Brevet", + "source": "Cochepaille", + "target": "Brevet", + "data": { "value": 2 } + }, + { + "id": "Cochepaille-Chenildieu", + "source": "Cochepaille", + "target": "Chenildieu", + "data": { "value": 2 } + }, + { + "id": "Cochepaille-Valjean", + "source": "Cochepaille", + "target": "Valjean", + "data": { "value": 2 } + }, + { + "id": "Cochepaille-Bamatabois", + "source": "Cochepaille", + "target": "Bamatabois", + "data": { "value": 1 } + }, + { + "id": "Pontmercy-Thenardier", + "source": "Pontmercy", + "target": "Thenardier", + "data": { "value": 1 } + }, + { + "id": "Boulatruelle-Thenardier", + "source": "Boulatruelle", + "target": "Thenardier", + "data": { "value": 1 } + }, + { + "id": "Eponine-Mme.Thenardier", + "source": "Eponine", + "target": "Mme.Thenardier", + "data": { "value": 2 } + }, + { + "id": "Eponine-Thenardier", + "source": "Eponine", + "target": "Thenardier", + "data": { "value": 3 } + }, + { + "id": "Anzelma-Eponine", + "source": "Anzelma", + "target": "Eponine", + "data": { "value": 2 } + }, + { + "id": "Anzelma-Thenardier", + "source": "Anzelma", + "target": "Thenardier", + "data": { "value": 2 } + }, + { + "id": "Anzelma-Mme.Thenardier", + "source": "Anzelma", + "target": "Mme.Thenardier", + "data": { "value": 1 } + }, + { + "id": "Woman2-Valjean", + "source": "Woman2", + "target": "Valjean", + "data": { "value": 3 } + }, + { + "id": "Woman2-Cosette", + "source": "Woman2", + "target": "Cosette", + "data": { "value": 1 } + }, + { + "id": "Woman2-Javert", + "source": "Woman2", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "MotherInnocent-Fauchelevent", + "source": "MotherInnocent", + "target": "Fauchelevent", + "data": { "value": 3 } + }, + { + "id": "MotherInnocent-Valjean", + "source": "MotherInnocent", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Gribier-Fauchelevent", + "source": "Gribier", + "target": "Fauchelevent", + "data": { "value": 2 } + }, + { + "id": "Mme.Burgon-Jondrette", + "source": "Mme.Burgon", + "target": "Jondrette", + "data": { "value": 1 } + }, + { + "id": "Gavroche-Mme.Burgon", + "source": "Gavroche", + "target": "Mme.Burgon", + "data": { "value": 2 } + }, + { + "id": "Gavroche-Thenardier", + "source": "Gavroche", + "target": "Thenardier", + "data": { "value": 1 } + }, + { + "id": "Gavroche-Javert", + "source": "Gavroche", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Gavroche-Valjean", + "source": "Gavroche", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Gillenormand-Cosette", + "source": "Gillenormand", + "target": "Cosette", + "data": { "value": 3 } + }, + { + "id": "Gillenormand-Valjean", + "source": "Gillenormand", + "target": "Valjean", + "data": { "value": 2 } + }, + { + "id": "Magnon-Gillenormand", + "source": "Magnon", + "target": "Gillenormand", + "data": { "value": 1 } + }, + { + "id": "Magnon-Mme.Thenardier", + "source": "Magnon", + "target": "Mme.Thenardier", + "data": { "value": 1 } + }, + { + "id": "Mlle.Gillenormand-Gillenormand", + "source": "Mlle.Gillenormand", + "target": "Gillenormand", + "data": { "value": 9 } + }, + { + "id": "Mlle.Gillenormand-Cosette", + "source": "Mlle.Gillenormand", + "target": "Cosette", + "data": { "value": 2 } + }, + { + "id": "Mlle.Gillenormand-Valjean", + "source": "Mlle.Gillenormand", + "target": "Valjean", + "data": { "value": 2 } + }, + { + "id": "Mme.Pontmercy-Mlle.Gillenormand", + "source": "Mme.Pontmercy", + "target": "Mlle.Gillenormand", + "data": { "value": 1 } + }, + { + "id": "Mme.Pontmercy-Pontmercy", + "source": "Mme.Pontmercy", + "target": "Pontmercy", + "data": { "value": 1 } + }, + { + "id": "Mlle.Vaubois-Mlle.Gillenormand", + "source": "Mlle.Vaubois", + "target": "Mlle.Gillenormand", + "data": { "value": 1 } + }, + { + "id": "Lt.Gillenormand-Mlle.Gillenormand", + "source": "Lt.Gillenormand", + "target": "Mlle.Gillenormand", + "data": { "value": 2 } + }, + { + "id": "Lt.Gillenormand-Gillenormand", + "source": "Lt.Gillenormand", + "target": "Gillenormand", + "data": { "value": 1 } + }, + { + "id": "Lt.Gillenormand-Cosette", + "source": "Lt.Gillenormand", + "target": "Cosette", + "data": { "value": 1 } + }, + { + "id": "Marius-Mlle.Gillenormand", + "source": "Marius", + "target": "Mlle.Gillenormand", + "data": { "value": 6 } + }, + { + "id": "Marius-Gillenormand", + "source": "Marius", + "target": "Gillenormand", + "data": { "value": 12 } + }, + { + "id": "Marius-Pontmercy", + "source": "Marius", + "target": "Pontmercy", + "data": { "value": 1 } + }, + { + "id": "Marius-Lt.Gillenormand", + "source": "Marius", + "target": "Lt.Gillenormand", + "data": { "value": 1 } + }, + { + "id": "Marius-Cosette", + "source": "Marius", + "target": "Cosette", + "data": { "value": 21 } + }, + { + "id": "Marius-Valjean", + "source": "Marius", + "target": "Valjean", + "data": { "value": 19 } + }, + { + "id": "Marius-Tholomyes", + "source": "Marius", + "target": "Tholomyes", + "data": { "value": 1 } + }, + { + "id": "Marius-Thenardier", + "source": "Marius", + "target": "Thenardier", + "data": { "value": 2 } + }, + { + "id": "Marius-Eponine", + "source": "Marius", + "target": "Eponine", + "data": { "value": 5 } + }, + { + "id": "Marius-Gavroche", + "source": "Marius", + "target": "Gavroche", + "data": { "value": 4 } + }, + { + "id": "BaronessT-Gillenormand", + "source": "BaronessT", + "target": "Gillenormand", + "data": { "value": 1 } + }, + { + "id": "BaronessT-Marius", + "source": "BaronessT", + "target": "Marius", + "data": { "value": 1 } + }, + { + "id": "Mabeuf-Marius", + "source": "Mabeuf", + "target": "Marius", + "data": { "value": 1 } + }, + { + "id": "Mabeuf-Eponine", + "source": "Mabeuf", + "target": "Eponine", + "data": { "value": 1 } + }, + { + "id": "Mabeuf-Gavroche", + "source": "Mabeuf", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Enjolras-Marius", + "source": "Enjolras", + "target": "Marius", + "data": { "value": 7 } + }, + { + "id": "Enjolras-Gavroche", + "source": "Enjolras", + "target": "Gavroche", + "data": { "value": 7 } + }, + { + "id": "Enjolras-Javert", + "source": "Enjolras", + "target": "Javert", + "data": { "value": 6 } + }, + { + "id": "Enjolras-Mabeuf", + "source": "Enjolras", + "target": "Mabeuf", + "data": { "value": 1 } + }, + { + "id": "Enjolras-Valjean", + "source": "Enjolras", + "target": "Valjean", + "data": { "value": 4 } + }, + { + "id": "Combeferre-Enjolras", + "source": "Combeferre", + "target": "Enjolras", + "data": { "value": 15 } + }, + { + "id": "Combeferre-Marius", + "source": "Combeferre", + "target": "Marius", + "data": { "value": 5 } + }, + { + "id": "Combeferre-Gavroche", + "source": "Combeferre", + "target": "Gavroche", + "data": { "value": 6 } + }, + { + "id": "Combeferre-Mabeuf", + "source": "Combeferre", + "target": "Mabeuf", + "data": { "value": 2 } + }, + { + "id": "Prouvaire-Gavroche", + "source": "Prouvaire", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Prouvaire-Enjolras", + "source": "Prouvaire", + "target": "Enjolras", + "data": { "value": 4 } + }, + { + "id": "Prouvaire-Combeferre", + "source": "Prouvaire", + "target": "Combeferre", + "data": { "value": 2 } + }, + { + "id": "Feuilly-Gavroche", + "source": "Feuilly", + "target": "Gavroche", + "data": { "value": 2 } + }, + { + "id": "Feuilly-Enjolras", + "source": "Feuilly", + "target": "Enjolras", + "data": { "value": 6 } + }, + { + "id": "Feuilly-Prouvaire", + "source": "Feuilly", + "target": "Prouvaire", + "data": { "value": 2 } + }, + { + "id": "Feuilly-Combeferre", + "source": "Feuilly", + "target": "Combeferre", + "data": { "value": 5 } + }, + { + "id": "Feuilly-Mabeuf", + "source": "Feuilly", + "target": "Mabeuf", + "data": { "value": 1 } + }, + { + "id": "Feuilly-Marius", + "source": "Feuilly", + "target": "Marius", + "data": { "value": 1 } + }, + { + "id": "Courfeyrac-Marius", + "source": "Courfeyrac", + "target": "Marius", + "data": { "value": 9 } + }, + { + "id": "Courfeyrac-Enjolras", + "source": "Courfeyrac", + "target": "Enjolras", + "data": { "value": 17 } + }, + { + "id": "Courfeyrac-Combeferre", + "source": "Courfeyrac", + "target": "Combeferre", + "data": { "value": 13 } + }, + { + "id": "Courfeyrac-Gavroche", + "source": "Courfeyrac", + "target": "Gavroche", + "data": { "value": 7 } + }, + { + "id": "Courfeyrac-Mabeuf", + "source": "Courfeyrac", + "target": "Mabeuf", + "data": { "value": 2 } + }, + { + "id": "Courfeyrac-Eponine", + "source": "Courfeyrac", + "target": "Eponine", + "data": { "value": 1 } + }, + { + "id": "Courfeyrac-Feuilly", + "source": "Courfeyrac", + "target": "Feuilly", + "data": { "value": 6 } + }, + { + "id": "Courfeyrac-Prouvaire", + "source": "Courfeyrac", + "target": "Prouvaire", + "data": { "value": 3 } + }, + { + "id": "Bahorel-Combeferre", + "source": "Bahorel", + "target": "Combeferre", + "data": { "value": 5 } + }, + { + "id": "Bahorel-Gavroche", + "source": "Bahorel", + "target": "Gavroche", + "data": { "value": 5 } + }, + { + "id": "Bahorel-Courfeyrac", + "source": "Bahorel", + "target": "Courfeyrac", + "data": { "value": 6 } + }, + { + "id": "Bahorel-Mabeuf", + "source": "Bahorel", + "target": "Mabeuf", + "data": { "value": 2 } + }, + { + "id": "Bahorel-Enjolras", + "source": "Bahorel", + "target": "Enjolras", + "data": { "value": 4 } + }, + { + "id": "Bahorel-Feuilly", + "source": "Bahorel", + "target": "Feuilly", + "data": { "value": 3 } + }, + { + "id": "Bahorel-Prouvaire", + "source": "Bahorel", + "target": "Prouvaire", + "data": { "value": 2 } + }, + { + "id": "Bahorel-Marius", + "source": "Bahorel", + "target": "Marius", + "data": { "value": 1 } + }, + { + "id": "Bossuet-Marius", + "source": "Bossuet", + "target": "Marius", + "data": { "value": 5 } + }, + { + "id": "Bossuet-Courfeyrac", + "source": "Bossuet", + "target": "Courfeyrac", + "data": { "value": 12 } + }, + { + "id": "Bossuet-Gavroche", + "source": "Bossuet", + "target": "Gavroche", + "data": { "value": 5 } + }, + { + "id": "Bossuet-Bahorel", + "source": "Bossuet", + "target": "Bahorel", + "data": { "value": 4 } + }, + { + "id": "Bossuet-Enjolras", + "source": "Bossuet", + "target": "Enjolras", + "data": { "value": 10 } + }, + { + "id": "Bossuet-Feuilly", + "source": "Bossuet", + "target": "Feuilly", + "data": { "value": 6 } + }, + { + "id": "Bossuet-Prouvaire", + "source": "Bossuet", + "target": "Prouvaire", + "data": { "value": 2 } + }, + { + "id": "Bossuet-Combeferre", + "source": "Bossuet", + "target": "Combeferre", + "data": { "value": 9 } + }, + { + "id": "Bossuet-Mabeuf", + "source": "Bossuet", + "target": "Mabeuf", + "data": { "value": 1 } + }, + { + "id": "Bossuet-Valjean", + "source": "Bossuet", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Joly-Bahorel", + "source": "Joly", + "target": "Bahorel", + "data": { "value": 5 } + }, + { + "id": "Joly-Bossuet", + "source": "Joly", + "target": "Bossuet", + "data": { "value": 7 } + }, + { + "id": "Joly-Gavroche", + "source": "Joly", + "target": "Gavroche", + "data": { "value": 3 } + }, + { + "id": "Joly-Courfeyrac", + "source": "Joly", + "target": "Courfeyrac", + "data": { "value": 5 } + }, + { + "id": "Joly-Enjolras", + "source": "Joly", + "target": "Enjolras", + "data": { "value": 5 } + }, + { + "id": "Joly-Feuilly", + "source": "Joly", + "target": "Feuilly", + "data": { "value": 5 } + }, + { + "id": "Joly-Prouvaire", + "source": "Joly", + "target": "Prouvaire", + "data": { "value": 2 } + }, + { + "id": "Joly-Combeferre", + "source": "Joly", + "target": "Combeferre", + "data": { "value": 5 } + }, + { + "id": "Joly-Mabeuf", + "source": "Joly", + "target": "Mabeuf", + "data": { "value": 1 } + }, + { + "id": "Joly-Marius", + "source": "Joly", + "target": "Marius", + "data": { "value": 2 } + }, + { + "id": "Grantaire-Bossuet", + "source": "Grantaire", + "target": "Bossuet", + "data": { "value": 3 } + }, + { + "id": "Grantaire-Enjolras", + "source": "Grantaire", + "target": "Enjolras", + "data": { "value": 3 } + }, + { + "id": "Grantaire-Combeferre", + "source": "Grantaire", + "target": "Combeferre", + "data": { "value": 1 } + }, + { + "id": "Grantaire-Courfeyrac", + "source": "Grantaire", + "target": "Courfeyrac", + "data": { "value": 2 } + }, + { + "id": "Grantaire-Joly", + "source": "Grantaire", + "target": "Joly", + "data": { "value": 2 } + }, + { + "id": "Grantaire-Gavroche", + "source": "Grantaire", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Grantaire-Bahorel", + "source": "Grantaire", + "target": "Bahorel", + "data": { "value": 1 } + }, + { + "id": "Grantaire-Feuilly", + "source": "Grantaire", + "target": "Feuilly", + "data": { "value": 1 } + }, + { + "id": "Grantaire-Prouvaire", + "source": "Grantaire", + "target": "Prouvaire", + "data": { "value": 1 } + }, + { + "id": "MotherPlutarch-Mabeuf", + "source": "MotherPlutarch", + "target": "Mabeuf", + "data": { "value": 3 } + }, + { + "id": "Gueulemer-Thenardier", + "source": "Gueulemer", + "target": "Thenardier", + "data": { "value": 5 } + }, + { + "id": "Gueulemer-Valjean", + "source": "Gueulemer", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Gueulemer-Mme.Thenardier", + "source": "Gueulemer", + "target": "Mme.Thenardier", + "data": { "value": 1 } + }, + { + "id": "Gueulemer-Javert", + "source": "Gueulemer", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Gueulemer-Gavroche", + "source": "Gueulemer", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Gueulemer-Eponine", + "source": "Gueulemer", + "target": "Eponine", + "data": { "value": 1 } + }, + { + "id": "Babet-Thenardier", + "source": "Babet", + "target": "Thenardier", + "data": { "value": 6 } + }, + { + "id": "Babet-Gueulemer", + "source": "Babet", + "target": "Gueulemer", + "data": { "value": 6 } + }, + { + "id": "Babet-Valjean", + "source": "Babet", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Babet-Mme.Thenardier", + "source": "Babet", + "target": "Mme.Thenardier", + "data": { "value": 1 } + }, + { + "id": "Babet-Javert", + "source": "Babet", + "target": "Javert", + "data": { "value": 2 } + }, + { + "id": "Babet-Gavroche", + "source": "Babet", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Babet-Eponine", + "source": "Babet", + "target": "Eponine", + "data": { "value": 1 } + }, + { + "id": "Claquesous-Thenardier", + "source": "Claquesous", + "target": "Thenardier", + "data": { "value": 4 } + }, + { + "id": "Claquesous-Babet", + "source": "Claquesous", + "target": "Babet", + "data": { "value": 4 } + }, + { + "id": "Claquesous-Gueulemer", + "source": "Claquesous", + "target": "Gueulemer", + "data": { "value": 4 } + }, + { + "id": "Claquesous-Valjean", + "source": "Claquesous", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Claquesous-Mme.Thenardier", + "source": "Claquesous", + "target": "Mme.Thenardier", + "data": { "value": 1 } + }, + { + "id": "Claquesous-Javert", + "source": "Claquesous", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Claquesous-Eponine", + "source": "Claquesous", + "target": "Eponine", + "data": { "value": 1 } + }, + { + "id": "Claquesous-Enjolras", + "source": "Claquesous", + "target": "Enjolras", + "data": { "value": 1 } + }, + { + "id": "Montparnasse-Javert", + "source": "Montparnasse", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Montparnasse-Babet", + "source": "Montparnasse", + "target": "Babet", + "data": { "value": 2 } + }, + { + "id": "Montparnasse-Gueulemer", + "source": "Montparnasse", + "target": "Gueulemer", + "data": { "value": 2 } + }, + { + "id": "Montparnasse-Claquesous", + "source": "Montparnasse", + "target": "Claquesous", + "data": { "value": 2 } + }, + { + "id": "Montparnasse-Valjean", + "source": "Montparnasse", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Montparnasse-Gavroche", + "source": "Montparnasse", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Montparnasse-Eponine", + "source": "Montparnasse", + "target": "Eponine", + "data": { "value": 1 } + }, + { + "id": "Montparnasse-Thenardier", + "source": "Montparnasse", + "target": "Thenardier", + "data": { "value": 1 } + }, + { + "id": "Toussaint-Cosette", + "source": "Toussaint", + "target": "Cosette", + "data": { "value": 2 } + }, + { + "id": "Toussaint-Javert", + "source": "Toussaint", + "target": "Javert", + "data": { "value": 1 } + }, + { + "id": "Toussaint-Valjean", + "source": "Toussaint", + "target": "Valjean", + "data": { "value": 1 } + }, + { + "id": "Child1-Gavroche", + "source": "Child1", + "target": "Gavroche", + "data": { "value": 2 } + }, + { + "id": "Child2-Gavroche", + "source": "Child2", + "target": "Gavroche", + "data": { "value": 2 } + }, + { + "id": "Child2-Child1", + "source": "Child2", + "target": "Child1", + "data": { "value": 3 } + }, + { + "id": "Brujon-Babet", + "source": "Brujon", + "target": "Babet", + "data": { "value": 3 } + }, + { + "id": "Brujon-Gueulemer", + "source": "Brujon", + "target": "Gueulemer", + "data": { "value": 3 } + }, + { + "id": "Brujon-Thenardier", + "source": "Brujon", + "target": "Thenardier", + "data": { "value": 3 } + }, + { + "id": "Brujon-Gavroche", + "source": "Brujon", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Brujon-Eponine", + "source": "Brujon", + "target": "Eponine", + "data": { "value": 1 } + }, + { + "id": "Brujon-Claquesous", + "source": "Brujon", + "target": "Claquesous", + "data": { "value": 1 } + }, + { + "id": "Brujon-Montparnasse", + "source": "Brujon", + "target": "Montparnasse", + "data": { "value": 1 } + }, + { + "id": "Mme.Hucheloup-Bossuet", + "source": "Mme.Hucheloup", + "target": "Bossuet", + "data": { "value": 1 } + }, + { + "id": "Mme.Hucheloup-Joly", + "source": "Mme.Hucheloup", + "target": "Joly", + "data": { "value": 1 } + }, + { + "id": "Mme.Hucheloup-Grantaire", + "source": "Mme.Hucheloup", + "target": "Grantaire", + "data": { "value": 1 } + }, + { + "id": "Mme.Hucheloup-Bahorel", + "source": "Mme.Hucheloup", + "target": "Bahorel", + "data": { "value": 1 } + }, + { + "id": "Mme.Hucheloup-Courfeyrac", + "source": "Mme.Hucheloup", + "target": "Courfeyrac", + "data": { "value": 1 } + }, + { + "id": "Mme.Hucheloup-Gavroche", + "source": "Mme.Hucheloup", + "target": "Gavroche", + "data": { "value": 1 } + }, + { + "id": "Mme.Hucheloup-Enjolras", + "source": "Mme.Hucheloup", + "target": "Enjolras", + "data": { "value": 1 } + } + ] +} diff --git a/packages/g6-extension-3d/__tests__/demos/index.ts b/packages/g6-extension-3d/__tests__/demos/index.ts index da8ae0afbd..9c1c971ad0 100644 --- a/packages/g6-extension-3d/__tests__/demos/index.ts +++ b/packages/g6-extension-3d/__tests__/demos/index.ts @@ -3,6 +3,7 @@ export * from './behavior-observe-canvas'; export * from './behavior-roll-canvas'; export * from './behavior-zoom-canvas'; export * from './layer-top'; +export * from './layout-d3-force-3d'; export * from './position'; export * from './shapes'; export * from './solar-system'; diff --git a/packages/g6-extension-3d/__tests__/demos/layout-d3-force-3d.ts b/packages/g6-extension-3d/__tests__/demos/layout-d3-force-3d.ts new file mode 100644 index 0000000000..932b028d7d --- /dev/null +++ b/packages/g6-extension-3d/__tests__/demos/layout-d3-force-3d.ts @@ -0,0 +1,58 @@ +import { CameraSetting, ExtensionCategory, Graph, register } from '@antv/g6'; +import { Light, Line3D, ObserveCanvas3D, Sphere, ZoomCanvas3D, renderer } from '../../src'; +import data from '../dataset/force-3d.json'; + +export const layoutD3Force3D: TestCase = async (context) => { + register(ExtensionCategory.PLUGIN, '3d-light', Light); + register(ExtensionCategory.NODE, 'sphere', Sphere); + register(ExtensionCategory.EDGE, 'line3d', Line3D); + register(ExtensionCategory.PLUGIN, 'camera-setting', CameraSetting); + register(ExtensionCategory.BEHAVIOR, 'zoom-canvas-3d', ZoomCanvas3D); + register(ExtensionCategory.BEHAVIOR, 'observe-canvas-3d', ObserveCanvas3D); + + const graph = new Graph({ + ...context, + animation: true, + renderer, + data, + layout: { + type: 'd3-force-3d', + }, + node: { + style: { + type: 'sphere', + materialType: 'phong', + }, + palette: { + color: 'tableau', + type: 'group', + field: 'group', + }, + }, + edge: { + style: { + type: 'line3d', + }, + }, + behaviors: ['observe-canvas-3d', 'zoom-canvas-3d'], + plugins: [ + { + type: 'camera-setting', + projectionMode: 'perspective', + near: 0.1, + far: 1000, + fov: 45, + aspect: 1, + }, + { + type: '3d-light', + directional: { + direction: [0, 0, 1], + }, + }, + ], + }); + + await graph.render(); + return graph; +}; diff --git a/packages/g6/__tests__/demos/index.ts b/packages/g6/__tests__/demos/index.ts index 8c1a7c9429..2149210069 100644 --- a/packages/g6/__tests__/demos/index.ts +++ b/packages/g6/__tests__/demos/index.ts @@ -61,6 +61,8 @@ export * from './layout-dagre'; export * from './layout-dendrogram-basic'; export * from './layout-dendrogram-tb'; export * from './layout-force'; +export * from './layout-force-collision'; +export * from './layout-force-lattice'; export * from './layout-forceatlas2-wasm'; export * from './layout-fruchterman-basic'; export * from './layout-fruchterman-cluster'; diff --git a/packages/g6/__tests__/demos/layout-d3-force.ts b/packages/g6/__tests__/demos/layout-d3-force.ts index a31c5555bb..6234df7739 100644 --- a/packages/g6/__tests__/demos/layout-d3-force.ts +++ b/packages/g6/__tests__/demos/layout-d3-force.ts @@ -10,6 +10,9 @@ export const layoutD3Force: TestCase = async (context) => { behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element', 'click-select'], layout: { type: 'd3force', + collide: { + strength: 0.5, + }, }, node: { style: { diff --git a/packages/g6/__tests__/demos/layout-force-collision.ts b/packages/g6/__tests__/demos/layout-force-collision.ts new file mode 100644 index 0000000000..b7ece04cfe --- /dev/null +++ b/packages/g6/__tests__/demos/layout-force-collision.ts @@ -0,0 +1,92 @@ +// ref: https://observablehq.com/@d3/collision-detection +import type { IPointerEvent, RuntimeContext } from '@/src'; +import { BaseBehavior, ExtensionCategory, Graph, register } from '@/src'; +import type { D3Force3DLayout, D3ForceLayout } from '@antv/layout'; + +export const layoutForceCollision: TestCase = async (context) => { + const width = 500; + + class CollisionElement extends BaseBehavior<{}> { + constructor(context: RuntimeContext) { + super(context, {}); + this.onPointerMove = this.onPointerMove.bind(this); + this.bindEvents(); + } + + bindEvents() { + this.context.graph.on('pointermove', this.onPointerMove); + } + + onPointerMove(event: IPointerEvent) { + const pos = this.context.graph.getCanvasByClient([event.client.x, event.client.y]); + ( + this.context.layout?.getLayoutInstance().find((layout) => ['d3-force', 'd3-force-3d'].includes(layout?.id)) as + | D3Force3DLayout + | D3ForceLayout + ).setFixedPosition(0, [...pos]); + } + } + + register(ExtensionCategory.BEHAVIOR, 'collision-element', CollisionElement); + + const graph = new Graph({ + ...context, + data: getData(500), + layout: { + type: 'd3force', + alphaTarget: 0.3, + velocityDecay: 0.1, + x: { + strength: 0.01, + }, + y: { + strength: 0.01, + }, + collide: { + radius: (d) => { + return d.data.size / 2; + }, + iterations: 3, + }, + manyBody: { + strength: (d, i) => (i ? 0 : (-width * 2) / 3), + }, + link: false, + }, + node: { + style: { + size: (d, i) => (i === 0 ? 0 : (d.data!.r as number) * 4), + }, + palette: { + color: 'tableau', + type: 'group', + field: (d) => d.data!.group as string, + }, + }, + behaviors: ['collision-element'], + }); + + await graph.render(); + return graph; +}; + +function getData(width: number, size = 200) { + const k = width / 200; + const r = randomUniform(k, k * 4); + const n = 4; + return { + nodes: Array.from({ length: size }, (_, i) => ({ id: i, data: { r: r(), group: i && (i % n) + 1 } })), + edges: [], + }; +} + +// d3-random +function randomUniform(min: number, max: number) { + min = min == null ? 0 : +min; + max = max == null ? 1 : +max; + if (arguments.length === 1) (max = min), (min = 0); + else max -= min; + return function () { + return Math.random() * max + min; + }; +} diff --git a/packages/g6/__tests__/demos/layout-force-lattice.ts b/packages/g6/__tests__/demos/layout-force-lattice.ts new file mode 100644 index 0000000000..df184dcfb0 --- /dev/null +++ b/packages/g6/__tests__/demos/layout-force-lattice.ts @@ -0,0 +1,48 @@ +// ref: https://observablehq.com/@d3/force-directed-lattice +import { Graph } from '@/src'; + +export const layoutForceLattice: TestCase = async (context) => { + const graph = new Graph({ + ...context, + data: getData(), + layout: { + type: 'd3force', + manyBody: { + strength: -30, + }, + link: { + strength: 1, + distance: 20, + iterations: 10, + }, + }, + node: { + style: { + size: 10, + color: '#000', + }, + }, + edge: { + style: { + color: '#000', + }, + }, + behaviors: [{ type: 'drag-element-force' }, 'zoom-canvas'], + }); + + await graph.render(); + + return graph; +}; + +function getData(size = 10) { + const nodes = Array.from({ length: size * size }, (_, i) => ({ id: `${i}` })); + const edges = []; + for (let y = 0; y < size; ++y) { + for (let x = 0; x < size; ++x) { + if (y > 0) edges.push({ source: `${(y - 1) * size + x}`, target: `${y * size + x}` }); + if (x > 0) edges.push({ source: `${y * size + (x - 1)}`, target: `${y * size + x}` }); + } + } + return { nodes, edges }; +} diff --git a/packages/g6/__tests__/snapshots/behaviors/drag-canvas/default.svg b/packages/g6/__tests__/snapshots/behaviors/drag-canvas/default.svg index 208ce5e0d2..ccaf42df8c 100644 --- a/packages/g6/__tests__/snapshots/behaviors/drag-canvas/default.svg +++ b/packages/g6/__tests__/snapshots/behaviors/drag-canvas/default.svg @@ -6,592 +6,592 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-edge.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-edge.svg index 038bff39bd..c05bc98cb5 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-edge.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-edge.svg @@ -6,431 +6,431 @@ - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -438,7 +438,7 @@ - + @@ -446,162 +446,162 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-node.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-node.svg index c00b10f9ce..6f82343793 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-node.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/1-degree-node.svg @@ -6,483 +6,483 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -490,7 +490,7 @@ - + @@ -498,7 +498,7 @@ - + @@ -506,7 +506,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -522,7 +522,7 @@ - + @@ -530,12 +530,12 @@ - + - + @@ -543,7 +543,7 @@ - + @@ -551,7 +551,7 @@ - + @@ -559,7 +559,7 @@ - + @@ -567,7 +567,7 @@ - + @@ -575,12 +575,12 @@ - + - + @@ -588,7 +588,7 @@ - + @@ -596,7 +596,7 @@ - + @@ -604,7 +604,7 @@ - + @@ -612,87 +612,87 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-edge.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-edge.svg index c00b10f9ce..6f82343793 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-edge.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-edge.svg @@ -6,483 +6,483 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -490,7 +490,7 @@ - + @@ -498,7 +498,7 @@ - + @@ -506,7 +506,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -522,7 +522,7 @@ - + @@ -530,12 +530,12 @@ - + - + @@ -543,7 +543,7 @@ - + @@ -551,7 +551,7 @@ - + @@ -559,7 +559,7 @@ - + @@ -567,7 +567,7 @@ - + @@ -575,12 +575,12 @@ - + - + @@ -588,7 +588,7 @@ - + @@ -596,7 +596,7 @@ - + @@ -604,7 +604,7 @@ - + @@ -612,87 +612,87 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-node.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-node.svg index 373aa0d27c..81e084e75c 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-node.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/2-degree-node.svg @@ -6,567 +6,567 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -574,7 +574,7 @@ - + @@ -582,7 +582,7 @@ - + @@ -590,7 +590,7 @@ - + @@ -598,7 +598,7 @@ - + @@ -606,7 +606,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -622,7 +622,7 @@ - + @@ -630,7 +630,7 @@ - + @@ -638,7 +638,7 @@ - + @@ -646,7 +646,7 @@ - + @@ -654,7 +654,7 @@ - + @@ -662,7 +662,7 @@ - + @@ -670,7 +670,7 @@ - + @@ -678,7 +678,7 @@ - + @@ -686,7 +686,7 @@ - + @@ -694,7 +694,7 @@ - + @@ -702,7 +702,7 @@ - + @@ -710,7 +710,7 @@ - + @@ -718,12 +718,12 @@ - + - + @@ -731,7 +731,7 @@ - + @@ -739,7 +739,7 @@ - + @@ -747,12 +747,12 @@ - + - + @@ -760,47 +760,47 @@ - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover-out.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover-out.svg index 226c621fd4..c3abf0ceb4 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover-out.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover-out.svg @@ -6,592 +6,592 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover.svg index 712d14110b..862ca0d883 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/after-hover.svg @@ -6,427 +6,427 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -434,167 +434,167 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/default.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/default.svg index 226c621fd4..c3abf0ceb4 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/default.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/default.svg @@ -6,592 +6,592 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/hover-element/state.svg b/packages/g6/__tests__/snapshots/behaviors/hover-element/state.svg index 644daa2bc8..e8bbc75a1c 100644 --- a/packages/g6/__tests__/snapshots/behaviors/hover-element/state.svg +++ b/packages/g6/__tests__/snapshots/behaviors/hover-element/state.svg @@ -6,427 +6,427 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -434,167 +434,167 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/behaviors/zoom-canvas/default.svg b/packages/g6/__tests__/snapshots/behaviors/zoom-canvas/default.svg index ab4f4a4185..c2afed046b 100644 --- a/packages/g6/__tests__/snapshots/behaviors/zoom-canvas/default.svg +++ b/packages/g6/__tests__/snapshots/behaviors/zoom-canvas/default.svg @@ -6,592 +6,592 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/snapshots/layouts/d3-force-collision/default.svg b/packages/g6/__tests__/snapshots/layouts/d3-force-collision/default.svg new file mode 100644 index 0000000000..54a10421e7 --- /dev/null +++ b/packages/g6/__tests__/snapshots/layouts/d3-force-collision/default.svg @@ -0,0 +1,1011 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/layouts/d3-force-lattice/default.svg b/packages/g6/__tests__/snapshots/layouts/d3-force-lattice/default.svg new file mode 100644 index 0000000000..3a3f5fc903 --- /dev/null +++ b/packages/g6/__tests__/snapshots/layouts/d3-force-lattice/default.svg @@ -0,0 +1,1772 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/g6/__tests__/snapshots/layouts/d3-force/default.svg b/packages/g6/__tests__/snapshots/layouts/d3-force/default.svg index 87d6bad93d..800916ad1f 100644 --- a/packages/g6/__tests__/snapshots/layouts/d3-force/default.svg +++ b/packages/g6/__tests__/snapshots/layouts/d3-force/default.svg @@ -1,124 +1,124 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -130,7 +130,7 @@ - + @@ -142,7 +142,7 @@ - + @@ -154,7 +154,7 @@ - + @@ -166,7 +166,7 @@ - + @@ -178,7 +178,7 @@ - + @@ -190,7 +190,7 @@ - + @@ -202,7 +202,7 @@ - + @@ -214,7 +214,7 @@ - + @@ -226,7 +226,7 @@ - + @@ -238,7 +238,7 @@ - + @@ -250,7 +250,7 @@ - + @@ -262,7 +262,7 @@ - + @@ -274,7 +274,7 @@ - + @@ -286,7 +286,7 @@ - + @@ -298,7 +298,7 @@ - + @@ -310,7 +310,7 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/legend/click-again.svg b/packages/g6/__tests__/snapshots/plugins/legend/click-again.svg index 9fc8f04ab5..74dfead886 100644 --- a/packages/g6/__tests__/snapshots/plugins/legend/click-again.svg +++ b/packages/g6/__tests__/snapshots/plugins/legend/click-again.svg @@ -303,427 +303,427 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -735,7 +735,7 @@ - + @@ -747,7 +747,7 @@ - + @@ -759,7 +759,7 @@ - + @@ -771,7 +771,7 @@ - + @@ -783,7 +783,7 @@ - + @@ -795,7 +795,7 @@ - + @@ -807,7 +807,7 @@ - + @@ -819,7 +819,7 @@ - + @@ -831,7 +831,7 @@ - + @@ -843,7 +843,7 @@ - + @@ -855,7 +855,7 @@ - + @@ -867,7 +867,7 @@ - + @@ -879,7 +879,7 @@ - + @@ -891,7 +891,7 @@ - + @@ -903,7 +903,7 @@ - + @@ -915,7 +915,7 @@ - + @@ -927,7 +927,7 @@ - + @@ -939,7 +939,7 @@ - + @@ -951,7 +951,7 @@ - + @@ -963,7 +963,7 @@ - + @@ -975,7 +975,7 @@ - + @@ -987,7 +987,7 @@ - + @@ -999,7 +999,7 @@ - + @@ -1011,7 +1011,7 @@ - + @@ -1023,7 +1023,7 @@ - + @@ -1035,7 +1035,7 @@ - + @@ -1047,7 +1047,7 @@ - + @@ -1059,7 +1059,7 @@ - + @@ -1071,7 +1071,7 @@ - + @@ -1083,7 +1083,7 @@ - + @@ -1095,7 +1095,7 @@ - + @@ -1107,7 +1107,7 @@ - + @@ -1119,7 +1119,7 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/legend/click.svg b/packages/g6/__tests__/snapshots/plugins/legend/click.svg index 90f233d8a8..79a6158c49 100644 --- a/packages/g6/__tests__/snapshots/plugins/legend/click.svg +++ b/packages/g6/__tests__/snapshots/plugins/legend/click.svg @@ -303,427 +303,427 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -738,7 +738,7 @@ - + @@ -753,7 +753,7 @@ - + @@ -768,7 +768,7 @@ - + @@ -783,7 +783,7 @@ - + @@ -798,7 +798,7 @@ - + @@ -813,7 +813,7 @@ - + @@ -828,7 +828,7 @@ - + @@ -843,7 +843,7 @@ - + @@ -858,7 +858,7 @@ - + @@ -873,7 +873,7 @@ - + @@ -888,7 +888,7 @@ - + @@ -903,7 +903,7 @@ - + @@ -918,7 +918,7 @@ - + @@ -930,7 +930,7 @@ - + @@ -942,7 +942,7 @@ - + @@ -954,7 +954,7 @@ - + @@ -966,7 +966,7 @@ - + @@ -978,7 +978,7 @@ - + @@ -990,7 +990,7 @@ - + @@ -1002,7 +1002,7 @@ - + @@ -1014,7 +1014,7 @@ - + @@ -1026,7 +1026,7 @@ - + @@ -1038,7 +1038,7 @@ - + @@ -1050,7 +1050,7 @@ - + @@ -1062,7 +1062,7 @@ - + @@ -1074,7 +1074,7 @@ - + @@ -1086,7 +1086,7 @@ - + @@ -1098,7 +1098,7 @@ - + @@ -1110,7 +1110,7 @@ - + @@ -1122,7 +1122,7 @@ - + @@ -1134,7 +1134,7 @@ - + @@ -1146,7 +1146,7 @@ - + @@ -1158,7 +1158,7 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg b/packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg index 911447626d..ca3de15568 100644 --- a/packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg +++ b/packages/g6/__tests__/snapshots/plugins/legend/mouseenter.svg @@ -303,427 +303,427 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -738,7 +738,7 @@ - + @@ -753,7 +753,7 @@ - + @@ -768,7 +768,7 @@ - + @@ -783,7 +783,7 @@ - + @@ -798,7 +798,7 @@ - + @@ -813,7 +813,7 @@ - + @@ -828,7 +828,7 @@ - + @@ -843,7 +843,7 @@ - + @@ -858,7 +858,7 @@ - + @@ -873,7 +873,7 @@ - + @@ -888,7 +888,7 @@ - + @@ -903,7 +903,7 @@ - + @@ -918,7 +918,7 @@ - + @@ -930,7 +930,7 @@ - + @@ -942,7 +942,7 @@ - + @@ -954,7 +954,7 @@ - + @@ -966,7 +966,7 @@ - + @@ -978,7 +978,7 @@ - + @@ -990,7 +990,7 @@ - + @@ -1002,7 +1002,7 @@ - + @@ -1014,7 +1014,7 @@ - + @@ -1026,7 +1026,7 @@ - + @@ -1038,7 +1038,7 @@ - + @@ -1050,7 +1050,7 @@ - + @@ -1062,7 +1062,7 @@ - + @@ -1074,7 +1074,7 @@ - + @@ -1086,7 +1086,7 @@ - + @@ -1098,7 +1098,7 @@ - + @@ -1110,7 +1110,7 @@ - + @@ -1122,7 +1122,7 @@ - + @@ -1134,7 +1134,7 @@ - + @@ -1146,7 +1146,7 @@ - + @@ -1158,7 +1158,7 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg b/packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg index 144ac3489b..47bd6cf03a 100644 --- a/packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg +++ b/packages/g6/__tests__/snapshots/plugins/legend/mouseleave.svg @@ -303,427 +303,427 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -735,7 +735,7 @@ - + @@ -747,7 +747,7 @@ - + @@ -759,7 +759,7 @@ - + @@ -771,7 +771,7 @@ - + @@ -783,7 +783,7 @@ - + @@ -795,7 +795,7 @@ - + @@ -807,7 +807,7 @@ - + @@ -819,7 +819,7 @@ - + @@ -831,7 +831,7 @@ - + @@ -843,7 +843,7 @@ - + @@ -855,7 +855,7 @@ - + @@ -867,7 +867,7 @@ - + @@ -879,7 +879,7 @@ - + @@ -891,7 +891,7 @@ - + @@ -903,7 +903,7 @@ - + @@ -915,7 +915,7 @@ - + @@ -927,7 +927,7 @@ - + @@ -939,7 +939,7 @@ - + @@ -951,7 +951,7 @@ - + @@ -963,7 +963,7 @@ - + @@ -975,7 +975,7 @@ - + @@ -987,7 +987,7 @@ - + @@ -999,7 +999,7 @@ - + @@ -1011,7 +1011,7 @@ - + @@ -1023,7 +1023,7 @@ - + @@ -1035,7 +1035,7 @@ - + @@ -1047,7 +1047,7 @@ - + @@ -1059,7 +1059,7 @@ - + @@ -1071,7 +1071,7 @@ - + @@ -1083,7 +1083,7 @@ - + @@ -1095,7 +1095,7 @@ - + @@ -1107,7 +1107,7 @@ - + @@ -1119,7 +1119,7 @@ - + diff --git a/packages/g6/__tests__/snapshots/plugins/legend/normal.svg b/packages/g6/__tests__/snapshots/plugins/legend/normal.svg index 772174fe10..ad070219d3 100644 --- a/packages/g6/__tests__/snapshots/plugins/legend/normal.svg +++ b/packages/g6/__tests__/snapshots/plugins/legend/normal.svg @@ -303,427 +303,427 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + @@ -735,7 +735,7 @@ - + @@ -747,7 +747,7 @@ - + @@ -759,7 +759,7 @@ - + @@ -771,7 +771,7 @@ - + @@ -783,7 +783,7 @@ - + @@ -795,7 +795,7 @@ - + @@ -807,7 +807,7 @@ - + @@ -819,7 +819,7 @@ - + @@ -831,7 +831,7 @@ - + @@ -843,7 +843,7 @@ - + @@ -855,7 +855,7 @@ - + @@ -867,7 +867,7 @@ - + @@ -879,7 +879,7 @@ - + @@ -891,7 +891,7 @@ - + @@ -903,7 +903,7 @@ - + @@ -915,7 +915,7 @@ - + @@ -927,7 +927,7 @@ - + @@ -939,7 +939,7 @@ - + @@ -951,7 +951,7 @@ - + @@ -963,7 +963,7 @@ - + @@ -975,7 +975,7 @@ - + @@ -987,7 +987,7 @@ - + @@ -999,7 +999,7 @@ - + @@ -1011,7 +1011,7 @@ - + @@ -1023,7 +1023,7 @@ - + @@ -1035,7 +1035,7 @@ - + @@ -1047,7 +1047,7 @@ - + @@ -1059,7 +1059,7 @@ - + @@ -1071,7 +1071,7 @@ - + @@ -1083,7 +1083,7 @@ - + @@ -1095,7 +1095,7 @@ - + @@ -1107,7 +1107,7 @@ - + @@ -1119,7 +1119,7 @@ - + diff --git a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-layout.svg b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-layout.svg index 0e09c77f58..3c21cdcba2 100644 --- a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-layout.svg +++ b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-layout.svg @@ -6,19 +6,19 @@ - - + + - + - + diff --git a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-rotate-90.svg b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-rotate-90.svg index 4bacb4f810..c6ea1fa544 100644 --- a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-rotate-90.svg +++ b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-rotate-90.svg @@ -6,19 +6,19 @@ - - + + - + - + diff --git a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate-node-1.svg b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate-node-1.svg index a30ee0f87e..310402ed26 100644 --- a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate-node-1.svg +++ b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate-node-1.svg @@ -6,19 +6,19 @@ - - + + - + - + diff --git a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate.svg b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate.svg index 26217aeb3a..26fa06e8af 100644 --- a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate.svg +++ b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-translate.svg @@ -6,19 +6,19 @@ - - + + - + - + diff --git a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-zoom-2.svg b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-zoom-2.svg index ade3fd04d0..ea9de42f09 100644 --- a/packages/g6/__tests__/snapshots/runtime/graph/graph/after-zoom-2.svg +++ b/packages/g6/__tests__/snapshots/runtime/graph/graph/after-zoom-2.svg @@ -6,19 +6,19 @@ - - + + - + - + diff --git a/packages/g6/__tests__/snapshots/runtime/graph/graph/before-draw.svg b/packages/g6/__tests__/snapshots/runtime/graph/graph/before-draw.svg index f7957c9b62..b264a455cb 100644 --- a/packages/g6/__tests__/snapshots/runtime/graph/graph/before-draw.svg +++ b/packages/g6/__tests__/snapshots/runtime/graph/graph/before-draw.svg @@ -6,592 +6,592 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/packages/g6/__tests__/unit/layouts/d3-force-collision.spec.ts b/packages/g6/__tests__/unit/layouts/d3-force-collision.spec.ts new file mode 100644 index 0000000000..efa9b4e6f8 --- /dev/null +++ b/packages/g6/__tests__/unit/layouts/d3-force-collision.spec.ts @@ -0,0 +1,20 @@ +import { layoutForceCollision } from '@/__tests__/demos'; +import { createDemoGraph } from '@@/utils'; +import { clear as clearMockRandom, mock as mockRandom } from 'jest-random-mock'; + +describe('layout d3 force collision', () => { + beforeAll(() => { + mockRandom(); + }); + + afterAll(() => { + clearMockRandom(); + }); + + it('render', async () => { + const graph = await createDemoGraph(layoutForceCollision); + await expect(graph).toMatchSnapshot(__filename); + + graph.destroy(); + }); +}); diff --git a/packages/g6/__tests__/unit/layouts/d3-force-lattice.spec.ts b/packages/g6/__tests__/unit/layouts/d3-force-lattice.spec.ts new file mode 100644 index 0000000000..b7834a24f0 --- /dev/null +++ b/packages/g6/__tests__/unit/layouts/d3-force-lattice.spec.ts @@ -0,0 +1,13 @@ +import { layoutForceLattice } from '@/__tests__/demos'; +import { createDemoGraph } from '@@/utils'; + +describe('layout d3 force lattice', () => { + it('render', async () => { + const graph = await createDemoGraph(layoutForceLattice); + await expect(graph).toMatchSnapshot(__filename); + + // drag + + graph.destroy(); + }); +}); diff --git a/packages/g6/__tests__/utils/create.ts b/packages/g6/__tests__/utils/create.ts index 1312359654..f884bf93a9 100644 --- a/packages/g6/__tests__/utils/create.ts +++ b/packages/g6/__tests__/utils/create.ts @@ -6,22 +6,14 @@ import type { Node, Point } from '@/src/types'; import type { IRenderer } from '@antv/g'; import { resetEntityCounter } from '@antv/g'; import { Renderer as CanvasRenderer } from '@antv/g-canvas'; -import { Plugin as Plugin3D } from '@antv/g-plugin-3d'; -import { Plugin as PluginControl } from '@antv/g-plugin-control'; import { Renderer as SVGRenderer } from '@antv/g-svg'; -import { Renderer as WebGLRenderer } from '@antv/g-webgl'; import { OffscreenCanvasContext } from './offscreen-canvas-context'; function getRenderer(renderer: string) { switch (renderer) { - case 'webgl': { - const instance = new WebGLRenderer(); - instance.registerPlugin(new Plugin3D()); - instance.registerPlugin(new PluginControl()); - return instance; - } case 'svg': return new SVGRenderer(); + case 'webgl': case 'canvas': return new CanvasRenderer(); default: diff --git a/packages/g6/package.json b/packages/g6/package.json index babea73c00..7174ae4258 100644 --- a/packages/g6/package.json +++ b/packages/g6/package.json @@ -62,14 +62,11 @@ "@antv/g-plugin-dragndrop": "^1.8.22", "@antv/graphlib": "^2.0.2", "@antv/hierarchy": "latest", - "@antv/layout": "^1.2.14-beta.1", + "@antv/layout": "^1.2.14-beta.3", "@antv/util": "^3.3.7" }, "devDependencies": { - "@antv/g-plugin-3d": "^1.9.34", - "@antv/g-plugin-control": "^1.9.22", "@antv/g-svg": "^1.10.26", - "@antv/g-webgl": "^1.9.37", "@antv/layout-gpu": "^1.1.5", "@antv/layout-wasm": "^1.4.0", "@types/xmlserializer": "^0.6.6", diff --git a/packages/g6/src/behaviors/drag-element-force.ts b/packages/g6/src/behaviors/drag-element-force.ts new file mode 100644 index 0000000000..bd54a7d901 --- /dev/null +++ b/packages/g6/src/behaviors/drag-element-force.ts @@ -0,0 +1,70 @@ +import type { ID } from '@antv/graphlib'; +import type { D3Force3DLayout, D3ForceLayout } from '@antv/layout'; +import type { Element, IDragEvent, Point } from '../types'; +import { idOf } from '../utils/id'; +import { add } from '../utils/vector'; +import type { DragElementOptions } from './drag-element'; +import { DragElement } from './drag-element'; + +export interface DragElementForceOptions extends Omit {} + +export class DragElementForce extends DragElement { + private get forceLayoutInstance() { + return this.context + .layout!.getLayoutInstance() + .find((layout) => ['d3-force', 'd3-force-3d'].includes(layout?.id)) as D3ForceLayout | D3Force3DLayout; + } + + protected validate(event: IDragEvent): boolean { + if (!this.context.layout) return false; + + // 未使用力导布局 / The force layout is not used + if (!this.forceLayoutInstance) { + console.warn('DragElementForce only works with d3-force or d3-force-3d layout'); + return false; + } + + return super.validate(event); + } + + protected async moveElement(ids: ID[], offset: Point) { + const layout = this.forceLayoutInstance; + this.context.graph.getNodeData(ids).forEach((element, index) => { + const { x = 0, y = 0 } = element.style || {}; + layout.setFixedPosition(ids[index], [...add([+x, +y], offset)]); + }); + } + + protected onDragStart(event: IDragEvent) { + this.enable = this.validate(event); + if (!this.enable) return; + + this.target = this.getSelectedNodeIDs([event.target.id]); + this.hideEdge(); + this.context.graph.frontElement(this.target); + + const layout = this.forceLayoutInstance; + layout.simulation.alphaTarget(0.3).restart(); + + this.context.graph.getNodeData(this.target).forEach((element) => { + const { x = 0, y = 0 } = element.style || {}; + layout.setFixedPosition(idOf(element), [+x, +y]); + }); + } + + protected onDrag(event: IDragEvent) { + if (!this.enable) return; + + const delta = this.getDelta(event); + this.moveElement(this.target, delta); + } + + protected onDragEnd() { + const layout = this.forceLayoutInstance; + layout.simulation.alphaTarget(0); + + this.context.graph.getNodeData(this.target).forEach((element) => { + layout.setFixedPosition(idOf(element), [null, null, null]); + }); + } +} diff --git a/packages/g6/src/behaviors/drag-element.ts b/packages/g6/src/behaviors/drag-element.ts index f0c071e610..5b887ad6aa 100644 --- a/packages/g6/src/behaviors/drag-element.ts +++ b/packages/g6/src/behaviors/drag-element.ts @@ -1,9 +1,9 @@ import type { BaseStyleProps } from '@antv/g'; import { Rect } from '@antv/g'; -import { ID } from '@antv/graphlib'; +import type { ID } from '@antv/graphlib'; import { isFunction } from '@antv/util'; import { COMBO_KEY, CommonEvent } from '../constants'; -import { RuntimeContext } from '../runtime/types'; +import type { RuntimeContext } from '../runtime/types'; import type { EdgeDirection, Element, IDragEvent, Point, PrefixObject } from '../types'; import { getBBoxSize, getCombinedBBox } from '../utils/bbox'; import { idOf } from '../utils/id'; @@ -96,11 +96,11 @@ export class DragElement extends BaseBehavior { shadowLineDash: [5, 5], }; - private enable: boolean = false; + protected enable: boolean = false; private enableElements = ['node', 'combo']; - private target: ID[] = []; + protected target: ID[] = []; private shadow?: Rect; @@ -115,6 +115,11 @@ export class DragElement extends BaseBehavior { constructor(context: RuntimeContext, options: DragElementOptions) { super(context, Object.assign({}, DragElement.defaultOptions, options)); + this.onDragStart = this.onDragStart.bind(this); + this.onDrag = this.onDrag.bind(this); + this.onDragEnd = this.onDragEnd.bind(this); + this.onDrop = this.onDrop.bind(this); + this.bindEvents(); } @@ -139,7 +144,7 @@ export class DragElement extends BaseBehavior { } } - private getSelectedNodeIDs(currTarget: ID[]) { + protected getSelectedNodeIDs(currTarget: ID[]) { return Array.from( new Set( this.context.graph @@ -150,7 +155,12 @@ export class DragElement extends BaseBehavior { ); } - private onDragStart = (event: IElementDragEvent) => { + protected getDelta(event: IElementDragEvent) { + const zoom = this.context.graph.getZoom(); + return divide([event.dx, event.dy], zoom); + } + + protected onDragStart(event: IElementDragEvent) { this.enable = this.validate(event); if (!this.enable) return; @@ -158,19 +168,17 @@ export class DragElement extends BaseBehavior { this.hideEdge(); this.context.graph.frontElement(this.target); if (this.options.shadow) this.createShadow(this.target); - }; + } - private onDrag = (event: IElementDragEvent) => { + protected onDrag(event: IElementDragEvent) { if (!this.enable) return; - const zoom = this.context.graph.getZoom(); - const { dx, dy } = event; - const delta = divide([dx, dy], zoom); + const delta = this.getDelta(event); if (this.options.shadow) this.moveShadow(delta); else this.moveElement(this.target, delta); - }; + } - private onDragEnd = () => { + protected onDragEnd() { this.enable = false; if (this.options.shadow) { if (!this.shadow) return; @@ -182,7 +190,7 @@ export class DragElement extends BaseBehavior { this.showEdges(); this.options.onFinish?.(this.target); this.target = []; - }; + } private onDrop = async (event: IElementDragEvent) => { if (this.options.dropEffect !== 'link') return; @@ -200,14 +208,14 @@ export class DragElement extends BaseBehavior { await element?.draw({ animation: true }); }; - private validate(event: IElementDragEvent) { + protected validate(event: IElementDragEvent) { if (this.destroyed) return false; const { enable } = this.options; if (isFunction(enable)) return enable(event); return !!enable; } - private async moveElement(ids: ID[], offset: Point) { + protected async moveElement(ids: ID[], offset: Point) { const { model, element } = this.context; const { dropEffect } = this.options; ids.forEach((id) => { @@ -262,7 +270,7 @@ export class DragElement extends BaseBehavior { this.hiddenEdges = []; } - private hideEdge() { + protected hideEdge() { const { hideEdge, shadow } = this.options; if (hideEdge === 'none' || shadow) return; const { graph } = this.context; diff --git a/packages/g6/src/behaviors/index.ts b/packages/g6/src/behaviors/index.ts index 28778f6697..85d01eb6f3 100644 --- a/packages/g6/src/behaviors/index.ts +++ b/packages/g6/src/behaviors/index.ts @@ -3,6 +3,7 @@ export { CollapseExpand } from './collapse-expand'; export { CreateEdge } from './create-edge'; export { DragCanvas } from './drag-canvas'; export { DragElement } from './drag-element'; +export { DragElementForce } from './drag-element-force'; export { FocusElement } from './focus-element'; export { HoverElement } from './hover-element'; export { ZoomCanvas } from './zoom-canvas'; @@ -12,6 +13,7 @@ export type { CollapseExpandOptions } from './collapse-expand'; export type { CreateEdgeOptions } from './create-edge'; export type { DragCanvasOptions } from './drag-canvas'; export type { DragElementOptions } from './drag-element'; +export type { DragElementForceOptions } from './drag-element-force'; export type { FocusElementOptions } from './focus-element'; export type { HoverElementOptions } from './hover-element'; export type { ZoomCanvasOptions } from './zoom-canvas'; diff --git a/packages/g6/src/layouts/index.ts b/packages/g6/src/layouts/index.ts index 58c9d8c1ec..2f7c4160ff 100644 --- a/packages/g6/src/layouts/index.ts +++ b/packages/g6/src/layouts/index.ts @@ -4,6 +4,7 @@ export { CircularLayout, ComboCombinedLayout, ConcentricLayout, + D3Force3DLayout, D3ForceLayout, DagreLayout, ForceAtlas2Layout, diff --git a/packages/g6/src/layouts/types.ts b/packages/g6/src/layouts/types.ts index 51fc8af89b..7b9b64f041 100644 --- a/packages/g6/src/layouts/types.ts +++ b/packages/g6/src/layouts/types.ts @@ -2,6 +2,7 @@ import type { AntVDagreLayoutOptions, CircularLayoutOptions, ConcentricLayoutOptions, + D3Force3DLayoutOptions, D3ForceLayoutOptions, DagreLayoutOptions, ForceAtlas2LayoutOptions, @@ -19,6 +20,7 @@ export type BuiltInLayoutOptions = | CircularLayout | ConcentricLayout | D3ForceLayout + | D3Force3DLayout | DagreLayout | ForceAtlas2 | ForceLayout @@ -28,7 +30,7 @@ export type BuiltInLayoutOptions = | RadialLayout | RandomLayout; -export interface BaseLayoutOptions extends AnimationOptions, WebWorkerLayoutOptions, PresetLayoutOptions { +export interface BaseLayoutOptions extends AnimationOptions, WebWorkerLayoutOptions { type: string; /** * 参与该布局的节点 @@ -73,6 +75,10 @@ interface D3ForceLayout extends BaseLayoutOptions, D3ForceLayoutOptions { type: 'd3force'; } +interface D3Force3DLayout extends BaseLayoutOptions, D3Force3DLayoutOptions { + type: 'd3force3d'; +} + interface ForceLayout extends BaseLayoutOptions, ForceLayoutOptions { type: 'force' | 'gforce'; } @@ -89,10 +95,6 @@ interface DagreLayout extends BaseLayoutOptions, DagreLayoutOptions { type: 'dagre'; } -export interface PresetLayoutOptions { - presetLayout?: BaseLayoutOptions; -} - export interface AnimationOptions { /** * 启用布局动画,对于迭代布局,会在两次迭代之间进行动画过渡 diff --git a/packages/g6/src/palettes/index.ts b/packages/g6/src/palettes/index.ts index 68aa10ca5f..808fe2b0f0 100644 --- a/packages/g6/src/palettes/index.ts +++ b/packages/g6/src/palettes/index.ts @@ -17,6 +17,19 @@ export const spectral = [ 'rgb(94, 79, 162)', ]; +export const tableau = [ + 'rgb(78, 121, 167)', + 'rgb(242, 142, 44)', + 'rgb(225, 87, 89)', + 'rgb(118, 183, 178)', + 'rgb(89, 161, 79)', + 'rgb(237, 201, 73)', + 'rgb(175, 122, 161)', + 'rgb(255, 157, 167)', + 'rgb(156, 117, 95)', + 'rgb(186, 176, 171)', +]; + export const oranges = [ 'rgb(255, 245, 235)', 'rgb(254, 230, 206)', diff --git a/packages/g6/src/registry/build-in.ts b/packages/g6/src/registry/build-in.ts index 4fb42783d1..9ac7c31835 100644 --- a/packages/g6/src/registry/build-in.ts +++ b/packages/g6/src/registry/build-in.ts @@ -4,6 +4,7 @@ import { CreateEdge, DragCanvas, DragElement, + DragElementForce, FocusElement, HoverElement, ZoomCanvas, @@ -31,6 +32,7 @@ import { CircularLayout, ComboCombinedLayout, ConcentricLayout, + D3Force3DLayout, D3ForceLayout, DagreLayout, ForceAtlas2Layout, @@ -45,7 +47,7 @@ import { indented, mindmap, } from '../layouts'; -import { blues, greens, oranges, spectral } from '../palettes'; +import { blues, greens, oranges, spectral, tableau } from '../palettes'; import { Contextmenu, GridLine, Legend, Toolbar, Tooltip, Watermark } from '../plugins'; import { dark, light } from '../themes'; import type { ExtensionRegistry } from './types'; @@ -64,6 +66,7 @@ export const BUILT_IN_EXTENSIONS: ExtensionRegistry = { 'zoom-canvas': ZoomCanvas, 'drag-canvas': DragCanvas, 'drag-element': DragElement, + 'drag-element-force': DragElementForce, 'collapse-expand': CollapseExpand, 'hover-element': HoverElement, 'focus-element': FocusElement, @@ -89,6 +92,7 @@ export const BUILT_IN_EXTENSIONS: ExtensionRegistry = { circular: CircularLayout, concentric: ConcentricLayout, d3force: D3ForceLayout, + 'd3-force-3d': D3Force3DLayout, dagre: DagreLayout, dendrogram, force: ForceLayout, @@ -112,6 +116,7 @@ export const BUILT_IN_EXTENSIONS: ExtensionRegistry = { }, palette: { spectral, + tableau, oranges, greens, blues, diff --git a/packages/g6/src/runtime/layout.ts b/packages/g6/src/runtime/layout.ts index 77bfd0dfbd..13bdede8e0 100644 --- a/packages/g6/src/runtime/layout.ts +++ b/packages/g6/src/runtime/layout.ts @@ -1,24 +1,22 @@ import type { IAnimation } from '@antv/g'; import type { Edge as GraphlibEdge, Node as GraphlibNode } from '@antv/graphlib'; import { Graph as Graphlib } from '@antv/graphlib'; -import type { ForceLayoutOptions, Layout, LayoutMapping } from '@antv/layout'; +import type { Layout, LayoutMapping } from '@antv/layout'; import { Supervisor, isLayoutWithIterations } from '@antv/layout'; -import { isNumber } from '@antv/util'; +import { deepMix } from '@antv/util'; import { COMBO_KEY, GraphEvent, TREE_KEY } from '../constants'; -import type { BaseLayoutOptions } from '../layouts/types'; import { getExtension } from '../registry'; import type { EdgeData, NodeData } from '../spec'; import type { STDLayoutOptions } from '../spec/layout'; -import type { NodeLikeData, PartialGraphData, Point, TreeData } from '../types'; +import type { PartialGraphData, TreeData } from '../types'; import { getAnimation } from '../utils/animation'; import { isVisible } from '../utils/element'; import { GraphLifeCycleEvent, emit } from '../utils/event'; import { createTreeStructure } from '../utils/graphlib'; -import { isComboLayout, isPositionSpecified, isTreeLayout } from '../utils/layout'; +import { isComboLayout, isTreeLayout } from '../utils/layout'; import { parsePoint } from '../utils/point'; import { parseSize } from '../utils/size'; import { dfs } from '../utils/traverse'; -import { add } from '../utils/vector'; import type { RuntimeContext } from './types'; type LayoutGraphlibModel = Graphlib['style'], Required['style']>; @@ -34,6 +32,8 @@ export class LayoutController { private instance?: Layout; + private instances: Layout[] = []; + private animationResult?: IAnimation | null; private get presetOptions() { @@ -51,45 +51,11 @@ export class LayoutController { this.context = context; } - /** - * 初始化布局位置 - * - * Initialize layout position - * @param model - 布局数据 | Layout data - * @param options - 预设布局配置项 | Preset layout options - * @returns 布局结果 | Layout result - */ - private async presetLayout(model: LayoutGraphlibModel, options?: BaseLayoutOptions) { - if (!options) return; - if (options.type) return this.stepLayout(model, options); - - const positions = model.getAllNodes().reduce( - (nodes, node) => { - if (!isPositionSpecified(node.data)) { - // 如果没有指定位置,将节点放置到邻居节点的中心 / If the position is not specified, place the node in the center of the neighboring node - let p: Point = [0, 0, 0]; - let count = 0; - model.getNeighbors(node.id).forEach((neighbor) => { - if (isPositionSpecified(neighbor.data)) { - const { x: nx, y: ny, z: nz = 0 } = neighbor.data; - p = add(p, [nx, ny, nz] as Point); - count++; - } - }); - if (count) { - nodes.push({ - id: node.id, - data: { x: p[0] / count, y: p[1] / count, z: p[2] / count }, - }); - } - } - - return nodes; - }, - [] as LayoutMapping['nodes'], - ); - - this.updateElementPosition({ nodes: positions, edges: [] }, false); + public getLayoutInstance(): Layout[]; + public getLayoutInstance(index: number): Layout; + public getLayoutInstance(index?: number) { + if (index === undefined) return this.instances; + return this.instance; } public async layout() { @@ -98,10 +64,9 @@ export class LayoutController { const { graph } = this.context; emit(graph, new GraphLifeCycleEvent(GraphEvent.BEFORE_LAYOUT)); for (const options of pipeline) { - const { presetLayout } = options; + const index = pipeline.indexOf(options); const model = this.getLayoutDataModel(options); - await this.presetLayout(model, presetLayout); - const result = await this.stepLayout(model, { ...this.presetOptions, ...options }); + const result = await this.stepLayout(model, { ...this.presetOptions, ...options }, index); if (!options.animation) { this.updateElementPosition(result, false); @@ -110,16 +75,24 @@ export class LayoutController { emit(graph, new GraphLifeCycleEvent(GraphEvent.AFTER_LAYOUT)); } - public async stepLayout(model: LayoutGraphlibModel, options: STDLayoutOptions): Promise { - if (isTreeLayout(options)) return await this.treeLayout(model, options); - return await this.graphLayout(model, options); + public async stepLayout( + model: LayoutGraphlibModel, + options: STDLayoutOptions, + index: number, + ): Promise { + if (isTreeLayout(options)) return await this.treeLayout(model, options, index); + return await this.graphLayout(model, options, index); } - private async graphLayout(model: LayoutGraphlibModel, options: STDLayoutOptions): Promise { - // TODO iterations 考虑基于动画时长进行计算 + private async graphLayout( + model: LayoutGraphlibModel, + options: STDLayoutOptions, + index: number, + ): Promise { const { animation, enableWorker, iterations = 300 } = options; const layout = this.initGraphLayout(model, options); + this.instances[index] = layout; this.instance = layout; // 使用 web worker 执行布局 / Use web worker to execute layout @@ -155,7 +128,11 @@ export class LayoutController { return layoutResult; } - private async treeLayout(model: LayoutGraphlibModel, options: STDLayoutOptions): Promise { + private async treeLayout( + model: LayoutGraphlibModel, + options: STDLayoutOptions, + index: number, + ): Promise { const { type, animation } = options; // @ts-expect-error @antv/hierarchy 布局格式与 @antv/layout 不一致,其导出的是一个方法,而非 class // The layout format of @antv/hierarchy is inconsistent with @antv/layout, it exports a method instead of a class @@ -324,32 +301,23 @@ export class LayoutController { if (!Ctor) throw new Error(`The layout type ${type} is not found`); - const config = { nodeSize, width, height, center, ...restOptions }; + const layout = new Ctor(); - if (type === 'force') { - Object.assign(config, { - getMass: (node: GraphlibNode['style']>) => { - const { id } = node; - // 此处 node 是经过 converter 转化的 / Here node is converted - const { getMass } = options as ForceLayoutOptions; - // @ts-expect-error size is incompatible, but it's okay - if (getMass) return getMass(node); - const { mass } = node.data; - if (isNumber(mass)) return mass; - // 如果有预布局位置或者数据中指定了坐标,则质量为 10 / If there is a pre-layout position or the coordinates are specified in the data, the mass is 10 - if (isPositionSpecified(model.getNode(id).data)) return 10; - if (isPositionSpecified(node.data)) return 10; - return 1; - }, - }); - } else if (type === 'dagre') { - Object.assign(config, { - // TODO 添加预设位置 - preset: [], - }); + const config = { nodeSize, width, height, center }; + + switch (layout.id) { + case 'd3-force': + case 'd3-force-3d': + Object.assign(config, { + center: { x: width / 2, y: height / 2, z: 0 }, + }); + break; + default: + break; } - return new Ctor(config); + deepMix(layout.options, config, restOptions); + return layout; } private updateElementPosition(layoutData: LayoutMapping, animation: boolean) { @@ -385,6 +353,7 @@ export class LayoutController { this.supervisor?.kill(); this.supervisor = undefined; this.instance = undefined; + this.instances = []; this.animationResult = undefined; } }