blob: 46d3ba708e6a9c27ffc90f9f544058e1db397b5d [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EventEmitter } from '@angular/core';
import { Link } from './link';
import { Node } from './node';
import * as d3 from 'd3';
const FORCES = {
LINKS: 1 / 50,
COLLISION: 1,
CHARGE: -10
};
export interface Options {
width: number;
height: number;
}
export class ForceDirectedGraph {
public ticker: EventEmitter<d3.Simulation<Node, Link>> = new EventEmitter();
public simulation: d3.Simulation<any, any>;
public nodes: Node[] = [];
public links: Link[] = [];
constructor(options: Options) {
this.initSimulation(options);
}
initNodes() {
if (!this.simulation) {
throw new Error('simulation was not initialized yet');
}
this.simulation.nodes(this.nodes);
}
initLinks() {
if (!this.simulation) {
throw new Error('simulation was not initialized yet');
}
// Initializing the links force simulation
this.simulation.force('links',
d3.forceLink(this.links)
.strength(FORCES.LINKS)
);
}
initSimulation(options: Options) {
if (!options || !options.width || !options.height) {
throw new Error('missing options when initializing simulation');
}
/** Creating the simulation */
if (!this.simulation) {
const ticker = this.ticker;
// Creating the force simulation and defining the charges
this.simulation = d3.forceSimulation()
.force('charge',
d3.forceManyBody()
.strength(FORCES.CHARGE)
);
// Connecting the d3 ticker to an angular event emitter
this.simulation.on('tick', function () {
ticker.emit(this);
});
this.initNodes();
this.initLinks();
}
/** Updating the central force of the simulation */
this.simulation.force('centers', d3.forceCenter(options.width / 2, options.height / 2));
/** Restarting the simulation internal timer */
this.simulation.restart();
}
stopSimulation() {
this.simulation.stop();
}
}