11<script setup>
22import { ref , computed , onMounted , onBeforeUnmount , watch } from " vue" ;
33import {
4+ adaptColorToBackground ,
5+ applyDataLabel ,
46 convertCustomPalette ,
57 createUid ,
8+ dataLabel ,
69 error ,
710 getMissingDatasetAttributes ,
811 objectIsEmpty ,
912 palette ,
1013 themePalettes ,
14+ translateSize ,
1115 XMLNS
1216} from " ../lib.js" ;
1317import { throttle } from " ../canvas-lib" ;
@@ -107,6 +111,9 @@ const limitedDataset = computed(() => {
107111});
108112
109113const size = ref (FINAL_CONFIG .value .style .size );
114+ const dataLabelSize = ref (FINAL_CONFIG .value .style .weightLabels .size );
115+ const plotRadius = ref (FINAL_CONFIG .value .style .plot .radius );
116+ const labelFontSize = ref (FINAL_CONFIG .value .style .labels .fontSize );
110117
111118const svg = ref ({
112119 height: FINAL_CONFIG .value .style .size ,
@@ -184,6 +191,30 @@ function prepareChart() {
184191 relations .value = [];
185192 createPlots ();
186193 createRelations ();
194+
195+ dataLabelSize .value = translateSize ({
196+ relator: size .value ,
197+ adjuster: FINAL_CONFIG .value .style .size ,
198+ source: FINAL_CONFIG .value .style .weightLabels .size ,
199+ threshold: 6 ,
200+ fallback: 6
201+ });
202+
203+ plotRadius .value = translateSize ({
204+ relator: size .value ,
205+ adjuster: FINAL_CONFIG .value .style .size ,
206+ source: FINAL_CONFIG .value .style .plot .radius ,
207+ threshold: 1 ,
208+ fallback: 1
209+ });
210+
211+ labelFontSize .value = translateSize ({
212+ relator: size .value ,
213+ adjuster: FINAL_CONFIG .value .style .size ,
214+ source: FINAL_CONFIG .value .style .labels .fontSize ,
215+ threshold: 6 ,
216+ fallback: 6
217+ })
187218 });
188219
189220 resizeObserver .value = new ResizeObserver (handleResize);
@@ -227,31 +258,70 @@ function createPlots() {
227258 let angle = 0 ;
228259 let regAngle = 0 ;
229260 limitedDataset .value .forEach ((plot , i ) => {
261+ const totalWeight = plot .weights .reduce ((a , b ) => a + b, 0 );
230262 const x = radius .value * Math .cos (angle) + (svg .value .width / 2 );
231263 const y = radius .value * Math .sin (angle) + svg .value .height / 2 + FINAL_CONFIG .value .style .circle .offsetY ;
232- circles .value .push ({x,y, ... plot, color: plot .color ? plot .color : customPalette .value [i] ? customPalette .value [i] : palette[i], regAngle});
264+ circles .value .push ({x,y, ... plot, color: plot .color ? plot .color : customPalette .value [i] ? customPalette .value [i] : palette[i], regAngle, totalWeight });
233265 angle += angleGap;
234266 regAngle += regAngleGap
235267 });
236268}
237269
270+ function getMiddlePoint (point1 , point2 ) {
271+ const middleX = (point1 .x + point2 .x ) / 2 ;
272+ const middleY = (point1 .y + point2 .y ) / 2 ;
273+ return { x: middleX, y: middleY };
274+ }
275+
238276function createRelations () {
277+ relations .value = [];
239278 circles .value .forEach ((plot ) => {
240279 let rels = circles .value .filter (c => c .relations .includes (plot .id ));
280+
241281 rels .forEach ((relation , i ) => {
282+ const indexOfRelation = relation .relations .indexOf (plot .id )
242283 relations .value .push ({
243- weight: plot .weights ? plot . weights [i ] ? plot .weights [i ] : 1 : 1 ,
284+ weight: relation .weights [indexOfRelation ] ? relation .weights [indexOfRelation ] : 0 ,
244285 relationId: ` ${ plot .id } _${ relation .id } ` ,
245286 x1: plot .x ,
246287 y1: plot .y ,
247288 x2: relation .x ,
248289 y2: relation .y ,
249290 colorSource: plot .color ,
250291 colorTarget: relation .color ,
292+ midPointLine: getMiddlePoint ({x: plot .x , y: plot .y }, {x: relation .x , y: relation .y }),
293+ midPointBezier: getBezierMidpoint ({
294+ x1: plot .x ,
295+ x2: relation .x ,
296+ y1: plot .y ,
297+ y2: relation .y
298+ }),
251299 ... plot
252- })
253- })
254- })
300+ });
301+ });
302+ });
303+ }
304+
305+ function getBezierMidpoint (relation ) {
306+ const P0 = { x: relation .x1 , y: relation .y1 };
307+ const P3 = { x: relation .x2 , y: relation .y2 };
308+ const P1 = { x: relation .x1 , y: relation .y1 };
309+ const P2 = {
310+ x: svg .value .width / 2 ,
311+ y: svg .value .height / 2 + FINAL_CONFIG .value .style .circle .offsetY
312+ };
313+ const t = 0.5 ;
314+ const x = Math .pow (1 - t, 3 ) * P0 .x +
315+ 3 * Math .pow (1 - t, 2 ) * t * P1 .x +
316+ 3 * (1 - t) * Math .pow (t, 2 ) * P2 .x +
317+ Math .pow (t, 3 ) * P3 .x ;
318+
319+ const y = Math .pow (1 - t, 3 ) * P0 .y +
320+ 3 * Math .pow (1 - t, 2 ) * t * P1 .y +
321+ 3 * (1 - t) * Math .pow (t, 2 ) * P2 .y +
322+ Math .pow (t, 3 ) * P3 .y ;
323+
324+ return { x, y };
255325}
256326
257327const maxWeight = computed (() => {
@@ -271,11 +341,7 @@ function getCircleOpacity(plot) {
271341}
272342
273343function getLineColor (plot ) {
274- if (Object .hasOwn (selectedPlot .value , ' x' )) {
275- return plot .colorTarget ;
276- } else {
277- return plot .colorSource ;
278- }
344+ return plot .colorSource
279345}
280346
281347function getLineOpacityAndWidth (plot ) {
@@ -290,6 +356,18 @@ function getLineOpacityAndWidth(plot) {
290356 }
291357}
292358
359+ function showLabel (plot ) {
360+ if (Object .hasOwn (selectedPlot .value , ' x' )) {
361+ if (selectedRelations .value .includes (plot .id ) && plot .relationId === ` ${ plot .id } _${ selectedPlot .value .id } ` || plot .relationId === ` ${ selectedPlot .value .id } _${ plot .id } ` ) {
362+ return true
363+ } else {
364+ return false
365+ }
366+ } else {
367+ return false
368+ }
369+ }
370+
293371function getTextAnchor (plot ) {
294372 if (plot .regAngle > 90 && plot .regAngle < 270 ) {
295373 return " end" ;
@@ -335,7 +413,8 @@ function selectPlot(plot) {
335413}
336414
337415function calcLinkWidth (plot ) {
338- return plot .weight / maxWeight .value * FINAL_CONFIG .value .style .links .maxWidth ;
416+ const w = plot .weight / maxWeight .value * FINAL_CONFIG .value .style .links .maxWidth ;
417+ return Math .max (0.3 , w);
339418}
340419
341420const isFullscreen = ref (false )
@@ -463,11 +542,56 @@ defineExpose({
463542 :stroke-width =" calcLinkWidth(relation)"
464543 stroke-linecap =" round"
465544 />
545+ <g v-for =" (relation, i) in relations" style =" pointer-events : none ;" >
546+ <slot
547+ name =" dataLabel"
548+ v-bind =" {
549+ x: relation.midPointBezier.x,
550+ y: relation.midPointBezier.y,
551+ color: getLineColor(relation),
552+ weight: relation.weight,
553+ fontSize: dataLabelSize
554+ }"
555+ v-if =" showLabel(relation)"
556+ />
557+ <circle
558+ v-if =" showLabel(relation) && !$slots.dataLabel"
559+ :cx =" relation.midPointBezier.x"
560+ :cy =" relation.midPointBezier.y"
561+ :fill =" getLineColor(relation)"
562+ :r =" dataLabelSize"
563+ :stroke =" FINAL_CONFIG.style.backgroundColor"
564+ stroke-width =" 1"
565+ />
566+ <text
567+ v-if =" showLabel(relation) && !$slots.dataLabel"
568+ :x =" relation.midPointBezier.x"
569+ :y =" relation.midPointBezier.y + dataLabelSize / 3"
570+ :fill =" adaptColorToBackground(getLineColor(relation))"
571+ text-anchor =" middle"
572+ :font-size =" dataLabelSize"
573+ >
574+ {{
575+ applyDataLabel(
576+ FINAL_CONFIG.style.weightLabels.formatter,
577+ relation.weight,
578+ dataLabel({
579+ p: FINAL_CONFIG.style.weightLabels.prefix,
580+ v: relation.weight,
581+ s: FINAL_CONFIG.style.weightLabels.suffix,
582+ r: FINAL_CONFIG.style.weightLabels.rounding
583+ }),
584+ { ...relation }
585+ )
586+ }}
587+ </text >
588+ </g >
466589 </g >
467590 <g v-else >
468591 <line v-for =" (relation,i) in relations"
469592 :key =" `relation_${i}`"
470593 :stroke =" getLineColor(relation)"
594+ :stroke-width =" calcLinkWidth(relation)"
471595 :style =" getLineOpacityAndWidth(relation)"
472596 :x1 =" relation.x1"
473597 :x2 =" relation.x2"
@@ -476,6 +600,50 @@ defineExpose({
476600 :class =" {'vue-ui-relation-circle-selected': selectedPlot.hasOwnProperty('id') && selectedRelations.includes(relation.id)}"
477601 stroke-linecap =" round"
478602 />
603+ <g v-for =" (relation, i) in relations" style =" pointer-events : none ;" >
604+ <slot
605+ name =" dataLabel"
606+ v-bind =" {
607+ x: relation.midPointLine.x,
608+ y: relation.midPointLine.y,
609+ color: getLineColor(relation),
610+ weight: relation.weight,
611+ fontSize: dataLabelSize
612+ }"
613+ v-if =" showLabel(relation)"
614+ />
615+ <circle
616+ v-if =" showLabel(relation) && !$slots.dataLabel && FINAL_CONFIG.style.weightLabels.show"
617+ :cx =" relation.midPointLine.x"
618+ :cy =" relation.midPointLine.y"
619+ :fill =" getLineColor(relation)"
620+ :r =" dataLabelSize"
621+ :stroke =" FINAL_CONFIG.style.backgroundColor"
622+ stroke-width =" 1"
623+ />
624+ <text
625+ v-if =" showLabel(relation) && !$slots.dataLabel && FINAL_CONFIG.style.weightLabels.show"
626+ :x =" relation.midPointLine.x"
627+ :y =" relation.midPointLine.y + dataLabelSize / 3"
628+ :fill =" adaptColorToBackground(getLineColor(relation))"
629+ text-anchor =" middle"
630+ :font-size =" dataLabelSize"
631+ >
632+ {{
633+ applyDataLabel(
634+ FINAL_CONFIG.style.weightLabels.formatter,
635+ relation.weight,
636+ dataLabel({
637+ p: FINAL_CONFIG.style.weightLabels.prefix,
638+ v: relation.weight,
639+ s: FINAL_CONFIG.style.weightLabels.suffix,
640+ r: FINAL_CONFIG.style.weightLabels.rounding
641+ }),
642+ { ...relation }
643+ )
644+ }}
645+ </text >
646+ </g >
479647 </g >
480648
481649 <text v-for =" (plot,i) in circles"
@@ -490,10 +658,22 @@ defineExpose({
490658 transform-origin =" start"
491659 :font-weight =" selectedPlot.id === plot.id ? '900' : '400'"
492660 :style =" `font-family:${FINAL_CONFIG.style.fontFamily};${getTextOpacity(plot)}`"
493- :font-size =" FINAL_CONFIG.style.labels.fontSize "
661+ :font-size =" labelFontSize "
494662 :fill =" FINAL_CONFIG.style.labels.color"
495663 >
496- {{plot.label}}
664+ {{plot.label}} ({{
665+ applyDataLabel(
666+ FINAL_CONFIG.style.weightLabels.formatter,
667+ plot.totalWeight,
668+ dataLabel({
669+ p: FINAL_CONFIG.style.weightLabels.prefix,
670+ v: plot.totalWeight,
671+ s: FINAL_CONFIG.style.weightLabels.suffix,
672+ r: FINAL_CONFIG.style.weightLabels.rounding
673+ }),
674+ { ...plot }
675+ )
676+ }})
497677 </text >
498678
499679 <circle v-for =" (plot,i) in circles"
@@ -503,9 +683,11 @@ defineExpose({
503683 :key =" `plot_${i}`"
504684 :style =" getCircleOpacity(plot)"
505685 class =" vue-ui-relation-circle-plot"
506- :fill =" FINAL_CONFIG.style.plot.color"
686+ :fill =" FINAL_CONFIG.style.plot.useSerieColor ? plot.color : FINAL_CONFIG.style.plot.color"
687+ :stroke =" FINAL_CONFIG.style.backgroundColor"
688+ stroke-width =" 1"
507689 @click =" selectPlot(plot)"
508- :r =" FINAL_CONFIG.style.plot.radius "
690+ :r =" plotRadius "
509691 />
510692 <slot name =" svg" :svg =" svg" />
511693 </svg >
0 commit comments