@krainovsd/graph 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/lib/cjs/index.cjs +1098 -253
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/esm/index.js +3 -0
  4. package/lib/esm/index.js.map +1 -1
  5. package/lib/esm/lib/draw-time.js +25 -0
  6. package/lib/esm/lib/draw-time.js.map +1 -0
  7. package/lib/esm/lib/get-controls-info.js +541 -0
  8. package/lib/esm/lib/get-controls-info.js.map +1 -0
  9. package/lib/esm/module/GraphCanvas/GraphCanvas.js +365 -155
  10. package/lib/esm/module/GraphCanvas/GraphCanvas.js.map +1 -1
  11. package/lib/esm/module/GraphCanvas/constants/settings.js +26 -7
  12. package/lib/esm/module/GraphCanvas/constants/settings.js.map +1 -1
  13. package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js +9 -4
  14. package/lib/esm/module/GraphCanvas/lib/settings/link-settings-getter.js.map +1 -1
  15. package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js +10 -5
  16. package/lib/esm/module/GraphCanvas/lib/settings/node-settings-getter.js.map +1 -1
  17. package/lib/esm/module/GraphCanvas/lib/utils/animation-by-progress.js +12 -0
  18. package/lib/esm/module/GraphCanvas/lib/utils/animation-by-progress.js.map +1 -0
  19. package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-radius.js +2 -2
  20. package/lib/esm/module/GraphCanvas/lib/utils/calculate-link-position-by-radius.js.map +1 -1
  21. package/lib/esm/module/GraphCanvas/lib/utils/get-particle-position.js +24 -0
  22. package/lib/esm/module/GraphCanvas/lib/utils/get-particle-position.js.map +1 -0
  23. package/lib/esm/module/GraphCanvas/lib/utils/is-node-visible.js +14 -0
  24. package/lib/esm/module/GraphCanvas/lib/utils/is-node-visible.js.map +1 -0
  25. package/lib/esm/module/GraphCanvas/lib/utils/link-iteration-extractor.js +1 -0
  26. package/lib/esm/module/GraphCanvas/lib/utils/link-iteration-extractor.js.map +1 -1
  27. package/lib/esm/module/GraphCanvas/lib/utils/node-iteration-extractor.js +1 -0
  28. package/lib/esm/module/GraphCanvas/lib/utils/node-iteration-extractor.js.map +1 -1
  29. package/lib/index.d.ts +84 -9
  30. package/package.json +4 -2
@@ -19,11 +19,16 @@ import { nodeByPointerGetter } from './lib/utils/node-by-pointer-getter.js';
19
19
  import { nodeIterationExtractor } from './lib/utils/node-iteration-extractor.js';
20
20
  import { drawText } from './lib/utils/draw-text.js';
21
21
  import { calculateLinkPositionByRadius } from './lib/utils/calculate-link-position-by-radius.js';
22
+ import { animationByProgress } from './lib/utils/animation-by-progress.js';
23
+ import { isNodeVisible } from './lib/utils/is-node-visible.js';
24
+ import { getParticlePosition } from './lib/utils/get-particle-position.js';
25
+ import { getDrawTime, setDrawTime } from '../../lib/draw-time.js';
22
26
 
23
27
  class GraphCanvas {
24
28
  /** initial data */
25
29
  nodes;
26
30
  links;
31
+ particles = {};
27
32
  width;
28
33
  height;
29
34
  root;
@@ -43,6 +48,8 @@ class GraphCanvas {
43
48
  draw;
44
49
  eventAbortController;
45
50
  cachedNodeText = {};
51
+ linkOptionsCache = {};
52
+ nodeOptionsCache = {};
46
53
  isDragging = false;
47
54
  highlightedNode = null;
48
55
  highlightedNeighbors = null;
@@ -115,20 +122,35 @@ class GraphCanvas {
115
122
  this.updateData(alpha);
116
123
  }
117
124
  changeSettings(options) {
118
- if (options.graphSettings)
125
+ if (options.graphSettings) {
119
126
  this.graphSettings = graphSettingsGetter(options.graphSettings, this.graphSettings);
120
- if (options.forceSettings)
127
+ this.draw = this.initDraw();
128
+ this.initZoom(this.areaTransform);
129
+ }
130
+ if (options.forceSettings) {
121
131
  this.forceSettings = forceSettingsGetter(options.forceSettings, this.forceSettings);
122
- if (options.linkSettings)
123
- this.linkSettings = linkSettingsGetter(options.linkSettings);
124
- if (options.nodeSettings)
125
- this.nodeSettings = nodeSettingsGetter(options.nodeSettings);
126
- if (options.forceSettings)
132
+ }
133
+ if (options.linkSettings) {
134
+ this.linkSettings = linkSettingsGetter(options.linkSettings, this.linkSettings);
135
+ this.linkOptionsCache = {};
136
+ }
137
+ if (options.nodeSettings) {
138
+ this.nodeSettings = nodeSettingsGetter(options.nodeSettings, this.nodeSettings);
139
+ this.cachedNodeText = {};
140
+ this.nodeOptionsCache = {};
141
+ }
142
+ if (options.forceSettings) {
127
143
  return void this.updateSimulation();
144
+ }
128
145
  this.tick();
129
146
  }
147
+ clearCache() {
148
+ this.nodeOptionsCache = {};
149
+ this.linkOptionsCache = {};
150
+ this.cachedNodeText = {};
151
+ }
130
152
  tick() {
131
- if (!this.simulationWorking)
153
+ if (!this.simulationWorking && !this.highlightWorking)
132
154
  this.draw();
133
155
  }
134
156
  restart(alpha) {
@@ -184,6 +206,8 @@ class GraphCanvas {
184
206
  }
185
207
  clearDataDependencies() {
186
208
  this.cachedNodeText = {};
209
+ this.nodeOptionsCache = {};
210
+ this.linkOptionsCache = {};
187
211
  }
188
212
  updateData(alpha) {
189
213
  this.clearDataDependencies();
@@ -207,7 +231,7 @@ class GraphCanvas {
207
231
  this.initZoom();
208
232
  this.initResize();
209
233
  this.initPointer();
210
- if (!this.simulationWorking)
234
+ if (!this.simulationWorking && !this.highlightWorking)
211
235
  this.draw();
212
236
  }
213
237
  init() {
@@ -228,6 +252,8 @@ class GraphCanvas {
228
252
  })
229
253
  .on("end", () => {
230
254
  this.listeners.onSimulationEnd?.(this.state);
255
+ if (this.graphSettings.showDrawTime)
256
+ getDrawTime();
231
257
  });
232
258
  this.initSimulationForces();
233
259
  }
@@ -250,9 +276,9 @@ class GraphCanvas {
250
276
  .distanceMax(this.forceSettings.chargeDistanceMax)
251
277
  .distanceMin(this.forceSettings.chargeDistanceMin))
252
278
  .force("center", forceCenter(this.forceSettings.centerPosition.x ?? 0, this.forceSettings.centerPosition.y ?? 0).strength(this.forceSettings.centerStrength));
253
- this.initCollideForce();
279
+ this.initCollideForce(true);
254
280
  }
255
- initCollideForce() {
281
+ initCollideForce(forceUpdate) {
256
282
  if (!this.simulation)
257
283
  return;
258
284
  if (!this.forceSettings.collideOn) {
@@ -263,9 +289,10 @@ class GraphCanvas {
263
289
  const isHasMax = this.forceSettings.collideOffMax.links != 0 && this.forceSettings.collideOffMax.nodes != 0;
264
290
  const isMaxCollideNodes = isHasMax && this.forceSettings.collideOffMax.nodes < this.nodes.length;
265
291
  const isMaxCollideLinks = isHasMax && this.forceSettings.collideOffMax.links < this.links.length;
266
- if (isMaxCollideNodes && isMaxCollideLinks)
292
+ if (isMaxCollideNodes && isMaxCollideLinks) {
267
293
  this.simulation.force("collide", null);
268
- else if (!this.simulation.force("collide"))
294
+ }
295
+ else if (!this.simulation.force("collide") || forceUpdate) {
269
296
  this.simulation.force("collide", forceCollide()
270
297
  .radius((node, index) => {
271
298
  if (this.forceSettings.collideRadius) {
@@ -283,6 +310,7 @@ class GraphCanvas {
283
310
  })
284
311
  .strength(this.forceSettings.collideStrength)
285
312
  .iterations(this.forceSettings.collideIterations));
313
+ }
286
314
  }
287
315
  initArea() {
288
316
  if (!this.area || !this.context || !this.container) {
@@ -317,27 +345,36 @@ class GraphCanvas {
317
345
  this.highlightProgress -= this.graphSettings.highlightDownStep;
318
346
  if (!this.simulationWorking)
319
347
  return void requestAnimationFrame(() => this.draw());
320
- return;
348
+ if (!this.linkSettings.particles)
349
+ return;
321
350
  }
322
351
  if (this.highlightWorking && this.highlightProgress < 1) {
323
352
  this.highlightProgress += this.graphSettings.highlightUpStep;
324
353
  if (!this.simulationWorking)
325
354
  return void requestAnimationFrame(() => this.draw());
326
- return;
355
+ if (!this.linkSettings.particles)
356
+ return;
357
+ }
358
+ if (this.linkSettings.particles && this.highlightWorking && !this.simulationWorking) {
359
+ return void requestAnimationFrame(() => this.draw());
327
360
  }
328
361
  if (!this.highlightWorking && this.highlightProgress <= 0) {
329
- if (this.highlightedNeighbors)
362
+ if (this.highlightedNeighbors || this.highlightedNode) {
330
363
  this.highlightedNeighbors = null;
331
- if (this.highlightedNode)
332
364
  this.highlightedNode = null;
365
+ this.particles = {};
366
+ if (!this.simulationWorking)
367
+ return void requestAnimationFrame(() => this.draw());
368
+ }
333
369
  }
334
370
  this.highlightDrawing = false;
335
371
  }
336
372
  function draw() {
337
373
  if (!this.context)
338
374
  return;
375
+ const state = this.state;
339
376
  if (this.listeners.onDraw) {
340
- this.listeners.onDraw(this.state, (status) => {
377
+ this.listeners.onDraw(state, (status) => {
341
378
  this.highlightDrawing = status;
342
379
  }, () => {
343
380
  if (this.highlightedNeighbors)
@@ -351,160 +388,331 @@ class GraphCanvas {
351
388
  this.context.clearRect(0, 0, this.width, this.height);
352
389
  this.context.translate(this.areaTransform.x, this.areaTransform.y);
353
390
  this.context.scale(this.areaTransform.k, this.areaTransform.k);
391
+ const textRenders = [];
392
+ const nodeRenders = [];
393
+ this.nodes.forEach(getDrawNode(nodeRenders, textRenders, state).bind(this));
354
394
  /** links */
355
- this.context.beginPath();
356
- this.links.forEach(drawLink.bind(this));
357
- this.context.stroke();
395
+ this.links.forEach(getDrawLink(state).bind(this));
358
396
  /** nodes */
359
- const textRenders = [];
360
- this.nodes.forEach(drawNode(textRenders).bind(this));
397
+ nodeRenders.forEach((render) => render());
361
398
  textRenders.forEach((render) => render());
362
399
  this.context.restore();
363
- this.listeners.onDrawFinished?.(this.state);
400
+ this.listeners.onDrawFinished?.(state);
364
401
  calculateHighlightFading.bind(this)();
365
402
  }
366
- function drawLink(link, index) {
367
- if (!this.context ||
368
- typeof link.source !== "object" ||
369
- typeof link.target !== "object" ||
370
- !link.source.x ||
371
- !link.source.y ||
372
- !link.target.x ||
373
- !link.target.y)
374
- return;
375
- const linkOptions = linkIterationExtractor(link, index, this.links, this.state, this.linkSettings.options ?? {}, linkOptionsGetter);
376
- let alpha = linkOptions.alpha;
377
- if (this.highlightedNeighbors && this.highlightedNode) {
378
- /** Not highlighted */
379
- if (this.highlightedNode.id != link.source.id &&
380
- this.highlightedNode.id != link.target.id) {
381
- if (linkOptions.highlightFading)
382
- alpha =
383
- this.graphSettings.highlightLinkFadingMin +
384
- (alpha - this.graphSettings.highlightLinkFadingMin) * (1 - this.highlightProgress);
403
+ function getDrawLink(state) {
404
+ return function drawLink(link, index) {
405
+ if (!this.context ||
406
+ typeof link.source !== "object" ||
407
+ typeof link.target !== "object" ||
408
+ !link.source.x ||
409
+ !link.source.y ||
410
+ !link.target.x ||
411
+ !link.target.y)
412
+ return;
413
+ if (!link.source._visible && !link.target._visible)
414
+ return;
415
+ const id = `${link.target.id}${link.source.id}`;
416
+ let linkOptions;
417
+ if (this.linkSettings.cache && this.linkOptionsCache[id]) {
418
+ linkOptions = this.linkOptionsCache[id];
385
419
  }
420
+ else {
421
+ linkOptions = linkIterationExtractor(link, index, this.links, state, this.linkSettings.options ?? {}, linkOptionsGetter);
422
+ if (this.linkSettings.cache) {
423
+ this.linkOptionsCache[id] = linkOptions;
424
+ }
425
+ }
426
+ if (linkOptions.drawLink) {
427
+ linkOptions.drawLink(link, linkOptions, state);
428
+ return;
429
+ }
430
+ let alpha = linkOptions.alpha;
431
+ let arrowAlpha = linkOptions.arrowReverseAppear ? 0 : linkOptions.arrowAlpha;
432
+ if (this.highlightedNeighbors && this.highlightedNode) {
433
+ /** Not highlighted */
434
+ if (this.highlightedNode.id != link.source.id &&
435
+ this.highlightedNode.id != link.target.id) {
436
+ if (linkOptions.highlightFading) {
437
+ const min = this.graphSettings.highlightLinkFadingMin < alpha
438
+ ? this.graphSettings.highlightLinkFadingMin
439
+ : alpha;
440
+ alpha = animationByProgress(min, alpha - min, 1 - this.highlightProgress);
441
+ }
442
+ if (linkOptions.arrow &&
443
+ linkOptions.arrowHighlightFading &&
444
+ !linkOptions.arrowReverseAppear) {
445
+ const min = this.graphSettings.highlightArrowFadingMin < arrowAlpha
446
+ ? this.graphSettings.highlightArrowFadingMin
447
+ : arrowAlpha;
448
+ arrowAlpha = animationByProgress(min, arrowAlpha - min, 1 - this.highlightProgress);
449
+ }
450
+ }
451
+ else {
452
+ // eslint-disable-next-line no-lonely-if
453
+ if (linkOptions.arrow && linkOptions.arrowReverseAppear) {
454
+ /** Highlighted */
455
+ arrowAlpha = animationByProgress(0, linkOptions.arrowAlpha, this.highlightProgress);
456
+ }
457
+ }
458
+ }
459
+ /** Link */
386
460
  this.context.beginPath();
387
- }
388
- this.context.globalAlpha = alpha;
389
- this.context.strokeStyle = linkOptions.color;
390
- this.context.lineWidth = linkOptions.width;
391
- if (linkOptions.pretty) {
392
- const { x1, x2, y1, y2 } = calculateLinkPositionByRadius(link) ?? {
393
- x1: 0,
394
- x2: 0,
395
- y1: 0,
396
- y2: 0,
397
- };
398
- this.context.moveTo(x1, y1);
399
- this.context.lineTo(x2, y2);
400
- }
401
- else {
402
- this.context.moveTo(link.source.x, link.source.y);
403
- this.context.lineTo(link.target.x, link.target.y);
404
- }
405
- if (this.highlightedNeighbors && this.highlightedNode)
461
+ this.context.globalAlpha = alpha;
462
+ this.context.strokeStyle = linkOptions.color;
463
+ this.context.lineWidth = linkOptions.width;
464
+ let xStart = link.source.x;
465
+ let yStart = link.source.y;
466
+ let xEnd = link.target.x;
467
+ let yEnd = link.target.y;
468
+ if (linkOptions.pretty) {
469
+ const isHasArrow = linkOptions.arrow && arrowAlpha > 0;
470
+ const { x1, x2, y1, y2 } = calculateLinkPositionByRadius(link, isHasArrow ? linkOptions.arrowSize : 0) ?? {
471
+ x1: 0,
472
+ x2: 0,
473
+ y1: 0,
474
+ y2: 0,
475
+ };
476
+ xStart = x1;
477
+ xEnd = x2;
478
+ yStart = y1;
479
+ yEnd = y2;
480
+ }
481
+ this.context.moveTo(xStart, yStart);
482
+ this.context.lineTo(xEnd, yEnd);
406
483
  this.context.stroke();
407
- }
408
- const drawNode = (textRenders) => function drawNode(node, index) {
409
- if (!this.context || !node.x || !node.y)
410
- return;
411
- const nodeOptions = nodeIterationExtractor(node, index, this.nodes, this.state, this.nodeSettings.options ?? {}, nodeOptionsGetter);
412
- let alpha = nodeOptions.alpha;
413
- let color = nodeOptions.color;
414
- let radiusInitial = nodeOptions.radius ?? this.graphSettings.nodeRadiusInitial;
415
- let textAlpha = nodeOptions.textAlpha;
416
- let textSize = nodeOptions.textSize;
417
- let textShiftX = nodeOptions.textShiftX;
418
- let textShiftY = nodeOptions.textShiftY;
419
- let textWeight = nodeOptions.textWeight;
420
- let textWidth = nodeOptions.textWidth;
421
- if (this.highlightedNeighbors && this.highlightedNode) {
422
- /** Not highlighted */
423
- if (!this.highlightedNeighbors.has(node.id) && this.highlightedNode.id != node.id) {
424
- if (nodeOptions.highlightFading) {
425
- alpha =
426
- this.graphSettings.highlightFadingMin +
427
- (alpha - this.graphSettings.highlightFadingMin) * (1 - this.highlightProgress);
428
- }
429
- if (nodeOptions.highlightTextFading) {
430
- textAlpha =
431
- this.graphSettings.highlightTextFadingMin +
432
- (textAlpha - this.graphSettings.highlightTextFadingMin) *
433
- (1 - this.highlightProgress);
484
+ /** Particle */
485
+ if (this.linkSettings.particles &&
486
+ this.highlightedNode &&
487
+ (this.highlightedNode.id === link.source.id || this.highlightedNode.id === link.target.id)) {
488
+ if (!this.particles[id]) {
489
+ const sourceId = link.source.id;
490
+ const targetId = link.target.id;
491
+ this.particles[id] = Array.from({ length: linkOptions.particleCount }, (_, index) => {
492
+ return {
493
+ step: 0,
494
+ wait: index * (linkOptions.particleSteps / linkOptions.particleCount),
495
+ sourceId,
496
+ targetId,
497
+ };
498
+ });
434
499
  }
435
- if (nodeOptions.highlightColor) {
436
- const colorRgb = extractRgb(colorToRgb(color));
437
- if (colorRgb) {
438
- const colorRgbFade = fadeRgb(colorRgb, this.graphSettings.highlightColorFadingMin);
439
- const colorFadeAnimation = rgbAnimationByProgress(colorRgb, colorRgbFade, this.highlightProgress);
440
- color = `rgb(${colorFadeAnimation.r}, ${colorFadeAnimation.g}, ${colorFadeAnimation.b})`;
500
+ this.particles[id].forEach((particle) => {
501
+ if (!this.context)
502
+ return;
503
+ getParticlePosition({
504
+ particle,
505
+ totalSteps: linkOptions.particleSteps,
506
+ xEnd,
507
+ xStart,
508
+ yEnd,
509
+ yStart,
510
+ });
511
+ if (particle.x != undefined && particle.y != undefined) {
512
+ this.context.beginPath();
513
+ this.context.arc(particle.x, particle.y, linkOptions.particleRadius, 0, Math.PI * 2);
514
+ this.context.fillStyle = linkOptions.particleColor;
515
+ this.context.fill();
516
+ this.context.stroke();
441
517
  }
518
+ });
519
+ }
520
+ /** Arrow */
521
+ if (linkOptions.arrow && arrowAlpha > 0) {
522
+ const { x1: xStart, x2: xEnd, y1: yStart, y2: yEnd, } = calculateLinkPositionByRadius(link) ?? {
523
+ x1: 0,
524
+ x2: 0,
525
+ y1: 0,
526
+ y2: 0,
527
+ };
528
+ const angle = Math.atan2(yEnd - yStart, xEnd - xStart);
529
+ this.context.beginPath();
530
+ this.context.globalAlpha = arrowAlpha;
531
+ this.context.moveTo(xEnd, yEnd);
532
+ this.context.lineTo(xEnd - linkOptions.arrowSize * Math.cos(angle - Math.PI / 6), yEnd - linkOptions.arrowSize * Math.sin(angle - Math.PI / 6));
533
+ this.context.lineTo(xEnd - linkOptions.arrowSize * Math.cos(angle + Math.PI / 6), yEnd - linkOptions.arrowSize * Math.sin(angle + Math.PI / 6));
534
+ this.context.closePath();
535
+ this.context.fillStyle = linkOptions.arrowColor;
536
+ this.context.fill();
537
+ this.context.stroke();
538
+ }
539
+ if (linkOptions.drawExtraLink) {
540
+ linkOptions.drawExtraLink(link, { ...linkOptions, alpha }, state);
541
+ }
542
+ };
543
+ }
544
+ function getDrawNode(nodeRenders, textRenders, state) {
545
+ return function drawNode(node, index) {
546
+ if (!this.context || !node.x || !node.y)
547
+ return;
548
+ let nodeOptions;
549
+ if (this.nodeSettings.cache && this.nodeOptionsCache[node.id]) {
550
+ nodeOptions = this.nodeOptionsCache[node.id];
551
+ }
552
+ else {
553
+ nodeOptions = nodeIterationExtractor(node, index, this.nodes, state, this.nodeSettings.options ?? {}, nodeOptionsGetter);
554
+ if (this.nodeSettings.cache) {
555
+ this.nodeOptionsCache[node.id] = nodeOptions;
442
556
  }
443
557
  }
444
- else if (!this.graphSettings.highlightOnlyRoot ||
445
- (this.graphSettings.highlightOnlyRoot && this.highlightedNode.id === node.id)) {
446
- /** Highlighted */
447
- if (nodeOptions.highlightSizing) {
448
- const radiusMax = radiusInitial + this.graphSettings.highlightSizingAdditional;
449
- radiusInitial += ((radiusMax - radiusInitial) / 100) * (this.highlightProgress * 100);
558
+ if (nodeOptions.nodeDraw) {
559
+ nodeOptions.nodeDraw(node, nodeOptions, state);
560
+ return;
561
+ }
562
+ let alpha = nodeOptions.alpha;
563
+ let color = nodeOptions.color;
564
+ let radiusInitial = nodeOptions.radius ?? this.graphSettings.nodeRadiusInitial;
565
+ let textAlpha = nodeOptions.textAlpha;
566
+ let textSize = nodeOptions.textSize;
567
+ let textShiftX = nodeOptions.textShiftX;
568
+ let textShiftY = nodeOptions.textShiftY;
569
+ let textWeight = nodeOptions.textWeight;
570
+ let textWidth = nodeOptions.textWidth;
571
+ if (this.highlightedNeighbors && this.highlightedNode) {
572
+ /** Not highlighted */
573
+ if (!this.highlightedNeighbors.has(node.id) && this.highlightedNode.id != node.id) {
574
+ if (nodeOptions.highlightFading) {
575
+ const min = this.graphSettings.highlightFadingMin < alpha
576
+ ? this.graphSettings.highlightFadingMin
577
+ : alpha;
578
+ alpha = animationByProgress(min, alpha - min, 1 - this.highlightProgress);
579
+ }
580
+ if (nodeOptions.highlightTextFading) {
581
+ const min = this.graphSettings.highlightTextFadingMin < textAlpha
582
+ ? this.graphSettings.highlightTextFadingMin
583
+ : textAlpha;
584
+ textAlpha = animationByProgress(min, textAlpha - min, 1 - this.highlightProgress);
585
+ }
586
+ if (nodeOptions.highlightColor) {
587
+ const colorRgb = extractRgb(colorToRgb(color));
588
+ if (colorRgb) {
589
+ const colorRgbFade = fadeRgb(colorRgb, this.graphSettings.highlightColorFadingMin);
590
+ const colorFadeAnimation = rgbAnimationByProgress(colorRgb, colorRgbFade, this.highlightProgress);
591
+ color = `rgb(${colorFadeAnimation.r}, ${colorFadeAnimation.g}, ${colorFadeAnimation.b})`;
592
+ }
593
+ }
450
594
  }
451
- if (nodeOptions.highlightTextSizing) {
452
- const textSizeMax = textSize + this.graphSettings.highlightTextSizingAdditional;
453
- const textShiftXMax = textShiftX + this.graphSettings.highlightTextShiftXAdditional;
454
- const textShiftYMax = textShiftY + this.graphSettings.highlightTextShiftYAdditional;
455
- const textWeightMax = textWeight + this.graphSettings.highlightTextWeightAdditional;
456
- const textWidthMax = textWidth + this.graphSettings.highlightTextWidthAdditional;
457
- textSize += ((textSizeMax - textSize) / 100) * (this.highlightProgress * 100);
458
- textShiftX += ((textShiftXMax - textShiftX) / 100) * (this.highlightProgress * 100);
459
- textShiftY += ((textShiftYMax - textShiftY) / 100) * (this.highlightProgress * 100);
460
- textWeight += ((textWeightMax - textWeight) / 100) * (this.highlightProgress * 100);
461
- textWidth += ((textWidthMax - textWidth) / 100) * (this.highlightProgress * 100);
595
+ else if (!this.graphSettings.highlightOnlyRoot ||
596
+ (this.graphSettings.highlightOnlyRoot && this.highlightedNode.id === node.id)) {
597
+ /** Highlighted */
598
+ if (nodeOptions.highlightSizing) {
599
+ radiusInitial = animationByProgress(radiusInitial, this.graphSettings.highlightSizingAdditional, this.highlightProgress);
600
+ }
601
+ if (nodeOptions.highlightTextSizing) {
602
+ textSize = animationByProgress(textSize, this.graphSettings.highlightTextSizingAdditional, this.highlightProgress);
603
+ textShiftX = animationByProgress(textShiftX, this.graphSettings.highlightTextShiftXAdditional, this.highlightProgress);
604
+ textShiftY = animationByProgress(textShiftY, this.graphSettings.highlightTextShiftYAdditional, this.highlightProgress);
605
+ textWeight = animationByProgress(textWeight, this.graphSettings.highlightTextWeightAdditional, this.highlightProgress);
606
+ textWidth = animationByProgress(textWidth, this.graphSettings.highlightTextWidthAdditional, this.highlightProgress);
607
+ }
462
608
  }
463
609
  }
464
- }
465
- const radius = nodeRadiusGetter({
466
- radiusFlexible: this.graphSettings.nodeRadiusFlexible,
467
- radiusInitial,
468
- radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
469
- radiusFactor: this.graphSettings.nodeRadiusFactor,
470
- linkCount: node.linkCount,
471
- });
472
- node._radius = radius;
473
- this.context.beginPath();
474
- this.context.globalAlpha = alpha;
475
- /** text */
476
- if (nodeOptions.textVisible && nodeOptions.text) {
477
- textRenders.push(() => {
478
- if (!this.context || !node.x || !node.y || !nodeOptions.text)
610
+ const radius = nodeRadiusGetter({
611
+ radiusFlexible: this.graphSettings.nodeRadiusFlexible,
612
+ radiusInitial,
613
+ radiusCoefficient: this.graphSettings.nodeRadiusCoefficient,
614
+ radiusFactor: this.graphSettings.nodeRadiusFactor,
615
+ linkCount: node.linkCount,
616
+ });
617
+ node._radius = radius;
618
+ if (!isNodeVisible({
619
+ height: this.height,
620
+ width: this.width,
621
+ x: node.x,
622
+ y: node.y,
623
+ radius,
624
+ transform: this.areaTransform,
625
+ })) {
626
+ node._visible = false;
627
+ return;
628
+ }
629
+ node._visible = true;
630
+ nodeRenders.push(() => {
631
+ if (!this.context || !node.x || !node.y)
479
632
  return;
480
633
  this.context.beginPath();
481
- this.context.globalAlpha = textAlpha;
482
- drawText({
483
- id: node.id,
484
- cachedNodeText: this.cachedNodeText,
485
- context: this.context,
486
- text: nodeOptions.text,
487
- textAlign: nodeOptions.textAlign,
488
- textColor: nodeOptions.textColor,
489
- textFont: nodeOptions.textFont,
634
+ this.context.globalAlpha = alpha;
635
+ /** circle */
636
+ this.context.lineWidth = nodeOptions.borderWidth;
637
+ this.context.strokeStyle = nodeOptions.borderColor;
638
+ this.context.fillStyle = color;
639
+ this.context.arc(node.x, node.y, radius, 0, 2 * Math.PI);
640
+ this.context.fill();
641
+ this.context.stroke();
642
+ });
643
+ /** text */
644
+ if (nodeOptions.textVisible && nodeOptions.text) {
645
+ textRenders.push(() => {
646
+ if (nodeOptions.textDraw) {
647
+ nodeOptions.textDraw(node, {
648
+ ...nodeOptions,
649
+ radius,
650
+ alpha,
651
+ color,
652
+ textAlpha,
653
+ textSize,
654
+ textShiftX,
655
+ textShiftY,
656
+ textWeight,
657
+ textWidth,
658
+ }, state);
659
+ return;
660
+ }
661
+ if (!this.context || !node.x || !node.y || !nodeOptions.text)
662
+ return;
663
+ this.context.beginPath();
664
+ this.context.globalAlpha = textAlpha;
665
+ drawText({
666
+ id: node.id,
667
+ cachedNodeText: this.cachedNodeText,
668
+ context: this.context,
669
+ text: nodeOptions.text,
670
+ textAlign: nodeOptions.textAlign,
671
+ textColor: nodeOptions.textColor,
672
+ textFont: nodeOptions.textFont,
673
+ textSize,
674
+ x: node.x + textShiftX,
675
+ y: node.y + radius + textShiftY,
676
+ maxWidth: textWidth,
677
+ textStyle: nodeOptions.textStyle,
678
+ textWeight,
679
+ textGap: nodeOptions.textGap,
680
+ });
681
+ if (nodeOptions.textExtraDraw) {
682
+ nodeOptions.textExtraDraw(node, {
683
+ ...nodeOptions,
684
+ radius,
685
+ alpha,
686
+ color,
687
+ textAlpha,
688
+ textSize,
689
+ textShiftX,
690
+ textShiftY,
691
+ textWeight,
692
+ textWidth,
693
+ }, state);
694
+ }
695
+ });
696
+ }
697
+ if (nodeOptions.nodeExtraDraw) {
698
+ nodeOptions.nodeExtraDraw(node, {
699
+ ...nodeOptions,
700
+ radius,
701
+ alpha,
702
+ color,
703
+ textAlpha,
490
704
  textSize,
491
- x: node.x + textShiftX,
492
- y: node.y + radius + textShiftY,
493
- maxWidth: textWidth,
494
- textStyle: nodeOptions.textStyle,
705
+ textShiftX,
706
+ textShiftY,
495
707
  textWeight,
496
- textGap: nodeOptions.textGap,
497
- });
498
- });
499
- }
500
- /** circle */
501
- this.context.lineWidth = nodeOptions.borderWidth;
502
- this.context.strokeStyle = nodeOptions.borderColor;
503
- this.context.fillStyle = color;
504
- this.context.arc(node.x, node.y, radius, 0, 2 * Math.PI);
505
- this.context.fill();
506
- this.context.stroke();
507
- };
708
+ textWidth,
709
+ }, state);
710
+ }
711
+ };
712
+ }
713
+ if (this.graphSettings.showDrawTime) {
714
+ return setDrawTime(draw.bind(this), this.graphSettings.showDrawTimeEveryTick);
715
+ }
508
716
  return draw;
509
717
  }
510
718
  initResize() {
@@ -698,7 +906,7 @@ class GraphCanvas {
698
906
  this.listeners.onEndDragFinished?.(event, this.state);
699
907
  }));
700
908
  }
701
- initZoom() {
909
+ initZoom(currentZoom) {
702
910
  if (!this.area)
703
911
  throw new Error("bad init data");
704
912
  const zoomInstance = zoom()
@@ -706,7 +914,9 @@ class GraphCanvas {
706
914
  .on("zoom", (event) => {
707
915
  this.listeners.onZoom?.(event);
708
916
  this.areaTransform = event.transform;
709
- if (!this.simulationWorking)
917
+ this.linkOptionsCache = {};
918
+ this.nodeOptionsCache = {};
919
+ if (!this.simulationWorking && !this.highlightWorking)
710
920
  requestAnimationFrame(() => this.draw());
711
921
  });
712
922
  if (this.graphSettings.translateExtentEnable) {
@@ -721,7 +931,7 @@ class GraphCanvas {
721
931
  ]);
722
932
  }
723
933
  select(this.area).call(zoomInstance).on("dblclick.zoom", null);
724
- const zoomInitial = this.graphSettings.zoomInitial;
934
+ const zoomInitial = currentZoom ?? this.graphSettings.zoomInitial;
725
935
  this.areaTransform = new ZoomTransform(zoomInitial?.k ?? 1, zoomInitial?.x ?? this.width / 2, zoomInitial?.y ?? this.height / 2);
726
936
  zoom().transform(select(this.area), this.areaTransform);
727
937
  }