diff --git a/frontend/src/Editor.svelte b/frontend/src/Editor.svelte
index db332a2..4c5632b 100644
--- a/frontend/src/Editor.svelte
+++ b/frontend/src/Editor.svelte
@@ -7,7 +7,7 @@
export let tasks: TasksFile;
- let repulsionForce: number = -600;
+ let repulsionForce: number = -1000;
let clicked: string[] = [];
let graph: Graph;
let currentTask: TaskDescriptor | null = null;
diff --git a/frontend/src/Graph.svelte b/frontend/src/Graph.svelte
index 1c48c1b..a164296 100644
--- a/frontend/src/Graph.svelte
+++ b/frontend/src/Graph.svelte
@@ -5,10 +5,11 @@
import * as d3 from "d3";
import type { TasksFile, TaskDescriptor } from "./task-loader";
import { createNodesAndEdges } from "./graph-types";
+ import { taskForce } from "./task-force";
export let tasks: TasksFile;
let hoveredTask: null | string = null;
- export let repulsionForce: number = -600;
+ export let repulsionForce: number = -1000;
// Svelte automatically fills these with a reference
let container: HTMLElement;
@@ -61,7 +62,8 @@
)
.force("charge", d3.forceManyBody().strength(repulsionForce)) // This adds repulsion between nodes. Play with the -400 for the repulsion strength
.force("x", d3.forceX()) // attracts elements to the zero X coord
- .force("y", d3.forceY()) // attracts elements to the zero Y coord
+ .force("y", d3.forceY().strength(0.5)) // attracts elements to the zero Y coord
+ .force("dependencies", taskForce())
.on("tick", ticked)
.on("end", ticked);
diff --git a/frontend/src/GraphEdge.svelte b/frontend/src/GraphEdge.svelte
index c56e8ab..7fe75dd 100644
--- a/frontend/src/GraphEdge.svelte
+++ b/frontend/src/GraphEdge.svelte
@@ -10,5 +10,8 @@
$: y2 = edge === undefined || edge.target === undefined || edge.target.y === undefined ? 0 : edge.target.y;
-
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/task-force.ts b/frontend/src/task-force.ts
new file mode 100644
index 0000000..fa30e90
--- /dev/null
+++ b/frontend/src/task-force.ts
@@ -0,0 +1,50 @@
+import type { TaskId } from "./graph-types";
+
+/* copied from graph-types.ts */
+function toMapById(nodes: TaskId[]): Map {
+ let nodeMap = new Map();
+ for (let task of nodes) {
+ if (task.id in nodeMap)
+ throw 'duplicate IDs';
+ nodeMap.set(task.id, task);
+ }
+ return nodeMap;
+}
+
+export function taskForce(): d3.Force {
+ let myNodes: TaskId[] | null = null;
+ let deps: Map = new Map();
+ let idMap: Map = new Map();
+
+ function getNumberOfDeps(task: TaskId): number {
+ if (deps.has(task.id)) return deps.get(task.id)!;
+
+ if (task.task.requires.length == 0) return 0;
+
+ let res = 0;
+ for (let r of task.task.requires) {
+ res += getNumberOfDeps(idMap.get(r)!) + 1;
+ }
+ deps.set(task.id, res);
+ return res;
+ }
+
+ let force: d3.Force = function(alpha: number) {
+ if (myNodes == null) throw 'nodes not initialized';
+
+ for (let task of myNodes) {
+ if (task.vy == null) {
+ task.vy = 0
+ }
+
+ task.vy += getNumberOfDeps(task) * 25 * alpha;
+ }
+ }
+
+ force.initialize = function(nodes: TaskId[]) {
+ myNodes = nodes;
+ idMap = toMapById(myNodes);
+ }
+
+ return force;
+}