Skip to content

Commit f57f728

Browse files
author
Sean McBeth
committed
moving more logic into individual nodes to prep for GPU compute version
1 parent 98ac59c commit f57f728

File tree

2 files changed

+58
-40
lines changed

2 files changed

+58
-40
lines changed

src/Juniper.TypeScript/@juniper-lib/widgets/src/ForceDirectedGraph/ForceDirectedGraph.ts

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,11 @@ export class ForceDirectedGraph<T> {
193193

194194
if (nextGrabbed !== lastGrabbed) {
195195
if (lastGrabbed) {
196-
lastGrabbed.element.classList.remove("top-most");
196+
lastGrabbed.grabbed = false;
197197
}
198198

199199
if (nextGrabbed) {
200-
nextGrabbed.element.classList.add("top-most");
200+
nextGrabbed.grabbed = true;
201201
nextGrabbed.pinned = true;
202202
}
203203
}
@@ -258,7 +258,7 @@ export class ForceDirectedGraph<T> {
258258

259259
const resizer = new ResizeObserver((evts) => {
260260
for (const evt of evts) {
261-
if (evt.target == this.connectorsCanvas) {
261+
if (evt.target === this.connectorsCanvas) {
262262
this.resize();
263263
}
264264
}
@@ -579,30 +579,14 @@ export class ForceDirectedGraph<T> {
579579
attractFunc: (connected: boolean, len: number) => number,
580580
repelFunc: (connected: boolean, len: number) => number) {
581581

582-
for (const node of this.graph.values()) {
583-
node.resetForce();
584-
}
585-
586582
// calculate forces
587583
for (const n1 of this.graph.values()) {
588-
if (this.displayDepth < 0 || n1.depth <= this.displayDepth) {
589-
if (n1 === this.grabbed) {
590-
n1.moveTo(this.mousePoint);
591-
}
592-
else if (!n1.pinned
593-
&& this.running
594-
&& this.performLayout) {
595-
596-
n1.gravitate(this.centeringGravity);
597-
598-
for (const n2 of this.graph.values()) {
599-
if (n1 !== n2
600-
&& (this.displayDepth < 0 || n2.depth <= this.displayDepth)) {
601-
n1.attractRepel(n2, this.attract, attractFunc, this.repel, repelFunc, this.getWeightMod);
602-
}
603-
}
604-
}
605-
}
584+
n1.applyForces(
585+
this.graph.values(),
586+
this.running, this.performLayout, this.displayDepth, this.centeringGravity,
587+
this.mousePoint,
588+
this.attract, this.repel, attractFunc, repelFunc,
589+
this.getWeightMod);
606590
}
607591

608592
for (const n1 of this.graph.values()) {

src/Juniper.TypeScript/@juniper-lib/widgets/src/ForceDirectedGraph/ForceDirectedNode.ts

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,24 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
4747
public readonly halfSize = new Vec2();
4848
public readonly dynamicForce = new Vec2();
4949

50-
private _pinned = false;
51-
5250
public depth = -1;
5351
public hidden = false;
5452

5553
get pinned() {
56-
return this._pinned;
54+
return this.element.classList.contains("pinned");
5755
}
5856

5957
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);
6268
}
6369

6470
get moving() {
@@ -74,7 +80,6 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
7480

7581
this.pinner = document.createElement("button");
7682
this.pinner.type = "button";
77-
this.pinner.innerHTML = unpinned;
7883
this.pinner.style.float = "right";
7984
this.pinner.style.backgroundColor = "transparent";
8085
this.pinner.addEventListener("click", () =>
@@ -90,6 +95,10 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
9095
this.element.append(this.pinner, this.content);
9196

9297
this.setContent(content);
98+
99+
this.pinned = false;
100+
this.grabbed = false;
101+
this.moving = false;
93102
}
94103

95104
setContent(content: string | HTMLElement) {
@@ -111,6 +120,40 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
111120
}
112121
}
113122

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+
114157
updatePosition(center: Vec2, maxDepth: number) {
115158
const { position, element } = this;
116159
element.style.display = this.isVisible(maxDepth) ? "" : "none";
@@ -129,11 +172,6 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
129172
.sub(this.mouseOffset);
130173
}
131174

132-
resetForce() {
133-
this.dynamicForce.x = 0;
134-
this.dynamicForce.y = 0;
135-
}
136-
137175
private isVisible(maxDepth: number) {
138176
return !this.hidden
139177
&& (maxDepth < 0
@@ -145,10 +183,6 @@ export class ForceDirectedNode<T> extends GraphNode<T> {
145183
&& !this.element.classList.contains("not-cycled");
146184
}
147185

148-
gravitate(gravity: number) {
149-
this.dynamicForce.scaleAndAdd(this.position, -gravity);
150-
}
151-
152186
attractRepel(
153187
n2: ForceDirectedNode<T>,
154188
attract: number, attractFunc: (connected: boolean, len: number) => number,

0 commit comments

Comments
 (0)