Compare commits
2 commits
main
...
fix_visual
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0fee5252b | ||
|
|
77a4b914e1 |
30 changed files with 2755 additions and 72 deletions
141
cognee-frontend/package-lock.json
generated
141
cognee-frontend/package-lock.json
generated
|
|
@ -13,9 +13,13 @@
|
||||||
"culori": "^4.0.1",
|
"culori": "^4.0.1",
|
||||||
"d3-force-3d": "^3.0.6",
|
"d3-force-3d": "^3.0.6",
|
||||||
"next": "15.3.3",
|
"next": "15.3.3",
|
||||||
|
"ngraph.forcelayout": "^3.3.1",
|
||||||
|
"ngraph.graph": "^20.1.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-force-graph-2d": "^1.27.1",
|
"react-force-graph-2d": "^1.27.1",
|
||||||
|
"three": "^0.175.0",
|
||||||
|
"troika-three-text": "^0.52.4",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -25,6 +29,7 @@
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@types/three": "^0.175.0",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "^15.3.3",
|
"eslint-config-next": "^15.3.3",
|
||||||
|
|
@ -1305,12 +1310,44 @@
|
||||||
"@types/react": "^18.0.0"
|
"@types/react": "^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/stats.js": {
|
||||||
|
"version": "0.17.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
|
||||||
|
"integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/three": {
|
||||||
|
"version": "0.175.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.175.0.tgz",
|
||||||
|
"integrity": "sha512-ldMSBgtZOZ3g9kJ3kOZSEtZIEITmJOzu8eKVpkhf036GuNkM4mt0NXecrjCn5tMm1OblOF7dZehlaDypBfNokw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@tweenjs/tween.js": "~23.1.3",
|
||||||
|
"@types/stats.js": "*",
|
||||||
|
"@types/webxr": "*",
|
||||||
|
"@webgpu/types": "*",
|
||||||
|
"fflate": "~0.8.2",
|
||||||
|
"meshoptimizer": "~0.18.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/three/node_modules/@tweenjs/tween.js": {
|
||||||
|
"version": "23.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
|
||||||
|
"integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/uuid": {
|
"node_modules/@types/uuid": {
|
||||||
"version": "9.0.8",
|
"version": "9.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
|
||||||
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
|
"integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/webxr": {
|
||||||
|
"version": "0.5.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
||||||
|
"integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.38.0",
|
"version": "8.38.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
|
||||||
|
|
@ -1834,6 +1871,12 @@
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@webgpu/types": {
|
||||||
|
"version": "0.1.66",
|
||||||
|
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.66.tgz",
|
||||||
|
"integrity": "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/accessor-fn": {
|
"node_modules/accessor-fn": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz",
|
||||||
|
|
@ -2124,6 +2167,14 @@
|
||||||
"url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md"
|
"url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bidi-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||||
|
"dependencies": {
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.12",
|
"version": "1.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||||
|
|
@ -3381,6 +3432,12 @@
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fflate": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/file-entry-cache": {
|
"node_modules/file-entry-cache": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||||
|
|
@ -4652,6 +4709,12 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/meshoptimizer": {
|
||||||
|
"version": "0.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz",
|
||||||
|
"integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
|
|
@ -4846,6 +4909,39 @@
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ngraph.events": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-NeDGI4DSyjBNBRtA86222JoYietsmCXbs8CEB0dZ51Xeh4lhVl1y3wpWLumczvnha8sFQIW4E0vvVWwgmX2mGw=="
|
||||||
|
},
|
||||||
|
"node_modules/ngraph.forcelayout": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngraph.forcelayout/-/ngraph.forcelayout-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-MKBuEh1wujyQHFTW57y5vd/uuEOK0XfXYxm3lC7kktjJLRdt/KEKEknyOlc6tjXflqBKEuYBBcu7Ax5VY+S6aw==",
|
||||||
|
"dependencies": {
|
||||||
|
"ngraph.events": "^1.0.0",
|
||||||
|
"ngraph.merge": "^1.0.0",
|
||||||
|
"ngraph.random": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ngraph.graph": {
|
||||||
|
"version": "20.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngraph.graph/-/ngraph.graph-20.1.0.tgz",
|
||||||
|
"integrity": "sha512-1jorNgIc0Kg0L9bTNN4+RCrVvbZ+4pqGVMrbhX3LLyqYcRdLvAQRRnxddmfj9l5f6Eq59SUTfbYZEm8cktiE7Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"ngraph.events": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ngraph.merge": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngraph.merge/-/ngraph.merge-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5J8YjGITUJeapsomtTALYsw7rFveYkM+lBj3QiYZ79EymQcuri65Nw3knQtFxQBU1r5iOaVRXrSwMENUPK62Vg=="
|
||||||
|
},
|
||||||
|
"node_modules/ngraph.random": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngraph.random/-/ngraph.random-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-4EUeAGbB2HWX9njd6bP6tciN6ByJfoaAvmVL9QTaZSeXrW46eNGA9GajiXiPBbvFqxUWFkEbyo6x5qsACUuVfA=="
|
||||||
|
},
|
||||||
"node_modules/oauth4webapi": {
|
"node_modules/oauth4webapi": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.6.0.tgz",
|
||||||
|
|
@ -5275,6 +5371,14 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.10",
|
"version": "1.22.10",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||||
|
|
@ -5858,6 +5962,11 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/three": {
|
||||||
|
"version": "0.175.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/three/-/three-0.175.0.tgz",
|
||||||
|
"integrity": "sha512-nNE3pnTHxXN/Phw768u0Grr7W4+rumGg/H6PgeseNJojkJtmeHJfZWi41Gp2mpXl1pg1pf1zjwR4McM1jTqkpg=="
|
||||||
|
},
|
||||||
"node_modules/tinycolor2": {
|
"node_modules/tinycolor2": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||||
|
|
@ -5917,6 +6026,33 @@
|
||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/troika-three-text": {
|
||||||
|
"version": "0.52.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
|
||||||
|
"integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
|
||||||
|
"dependencies": {
|
||||||
|
"bidi-js": "^1.0.2",
|
||||||
|
"troika-three-utils": "^0.52.4",
|
||||||
|
"troika-worker-utils": "^0.52.0",
|
||||||
|
"webgl-sdf-generator": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"three": ">=0.125.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/troika-three-utils": {
|
||||||
|
"version": "0.52.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
|
||||||
|
"integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"three": ">=0.125.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/troika-worker-utils": {
|
||||||
|
"version": "0.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
|
||||||
|
"integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw=="
|
||||||
|
},
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
||||||
|
|
@ -6132,6 +6268,11 @@
|
||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webgl-sdf-generator": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA=="
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,11 @@
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-force-graph-2d": "^1.27.1",
|
"react-force-graph-2d": "^1.27.1",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1",
|
||||||
|
"ngraph.forcelayout": "^3.3.1",
|
||||||
|
"ngraph.graph": "^20.1.0",
|
||||||
|
"three": "^0.175.0",
|
||||||
|
"troika-three-text": "^0.52.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
|
|
@ -27,6 +31,7 @@
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
|
"@types/three": "^0.175.0",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "^15.3.3",
|
"eslint-config-next": "^15.3.3",
|
||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import { ChangeEvent, useCallback, useEffect, useState } from "react";
|
import { ChangeEvent, useCallback, useEffect, useState } from "react";
|
||||||
import { useBoolean } from "@/utils";
|
import { useBoolean } from "@/utils";
|
||||||
import { Accordion, CTAButton, GhostButton, IconButton, Input, Modal, PopupMenu } from "@/ui/elements";
|
import { Accordion, CTAButton, GhostButton, IconButton, Input, Modal, PopupMenu } from "@/ui/elements";
|
||||||
|
|
@ -258,15 +259,12 @@ export default function DatasetsAccordion({
|
||||||
tools={(
|
tools={(
|
||||||
<IconButton className="relative">
|
<IconButton className="relative">
|
||||||
<PopupMenu>
|
<PopupMenu>
|
||||||
<div className="flex flex-col gap-0.5">
|
<div className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer relative">
|
||||||
<div className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer relative">
|
<input tabIndex={-1} type="file" multiple onChange={handleAddFiles.bind(null, dataset)} className="absolute w-full h-full cursor-pointer opacity-0" />
|
||||||
<input tabIndex={-1} type="file" multiple onChange={handleAddFiles.bind(null, dataset)} className="absolute w-full h-full cursor-pointer opacity-0" />
|
<span>add data</span>
|
||||||
<span>add data</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-0.5 items-start">
|
|
||||||
<div onClick={() => handleDatasetRemove(dataset)} className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer">delete</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Link target="_blank" href={`/visualize/${dataset.id}`}>visualize</Link>
|
||||||
|
<div onClick={() => handleDatasetRemove(dataset)} className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer">delete</div>
|
||||||
</PopupMenu>
|
</PopupMenu>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
38
cognee-frontend/src/app/visualize/[datasetId]/page.tsx
Normal file
38
cognee-frontend/src/app/visualize/[datasetId]/page.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { fetch } from "@/utils";
|
||||||
|
|
||||||
|
import { Edge, Node } from "@/ui/rendering/graph/types";
|
||||||
|
import GraphVisualization from "@/ui/elements/GraphVisualization";
|
||||||
|
|
||||||
|
interface VisualizePageProps {
|
||||||
|
params: { datasetId: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page({ params }: VisualizePageProps) {
|
||||||
|
const [graphData, setGraphData] = useState<{ nodes: Node[], edges: Edge[] }>();
|
||||||
|
useEffect(() => {
|
||||||
|
async function getData() {
|
||||||
|
const datasetId = (await params).datasetId;
|
||||||
|
const response = await fetch(`/v1/datasets/${datasetId}/graph`);
|
||||||
|
const newGraphData = await response.json();
|
||||||
|
setGraphData(newGraphData);
|
||||||
|
}
|
||||||
|
getData();
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen">
|
||||||
|
{graphData && (
|
||||||
|
<GraphVisualization
|
||||||
|
nodes={graphData.nodes}
|
||||||
|
edges={graphData.edges}
|
||||||
|
config={{
|
||||||
|
fontSize: 10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
35
cognee-frontend/src/ui/elements/GraphVisualization.tsx
Normal file
35
cognee-frontend/src/ui/elements/GraphVisualization.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
import { Edge, Node } from "@/ui/rendering/graph/types";
|
||||||
|
import animate from "@/ui/rendering/animate";
|
||||||
|
|
||||||
|
interface GraphVisualizationProps {
|
||||||
|
nodes: Node[];
|
||||||
|
edges: Edge[];
|
||||||
|
className?: string;
|
||||||
|
config?: { fontSize: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function GraphVisualization({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
className,
|
||||||
|
config,
|
||||||
|
}: GraphVisualizationProps) {
|
||||||
|
const visualizationRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const visualizationContainer = visualizationRef.current;
|
||||||
|
|
||||||
|
if (visualizationContainer) {
|
||||||
|
animate(nodes, edges, visualizationContainer, config);
|
||||||
|
}
|
||||||
|
}, [config, edges, nodes]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames("min-w-full min-h-full", className)} ref={visualizationRef} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,16 +2,15 @@
|
||||||
|
|
||||||
import { v4 as uuid4 } from "uuid";
|
import { v4 as uuid4 } from "uuid";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Fragment, MouseEvent, MutableRefObject, useCallback, useEffect, useRef, useState } from "react";
|
import { Fragment, MouseEvent, useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { useModal } from "@/ui/elements/Modal";
|
import { useModal } from "@/ui/elements/Modal";
|
||||||
import { CaretIcon, CloseIcon, PlusIcon } from "@/ui/Icons";
|
import { CaretIcon, CloseIcon, PlusIcon } from "@/ui/Icons";
|
||||||
import { IconButton, PopupMenu, TextArea, Modal, GhostButton, CTAButton } from "@/ui/elements";
|
import { IconButton, PopupMenu, TextArea, Modal, GhostButton, CTAButton } from "@/ui/elements";
|
||||||
import { GraphControlsAPI } from "@/app/(graph)/GraphControls";
|
|
||||||
import GraphVisualization, { GraphVisualizationAPI } from "@/app/(graph)/GraphVisualization";
|
|
||||||
|
|
||||||
import NotebookCellHeader from "./NotebookCellHeader";
|
import NotebookCellHeader from "./NotebookCellHeader";
|
||||||
import { Cell, Notebook as NotebookType } from "./types";
|
import { Cell, Notebook as NotebookType } from "./types";
|
||||||
|
import GraphVisualization from "../GraphVisualization";
|
||||||
|
|
||||||
interface NotebookProps {
|
interface NotebookProps {
|
||||||
notebook: NotebookType;
|
notebook: NotebookType;
|
||||||
|
|
@ -282,25 +281,23 @@ export default function Notebook({ notebook, updateNotebook, runCell }: Notebook
|
||||||
function CellResult({ content }: { content: [] }) {
|
function CellResult({ content }: { content: [] }) {
|
||||||
const parsedContent = [];
|
const parsedContent = [];
|
||||||
|
|
||||||
const graphRef = useRef<GraphVisualizationAPI>();
|
|
||||||
const graphControls = useRef<GraphControlsAPI>({
|
|
||||||
setSelectedNode: () => {},
|
|
||||||
getSelectedNode: () => null,
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const line of content) {
|
for (const line of content) {
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(line)) {
|
if (Array.isArray(line)) {
|
||||||
// Insights search returns uncommon graph data structure
|
// Insights search returns uncommon graph data structure
|
||||||
if (Array.from(line).length > 0 && Array.isArray(line[0]) && line[0][1]["relationship_name"]) {
|
if (Array.from(line).length > 0 && Array.isArray(line[0]) && line[0][1]["relationship_name"]) {
|
||||||
|
const data = transformInsightsGraphData(line);
|
||||||
|
|
||||||
parsedContent.push(
|
parsedContent.push(
|
||||||
<div key={line[0][1]["relationship_name"]} className="w-full h-full bg-white">
|
<div key={line[0][1]["relationship_name"]} className="flex flex-col w-full h-full min-h-80 bg-white">
|
||||||
<span className="text-sm pl-2 mb-4">reasoning graph</span>
|
<span className="text-sm pl-2 mb-4">reasoning graph</span>
|
||||||
<GraphVisualization
|
<GraphVisualization
|
||||||
data={transformInsightsGraphData(line)}
|
nodes={data.nodes}
|
||||||
ref={graphRef as MutableRefObject<GraphVisualizationAPI>}
|
edges={data.edges}
|
||||||
graphControls={graphControls}
|
className="flex-1"
|
||||||
className="min-h-80"
|
config={{
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -342,13 +339,15 @@ function CellResult({ content }: { content: [] }) {
|
||||||
if (typeof item === "object" && item["graphs"] && typeof item["graphs"] === "object") {
|
if (typeof item === "object" && item["graphs"] && typeof item["graphs"] === "object") {
|
||||||
Object.entries<{ nodes: []; edges: []; }>(item["graphs"]).forEach(([datasetName, graph]) => {
|
Object.entries<{ nodes: []; edges: []; }>(item["graphs"]).forEach(([datasetName, graph]) => {
|
||||||
parsedContent.push(
|
parsedContent.push(
|
||||||
<div key={datasetName} className="w-full h-full bg-white">
|
<div key={datasetName} className="flex flex-col w-full h-full min-h-80 bg-white">
|
||||||
<span className="text-sm pl-2 mb-4">reasoning graph (datasets: {datasetName})</span>
|
<span className="text-sm pl-2 mb-4">reasoning graph (datasets: {datasetName})</span>
|
||||||
<GraphVisualization
|
<GraphVisualization
|
||||||
data={transformToVisualizationData(graph)}
|
nodes={graph.nodes}
|
||||||
ref={graphRef as MutableRefObject<GraphVisualizationAPI>}
|
edges={graph.edges}
|
||||||
graphControls={graphControls}
|
className="flex-1"
|
||||||
className="min-h-80"
|
config={{
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -373,13 +372,15 @@ function CellResult({ content }: { content: [] }) {
|
||||||
if (typeof(line) === "object" && line["graphs"]) {
|
if (typeof(line) === "object" && line["graphs"]) {
|
||||||
Object.entries<{ nodes: []; edges: []; }>(line["graphs"]).forEach(([datasetName, graph]) => {
|
Object.entries<{ nodes: []; edges: []; }>(line["graphs"]).forEach(([datasetName, graph]) => {
|
||||||
parsedContent.push(
|
parsedContent.push(
|
||||||
<div key={datasetName} className="w-full h-full bg-white">
|
<div key={datasetName} className="flex flex-col w-full h-full min-h-80 bg-white">
|
||||||
<span className="text-sm pl-2 mb-4">reasoning graph (datasets: {datasetName})</span>
|
<span className="text-sm pl-2 mb-4">reasoning graph (datasets: {datasetName})</span>
|
||||||
<GraphVisualization
|
<GraphVisualization
|
||||||
data={transformToVisualizationData(graph)}
|
nodes={graph.nodes}
|
||||||
ref={graphRef as MutableRefObject<GraphVisualizationAPI>}
|
edges={graph.edges}
|
||||||
graphControls={graphControls}
|
className="flex-1"
|
||||||
className="min-h-80"
|
config={{
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -418,13 +419,6 @@ function CellResult({ content }: { content: [] }) {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function transformToVisualizationData(graph: { nodes: [], edges: [] }) {
|
|
||||||
return {
|
|
||||||
nodes: graph.nodes,
|
|
||||||
links: graph.edges,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type Triplet = [{
|
type Triplet = [{
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
|
|
@ -445,8 +439,9 @@ function transformInsightsGraphData(triplets: Triplet[]) {
|
||||||
type: string,
|
type: string,
|
||||||
}
|
}
|
||||||
} = {};
|
} = {};
|
||||||
const links: {
|
const edges: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
id: string,
|
||||||
source: string,
|
source: string,
|
||||||
target: string,
|
target: string,
|
||||||
label: string,
|
label: string,
|
||||||
|
|
@ -465,7 +460,8 @@ function transformInsightsGraphData(triplets: Triplet[]) {
|
||||||
type: triplet[2].type,
|
type: triplet[2].type,
|
||||||
};
|
};
|
||||||
const linkKey = `${triplet[0]["id"]}_${triplet[1]["relationship_name"]}_${triplet[2]["id"]}`;
|
const linkKey = `${triplet[0]["id"]}_${triplet[1]["relationship_name"]}_${triplet[2]["id"]}`;
|
||||||
links[linkKey] = {
|
edges[linkKey] = {
|
||||||
|
id: linkKey,
|
||||||
source: triplet[0].id,
|
source: triplet[0].id,
|
||||||
target: triplet[2].id,
|
target: triplet[2].id,
|
||||||
label: triplet[1]["relationship_name"],
|
label: triplet[1]["relationship_name"],
|
||||||
|
|
@ -474,6 +470,6 @@ function transformInsightsGraphData(triplets: Triplet[]) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nodes: Object.values(nodes),
|
nodes: Object.values(nodes),
|
||||||
links: Object.values(links),
|
edges: Object.values(edges),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
445
cognee-frontend/src/ui/rendering/animate.ts
Normal file
445
cognee-frontend/src/ui/rendering/animate.ts
Normal file
|
|
@ -0,0 +1,445 @@
|
||||||
|
import { Graph, Node as GraphNode, Link as GraphLink } from "ngraph.graph";
|
||||||
|
import {
|
||||||
|
Color,
|
||||||
|
DataTexture,
|
||||||
|
OrthographicCamera,
|
||||||
|
RGBAFormat,
|
||||||
|
Scene,
|
||||||
|
UnsignedByteType,
|
||||||
|
Vector2,
|
||||||
|
WebGLRenderer,
|
||||||
|
WebGLRenderTarget,
|
||||||
|
} from "three";
|
||||||
|
import { OrbitControls } from "three/examples/jsm/Addons.js";
|
||||||
|
import createForceLayout, { Layout } from "ngraph.forcelayout";
|
||||||
|
|
||||||
|
import { Edge, Node } from "./graph/types";
|
||||||
|
import createGraph from "./graph/createGraph";
|
||||||
|
|
||||||
|
import createLabel from "./meshes/createLabel";
|
||||||
|
import pickNodeIndex from "./picking/pickNodeIndex";
|
||||||
|
import createEdgeMesh from "./meshes/createEdgeMesh";
|
||||||
|
import createPickingMesh from "./meshes/createPickingMesh";
|
||||||
|
import createNodeSwarmMesh from "./meshes/createNodeSwarmMesh";
|
||||||
|
import createNodePositionsTexture from "./textures/createNodePositionsTexture";
|
||||||
|
import createDensityRenderTarget from "./render-targets/createDensityRenderTarget";
|
||||||
|
import createDensityAccumulatorMesh from "./meshes/createDensityAccumulatorMesh";
|
||||||
|
import createMetaballMesh from "./meshes/createMetaballMesh";
|
||||||
|
|
||||||
|
const INITIAL_CAMERA_DISTANCE = 2000;
|
||||||
|
|
||||||
|
interface Config {
|
||||||
|
fontSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function animate(
|
||||||
|
nodes: Node[],
|
||||||
|
edges: Edge[],
|
||||||
|
parentElement: HTMLElement,
|
||||||
|
config?: Config
|
||||||
|
): void {
|
||||||
|
const nodeLabelMap = new Map();
|
||||||
|
const edgeLabelMap = new Map();
|
||||||
|
const colorPalette = [
|
||||||
|
new Color("#5C10F4"),
|
||||||
|
new Color("#A550FF"),
|
||||||
|
new Color("#0DFF00"),
|
||||||
|
new Color("#F4F4F4"),
|
||||||
|
new Color("#D8D8D8"),
|
||||||
|
];
|
||||||
|
let lastColorIndex = 0;
|
||||||
|
const colorPerType = new Map();
|
||||||
|
|
||||||
|
function getColorForType(nodeType: string): Color {
|
||||||
|
if (colorPerType.has(nodeType)) {
|
||||||
|
return colorPerType.get(nodeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
const color = colorPalette[lastColorIndex % colorPalette.length];
|
||||||
|
colorPerType.set(nodeType, color);
|
||||||
|
lastColorIndex += 1;
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mousePosition = new Vector2();
|
||||||
|
|
||||||
|
// Node related data
|
||||||
|
const nodeColors = new Float32Array(nodes.length * 3);
|
||||||
|
const nodeIndices = new Map();
|
||||||
|
const textureSize = Math.ceil(Math.sqrt(nodes.length));
|
||||||
|
const nodePositionsData = new Float32Array(textureSize * textureSize * 4);
|
||||||
|
|
||||||
|
let nodeIndex = 0;
|
||||||
|
function forNode(node: Node) {
|
||||||
|
const color = getColorForType(node.type);
|
||||||
|
nodeColors[nodeIndex * 3 + 0] = color.r;
|
||||||
|
nodeColors[nodeIndex * 3 + 1] = color.g;
|
||||||
|
nodeColors[nodeIndex * 3 + 2] = color.b;
|
||||||
|
|
||||||
|
nodePositionsData[nodeIndex * 4 + 0] = 0.0;
|
||||||
|
nodePositionsData[nodeIndex * 4 + 1] = 0.0;
|
||||||
|
nodePositionsData[nodeIndex * 4 + 2] = 0.0;
|
||||||
|
nodePositionsData[nodeIndex * 4 + 3] = 1.0;
|
||||||
|
|
||||||
|
nodeIndices.set(node.id, nodeIndex);
|
||||||
|
|
||||||
|
nodeIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node related data
|
||||||
|
const edgeIndices = new Float32Array(edges.length * 2);
|
||||||
|
|
||||||
|
let edgeIndex = 0;
|
||||||
|
function forEdge(edge: Edge) {
|
||||||
|
const fromIndex = nodeIndices.get(edge.source);
|
||||||
|
const toIndex = nodeIndices.get(edge.target);
|
||||||
|
edgeIndices[edgeIndex * 2 + 0] = fromIndex;
|
||||||
|
edgeIndices[edgeIndex * 2 + 1] = toIndex;
|
||||||
|
|
||||||
|
edgeIndex += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graph creation and layout
|
||||||
|
const graph = createGraph(nodes, edges, forNode, forEdge);
|
||||||
|
|
||||||
|
const graphLayout = createForceLayout(graph, {
|
||||||
|
dragCoefficient: 1.0,
|
||||||
|
springLength: 200,
|
||||||
|
springCoefficient: 0.2,
|
||||||
|
gravity: -1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Node Mesh
|
||||||
|
const nodePositionsTexture = createNodePositionsTexture(
|
||||||
|
nodes,
|
||||||
|
nodePositionsData
|
||||||
|
);
|
||||||
|
|
||||||
|
const nodeSwarmMesh = createNodeSwarmMesh(
|
||||||
|
nodes,
|
||||||
|
nodePositionsTexture,
|
||||||
|
nodeColors,
|
||||||
|
INITIAL_CAMERA_DISTANCE
|
||||||
|
);
|
||||||
|
|
||||||
|
const edgeMesh = createEdgeMesh(
|
||||||
|
edges,
|
||||||
|
nodePositionsTexture,
|
||||||
|
edgeIndices,
|
||||||
|
INITIAL_CAMERA_DISTANCE
|
||||||
|
);
|
||||||
|
|
||||||
|
// Density cloud setup
|
||||||
|
const densityCloudScene = new Scene();
|
||||||
|
const densityCloudTarget = createDensityRenderTarget(512);
|
||||||
|
|
||||||
|
const densityAccumulatorMesh = createDensityAccumulatorMesh(
|
||||||
|
nodes,
|
||||||
|
nodeColors,
|
||||||
|
nodePositionsTexture,
|
||||||
|
INITIAL_CAMERA_DISTANCE
|
||||||
|
);
|
||||||
|
|
||||||
|
const metaballMesh = createMetaballMesh(densityCloudTarget);
|
||||||
|
|
||||||
|
// const densityCloudDebugMesh = createDebugViewMesh(densityCloudTarget);
|
||||||
|
// Density cloud setup end
|
||||||
|
|
||||||
|
let pickedNodeIndex = -1;
|
||||||
|
const lastPickedNodeIndex = -1;
|
||||||
|
const pickNodeFromScene = (event: unknown) => {
|
||||||
|
pickedNodeIndex = pickNodeIndexFromScene(event as MouseEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
parentElement.addEventListener("mousemove", (event) => {
|
||||||
|
const rect = parentElement.getBoundingClientRect();
|
||||||
|
mousePosition.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
mousePosition.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
|
pickNodeFromScene(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
const scene = new Scene();
|
||||||
|
scene.background = new Color("#000000");
|
||||||
|
const renderer = new WebGLRenderer({ antialias: true });
|
||||||
|
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
renderer.setSize(parentElement.clientWidth, parentElement.clientHeight);
|
||||||
|
if (parentElement.children.length === 0) {
|
||||||
|
parentElement.appendChild(renderer.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup camera
|
||||||
|
const aspect = parentElement.clientWidth / parentElement.clientHeight;
|
||||||
|
const frustumSize = INITIAL_CAMERA_DISTANCE;
|
||||||
|
|
||||||
|
const camera = new OrthographicCamera(
|
||||||
|
(-frustumSize * aspect) / 2,
|
||||||
|
(frustumSize * aspect) / 2,
|
||||||
|
frustumSize / 2,
|
||||||
|
-frustumSize / 2,
|
||||||
|
1,
|
||||||
|
5000
|
||||||
|
);
|
||||||
|
|
||||||
|
camera.position.set(0, 0, INITIAL_CAMERA_DISTANCE);
|
||||||
|
camera.lookAt(0, 0, 0);
|
||||||
|
|
||||||
|
// Setup controls
|
||||||
|
const controls = new OrbitControls(camera, renderer.domElement);
|
||||||
|
controls.enableRotate = false;
|
||||||
|
controls.enablePan = true;
|
||||||
|
controls.enableZoom = true;
|
||||||
|
controls.screenSpacePanning = true;
|
||||||
|
controls.minZoom = 1;
|
||||||
|
controls.maxZoom = 4;
|
||||||
|
controls.enableDamping = true;
|
||||||
|
controls.dampingFactor = 0.05;
|
||||||
|
controls.target.set(0, 0, 0);
|
||||||
|
|
||||||
|
controls.update();
|
||||||
|
|
||||||
|
// Handle resizing
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
const aspect = parentElement.clientWidth / parentElement.clientHeight;
|
||||||
|
camera.left = (-frustumSize * aspect) / 2;
|
||||||
|
camera.right = (frustumSize * aspect) / 2;
|
||||||
|
camera.top = frustumSize / 2;
|
||||||
|
camera.bottom = -frustumSize / 2;
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(parentElement.clientWidth, parentElement.clientHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Node picking setup
|
||||||
|
const pickingTarget = new WebGLRenderTarget(
|
||||||
|
window.innerWidth,
|
||||||
|
window.innerHeight,
|
||||||
|
{
|
||||||
|
format: RGBAFormat,
|
||||||
|
type: UnsignedByteType,
|
||||||
|
depthBuffer: true,
|
||||||
|
stencilBuffer: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const pickingScene = new Scene();
|
||||||
|
|
||||||
|
function pickNodeIndexFromScene(event: MouseEvent): number {
|
||||||
|
pickingScene.add(pickingMesh);
|
||||||
|
|
||||||
|
const pickedNodeIndex = pickNodeIndex(
|
||||||
|
event,
|
||||||
|
renderer,
|
||||||
|
pickingScene,
|
||||||
|
camera,
|
||||||
|
pickingTarget
|
||||||
|
);
|
||||||
|
|
||||||
|
return pickedNodeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pickingMesh = createPickingMesh(
|
||||||
|
nodes,
|
||||||
|
nodePositionsTexture,
|
||||||
|
nodeColors,
|
||||||
|
INITIAL_CAMERA_DISTANCE
|
||||||
|
);
|
||||||
|
|
||||||
|
renderer.domElement.addEventListener("mousedown", (event) => {
|
||||||
|
const pickedNodeIndex = pickNodeIndexFromScene(event);
|
||||||
|
console.log("Picked node index: ", pickedNodeIndex);
|
||||||
|
});
|
||||||
|
// Node picking setup end
|
||||||
|
|
||||||
|
// Setup scene
|
||||||
|
for (let i = 0; i < 500; i++) {
|
||||||
|
graphLayout.step();
|
||||||
|
}
|
||||||
|
|
||||||
|
let visibleLabels: unknown[] = [];
|
||||||
|
|
||||||
|
const entityTypeLabels: [string, unknown][] = [];
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.type === "EntityType") {
|
||||||
|
const label = createLabel(node.label, config?.fontSize);
|
||||||
|
entityTypeLabels.push([node.id, label]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const processingStep = 0;
|
||||||
|
|
||||||
|
// Render loop
|
||||||
|
function render() {
|
||||||
|
graphLayout.step();
|
||||||
|
|
||||||
|
controls.update();
|
||||||
|
|
||||||
|
updateNodePositions(
|
||||||
|
nodes,
|
||||||
|
graphLayout,
|
||||||
|
nodePositionsData,
|
||||||
|
nodePositionsTexture
|
||||||
|
);
|
||||||
|
const textScale = Math.max(1, 4 / camera.zoom);
|
||||||
|
|
||||||
|
nodeSwarmMesh.material.uniforms.camDist.value = Math.floor(
|
||||||
|
camera.zoom * 500
|
||||||
|
);
|
||||||
|
nodeSwarmMesh.material.uniforms.mousePos.value.set(
|
||||||
|
mousePosition.x,
|
||||||
|
mousePosition.y
|
||||||
|
);
|
||||||
|
// @ts-expect-error uniforms does exist on material
|
||||||
|
edgeMesh.material.uniforms.camDist.value = Math.floor(camera.zoom * 500);
|
||||||
|
// @ts-expect-error uniforms does exist on material
|
||||||
|
edgeMesh.material.uniforms.mousePos.value.set(
|
||||||
|
mousePosition.x,
|
||||||
|
mousePosition.y
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-expect-error uniforms does exist on material
|
||||||
|
pickingMesh.material.uniforms.camDist.value = Math.floor(camera.zoom * 500);
|
||||||
|
|
||||||
|
edgeMesh.renderOrder = 1;
|
||||||
|
nodeSwarmMesh.renderOrder = 2;
|
||||||
|
|
||||||
|
scene.add(edgeMesh);
|
||||||
|
scene.add(nodeSwarmMesh);
|
||||||
|
|
||||||
|
// Pass 1: draw points into density texture
|
||||||
|
renderer.setRenderTarget(densityCloudTarget);
|
||||||
|
renderer.clear();
|
||||||
|
densityCloudScene.clear();
|
||||||
|
densityCloudScene.add(densityAccumulatorMesh);
|
||||||
|
renderer.render(densityCloudScene, camera);
|
||||||
|
|
||||||
|
// Pass 2: render density map to screen
|
||||||
|
renderer.setRenderTarget(null);
|
||||||
|
renderer.clear();
|
||||||
|
metaballMesh.renderOrder = 0;
|
||||||
|
scene.add(metaballMesh);
|
||||||
|
|
||||||
|
for (const [nodeId, label] of entityTypeLabels) {
|
||||||
|
const nodePosition = graphLayout.getNodePosition(nodeId);
|
||||||
|
// @ts-expect-error label is Text from troika-three-text
|
||||||
|
label.position.set(nodePosition.x, nodePosition.y, 1.0);
|
||||||
|
// @ts-expect-error label is Text from troika-three-text
|
||||||
|
label.scale.setScalar(textScale);
|
||||||
|
// @ts-expect-error label is Text from troika-three-text
|
||||||
|
scene.add(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pickedNodeIndex >= 0) {
|
||||||
|
if (pickedNodeIndex !== lastPickedNodeIndex) {
|
||||||
|
for (const label of visibleLabels) {
|
||||||
|
// @ts-expect-error label is Text from troika-three-text
|
||||||
|
label.visible = false;
|
||||||
|
}
|
||||||
|
visibleLabels = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const pickedNode = nodes[pickedNodeIndex];
|
||||||
|
|
||||||
|
parentElement.style.cursor = "pointer";
|
||||||
|
|
||||||
|
const pickedNodePosition = graphLayout.getNodePosition(pickedNode.id);
|
||||||
|
|
||||||
|
let pickedNodeLabel = nodeLabelMap.get(pickedNode.id);
|
||||||
|
if (!pickedNodeLabel) {
|
||||||
|
pickedNodeLabel = createLabel(pickedNode.label, config?.fontSize);
|
||||||
|
nodeLabelMap.set(pickedNode.id, pickedNodeLabel);
|
||||||
|
}
|
||||||
|
pickedNodeLabel.position.set(
|
||||||
|
pickedNodePosition.x,
|
||||||
|
pickedNodePosition.y,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
pickedNodeLabel.scale.setScalar(textScale);
|
||||||
|
|
||||||
|
if (camera.zoom > 2) {
|
||||||
|
graph.forEachLinkedNode(
|
||||||
|
pickedNode.id,
|
||||||
|
(otherNode: GraphNode, edge: GraphLink) => {
|
||||||
|
if (visibleLabels.length > 10) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let otherNodeLabel = nodeLabelMap.get(otherNode.id);
|
||||||
|
if (!otherNodeLabel) {
|
||||||
|
otherNodeLabel = createLabel(otherNode.data.label, config?.fontSize);
|
||||||
|
nodeLabelMap.set(otherNode.id, otherNodeLabel);
|
||||||
|
}
|
||||||
|
const otherNodePosition = graphLayout.getNodePosition(otherNode.id);
|
||||||
|
otherNodeLabel.position.set(
|
||||||
|
otherNodePosition.x,
|
||||||
|
otherNodePosition.y,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
let linkLabel = edgeLabelMap.get(edge.id);
|
||||||
|
if (!linkLabel) {
|
||||||
|
linkLabel = createLabel(edge.data.label, config?.fontSize);
|
||||||
|
edgeLabelMap.set(edge.id, linkLabel);
|
||||||
|
}
|
||||||
|
const linkPosition = graphLayout.getLinkPosition(edge.id);
|
||||||
|
const middleLinkPosition = new Vector2(
|
||||||
|
(linkPosition.from.x + linkPosition.to.x) / 2,
|
||||||
|
(linkPosition.from.y + linkPosition.to.y) / 2
|
||||||
|
);
|
||||||
|
linkLabel.position.set(
|
||||||
|
middleLinkPosition.x,
|
||||||
|
middleLinkPosition.y,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
linkLabel.visible = true;
|
||||||
|
linkLabel.scale.setScalar(textScale);
|
||||||
|
visibleLabels.push(linkLabel);
|
||||||
|
otherNodeLabel.visible = true;
|
||||||
|
otherNodeLabel.scale.setScalar(textScale);
|
||||||
|
visibleLabels.push(otherNodeLabel);
|
||||||
|
|
||||||
|
scene.add(linkLabel);
|
||||||
|
scene.add(otherNodeLabel);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pickedNodeLabel.visible = true;
|
||||||
|
visibleLabels.push(pickedNodeLabel);
|
||||||
|
|
||||||
|
scene.add(pickedNodeLabel);
|
||||||
|
} else {
|
||||||
|
parentElement.style.cursor = "default";
|
||||||
|
|
||||||
|
for (const label of visibleLabels) {
|
||||||
|
// @ts-expect-error label is Text from troika-three-text
|
||||||
|
label.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
visibleLabels = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
|
||||||
|
requestAnimationFrame(render);
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNodePositions(
|
||||||
|
nodes: Node[],
|
||||||
|
graphLayout: Layout<Graph>,
|
||||||
|
nodePositionsData: Float32Array,
|
||||||
|
nodePositionsTexture: DataTexture
|
||||||
|
) {
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
const p = graphLayout.getNodePosition(nodes[i].id);
|
||||||
|
nodePositionsData[i * 4 + 0] = p.x;
|
||||||
|
nodePositionsData[i * 4 + 1] = p.y;
|
||||||
|
nodePositionsData[i * 4 + 2] = 0.0;
|
||||||
|
nodePositionsData[i * 4 + 3] = 1.0;
|
||||||
|
}
|
||||||
|
nodePositionsTexture.needsUpdate = true;
|
||||||
|
}
|
||||||
28
cognee-frontend/src/ui/rendering/graph/createGraph.ts
Normal file
28
cognee-frontend/src/ui/rendering/graph/createGraph.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import createNgraph, { Graph } from "ngraph.graph";
|
||||||
|
import { Edge, Node } from "./types";
|
||||||
|
|
||||||
|
export default function createGraph(
|
||||||
|
nodes: Node[],
|
||||||
|
edges: Edge[],
|
||||||
|
forNode?: (node: Node) => void,
|
||||||
|
forEdge?: (node: Edge) => void
|
||||||
|
): Graph {
|
||||||
|
const graph = createNgraph();
|
||||||
|
|
||||||
|
for (const node of nodes) {
|
||||||
|
graph.addNode(node.id, {
|
||||||
|
id: node.id,
|
||||||
|
label: node.label,
|
||||||
|
});
|
||||||
|
forNode?.(node);
|
||||||
|
}
|
||||||
|
for (const edge of edges) {
|
||||||
|
graph.addLink(edge.source, edge.target, {
|
||||||
|
id: edge.id,
|
||||||
|
label: edge.label,
|
||||||
|
});
|
||||||
|
forEdge?.(edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
12
cognee-frontend/src/ui/rendering/graph/types.ts
Normal file
12
cognee-frontend/src/ui/rendering/graph/types.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface Node {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Edge {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
source: string;
|
||||||
|
target: string;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { ShaderMaterial, Texture, Vector2 } from "three";
|
||||||
|
|
||||||
|
export function createBlurPassMaterial(
|
||||||
|
texture: Texture,
|
||||||
|
direction = new Vector2(1.0, 0.0)
|
||||||
|
) {
|
||||||
|
return new ShaderMaterial({
|
||||||
|
uniforms: {
|
||||||
|
densityTex: { value: texture },
|
||||||
|
direction: { value: direction }, // (1,0) = horizontal, (0,1) = vertical
|
||||||
|
texSize: { value: new Vector2(512, 512) },
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
varying vec2 vUv;
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = vec4(position.xy, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
precision highp float;
|
||||||
|
uniform sampler2D densityTex;
|
||||||
|
uniform vec2 direction;
|
||||||
|
uniform vec2 texSize;
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 texel = direction / texSize;
|
||||||
|
float kernel[5];
|
||||||
|
kernel[0] = 0.204164;
|
||||||
|
kernel[1] = 0.304005;
|
||||||
|
kernel[2] = 0.193783;
|
||||||
|
kernel[3] = 0.072184;
|
||||||
|
kernel[4] = 0.025864;
|
||||||
|
|
||||||
|
vec4 sum = texture2D(densityTex, vUv) * kernel[0];
|
||||||
|
for (int i = 1; i < 5; i++) {
|
||||||
|
sum += texture2D(densityTex, vUv + texel * float(i)) * kernel[i];
|
||||||
|
sum += texture2D(densityTex, vUv - texel * float(i)) * kernel[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = sum;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { ShaderMaterial, Texture } from "three";
|
||||||
|
|
||||||
|
export function createDebugViewMaterial(fieldTexture: Texture) {
|
||||||
|
return new ShaderMaterial({
|
||||||
|
uniforms: {
|
||||||
|
fieldTex: { value: fieldTexture },
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
// void main() {
|
||||||
|
// gl_Position = vec4(position, 1.0);
|
||||||
|
// }
|
||||||
|
varying vec2 vUv;
|
||||||
|
void main() { vUv = uv; gl_Position = vec4(position.xy, 0.0, 1.0); }
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
uniform sampler2D fieldTex;
|
||||||
|
varying vec2 vUv;
|
||||||
|
void main() {
|
||||||
|
// gl_FragColor = texture2D(fieldTex, vUv);
|
||||||
|
|
||||||
|
float field = texture2D(fieldTex, vUv).r;
|
||||||
|
field = pow(field * 2.0, 0.5); // optional tone mapping
|
||||||
|
gl_FragColor = vec4(vec3(field), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// precision highp float;
|
||||||
|
// uniform sampler2D fieldTex;
|
||||||
|
|
||||||
|
// void main() {
|
||||||
|
// vec2 uv = gl_FragCoord.xy / vec2(textureSize(fieldTex, 0));
|
||||||
|
// float field = texture2D(fieldTex, uv).r;
|
||||||
|
// // visualize the field as grayscale
|
||||||
|
// gl_FragColor = vec4(vec3(field), 1.0);
|
||||||
|
// }
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { AdditiveBlending, DataTexture, ShaderMaterial } from "three";
|
||||||
|
|
||||||
|
export default function createDensityAccumulatorMaterial(
|
||||||
|
nodePositionsTexture: DataTexture,
|
||||||
|
initialCameraDistance: number
|
||||||
|
) {
|
||||||
|
const densityCloudMaterial = new ShaderMaterial({
|
||||||
|
depthWrite: false,
|
||||||
|
depthTest: false,
|
||||||
|
transparent: true,
|
||||||
|
blending: AdditiveBlending,
|
||||||
|
uniforms: {
|
||||||
|
nodePositionsTexture: {
|
||||||
|
value: nodePositionsTexture,
|
||||||
|
},
|
||||||
|
textureSize: {
|
||||||
|
value: nodePositionsTexture.image.width,
|
||||||
|
},
|
||||||
|
camDist: {
|
||||||
|
value: initialCameraDistance,
|
||||||
|
},
|
||||||
|
radius: { value: 0.05 },
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
uniform sampler2D nodePositionsTexture;
|
||||||
|
uniform float textureSize;
|
||||||
|
uniform float camDist;
|
||||||
|
attribute vec3 nodeColor;
|
||||||
|
varying vec3 vColor;
|
||||||
|
varying vec2 vUv;
|
||||||
|
varying float nodeSize;
|
||||||
|
|
||||||
|
vec3 getNodePos(float idx) {
|
||||||
|
float fx = mod(idx, textureSize);
|
||||||
|
float fy = floor(idx / textureSize);
|
||||||
|
vec2 uv = (vec2(fx, fy) + 0.5) / textureSize;
|
||||||
|
return texture2D(nodePositionsTexture, uv).xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
vColor = nodeColor;
|
||||||
|
vec3 nodePos = getNodePos(float(gl_InstanceID));
|
||||||
|
|
||||||
|
float baseNodeSize = 8.0;
|
||||||
|
|
||||||
|
// Normalize camera distance into [0,1]
|
||||||
|
float t = clamp((camDist - 500.0) / (2000.0 - 500.0), 0.0, 1.0);
|
||||||
|
nodeSize = baseNodeSize * mix(10.0, 12.0, t);
|
||||||
|
|
||||||
|
vec3 transformed = nodePos + position * nodeSize;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
varying float nodeSize;
|
||||||
|
varying vec3 vColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 pCoord = vUv - 0.5;
|
||||||
|
float distSq = dot(pCoord, pCoord) * 4.0;
|
||||||
|
|
||||||
|
if (distSq > 1.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
float radiusSq = (nodeSize / 2.0) * (nodeSize / 2.0);
|
||||||
|
float falloff = max(0.0, 1.0 - distSq);
|
||||||
|
float influence = radiusSq * falloff * falloff;
|
||||||
|
vec3 accumulatedColor = vColor * influence;
|
||||||
|
|
||||||
|
gl_FragColor = vec4(accumulatedColor, influence);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return densityCloudMaterial;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import * as three from "three";
|
||||||
|
|
||||||
|
export default function createEdgeMaterial(
|
||||||
|
texture: three.DataTexture,
|
||||||
|
initialCameraDistance: number
|
||||||
|
): three.ShaderMaterial {
|
||||||
|
const material = new three.ShaderMaterial({
|
||||||
|
transparent: true,
|
||||||
|
depthWrite: false,
|
||||||
|
blending: three.AdditiveBlending,
|
||||||
|
uniforms: {
|
||||||
|
nodePosTex: { value: texture },
|
||||||
|
textureSize: { value: texture.image.width },
|
||||||
|
camDist: { value: initialCameraDistance },
|
||||||
|
mousePos: { value: new three.Vector2(9999, 9999) }, // start offscreen
|
||||||
|
color: { value: new three.Color(0xffffff) },
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
attribute vec2 edgeIndices;
|
||||||
|
uniform sampler2D nodePosTex;
|
||||||
|
uniform float textureSize;
|
||||||
|
uniform float camDist;
|
||||||
|
uniform vec2 mousePos;
|
||||||
|
|
||||||
|
varying float vFade;
|
||||||
|
varying float vHighlight;
|
||||||
|
|
||||||
|
vec3 getNodePos(float idx) {
|
||||||
|
float x = mod(idx, textureSize);
|
||||||
|
float y = floor(idx / textureSize);
|
||||||
|
vec2 uv = (vec2(x, y) + 0.5) / textureSize;
|
||||||
|
return texture2D(nodePosTex, uv).xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 start = getNodePos(edgeIndices.x);
|
||||||
|
vec3 end = getNodePos(edgeIndices.y);
|
||||||
|
vec3 nodePos = mix(start, end, position.x);
|
||||||
|
|
||||||
|
// Project world-space position to clip-space
|
||||||
|
vec4 clipPos = projectionMatrix * modelViewMatrix * vec4(nodePos, 1.0);
|
||||||
|
vec3 ndc = clipPos.xyz / clipPos.w; // normalized device coordinates [-1,1]
|
||||||
|
|
||||||
|
float distanceFromMouse = length(ndc.xy - mousePos);
|
||||||
|
vHighlight = smoothstep(0.2, 0.0, distanceFromMouse);
|
||||||
|
|
||||||
|
vFade = smoothstep(500.0, 1500.0, camDist);
|
||||||
|
vFade = 0.2 * clamp(vFade, 0.0, 1.0);
|
||||||
|
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(nodePos, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
uniform vec3 color;
|
||||||
|
varying vec3 vColor;
|
||||||
|
varying float vFade;
|
||||||
|
varying float vHighlight;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 finalColor = mix(color, vec3(1.0), vHighlight * 0.8);
|
||||||
|
float alpha = mix(vFade, 0.8, vHighlight);
|
||||||
|
gl_FragColor = vec4(finalColor, alpha);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { ShaderMaterial, Texture } from "three";
|
||||||
|
|
||||||
|
export function createMetaballMaterial(fieldTexture: Texture) {
|
||||||
|
return new ShaderMaterial({
|
||||||
|
transparent: true,
|
||||||
|
uniforms: {
|
||||||
|
fieldTex: { value: fieldTexture },
|
||||||
|
threshold: { value: 25000.0 },
|
||||||
|
smoothing: { value: 5000.0 },
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
uniform float threshold;
|
||||||
|
uniform float smoothing;
|
||||||
|
uniform sampler2D fieldTex;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 fieldData = texture2D(fieldTex, vUv);
|
||||||
|
vec3 accumulatedColor = fieldData.rgb;
|
||||||
|
float totalInfluence = fieldData.a;
|
||||||
|
|
||||||
|
vec3 finalColor = vec3(0.0);
|
||||||
|
|
||||||
|
if (totalInfluence > 0.0) {
|
||||||
|
finalColor = accumulatedColor / totalInfluence;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth transition around threshold
|
||||||
|
float alphaEdge = smoothstep(threshold - smoothing, threshold + smoothing, totalInfluence);
|
||||||
|
float alpha = alphaEdge * 0.3;
|
||||||
|
|
||||||
|
if (alpha < 0.01) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(finalColor, alpha);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import * as three from "three";
|
||||||
|
|
||||||
|
export default function createNodeSwarmMaterial(
|
||||||
|
nodePositionsTexture: three.DataTexture,
|
||||||
|
initialCameraDistance: number
|
||||||
|
) {
|
||||||
|
const material = new three.ShaderMaterial({
|
||||||
|
transparent: true,
|
||||||
|
uniforms: {
|
||||||
|
nodePosTex: { value: nodePositionsTexture },
|
||||||
|
textureSize: { value: nodePositionsTexture.image.width },
|
||||||
|
camDist: { value: initialCameraDistance },
|
||||||
|
mousePos: { value: new three.Vector2(9999, 9999) }, // start offscreen
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
uniform sampler2D nodePosTex;
|
||||||
|
uniform float textureSize;
|
||||||
|
uniform float camDist;
|
||||||
|
uniform vec2 mousePos;
|
||||||
|
attribute vec3 nodeColor;
|
||||||
|
varying vec3 vColor;
|
||||||
|
varying float vHighlight;
|
||||||
|
|
||||||
|
vec3 getNodePos(float idx) {
|
||||||
|
float size = textureSize;
|
||||||
|
float fx = mod(idx, size);
|
||||||
|
float fy = floor(idx / size);
|
||||||
|
vec2 uv = (vec2(fx, fy) + 0.5) / size;
|
||||||
|
return texture2D(nodePosTex, uv).xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vColor = nodeColor;
|
||||||
|
vec3 nodePos = getNodePos(float(gl_InstanceID));
|
||||||
|
|
||||||
|
// Project world-space position to clip-space
|
||||||
|
vec4 clipPos = projectionMatrix * modelViewMatrix * vec4(nodePos, 1.0);
|
||||||
|
vec3 ndc = clipPos.xyz / clipPos.w; // normalized device coordinates [-1,1]
|
||||||
|
|
||||||
|
float distanceFromMouse = length(ndc.xy - mousePos);
|
||||||
|
vHighlight = smoothstep(0.2, 0.0, distanceFromMouse);
|
||||||
|
|
||||||
|
float baseNodeSize = 8.0;
|
||||||
|
|
||||||
|
// Normalize camera distance into [0,1]
|
||||||
|
float t = clamp((camDist - 500.0) / (2000.0 - 500.0), 0.0, 1.0);
|
||||||
|
float nodeSize = baseNodeSize * mix(1.1, 1.3, t);
|
||||||
|
|
||||||
|
vec3 transformed = nodePos + position * nodeSize;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
varying vec3 vColor;
|
||||||
|
varying float vHighlight;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 finalColor = mix(vColor, vec3(1.0), vHighlight * 0.3);
|
||||||
|
gl_FragColor = vec4(finalColor, 1.0);
|
||||||
|
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import * as three from "three";
|
||||||
|
|
||||||
|
export default function createPickingMaterial(
|
||||||
|
nodePositionsTexture: three.DataTexture,
|
||||||
|
initialCameraDistance: number
|
||||||
|
) {
|
||||||
|
const pickingMaterial = new three.ShaderMaterial({
|
||||||
|
depthTest: true,
|
||||||
|
depthWrite: true,
|
||||||
|
transparent: false,
|
||||||
|
blending: three.NoBlending,
|
||||||
|
uniforms: {
|
||||||
|
nodePosTex: { value: nodePositionsTexture },
|
||||||
|
textureSize: { value: nodePositionsTexture.image.width },
|
||||||
|
camDist: { value: initialCameraDistance },
|
||||||
|
},
|
||||||
|
vertexShader: `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
uniform sampler2D nodePosTex;
|
||||||
|
uniform float textureSize;
|
||||||
|
uniform float camDist;
|
||||||
|
varying vec3 vColor;
|
||||||
|
|
||||||
|
vec3 getNodePos(float idx) {
|
||||||
|
float size = textureSize;
|
||||||
|
float fx = mod(idx, size);
|
||||||
|
float fy = floor(idx / size);
|
||||||
|
vec2 uv = (vec2(fx, fy) + 0.5) / size;
|
||||||
|
return texture2D(nodePosTex, uv).xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float id = float(gl_InstanceID);
|
||||||
|
vec3 nodePos = getNodePos(id);
|
||||||
|
vColor = vec3(
|
||||||
|
mod(id, 256.0) / 255.0,
|
||||||
|
mod(floor(id / 256.0), 256.0) / 255.0,
|
||||||
|
floor(id / 65536.0) / 255.0
|
||||||
|
);
|
||||||
|
// vColor = vec3(fract(sin(id * 12.9898) * 43758.5453));
|
||||||
|
|
||||||
|
float baseNodeSize = 4.0;
|
||||||
|
|
||||||
|
// Normalize camera distance into [0,1]
|
||||||
|
float t = clamp((camDist - 500.0) / (2000.0 - 500.0), 0.0, 1.0);
|
||||||
|
float nodeSize = baseNodeSize * mix(1.0, 2.0, t);
|
||||||
|
|
||||||
|
vec3 transformed = nodePos + position * nodeSize;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fragmentShader: `
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
varying vec3 vColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = vec4(vColor, 1.0);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return pickingMaterial;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Mesh, PlaneGeometry, WebGLRenderTarget } from "three";
|
||||||
|
import { createDebugViewMaterial } from "../materials/createDebugViewMaterial";
|
||||||
|
|
||||||
|
export default function createDebugViewMesh(renderTarget: WebGLRenderTarget) {
|
||||||
|
const debugQuad = new Mesh(
|
||||||
|
new PlaneGeometry(2, 2),
|
||||||
|
createDebugViewMaterial(renderTarget.texture)
|
||||||
|
);
|
||||||
|
|
||||||
|
debugQuad.frustumCulled = false;
|
||||||
|
|
||||||
|
return debugQuad;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import {
|
||||||
|
InstancedBufferAttribute,
|
||||||
|
InstancedMesh,
|
||||||
|
DataTexture,
|
||||||
|
PlaneGeometry,
|
||||||
|
} from "three";
|
||||||
|
import { Node } from "../graph/types";
|
||||||
|
import createDensityAccumulatorMaterial from "../materials/createDensityAccumulatorMaterial";
|
||||||
|
|
||||||
|
export default function createDensityAccumulatorMesh(
|
||||||
|
nodes: Node[],
|
||||||
|
nodeColors: Float32Array,
|
||||||
|
nodePositionsTexture: DataTexture,
|
||||||
|
initialCameraDistance: number
|
||||||
|
) {
|
||||||
|
const geometry = new PlaneGeometry(2, 2);
|
||||||
|
|
||||||
|
const material = createDensityAccumulatorMaterial(
|
||||||
|
nodePositionsTexture,
|
||||||
|
initialCameraDistance
|
||||||
|
);
|
||||||
|
|
||||||
|
geometry.setAttribute(
|
||||||
|
"nodeColor",
|
||||||
|
new InstancedBufferAttribute(nodeColors, 3)
|
||||||
|
);
|
||||||
|
|
||||||
|
const mesh = new InstancedMesh(geometry, material, nodes.length);
|
||||||
|
|
||||||
|
mesh.frustumCulled = false;
|
||||||
|
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
35
cognee-frontend/src/ui/rendering/meshes/createEdgeMesh.ts
Normal file
35
cognee-frontend/src/ui/rendering/meshes/createEdgeMesh.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import * as three from "three";
|
||||||
|
import createEdgeMaterial from "../materials/createEdgeMaterial";
|
||||||
|
import { Edge } from "../graph/types";
|
||||||
|
|
||||||
|
export default function createEdgeMesh(
|
||||||
|
edges: Edge[],
|
||||||
|
nodePositionTexture: three.DataTexture,
|
||||||
|
edgeIndices: Float32Array,
|
||||||
|
initialCameraDistance: number
|
||||||
|
): three.LineSegments {
|
||||||
|
const numberOfEdges = edges.length;
|
||||||
|
|
||||||
|
const instGeom = new three.InstancedBufferGeometry();
|
||||||
|
instGeom.setAttribute(
|
||||||
|
"position",
|
||||||
|
new three.BufferAttribute(new Float32Array([0, 0, 0, 1, 0, 0]), 3)
|
||||||
|
);
|
||||||
|
// instGeom.index = baseGeom.index;
|
||||||
|
instGeom.instanceCount = numberOfEdges;
|
||||||
|
|
||||||
|
instGeom.setAttribute(
|
||||||
|
"edgeIndices",
|
||||||
|
new three.InstancedBufferAttribute(edgeIndices, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
const material = createEdgeMaterial(
|
||||||
|
nodePositionTexture,
|
||||||
|
initialCameraDistance
|
||||||
|
);
|
||||||
|
|
||||||
|
const edgeMesh = new three.LineSegments(instGeom, material);
|
||||||
|
edgeMesh.frustumCulled = false;
|
||||||
|
|
||||||
|
return edgeMesh;
|
||||||
|
}
|
||||||
24
cognee-frontend/src/ui/rendering/meshes/createLabel.ts
Normal file
24
cognee-frontend/src/ui/rendering/meshes/createLabel.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Color } from "three";
|
||||||
|
import { Text } from "troika-three-text";
|
||||||
|
|
||||||
|
const LABEL_FONT_SIZE = 14;
|
||||||
|
|
||||||
|
export default function createLabel(text = "", fontSize = LABEL_FONT_SIZE): Text {
|
||||||
|
const label = new Text();
|
||||||
|
label.text = text;
|
||||||
|
label.fontSize = fontSize;
|
||||||
|
label.color = new Color("#ffffff");
|
||||||
|
label.strokeColor = new Color("#ffffff");
|
||||||
|
label.outlineWidth = 2;
|
||||||
|
label.outlineColor = new Color("#000000");
|
||||||
|
label.outlineOpacity = 0.5;
|
||||||
|
label.anchorX = "center";
|
||||||
|
label.anchorY = "middle";
|
||||||
|
label.visible = true;
|
||||||
|
label.frustumCulled = false;
|
||||||
|
label.renderOrder = 5;
|
||||||
|
label.maxWidth = 200;
|
||||||
|
label.sync();
|
||||||
|
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Mesh, PlaneGeometry, WebGLRenderTarget } from "three";
|
||||||
|
import { createMetaballMaterial } from "../materials/createMetaballMaterial";
|
||||||
|
|
||||||
|
export default function createMetaballMesh(
|
||||||
|
fieldRenderTarget: WebGLRenderTarget
|
||||||
|
) {
|
||||||
|
const quadGeo = new PlaneGeometry(2, 2);
|
||||||
|
|
||||||
|
const metaballMat = createMetaballMaterial(fieldRenderTarget.texture);
|
||||||
|
|
||||||
|
const quad = new Mesh(quadGeo, metaballMat);
|
||||||
|
quad.frustumCulled = false;
|
||||||
|
|
||||||
|
return quad;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import {
|
||||||
|
Mesh,
|
||||||
|
DataTexture,
|
||||||
|
CircleGeometry,
|
||||||
|
InstancedBufferAttribute,
|
||||||
|
InstancedBufferGeometry,
|
||||||
|
} from "three";
|
||||||
|
import { Node } from "../graph/types";
|
||||||
|
import createNodeSwarmMaterial from "../materials/createNodeSwarmMaterial";
|
||||||
|
|
||||||
|
export default function createNodeSwarmMesh(
|
||||||
|
nodes: Node[],
|
||||||
|
nodePositionsTexture: DataTexture,
|
||||||
|
nodeColors: Float32Array,
|
||||||
|
initialCameraDistance: number
|
||||||
|
) {
|
||||||
|
const nodeGeom = new CircleGeometry(2, 16);
|
||||||
|
const geom = new InstancedBufferGeometry();
|
||||||
|
geom.index = nodeGeom.index;
|
||||||
|
geom.instanceCount = nodes.length;
|
||||||
|
|
||||||
|
geom.setAttribute("position", nodeGeom.attributes.position);
|
||||||
|
geom.setAttribute("uv", nodeGeom.attributes.uv);
|
||||||
|
geom.setAttribute("nodeColor", new InstancedBufferAttribute(nodeColors, 3));
|
||||||
|
|
||||||
|
const material = createNodeSwarmMaterial(
|
||||||
|
nodePositionsTexture,
|
||||||
|
initialCameraDistance
|
||||||
|
);
|
||||||
|
|
||||||
|
const nodeSwarmMesh = new Mesh(geom, material);
|
||||||
|
nodeSwarmMesh.frustumCulled = false;
|
||||||
|
|
||||||
|
return nodeSwarmMesh;
|
||||||
|
}
|
||||||
35
cognee-frontend/src/ui/rendering/meshes/createPickingMesh.ts
Normal file
35
cognee-frontend/src/ui/rendering/meshes/createPickingMesh.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import {
|
||||||
|
Mesh,
|
||||||
|
DataTexture,
|
||||||
|
CircleGeometry,
|
||||||
|
InstancedBufferGeometry,
|
||||||
|
InstancedBufferAttribute,
|
||||||
|
} from "three";
|
||||||
|
import { Node } from "../graph/types";
|
||||||
|
import createPickingMaterial from "../materials/createPickingMaterial";
|
||||||
|
|
||||||
|
export default function createPickingMesh(
|
||||||
|
nodes: Node[],
|
||||||
|
nodePositionsTexture: DataTexture,
|
||||||
|
nodeColors: Float32Array,
|
||||||
|
initialCameraDistance: number
|
||||||
|
): Mesh {
|
||||||
|
const nodeGeom = new CircleGeometry(2, 16);
|
||||||
|
const geom = new InstancedBufferGeometry();
|
||||||
|
geom.index = nodeGeom.index;
|
||||||
|
geom.instanceCount = nodes.length;
|
||||||
|
|
||||||
|
geom.setAttribute("position", nodeGeom.attributes.position);
|
||||||
|
geom.setAttribute("uv", nodeGeom.attributes.uv);
|
||||||
|
geom.setAttribute("nodeColor", new InstancedBufferAttribute(nodeColors, 3));
|
||||||
|
|
||||||
|
const pickingMaterial = createPickingMaterial(
|
||||||
|
nodePositionsTexture,
|
||||||
|
initialCameraDistance
|
||||||
|
);
|
||||||
|
|
||||||
|
const pickingMesh = new Mesh(geom, pickingMaterial);
|
||||||
|
pickingMesh.frustumCulled = false;
|
||||||
|
|
||||||
|
return pickingMesh;
|
||||||
|
}
|
||||||
40
cognee-frontend/src/ui/rendering/picking/pickNodeIndex.ts
Normal file
40
cognee-frontend/src/ui/rendering/picking/pickNodeIndex.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
OrthographicCamera,
|
||||||
|
Scene,
|
||||||
|
WebGLRenderer,
|
||||||
|
WebGLRenderTarget,
|
||||||
|
} from "three";
|
||||||
|
|
||||||
|
const pixelBuffer = new Uint8Array(4);
|
||||||
|
|
||||||
|
export default function pickNodeIndex(
|
||||||
|
event: MouseEvent,
|
||||||
|
renderer: WebGLRenderer,
|
||||||
|
pickingScene: Scene,
|
||||||
|
camera: OrthographicCamera,
|
||||||
|
pickingRenderTarget: WebGLRenderTarget
|
||||||
|
) {
|
||||||
|
const rect = renderer.domElement.getBoundingClientRect();
|
||||||
|
// Convert from client coords to pixel coords in render target
|
||||||
|
const x =
|
||||||
|
((event.clientX - rect.left) / rect.width) * pickingRenderTarget.width;
|
||||||
|
const y =
|
||||||
|
pickingRenderTarget.height -
|
||||||
|
((event.clientY - rect.top) / rect.height) * pickingRenderTarget.height;
|
||||||
|
|
||||||
|
renderer.setRenderTarget(pickingRenderTarget);
|
||||||
|
renderer.clear();
|
||||||
|
renderer.render(pickingScene, camera);
|
||||||
|
renderer.readRenderTargetPixels(
|
||||||
|
pickingRenderTarget,
|
||||||
|
Math.floor(x),
|
||||||
|
Math.floor(y),
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
pixelBuffer
|
||||||
|
);
|
||||||
|
renderer.setRenderTarget(null);
|
||||||
|
|
||||||
|
const id = pixelBuffer[0] + pixelBuffer[1] * 256 + pixelBuffer[2] * 256 * 256;
|
||||||
|
return id || -1;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { FloatType, LinearFilter, RGBAFormat, WebGLRenderTarget } from "three";
|
||||||
|
|
||||||
|
export default function createDensityRenderTarget(size = 512) {
|
||||||
|
return new WebGLRenderTarget(size, size, {
|
||||||
|
format: RGBAFormat,
|
||||||
|
type: FloatType,
|
||||||
|
minFilter: LinearFilter,
|
||||||
|
magFilter: LinearFilter,
|
||||||
|
depthBuffer: false,
|
||||||
|
stencilBuffer: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import * as three from "three";
|
||||||
|
import { Node } from "../graph/types";
|
||||||
|
|
||||||
|
export default function createNodePositionsTexture(
|
||||||
|
nodes: Node[],
|
||||||
|
nodePositionData: Float32Array
|
||||||
|
): three.DataTexture {
|
||||||
|
const textureSize = Math.ceil(Math.sqrt(nodes.length));
|
||||||
|
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
nodePositionData[i * 4 + 0] = 0.0;
|
||||||
|
nodePositionData[i * 4 + 1] = 0.0;
|
||||||
|
nodePositionData[i * 4 + 2] = 0.0;
|
||||||
|
nodePositionData[i * 4 + 3] = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const texture = new three.DataTexture(
|
||||||
|
nodePositionData,
|
||||||
|
textureSize,
|
||||||
|
textureSize,
|
||||||
|
three.RGBAFormat,
|
||||||
|
three.FloatType
|
||||||
|
);
|
||||||
|
texture.needsUpdate = true;
|
||||||
|
texture.minFilter = three.NearestFilter;
|
||||||
|
texture.magFilter = three.NearestFilter;
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
465
cognee-frontend/types/troika-three-text.d.ts
vendored
Normal file
465
cognee-frontend/types/troika-three-text.d.ts
vendored
Normal file
|
|
@ -0,0 +1,465 @@
|
||||||
|
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
declare module "troika-three-text";
|
||||||
|
|
||||||
|
// import type { Color, Material, Object3D, Object3DEventMap } from "three";
|
||||||
|
|
||||||
|
// export class BatchedText {
|
||||||
|
// constructor(...args: any[]);
|
||||||
|
|
||||||
|
// add(...args: any[]): void;
|
||||||
|
|
||||||
|
// addText(...args: any[]): void;
|
||||||
|
|
||||||
|
// copy(...args: any[]): void;
|
||||||
|
|
||||||
|
// createDerivedMaterial(...args: any[]): void;
|
||||||
|
|
||||||
|
// dispose(...args: any[]): void;
|
||||||
|
|
||||||
|
// hasOutline(...args: any[]): void;
|
||||||
|
|
||||||
|
// remove(...args: any[]): void;
|
||||||
|
|
||||||
|
// removeText(...args: any[]): void;
|
||||||
|
|
||||||
|
// sync(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateBounds(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateMatrixWorld(...args: any[]): void;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export class GlyphsGeometry {
|
||||||
|
// constructor(...args: any[]);
|
||||||
|
|
||||||
|
// applyClipRect(...args: any[]): void;
|
||||||
|
|
||||||
|
// computeBoundingBox(...args: any[]): void;
|
||||||
|
|
||||||
|
// computeBoundingSphere(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateAttributeData(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateGlyphs(...args: any[]): void;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export class Text extends Object3D<Object3DEventMap> {
|
||||||
|
// public text: string;
|
||||||
|
// public fontSize: number;
|
||||||
|
// public color: Color;
|
||||||
|
// public anchorX;
|
||||||
|
// public anchorY;
|
||||||
|
// public font: string;
|
||||||
|
// public material: Material;
|
||||||
|
|
||||||
|
// constructor(...args: any[]): Object3D<Object3DEventMap>;
|
||||||
|
|
||||||
|
// clone(...args: any[]): Object3D<Object3DEventMap>;
|
||||||
|
|
||||||
|
// copy(...args: any[]): Object3D<Object3DEventMap>;
|
||||||
|
|
||||||
|
// createDerivedMaterial(...args: any[]): void;
|
||||||
|
|
||||||
|
// dispose(...args: any[]): void;
|
||||||
|
|
||||||
|
// hasOutline(...args: any[]): void;
|
||||||
|
|
||||||
|
// localPositionToTextCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// onBeforeRender(...args: any[]): void;
|
||||||
|
|
||||||
|
// raycast(...args: any[]): void;
|
||||||
|
|
||||||
|
// sync(...args: any[]): void;
|
||||||
|
|
||||||
|
// worldPositionToTextCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function configureTextBuilder(config: any): void;
|
||||||
|
|
||||||
|
// export function createTextDerivedMaterial(baseMaterial: any): any;
|
||||||
|
|
||||||
|
// export function dumpSDFTextures(): void;
|
||||||
|
|
||||||
|
// export function fontResolverWorkerModule(...args: any[]): any;
|
||||||
|
|
||||||
|
// export function getCaretAtPoint(textRenderInfo: any, x: any, y: any): any;
|
||||||
|
|
||||||
|
// export function getSelectionRects(
|
||||||
|
// textRenderInfo: any,
|
||||||
|
// start: any,
|
||||||
|
// end: any
|
||||||
|
// ): any;
|
||||||
|
|
||||||
|
// export function getTextRenderInfo(args: any, callback: any): any;
|
||||||
|
|
||||||
|
// export function preloadFont(
|
||||||
|
// { font, characters, sdfGlyphSize }: any,
|
||||||
|
// callback: any
|
||||||
|
// ): void;
|
||||||
|
|
||||||
|
// export function typesetterWorkerModule(...args: any[]): any;
|
||||||
|
|
||||||
|
// export namespace BatchedText {
|
||||||
|
// namespace DEFAULT_UP {
|
||||||
|
// const isVector3: boolean;
|
||||||
|
|
||||||
|
// const x: number;
|
||||||
|
|
||||||
|
// const y: number;
|
||||||
|
|
||||||
|
// const z: number;
|
||||||
|
|
||||||
|
// function add(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScaledVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function angleTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyAxisAngle(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix3(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix4(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyNormalMatrix(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyQuaternion(...args: any[]): void;
|
||||||
|
|
||||||
|
// function ceil(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clamp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clone(...args: any[]): void;
|
||||||
|
|
||||||
|
// function copy(...args: any[]): void;
|
||||||
|
|
||||||
|
// function cross(...args: any[]): void;
|
||||||
|
|
||||||
|
// function crossVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceToSquared(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divide(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divideScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function dot(...args: any[]): void;
|
||||||
|
|
||||||
|
// function equals(...args: any[]): void;
|
||||||
|
|
||||||
|
// function floor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromBufferAttribute(...args: any[]): void;
|
||||||
|
|
||||||
|
// function getComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function length(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lengthSq(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerpVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanDistanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function max(...args: any[]): void;
|
||||||
|
|
||||||
|
// function min(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiply(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function negate(...args: any[]): void;
|
||||||
|
|
||||||
|
// function normalize(...args: any[]): void;
|
||||||
|
|
||||||
|
// function project(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnPlane(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function random(...args: any[]): void;
|
||||||
|
|
||||||
|
// function randomDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function reflect(...args: any[]): void;
|
||||||
|
|
||||||
|
// function round(...args: any[]): void;
|
||||||
|
|
||||||
|
// function roundToZero(...args: any[]): void;
|
||||||
|
|
||||||
|
// function set(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromColor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindrical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindricalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrix3Column(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixColumn(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixPosition(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixScale(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSpherical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSphericalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setX(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setY(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setZ(...args: any[]): void;
|
||||||
|
|
||||||
|
// function sub(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function toArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function transformDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function unproject(...args: any[]): void;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export namespace Text {
|
||||||
|
// namespace DEFAULT_UP {
|
||||||
|
// const isVector3: boolean;
|
||||||
|
|
||||||
|
// const x: number;
|
||||||
|
|
||||||
|
// const y: number;
|
||||||
|
|
||||||
|
// const z: number;
|
||||||
|
|
||||||
|
// function add(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScaledVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function angleTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyAxisAngle(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix3(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix4(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyNormalMatrix(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyQuaternion(...args: any[]): void;
|
||||||
|
|
||||||
|
// function ceil(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clamp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clone(...args: any[]): void;
|
||||||
|
|
||||||
|
// function copy(...args: any[]): void;
|
||||||
|
|
||||||
|
// function cross(...args: any[]): void;
|
||||||
|
|
||||||
|
// function crossVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceToSquared(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divide(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divideScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function dot(...args: any[]): void;
|
||||||
|
|
||||||
|
// function equals(...args: any[]): void;
|
||||||
|
|
||||||
|
// function floor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromBufferAttribute(...args: any[]): void;
|
||||||
|
|
||||||
|
// function getComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function length(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lengthSq(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerpVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanDistanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function max(...args: any[]): void;
|
||||||
|
|
||||||
|
// function min(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiply(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function negate(...args: any[]): void;
|
||||||
|
|
||||||
|
// function normalize(...args: any[]): void;
|
||||||
|
|
||||||
|
// function project(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnPlane(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function random(...args: any[]): void;
|
||||||
|
|
||||||
|
// function randomDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function reflect(...args: any[]): void;
|
||||||
|
|
||||||
|
// function round(...args: any[]): void;
|
||||||
|
|
||||||
|
// function roundToZero(...args: any[]): void;
|
||||||
|
|
||||||
|
// function set(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromColor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindrical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindricalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrix3Column(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixColumn(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixPosition(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixScale(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSpherical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSphericalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setX(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setY(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setZ(...args: any[]): void;
|
||||||
|
|
||||||
|
// function sub(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function toArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function transformDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function unproject(...args: any[]): void;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export namespace fontResolverWorkerModule {
|
||||||
|
// const workerModuleData: {
|
||||||
|
// dependencies: {
|
||||||
|
// dependencies: any;
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// }[];
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// function onMainThread(...args: any[]): any;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export namespace typesetterWorkerModule {
|
||||||
|
// const workerModuleData: {
|
||||||
|
// dependencies: {
|
||||||
|
// dependencies: any;
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// }[];
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// function onMainThread(...args: any[]): any;
|
||||||
|
// }
|
||||||
465
cognee-frontend/types/troika-three-utils.d.ts
vendored
Normal file
465
cognee-frontend/types/troika-three-utils.d.ts
vendored
Normal file
|
|
@ -0,0 +1,465 @@
|
||||||
|
// /* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
declare module "troika-three-utils";
|
||||||
|
|
||||||
|
// import type { Color, Material, Object3D, Object3DEventMap } from "three";
|
||||||
|
|
||||||
|
// export class BatchedText {
|
||||||
|
// constructor(...args: any[]);
|
||||||
|
|
||||||
|
// add(...args: any[]): void;
|
||||||
|
|
||||||
|
// addText(...args: any[]): void;
|
||||||
|
|
||||||
|
// copy(...args: any[]): void;
|
||||||
|
|
||||||
|
// createDerivedMaterial(...args: any[]): void;
|
||||||
|
|
||||||
|
// dispose(...args: any[]): void;
|
||||||
|
|
||||||
|
// hasOutline(...args: any[]): void;
|
||||||
|
|
||||||
|
// remove(...args: any[]): void;
|
||||||
|
|
||||||
|
// removeText(...args: any[]): void;
|
||||||
|
|
||||||
|
// sync(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateBounds(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateMatrixWorld(...args: any[]): void;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export class GlyphsGeometry {
|
||||||
|
// constructor(...args: any[]);
|
||||||
|
|
||||||
|
// applyClipRect(...args: any[]): void;
|
||||||
|
|
||||||
|
// computeBoundingBox(...args: any[]): void;
|
||||||
|
|
||||||
|
// computeBoundingSphere(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateAttributeData(...args: any[]): void;
|
||||||
|
|
||||||
|
// updateGlyphs(...args: any[]): void;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export class Text extends Object3D<Object3DEventMap> {
|
||||||
|
// public text: string;
|
||||||
|
// public fontSize: number;
|
||||||
|
// public color: Color;
|
||||||
|
// public anchorX;
|
||||||
|
// public anchorY;
|
||||||
|
// public font: string;
|
||||||
|
// public material: Material;
|
||||||
|
|
||||||
|
// constructor(...args: any[]): Object3D<Object3DEventMap>;
|
||||||
|
|
||||||
|
// clone(...args: any[]): Object3D<Object3DEventMap>;
|
||||||
|
|
||||||
|
// copy(...args: any[]): Object3D<Object3DEventMap>;
|
||||||
|
|
||||||
|
// createDerivedMaterial(...args: any[]): void;
|
||||||
|
|
||||||
|
// dispose(...args: any[]): void;
|
||||||
|
|
||||||
|
// hasOutline(...args: any[]): void;
|
||||||
|
|
||||||
|
// localPositionToTextCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// onBeforeRender(...args: any[]): void;
|
||||||
|
|
||||||
|
// raycast(...args: any[]): void;
|
||||||
|
|
||||||
|
// sync(...args: any[]): void;
|
||||||
|
|
||||||
|
// worldPositionToTextCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_AUTO_UPDATE: boolean;
|
||||||
|
|
||||||
|
// static DEFAULT_MATRIX_WORLD_AUTO_UPDATE: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function configureTextBuilder(config: any): void;
|
||||||
|
|
||||||
|
// export function createTextDerivedMaterial(baseMaterial: any): any;
|
||||||
|
|
||||||
|
// export function dumpSDFTextures(): void;
|
||||||
|
|
||||||
|
// export function fontResolverWorkerModule(...args: any[]): any;
|
||||||
|
|
||||||
|
// export function getCaretAtPoint(textRenderInfo: any, x: any, y: any): any;
|
||||||
|
|
||||||
|
// export function getSelectionRects(
|
||||||
|
// textRenderInfo: any,
|
||||||
|
// start: any,
|
||||||
|
// end: any
|
||||||
|
// ): any;
|
||||||
|
|
||||||
|
// export function getTextRenderInfo(args: any, callback: any): any;
|
||||||
|
|
||||||
|
// export function preloadFont(
|
||||||
|
// { font, characters, sdfGlyphSize }: any,
|
||||||
|
// callback: any
|
||||||
|
// ): void;
|
||||||
|
|
||||||
|
// export function typesetterWorkerModule(...args: any[]): any;
|
||||||
|
|
||||||
|
// export namespace BatchedText {
|
||||||
|
// namespace DEFAULT_UP {
|
||||||
|
// const isVector3: boolean;
|
||||||
|
|
||||||
|
// const x: number;
|
||||||
|
|
||||||
|
// const y: number;
|
||||||
|
|
||||||
|
// const z: number;
|
||||||
|
|
||||||
|
// function add(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScaledVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function angleTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyAxisAngle(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix3(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix4(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyNormalMatrix(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyQuaternion(...args: any[]): void;
|
||||||
|
|
||||||
|
// function ceil(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clamp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clone(...args: any[]): void;
|
||||||
|
|
||||||
|
// function copy(...args: any[]): void;
|
||||||
|
|
||||||
|
// function cross(...args: any[]): void;
|
||||||
|
|
||||||
|
// function crossVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceToSquared(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divide(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divideScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function dot(...args: any[]): void;
|
||||||
|
|
||||||
|
// function equals(...args: any[]): void;
|
||||||
|
|
||||||
|
// function floor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromBufferAttribute(...args: any[]): void;
|
||||||
|
|
||||||
|
// function getComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function length(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lengthSq(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerpVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanDistanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function max(...args: any[]): void;
|
||||||
|
|
||||||
|
// function min(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiply(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function negate(...args: any[]): void;
|
||||||
|
|
||||||
|
// function normalize(...args: any[]): void;
|
||||||
|
|
||||||
|
// function project(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnPlane(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function random(...args: any[]): void;
|
||||||
|
|
||||||
|
// function randomDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function reflect(...args: any[]): void;
|
||||||
|
|
||||||
|
// function round(...args: any[]): void;
|
||||||
|
|
||||||
|
// function roundToZero(...args: any[]): void;
|
||||||
|
|
||||||
|
// function set(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromColor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindrical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindricalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrix3Column(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixColumn(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixPosition(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixScale(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSpherical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSphericalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setX(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setY(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setZ(...args: any[]): void;
|
||||||
|
|
||||||
|
// function sub(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function toArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function transformDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function unproject(...args: any[]): void;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export namespace Text {
|
||||||
|
// namespace DEFAULT_UP {
|
||||||
|
// const isVector3: boolean;
|
||||||
|
|
||||||
|
// const x: number;
|
||||||
|
|
||||||
|
// const y: number;
|
||||||
|
|
||||||
|
// const z: number;
|
||||||
|
|
||||||
|
// function add(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addScaledVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function addVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function angleTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyAxisAngle(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix3(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyMatrix4(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyNormalMatrix(...args: any[]): void;
|
||||||
|
|
||||||
|
// function applyQuaternion(...args: any[]): void;
|
||||||
|
|
||||||
|
// function ceil(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clamp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clampScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function clone(...args: any[]): void;
|
||||||
|
|
||||||
|
// function copy(...args: any[]): void;
|
||||||
|
|
||||||
|
// function cross(...args: any[]): void;
|
||||||
|
|
||||||
|
// function crossVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function distanceToSquared(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divide(...args: any[]): void;
|
||||||
|
|
||||||
|
// function divideScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function dot(...args: any[]): void;
|
||||||
|
|
||||||
|
// function equals(...args: any[]): void;
|
||||||
|
|
||||||
|
// function floor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function fromBufferAttribute(...args: any[]): void;
|
||||||
|
|
||||||
|
// function getComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function length(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lengthSq(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerp(...args: any[]): void;
|
||||||
|
|
||||||
|
// function lerpVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanDistanceTo(...args: any[]): void;
|
||||||
|
|
||||||
|
// function manhattanLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function max(...args: any[]): void;
|
||||||
|
|
||||||
|
// function min(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiply(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function multiplyVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function negate(...args: any[]): void;
|
||||||
|
|
||||||
|
// function normalize(...args: any[]): void;
|
||||||
|
|
||||||
|
// function project(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnPlane(...args: any[]): void;
|
||||||
|
|
||||||
|
// function projectOnVector(...args: any[]): void;
|
||||||
|
|
||||||
|
// function random(...args: any[]): void;
|
||||||
|
|
||||||
|
// function randomDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function reflect(...args: any[]): void;
|
||||||
|
|
||||||
|
// function round(...args: any[]): void;
|
||||||
|
|
||||||
|
// function roundToZero(...args: any[]): void;
|
||||||
|
|
||||||
|
// function set(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setComponent(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromColor(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindrical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromCylindricalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromEuler(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrix3Column(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixColumn(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixPosition(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromMatrixScale(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSpherical(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setFromSphericalCoords(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setLength(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setX(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setY(...args: any[]): void;
|
||||||
|
|
||||||
|
// function setZ(...args: any[]): void;
|
||||||
|
|
||||||
|
// function sub(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subScalar(...args: any[]): void;
|
||||||
|
|
||||||
|
// function subVectors(...args: any[]): void;
|
||||||
|
|
||||||
|
// function toArray(...args: any[]): void;
|
||||||
|
|
||||||
|
// function transformDirection(...args: any[]): void;
|
||||||
|
|
||||||
|
// function unproject(...args: any[]): void;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export namespace fontResolverWorkerModule {
|
||||||
|
// const workerModuleData: {
|
||||||
|
// dependencies: {
|
||||||
|
// dependencies: any;
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// }[];
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// function onMainThread(...args: any[]): any;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export namespace typesetterWorkerModule {
|
||||||
|
// const workerModuleData: {
|
||||||
|
// dependencies: {
|
||||||
|
// dependencies: any;
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// }[];
|
||||||
|
// getTransferables: any;
|
||||||
|
// id: string;
|
||||||
|
// init: string;
|
||||||
|
// isWorkerModule: boolean;
|
||||||
|
// name: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// function onMainThread(...args: any[]): any;
|
||||||
|
// }
|
||||||
|
|
@ -55,6 +55,7 @@ class DataDTO(OutDTO):
|
||||||
class GraphNodeDTO(OutDTO):
|
class GraphNodeDTO(OutDTO):
|
||||||
id: UUID
|
id: UUID
|
||||||
label: str
|
label: str
|
||||||
|
type: str
|
||||||
properties: dict
|
properties: dict
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,17 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
|
|
||||||
nodes_list = []
|
nodes_list = []
|
||||||
color_map = {
|
color_map = {
|
||||||
"Entity": "#f47710",
|
"Entity": "#5C10F4",
|
||||||
"EntityType": "#6510f4",
|
"EntityType": "#A550FF",
|
||||||
"DocumentChunk": "#801212",
|
"DocumentChunk": "#0DFF00",
|
||||||
"TextSummary": "#1077f4",
|
"TextSummary": "#5C10F4",
|
||||||
"TableRow": "#f47710",
|
"TableRow": "#A550FF",
|
||||||
"TableType": "#6510f4",
|
"TableType": "#5C10F4",
|
||||||
"ColumnValue": "#13613a",
|
"ColumnValue": "#757470",
|
||||||
"SchemaTable": "#f47710",
|
"SchemaTable": "#A550FF",
|
||||||
"DatabaseSchema": "#6510f4",
|
"DatabaseSchema": "#5C10F4",
|
||||||
"SchemaRelationship": "#13613a",
|
"SchemaRelationship": "#323332",
|
||||||
"default": "#D3D3D3",
|
"default": "#D8D8D8",
|
||||||
}
|
}
|
||||||
|
|
||||||
for node_id, node_info in nodes_data:
|
for node_id, node_info in nodes_data:
|
||||||
|
|
@ -98,16 +98,19 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script src="https://d3js.org/d3.v5.min.js"></script>
|
<script src="https://d3js.org/d3.v5.min.js"></script>
|
||||||
|
<script src="https://d3js.org/d3-contour.v1.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: linear-gradient(90deg, #101010, #1a1a2e); color: white; font-family: 'Inter', sans-serif; }
|
body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: linear-gradient(90deg, #101010, #1a1a2e); color: white; font-family: 'Inter', sans-serif; }
|
||||||
|
|
||||||
svg { width: 100vw; height: 100vh; display: block; }
|
svg { width: 100vw; height: 100vh; display: block; }
|
||||||
.links line { stroke: rgba(255, 255, 255, 0.4); stroke-width: 2px; }
|
.links line { stroke: rgba(160, 160, 160, 0.25); stroke-width: 1.5px; stroke-linecap: round; }
|
||||||
.links line.weighted { stroke: rgba(255, 215, 0, 0.7); }
|
.links line.weighted { stroke: rgba(255, 215, 0, 0.4); }
|
||||||
.links line.multi-weighted { stroke: rgba(0, 255, 127, 0.8); }
|
.links line.multi-weighted { stroke: rgba(0, 255, 127, 0.45); }
|
||||||
.nodes circle { stroke: white; stroke-width: 0.5px; filter: drop-shadow(0 0 5px rgba(255,255,255,0.3)); }
|
.nodes circle { stroke: white; stroke-width: 0.5px; }
|
||||||
.node-label { font-size: 5px; font-weight: bold; fill: white; text-anchor: middle; dominant-baseline: middle; font-family: 'Inter', sans-serif; pointer-events: none; }
|
.node-label { font-size: 5px; font-weight: bold; fill: #F4F4F4; text-anchor: middle; dominant-baseline: middle; font-family: 'Inter', sans-serif; pointer-events: none; }
|
||||||
.edge-label { font-size: 3px; fill: rgba(255, 255, 255, 0.7); text-anchor: middle; dominant-baseline: middle; font-family: 'Inter', sans-serif; pointer-events: none; }
|
.edge-label { font-size: 3px; fill: #F4F4F4; text-anchor: middle; dominant-baseline: middle; font-family: 'Inter', sans-serif; pointer-events: none; paint-order: stroke; stroke: rgba(50,51,50,0.75); stroke-width: 1px; }
|
||||||
|
|
||||||
|
.density path { mix-blend-mode: screen; }
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -125,11 +128,32 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
#info-panel {
|
||||||
|
position: fixed;
|
||||||
|
left: 12px;
|
||||||
|
top: 12px;
|
||||||
|
width: 340px;
|
||||||
|
max-height: calc(100vh - 24px);
|
||||||
|
overflow: auto;
|
||||||
|
background: rgba(50, 51, 50, 0.7);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
border: 1px solid rgba(216, 216, 216, 0.35);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #F4F4F4;
|
||||||
|
padding: 12px 14px;
|
||||||
|
z-index: 1100;
|
||||||
|
}
|
||||||
|
#info-panel h3 { margin: 0 0 8px 0; font-size: 14px; color: #F4F4F4; }
|
||||||
|
#info-panel .kv { font-size: 12px; line-height: 1.4; }
|
||||||
|
#info-panel .kv .k { color: #D8D8D8; }
|
||||||
|
#info-panel .kv .v { color: #F4F4F4; }
|
||||||
|
#info-panel .placeholder { opacity: 0.7; font-size: 12px; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<svg></svg>
|
<svg></svg>
|
||||||
<div class="tooltip" id="tooltip"></div>
|
<div class="tooltip" id="tooltip"></div>
|
||||||
|
<div id="info-panel"><div class="placeholder">Hover a node or edge to inspect details</div></div>
|
||||||
<script>
|
<script>
|
||||||
var nodes = {nodes};
|
var nodes = {nodes};
|
||||||
var links = {links};
|
var links = {links};
|
||||||
|
|
@ -140,19 +164,140 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
|
|
||||||
var container = svg.append("g");
|
var container = svg.append("g");
|
||||||
var tooltip = d3.select("#tooltip");
|
var tooltip = d3.select("#tooltip");
|
||||||
|
var infoPanel = d3.select('#info-panel');
|
||||||
|
|
||||||
|
function renderInfo(title, entries){
|
||||||
|
function esc(s){ return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||||||
|
var html = '<h3>' + esc(title) + '</h3>';
|
||||||
|
html += '<div class="kv">';
|
||||||
|
entries.forEach(function(e){
|
||||||
|
html += '<div><span class="k">' + esc(e.k) + ':</span> <span class="v">' + esc(e.v) + '</span></div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
infoPanel.html(html);
|
||||||
|
}
|
||||||
|
function pickDescription(obj){
|
||||||
|
if (!obj) return null;
|
||||||
|
var keys = ['description','summary','text','content'];
|
||||||
|
for (var i=0; i<keys.length; i++){
|
||||||
|
var v = obj[keys[i]];
|
||||||
|
if (typeof v === 'string' && v.trim()) return v.trim();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function truncate(s, n){ if (!s) return s; return s.length > n ? (s.slice(0, n) + '…') : s; }
|
||||||
|
function renderNodeInfo(n){
|
||||||
|
var entries = [];
|
||||||
|
if (n.name) entries.push({k:'Name', v: n.name});
|
||||||
|
if (n.type) entries.push({k:'Type', v: n.type});
|
||||||
|
if (n.id) entries.push({k:'ID', v: n.id});
|
||||||
|
var desc = pickDescription(n) || pickDescription(n.properties);
|
||||||
|
if (desc) entries.push({k:'Description', v: truncate(desc.replace(/\s+/g,' ').trim(), 280)});
|
||||||
|
if (n.properties) {
|
||||||
|
Object.keys(n.properties).slice(0, 12).forEach(function(key){
|
||||||
|
var v = n.properties[key];
|
||||||
|
if (v !== undefined && v !== null && typeof v !== 'object') entries.push({k: key, v: String(v)});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
renderInfo(n.name || 'Node', entries);
|
||||||
|
}
|
||||||
|
function renderEdgeInfo(e){
|
||||||
|
var entries = [];
|
||||||
|
if (e.relation) entries.push({k:'Relation', v: e.relation});
|
||||||
|
if (e.weight !== undefined && e.weight !== null) entries.push({k:'Weight', v: e.weight});
|
||||||
|
if (e.all_weights && Object.keys(e.all_weights).length){
|
||||||
|
Object.keys(e.all_weights).slice(0, 8).forEach(function(k){ entries.push({k: 'w.'+k, v: e.all_weights[k]}); });
|
||||||
|
}
|
||||||
|
if (e.relationship_type) entries.push({k:'Type', v: e.relationship_type});
|
||||||
|
var edesc = pickDescription(e.edge_info);
|
||||||
|
if (edesc) entries.push({k:'Description', v: truncate(edesc.replace(/\s+/g,' ').trim(), 280)});
|
||||||
|
renderInfo('Edge', entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic runtime diagnostics
|
||||||
|
console.log('[Cognee Visualization] nodes:', nodes ? nodes.length : 0, 'links:', links ? links.length : 0);
|
||||||
|
window.addEventListener('error', function(e){
|
||||||
|
try {
|
||||||
|
tooltip.html('<strong>Error:</strong> ' + e.message)
|
||||||
|
.style('left', '12px')
|
||||||
|
.style('top', '12px')
|
||||||
|
.style('opacity', 1);
|
||||||
|
} catch(_) {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Normalize node IDs and link endpoints for robustness
|
||||||
|
function resolveId(d){ return (d && (d.id || d.node_id || d.uuid || d.external_id || d.name)) || undefined; }
|
||||||
|
if (Array.isArray(nodes)) {
|
||||||
|
nodes.forEach(function(n){ var id = resolveId(n); if (id !== undefined) n.id = id; });
|
||||||
|
}
|
||||||
|
if (Array.isArray(links)) {
|
||||||
|
links.forEach(function(l){
|
||||||
|
if (typeof l.source === 'object') l.source = resolveId(l.source);
|
||||||
|
if (typeof l.target === 'object') l.target = resolveId(l.target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nodes || nodes.length === 0) {
|
||||||
|
container.append('text')
|
||||||
|
.attr('x', width / 2)
|
||||||
|
.attr('y', height / 2)
|
||||||
|
.attr('fill', '#fff')
|
||||||
|
.attr('font-size', 14)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.text('No graph data available');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visual defs - reusable glow
|
||||||
|
var defs = svg.append("defs");
|
||||||
|
var glow = defs.append("filter").attr("id", "glow")
|
||||||
|
.attr("x", "-30%")
|
||||||
|
.attr("y", "-30%")
|
||||||
|
.attr("width", "160%")
|
||||||
|
.attr("height", "160%");
|
||||||
|
glow.append("feGaussianBlur").attr("stdDeviation", 8).attr("result", "coloredBlur");
|
||||||
|
var feMerge = glow.append("feMerge");
|
||||||
|
feMerge.append("feMergeNode").attr("in", "coloredBlur");
|
||||||
|
feMerge.append("feMergeNode").attr("in", "SourceGraphic");
|
||||||
|
|
||||||
|
// Stronger glow for hovered adjacency
|
||||||
|
var glowStrong = defs.append("filter").attr("id", "glow-strong")
|
||||||
|
.attr("x", "-40%")
|
||||||
|
.attr("y", "-40%")
|
||||||
|
.attr("width", "180%")
|
||||||
|
.attr("height", "180%");
|
||||||
|
glowStrong.append("feGaussianBlur").attr("stdDeviation", 14).attr("result", "coloredBlur");
|
||||||
|
var feMerge2 = glowStrong.append("feMerge");
|
||||||
|
feMerge2.append("feMergeNode").attr("in", "coloredBlur");
|
||||||
|
feMerge2.append("feMergeNode").attr("in", "SourceGraphic");
|
||||||
|
|
||||||
|
var currentTransform = d3.zoomIdentity;
|
||||||
|
var densityZoomTimer = null;
|
||||||
|
var labelBaseSize = 10;
|
||||||
|
function getGroupKey(d){ return d && (d.type || d.category || d.group || d.color) || 'default'; }
|
||||||
|
|
||||||
var simulation = d3.forceSimulation(nodes)
|
var simulation = d3.forceSimulation(nodes)
|
||||||
.force("link", d3.forceLink(links).id(d => d.id).strength(0.1))
|
.force("link", d3.forceLink(links).id(function(d){ return d.id; }).distance(100).strength(0.2))
|
||||||
.force("charge", d3.forceManyBody().strength(-275))
|
.force("charge", d3.forceManyBody().strength(-180))
|
||||||
|
.force("collide", d3.forceCollide().radius(16).iterations(2))
|
||||||
.force("center", d3.forceCenter(width / 2, height / 2))
|
.force("center", d3.forceCenter(width / 2, height / 2))
|
||||||
.force("x", d3.forceX().strength(0.1).x(width / 2))
|
.force("x", d3.forceX().strength(0.06).x(width / 2))
|
||||||
.force("y", d3.forceY().strength(0.1).y(height / 2));
|
.force("y", d3.forceY().strength(0.06).y(height / 2))
|
||||||
|
.alphaDecay(0.06)
|
||||||
|
.velocityDecay(0.6);
|
||||||
|
|
||||||
|
// Density layer (sibling of container to avoid double transforms)
|
||||||
|
var densityLayer = svg.append("g")
|
||||||
|
.attr("class", "density")
|
||||||
|
.style("pointer-events", "none");
|
||||||
|
if (densityLayer.lower) densityLayer.lower();
|
||||||
|
|
||||||
var link = container.append("g")
|
var link = container.append("g")
|
||||||
.attr("class", "links")
|
.attr("class", "links")
|
||||||
.selectAll("line")
|
.selectAll("line")
|
||||||
.data(links)
|
.data(links)
|
||||||
.enter().append("line")
|
.enter().append("line")
|
||||||
|
.style("opacity", 0)
|
||||||
|
.style("pointer-events", "none")
|
||||||
.attr("stroke-width", d => {
|
.attr("stroke-width", d => {
|
||||||
if (d.weight) return Math.max(2, d.weight * 5);
|
if (d.weight) return Math.max(2, d.weight * 5);
|
||||||
if (d.all_weights && Object.keys(d.all_weights).length > 0) {
|
if (d.all_weights && Object.keys(d.all_weights).length > 0) {
|
||||||
|
|
@ -168,6 +313,7 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
})
|
})
|
||||||
.on("mouseover", function(d) {
|
.on("mouseover", function(d) {
|
||||||
// Create tooltip content for edge
|
// Create tooltip content for edge
|
||||||
|
renderEdgeInfo(d);
|
||||||
var content = "<strong>Edge Information</strong><br/>";
|
var content = "<strong>Edge Information</strong><br/>";
|
||||||
content += "Relationship: " + d.relation + "<br/>";
|
content += "Relationship: " + d.relation + "<br/>";
|
||||||
|
|
||||||
|
|
@ -212,6 +358,7 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
.data(links)
|
.data(links)
|
||||||
.enter().append("text")
|
.enter().append("text")
|
||||||
.attr("class", "edge-label")
|
.attr("class", "edge-label")
|
||||||
|
.style("opacity", 0)
|
||||||
.text(d => {
|
.text(d => {
|
||||||
var label = d.relation;
|
var label = d.relation;
|
||||||
if (d.all_weights && Object.keys(d.all_weights).length > 1) {
|
if (d.all_weights && Object.keys(d.all_weights).length > 1) {
|
||||||
|
|
@ -232,21 +379,222 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
.data(nodes)
|
.data(nodes)
|
||||||
.enter().append("g");
|
.enter().append("g");
|
||||||
|
|
||||||
|
// Color fallback by type when d.color is missing
|
||||||
|
var colorByType = {
|
||||||
|
"Entity": "#5C10F4",
|
||||||
|
"EntityType": "#A550FF",
|
||||||
|
"DocumentChunk": "#0DFF00",
|
||||||
|
"TextSummary": "#5C10F4",
|
||||||
|
"TableRow": "#A550FF",
|
||||||
|
"TableType": "#5C10F4",
|
||||||
|
"ColumnValue": "#757470",
|
||||||
|
"SchemaTable": "#A550FF",
|
||||||
|
"DatabaseSchema": "#5C10F4",
|
||||||
|
"SchemaRelationship": "#323332"
|
||||||
|
};
|
||||||
|
|
||||||
var node = nodeGroup.append("circle")
|
var node = nodeGroup.append("circle")
|
||||||
.attr("r", 13)
|
.attr("r", 13)
|
||||||
.attr("fill", d => d.color)
|
.attr("fill", function(d){ return d.color || colorByType[d.type] || "#D3D3D3"; })
|
||||||
|
.style("filter", "url(#glow)")
|
||||||
|
.attr("shape-rendering", "geometricPrecision")
|
||||||
.call(d3.drag()
|
.call(d3.drag()
|
||||||
.on("start", dragstarted)
|
.on("start", dragstarted)
|
||||||
.on("drag", dragged)
|
.on("drag", function(d){ dragged(d); updateDensity(); showAdjacency(d); })
|
||||||
.on("end", dragended));
|
.on("end", dragended));
|
||||||
|
|
||||||
nodeGroup.append("text")
|
// Show links only for hovered node adjacency
|
||||||
|
function isAdjacent(linkDatum, nodeId) {
|
||||||
|
var sid = linkDatum && linkDatum.source && (linkDatum.source.id || linkDatum.source);
|
||||||
|
var tid = linkDatum && linkDatum.target && (linkDatum.target.id || linkDatum.target);
|
||||||
|
return sid === nodeId || tid === nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAdjacency(d) {
|
||||||
|
var nodeId = d && (d.id || d.node_id || d.uuid || d.external_id || d.name);
|
||||||
|
if (!nodeId) return;
|
||||||
|
// Build neighbor set
|
||||||
|
var neighborIds = {};
|
||||||
|
neighborIds[nodeId] = true;
|
||||||
|
for (var i = 0; i < links.length; i++) {
|
||||||
|
var l = links[i];
|
||||||
|
var sid = l && l.source && (l.source.id || l.source);
|
||||||
|
var tid = l && l.target && (l.target.id || l.target);
|
||||||
|
if (sid === nodeId) neighborIds[tid] = true;
|
||||||
|
if (tid === nodeId) neighborIds[sid] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
link
|
||||||
|
.style("opacity", function(l){ return isAdjacent(l, nodeId) ? 0.95 : 0; })
|
||||||
|
.style("stroke", function(l){ return isAdjacent(l, nodeId) ? "rgba(255,255,255,0.95)" : null; })
|
||||||
|
.style("stroke-width", function(l){ return isAdjacent(l, nodeId) ? 2.5 : 1.5; });
|
||||||
|
edgeLabels.style("opacity", function(l){ return isAdjacent(l, nodeId) ? 1 : 0; });
|
||||||
|
densityLayer.style("opacity", 0.35);
|
||||||
|
|
||||||
|
// Highlight neighbor nodes and dim others
|
||||||
|
node
|
||||||
|
.style("opacity", function(n){ return neighborIds[n.id] ? 1 : 0.25; })
|
||||||
|
.style("filter", function(n){ return neighborIds[n.id] ? "url(#glow-strong)" : "url(#glow)"; })
|
||||||
|
.attr("r", function(n){ return neighborIds[n.id] ? 15 : 13; });
|
||||||
|
// Raise highlighted nodes
|
||||||
|
node.filter(function(n){ return neighborIds[n.id]; }).raise();
|
||||||
|
// Neighbor labels brighter
|
||||||
|
nodeGroup.select("text")
|
||||||
|
.style("opacity", function(n){ return neighborIds[n.id] ? 1 : 0.2; })
|
||||||
|
.style("font-size", function(n){
|
||||||
|
var size = neighborIds[n.id] ? Math.min(22, labelBaseSize * 1.25) : labelBaseSize;
|
||||||
|
return size + "px";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAdjacency() {
|
||||||
|
link.style("opacity", 0)
|
||||||
|
.style("stroke", null)
|
||||||
|
.style("stroke-width", 1.5);
|
||||||
|
edgeLabels.style("opacity", 0);
|
||||||
|
densityLayer.style("opacity", 1);
|
||||||
|
node
|
||||||
|
.style("opacity", 1)
|
||||||
|
.style("filter", "url(#glow)")
|
||||||
|
.attr("r", 13);
|
||||||
|
nodeGroup.select("text")
|
||||||
|
.style("opacity", 1)
|
||||||
|
.style("font-size", labelBaseSize + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
node.on("mouseover", function(d){ showAdjacency(d); })
|
||||||
|
.on("mouseout", function(){ clearAdjacency(); });
|
||||||
|
node.on("mouseover", function(d){ renderNodeInfo(d); tooltip.style('opacity', 0); });
|
||||||
|
// Also bind on the group so labels trigger adjacency too
|
||||||
|
nodeGroup.on("mouseover", function(d){ showAdjacency(d); })
|
||||||
|
.on("mouseout", function(){ clearAdjacency(); });
|
||||||
|
|
||||||
|
// Density always on; no hover gating
|
||||||
|
|
||||||
|
// Add labels sparsely to reduce clutter (every ~50th node), and truncate long text
|
||||||
|
nodeGroup
|
||||||
|
.filter(function(d, i){ return i % 15 === 0; })
|
||||||
|
.append("text")
|
||||||
.attr("class", "node-label")
|
.attr("class", "node-label")
|
||||||
.attr("dy", 4)
|
.attr("dy", 4)
|
||||||
.attr("text-anchor", "middle")
|
.attr("text-anchor", "middle")
|
||||||
.text(d => d.name);
|
.text(function(d){
|
||||||
|
var s = d && d.name ? String(d.name) : '';
|
||||||
|
return s.length > 40 ? (s.slice(0, 40) + "…") : s;
|
||||||
|
})
|
||||||
|
.style("font-size", labelBaseSize + "px");
|
||||||
|
|
||||||
node.append("title").text(d => JSON.stringify(d));
|
function applyLabelSize() {
|
||||||
|
var k = (currentTransform && currentTransform.k) || 1;
|
||||||
|
// Keep labels readable across zoom levels: shrink slowly with zoom-in, grow slowly when zooming out
|
||||||
|
labelBaseSize = Math.max(7, Math.min(18, 10 / Math.sqrt(k)));
|
||||||
|
nodeGroup.select("text").style("font-size", labelBaseSize + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Density cloud computation (throttled)
|
||||||
|
var densityTick = 0;
|
||||||
|
var geoPath = d3.geoPath().projection(null);
|
||||||
|
var MAX_POINTS_PER_GROUP = 400;
|
||||||
|
function updateDensity() {
|
||||||
|
try {
|
||||||
|
if (typeof d3 === 'undefined' || typeof d3.contourDensity !== 'function') {
|
||||||
|
return; // d3-contour not available; skip gracefully
|
||||||
|
}
|
||||||
|
if (!nodes || nodes.length === 0) return;
|
||||||
|
var usable = nodes.filter(function(d){ return d && typeof d.x === 'number' && isFinite(d.x) && typeof d.y === 'number' && isFinite(d.y); });
|
||||||
|
if (usable.length < 3) return; // not enough positioned points yet
|
||||||
|
|
||||||
|
var t = currentTransform || d3.zoomIdentity;
|
||||||
|
if (t.k && t.k < 0.08) {
|
||||||
|
// Skip density at extreme zoom-out to avoid numerical instability/perf issues
|
||||||
|
densityLayer.selectAll('*').remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hexToRgb(hex){
|
||||||
|
if (!hex) return {r: 0, g: 200, b: 255};
|
||||||
|
var c = hex.replace('#','');
|
||||||
|
if (c.length === 3) c = c.split('').map(function(x){ return x+x; }).join('');
|
||||||
|
var num = parseInt(c, 16);
|
||||||
|
return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build groups across all nodes
|
||||||
|
var groups = {};
|
||||||
|
for (var i = 0; i < usable.length; i++) {
|
||||||
|
var k = getGroupKey(usable[i]);
|
||||||
|
if (!groups[k]) groups[k] = [];
|
||||||
|
groups[k].push(usable[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
densityLayer.selectAll('*').remove();
|
||||||
|
|
||||||
|
Object.keys(groups).forEach(function(key){
|
||||||
|
var arr = groups[key];
|
||||||
|
if (!arr || arr.length < 3) return;
|
||||||
|
|
||||||
|
// Transform positions into screen space and sample to cap cost
|
||||||
|
var arrT = [];
|
||||||
|
var step = Math.max(1, Math.floor(arr.length / MAX_POINTS_PER_GROUP));
|
||||||
|
for (var j = 0; j < arr.length; j += step) {
|
||||||
|
var nx = t.applyX(arr[j].x);
|
||||||
|
var ny = t.applyY(arr[j].y);
|
||||||
|
if (isFinite(nx) && isFinite(ny)) {
|
||||||
|
arrT.push({ x: nx, y: ny, type: arr[j].type, color: arr[j].color });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arrT.length < 3) return;
|
||||||
|
|
||||||
|
// Compute adaptive bandwidth based on group spread
|
||||||
|
var cx = 0, cy = 0;
|
||||||
|
for (var k = 0; k < arrT.length; k++){ cx += arrT[k].x; cy += arrT[k].y; }
|
||||||
|
cx /= arrT.length; cy /= arrT.length;
|
||||||
|
var sumR = 0;
|
||||||
|
for (var k2 = 0; k2 < arrT.length; k2++){
|
||||||
|
var dx = arrT[k2].x - cx, dy = arrT[k2].y - cy;
|
||||||
|
sumR += Math.sqrt(dx*dx + dy*dy);
|
||||||
|
}
|
||||||
|
var avgR = sumR / arrT.length;
|
||||||
|
var dynamicBandwidth = Math.max(12, Math.min(80, avgR));
|
||||||
|
var densityBandwidth = dynamicBandwidth / (t.k || 1);
|
||||||
|
|
||||||
|
var contours = d3.contourDensity()
|
||||||
|
.x(function(d){ return d.x; })
|
||||||
|
.y(function(d){ return d.y; })
|
||||||
|
.size([width, height])
|
||||||
|
.bandwidth(densityBandwidth)
|
||||||
|
.thresholds(8)
|
||||||
|
(arrT);
|
||||||
|
|
||||||
|
if (!contours || contours.length === 0) return;
|
||||||
|
var maxVal = d3.max(contours, function(d){ return d.value; }) || 1;
|
||||||
|
|
||||||
|
// Use the first node color in the group or fallback neon palette
|
||||||
|
var baseColor = (arr.find(function(d){ return d && d.color; }) || {}).color || '#00c8ff';
|
||||||
|
var rgb = hexToRgb(baseColor);
|
||||||
|
|
||||||
|
var g = densityLayer.append('g').attr('data-group', key);
|
||||||
|
g.selectAll('path')
|
||||||
|
.data(contours)
|
||||||
|
.enter()
|
||||||
|
.append('path')
|
||||||
|
.attr('d', geoPath)
|
||||||
|
.attr('fill', 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')')
|
||||||
|
.attr('stroke', 'none')
|
||||||
|
.style('opacity', function(d){
|
||||||
|
var v = maxVal ? (d.value / maxVal) : 0;
|
||||||
|
var alpha = Math.pow(Math.max(0, Math.min(1, v)), 1.6); // accentuate clusters
|
||||||
|
return 0.65 * alpha; // up to 0.65 opacity at peak density
|
||||||
|
})
|
||||||
|
.style('filter', 'blur(2px)');
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// Reduce impact of any runtime errors during zoom
|
||||||
|
console.warn('Density update failed:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
simulation.on("tick", function() {
|
simulation.on("tick", function() {
|
||||||
link.attr("x1", d => d.source.x)
|
link.attr("x1", d => d.source.x)
|
||||||
|
|
@ -266,10 +614,17 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
.attr("y", d => d.y)
|
.attr("y", d => d.y)
|
||||||
.attr("dy", 4)
|
.attr("dy", 4)
|
||||||
.attr("text-anchor", "middle");
|
.attr("text-anchor", "middle");
|
||||||
|
|
||||||
|
densityTick += 1;
|
||||||
|
if (densityTick % 24 === 0) updateDensity();
|
||||||
});
|
});
|
||||||
|
|
||||||
svg.call(d3.zoom().on("zoom", function() {
|
svg.call(d3.zoom().on("zoom", function() {
|
||||||
container.attr("transform", d3.event.transform);
|
currentTransform = d3.event.transform;
|
||||||
|
container.attr("transform", currentTransform);
|
||||||
|
if (densityZoomTimer) clearTimeout(densityZoomTimer);
|
||||||
|
densityZoomTimer = setTimeout(updateDensity, 120);
|
||||||
|
applyLabelSize();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function dragstarted(d) {
|
function dragstarted(d) {
|
||||||
|
|
@ -295,7 +650,13 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
svg.attr("width", width).attr("height", height);
|
svg.attr("width", width).attr("height", height);
|
||||||
simulation.force("center", d3.forceCenter(width / 2, height / 2));
|
simulation.force("center", d3.forceCenter(width / 2, height / 2));
|
||||||
simulation.alpha(1).restart();
|
simulation.alpha(1).restart();
|
||||||
|
updateDensity();
|
||||||
|
applyLabelSize();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initial density draw
|
||||||
|
updateDensity();
|
||||||
|
applyLabelSize();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svg style="position: fixed; bottom: 10px; right: 10px; width: 150px; height: auto; z-index: 9999;" viewBox="0 0 158 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg style="position: fixed; bottom: 10px; right: 10px; width: 150px; height: auto; z-index: 9999;" viewBox="0 0 158 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
@ -305,8 +666,12 @@ async def cognee_network_visualization(graph_data, destination_file_path: str =
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
html_content = html_template.replace("{nodes}", json.dumps(nodes_list))
|
# Safely embed JSON inside <script> by escaping </ to avoid prematurely closing the tag
|
||||||
html_content = html_content.replace("{links}", json.dumps(links_list))
|
def _safe_json_embed(obj):
|
||||||
|
return json.dumps(obj).replace("</", "<\\/")
|
||||||
|
|
||||||
|
html_content = html_template.replace("{nodes}", _safe_json_embed(nodes_list))
|
||||||
|
html_content = html_content.replace("{links}", _safe_json_embed(links_list))
|
||||||
|
|
||||||
if not destination_file_path:
|
if not destination_file_path:
|
||||||
home_dir = os.path.expanduser("~")
|
home_dir = os.path.expanduser("~")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue