From 835f7fe0379e0f66999fda7b7cea28ed43720b45 Mon Sep 17 00:00:00 2001
From: Yuxin <55794321+yvonneyx@users.noreply.github.com>
Date: Thu, 22 Aug 2024 20:32:19 +0800
Subject: [PATCH] feat(plugin): add edge filter lens plugin (#6224)
* feat(plugin): add edge filter lens
* test: add unit tests
* refactor(plugins): fix cr issues
* test: update unit test
* docs: add demo
* fix: update demo and tests
---
packages/g6/__tests__/dataset/relations.json | 2594 +++--------------
packages/g6/__tests__/demos/index.ts | 1 +
.../demos/plugin-edge-filter-lens.ts | 33 +
.../plugins/edge-filter-lens/default.svg | 2172 ++++++++++++++
.../edge-filter-lens/move-lens-click-1.svg | 2182 ++++++++++++++
.../edge-filter-lens/move-lens-click-2.svg | 2234 ++++++++++++++
.../edge-filter-lens/move-lens-drag.svg | 2234 ++++++++++++++
.../move-lens-pointermove.svg | 2234 ++++++++++++++
.../edge-filter-lens/node-type-either.svg | 2210 ++++++++++++++
.../edge-filter-lens/node-type-source.svg | 2196 ++++++++++++++
.../edge-filter-lens/node-type-target.svg | 2196 ++++++++++++++
.../plugins/edge-filter-lens/scale-larger.svg | 2234 ++++++++++++++
.../edge-filter-lens/scale-smaller.svg | 2234 ++++++++++++++
.../unit/plugins/edge-filter-lens.spec.ts | 77 +
packages/g6/src/exports.ts | 2 +
.../g6/src/plugins/edge-filter-lens/index.ts | 391 +++
packages/g6/src/plugins/index.ts | 2 +
packages/g6/src/registry/build-in.ts | 2 +
.../plugin/edge-filter-lens/demo/basic.js | 67 +
.../plugin/edge-filter-lens/demo/meta.json | 16 +
.../plugin/edge-filter-lens/index.en.md | 5 +
.../plugin/edge-filter-lens/index.zh.md | 5 +
22 files changed, 23058 insertions(+), 2263 deletions(-)
create mode 100644 packages/g6/__tests__/demos/plugin-edge-filter-lens.ts
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/default.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-1.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-2.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-drag.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-pointermove.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-either.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-source.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-target.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-larger.svg
create mode 100644 packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-smaller.svg
create mode 100644 packages/g6/__tests__/unit/plugins/edge-filter-lens.spec.ts
create mode 100644 packages/g6/src/plugins/edge-filter-lens/index.ts
create mode 100644 packages/site/examples/plugin/edge-filter-lens/demo/basic.js
create mode 100644 packages/site/examples/plugin/edge-filter-lens/demo/meta.json
create mode 100644 packages/site/examples/plugin/edge-filter-lens/index.en.md
create mode 100644 packages/site/examples/plugin/edge-filter-lens/index.zh.md
diff --git a/packages/g6/__tests__/dataset/relations.json b/packages/g6/__tests__/dataset/relations.json
index 8d5f416074..b24fd5ae7a 100644
--- a/packages/g6/__tests__/dataset/relations.json
+++ b/packages/g6/__tests__/dataset/relations.json
@@ -1,2269 +1,337 @@
{
"nodes": [
- {
- "id": "Myriel"
- },
- {
- "id": "Napoleon"
- },
- {
- "id": "Mlle.Baptistine"
- },
- {
- "id": "Mme.Magloire"
- },
- {
- "id": "CountessdeLo"
- },
- {
- "id": "Geborand"
- },
- {
- "id": "Champtercier"
- },
- {
- "id": "Cravatte"
- },
- {
- "id": "Count"
- },
- {
- "id": "OldMan"
- },
- {
- "id": "Labarre"
- },
- {
- "id": "Valjean"
- },
- {
- "id": "Marguerite"
- },
- {
- "id": "Mme.deR"
- },
- {
- "id": "Isabeau"
- },
- {
- "id": "Gervais"
- },
- {
- "id": "Tholomyes"
- },
- {
- "id": "Listolier"
- },
- {
- "id": "Fameuil"
- },
- {
- "id": "Blacheville"
- },
- {
- "id": "Favourite"
- },
- {
- "id": "Dahlia"
- },
- {
- "id": "Zephine"
- },
- {
- "id": "Fantine"
- },
- {
- "id": "Mme.Thenardier"
- },
- {
- "id": "Thenardier"
- },
- {
- "id": "Cosette"
- },
- {
- "id": "Javert"
- },
- {
- "id": "Fauchelevent"
- },
- {
- "id": "Bamatabois"
- },
- {
- "id": "Perpetue"
- },
- {
- "id": "Simplice"
- },
- {
- "id": "Scaufflaire"
- },
- {
- "id": "Woman1"
- },
- {
- "id": "Judge"
- },
- {
- "id": "Champmathieu"
- },
- {
- "id": "Brevet"
- },
- {
- "id": "Chenildieu"
- },
- {
- "id": "Cochepaille"
- },
- {
- "id": "Pontmercy"
- },
- {
- "id": "Boulatruelle"
- },
- {
- "id": "Eponine"
- },
- {
- "id": "Anzelma"
- },
- {
- "id": "Woman2"
- },
- {
- "id": "MotherInnocent"
- },
- {
- "id": "Gribier"
- },
- {
- "id": "Jondrette"
- },
- {
- "id": "Mme.Burgon"
- },
- {
- "id": "Gavroche"
- },
- {
- "id": "Gillenormand"
- },
- {
- "id": "Magnon"
- },
- {
- "id": "Mlle.Gillenormand"
- },
- {
- "id": "Mme.Pontmercy"
- },
- {
- "id": "Mlle.Vaubois"
- },
- {
- "id": "Lt.Gillenormand"
- },
- {
- "id": "Marius"
- },
- {
- "id": "BaronessT"
- },
- {
- "id": "Mabeuf"
- },
- {
- "id": "Enjolras"
- },
- {
- "id": "Combeferre"
- },
- {
- "id": "Prouvaire"
- },
- {
- "id": "Feuilly"
- },
- {
- "id": "Courfeyrac"
- },
- {
- "id": "Bahorel"
- },
- {
- "id": "Bossuet"
- },
- {
- "id": "Joly"
- },
- {
- "id": "Grantaire"
- },
- {
- "id": "MotherPlutarch"
- },
- {
- "id": "Gueulemer"
- },
- {
- "id": "Babet"
- },
- {
- "id": "Claquesous"
- },
- {
- "id": "Montparnasse"
- },
- {
- "id": "Toussaint"
- },
- {
- "id": "Child1"
- },
- {
- "id": "Child2"
- },
- {
- "id": "Brujon"
- },
- {
- "id": "Mme.Hucheloup"
- }
+ { "id": "Myriel", "style": { "x": 197.13154409979438, "y": 58.49567372045294 } },
+ { "id": "Napoleon", "style": { "x": 147.01896389692396, "y": 22.47017586685877 } },
+ { "id": "Mlle.Baptistine", "style": { "x": 225.53929622396657, "y": 141.52203994343503 } },
+ { "id": "Mme.Magloire", "style": { "x": 255.07906424356426, "y": 120.2538776202175 } },
+ { "id": "CountessdeLo", "style": { "x": 151.886941377147, "y": -3.5440526274605024 } },
+ { "id": "Geborand", "style": { "x": 136.99780912786676, "y": 41.74972346367764 } },
+ { "id": "Champtercier", "style": { "x": 227.06448529213904, "y": 8.803245731763797 } },
+ { "id": "Cravatte", "style": { "x": 172.28712104569624, "y": -10.28659385020346 } },
+ { "id": "Count", "style": { "x": 172.9776128536988, "y": 12.515280485950003 } },
+ { "id": "OldMan", "style": { "x": 198.7549153659034, "y": -6.143466139379697 } },
+ { "id": "Labarre", "style": { "x": 266.5746386228216, "y": 203.98384539788222 } },
+ { "id": "Valjean", "style": { "x": 322.22242753596396, "y": 221.58991461580462 } },
+ { "id": "Marguerite", "style": { "x": 265.1218339265034, "y": 171.59761511302105 } },
+ { "id": "Mme.deR", "style": { "x": 299.78639359854327, "y": 133.57398015667923 } },
+ { "id": "Isabeau", "style": { "x": 282.69786358028415, "y": 191.50678051232913 } },
+ { "id": "Gervais", "style": { "x": 334.4562033716733, "y": 148.86340203151713 } },
+ { "id": "Tholomyes", "style": { "x": 359.6758601570104, "y": 158.51932058679517 } },
+ { "id": "Listolier", "style": { "x": 308.6408107258377, "y": 80.08978211784734 } },
+ { "id": "Fameuil", "style": { "x": 329.1208783621155, "y": 89.50783923513406 } },
+ { "id": "Blacheville", "style": { "x": 351.31710942912247, "y": 95.62381874446997 } },
+ { "id": "Favourite", "style": { "x": 284.0990966456606, "y": 153.6649901350214 } },
+ { "id": "Dahlia", "style": { "x": 303.2794454950651, "y": 170.87469919068386 } },
+ { "id": "Zephine", "style": { "x": 286.9038607953858, "y": 94.82364610010669 } },
+ { "id": "Fantine", "style": { "x": 337.7295856292113, "y": 187.2760733153313 } },
+ { "id": "Mme.Thenardier", "style": { "x": 283.8431887426204, "y": 267.7101161193055 } },
+ { "id": "Thenardier", "style": { "x": 317.6539018281542, "y": 300.0586304481375 } },
+ { "id": "Cosette", "style": { "x": 343.4495217104461, "y": 248.14013534143953 } },
+ { "id": "Javert", "style": { "x": 368.6281356589531, "y": 263.5847126845181 } },
+ { "id": "Fauchelevent", "style": { "x": 377.3520676841103, "y": 176.72534157485532 } },
+ { "id": "Bamatabois", "style": { "x": 391.75313851634024, "y": 156.5212161097912 } },
+ { "id": "Perpetue", "style": { "x": 234.8199749437348, "y": 195.99976079362335 } },
+ { "id": "Simplice", "style": { "x": 286.4937544345336, "y": 227.73420851527578 } },
+ { "id": "Scaufflaire", "style": { "x": 250.02919011143416, "y": 231.2513211913802 } },
+ { "id": "Woman1", "style": { "x": 375.4668487891018, "y": 202.783515421686 } },
+ { "id": "Judge", "style": { "x": 370.1700307319093, "y": 139.4810861650384 } },
+ { "id": "Champmathieu", "style": { "x": 404.6422482933774, "y": 216.58364918349568 } },
+ { "id": "Brevet", "style": { "x": 399.2513775912632, "y": 183.03026453336724 } },
+ { "id": "Chenildieu", "style": { "x": 425.90996667472837, "y": 194.79658513642403 } },
+ { "id": "Cochepaille", "style": { "x": 419.38361105364334, "y": 148.69180823448008 } },
+ { "id": "Pontmercy", "style": { "x": 375.2946100421193, "y": 307.66682817782345 } },
+ { "id": "Boulatruelle", "style": { "x": 260.66757416917164, "y": 279.0949406815367 } },
+ { "id": "Eponine", "style": { "x": 268.68796660221636, "y": 365.8200533034293 } },
+ { "id": "Anzelma", "style": { "x": 234.53762633403787, "y": 303.08504254821366 } },
+ { "id": "Woman2", "style": { "x": 304.29126463264043, "y": 254.05392981470945 } },
+ { "id": "MotherInnocent", "style": { "x": 350.35613429759803, "y": 214.42252912270644 } },
+ { "id": "Gribier", "style": { "x": 437.51920169330805, "y": 160.14388411785757 } },
+ { "id": "Jondrette", "style": { "x": 510.1406569699257, "y": 327.7456828911454 } },
+ { "id": "Mme.Burgon", "style": { "x": 466.0856874797108, "y": 368.0210264990602 } },
+ { "id": "Gavroche", "style": { "x": 393.6973181801981, "y": 380.40382743216634 } },
+ { "id": "Gillenormand", "style": { "x": 338.1148595335302, "y": 286.4434006942807 } },
+ { "id": "Magnon", "style": { "x": 277.12320020410266, "y": 317.4384382481713 } },
+ { "id": "Mlle.Gillenormand", "style": { "x": 257.52167498720337, "y": 306.4604520400414 } },
+ { "id": "Mme.Pontmercy", "style": { "x": 307.71325168392366, "y": 318.0074114921048 } },
+ { "id": "Mlle.Vaubois", "style": { "x": 197.63137784390082, "y": 325.2999365859076 } },
+ { "id": "Lt.Gillenormand", "style": { "x": 294.4105849543593, "y": 296.53686533697186 } },
+ { "id": "Marius", "style": { "x": 336.3436812430268, "y": 350.8376519695578 } },
+ { "id": "BaronessT", "style": { "x": 390.6807729530675, "y": 322.9175698803163 } },
+ { "id": "Mabeuf", "style": { "x": 366.77554563642803, "y": 445.26666512175433 } },
+ { "id": "Enjolras", "style": { "x": 376.9421415192702, "y": 371.1750781444891 } },
+ { "id": "Combeferre", "style": { "x": 397.0516872015465, "y": 416.38478793328625 } },
+ { "id": "Prouvaire", "style": { "x": 309.0241345496318, "y": 426.44215271462605 } },
+ { "id": "Feuilly", "style": { "x": 314.71137563489117, "y": 456.80172690673896 } },
+ { "id": "Courfeyrac", "style": { "x": 332.8405296045364, "y": 435.8881866127797 } },
+ { "id": "Bahorel", "style": { "x": 343.1268360879219, "y": 466.9404473411801 } },
+ { "id": "Bossuet", "style": { "x": 305.84814130923144, "y": 382.89355947309724 } },
+ { "id": "Joly", "style": { "x": 371.447442010866, "y": 415.99688422022257 } },
+ { "id": "Grantaire", "style": { "x": 370.72651876919826, "y": 466.96671298340794 } },
+ { "id": "MotherPlutarch", "style": { "x": 424.04457501182867, "y": 461.9373924104361 } },
+ { "id": "Gueulemer", "style": { "x": 344.1315821958891, "y": 323.7890765583486 } },
+ { "id": "Babet", "style": { "x": 367.3969014122835, "y": 319.2359576043117 } },
+ { "id": "Claquesous", "style": { "x": 303.23885194199465, "y": 347.8041412708572 } },
+ { "id": "Montparnasse", "style": { "x": 322.6528688110919, "y": 330.01757397802925 } },
+ { "id": "Toussaint", "style": { "x": 306.6921797724685, "y": 277.05255454452566 } },
+ { "id": "Child1", "style": { "x": 361.1652068827243, "y": 387.9769951347244 } },
+ { "id": "Child2", "style": { "x": 415.98942162128606, "y": 432.37341762016945 } },
+ { "id": "Brujon", "style": { "x": 330.44198511493056, "y": 394.6025799878689 } },
+ { "id": "Mme.Hucheloup", "style": { "x": 394.43875881505835, "y": 450.4056149101193 } }
],
"edges": [
- {
- "id": "0",
- "source": "Napoleon",
- "target": "Myriel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "1",
- "source": "Mlle.Baptistine",
- "target": "Myriel",
- "data": {
- "value": 8
- }
- },
- {
- "id": "2",
- "source": "Mme.Magloire",
- "target": "Myriel",
- "data": {
- "value": 10
- }
- },
- {
- "id": "3",
- "source": "Mme.Magloire",
- "target": "Mlle.Baptistine",
- "data": {
- "value": 6
- }
- },
- {
- "id": "4",
- "source": "CountessdeLo",
- "target": "Myriel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "5",
- "source": "Geborand",
- "target": "Myriel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "6",
- "source": "Champtercier",
- "target": "Myriel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "7",
- "source": "Cravatte",
- "target": "Myriel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "8",
- "source": "Count",
- "target": "Myriel",
- "data": {
- "value": 2
- }
- },
- {
- "id": "9",
- "source": "OldMan",
- "target": "Myriel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "10",
- "source": "Valjean",
- "target": "Labarre",
- "data": {
- "value": 1
- }
- },
- {
- "id": "11",
- "source": "Valjean",
- "target": "Mme.Magloire",
- "data": {
- "value": 3
- }
- },
- {
- "id": "12",
- "source": "Valjean",
- "target": "Mlle.Baptistine",
- "data": {
- "value": 3
- }
- },
- {
- "id": "13",
- "source": "Valjean",
- "target": "Myriel",
- "data": {
- "value": 5
- }
- },
- {
- "id": "14",
- "source": "Marguerite",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "15",
- "source": "Mme.deR",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "16",
- "source": "Isabeau",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "17",
- "source": "Gervais",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "18",
- "source": "Listolier",
- "target": "Tholomyes",
- "data": {
- "value": 4
- }
- },
- {
- "id": "19",
- "source": "Fameuil",
- "target": "Tholomyes",
- "data": {
- "value": 4
- }
- },
- {
- "id": "20",
- "source": "Fameuil",
- "target": "Listolier",
- "data": {
- "value": 4
- }
- },
- {
- "id": "21",
- "source": "Blacheville",
- "target": "Tholomyes",
- "data": {
- "value": 4
- }
- },
- {
- "id": "22",
- "source": "Blacheville",
- "target": "Listolier",
- "data": {
- "value": 4
- }
- },
- {
- "id": "23",
- "source": "Blacheville",
- "target": "Fameuil",
- "data": {
- "value": 4
- }
- },
- {
- "id": "24",
- "source": "Favourite",
- "target": "Tholomyes",
- "data": {
- "value": 3
- }
- },
- {
- "id": "25",
- "source": "Favourite",
- "target": "Listolier",
- "data": {
- "value": 3
- }
- },
- {
- "id": "26",
- "source": "Favourite",
- "target": "Fameuil",
- "data": {
- "value": 3
- }
- },
- {
- "id": "27",
- "source": "Favourite",
- "target": "Blacheville",
- "data": {
- "value": 4
- }
- },
- {
- "id": "28",
- "source": "Dahlia",
- "target": "Tholomyes",
- "data": {
- "value": 3
- }
- },
- {
- "id": "29",
- "source": "Dahlia",
- "target": "Listolier",
- "data": {
- "value": 3
- }
- },
- {
- "id": "30",
- "source": "Dahlia",
- "target": "Fameuil",
- "data": {
- "value": 3
- }
- },
- {
- "id": "31",
- "source": "Dahlia",
- "target": "Blacheville",
- "data": {
- "value": 3
- }
- },
- {
- "id": "32",
- "source": "Dahlia",
- "target": "Favourite",
- "data": {
- "value": 5
- }
- },
- {
- "id": "33",
- "source": "Zephine",
- "target": "Tholomyes",
- "data": {
- "value": 3
- }
- },
- {
- "id": "34",
- "source": "Zephine",
- "target": "Listolier",
- "data": {
- "value": 3
- }
- },
- {
- "id": "35",
- "source": "Zephine",
- "target": "Fameuil",
- "data": {
- "value": 3
- }
- },
- {
- "id": "36",
- "source": "Zephine",
- "target": "Blacheville",
- "data": {
- "value": 3
- }
- },
- {
- "id": "37",
- "source": "Zephine",
- "target": "Favourite",
- "data": {
- "value": 4
- }
- },
- {
- "id": "38",
- "source": "Zephine",
- "target": "Dahlia",
- "data": {
- "value": 4
- }
- },
- {
- "id": "39",
- "source": "Fantine",
- "target": "Tholomyes",
- "data": {
- "value": 3
- }
- },
- {
- "id": "40",
- "source": "Fantine",
- "target": "Listolier",
- "data": {
- "value": 3
- }
- },
- {
- "id": "41",
- "source": "Fantine",
- "target": "Fameuil",
- "data": {
- "value": 3
- }
- },
- {
- "id": "42",
- "source": "Fantine",
- "target": "Blacheville",
- "data": {
- "value": 3
- }
- },
- {
- "id": "43",
- "source": "Fantine",
- "target": "Favourite",
- "data": {
- "value": 4
- }
- },
- {
- "id": "44",
- "source": "Fantine",
- "target": "Dahlia",
- "data": {
- "value": 4
- }
- },
- {
- "id": "45",
- "source": "Fantine",
- "target": "Zephine",
- "data": {
- "value": 4
- }
- },
- {
- "id": "46",
- "source": "Fantine",
- "target": "Marguerite",
- "data": {
- "value": 2
- }
- },
- {
- "id": "47",
- "source": "Fantine",
- "target": "Valjean",
- "data": {
- "value": 9
- }
- },
- {
- "id": "48",
- "source": "Mme.Thenardier",
- "target": "Fantine",
- "data": {
- "value": 2
- }
- },
- {
- "id": "49",
- "source": "Mme.Thenardier",
- "target": "Valjean",
- "data": {
- "value": 7
- }
- },
- {
- "id": "50",
- "source": "Thenardier",
- "target": "Mme.Thenardier",
- "data": {
- "value": 13
- }
- },
- {
- "id": "51",
- "source": "Thenardier",
- "target": "Fantine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "52",
- "source": "Thenardier",
- "target": "Valjean",
- "data": {
- "value": 12
- }
- },
- {
- "id": "53",
- "source": "Cosette",
- "target": "Mme.Thenardier",
- "data": {
- "value": 4
- }
- },
- {
- "id": "54",
- "source": "Cosette",
- "target": "Valjean",
- "data": {
- "value": 31
- }
- },
- {
- "id": "55",
- "source": "Cosette",
- "target": "Tholomyes",
- "data": {
- "value": 1
- }
- },
- {
- "id": "56",
- "source": "Cosette",
- "target": "Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "57",
- "source": "Javert",
- "target": "Valjean",
- "data": {
- "value": 17
- }
- },
- {
- "id": "58",
- "source": "Javert",
- "target": "Fantine",
- "data": {
- "value": 5
- }
- },
- {
- "id": "59",
- "source": "Javert",
- "target": "Thenardier",
- "data": {
- "value": 5
- }
- },
- {
- "id": "60",
- "source": "Javert",
- "target": "Mme.Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "61",
- "source": "Javert",
- "target": "Cosette",
- "data": {
- "value": 1
- }
- },
- {
- "id": "62",
- "source": "Fauchelevent",
- "target": "Valjean",
- "data": {
- "value": 8
- }
- },
- {
- "id": "63",
- "source": "Fauchelevent",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "64",
- "source": "Bamatabois",
- "target": "Fantine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "65",
- "source": "Bamatabois",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "66",
- "source": "Bamatabois",
- "target": "Valjean",
- "data": {
- "value": 2
- }
- },
- {
- "id": "67",
- "source": "Perpetue",
- "target": "Fantine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "68",
- "source": "Simplice",
- "target": "Perpetue",
- "data": {
- "value": 2
- }
- },
- {
- "id": "69",
- "source": "Simplice",
- "target": "Valjean",
- "data": {
- "value": 3
- }
- },
- {
- "id": "70",
- "source": "Simplice",
- "target": "Fantine",
- "data": {
- "value": 2
- }
- },
- {
- "id": "71",
- "source": "Simplice",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "72",
- "source": "Scaufflaire",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "73",
- "source": "Woman1",
- "target": "Valjean",
- "data": {
- "value": 2
- }
- },
- {
- "id": "74",
- "source": "Woman1",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "75",
- "source": "Judge",
- "target": "Valjean",
- "data": {
- "value": 3
- }
- },
- {
- "id": "76",
- "source": "Judge",
- "target": "Bamatabois",
- "data": {
- "value": 2
- }
- },
- {
- "id": "77",
- "source": "Champmathieu",
- "target": "Valjean",
- "data": {
- "value": 3
- }
- },
- {
- "id": "78",
- "source": "Champmathieu",
- "target": "Judge",
- "data": {
- "value": 3
- }
- },
- {
- "id": "79",
- "source": "Champmathieu",
- "target": "Bamatabois",
- "data": {
- "value": 2
- }
- },
- {
- "id": "80",
- "source": "Brevet",
- "target": "Judge",
- "data": {
- "value": 2
- }
- },
- {
- "id": "81",
- "source": "Brevet",
- "target": "Champmathieu",
- "data": {
- "value": 2
- }
- },
- {
- "id": "82",
- "source": "Brevet",
- "target": "Valjean",
- "data": {
- "value": 2
- }
- },
- {
- "id": "83",
- "source": "Brevet",
- "target": "Bamatabois",
- "data": {
- "value": 1
- }
- },
- {
- "id": "84",
- "source": "Chenildieu",
- "target": "Judge",
- "data": {
- "value": 2
- }
- },
- {
- "id": "85",
- "source": "Chenildieu",
- "target": "Champmathieu",
- "data": {
- "value": 2
- }
- },
- {
- "id": "86",
- "source": "Chenildieu",
- "target": "Brevet",
- "data": {
- "value": 2
- }
- },
- {
- "id": "87",
- "source": "Chenildieu",
- "target": "Valjean",
- "data": {
- "value": 2
- }
- },
- {
- "id": "88",
- "source": "Chenildieu",
- "target": "Bamatabois",
- "data": {
- "value": 1
- }
- },
- {
- "id": "89",
- "source": "Cochepaille",
- "target": "Judge",
- "data": {
- "value": 2
- }
- },
- {
- "id": "90",
- "source": "Cochepaille",
- "target": "Champmathieu",
- "data": {
- "value": 2
- }
- },
- {
- "id": "91",
- "source": "Cochepaille",
- "target": "Brevet",
- "data": {
- "value": 2
- }
- },
- {
- "id": "92",
- "source": "Cochepaille",
- "target": "Chenildieu",
- "data": {
- "value": 2
- }
- },
- {
- "id": "93",
- "source": "Cochepaille",
- "target": "Valjean",
- "data": {
- "value": 2
- }
- },
- {
- "id": "94",
- "source": "Cochepaille",
- "target": "Bamatabois",
- "data": {
- "value": 1
- }
- },
- {
- "id": "95",
- "source": "Pontmercy",
- "target": "Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "96",
- "source": "Boulatruelle",
- "target": "Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "97",
- "source": "Eponine",
- "target": "Mme.Thenardier",
- "data": {
- "value": 2
- }
- },
- {
- "id": "98",
- "source": "Eponine",
- "target": "Thenardier",
- "data": {
- "value": 3
- }
- },
- {
- "id": "99",
- "source": "Anzelma",
- "target": "Eponine",
- "data": {
- "value": 2
- }
- },
- {
- "id": "100",
- "source": "Anzelma",
- "target": "Thenardier",
- "data": {
- "value": 2
- }
- },
- {
- "id": "101",
- "source": "Anzelma",
- "target": "Mme.Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "102",
- "source": "Woman2",
- "target": "Valjean",
- "data": {
- "value": 3
- }
- },
- {
- "id": "103",
- "source": "Woman2",
- "target": "Cosette",
- "data": {
- "value": 1
- }
- },
- {
- "id": "104",
- "source": "Woman2",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "105",
- "source": "MotherInnocent",
- "target": "Fauchelevent",
- "data": {
- "value": 3
- }
- },
- {
- "id": "106",
- "source": "MotherInnocent",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "107",
- "source": "Gribier",
- "target": "Fauchelevent",
- "data": {
- "value": 2
- }
- },
- {
- "id": "108",
- "source": "Mme.Burgon",
- "target": "Jondrette",
- "data": {
- "value": 1
- }
- },
- {
- "id": "109",
- "source": "Gavroche",
- "target": "Mme.Burgon",
- "data": {
- "value": 2
- }
- },
- {
- "id": "110",
- "source": "Gavroche",
- "target": "Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "111",
- "source": "Gavroche",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "112",
- "source": "Gavroche",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "113",
- "source": "Gillenormand",
- "target": "Cosette",
- "data": {
- "value": 3
- }
- },
- {
- "id": "114",
- "source": "Gillenormand",
- "target": "Valjean",
- "data": {
- "value": 2
- }
- },
- {
- "id": "115",
- "source": "Magnon",
- "target": "Gillenormand",
- "data": {
- "value": 1
- }
- },
- {
- "id": "116",
- "source": "Magnon",
- "target": "Mme.Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "117",
- "source": "Mlle.Gillenormand",
- "target": "Gillenormand",
- "data": {
- "value": 9
- }
- },
- {
- "id": "118",
- "source": "Mlle.Gillenormand",
- "target": "Cosette",
- "data": {
- "value": 2
- }
- },
- {
- "id": "119",
- "source": "Mlle.Gillenormand",
- "target": "Valjean",
- "data": {
- "value": 2
- }
- },
- {
- "id": "120",
- "source": "Mme.Pontmercy",
- "target": "Mlle.Gillenormand",
- "data": {
- "value": 1
- }
- },
- {
- "id": "121",
- "source": "Mme.Pontmercy",
- "target": "Pontmercy",
- "data": {
- "value": 1
- }
- },
- {
- "id": "122",
- "source": "Mlle.Vaubois",
- "target": "Mlle.Gillenormand",
- "data": {
- "value": 1
- }
- },
- {
- "id": "123",
- "source": "Lt.Gillenormand",
- "target": "Mlle.Gillenormand",
- "data": {
- "value": 2
- }
- },
- {
- "id": "124",
- "source": "Lt.Gillenormand",
- "target": "Gillenormand",
- "data": {
- "value": 1
- }
- },
- {
- "id": "125",
- "source": "Lt.Gillenormand",
- "target": "Cosette",
- "data": {
- "value": 1
- }
- },
- {
- "id": "126",
- "source": "Marius",
- "target": "Mlle.Gillenormand",
- "data": {
- "value": 6
- }
- },
- {
- "id": "127",
- "source": "Marius",
- "target": "Gillenormand",
- "data": {
- "value": 12
- }
- },
- {
- "id": "128",
- "source": "Marius",
- "target": "Pontmercy",
- "data": {
- "value": 1
- }
- },
- {
- "id": "129",
- "source": "Marius",
- "target": "Lt.Gillenormand",
- "data": {
- "value": 1
- }
- },
- {
- "id": "130",
- "source": "Marius",
- "target": "Cosette",
- "data": {
- "value": 21
- }
- },
- {
- "id": "131",
- "source": "Marius",
- "target": "Valjean",
- "data": {
- "value": 19
- }
- },
- {
- "id": "132",
- "source": "Marius",
- "target": "Tholomyes",
- "data": {
- "value": 1
- }
- },
- {
- "id": "133",
- "source": "Marius",
- "target": "Thenardier",
- "data": {
- "value": 2
- }
- },
- {
- "id": "134",
- "source": "Marius",
- "target": "Eponine",
- "data": {
- "value": 5
- }
- },
- {
- "id": "135",
- "source": "Marius",
- "target": "Gavroche",
- "data": {
- "value": 4
- }
- },
- {
- "id": "136",
- "source": "BaronessT",
- "target": "Gillenormand",
- "data": {
- "value": 1
- }
- },
- {
- "id": "137",
- "source": "BaronessT",
- "target": "Marius",
- "data": {
- "value": 1
- }
- },
- {
- "id": "138",
- "source": "Mabeuf",
- "target": "Marius",
- "data": {
- "value": 1
- }
- },
- {
- "id": "139",
- "source": "Mabeuf",
- "target": "Eponine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "140",
- "source": "Mabeuf",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "141",
- "source": "Enjolras",
- "target": "Marius",
- "data": {
- "value": 7
- }
- },
- {
- "id": "142",
- "source": "Enjolras",
- "target": "Gavroche",
- "data": {
- "value": 7
- }
- },
- {
- "id": "143",
- "source": "Enjolras",
- "target": "Javert",
- "data": {
- "value": 6
- }
- },
- {
- "id": "144",
- "source": "Enjolras",
- "target": "Mabeuf",
- "data": {
- "value": 1
- }
- },
- {
- "id": "145",
- "source": "Enjolras",
- "target": "Valjean",
- "data": {
- "value": 4
- }
- },
- {
- "id": "146",
- "source": "Combeferre",
- "target": "Enjolras",
- "data": {
- "value": 15
- }
- },
- {
- "id": "147",
- "source": "Combeferre",
- "target": "Marius",
- "data": {
- "value": 5
- }
- },
- {
- "id": "148",
- "source": "Combeferre",
- "target": "Gavroche",
- "data": {
- "value": 6
- }
- },
- {
- "id": "149",
- "source": "Combeferre",
- "target": "Mabeuf",
- "data": {
- "value": 2
- }
- },
- {
- "id": "150",
- "source": "Prouvaire",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "151",
- "source": "Prouvaire",
- "target": "Enjolras",
- "data": {
- "value": 4
- }
- },
- {
- "id": "152",
- "source": "Prouvaire",
- "target": "Combeferre",
- "data": {
- "value": 2
- }
- },
- {
- "id": "153",
- "source": "Feuilly",
- "target": "Gavroche",
- "data": {
- "value": 2
- }
- },
- {
- "id": "154",
- "source": "Feuilly",
- "target": "Enjolras",
- "data": {
- "value": 6
- }
- },
- {
- "id": "155",
- "source": "Feuilly",
- "target": "Prouvaire",
- "data": {
- "value": 2
- }
- },
- {
- "id": "156",
- "source": "Feuilly",
- "target": "Combeferre",
- "data": {
- "value": 5
- }
- },
- {
- "id": "157",
- "source": "Feuilly",
- "target": "Mabeuf",
- "data": {
- "value": 1
- }
- },
- {
- "id": "158",
- "source": "Feuilly",
- "target": "Marius",
- "data": {
- "value": 1
- }
- },
- {
- "id": "159",
- "source": "Courfeyrac",
- "target": "Marius",
- "data": {
- "value": 9
- }
- },
- {
- "id": "160",
- "source": "Courfeyrac",
- "target": "Enjolras",
- "data": {
- "value": 17
- }
- },
- {
- "id": "161",
- "source": "Courfeyrac",
- "target": "Combeferre",
- "data": {
- "value": 13
- }
- },
- {
- "id": "162",
- "source": "Courfeyrac",
- "target": "Gavroche",
- "data": {
- "value": 7
- }
- },
- {
- "id": "163",
- "source": "Courfeyrac",
- "target": "Mabeuf",
- "data": {
- "value": 2
- }
- },
- {
- "id": "164",
- "source": "Courfeyrac",
- "target": "Eponine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "165",
- "source": "Courfeyrac",
- "target": "Feuilly",
- "data": {
- "value": 6
- }
- },
- {
- "id": "166",
- "source": "Courfeyrac",
- "target": "Prouvaire",
- "data": {
- "value": 3
- }
- },
- {
- "id": "167",
- "source": "Bahorel",
- "target": "Combeferre",
- "data": {
- "value": 5
- }
- },
- {
- "id": "168",
- "source": "Bahorel",
- "target": "Gavroche",
- "data": {
- "value": 5
- }
- },
- {
- "id": "169",
- "source": "Bahorel",
- "target": "Courfeyrac",
- "data": {
- "value": 6
- }
- },
- {
- "id": "170",
- "source": "Bahorel",
- "target": "Mabeuf",
- "data": {
- "value": 2
- }
- },
- {
- "id": "171",
- "source": "Bahorel",
- "target": "Enjolras",
- "data": {
- "value": 4
- }
- },
- {
- "id": "172",
- "source": "Bahorel",
- "target": "Feuilly",
- "data": {
- "value": 3
- }
- },
- {
- "id": "173",
- "source": "Bahorel",
- "target": "Prouvaire",
- "data": {
- "value": 2
- }
- },
- {
- "id": "174",
- "source": "Bahorel",
- "target": "Marius",
- "data": {
- "value": 1
- }
- },
- {
- "id": "175",
- "source": "Bossuet",
- "target": "Marius",
- "data": {
- "value": 5
- }
- },
- {
- "id": "176",
- "source": "Bossuet",
- "target": "Courfeyrac",
- "data": {
- "value": 12
- }
- },
- {
- "id": "177",
- "source": "Bossuet",
- "target": "Gavroche",
- "data": {
- "value": 5
- }
- },
- {
- "id": "178",
- "source": "Bossuet",
- "target": "Bahorel",
- "data": {
- "value": 4
- }
- },
- {
- "id": "179",
- "source": "Bossuet",
- "target": "Enjolras",
- "data": {
- "value": 10
- }
- },
- {
- "id": "180",
- "source": "Bossuet",
- "target": "Feuilly",
- "data": {
- "value": 6
- }
- },
- {
- "id": "181",
- "source": "Bossuet",
- "target": "Prouvaire",
- "data": {
- "value": 2
- }
- },
- {
- "id": "182",
- "source": "Bossuet",
- "target": "Combeferre",
- "data": {
- "value": 9
- }
- },
- {
- "id": "183",
- "source": "Bossuet",
- "target": "Mabeuf",
- "data": {
- "value": 1
- }
- },
- {
- "id": "184",
- "source": "Bossuet",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "185",
- "source": "Joly",
- "target": "Bahorel",
- "data": {
- "value": 5
- }
- },
- {
- "id": "186",
- "source": "Joly",
- "target": "Bossuet",
- "data": {
- "value": 7
- }
- },
- {
- "id": "187",
- "source": "Joly",
- "target": "Gavroche",
- "data": {
- "value": 3
- }
- },
- {
- "id": "188",
- "source": "Joly",
- "target": "Courfeyrac",
- "data": {
- "value": 5
- }
- },
- {
- "id": "189",
- "source": "Joly",
- "target": "Enjolras",
- "data": {
- "value": 5
- }
- },
- {
- "id": "190",
- "source": "Joly",
- "target": "Feuilly",
- "data": {
- "value": 5
- }
- },
- {
- "id": "191",
- "source": "Joly",
- "target": "Prouvaire",
- "data": {
- "value": 2
- }
- },
- {
- "id": "192",
- "source": "Joly",
- "target": "Combeferre",
- "data": {
- "value": 5
- }
- },
- {
- "id": "193",
- "source": "Joly",
- "target": "Mabeuf",
- "data": {
- "value": 1
- }
- },
- {
- "id": "194",
- "source": "Joly",
- "target": "Marius",
- "data": {
- "value": 2
- }
- },
- {
- "id": "195",
- "source": "Grantaire",
- "target": "Bossuet",
- "data": {
- "value": 3
- }
- },
- {
- "id": "196",
- "source": "Grantaire",
- "target": "Enjolras",
- "data": {
- "value": 3
- }
- },
- {
- "id": "197",
- "source": "Grantaire",
- "target": "Combeferre",
- "data": {
- "value": 1
- }
- },
- {
- "id": "198",
- "source": "Grantaire",
- "target": "Courfeyrac",
- "data": {
- "value": 2
- }
- },
- {
- "id": "199",
- "source": "Grantaire",
- "target": "Joly",
- "data": {
- "value": 2
- }
- },
- {
- "id": "200",
- "source": "Grantaire",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "201",
- "source": "Grantaire",
- "target": "Bahorel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "202",
- "source": "Grantaire",
- "target": "Feuilly",
- "data": {
- "value": 1
- }
- },
- {
- "id": "203",
- "source": "Grantaire",
- "target": "Prouvaire",
- "data": {
- "value": 1
- }
- },
- {
- "id": "204",
- "source": "MotherPlutarch",
- "target": "Mabeuf",
- "data": {
- "value": 3
- }
- },
- {
- "id": "205",
- "source": "Gueulemer",
- "target": "Thenardier",
- "data": {
- "value": 5
- }
- },
- {
- "id": "206",
- "source": "Gueulemer",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "207",
- "source": "Gueulemer",
- "target": "Mme.Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "208",
- "source": "Gueulemer",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "209",
- "source": "Gueulemer",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "210",
- "source": "Gueulemer",
- "target": "Eponine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "211",
- "source": "Babet",
- "target": "Thenardier",
- "data": {
- "value": 6
- }
- },
- {
- "id": "212",
- "source": "Babet",
- "target": "Gueulemer",
- "data": {
- "value": 6
- }
- },
- {
- "id": "213",
- "source": "Babet",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "214",
- "source": "Babet",
- "target": "Mme.Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "215",
- "source": "Babet",
- "target": "Javert",
- "data": {
- "value": 2
- }
- },
- {
- "id": "216",
- "source": "Babet",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "217",
- "source": "Babet",
- "target": "Eponine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "218",
- "source": "Claquesous",
- "target": "Thenardier",
- "data": {
- "value": 4
- }
- },
- {
- "id": "219",
- "source": "Claquesous",
- "target": "Babet",
- "data": {
- "value": 4
- }
- },
- {
- "id": "220",
- "source": "Claquesous",
- "target": "Gueulemer",
- "data": {
- "value": 4
- }
- },
- {
- "id": "221",
- "source": "Claquesous",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "222",
- "source": "Claquesous",
- "target": "Mme.Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "223",
- "source": "Claquesous",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "224",
- "source": "Claquesous",
- "target": "Eponine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "225",
- "source": "Claquesous",
- "target": "Enjolras",
- "data": {
- "value": 1
- }
- },
- {
- "id": "226",
- "source": "Montparnasse",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "227",
- "source": "Montparnasse",
- "target": "Babet",
- "data": {
- "value": 2
- }
- },
- {
- "id": "228",
- "source": "Montparnasse",
- "target": "Gueulemer",
- "data": {
- "value": 2
- }
- },
- {
- "id": "229",
- "source": "Montparnasse",
- "target": "Claquesous",
- "data": {
- "value": 2
- }
- },
- {
- "id": "230",
- "source": "Montparnasse",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "231",
- "source": "Montparnasse",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "232",
- "source": "Montparnasse",
- "target": "Eponine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "233",
- "source": "Montparnasse",
- "target": "Thenardier",
- "data": {
- "value": 1
- }
- },
- {
- "id": "234",
- "source": "Toussaint",
- "target": "Cosette",
- "data": {
- "value": 2
- }
- },
- {
- "id": "235",
- "source": "Toussaint",
- "target": "Javert",
- "data": {
- "value": 1
- }
- },
- {
- "id": "236",
- "source": "Toussaint",
- "target": "Valjean",
- "data": {
- "value": 1
- }
- },
- {
- "id": "237",
- "source": "Child1",
- "target": "Gavroche",
- "data": {
- "value": 2
- }
- },
- {
- "id": "238",
- "source": "Child2",
- "target": "Gavroche",
- "data": {
- "value": 2
- }
- },
- {
- "id": "239",
- "source": "Child2",
- "target": "Child1",
- "data": {
- "value": 3
- }
- },
- {
- "id": "240",
- "source": "Brujon",
- "target": "Babet",
- "data": {
- "value": 3
- }
- },
- {
- "id": "241",
- "source": "Brujon",
- "target": "Gueulemer",
- "data": {
- "value": 3
- }
- },
- {
- "id": "242",
- "source": "Brujon",
- "target": "Thenardier",
- "data": {
- "value": 3
- }
- },
- {
- "id": "243",
- "source": "Brujon",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "244",
- "source": "Brujon",
- "target": "Eponine",
- "data": {
- "value": 1
- }
- },
- {
- "id": "245",
- "source": "Brujon",
- "target": "Claquesous",
- "data": {
- "value": 1
- }
- },
- {
- "id": "246",
- "source": "Brujon",
- "target": "Montparnasse",
- "data": {
- "value": 1
- }
- },
- {
- "id": "247",
- "source": "Mme.Hucheloup",
- "target": "Bossuet",
- "data": {
- "value": 1
- }
- },
- {
- "id": "248",
- "source": "Mme.Hucheloup",
- "target": "Joly",
- "data": {
- "value": 1
- }
- },
- {
- "id": "249",
- "source": "Mme.Hucheloup",
- "target": "Grantaire",
- "data": {
- "value": 1
- }
- },
- {
- "id": "250",
- "source": "Mme.Hucheloup",
- "target": "Bahorel",
- "data": {
- "value": 1
- }
- },
- {
- "id": "251",
- "source": "Mme.Hucheloup",
- "target": "Courfeyrac",
- "data": {
- "value": 1
- }
- },
- {
- "id": "252",
- "source": "Mme.Hucheloup",
- "target": "Gavroche",
- "data": {
- "value": 1
- }
- },
- {
- "id": "253",
- "source": "Mme.Hucheloup",
- "target": "Enjolras",
- "data": {
- "value": 1
- }
- }
+ { "id": "0", "source": "Napoleon", "target": "Myriel", "data": { "value": 1 } },
+ { "id": "1", "source": "Mlle.Baptistine", "target": "Myriel", "data": { "value": 8 } },
+ { "id": "2", "source": "Mme.Magloire", "target": "Myriel", "data": { "value": 10 } },
+ { "id": "3", "source": "Mme.Magloire", "target": "Mlle.Baptistine", "data": { "value": 6 } },
+ { "id": "4", "source": "CountessdeLo", "target": "Myriel", "data": { "value": 1 } },
+ { "id": "5", "source": "Geborand", "target": "Myriel", "data": { "value": 1 } },
+ { "id": "6", "source": "Champtercier", "target": "Myriel", "data": { "value": 1 } },
+ { "id": "7", "source": "Cravatte", "target": "Myriel", "data": { "value": 1 } },
+ { "id": "8", "source": "Count", "target": "Myriel", "data": { "value": 2 } },
+ { "id": "9", "source": "OldMan", "target": "Myriel", "data": { "value": 1 } },
+ { "id": "10", "source": "Valjean", "target": "Labarre", "data": { "value": 1 } },
+ { "id": "11", "source": "Valjean", "target": "Mme.Magloire", "data": { "value": 3 } },
+ { "id": "12", "source": "Valjean", "target": "Mlle.Baptistine", "data": { "value": 3 } },
+ { "id": "13", "source": "Valjean", "target": "Myriel", "data": { "value": 5 } },
+ { "id": "14", "source": "Marguerite", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "15", "source": "Mme.deR", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "16", "source": "Isabeau", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "17", "source": "Gervais", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "18", "source": "Listolier", "target": "Tholomyes", "data": { "value": 4 } },
+ { "id": "19", "source": "Fameuil", "target": "Tholomyes", "data": { "value": 4 } },
+ { "id": "20", "source": "Fameuil", "target": "Listolier", "data": { "value": 4 } },
+ { "id": "21", "source": "Blacheville", "target": "Tholomyes", "data": { "value": 4 } },
+ { "id": "22", "source": "Blacheville", "target": "Listolier", "data": { "value": 4 } },
+ { "id": "23", "source": "Blacheville", "target": "Fameuil", "data": { "value": 4 } },
+ { "id": "24", "source": "Favourite", "target": "Tholomyes", "data": { "value": 3 } },
+ { "id": "25", "source": "Favourite", "target": "Listolier", "data": { "value": 3 } },
+ { "id": "26", "source": "Favourite", "target": "Fameuil", "data": { "value": 3 } },
+ { "id": "27", "source": "Favourite", "target": "Blacheville", "data": { "value": 4 } },
+ { "id": "28", "source": "Dahlia", "target": "Tholomyes", "data": { "value": 3 } },
+ { "id": "29", "source": "Dahlia", "target": "Listolier", "data": { "value": 3 } },
+ { "id": "30", "source": "Dahlia", "target": "Fameuil", "data": { "value": 3 } },
+ { "id": "31", "source": "Dahlia", "target": "Blacheville", "data": { "value": 3 } },
+ { "id": "32", "source": "Dahlia", "target": "Favourite", "data": { "value": 5 } },
+ { "id": "33", "source": "Zephine", "target": "Tholomyes", "data": { "value": 3 } },
+ { "id": "34", "source": "Zephine", "target": "Listolier", "data": { "value": 3 } },
+ { "id": "35", "source": "Zephine", "target": "Fameuil", "data": { "value": 3 } },
+ { "id": "36", "source": "Zephine", "target": "Blacheville", "data": { "value": 3 } },
+ { "id": "37", "source": "Zephine", "target": "Favourite", "data": { "value": 4 } },
+ { "id": "38", "source": "Zephine", "target": "Dahlia", "data": { "value": 4 } },
+ { "id": "39", "source": "Fantine", "target": "Tholomyes", "data": { "value": 3 } },
+ { "id": "40", "source": "Fantine", "target": "Listolier", "data": { "value": 3 } },
+ { "id": "41", "source": "Fantine", "target": "Fameuil", "data": { "value": 3 } },
+ { "id": "42", "source": "Fantine", "target": "Blacheville", "data": { "value": 3 } },
+ { "id": "43", "source": "Fantine", "target": "Favourite", "data": { "value": 4 } },
+ { "id": "44", "source": "Fantine", "target": "Dahlia", "data": { "value": 4 } },
+ { "id": "45", "source": "Fantine", "target": "Zephine", "data": { "value": 4 } },
+ { "id": "46", "source": "Fantine", "target": "Marguerite", "data": { "value": 2 } },
+ { "id": "47", "source": "Fantine", "target": "Valjean", "data": { "value": 9 } },
+ { "id": "48", "source": "Mme.Thenardier", "target": "Fantine", "data": { "value": 2 } },
+ { "id": "49", "source": "Mme.Thenardier", "target": "Valjean", "data": { "value": 7 } },
+ { "id": "50", "source": "Thenardier", "target": "Mme.Thenardier", "data": { "value": 13 } },
+ { "id": "51", "source": "Thenardier", "target": "Fantine", "data": { "value": 1 } },
+ { "id": "52", "source": "Thenardier", "target": "Valjean", "data": { "value": 12 } },
+ { "id": "53", "source": "Cosette", "target": "Mme.Thenardier", "data": { "value": 4 } },
+ { "id": "54", "source": "Cosette", "target": "Valjean", "data": { "value": 31 } },
+ { "id": "55", "source": "Cosette", "target": "Tholomyes", "data": { "value": 1 } },
+ { "id": "56", "source": "Cosette", "target": "Thenardier", "data": { "value": 1 } },
+ { "id": "57", "source": "Javert", "target": "Valjean", "data": { "value": 17 } },
+ { "id": "58", "source": "Javert", "target": "Fantine", "data": { "value": 5 } },
+ { "id": "59", "source": "Javert", "target": "Thenardier", "data": { "value": 5 } },
+ { "id": "60", "source": "Javert", "target": "Mme.Thenardier", "data": { "value": 1 } },
+ { "id": "61", "source": "Javert", "target": "Cosette", "data": { "value": 1 } },
+ { "id": "62", "source": "Fauchelevent", "target": "Valjean", "data": { "value": 8 } },
+ { "id": "63", "source": "Fauchelevent", "target": "Javert", "data": { "value": 1 } },
+ { "id": "64", "source": "Bamatabois", "target": "Fantine", "data": { "value": 1 } },
+ { "id": "65", "source": "Bamatabois", "target": "Javert", "data": { "value": 1 } },
+ { "id": "66", "source": "Bamatabois", "target": "Valjean", "data": { "value": 2 } },
+ { "id": "67", "source": "Perpetue", "target": "Fantine", "data": { "value": 1 } },
+ { "id": "68", "source": "Simplice", "target": "Perpetue", "data": { "value": 2 } },
+ { "id": "69", "source": "Simplice", "target": "Valjean", "data": { "value": 3 } },
+ { "id": "70", "source": "Simplice", "target": "Fantine", "data": { "value": 2 } },
+ { "id": "71", "source": "Simplice", "target": "Javert", "data": { "value": 1 } },
+ { "id": "72", "source": "Scaufflaire", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "73", "source": "Woman1", "target": "Valjean", "data": { "value": 2 } },
+ { "id": "74", "source": "Woman1", "target": "Javert", "data": { "value": 1 } },
+ { "id": "75", "source": "Judge", "target": "Valjean", "data": { "value": 3 } },
+ { "id": "76", "source": "Judge", "target": "Bamatabois", "data": { "value": 2 } },
+ { "id": "77", "source": "Champmathieu", "target": "Valjean", "data": { "value": 3 } },
+ { "id": "78", "source": "Champmathieu", "target": "Judge", "data": { "value": 3 } },
+ { "id": "79", "source": "Champmathieu", "target": "Bamatabois", "data": { "value": 2 } },
+ { "id": "80", "source": "Brevet", "target": "Judge", "data": { "value": 2 } },
+ { "id": "81", "source": "Brevet", "target": "Champmathieu", "data": { "value": 2 } },
+ { "id": "82", "source": "Brevet", "target": "Valjean", "data": { "value": 2 } },
+ { "id": "83", "source": "Brevet", "target": "Bamatabois", "data": { "value": 1 } },
+ { "id": "84", "source": "Chenildieu", "target": "Judge", "data": { "value": 2 } },
+ { "id": "85", "source": "Chenildieu", "target": "Champmathieu", "data": { "value": 2 } },
+ { "id": "86", "source": "Chenildieu", "target": "Brevet", "data": { "value": 2 } },
+ { "id": "87", "source": "Chenildieu", "target": "Valjean", "data": { "value": 2 } },
+ { "id": "88", "source": "Chenildieu", "target": "Bamatabois", "data": { "value": 1 } },
+ { "id": "89", "source": "Cochepaille", "target": "Judge", "data": { "value": 2 } },
+ { "id": "90", "source": "Cochepaille", "target": "Champmathieu", "data": { "value": 2 } },
+ { "id": "91", "source": "Cochepaille", "target": "Brevet", "data": { "value": 2 } },
+ { "id": "92", "source": "Cochepaille", "target": "Chenildieu", "data": { "value": 2 } },
+ { "id": "93", "source": "Cochepaille", "target": "Valjean", "data": { "value": 2 } },
+ { "id": "94", "source": "Cochepaille", "target": "Bamatabois", "data": { "value": 1 } },
+ { "id": "95", "source": "Pontmercy", "target": "Thenardier", "data": { "value": 1 } },
+ { "id": "96", "source": "Boulatruelle", "target": "Thenardier", "data": { "value": 1 } },
+ { "id": "97", "source": "Eponine", "target": "Mme.Thenardier", "data": { "value": 2 } },
+ { "id": "98", "source": "Eponine", "target": "Thenardier", "data": { "value": 3 } },
+ { "id": "99", "source": "Anzelma", "target": "Eponine", "data": { "value": 2 } },
+ { "id": "100", "source": "Anzelma", "target": "Thenardier", "data": { "value": 2 } },
+ { "id": "101", "source": "Anzelma", "target": "Mme.Thenardier", "data": { "value": 1 } },
+ { "id": "102", "source": "Woman2", "target": "Valjean", "data": { "value": 3 } },
+ { "id": "103", "source": "Woman2", "target": "Cosette", "data": { "value": 1 } },
+ { "id": "104", "source": "Woman2", "target": "Javert", "data": { "value": 1 } },
+ { "id": "105", "source": "MotherInnocent", "target": "Fauchelevent", "data": { "value": 3 } },
+ { "id": "106", "source": "MotherInnocent", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "107", "source": "Gribier", "target": "Fauchelevent", "data": { "value": 2 } },
+ { "id": "108", "source": "Mme.Burgon", "target": "Jondrette", "data": { "value": 1 } },
+ { "id": "109", "source": "Gavroche", "target": "Mme.Burgon", "data": { "value": 2 } },
+ { "id": "110", "source": "Gavroche", "target": "Thenardier", "data": { "value": 1 } },
+ { "id": "111", "source": "Gavroche", "target": "Javert", "data": { "value": 1 } },
+ { "id": "112", "source": "Gavroche", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "113", "source": "Gillenormand", "target": "Cosette", "data": { "value": 3 } },
+ { "id": "114", "source": "Gillenormand", "target": "Valjean", "data": { "value": 2 } },
+ { "id": "115", "source": "Magnon", "target": "Gillenormand", "data": { "value": 1 } },
+ { "id": "116", "source": "Magnon", "target": "Mme.Thenardier", "data": { "value": 1 } },
+ { "id": "117", "source": "Mlle.Gillenormand", "target": "Gillenormand", "data": { "value": 9 } },
+ { "id": "118", "source": "Mlle.Gillenormand", "target": "Cosette", "data": { "value": 2 } },
+ { "id": "119", "source": "Mlle.Gillenormand", "target": "Valjean", "data": { "value": 2 } },
+ { "id": "120", "source": "Mme.Pontmercy", "target": "Mlle.Gillenormand", "data": { "value": 1 } },
+ { "id": "121", "source": "Mme.Pontmercy", "target": "Pontmercy", "data": { "value": 1 } },
+ { "id": "122", "source": "Mlle.Vaubois", "target": "Mlle.Gillenormand", "data": { "value": 1 } },
+ { "id": "123", "source": "Lt.Gillenormand", "target": "Mlle.Gillenormand", "data": { "value": 2 } },
+ { "id": "124", "source": "Lt.Gillenormand", "target": "Gillenormand", "data": { "value": 1 } },
+ { "id": "125", "source": "Lt.Gillenormand", "target": "Cosette", "data": { "value": 1 } },
+ { "id": "126", "source": "Marius", "target": "Mlle.Gillenormand", "data": { "value": 6 } },
+ { "id": "127", "source": "Marius", "target": "Gillenormand", "data": { "value": 12 } },
+ { "id": "128", "source": "Marius", "target": "Pontmercy", "data": { "value": 1 } },
+ { "id": "129", "source": "Marius", "target": "Lt.Gillenormand", "data": { "value": 1 } },
+ { "id": "130", "source": "Marius", "target": "Cosette", "data": { "value": 21 } },
+ { "id": "131", "source": "Marius", "target": "Valjean", "data": { "value": 19 } },
+ { "id": "132", "source": "Marius", "target": "Tholomyes", "data": { "value": 1 } },
+ { "id": "133", "source": "Marius", "target": "Thenardier", "data": { "value": 2 } },
+ { "id": "134", "source": "Marius", "target": "Eponine", "data": { "value": 5 } },
+ { "id": "135", "source": "Marius", "target": "Gavroche", "data": { "value": 4 } },
+ { "id": "136", "source": "BaronessT", "target": "Gillenormand", "data": { "value": 1 } },
+ { "id": "137", "source": "BaronessT", "target": "Marius", "data": { "value": 1 } },
+ { "id": "138", "source": "Mabeuf", "target": "Marius", "data": { "value": 1 } },
+ { "id": "139", "source": "Mabeuf", "target": "Eponine", "data": { "value": 1 } },
+ { "id": "140", "source": "Mabeuf", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "141", "source": "Enjolras", "target": "Marius", "data": { "value": 7 } },
+ { "id": "142", "source": "Enjolras", "target": "Gavroche", "data": { "value": 7 } },
+ { "id": "143", "source": "Enjolras", "target": "Javert", "data": { "value": 6 } },
+ { "id": "144", "source": "Enjolras", "target": "Mabeuf", "data": { "value": 1 } },
+ { "id": "145", "source": "Enjolras", "target": "Valjean", "data": { "value": 4 } },
+ { "id": "146", "source": "Combeferre", "target": "Enjolras", "data": { "value": 15 } },
+ { "id": "147", "source": "Combeferre", "target": "Marius", "data": { "value": 5 } },
+ { "id": "148", "source": "Combeferre", "target": "Gavroche", "data": { "value": 6 } },
+ { "id": "149", "source": "Combeferre", "target": "Mabeuf", "data": { "value": 2 } },
+ { "id": "150", "source": "Prouvaire", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "151", "source": "Prouvaire", "target": "Enjolras", "data": { "value": 4 } },
+ { "id": "152", "source": "Prouvaire", "target": "Combeferre", "data": { "value": 2 } },
+ { "id": "153", "source": "Feuilly", "target": "Gavroche", "data": { "value": 2 } },
+ { "id": "154", "source": "Feuilly", "target": "Enjolras", "data": { "value": 6 } },
+ { "id": "155", "source": "Feuilly", "target": "Prouvaire", "data": { "value": 2 } },
+ { "id": "156", "source": "Feuilly", "target": "Combeferre", "data": { "value": 5 } },
+ { "id": "157", "source": "Feuilly", "target": "Mabeuf", "data": { "value": 1 } },
+ { "id": "158", "source": "Feuilly", "target": "Marius", "data": { "value": 1 } },
+ { "id": "159", "source": "Courfeyrac", "target": "Marius", "data": { "value": 9 } },
+ { "id": "160", "source": "Courfeyrac", "target": "Enjolras", "data": { "value": 17 } },
+ { "id": "161", "source": "Courfeyrac", "target": "Combeferre", "data": { "value": 13 } },
+ { "id": "162", "source": "Courfeyrac", "target": "Gavroche", "data": { "value": 7 } },
+ { "id": "163", "source": "Courfeyrac", "target": "Mabeuf", "data": { "value": 2 } },
+ { "id": "164", "source": "Courfeyrac", "target": "Eponine", "data": { "value": 1 } },
+ { "id": "165", "source": "Courfeyrac", "target": "Feuilly", "data": { "value": 6 } },
+ { "id": "166", "source": "Courfeyrac", "target": "Prouvaire", "data": { "value": 3 } },
+ { "id": "167", "source": "Bahorel", "target": "Combeferre", "data": { "value": 5 } },
+ { "id": "168", "source": "Bahorel", "target": "Gavroche", "data": { "value": 5 } },
+ { "id": "169", "source": "Bahorel", "target": "Courfeyrac", "data": { "value": 6 } },
+ { "id": "170", "source": "Bahorel", "target": "Mabeuf", "data": { "value": 2 } },
+ { "id": "171", "source": "Bahorel", "target": "Enjolras", "data": { "value": 4 } },
+ { "id": "172", "source": "Bahorel", "target": "Feuilly", "data": { "value": 3 } },
+ { "id": "173", "source": "Bahorel", "target": "Prouvaire", "data": { "value": 2 } },
+ { "id": "174", "source": "Bahorel", "target": "Marius", "data": { "value": 1 } },
+ { "id": "175", "source": "Bossuet", "target": "Marius", "data": { "value": 5 } },
+ { "id": "176", "source": "Bossuet", "target": "Courfeyrac", "data": { "value": 12 } },
+ { "id": "177", "source": "Bossuet", "target": "Gavroche", "data": { "value": 5 } },
+ { "id": "178", "source": "Bossuet", "target": "Bahorel", "data": { "value": 4 } },
+ { "id": "179", "source": "Bossuet", "target": "Enjolras", "data": { "value": 10 } },
+ { "id": "180", "source": "Bossuet", "target": "Feuilly", "data": { "value": 6 } },
+ { "id": "181", "source": "Bossuet", "target": "Prouvaire", "data": { "value": 2 } },
+ { "id": "182", "source": "Bossuet", "target": "Combeferre", "data": { "value": 9 } },
+ { "id": "183", "source": "Bossuet", "target": "Mabeuf", "data": { "value": 1 } },
+ { "id": "184", "source": "Bossuet", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "185", "source": "Joly", "target": "Bahorel", "data": { "value": 5 } },
+ { "id": "186", "source": "Joly", "target": "Bossuet", "data": { "value": 7 } },
+ { "id": "187", "source": "Joly", "target": "Gavroche", "data": { "value": 3 } },
+ { "id": "188", "source": "Joly", "target": "Courfeyrac", "data": { "value": 5 } },
+ { "id": "189", "source": "Joly", "target": "Enjolras", "data": { "value": 5 } },
+ { "id": "190", "source": "Joly", "target": "Feuilly", "data": { "value": 5 } },
+ { "id": "191", "source": "Joly", "target": "Prouvaire", "data": { "value": 2 } },
+ { "id": "192", "source": "Joly", "target": "Combeferre", "data": { "value": 5 } },
+ { "id": "193", "source": "Joly", "target": "Mabeuf", "data": { "value": 1 } },
+ { "id": "194", "source": "Joly", "target": "Marius", "data": { "value": 2 } },
+ { "id": "195", "source": "Grantaire", "target": "Bossuet", "data": { "value": 3 } },
+ { "id": "196", "source": "Grantaire", "target": "Enjolras", "data": { "value": 3 } },
+ { "id": "197", "source": "Grantaire", "target": "Combeferre", "data": { "value": 1 } },
+ { "id": "198", "source": "Grantaire", "target": "Courfeyrac", "data": { "value": 2 } },
+ { "id": "199", "source": "Grantaire", "target": "Joly", "data": { "value": 2 } },
+ { "id": "200", "source": "Grantaire", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "201", "source": "Grantaire", "target": "Bahorel", "data": { "value": 1 } },
+ { "id": "202", "source": "Grantaire", "target": "Feuilly", "data": { "value": 1 } },
+ { "id": "203", "source": "Grantaire", "target": "Prouvaire", "data": { "value": 1 } },
+ { "id": "204", "source": "MotherPlutarch", "target": "Mabeuf", "data": { "value": 3 } },
+ { "id": "205", "source": "Gueulemer", "target": "Thenardier", "data": { "value": 5 } },
+ { "id": "206", "source": "Gueulemer", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "207", "source": "Gueulemer", "target": "Mme.Thenardier", "data": { "value": 1 } },
+ { "id": "208", "source": "Gueulemer", "target": "Javert", "data": { "value": 1 } },
+ { "id": "209", "source": "Gueulemer", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "210", "source": "Gueulemer", "target": "Eponine", "data": { "value": 1 } },
+ { "id": "211", "source": "Babet", "target": "Thenardier", "data": { "value": 6 } },
+ { "id": "212", "source": "Babet", "target": "Gueulemer", "data": { "value": 6 } },
+ { "id": "213", "source": "Babet", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "214", "source": "Babet", "target": "Mme.Thenardier", "data": { "value": 1 } },
+ { "id": "215", "source": "Babet", "target": "Javert", "data": { "value": 2 } },
+ { "id": "216", "source": "Babet", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "217", "source": "Babet", "target": "Eponine", "data": { "value": 1 } },
+ { "id": "218", "source": "Claquesous", "target": "Thenardier", "data": { "value": 4 } },
+ { "id": "219", "source": "Claquesous", "target": "Babet", "data": { "value": 4 } },
+ { "id": "220", "source": "Claquesous", "target": "Gueulemer", "data": { "value": 4 } },
+ { "id": "221", "source": "Claquesous", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "222", "source": "Claquesous", "target": "Mme.Thenardier", "data": { "value": 1 } },
+ { "id": "223", "source": "Claquesous", "target": "Javert", "data": { "value": 1 } },
+ { "id": "224", "source": "Claquesous", "target": "Eponine", "data": { "value": 1 } },
+ { "id": "225", "source": "Claquesous", "target": "Enjolras", "data": { "value": 1 } },
+ { "id": "226", "source": "Montparnasse", "target": "Javert", "data": { "value": 1 } },
+ { "id": "227", "source": "Montparnasse", "target": "Babet", "data": { "value": 2 } },
+ { "id": "228", "source": "Montparnasse", "target": "Gueulemer", "data": { "value": 2 } },
+ { "id": "229", "source": "Montparnasse", "target": "Claquesous", "data": { "value": 2 } },
+ { "id": "230", "source": "Montparnasse", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "231", "source": "Montparnasse", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "232", "source": "Montparnasse", "target": "Eponine", "data": { "value": 1 } },
+ { "id": "233", "source": "Montparnasse", "target": "Thenardier", "data": { "value": 1 } },
+ { "id": "234", "source": "Toussaint", "target": "Cosette", "data": { "value": 2 } },
+ { "id": "235", "source": "Toussaint", "target": "Javert", "data": { "value": 1 } },
+ { "id": "236", "source": "Toussaint", "target": "Valjean", "data": { "value": 1 } },
+ { "id": "237", "source": "Child1", "target": "Gavroche", "data": { "value": 2 } },
+ { "id": "238", "source": "Child2", "target": "Gavroche", "data": { "value": 2 } },
+ { "id": "239", "source": "Child2", "target": "Child1", "data": { "value": 3 } },
+ { "id": "240", "source": "Brujon", "target": "Babet", "data": { "value": 3 } },
+ { "id": "241", "source": "Brujon", "target": "Gueulemer", "data": { "value": 3 } },
+ { "id": "242", "source": "Brujon", "target": "Thenardier", "data": { "value": 3 } },
+ { "id": "243", "source": "Brujon", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "244", "source": "Brujon", "target": "Eponine", "data": { "value": 1 } },
+ { "id": "245", "source": "Brujon", "target": "Claquesous", "data": { "value": 1 } },
+ { "id": "246", "source": "Brujon", "target": "Montparnasse", "data": { "value": 1 } },
+ { "id": "247", "source": "Mme.Hucheloup", "target": "Bossuet", "data": { "value": 1 } },
+ { "id": "248", "source": "Mme.Hucheloup", "target": "Joly", "data": { "value": 1 } },
+ { "id": "249", "source": "Mme.Hucheloup", "target": "Grantaire", "data": { "value": 1 } },
+ { "id": "250", "source": "Mme.Hucheloup", "target": "Bahorel", "data": { "value": 1 } },
+ { "id": "251", "source": "Mme.Hucheloup", "target": "Courfeyrac", "data": { "value": 1 } },
+ { "id": "252", "source": "Mme.Hucheloup", "target": "Gavroche", "data": { "value": 1 } },
+ { "id": "253", "source": "Mme.Hucheloup", "target": "Enjolras", "data": { "value": 1 } }
]
}
diff --git a/packages/g6/__tests__/demos/index.ts b/packages/g6/__tests__/demos/index.ts
index 48f190d296..dac2f306d4 100644
--- a/packages/g6/__tests__/demos/index.ts
+++ b/packages/g6/__tests__/demos/index.ts
@@ -113,6 +113,7 @@ export { pluginBackground } from './plugin-background';
export { pluginBubbleSets } from './plugin-bubble-sets';
export { pluginCameraSetting } from './plugin-camera-setting';
export { pluginContextmenu } from './plugin-contextmenu';
+export { pluginEdgeFilterLens } from './plugin-edge-filter-lens';
export { pluginFullscreen } from './plugin-fullscreen';
export { pluginGridLine } from './plugin-grid-line';
export { pluginHistory } from './plugin-history';
diff --git a/packages/g6/__tests__/demos/plugin-edge-filter-lens.ts b/packages/g6/__tests__/demos/plugin-edge-filter-lens.ts
new file mode 100644
index 0000000000..c69d209dde
--- /dev/null
+++ b/packages/g6/__tests__/demos/plugin-edge-filter-lens.ts
@@ -0,0 +1,33 @@
+import data from '@@/dataset/relations.json';
+import { Graph } from '@antv/g6';
+
+export const pluginEdgeFilterLens: TestCase = async (context) => {
+ const graph = new Graph({
+ ...context,
+ data,
+ node: {
+ style: { size: 16 },
+ palette: {
+ field: (datum) => Math.floor(Number(datum.style?.y) / 60),
+ },
+ },
+ edge: {
+ style: {
+ label: false,
+ labelText: (d) => d.data!.value?.toString(),
+ stroke: '#ccc',
+ },
+ },
+ plugins: [
+ {
+ key: 'edge-filter-lens',
+ type: 'edge-filter-lens',
+ },
+ ],
+ autoFit: 'view',
+ });
+
+ await graph.render();
+
+ return graph;
+};
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/default.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/default.svg
new file mode 100644
index 0000000000..ddc9c88670
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/default.svg
@@ -0,0 +1,2172 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-1.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-1.svg
new file mode 100644
index 0000000000..88855fc8ae
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-1.svg
@@ -0,0 +1,2182 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-2.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-2.svg
new file mode 100644
index 0000000000..46b4583941
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-click-2.svg
@@ -0,0 +1,2234 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-drag.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-drag.svg
new file mode 100644
index 0000000000..28d1d2d9e6
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-drag.svg
@@ -0,0 +1,2234 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-pointermove.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-pointermove.svg
new file mode 100644
index 0000000000..46b4583941
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/move-lens-pointermove.svg
@@ -0,0 +1,2234 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-either.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-either.svg
new file mode 100644
index 0000000000..20c1f86ded
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-either.svg
@@ -0,0 +1,2210 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-source.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-source.svg
new file mode 100644
index 0000000000..4fe795d317
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-source.svg
@@ -0,0 +1,2196 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-target.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-target.svg
new file mode 100644
index 0000000000..5293ab7135
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/node-type-target.svg
@@ -0,0 +1,2196 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-larger.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-larger.svg
new file mode 100644
index 0000000000..e76d198c61
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-larger.svg
@@ -0,0 +1,2234 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-smaller.svg b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-smaller.svg
new file mode 100644
index 0000000000..69a6824d1f
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/edge-filter-lens/scale-smaller.svg
@@ -0,0 +1,2234 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/unit/plugins/edge-filter-lens.spec.ts b/packages/g6/__tests__/unit/plugins/edge-filter-lens.spec.ts
new file mode 100644
index 0000000000..3810ab6c96
--- /dev/null
+++ b/packages/g6/__tests__/unit/plugins/edge-filter-lens.spec.ts
@@ -0,0 +1,77 @@
+import { pluginEdgeFilterLens } from '@@/demos';
+import { CommonEvent, Graph } from '@antv/g6';
+import { createDemoGraph, dispatchCanvasEvent } from '../../utils';
+
+describe('edge-filter-lens', () => {
+ let graph: Graph;
+
+ beforeAll(async () => {
+ graph = await createDemoGraph(pluginEdgeFilterLens, { animation: false });
+ });
+
+ afterAll(() => {
+ graph.destroy();
+ });
+
+ it('move lens by pointermove', async () => {
+ await expect(graph).toMatchSnapshot(__filename);
+
+ dispatchCanvasEvent(graph, CommonEvent.POINTER_MOVE, { canvas: { x: 200, y: 100 } });
+
+ await expect(graph).toMatchSnapshot(__filename, 'move-lens-pointermove');
+ });
+
+ it('move lens by click', async () => {
+ graph.updatePlugin({ key: 'edge-filter-lens', trigger: 'click' });
+
+ dispatchCanvasEvent(graph, CommonEvent.CLICK, { canvas: { x: 180, y: 100 } });
+ await expect(graph).toMatchSnapshot(__filename, 'move-lens-click-1');
+
+ dispatchCanvasEvent(graph, CommonEvent.CLICK, { canvas: { x: 200, y: 100 } });
+ await expect(graph).toMatchSnapshot(__filename, 'move-lens-click-2');
+ });
+
+ it('move lens by drag', async () => {
+ graph.updatePlugin({ key: 'edge-filter-lens', trigger: 'drag' });
+
+ dispatchCanvasEvent(graph, CommonEvent.CLICK, { canvas: { x: 180, y: 100 } });
+ dispatchCanvasEvent(graph, CommonEvent.DRAG_START, { canvas: { x: 200, y: 100 } });
+ dispatchCanvasEvent(graph, CommonEvent.DRAG, { canvas: { x: 220, y: 100 } });
+ dispatchCanvasEvent(graph, CommonEvent.DRAG_END);
+
+ await expect(graph).toMatchSnapshot(__filename, 'move-lens-drag');
+ });
+
+ it('scale lens by wheel', async () => {
+ function emitWheelEvent(options?: { deltaX: number; deltaY: number; clientX: number; clientY: number }) {
+ const dom = graph.getCanvas().getContextService().getDomElement();
+ dom?.dispatchEvent(new WheelEvent(CommonEvent.WHEEL, options));
+ }
+
+ emitWheelEvent({ deltaX: 1, deltaY: 2, clientX: 200, clientY: 100 });
+ emitWheelEvent({ deltaX: 1, deltaY: 2, clientX: 200, clientY: 100 });
+
+ await expect(graph).toMatchSnapshot(__filename, 'scale-larger');
+
+ emitWheelEvent({ deltaX: -1, deltaY: -2, clientX: 200, clientY: 100 });
+ emitWheelEvent({ deltaX: -1, deltaY: -2, clientX: 200, clientY: 100 });
+ emitWheelEvent({ deltaX: -1, deltaY: -2, clientX: 200, clientY: 100 });
+ emitWheelEvent({ deltaX: -1, deltaY: -2, clientX: 200, clientY: 100 });
+
+ await expect(graph).toMatchSnapshot(__filename, 'scale-smaller');
+ });
+
+ it('show edge when only its source/target node in lens', async () => {
+ graph.updatePlugin({ key: 'edge-filter-lens', trigger: 'click', nodeType: 'source' });
+ dispatchCanvasEvent(graph, CommonEvent.CLICK, { canvas: { x: 200, y: 200 } });
+ await expect(graph).toMatchSnapshot(__filename, 'node-type-source');
+
+ graph.updatePlugin({ key: 'edge-filter-lens', trigger: 'click', nodeType: 'target' });
+ dispatchCanvasEvent(graph, CommonEvent.CLICK, { canvas: { x: 200, y: 200 } });
+ await expect(graph).toMatchSnapshot(__filename, 'node-type-target');
+
+ graph.updatePlugin({ key: 'edge-filter-lens', trigger: 'click', nodeType: 'either' });
+ dispatchCanvasEvent(graph, CommonEvent.CLICK, { canvas: { x: 200, y: 200 } });
+ await expect(graph).toMatchSnapshot(__filename, 'node-type-either');
+ });
+});
diff --git a/packages/g6/src/exports.ts b/packages/g6/src/exports.ts
index 154a39c453..80df619c4b 100644
--- a/packages/g6/src/exports.ts
+++ b/packages/g6/src/exports.ts
@@ -67,6 +67,7 @@ export {
BubbleSets,
CameraSetting,
Contextmenu,
+ EdgeFilterLens,
Fullscreen,
GridLine,
History,
@@ -168,6 +169,7 @@ export type {
BubbleSetsOptions,
CameraSettingOptions,
ContextmenuOptions,
+ EdgeFilterLensOptions,
FullscreenOptions,
GridLineOptions,
HistoryOptions,
diff --git a/packages/g6/src/plugins/edge-filter-lens/index.ts b/packages/g6/src/plugins/edge-filter-lens/index.ts
new file mode 100644
index 0000000000..1125e5cbd7
--- /dev/null
+++ b/packages/g6/src/plugins/edge-filter-lens/index.ts
@@ -0,0 +1,391 @@
+import type { BaseStyleProps } from '@antv/g';
+import { CommonEvent } from '../../constants';
+import { Circle } from '../../elements';
+import type { RuntimeContext } from '../../runtime/types';
+import type { EdgeData, GraphData, NodeData } from '../../spec';
+import type { EdgeStyle } from '../../spec/element/edge';
+import type { NodeStyle } from '../../spec/element/node';
+import type {
+ Element,
+ ElementDatum,
+ ElementType,
+ ID,
+ IDragEvent,
+ IPointerEvent,
+ Point,
+ PointObject,
+} from '../../types';
+import { idOf } from '../../utils/id';
+import { parsePoint } from '../../utils/point';
+import { positionOf } from '../../utils/position';
+import { distance } from '../../utils/vector';
+import type { BasePluginOptions } from '../base-plugin';
+import { BasePlugin } from '../base-plugin';
+
+export interface EdgeFilterLensOptions extends BasePluginOptions {
+ /**
+ * 移动透镜的方式
+ * - `'pointermove'`:始终跟随鼠标移动
+ * - `'click'`:鼠标点击时透镜移动
+ * - `'drag'`:拖拽透镜
+ *
+ * The way to move the lens
+ * - `'pointermove'`: always follow the mouse movement
+ * - `'click'`: move the lens when the mouse clicks
+ * - `'drag'`: drag the lens
+ * @defaultValue 'pointermove'
+ */
+ trigger?: 'pointermove' | 'click' | 'drag';
+ /**
+ * 透镜的半径
+ *
+ * The radius of the lens
+ * @defaultValue 60
+ */
+ r?: number;
+ /**
+ * 透镜的最大半径。只有在开启 `scaleRByWheel` 时生效
+ *
+ * The maximum radius of the lens. Only valid when `scaleRByWheel` is enabled
+ * @defaultValue canvas 宽高最小值的一半
+ */
+ maxR?: number;
+ /**
+ * 透镜的最小半径。只有在开启 `scaleRByWheel` 时生效
+ *
+ * The minimum radius of the lens. Only valid when `scaleRByWheel` is enabled
+ * @defaultValue 0
+ */
+ minR?: number;
+ /**
+ * 是否通过滚轮缩放透镜的半径
+ *
+ * Whether to scale the radius of the lens by wheel
+ * @defaultValue true
+ */
+ scaleRByWheel?: boolean;
+ /**
+ * 边显示的条件
+ * - `'both'`:只有起始节点和目标节点都在透镜中时,边才会显示
+ * - `'source'`:只有起始节点在透镜中时,边才会显示
+ * - `'target'`:只有目标节点在透镜中时,边才会显示
+ * - `'either'`:只要起始节点或目标节点有一个在透镜中时,边就会显示
+ *
+ * The condition for displaying the edge
+ * - `'both'`: The edge is displayed only when both the source node and the target node are in the lens
+ * - `'source'`: The edge is displayed only when the source node is in the lens
+ * - `'target'`: The edge is displayed only when the target node is in the lens
+ * - `'either'`: The edge is displayed when either the source node or the target node is in the lens
+ * @defaultValue 'both'
+ */
+ nodeType?: 'both' | 'source' | 'target' | 'either';
+ /**
+ * 过滤出始终不在透镜中显示的元素
+ *
+ * Filter elements that are never displayed in the lens
+ * @param id - 元素的 id | The id of the element
+ * @param elementType - 元素的类型 | The type of the element
+ * @returns 是否显示 | Whether to display
+ */
+ filter?: (id: ID, elementType: ElementType) => boolean;
+ /**
+ * 透镜的样式
+ *
+ * The style of the lens
+ */
+ style?: BaseStyleProps;
+ /**
+ * 在透镜中节点的样式
+ *
+ * The style of the nodes displayed in the lens
+ */
+ nodeStyle?: NodeStyle | ((datum: NodeData) => NodeStyle);
+ /**
+ * 在透镜中边的样式
+ *
+ * The style of the edges displayed in the lens
+ */
+ edgeStyle?: EdgeStyle | ((datum: EdgeData) => EdgeStyle);
+ /**
+ * 是否阻止默认事件
+ *
+ * Whether to prevent the default event
+ * @defaultValue true
+ */
+ preventDefault?: boolean;
+}
+
+const defaultLensStyle: BaseStyleProps = {
+ fill: '#fff',
+ fillOpacity: 1,
+ lineWidth: 1,
+ stroke: '#000',
+ strokeOpacity: 0.8,
+};
+
+const DELTA = 0.05;
+
+export class EdgeFilterLens extends BasePlugin {
+ static defaultOptions: Partial = {
+ trigger: 'pointermove',
+ r: 60,
+ nodeType: 'both',
+ filter: () => true,
+ style: { lineWidth: 2 },
+ nodeStyle: { label: false },
+ edgeStyle: { label: true },
+ scaleRByWheel: true,
+ preventDefault: true,
+ };
+
+ constructor(context: RuntimeContext, options: EdgeFilterLensOptions) {
+ super(context, Object.assign({}, EdgeFilterLens.defaultOptions, options));
+ this.bindEvents();
+ }
+
+ private lens!: Circle;
+
+ private shapes = new Map();
+
+ private r = this.options.r;
+
+ private get canvas() {
+ return this.context.canvas.getLayer('transient');
+ }
+
+ private get isLensOn() {
+ return this.lens && !this.lens.destroyed;
+ }
+
+ protected onEdgeFilter = (event: IPointerEvent) => {
+ if (this.options.trigger === 'drag' && this.isLensOn) return;
+
+ const origin = parsePoint(event.canvas as PointObject);
+ this.renderLens(origin);
+ this.renderFocusElements();
+ };
+
+ private renderLens = (origin: Point) => {
+ const [x, y] = origin;
+ const positionStyle = { size: this.r * 2, x, y };
+
+ if (!this.isLensOn) {
+ const style = Object.assign({}, defaultLensStyle, this.options.style, positionStyle);
+ this.lens = new Circle({ style });
+ } else {
+ this.lens.update(positionStyle);
+ }
+
+ this.canvas.appendChild(this.lens);
+ };
+
+ private getFilterData = (): Required => {
+ const { filter } = this.options;
+ const { model } = this.context;
+ const data = model.getData();
+
+ if (!filter) return data;
+
+ const { nodes, edges, combos } = data;
+
+ return {
+ nodes: nodes.filter((node) => filter(idOf(node), 'node')),
+ edges: edges.filter((edge) => filter(idOf(edge), 'edge')),
+ combos: combos.filter((combo) => filter(idOf(combo), 'combo')),
+ };
+ };
+
+ private getFocusElements = (origin: Point) => {
+ const { nodes, edges } = this.getFilterData();
+
+ const focusNodes = nodes.filter((datum) => distance(positionOf(datum), origin) < this.r);
+ const focusNodeIds = focusNodes.map((node) => idOf(node));
+
+ const focusEdges = edges.filter((datum) => {
+ const { source, target } = datum;
+ const isSourceFocus = focusNodeIds.includes(source);
+ const isTargetFocus = focusNodeIds.includes(target);
+
+ switch (this.options.nodeType) {
+ case 'both':
+ return isSourceFocus && isTargetFocus;
+ case 'either':
+ return isSourceFocus !== isTargetFocus;
+ case 'source':
+ return isSourceFocus && !isTargetFocus;
+ case 'target':
+ return !isSourceFocus && isTargetFocus;
+ default:
+ return false;
+ }
+ });
+
+ return { nodes: focusNodes, edges: focusEdges };
+ };
+
+ private renderFocusElements = () => {
+ const { element, graph } = this.context;
+ if (!this.isLensOn) return;
+
+ const origin = this.lens.getCenter();
+ const { nodes, edges } = this.getFocusElements(origin);
+
+ const ids = new Set();
+
+ const { nodeStyle, edgeStyle } = this.options;
+
+ const iterate = (datum: ElementDatum) => {
+ const id = idOf(datum);
+ ids.add(id);
+
+ const shape = element!.getElement(id);
+ if (!shape) return;
+
+ const cloneShape = this.shapes.get(id) || shape.cloneNode();
+
+ cloneShape.setPosition(shape.getPosition());
+ cloneShape.id = shape.id;
+
+ if (!this.shapes.has(id)) {
+ this.canvas.appendChild(cloneShape);
+ this.shapes.set(id, cloneShape);
+ } else {
+ Object.entries(shape.attributes).forEach(([key, value]) => {
+ if (cloneShape.style[key] !== value) cloneShape.style[key] = value;
+ });
+ }
+
+ const elementType = graph.getElementType(id) as Exclude;
+ const style = this.getElementStyle(elementType, datum);
+
+ // @ts-ignore
+ cloneShape.update(style);
+ };
+
+ nodes.forEach(iterate);
+ edges.forEach(iterate);
+
+ this.shapes.forEach((shape, id) => {
+ if (!ids.has(id)) {
+ shape.destroy();
+ this.shapes.delete(id);
+ }
+ });
+ };
+
+ private getElementStyle(elementType: ElementType, datum: ElementDatum) {
+ const styler = elementType === 'node' ? this.options.nodeStyle : this.options.edgeStyle;
+ if (typeof styler === 'function') return styler(datum as any);
+ return styler;
+ }
+
+ private scaleRByWheel = (event: WheelEvent) => {
+ if (this.options.preventDefault) event.preventDefault();
+ const { clientX, clientY, deltaX, deltaY } = event;
+ const { graph, canvas } = this.context;
+ const scaleOrigin = graph.getCanvasByClient([clientX, clientY]);
+ const origin = this.lens?.getCenter();
+
+ if (!this.isLensOn || distance(scaleOrigin, origin) > this.r) {
+ return;
+ }
+
+ const { maxR, minR } = this.options;
+ const ratio = deltaX + deltaY > 0 ? 1 / (1 - DELTA) : 1 - DELTA;
+ const canvasR = Math.min(...canvas.getSize()) / 2;
+ this.r = Math.max(minR || 0, Math.min(maxR || canvasR, this.r * ratio));
+
+ this.renderLens(origin);
+ this.renderFocusElements();
+ };
+
+ get graphDom() {
+ return this.context.graph.getCanvas().getContextService().getDomElement();
+ }
+
+ private isLensDragging = false;
+
+ private onDragStart = (event: IDragEvent) => {
+ const dragOrigin = parsePoint(event.canvas as PointObject);
+ const origin = this.lens?.getCenter();
+
+ if (!this.isLensOn || distance(dragOrigin, origin) > this.r) return;
+
+ this.isLensDragging = true;
+ };
+
+ private onDrag = (event: IDragEvent) => {
+ if (!this.isLensDragging) return;
+
+ const dragOrigin = parsePoint(event.canvas as PointObject);
+ this.renderLens(dragOrigin);
+ this.renderFocusElements();
+ };
+
+ private onDragEnd = () => {
+ this.isLensDragging = false;
+ };
+
+ private bindEvents() {
+ const { graph } = this.context;
+ const { trigger, scaleRByWheel } = this.options;
+
+ const canvas = graph.getCanvas().getLayer();
+
+ if (['click', 'drag'].includes(trigger)) {
+ canvas.addEventListener(CommonEvent.CLICK, this.onEdgeFilter);
+ }
+
+ if (trigger === 'pointermove') {
+ canvas.addEventListener(CommonEvent.POINTER_MOVE, this.onEdgeFilter);
+ } else if (trigger === 'drag') {
+ canvas.addEventListener(CommonEvent.DRAG_START, this.onDragStart);
+ canvas.addEventListener(CommonEvent.DRAG, this.onDrag);
+ canvas.addEventListener(CommonEvent.DRAG_END, this.onDragEnd);
+ }
+
+ if (scaleRByWheel) {
+ this.graphDom?.addEventListener(CommonEvent.WHEEL, this.scaleRByWheel, { passive: false });
+ }
+ }
+
+ private unbindEvents() {
+ const { graph } = this.context;
+ const { trigger, scaleRByWheel } = this.options;
+ const canvas = graph.getCanvas().getLayer();
+
+ if (['click', 'drag'].includes(trigger)) {
+ canvas.removeEventListener(CommonEvent.CLICK, this.onEdgeFilter);
+ }
+
+ if (trigger === 'pointermove') {
+ canvas.removeEventListener(CommonEvent.POINTER_MOVE, this.onEdgeFilter);
+ } else if (trigger === 'drag') {
+ canvas.removeEventListener(CommonEvent.DRAG_START, this.onDragStart);
+ canvas.removeEventListener(CommonEvent.DRAG, this.onDrag);
+ canvas.removeEventListener(CommonEvent.DRAG_END, this.onDragEnd);
+ }
+
+ if (scaleRByWheel) {
+ this.graphDom?.removeEventListener(CommonEvent.WHEEL, this.scaleRByWheel);
+ }
+ }
+
+ public update(options: Partial) {
+ this.unbindEvents();
+ super.update(options);
+ this.bindEvents();
+ }
+
+ public destroy() {
+ this.unbindEvents();
+ if (this.isLensOn) {
+ this.lens.destroy();
+ }
+ this.shapes.forEach((shape, id) => {
+ shape.destroy();
+ this.shapes.delete(id);
+ });
+ super.destroy();
+ }
+}
diff --git a/packages/g6/src/plugins/index.ts b/packages/g6/src/plugins/index.ts
index 2035982c09..cbee7c712a 100644
--- a/packages/g6/src/plugins/index.ts
+++ b/packages/g6/src/plugins/index.ts
@@ -3,6 +3,7 @@ export { BasePlugin } from './base-plugin';
export { BubbleSets } from './bubble-sets';
export { CameraSetting } from './camera-setting';
export { Contextmenu } from './contextmenu';
+export { EdgeFilterLens } from './edge-filter-lens';
export { Fullscreen } from './fullscreen';
export { GridLine } from './grid-line';
export { History } from './history';
@@ -20,6 +21,7 @@ export type { BasePluginOptions } from './base-plugin';
export type { BubbleSetsOptions } from './bubble-sets';
export type { CameraSettingOptions } from './camera-setting';
export type { ContextmenuOptions } from './contextmenu';
+export type { EdgeFilterLensOptions } from './edge-filter-lens';
export type { FullscreenOptions } from './fullscreen';
export type { GridLineOptions } from './grid-line';
export type { HistoryOptions } from './history';
diff --git a/packages/g6/src/registry/build-in.ts b/packages/g6/src/registry/build-in.ts
index b093c53c80..340c03cc07 100644
--- a/packages/g6/src/registry/build-in.ts
+++ b/packages/g6/src/registry/build-in.ts
@@ -59,6 +59,7 @@ import {
Background,
BubbleSets,
Contextmenu,
+ EdgeFilterLens,
Fullscreen,
GridLine,
History,
@@ -169,6 +170,7 @@ const BUILT_IN_EXTENSIONS: ExtensionRegistry = {
},
plugin: {
'bubble-sets': BubbleSets,
+ 'edge-filter-lens': EdgeFilterLens,
'grid-line': GridLine,
background: Background,
contextmenu: Contextmenu,
diff --git a/packages/site/examples/plugin/edge-filter-lens/demo/basic.js b/packages/site/examples/plugin/edge-filter-lens/demo/basic.js
new file mode 100644
index 0000000000..0998597b0a
--- /dev/null
+++ b/packages/site/examples/plugin/edge-filter-lens/demo/basic.js
@@ -0,0 +1,67 @@
+import { Graph } from '@antv/g6';
+
+fetch('https://assets.antv.antgroup.com/g6/relations.json')
+ .then((res) => res.json())
+ .then((data) => {
+ const graph = new Graph({
+ container: 'container',
+ data,
+ autoFit: 'view',
+ node: {
+ style: { size: 16 },
+ palette: {
+ field: (datum) => Math.floor(datum.style?.y / 60),
+ },
+ },
+ edge: {
+ style: {
+ label: false,
+ labelText: (d) => d.data.value?.toString(),
+ stroke: '#ccc',
+ },
+ },
+ plugins: [
+ {
+ key: 'edge-filter-lens',
+ type: 'edge-filter-lens',
+ },
+ ],
+ });
+ graph.render();
+
+ const config = {
+ trigger: 'pointermove',
+ scaleRByWheel: true,
+ nodeType: 'both',
+ };
+
+ window.addPanel((gui) => {
+ gui
+ .add(config, 'trigger', ['pointermove', 'click', 'drag'])
+ .name('Trigger')
+ .onChange((value) => {
+ graph.updatePlugin({
+ key: 'edge-filter-lens',
+ trigger: value,
+ });
+ });
+ gui
+ .add(config, 'scaleRByWheel')
+ .name('Scale R by Wheel')
+ .onChange((value) => {
+ graph.updatePlugin({
+ key: 'edge-filter-lens',
+ scaleRByWheel: value,
+ });
+ });
+ gui
+ .add(config, 'nodeType', ['source', 'target', 'both', 'either'])
+ .name('Node Type')
+ .onChange((value) => {
+ graph.updatePlugin({
+ key: 'edge-filter-lens',
+ nodeType: value,
+ });
+ });
+ });
+ });
diff --git a/packages/site/examples/plugin/edge-filter-lens/demo/meta.json b/packages/site/examples/plugin/edge-filter-lens/demo/meta.json
new file mode 100644
index 0000000000..3ebce69fed
--- /dev/null
+++ b/packages/site/examples/plugin/edge-filter-lens/demo/meta.json
@@ -0,0 +1,16 @@
+{
+ "title": {
+ "zh": "中文分类",
+ "en": "Category"
+ },
+ "demos": [
+ {
+ "filename": "basic.js",
+ "title": {
+ "zh": "边过滤镜",
+ "en": "Edge Filter Lens"
+ },
+ "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*62FuSY-LFEIAAAAAAAAAAAAADmJ7AQ/original"
+ }
+ ]
+}
diff --git a/packages/site/examples/plugin/edge-filter-lens/index.en.md b/packages/site/examples/plugin/edge-filter-lens/index.en.md
new file mode 100644
index 0000000000..38da7b0f18
--- /dev/null
+++ b/packages/site/examples/plugin/edge-filter-lens/index.en.md
@@ -0,0 +1,5 @@
+---
+title: EdgeFilterLens
+---
+
+EdgeFilterLens can keep the focused edges within the lens range, while other edges will not be displayed within that range.
diff --git a/packages/site/examples/plugin/edge-filter-lens/index.zh.md b/packages/site/examples/plugin/edge-filter-lens/index.zh.md
new file mode 100644
index 0000000000..593e6218d7
--- /dev/null
+++ b/packages/site/examples/plugin/edge-filter-lens/index.zh.md
@@ -0,0 +1,5 @@
+---
+title: EdgeFilterLens 边过滤镜
+---
+
+边过滤镜可以将关注的边保留在过滤镜范围内,其他边将在该范围内不显示。