import {forceSimulation, Simulation, SimulationNodeDatum, SimulationLinkDatum, forceManyBody, forceLink, forceCenter, forceCollide } from 'd3-force' import nodeTest from 'node:test' import {Link} from './Link' import {Node} from './Node' export type GraphData = { nodes: Node[] links: Link[] } type typeOfArray = T extends (infer Item)[] ? Item : T export type GraphNodeType = typeOfArray & SimulationNodeDatum export type GraphLinkType = typeOfArray & SimulationLinkDatum export class ForceGraph { //propeties private data : GraphData private width: number private height: number private simulation: Simulation | undefined constructor(width:number, height: number , data?: GraphData){ this.data = data || {nodes: [], links: []} this.width = width this.height = height } //getters getData = () => this.data getWidth = () => this.width getHeighth = () => this.height //setters setData = (data: GraphData) => {this.data = data} setWidth = (width:number) => {this.width = width} setHeight = (height:number) => {this.height = height} start = () => { this.simulation = forceSimulation(this.data.nodes) .force("charge", forceManyBody().strength(0.1)) .force("collide", forceCollide(node => node.getSize()).strength(1)) .force("link", forceLink().id(node => node.id).strength(-1)) .force("center", forceCenter(10, 10).strength(1.2)) this.simulation.on("tick" , this.tick) } getNodes = () => { return this.simulation?.nodes().map(node => ({...node, x: node.x ? node.x < 0 ? Math.max(node.x , -this.width/2 ) : Math.min(node.x , this.width/2) : 0 ,y: node.y ? node.y < 0 ? Math.max(node.y , -this.height/2 ) : Math.min(node.y , this.height/2) : 0 } as GraphNodeType)) || [] } tick = () => { console.log("ticking") } }