@@ -47,18 +47,24 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
47
47
public readonly halfSize = new Vec2 ( ) ;
48
48
public readonly dynamicForce = new Vec2 ( ) ;
49
49
50
- private _pinned = false ;
51
-
52
50
public depth = - 1 ;
53
51
public hidden = false ;
54
52
55
53
get pinned ( ) {
56
- return this . _pinned ;
54
+ return this . element . classList . contains ( "pinned" ) ;
57
55
}
58
56
59
57
set pinned ( v ) {
60
- this . _pinned = v ;
61
- this . pinner . innerHTML = this . pinned ? pinned : unpinned ;
58
+ this . element . classList . toggle ( "pinned" , v ) ;
59
+ this . pinner . innerHTML = v ? pinned : unpinned ;
60
+ }
61
+
62
+ get grabbed ( ) {
63
+ return this . element . classList . contains ( "top-most" ) ;
64
+ }
65
+
66
+ set grabbed ( v ) {
67
+ this . element . classList . toggle ( "top-most" , v ) ;
62
68
}
63
69
64
70
get moving ( ) {
@@ -74,7 +80,6 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
74
80
75
81
this . pinner = document . createElement ( "button" ) ;
76
82
this . pinner . type = "button" ;
77
- this . pinner . innerHTML = unpinned ;
78
83
this . pinner . style . float = "right" ;
79
84
this . pinner . style . backgroundColor = "transparent" ;
80
85
this . pinner . addEventListener ( "click" , ( ) =>
@@ -90,6 +95,10 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
90
95
this . element . append ( this . pinner , this . content ) ;
91
96
92
97
this . setContent ( content ) ;
98
+
99
+ this . pinned = false ;
100
+ this . grabbed = false ;
101
+ this . moving = false ;
93
102
}
94
103
95
104
setContent ( content : string | HTMLElement ) {
@@ -111,6 +120,40 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
111
120
}
112
121
}
113
122
123
+ applyForces ( nodes : Iterable < ForceDirectedNode < T > > ,
124
+ running : boolean ,
125
+ performLayout : boolean ,
126
+ displayDepth : number ,
127
+ centeringGravity : number ,
128
+ mousePoint : Vec2 ,
129
+ attract : number ,
130
+ repel : number ,
131
+ attractFunc : ( connected : boolean , len : number ) => number ,
132
+ repelFunc : ( connected : boolean , len : number ) => number ,
133
+ getWeightMod : ( connected : boolean , dist : number , a : T , b : T ) => number ) {
134
+ this . dynamicForce . x = 0 ;
135
+ this . dynamicForce . y = 0 ;
136
+
137
+ if ( displayDepth < 0 || this . depth <= displayDepth ) {
138
+ if ( this . grabbed ) {
139
+ this . moveTo ( mousePoint ) ;
140
+ }
141
+ else if ( ! this . pinned
142
+ && running
143
+ && performLayout ) {
144
+
145
+ this . dynamicForce . scaleAndAdd ( this . position , - centeringGravity ) ;
146
+
147
+ for ( const n2 of nodes ) {
148
+ if ( this !== n2
149
+ && ( displayDepth < 0 || n2 . depth <= displayDepth ) ) {
150
+ this . attractRepel ( n2 , attract , attractFunc , repel , repelFunc , getWeightMod ) ;
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+
114
157
updatePosition ( center : Vec2 , maxDepth : number ) {
115
158
const { position, element } = this ;
116
159
element . style . display = this . isVisible ( maxDepth ) ? "" : "none" ;
@@ -129,11 +172,6 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
129
172
. sub ( this . mouseOffset ) ;
130
173
}
131
174
132
- resetForce ( ) {
133
- this . dynamicForce . x = 0 ;
134
- this . dynamicForce . y = 0 ;
135
- }
136
-
137
175
private isVisible ( maxDepth : number ) {
138
176
return ! this . hidden
139
177
&& ( maxDepth < 0
@@ -145,10 +183,6 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
145
183
&& ! this . element . classList . contains ( "not-cycled" ) ;
146
184
}
147
185
148
- gravitate ( gravity : number ) {
149
- this . dynamicForce . scaleAndAdd ( this . position , - gravity ) ;
150
- }
151
-
152
186
attractRepel (
153
187
n2 : ForceDirectedNode < T > ,
154
188
attract : number , attractFunc : ( connected : boolean , len : number ) => number ,
0 commit comments