66 createUid ,
77 dataLabel ,
88 error ,
9+ lightenHexColor ,
910 makeDonut ,
1011 objectIsEmpty ,
1112 shiftHue ,
@@ -246,8 +247,8 @@ function buildTicks3D({
246247 cx, cy, radius, innerRatio = 0.8 ,
247248 count = 120 ,
248249 startDeg = 0 ,
249- axDeg = 50 , // tilt angle (bigger = more perspective)
250- f = 520 , // focal length (smaller = stronger perspective)
250+ axDeg = 50 ,
251+ f = 520 ,
251252 baseStroke = 5 ,
252253 activeColor,
253254 inactiveColor,
@@ -260,21 +261,17 @@ function buildTicks3D({
260261 for (let i = 0 ; i < count; i += 1 ) {
261262 const a = ((i / count) * 360 + startDeg) * Math .PI / 180 ;
262263
263- // Points on the circle plane Z=0
264264 const xo = cx + outerR * Math .cos (a);
265265 const yo = cy + outerR * Math .sin (a);
266266 const xi = cx + innerR * Math .cos (a);
267267 const yi = cy + innerR * Math .sin (a);
268268
269- // Move to local ring coords around (0,0) for rotation
270269 const po = [xo - cx, yo - cy, 0 ];
271270 const pi = [xi - cx, yi - cy, 0 ];
272271
273- // Rotate the ring in 3D
274272 const [rxo , ryo , rzo ] = rotateX (po, ax);
275273 const [rxi , ryi , rzi ] = rotateX (pi, ax);
276274
277- // Project to 2D
278275 const [pxo , pyo , , so ] = perspectiveProject ([rxo, ryo, rzo], f);
279276 const [pxi , pyi , , si ] = perspectiveProject ([rxi, ryi, rzi], f);
280277
@@ -349,22 +346,27 @@ function buildCirclePath3D({ cx, cy, r, count = 180, startDeg = -90, axDeg = 50,
349346 return { d, avgScale, pts };
350347}
351348
352-
353349const vb3D = computed (() => {
354350 if (FINAL_CONFIG .value .layout !== ' 3d' ) return null ;
355351
356352 const f = Math .min (svg .value .width , svg .value .height ) * 1.45 ;
357353 const ax = FINAL_CONFIG .value .style .chart .layout .wheel .tiltAngle3d ;
358- const r = wheel .value .radius ;
359354
360- const { pts } = buildCirclePath3D ({
355+
356+ const outerR = wheel .value .radius ;
357+ const { pts , avgScale } = (() => {
358+ const r = outerR;
359+ const axDeg = ax;
360+ const { d , avgScale , pts } = buildCirclePath3D ({
361361 cx: wheel .value .centerX ,
362362 cy: wheel .value .centerY ,
363363 r,
364364 startDeg: - 90 ,
365- axDeg: ax ,
365+ axDeg,
366366 f
367- });
367+ });
368+ return { pts, avgScale };
369+ })();
368370
369371 let minX = Infinity , minY = Infinity , maxX = - Infinity , maxY = - Infinity ;
370372 for (const [x , y ] of pts) {
@@ -373,11 +375,23 @@ const vb3D = computed(() => {
373375 if (x > maxX) maxX = x;
374376 if (y > maxY) maxY = y;
375377 }
378+
379+ const tickStroke =
380+ (FINAL_CONFIG .value .style .chart .layout .wheel .ticks .strokeWidth / 360 ) *
381+ Math .min (svg .value .width , svg .value .height );
382+
383+ const innerStroke = FINAL_CONFIG .value .style .chart .layout .innerCircle .strokeWidth || 0 ;
384+
385+ const strokePad = 0.5 * Math .max (tickStroke, innerStroke * (avgScale || 1 ));
386+ const depthPad = Math .max (0 , Number (FINAL_CONFIG .value .style .chart .layout .wheel .ticks .depth3d ) || 0 );
387+
388+ const pad = strokePad;
389+
376390 return {
377- x: minX,
378- y: minY,
379- w: (maxX - minX),
380- h: (maxY - minY),
391+ x: minX - pad ,
392+ y: ( minY - depthPad) - pad ,
393+ w: (maxX - minX) + 2 * pad ,
394+ h: (maxY - ( minY - depthPad)) + 2 * pad
381395 };
382396});
383397
@@ -422,46 +436,41 @@ function buildArcTicks3D({
422436 f = 600 ,
423437 activeColor,
424438 inactiveColor,
425- getActive
439+ getActive,
440+ Y = 0
426441}) {
427442 const ax = (axDeg * Math .PI ) / 180 ;
428443 const outerR = radius;
429444 const innerR = radius * innerRatio;
430445 const step = (2 * Math .PI ) / count;
431-
432446 const wedges = [];
433447
434448 for (let i = 0 ; i < count; i += 1 ) {
435449 const a0 = ((startDeg * Math .PI ) / 180 ) + step * i;
436- const a1 = a0 + step * Math .min (1 , FINAL_CONFIG .value .style .chart .layout .wheel .ticks .spacingRatio3d ); // 1 = no spacing
437-
438- const o0 = projectRingPoint ({ cx, cy, r: outerR, aRad: a0, ax, f });
439- const o1 = projectRingPoint ({ cx, cy, r: outerR, aRad: a1, ax, f });
440- const i1 = projectRingPoint ({ cx, cy, r: innerR, aRad: a1, ax, f });
441- const i0 = projectRingPoint ({ cx, cy, r: innerR, aRad: a0, ax, f });
442-
450+ const a1 = a0 + step * Math .min (1 , FINAL_CONFIG .value .style .chart .layout .wheel .ticks .spacingRatio3d );
451+ const o0 = projectRingPoint ({ cx, cy: cy + Y , r: outerR, aRad: a0, ax, f });
452+ const o1 = projectRingPoint ({ cx, cy: cy + Y , r: outerR, aRad: a1, ax, f });
453+ const i1 = projectRingPoint ({ cx, cy: cy + Y , r: innerR, aRad: a1, ax, f });
454+ const i0 = projectRingPoint ({ cx, cy: cy + Y , r: innerR, aRad: a0, ax, f });
443455 const zAvg = (o0 .z + o1 .z + i0 .z + i1 .z ) / 4 ;
444-
445456 const isActive = getActive ? getActive (i) : true ;
446-
447457 const base = isActive
448- ? (FINAL_CONFIG .value .style .chart .layout .wheel .ticks .gradient .show
449- ? shiftHue (
450- FINAL_CONFIG .value .style .chart .layout .wheel .ticks .activeColor ,
451- (i * (100 / count)) / 100 *
452- (FINAL_CONFIG .value .style .chart .layout .wheel .ticks .gradient .shiftHueIntensity / 100 )
453- )
454- : activeColor)
455- : inactiveColor;
458+ ? (FINAL_CONFIG .value .style .chart .layout .wheel .ticks .gradient .show
459+ ? shiftHue (
460+ FINAL_CONFIG .value .style .chart .layout .wheel .ticks .activeColor ,
461+ (i * (100 / count)) / 100 *
462+ (FINAL_CONFIG .value .style .chart .layout .wheel .ticks .gradient .shiftHueIntensity / 100 )
463+ )
464+ : activeColor)
465+ : inactiveColor;
456466
457467 const depth = (() => {
458- const amp = outerR * Math .sin (ax) || 1 ;
459- return (zAvg - (- amp)) / (2 * amp);
468+ const amp = outerR * Math .sin (ax) || 1 ;
469+ return (zAvg - (- amp)) / (2 * amp);
460470 })();
461- const fill = shadeColor (base, depth);
462471
472+ const fill = shadeColor (base, depth);
463473 const d = ` M ${ o0 .x } ${ o0 .y } L ${ o1 .x } ${ o1 .y } L ${ i1 .x } ${ i1 .y } L ${ i0 .x } ${ i0 .y } Z` ;
464-
465474 wedges .push ({ i, d, fill, z: zAvg });
466475 }
467476
@@ -473,7 +482,7 @@ const arcTicks3D = computed(() => {
473482 if (FINAL_CONFIG .value .layout !== ' 3d' ) return null ;
474483
475484 const count = tickAmount .value ;
476- return buildArcTicks3D ({
485+ return ( Y ) => buildArcTicks3D ({
477486 cx: wheel .value .centerX ,
478487 cy: wheel .value .centerY ,
479488 radius: wheel .value .radius ,
@@ -484,7 +493,8 @@ const arcTicks3D = computed(() => {
484493 f: Math .min (svg .value .width , svg .value .height ) * 1.45 ,
485494 activeColor: FINAL_CONFIG .value .style .chart .layout .wheel .ticks .activeColor ,
486495 inactiveColor: FINAL_CONFIG .value .style .chart .layout .wheel .ticks .inactiveColor ,
487- getActive : (i ) => activeValue .value > (i * (100 / count))
496+ getActive : (i ) => activeValue .value > (i * (100 / count)),
497+ Y
488498 });
489499});
490500
@@ -509,9 +519,12 @@ function useAnimation(targetValue) {
509519
510520const tickAmount = computed (() => {
511521 if (FINAL_CONFIG .value .debug && FINAL_CONFIG .value .style .chart .layout .wheel .ticks .quantity < 12 ) {
512- console .warn (` VueUiWheel - The minimal number of ticks is 12` )
522+ console .warn (` VueUiWheel - The min number of ticks is 12` );
523+ }
524+ if (FINAL_CONFIG .value .debug && FINAL_CONFIG .value .style .chart .layout .wheel .ticks .quantity > 200 ) {
525+ console .warn (` VueUiWheel - The max number of ticks is 200` );
513526 }
514- return Math .max (12 , FINAL_CONFIG .value .style .chart .layout .wheel .ticks .quantity );
527+ return Math .max (12 , Math . min ( FINAL_CONFIG .value .style .chart .layout .wheel .ticks .quantity , 200 ) );
515528});
516529
517530const percentageToTickAmount = computed (() => 100 / tickAmount .value );
@@ -528,9 +541,9 @@ const ticks = computed(() => {
528541 x2,
529542 y2,
530543 color: FINAL_CONFIG .value .style .chart .layout .wheel .ticks .gradient .show ? shiftHue (color, (i * percentageToTickAmount .value ) / tickAmount .value * (FINAL_CONFIG .value .style .chart .layout .wheel .ticks .gradient .shiftHueIntensity / 100 )) : color
531- })
544+ });
532545 }
533- return tickArray
546+ return tickArray;
534547});
535548
536549const arcTicks = computed (() => {
@@ -570,16 +583,20 @@ async function getImage({ scale = 2} = {}) {
570583}
571584
572585const tickWidthStart = computed (() => {
573- return FINAL_CONFIG .value .style .chart .layout .wheel .ticks .strokeWidth * 2
586+ return FINAL_CONFIG .value .style .chart .layout .wheel .ticks .strokeWidth * 2 ;
574587});
575588
576589const tickWidthMid = computed (() => {
577- return FINAL_CONFIG .value .style .chart .layout .wheel .ticks .strokeWidth * 2 * 0.75
578- })
590+ return FINAL_CONFIG .value .style .chart .layout .wheel .ticks .strokeWidth * 2 * 0.75 ;
591+ });
579592
580593const tickWidthEnd = computed (() => {
581- return FINAL_CONFIG .value .style .chart .layout .wheel .ticks .strokeWidth
582- })
594+ return FINAL_CONFIG .value .style .chart .layout .wheel .ticks .strokeWidth ;
595+ });
596+
597+ const depth3d = computed (() => {
598+ return Math .max (1 , Math .min (20 , FINAL_CONFIG .value .style .chart .layout .wheel .ticks .depth3d ));
599+ });
583600
584601defineExpose ({
585602 getImage,
@@ -703,12 +720,47 @@ defineExpose({
703720 <slot name =" chart-background" />
704721 </foreignObject >
705722
723+ <!-- HOLLOW 3D -->
724+ <path
725+ class =" vue-ui-wheel-inner-circle"
726+ v-if =" FINAL_CONFIG.layout === '3d' && inner3D"
727+ :d =" inner3D.d"
728+ :stroke =" FINAL_CONFIG.style.chart.layout.innerCircle.stroke"
729+ :stroke-width =" FINAL_CONFIG.style.chart.layout.innerCircle.strokeWidth"
730+ fill =" none"
731+ />
732+
733+ <!-- HOLLOW 2D -->
734+ <circle
735+ data-cy =" inner-circle"
736+ class =" vue-ui-wheel-inner-circle"
737+ v-else-if =" FINAL_CONFIG.style.chart.layout.innerCircle.show"
738+ :cx =" wheel.centerX"
739+ :cy =" wheel.centerY"
740+ :r =" Math.max(0, wheel.radius * FINAL_CONFIG.style.chart.layout.innerCircle.radiusRatio * 0.8)"
741+ :stroke =" FINAL_CONFIG.style.chart.layout.innerCircle.stroke"
742+ :stroke-width =" FINAL_CONFIG.style.chart.layout.innerCircle.strokeWidth"
743+ fill =" none"
744+ />
745+
706746 <template v-if =" FINAL_CONFIG .layout === ' 3d' " >
707747 <g v-if =" FINAL_CONFIG.style.chart.layout.wheel.ticks.type === 'classic'" >
748+ <g v-for =" n in depth3d" >
749+ <line
750+ v-for =" t in ticks3D || []"
751+ :key =" t.i"
752+ :x1 =" t.x1" :y1 =" t.y1 - n" :x2 =" t.x2" :y2 =" t.y2 - n"
753+ :stroke =" lightenHexColor(t.color, 0.25 * n / 5)"
754+ :stroke-width =" (FINAL_CONFIG.style.chart.layout.wheel.ticks.strokeWidth / 360) * Math.min(svg.width, svg.height)"
755+ :stroke-linecap =" FINAL_CONFIG.style.chart.layout.wheel.ticks.rounded ? 'round' : 'butt'"
756+ stroke-linecap =" round"
757+ :class =" { 'vue-ui-wheel-tick' : true, 'vue-ui-tick-animated': FINAL_CONFIG.style.chart.animation.use && (t.i * percentageToTickAmount) <= activeValue }"
758+ />
759+ </g >
708760 <line
709761 v-for =" t in ticks3D || []"
710762 :key =" t.i"
711- :x1 =" t.x1" :y1 =" t.y1" :x2 =" t.x2" :y2 =" t.y2"
763+ :x1 =" t.x1" :y1 =" t.y1 - depth3d " :x2 =" t.x2" :y2 =" t.y2 - depth3d "
712764 :stroke =" t.color"
713765 :stroke-width =" (FINAL_CONFIG.style.chart.layout.wheel.ticks.strokeWidth / 360) * Math.min(svg.width, svg.height)"
714766 :stroke-linecap =" FINAL_CONFIG.style.chart.layout.wheel.ticks.rounded ? 'round' : 'butt'"
@@ -717,28 +769,43 @@ defineExpose({
717769 />
718770 </g >
719771 <g v-else >
720- <path
721- v-for =" w in arcTicks3D || []"
722- :key =" w.i"
723- :d =" w.d"
724- :fill =" FINAL_CONFIG.style.chart.layout.wheel.ticks.inactiveColor"
725- :stroke =" FINAL_CONFIG.style.chart.layout.wheel.ticks.stroke"
726- :stroke-width =" FINAL_CONFIG.style.chart.layout.wheel.ticks.strokeWidth"
727- stroke-linecap =" round"
728- stroke-linejoin =" round"
729- class =" vue-ui-wheel-tick"
730- />
731- <path
732- v-for =" w in arcTicks3D || []"
733- :key =" w.i"
734- :d =" w.d"
735- :fill =" w.fill"
736- :stroke =" FINAL_CONFIG.style.chart.layout.wheel.ticks.stroke"
737- :stroke-width =" FINAL_CONFIG.style.chart.layout.wheel.ticks.strokeWidth"
738- stroke-linecap =" round"
739- stroke-linejoin =" round"
740- :class =" { 'vue-ui-wheel-tick' : true, 'vue-ui-tick-animated-3d': FINAL_CONFIG.style.chart.animation.use && (w.i * percentageToTickAmount) <= activeValue }"
741- />
772+ <g v-for =" n in depth3d" >
773+ <path
774+ v-for =" w in arcTicks3D(-n) || []"
775+ :key =" w.i"
776+ :d =" w.d"
777+ :fill =" FINAL_CONFIG.style.chart.layout.wheel.ticks.inactiveColor"
778+ :stroke =" FINAL_CONFIG.style.chart.layout.wheel.ticks.stroke"
779+ :stroke-width =" FINAL_CONFIG.style.chart.layout.wheel.ticks.strokeWidth"
780+ stroke-linecap =" round"
781+ stroke-linejoin =" round"
782+ class =" vue-ui-wheel-tick"
783+ />
784+ <path
785+ v-for =" w in arcTicks3D(-n) || []"
786+ :key =" w.i"
787+ :d =" w.d"
788+ :fill =" lightenHexColor(w.fill, 0.5 * n / depth3d)"
789+ :stroke =" FINAL_CONFIG.style.chart.layout.wheel.ticks.stroke"
790+ :stroke-width =" FINAL_CONFIG.style.chart.layout.wheel.ticks.strokeWidth"
791+ stroke-linecap =" round"
792+ stroke-linejoin =" round"
793+ :class =" { 'vue-ui-wheel-tick' : true, 'vue-ui-tick-animated-3d': FINAL_CONFIG.style.chart.animation.use && (w.i * percentageToTickAmount) <= activeValue }"
794+ />
795+ </g >
796+ <g >
797+ <path
798+ v-for =" w in arcTicks3D(-depth3d) || []"
799+ :key =" w.i"
800+ :d =" w.d"
801+ :fill =" w.fill"
802+ :stroke =" FINAL_CONFIG.style.chart.layout.wheel.ticks.stroke"
803+ :stroke-width =" FINAL_CONFIG.style.chart.layout.wheel.ticks.strokeWidth"
804+ stroke-linecap =" round"
805+ stroke-linejoin =" round"
806+ :class =" { 'vue-ui-wheel-tick' : true, 'vue-ui-tick-animated-3d': FINAL_CONFIG.style.chart.animation.use && (w.i * percentageToTickAmount) <= activeValue }"
807+ />
808+ </g >
742809 </g >
743810 </template >
744811
@@ -769,29 +836,6 @@ defineExpose({
769836 />
770837 </template >
771838 </template >
772-
773- <!-- HOLLOW 3D -->
774- <path
775- class =" vue-ui-wheel-inner-circle"
776- v-if =" FINAL_CONFIG.layout === '3d' && inner3D"
777- :d =" inner3D.d"
778- :stroke =" FINAL_CONFIG.style.chart.layout.innerCircle.stroke"
779- :stroke-width =" FINAL_CONFIG.style.chart.layout.innerCircle.strokeWidth"
780- fill =" none"
781- />
782-
783- <!-- HOLLOW 2D -->
784- <circle
785- data-cy =" inner-circle"
786- class =" vue-ui-wheel-inner-circle"
787- v-else-if =" FINAL_CONFIG.style.chart.layout.innerCircle.show"
788- :cx =" wheel.centerX"
789- :cy =" wheel.centerY"
790- :r =" Math.max(0, wheel.radius * FINAL_CONFIG.style.chart.layout.innerCircle.radiusRatio * 0.8)"
791- :stroke =" FINAL_CONFIG.style.chart.layout.innerCircle.stroke"
792- :stroke-width =" FINAL_CONFIG.style.chart.layout.innerCircle.strokeWidth"
793- fill =" none"
794- />
795839
796840 <g v-if =" FINAL_CONFIG.style.chart.layout.percentage.show" >
797841 <rect
@@ -815,6 +859,8 @@ defineExpose({
815859 style =" font-variant-numeric :tabluar-nums"
816860 :stroke =" FINAL_CONFIG.style.chart.layout.percentage.stroke"
817861 :stroke-width =" FINAL_CONFIG.style.chart.layout.percentage.strokeWidth"
862+ stroke-linecap =" round"
863+ stroke-linejoin =" round"
818864 paint-order =" stroke fill"
819865 :class =" { 'vue-ui-wheel-label': FINAL_CONFIG.layout === '3d' }"
820866 >
0 commit comments