• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphty-org / graphty-element / 16571675313

28 Jul 2025 02:22PM UTC coverage: 81.618% (+11.7%) from 69.879%
16571675313

push

github

apowers313
test: fix flaky tests

987 of 1241 branches covered (79.53%)

Branch coverage included in aggregate %.

5802 of 7077 relevant lines covered (81.98%)

705.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.74
/src/layout/NGraphLayoutEngine.ts
1
import ngraphCreateLayout, {Layout as NGraphLayout} from "ngraph.forcelayout";
2!
2
import createGraph, {Graph as NGraph, Link as NGraphLink, Node as NGraphNode} from "ngraph.graph";
2!
3
import random from "ngraph.random";
2!
4

5
import type {Edge} from "../Edge";
6
import type {Node} from "../Node";
7
import {EdgePosition, LayoutEngine, Position} from "./LayoutEngine";
2✔
8

9
export class NGraphEngine extends LayoutEngine {
2✔
10
    static type = "ngraph";
2✔
11
    static maxDimensions = 3;
2✔
12
    ngraph: NGraph;
13
    ngraphLayout: NGraphLayout<NGraph>;
14
    nodeMapping = new Map<Node, NGraphNode>();
2✔
15
    edgeMapping = new Map<Edge, NGraphLink>();
2✔
16
    _settled = true;
2✔
17
    _stepCount = 0;
2✔
18
    _lastMoves: number[] = [];
2✔
19

20
    constructor(config: object = {}) {
2✔
21
        super();
189✔
22
        this.ngraph = createGraph();
189✔
23

24
        // Cast config to a more specific type for property access
25
        const typedConfig = config as Record<string, unknown>;
189✔
26

27
        // Build ngraph configuration from provided config
28
        const ngraphConfig: Record<string, unknown> = {
189✔
29
            dimensions: typedConfig.dim !== undefined ? typedConfig.dim : 3,
189✔
30
        };
189✔
31

32
        // Map from layout config to ngraph parameters
33
        if (typedConfig.springLength !== undefined) {
189!
34
            ngraphConfig.springLength = typedConfig.springLength;
×
35
        }
×
36

37
        if (typedConfig.springCoefficient !== undefined) {
189!
38
            ngraphConfig.springCoefficient = typedConfig.springCoefficient;
×
39
        }
×
40

41
        if (typedConfig.gravity !== undefined) {
189!
42
            ngraphConfig.gravity = typedConfig.gravity;
×
43
        }
×
44

45
        if (typedConfig.theta !== undefined) {
189!
46
            ngraphConfig.theta = typedConfig.theta;
×
47
        }
×
48

49
        if (typedConfig.dragCoefficient !== undefined) {
189!
50
            ngraphConfig.dragCoefficient = typedConfig.dragCoefficient;
×
51
        }
×
52

53
        if (typedConfig.timeStep !== undefined) {
189!
54
            ngraphConfig.timeStep = typedConfig.timeStep;
×
55
        }
×
56

57
        // Add random number generator with seed if provided
58
        if (typedConfig.seed !== undefined && typeof typedConfig.seed === "number") {
189✔
59
            ngraphConfig.random = random(typedConfig.seed);
45✔
60
        }
45✔
61

62
        this.ngraphLayout = ngraphCreateLayout(this.ngraph, ngraphConfig);
189✔
63
    }
189✔
64

65
    // eslint-disable-next-line @typescript-eslint/no-empty-function
66
    async init(): Promise<void> {}
2✔
67

68
    step(): void {
2✔
69
        const ngraphSettled = this.ngraphLayout.step();
513✔
70
        const {lastMove} = this.ngraphLayout;
513✔
71
        const nodeCount = this.nodeMapping.size;
513✔
72
        const ratio = nodeCount > 0 ? lastMove / nodeCount : 0;
513✔
73

74
        this._stepCount++;
513✔
75

76
        // Keep track of last 10 moves for averaging
77
        this._lastMoves.push(ratio);
513✔
78
        if (this._lastMoves.length > 10) {
513✔
79
            this._lastMoves.shift();
207✔
80
        }
207✔
81

82
        // Calculate average movement over last 10 steps
83
        const avgMovement = this._lastMoves.length > 0 ?
513✔
84
            this._lastMoves.reduce((a, b) => a + b, 0) / this._lastMoves.length :
513!
85
            0;
×
86

87
        // Use a more forgiving threshold or force settling after many steps
88
        const customThreshold = 0.05; // More forgiving than ngraph's 0.01
513✔
89
        const maxSteps = 1000; // Force settling after 1000 steps
513✔
90

91
        this._settled = ngraphSettled ||
513✔
92
                       avgMovement <= customThreshold ||
429✔
93
                       this._stepCount >= maxSteps;
428✔
94
    }
513✔
95

96
    get isSettled(): boolean {
2✔
97
        return this._settled;
880✔
98
    }
880✔
99

100
    addNode(n: Node): void {
2✔
101
        const ngraphNode: NGraphNode = this.ngraph.addNode(n.id, {parentNode: n});
1,055✔
102
        this.nodeMapping.set(n, ngraphNode);
1,055✔
103
        this._settled = false;
1,055✔
104
        this._stepCount = 0;
1,055✔
105
        this._lastMoves = [];
1,055✔
106
    }
1,055✔
107

108
    addEdge(e: Edge): void {
2✔
109
        const ngraphEdge = this.ngraph.addLink(e.srcId, e.dstId, {parentEdge: this});
1,608✔
110
        this.edgeMapping.set(e, ngraphEdge);
1,608✔
111
        this._settled = false;
1,608✔
112
        this._stepCount = 0;
1,608✔
113
        this._lastMoves = [];
1,608✔
114
    }
1,608✔
115

116
    getNodePosition(n: Node): Position {
2✔
117
        const ngraphNode = this._getMappedNode(n);
8,170✔
118
        return this.ngraphLayout.getNodePosition(ngraphNode.id);
8,170✔
119
    }
8,170✔
120

121
    setNodePosition(n: Node, newPos: Position): void {
2✔
122
        const ngraphNode = this._getMappedNode(n);
×
123
        const currPos = this.ngraphLayout.getNodePosition(ngraphNode.id);
×
124
        currPos.x = newPos.x;
×
125
        currPos.y = newPos.y;
×
126
        currPos.z = newPos.z;
×
127
    }
×
128

129
    getEdgePosition(e: Edge): EdgePosition {
2✔
130
        const ngraphEdge = this._getMappedEdge(e);
12,380✔
131
        const pos = this.ngraphLayout.getLinkPosition(ngraphEdge.id);
12,380✔
132
        return {
12,380✔
133
            src: {
12,380✔
134
                x: pos.from.x,
12,380✔
135
                y: pos.from.y,
12,380✔
136
                z: pos.from.z,
12,380✔
137
            },
12,380✔
138
            dst: {
12,380✔
139
                x: pos.to.x,
12,380✔
140
                y: pos.to.y,
12,380✔
141
                z: pos.to.z,
12,380✔
142
            },
12,380✔
143
        };
12,380✔
144
    }
12,380✔
145

146
    get nodes(): Iterable<Node> {
2✔
147
        // ...is this cheating?
148
        return this.nodeMapping.keys();
550✔
149
    }
550✔
150

151
    get edges(): Iterable<Edge> {
2✔
152
        return this.edgeMapping.keys();
904✔
153
    }
904✔
154

155
    pin(n: Node): void {
2✔
156
        const ngraphNode = this._getMappedNode(n);
×
157
        this.ngraphLayout.pinNode(ngraphNode, true);
×
158
    }
×
159

160
    unpin(n: Node): void {
2✔
161
        const ngraphNode = this._getMappedNode(n);
×
162
        this.ngraphLayout.pinNode(ngraphNode, false);
×
163
    }
×
164

165
    private _getMappedNode(n: Node): NGraphNode {
2✔
166
        const ngraphNode = this.nodeMapping.get(n);
8,170✔
167
        if (!ngraphNode) {
8,170!
168
            throw new Error("Internal error: Node not found in NGraphEngine");
×
169
        }
×
170

171
        return ngraphNode;
8,170✔
172
    }
8,170✔
173

174
    private _getMappedEdge(e: Edge): NGraphLink {
2✔
175
        const ngraphNode = this.edgeMapping.get(e);
12,380✔
176
        if (!ngraphNode) {
12,380!
177
            throw new Error("Internal error: Edge not found in NGraphEngine");
×
178
        }
×
179

180
        return ngraphNode;
12,380✔
181
    }
12,380✔
182
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc