@vope/utils 1.4.19 → 1.4.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.js +337 -10
- package/lib/index.js +6 -2
- package/lib/neo4j-anchors.js +167 -0
- package/lib/neo4j.js +164 -0
- package/package.json +2 -1
- package/types/index.d.ts +2 -0
- package/types/neo4j-anchors.d.ts +45 -0
- package/types/neo4j.d.ts +82 -0
package/cjs/index.js
CHANGED
|
@@ -25,6 +25,8 @@ __export(src_exports, {
|
|
|
25
25
|
MemoryList: () => MemoryList,
|
|
26
26
|
MemoryPower: () => MemoryPower,
|
|
27
27
|
MemorySimple: () => MemorySimple,
|
|
28
|
+
Neo4j: () => Neo4j,
|
|
29
|
+
Neo4jAhchors: () => Neo4jAhchors,
|
|
28
30
|
Subscriber: () => Subscriber,
|
|
29
31
|
Union: () => Union,
|
|
30
32
|
clamp: () => clamp,
|
|
@@ -590,6 +592,341 @@ var MemorySimple = class {
|
|
|
590
592
|
}
|
|
591
593
|
};
|
|
592
594
|
|
|
595
|
+
// src/rotate/rad.ts
|
|
596
|
+
var rotateRad = (from, rad, visit = [0, 0]) => {
|
|
597
|
+
const x = from[0] - visit[0];
|
|
598
|
+
const y = from[1] - visit[1];
|
|
599
|
+
return [
|
|
600
|
+
x * Math.cos(rad) + y * Math.sin(rad) + visit[0],
|
|
601
|
+
y * Math.cos(rad) - x * Math.sin(rad) + visit[1]
|
|
602
|
+
];
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// src/neo4j-anchors.ts
|
|
606
|
+
var Neo4jAhchors = class {
|
|
607
|
+
constructor(neo4j, config) {
|
|
608
|
+
this.neo4j = neo4j;
|
|
609
|
+
this.config = Object.assign({}, config);
|
|
610
|
+
}
|
|
611
|
+
config;
|
|
612
|
+
#anchors = {};
|
|
613
|
+
getDir(start, end) {
|
|
614
|
+
const { neo4j } = this;
|
|
615
|
+
return neo4j.dirKey({
|
|
616
|
+
[neo4j.sourceField]: start,
|
|
617
|
+
[neo4j.targetField]: end
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* 当图数据准备好后调用
|
|
622
|
+
*/
|
|
623
|
+
read() {
|
|
624
|
+
const { neo4j } = this;
|
|
625
|
+
const { nodeField, edgeField, sourceField, targetField } = neo4j;
|
|
626
|
+
const anchors = {};
|
|
627
|
+
neo4j.nodes.forEach((node) => {
|
|
628
|
+
const nodeId = node[nodeField];
|
|
629
|
+
let count = 0;
|
|
630
|
+
[
|
|
631
|
+
neo4j.sources.get(nodeId),
|
|
632
|
+
neo4j.targets.get(nodeId)
|
|
633
|
+
].forEach((list, i) => {
|
|
634
|
+
if (!list)
|
|
635
|
+
return;
|
|
636
|
+
let diffCount = 0;
|
|
637
|
+
let sameCount = 0;
|
|
638
|
+
list.forEach((edge) => {
|
|
639
|
+
const edgeId = edge[edgeField];
|
|
640
|
+
if (edge[sourceField] !== edge[targetField]) {
|
|
641
|
+
const anchor = anchors[edgeId] || (anchors[edgeId] = {});
|
|
642
|
+
anchor[i ? "target" : "source"] = count + diffCount++;
|
|
643
|
+
} else if (edgeId in anchors === false) {
|
|
644
|
+
anchors[edgeId] = {
|
|
645
|
+
source: sameCount,
|
|
646
|
+
target: sameCount++
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
count += diffCount;
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
this.#anchors = anchors;
|
|
654
|
+
}
|
|
655
|
+
calcPoints(edge, index, total, type) {
|
|
656
|
+
const { neo4j, config: { getBox, radOffsetRaidx = 0.3125 } } = this;
|
|
657
|
+
const source = getBox(edge[neo4j.sourceField]);
|
|
658
|
+
const target = getBox(edge[neo4j.targetField]);
|
|
659
|
+
const rad = Math.atan2(
|
|
660
|
+
(source.centerY - target.centerY) * type,
|
|
661
|
+
(source.centerX - target.centerX) * type
|
|
662
|
+
);
|
|
663
|
+
let point = rotateRad([0.5, 0], rad);
|
|
664
|
+
if (total !== 1) {
|
|
665
|
+
point = rotateRad(point, radOffsetRaidx * (total / 2 - 0.5 - index));
|
|
666
|
+
}
|
|
667
|
+
const [x, y] = point;
|
|
668
|
+
return [x + 0.5, 0.5 - y];
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* 获取指定节点上出入边连接点的坐标
|
|
672
|
+
*/
|
|
673
|
+
getPoints(id) {
|
|
674
|
+
const { neo4j } = this;
|
|
675
|
+
const { dirs, edgeField, sourceField, targetField } = neo4j;
|
|
676
|
+
const anchors = this.#anchors;
|
|
677
|
+
const points = [];
|
|
678
|
+
const caches = {};
|
|
679
|
+
neo4j.sources.get(id)?.forEach((edge) => {
|
|
680
|
+
const source = edge[sourceField];
|
|
681
|
+
const target = edge[targetField];
|
|
682
|
+
const direct = this.getDir(source, target);
|
|
683
|
+
const invert = this.getDir(target, source);
|
|
684
|
+
if (direct in caches)
|
|
685
|
+
return;
|
|
686
|
+
const directs = dirs.get(direct) || [];
|
|
687
|
+
const inverts = dirs.get(invert) || [];
|
|
688
|
+
const directsLength = directs.length;
|
|
689
|
+
const invertsLength = inverts.length;
|
|
690
|
+
const total = directsLength + invertsLength;
|
|
691
|
+
directs.forEach((data, index) => {
|
|
692
|
+
points[anchors[data[edgeField]].source] = this.calcPoints(data, directsLength - 1 - index, total, -1);
|
|
693
|
+
});
|
|
694
|
+
caches[direct] = direct;
|
|
695
|
+
});
|
|
696
|
+
neo4j.targets.get(id)?.forEach((edge) => {
|
|
697
|
+
const source = edge[sourceField];
|
|
698
|
+
const target = edge[targetField];
|
|
699
|
+
const direct = this.getDir(source, target);
|
|
700
|
+
const invert = this.getDir(target, source);
|
|
701
|
+
if (direct in caches)
|
|
702
|
+
return;
|
|
703
|
+
const directs = neo4j.dirs.get(direct) || [];
|
|
704
|
+
const inverts = neo4j.dirs.get(invert) || [];
|
|
705
|
+
const directsLength = directs.length;
|
|
706
|
+
const invertsLength = inverts.length;
|
|
707
|
+
const total = directsLength + invertsLength;
|
|
708
|
+
directs.forEach((data, index) => {
|
|
709
|
+
points[anchors[data[edgeField]].target] = this.calcPoints(data, invertsLength + index, total, 1);
|
|
710
|
+
});
|
|
711
|
+
caches[direct] = direct;
|
|
712
|
+
});
|
|
713
|
+
return points;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* 查找边连接点的位置
|
|
717
|
+
*/
|
|
718
|
+
findAnchor(id) {
|
|
719
|
+
const anchor = this.#anchors[id] || {};
|
|
720
|
+
return {
|
|
721
|
+
source: anchor.source || 0,
|
|
722
|
+
target: anchor.target || 0
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* 获取指定边控制点距离两端点连线的距离(可理解为控制边的弯曲程度)
|
|
727
|
+
* @description 返回值是一个相对值,需要乘以一个基数值(基数值应为负值,如-30)
|
|
728
|
+
*/
|
|
729
|
+
getOffset(id) {
|
|
730
|
+
const { neo4j } = this;
|
|
731
|
+
const edge = neo4j.edges.get(id);
|
|
732
|
+
if (!edge)
|
|
733
|
+
return -1;
|
|
734
|
+
const { dirs, edgeField, sourceField, targetField } = neo4j;
|
|
735
|
+
const source = edge[sourceField];
|
|
736
|
+
const target = edge[targetField];
|
|
737
|
+
const directs = dirs.get(this.getDir(source, target)) || [];
|
|
738
|
+
const inverts = dirs.get(this.getDir(target, source)) || [];
|
|
739
|
+
let count = 0;
|
|
740
|
+
let index = 0;
|
|
741
|
+
[directs, inverts].some((list) => {
|
|
742
|
+
const result = list.some((data, i) => {
|
|
743
|
+
if (data[edgeField] === id) {
|
|
744
|
+
index = i + count;
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
count += list.length;
|
|
749
|
+
return result;
|
|
750
|
+
});
|
|
751
|
+
const directsSize = directs.length;
|
|
752
|
+
const invertsSize = inverts.length;
|
|
753
|
+
const total = directsSize + invertsSize;
|
|
754
|
+
if (total === 1)
|
|
755
|
+
return -1;
|
|
756
|
+
if (directsSize === 0 || invertsSize === 0) {
|
|
757
|
+
return total / 2 - index - 0.5;
|
|
758
|
+
} else {
|
|
759
|
+
return Math.abs(directsSize - invertsSize) * (directsSize >= invertsSize ? 1 : -1) / 2 - index - 0.5;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* 清理数据
|
|
764
|
+
*/
|
|
765
|
+
clear() {
|
|
766
|
+
this.#anchors = {};
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// src/neo4j.ts
|
|
771
|
+
var Neo4j = class {
|
|
772
|
+
#nPrimaryKey;
|
|
773
|
+
#ePrimaryKey;
|
|
774
|
+
#sourceKey;
|
|
775
|
+
#targetKey;
|
|
776
|
+
#dirKey;
|
|
777
|
+
#nodes;
|
|
778
|
+
#edges;
|
|
779
|
+
#sources;
|
|
780
|
+
#targets;
|
|
781
|
+
#dirs;
|
|
782
|
+
get nodeField() {
|
|
783
|
+
return this.#nPrimaryKey;
|
|
784
|
+
}
|
|
785
|
+
get edgeField() {
|
|
786
|
+
return this.#ePrimaryKey;
|
|
787
|
+
}
|
|
788
|
+
get sourceField() {
|
|
789
|
+
return this.#sourceKey;
|
|
790
|
+
}
|
|
791
|
+
get targetField() {
|
|
792
|
+
return this.#targetKey;
|
|
793
|
+
}
|
|
794
|
+
get dirKey() {
|
|
795
|
+
return this.#dirKey;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* 点视图
|
|
799
|
+
*/
|
|
800
|
+
nodes;
|
|
801
|
+
/**
|
|
802
|
+
* 边视图
|
|
803
|
+
*/
|
|
804
|
+
edges;
|
|
805
|
+
/**
|
|
806
|
+
* (边)出点视图
|
|
807
|
+
*/
|
|
808
|
+
sources;
|
|
809
|
+
/**
|
|
810
|
+
* (边)入点视图
|
|
811
|
+
*/
|
|
812
|
+
targets;
|
|
813
|
+
/**
|
|
814
|
+
* (边)方向视图
|
|
815
|
+
*/
|
|
816
|
+
dirs;
|
|
817
|
+
constructor(nodeList, edgeList, {
|
|
818
|
+
nid,
|
|
819
|
+
eid,
|
|
820
|
+
sourceKey,
|
|
821
|
+
targetKey,
|
|
822
|
+
dir = (edge) => edge[sourceKey] + "|" + edge[targetKey]
|
|
823
|
+
}) {
|
|
824
|
+
shield(this, "nodes", this.#nodes = new MemorySimple(this.#nPrimaryKey = nid));
|
|
825
|
+
shield(this, "edges", this.#edges = new MemorySimple(this.#ePrimaryKey = eid));
|
|
826
|
+
shield(this, "sources", this.#sources = new MemoryList(this.#sourceKey = sourceKey));
|
|
827
|
+
shield(this, "targets", this.#targets = new MemoryList(this.#targetKey = targetKey));
|
|
828
|
+
shield(this, "dirs", this.#dirs = new MemoryList(this.#dirKey = dir));
|
|
829
|
+
nodeList.forEach((node) => this.node("add", node));
|
|
830
|
+
edgeList.forEach((edge) => this.edge("add", edge));
|
|
831
|
+
}
|
|
832
|
+
node(type, value) {
|
|
833
|
+
const nodes = this.#nodes;
|
|
834
|
+
if (type === "add") {
|
|
835
|
+
nodes.push(value);
|
|
836
|
+
} else if (type === "delete") {
|
|
837
|
+
const node = nodes.get(value);
|
|
838
|
+
if (!node)
|
|
839
|
+
return [];
|
|
840
|
+
nodes.delete(node);
|
|
841
|
+
const id = node[this.#nPrimaryKey];
|
|
842
|
+
const sourceKey = this.#sourceKey;
|
|
843
|
+
const targetKey = this.#targetKey;
|
|
844
|
+
const edges = this.#edges;
|
|
845
|
+
const sources = this.#sources;
|
|
846
|
+
const targets = this.#targets;
|
|
847
|
+
const dirs = this.#dirs;
|
|
848
|
+
const list = [];
|
|
849
|
+
sources.get(id)?.reduce((_, edge) => {
|
|
850
|
+
edges.delete(edge);
|
|
851
|
+
targets.delete(edge);
|
|
852
|
+
edge[sourceKey] === edge[targetKey] || dirs.delete(edge);
|
|
853
|
+
list.push(edge);
|
|
854
|
+
return true;
|
|
855
|
+
}, false) && sources.deleteType(id);
|
|
856
|
+
targets.get(id)?.reduce((_, edge) => {
|
|
857
|
+
edges.delete(edge);
|
|
858
|
+
sources.delete(edge);
|
|
859
|
+
edge[sourceKey] === edge[targetKey] || dirs.delete(edge);
|
|
860
|
+
list.push(edge);
|
|
861
|
+
return true;
|
|
862
|
+
}, false) && targets.deleteType(id);
|
|
863
|
+
return list;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
edge(type, value) {
|
|
867
|
+
const sourceKey = this.#sourceKey;
|
|
868
|
+
const targetKey = this.#targetKey;
|
|
869
|
+
const edges = this.#edges;
|
|
870
|
+
const sources = this.#sources;
|
|
871
|
+
const targets = this.#targets;
|
|
872
|
+
const dirs = this.#dirs;
|
|
873
|
+
if (type === "add") {
|
|
874
|
+
const edge = value;
|
|
875
|
+
edges.push(edge);
|
|
876
|
+
sources.push(edge);
|
|
877
|
+
targets.push(edge);
|
|
878
|
+
edge[sourceKey] === edge[targetKey] || dirs.push(edge);
|
|
879
|
+
} else if (type === "delete") {
|
|
880
|
+
const edge = edges.get(value);
|
|
881
|
+
if (!edge)
|
|
882
|
+
return false;
|
|
883
|
+
edges.delete(edge);
|
|
884
|
+
sources.delete(edge);
|
|
885
|
+
targets.delete(edge);
|
|
886
|
+
edge[sourceKey] === edge[targetKey] || dirs.delete(edge);
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* 清理图管理数据
|
|
892
|
+
*/
|
|
893
|
+
clear() {
|
|
894
|
+
this.#nodes.clear();
|
|
895
|
+
this.#edges.clear();
|
|
896
|
+
this.#sources.clear();
|
|
897
|
+
this.#targets.clear();
|
|
898
|
+
this.#dirs.clear();
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
var shield_keys = hash8strings([
|
|
902
|
+
"push",
|
|
903
|
+
"delete",
|
|
904
|
+
"deleteType",
|
|
905
|
+
"clear"
|
|
906
|
+
]);
|
|
907
|
+
var shield = (obj, key, origin) => {
|
|
908
|
+
const proxy = new Proxy({}, {
|
|
909
|
+
get(target, p) {
|
|
910
|
+
if (p in shield_keys)
|
|
911
|
+
return;
|
|
912
|
+
const value = origin[p];
|
|
913
|
+
if (typeof value === "function") {
|
|
914
|
+
return function(...args) {
|
|
915
|
+
const result = origin[p](...args);
|
|
916
|
+
return result === origin ? proxy : result;
|
|
917
|
+
};
|
|
918
|
+
} else {
|
|
919
|
+
return value;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
Object.defineProperty(obj, key, {
|
|
924
|
+
get() {
|
|
925
|
+
return proxy;
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
};
|
|
929
|
+
|
|
593
930
|
// src/once.ts
|
|
594
931
|
var once = (func, thisArg) => {
|
|
595
932
|
let local;
|
|
@@ -631,16 +968,6 @@ var parseNum = (num) => {
|
|
|
631
968
|
}
|
|
632
969
|
};
|
|
633
970
|
|
|
634
|
-
// src/rotate/rad.ts
|
|
635
|
-
var rotateRad = (from, rad, visit = [0, 0]) => {
|
|
636
|
-
const x = from[0] - visit[0];
|
|
637
|
-
const y = from[1] - visit[1];
|
|
638
|
-
return [
|
|
639
|
-
x * Math.cos(rad) + y * Math.sin(rad) + visit[0],
|
|
640
|
-
y * Math.cos(rad) - x * Math.sin(rad) + visit[1]
|
|
641
|
-
];
|
|
642
|
-
};
|
|
643
|
-
|
|
644
971
|
// src/rotate/angle.ts
|
|
645
972
|
var rotateAngle = (from, angle, visit) => rotateRad(from, angle / 180 * Math.PI, visit);
|
|
646
973
|
|
package/lib/index.js
CHANGED
|
@@ -17,13 +17,15 @@ import { MemoryBasic } from "./memory/basic";
|
|
|
17
17
|
import { MemoryList } from "./memory/list";
|
|
18
18
|
import { MemoryPower } from "./memory/power";
|
|
19
19
|
import { MemorySimple } from "./memory/simple";
|
|
20
|
+
import { default as default2 } from "./neo4j-anchors";
|
|
21
|
+
import { default as default3 } from "./neo4j";
|
|
20
22
|
import { once } from "./once";
|
|
21
23
|
import { parseNum } from "./parse/num";
|
|
22
24
|
import { deconPowersSum } from "./power/decon";
|
|
23
25
|
import { rotateAngle } from "./rotate/angle";
|
|
24
26
|
import { rotateRad } from "./rotate/rad";
|
|
25
27
|
import { css } from "./style/css";
|
|
26
|
-
import { default as
|
|
28
|
+
import { default as default4 } from "./subscriber";
|
|
27
29
|
import { FlexTable } from "./table/flex";
|
|
28
30
|
import { Union } from "./union";
|
|
29
31
|
import { joinPaths } from "./url/join";
|
|
@@ -35,7 +37,9 @@ export {
|
|
|
35
37
|
MemoryList,
|
|
36
38
|
MemoryPower,
|
|
37
39
|
MemorySimple,
|
|
38
|
-
|
|
40
|
+
default3 as Neo4j,
|
|
41
|
+
default2 as Neo4jAhchors,
|
|
42
|
+
default4 as Subscriber,
|
|
39
43
|
Union,
|
|
40
44
|
clamp,
|
|
41
45
|
classnames,
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { rotateRad } from "./rotate/rad";
|
|
2
|
+
class Neo4jAhchors {
|
|
3
|
+
constructor(neo4j, config) {
|
|
4
|
+
this.neo4j = neo4j;
|
|
5
|
+
this.config = Object.assign({}, config);
|
|
6
|
+
}
|
|
7
|
+
config;
|
|
8
|
+
#anchors = {};
|
|
9
|
+
getDir(start, end) {
|
|
10
|
+
const { neo4j } = this;
|
|
11
|
+
return neo4j.dirKey({
|
|
12
|
+
[neo4j.sourceField]: start,
|
|
13
|
+
[neo4j.targetField]: end
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 当图数据准备好后调用
|
|
18
|
+
*/
|
|
19
|
+
read() {
|
|
20
|
+
const { neo4j } = this;
|
|
21
|
+
const { nodeField, edgeField, sourceField, targetField } = neo4j;
|
|
22
|
+
const anchors = {};
|
|
23
|
+
neo4j.nodes.forEach((node) => {
|
|
24
|
+
const nodeId = node[nodeField];
|
|
25
|
+
let count = 0;
|
|
26
|
+
[
|
|
27
|
+
neo4j.sources.get(nodeId),
|
|
28
|
+
neo4j.targets.get(nodeId)
|
|
29
|
+
].forEach((list, i) => {
|
|
30
|
+
if (!list)
|
|
31
|
+
return;
|
|
32
|
+
let diffCount = 0;
|
|
33
|
+
let sameCount = 0;
|
|
34
|
+
list.forEach((edge) => {
|
|
35
|
+
const edgeId = edge[edgeField];
|
|
36
|
+
if (edge[sourceField] !== edge[targetField]) {
|
|
37
|
+
const anchor = anchors[edgeId] || (anchors[edgeId] = {});
|
|
38
|
+
anchor[i ? "target" : "source"] = count + diffCount++;
|
|
39
|
+
} else if (edgeId in anchors === false) {
|
|
40
|
+
anchors[edgeId] = {
|
|
41
|
+
source: sameCount,
|
|
42
|
+
target: sameCount++
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
count += diffCount;
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
this.#anchors = anchors;
|
|
50
|
+
}
|
|
51
|
+
calcPoints(edge, index, total, type) {
|
|
52
|
+
const { neo4j, config: { getBox, radOffsetRaidx = 0.3125 } } = this;
|
|
53
|
+
const source = getBox(edge[neo4j.sourceField]);
|
|
54
|
+
const target = getBox(edge[neo4j.targetField]);
|
|
55
|
+
const rad = Math.atan2(
|
|
56
|
+
(source.centerY - target.centerY) * type,
|
|
57
|
+
(source.centerX - target.centerX) * type
|
|
58
|
+
);
|
|
59
|
+
let point = rotateRad([0.5, 0], rad);
|
|
60
|
+
if (total !== 1) {
|
|
61
|
+
point = rotateRad(point, radOffsetRaidx * (total / 2 - 0.5 - index));
|
|
62
|
+
}
|
|
63
|
+
const [x, y] = point;
|
|
64
|
+
return [x + 0.5, 0.5 - y];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 获取指定节点上出入边连接点的坐标
|
|
68
|
+
*/
|
|
69
|
+
getPoints(id) {
|
|
70
|
+
const { neo4j } = this;
|
|
71
|
+
const { dirs, edgeField, sourceField, targetField } = neo4j;
|
|
72
|
+
const anchors = this.#anchors;
|
|
73
|
+
const points = [];
|
|
74
|
+
const caches = {};
|
|
75
|
+
neo4j.sources.get(id)?.forEach((edge) => {
|
|
76
|
+
const source = edge[sourceField];
|
|
77
|
+
const target = edge[targetField];
|
|
78
|
+
const direct = this.getDir(source, target);
|
|
79
|
+
const invert = this.getDir(target, source);
|
|
80
|
+
if (direct in caches)
|
|
81
|
+
return;
|
|
82
|
+
const directs = dirs.get(direct) || [];
|
|
83
|
+
const inverts = dirs.get(invert) || [];
|
|
84
|
+
const directsLength = directs.length;
|
|
85
|
+
const invertsLength = inverts.length;
|
|
86
|
+
const total = directsLength + invertsLength;
|
|
87
|
+
directs.forEach((data, index) => {
|
|
88
|
+
points[anchors[data[edgeField]].source] = this.calcPoints(data, directsLength - 1 - index, total, -1);
|
|
89
|
+
});
|
|
90
|
+
caches[direct] = direct;
|
|
91
|
+
});
|
|
92
|
+
neo4j.targets.get(id)?.forEach((edge) => {
|
|
93
|
+
const source = edge[sourceField];
|
|
94
|
+
const target = edge[targetField];
|
|
95
|
+
const direct = this.getDir(source, target);
|
|
96
|
+
const invert = this.getDir(target, source);
|
|
97
|
+
if (direct in caches)
|
|
98
|
+
return;
|
|
99
|
+
const directs = neo4j.dirs.get(direct) || [];
|
|
100
|
+
const inverts = neo4j.dirs.get(invert) || [];
|
|
101
|
+
const directsLength = directs.length;
|
|
102
|
+
const invertsLength = inverts.length;
|
|
103
|
+
const total = directsLength + invertsLength;
|
|
104
|
+
directs.forEach((data, index) => {
|
|
105
|
+
points[anchors[data[edgeField]].target] = this.calcPoints(data, invertsLength + index, total, 1);
|
|
106
|
+
});
|
|
107
|
+
caches[direct] = direct;
|
|
108
|
+
});
|
|
109
|
+
return points;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 查找边连接点的位置
|
|
113
|
+
*/
|
|
114
|
+
findAnchor(id) {
|
|
115
|
+
const anchor = this.#anchors[id] || {};
|
|
116
|
+
return {
|
|
117
|
+
source: anchor.source || 0,
|
|
118
|
+
target: anchor.target || 0
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 获取指定边控制点距离两端点连线的距离(可理解为控制边的弯曲程度)
|
|
123
|
+
* @description 返回值是一个相对值,需要乘以一个基数值(基数值应为负值,如-30)
|
|
124
|
+
*/
|
|
125
|
+
getOffset(id) {
|
|
126
|
+
const { neo4j } = this;
|
|
127
|
+
const edge = neo4j.edges.get(id);
|
|
128
|
+
if (!edge)
|
|
129
|
+
return -1;
|
|
130
|
+
const { dirs, edgeField, sourceField, targetField } = neo4j;
|
|
131
|
+
const source = edge[sourceField];
|
|
132
|
+
const target = edge[targetField];
|
|
133
|
+
const directs = dirs.get(this.getDir(source, target)) || [];
|
|
134
|
+
const inverts = dirs.get(this.getDir(target, source)) || [];
|
|
135
|
+
let count = 0;
|
|
136
|
+
let index = 0;
|
|
137
|
+
[directs, inverts].some((list) => {
|
|
138
|
+
const result = list.some((data, i) => {
|
|
139
|
+
if (data[edgeField] === id) {
|
|
140
|
+
index = i + count;
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
count += list.length;
|
|
145
|
+
return result;
|
|
146
|
+
});
|
|
147
|
+
const directsSize = directs.length;
|
|
148
|
+
const invertsSize = inverts.length;
|
|
149
|
+
const total = directsSize + invertsSize;
|
|
150
|
+
if (total === 1)
|
|
151
|
+
return -1;
|
|
152
|
+
if (directsSize === 0 || invertsSize === 0) {
|
|
153
|
+
return total / 2 - index - 0.5;
|
|
154
|
+
} else {
|
|
155
|
+
return Math.abs(directsSize - invertsSize) * (directsSize >= invertsSize ? 1 : -1) / 2 - index - 0.5;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 清理数据
|
|
160
|
+
*/
|
|
161
|
+
clear() {
|
|
162
|
+
this.#anchors = {};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export {
|
|
166
|
+
Neo4jAhchors as default
|
|
167
|
+
};
|
package/lib/neo4j.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { hash8strings } from "./convert/strings";
|
|
2
|
+
import { MemoryList } from "./memory/list";
|
|
3
|
+
import { MemorySimple } from "./memory/simple";
|
|
4
|
+
class Neo4j {
|
|
5
|
+
#nPrimaryKey;
|
|
6
|
+
#ePrimaryKey;
|
|
7
|
+
#sourceKey;
|
|
8
|
+
#targetKey;
|
|
9
|
+
#dirKey;
|
|
10
|
+
#nodes;
|
|
11
|
+
#edges;
|
|
12
|
+
#sources;
|
|
13
|
+
#targets;
|
|
14
|
+
#dirs;
|
|
15
|
+
get nodeField() {
|
|
16
|
+
return this.#nPrimaryKey;
|
|
17
|
+
}
|
|
18
|
+
get edgeField() {
|
|
19
|
+
return this.#ePrimaryKey;
|
|
20
|
+
}
|
|
21
|
+
get sourceField() {
|
|
22
|
+
return this.#sourceKey;
|
|
23
|
+
}
|
|
24
|
+
get targetField() {
|
|
25
|
+
return this.#targetKey;
|
|
26
|
+
}
|
|
27
|
+
get dirKey() {
|
|
28
|
+
return this.#dirKey;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 点视图
|
|
32
|
+
*/
|
|
33
|
+
nodes;
|
|
34
|
+
/**
|
|
35
|
+
* 边视图
|
|
36
|
+
*/
|
|
37
|
+
edges;
|
|
38
|
+
/**
|
|
39
|
+
* (边)出点视图
|
|
40
|
+
*/
|
|
41
|
+
sources;
|
|
42
|
+
/**
|
|
43
|
+
* (边)入点视图
|
|
44
|
+
*/
|
|
45
|
+
targets;
|
|
46
|
+
/**
|
|
47
|
+
* (边)方向视图
|
|
48
|
+
*/
|
|
49
|
+
dirs;
|
|
50
|
+
constructor(nodeList, edgeList, {
|
|
51
|
+
nid,
|
|
52
|
+
eid,
|
|
53
|
+
sourceKey,
|
|
54
|
+
targetKey,
|
|
55
|
+
dir = (edge) => edge[sourceKey] + "|" + edge[targetKey]
|
|
56
|
+
}) {
|
|
57
|
+
shield(this, "nodes", this.#nodes = new MemorySimple(this.#nPrimaryKey = nid));
|
|
58
|
+
shield(this, "edges", this.#edges = new MemorySimple(this.#ePrimaryKey = eid));
|
|
59
|
+
shield(this, "sources", this.#sources = new MemoryList(this.#sourceKey = sourceKey));
|
|
60
|
+
shield(this, "targets", this.#targets = new MemoryList(this.#targetKey = targetKey));
|
|
61
|
+
shield(this, "dirs", this.#dirs = new MemoryList(this.#dirKey = dir));
|
|
62
|
+
nodeList.forEach((node) => this.node("add", node));
|
|
63
|
+
edgeList.forEach((edge) => this.edge("add", edge));
|
|
64
|
+
}
|
|
65
|
+
node(type, value) {
|
|
66
|
+
const nodes = this.#nodes;
|
|
67
|
+
if (type === "add") {
|
|
68
|
+
nodes.push(value);
|
|
69
|
+
} else if (type === "delete") {
|
|
70
|
+
const node = nodes.get(value);
|
|
71
|
+
if (!node)
|
|
72
|
+
return [];
|
|
73
|
+
nodes.delete(node);
|
|
74
|
+
const id = node[this.#nPrimaryKey];
|
|
75
|
+
const sourceKey = this.#sourceKey;
|
|
76
|
+
const targetKey = this.#targetKey;
|
|
77
|
+
const edges = this.#edges;
|
|
78
|
+
const sources = this.#sources;
|
|
79
|
+
const targets = this.#targets;
|
|
80
|
+
const dirs = this.#dirs;
|
|
81
|
+
const list = [];
|
|
82
|
+
sources.get(id)?.reduce((_, edge) => {
|
|
83
|
+
edges.delete(edge);
|
|
84
|
+
targets.delete(edge);
|
|
85
|
+
edge[sourceKey] === edge[targetKey] || dirs.delete(edge);
|
|
86
|
+
list.push(edge);
|
|
87
|
+
return true;
|
|
88
|
+
}, false) && sources.deleteType(id);
|
|
89
|
+
targets.get(id)?.reduce((_, edge) => {
|
|
90
|
+
edges.delete(edge);
|
|
91
|
+
sources.delete(edge);
|
|
92
|
+
edge[sourceKey] === edge[targetKey] || dirs.delete(edge);
|
|
93
|
+
list.push(edge);
|
|
94
|
+
return true;
|
|
95
|
+
}, false) && targets.deleteType(id);
|
|
96
|
+
return list;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
edge(type, value) {
|
|
100
|
+
const sourceKey = this.#sourceKey;
|
|
101
|
+
const targetKey = this.#targetKey;
|
|
102
|
+
const edges = this.#edges;
|
|
103
|
+
const sources = this.#sources;
|
|
104
|
+
const targets = this.#targets;
|
|
105
|
+
const dirs = this.#dirs;
|
|
106
|
+
if (type === "add") {
|
|
107
|
+
const edge = value;
|
|
108
|
+
edges.push(edge);
|
|
109
|
+
sources.push(edge);
|
|
110
|
+
targets.push(edge);
|
|
111
|
+
edge[sourceKey] === edge[targetKey] || dirs.push(edge);
|
|
112
|
+
} else if (type === "delete") {
|
|
113
|
+
const edge = edges.get(value);
|
|
114
|
+
if (!edge)
|
|
115
|
+
return false;
|
|
116
|
+
edges.delete(edge);
|
|
117
|
+
sources.delete(edge);
|
|
118
|
+
targets.delete(edge);
|
|
119
|
+
edge[sourceKey] === edge[targetKey] || dirs.delete(edge);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 清理图管理数据
|
|
125
|
+
*/
|
|
126
|
+
clear() {
|
|
127
|
+
this.#nodes.clear();
|
|
128
|
+
this.#edges.clear();
|
|
129
|
+
this.#sources.clear();
|
|
130
|
+
this.#targets.clear();
|
|
131
|
+
this.#dirs.clear();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const shield_keys = hash8strings([
|
|
135
|
+
"push",
|
|
136
|
+
"delete",
|
|
137
|
+
"deleteType",
|
|
138
|
+
"clear"
|
|
139
|
+
]);
|
|
140
|
+
const shield = (obj, key, origin) => {
|
|
141
|
+
const proxy = new Proxy({}, {
|
|
142
|
+
get(target, p) {
|
|
143
|
+
if (p in shield_keys)
|
|
144
|
+
return;
|
|
145
|
+
const value = origin[p];
|
|
146
|
+
if (typeof value === "function") {
|
|
147
|
+
return function(...args) {
|
|
148
|
+
const result = origin[p](...args);
|
|
149
|
+
return result === origin ? proxy : result;
|
|
150
|
+
};
|
|
151
|
+
} else {
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
Object.defineProperty(obj, key, {
|
|
157
|
+
get() {
|
|
158
|
+
return proxy;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
export {
|
|
163
|
+
Neo4j as default
|
|
164
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vope/utils",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.21",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"typescript": "^5.3.3"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
+
"@type-challenges/utils": "^0.1.1",
|
|
52
53
|
"@types/lodash-es": "^4.17.9",
|
|
53
54
|
"@vope/types": "^1.0.12",
|
|
54
55
|
"csstype": "^3.1.3",
|
package/types/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export { MemoryBasic } from './memory/basic';
|
|
|
17
17
|
export { MemoryList } from './memory/list';
|
|
18
18
|
export { MemoryPower } from './memory/power';
|
|
19
19
|
export { MemorySimple } from './memory/simple';
|
|
20
|
+
export { default as Neo4jAhchors } from './neo4j-anchors';
|
|
21
|
+
export { default as Neo4j } from './neo4j';
|
|
20
22
|
export { once } from './once';
|
|
21
23
|
export { parseNum } from './parse/num';
|
|
22
24
|
export { deconPowersSum } from './power/decon';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Neo4j from './neo4j';
|
|
2
|
+
interface IBox {
|
|
3
|
+
centerX: number;
|
|
4
|
+
centerY: number;
|
|
5
|
+
}
|
|
6
|
+
interface Config {
|
|
7
|
+
radOffsetRaidx?: number;
|
|
8
|
+
getBox(id: string): IBox;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 图布局之二阶贝塞尔曲线边锚点管理
|
|
12
|
+
*/
|
|
13
|
+
export default class Neo4jAhchors<INode extends object, IEdge extends object> {
|
|
14
|
+
#private;
|
|
15
|
+
protected neo4j: Neo4j<INode, IEdge>;
|
|
16
|
+
protected config: Config;
|
|
17
|
+
constructor(neo4j: Neo4j<INode, IEdge>, config: Config);
|
|
18
|
+
protected getDir(start: string, end: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* 当图数据准备好后调用
|
|
21
|
+
*/
|
|
22
|
+
read(): void;
|
|
23
|
+
protected calcPoints(edge: IEdge, index: number, total: number, type: 1 | -1): number[];
|
|
24
|
+
/**
|
|
25
|
+
* 获取指定节点上出入边连接点的坐标
|
|
26
|
+
*/
|
|
27
|
+
getPoints(id: string): number[][];
|
|
28
|
+
/**
|
|
29
|
+
* 查找边连接点的位置
|
|
30
|
+
*/
|
|
31
|
+
findAnchor(id: string): {
|
|
32
|
+
source: number;
|
|
33
|
+
target: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* 获取指定边控制点距离两端点连线的距离(可理解为控制边的弯曲程度)
|
|
37
|
+
* @description 返回值是一个相对值,需要乘以一个基数值(基数值应为负值,如-30)
|
|
38
|
+
*/
|
|
39
|
+
getOffset(id: string): number;
|
|
40
|
+
/**
|
|
41
|
+
* 清理数据
|
|
42
|
+
*/
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|
|
45
|
+
export {};
|
package/types/neo4j.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Equal } from '@type-challenges/utils';
|
|
2
|
+
import { GetGivenProps } from '@vope/types';
|
|
3
|
+
import { MemoryList } from './memory/list';
|
|
4
|
+
import { MemorySimple } from './memory/simple';
|
|
5
|
+
interface Config<INode extends object, IEdge extends object> {
|
|
6
|
+
/**
|
|
7
|
+
* 点主键字段
|
|
8
|
+
*/
|
|
9
|
+
nid: Extract<GetGivenProps<INode, string>, string>;
|
|
10
|
+
/**
|
|
11
|
+
* 边主键字段
|
|
12
|
+
*/
|
|
13
|
+
eid: Extract<GetGivenProps<IEdge, string>, string>;
|
|
14
|
+
/**
|
|
15
|
+
* (边)出点字段
|
|
16
|
+
*/
|
|
17
|
+
sourceKey: Extract<GetGivenProps<IEdge, string>, string>;
|
|
18
|
+
/**
|
|
19
|
+
* (边)入点字段
|
|
20
|
+
*/
|
|
21
|
+
targetKey: Extract<GetGivenProps<IEdge, string>, string>;
|
|
22
|
+
/**
|
|
23
|
+
* (边)方向键
|
|
24
|
+
*/
|
|
25
|
+
dir?(edge: IEdge): string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 图管理
|
|
29
|
+
*/
|
|
30
|
+
export default class Neo4j<INode extends object, IEdge extends object> {
|
|
31
|
+
#private;
|
|
32
|
+
get nodeField(): keyof INode;
|
|
33
|
+
get edgeField(): keyof IEdge;
|
|
34
|
+
get sourceField(): keyof IEdge;
|
|
35
|
+
get targetField(): keyof IEdge;
|
|
36
|
+
get dirKey(): (edge: IEdge) => string;
|
|
37
|
+
/**
|
|
38
|
+
* 点视图
|
|
39
|
+
*/
|
|
40
|
+
nodes: Shield<MemorySimple<INode, string>>;
|
|
41
|
+
/**
|
|
42
|
+
* 边视图
|
|
43
|
+
*/
|
|
44
|
+
edges: Shield<MemorySimple<IEdge, string>>;
|
|
45
|
+
/**
|
|
46
|
+
* (边)出点视图
|
|
47
|
+
*/
|
|
48
|
+
sources: Shield<MemoryList<IEdge, string>>;
|
|
49
|
+
/**
|
|
50
|
+
* (边)入点视图
|
|
51
|
+
*/
|
|
52
|
+
targets: Shield<MemoryList<IEdge, string>>;
|
|
53
|
+
/**
|
|
54
|
+
* (边)方向视图
|
|
55
|
+
*/
|
|
56
|
+
dirs: Shield<MemoryList<IEdge, string>>;
|
|
57
|
+
constructor(nodeList: INode[], edgeList: IEdge[], { nid, eid, sourceKey, targetKey, dir, }: Config<INode, IEdge>);
|
|
58
|
+
/**
|
|
59
|
+
* 点操作
|
|
60
|
+
*/
|
|
61
|
+
node(type: 'add', value: INode): void;
|
|
62
|
+
node(type: 'delete', value: string): IEdge[];
|
|
63
|
+
/**
|
|
64
|
+
* 边操作
|
|
65
|
+
*/
|
|
66
|
+
edge(type: 'add', value: IEdge): void;
|
|
67
|
+
edge(type: 'delete', value: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* 清理图管理数据
|
|
70
|
+
*/
|
|
71
|
+
clear(): void;
|
|
72
|
+
}
|
|
73
|
+
declare const shield_keys: {
|
|
74
|
+
push: number;
|
|
75
|
+
clear: number;
|
|
76
|
+
delete: number;
|
|
77
|
+
deleteType: number;
|
|
78
|
+
};
|
|
79
|
+
type Shield<T> = {
|
|
80
|
+
[P in Exclude<keyof T, keyof typeof shield_keys>]: T[P] extends (...args: infer A) => infer B ? Equal<[B], [T]> extends true ? (...args: A) => Shield<T> : T[P] : T[P];
|
|
81
|
+
};
|
|
82
|
+
export {};
|