()
| 225 | } |
| 226 | |
| 227 | draw() { |
| 228 | this.set_ranges(); |
| 229 | const x_scale = this.scales.x; |
| 230 | const y_scale = this.scales.y; |
| 231 | const link_color_scale = this.scales.link_color; |
| 232 | |
| 233 | // clean up the old graph |
| 234 | this.d3el.selectAll('.node').remove(); |
| 235 | this.d3el.selectAll('.link').remove(); |
| 236 | |
| 237 | if (x_scale && y_scale) { |
| 238 | //set x and y on mark data manually |
| 239 | this.model.mark_data.forEach((d) => { |
| 240 | d.x = x_scale.scale(d.xval) + x_scale.offset; |
| 241 | d.y = y_scale.scale(d.yval) + y_scale.offset; |
| 242 | }); |
| 243 | } |
| 244 | |
| 245 | const box = this.parent.fig.node().getBBox(); |
| 246 | const width = box.width; |
| 247 | const height = box.height; |
| 248 | this.force_layout = d3 |
| 249 | .forceSimulation<NodeData>() |
| 250 | .force('center', d3.forceCenter(width / 2, height / 2)) |
| 251 | .force( |
| 252 | 'forceX', |
| 253 | d3 |
| 254 | .forceX() |
| 255 | .strength(0.1) |
| 256 | .x(width / 2) |
| 257 | ) |
| 258 | .force( |
| 259 | 'forceY', |
| 260 | d3 |
| 261 | .forceY() |
| 262 | .strength(0.1) |
| 263 | .y(height / 2) |
| 264 | ); |
| 265 | |
| 266 | if (!x_scale && !y_scale) { |
| 267 | this.force_layout |
| 268 | .force('charge', d3.forceManyBody().strength(this.model.get('charge'))) |
| 269 | .on('tick', this.tick.bind(this)); |
| 270 | } |
| 271 | |
| 272 | this.links = this.d3el |
| 273 | .selectAll('.link') |
| 274 | .data(this.model.link_data) |
| 275 | .enter() |
| 276 | .append('path') |
| 277 | .attr('class', 'link') |
| 278 | .style('stroke', (d) => { |
| 279 | return link_color_scale ? link_color_scale.scale(d.value) : null; |
| 280 | }) |
| 281 | .style('stroke-width', (d: any) => { |
| 282 | return d.link_width; |
| 283 | }) |
| 284 | .attr('marker-end', this.model.directed ? 'url(#arrow)' : null); |
no test coverage detected