@alphacifer/slidev-addon-theme 0.0.7 → 0.0.9
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.
- package/README.md +186 -76
- package/components/{chevron-card → card/chevron-card}/ChevronCard.vue +2 -2
- package/components/{chevron-card → card/chevron-card}/ChevronCardBadge.vue +1 -1
- package/components/{chevron-card → card/chevron-card}/ChevronCardContent.vue +1 -1
- package/components/{chevron-card → card/chevron-card}/ChevronCardHeading.vue +1 -1
- package/components/{horiz-card → card/horiz-card}/HorizCard.vue +2 -2
- package/components/{horiz-card → card/horiz-card}/HorizCardBadge.vue +2 -2
- package/components/{horiz-card → card/horiz-card}/HorizCardContent.vue +2 -2
- package/components/{horiz-card → card/horiz-card}/HorizCardHeading.vue +2 -2
- package/components/{horiz-card → card/horiz-card}/HorizCardIcon.vue +2 -2
- package/components/{vert-card → card/vert-card}/VertCard.vue +2 -2
- package/components/{vert-card → card/vert-card}/VertCardBadge.vue +5 -2
- package/components/{vert-card → card/vert-card}/VertCardContent.vue +2 -2
- package/components/{vert-card → card/vert-card}/VertCardHeading.vue +2 -2
- package/components/{vert-card → card/vert-card}/VertCardIcon.vue +2 -2
- package/components/{vert-card → card/vert-card}/VertCardTitle.vue +2 -2
- package/components/comparisons/arc-comparison/ArcComparison.vue +654 -0
- package/components/comparisons/arc-comparison/ArcComparisonBadge.vue +29 -0
- package/components/comparisons/arc-comparison/ArcComparisonContent.vue +29 -0
- package/components/comparisons/arc-comparison/ArcComparisonContents.vue +17 -0
- package/components/comparisons/arc-comparison/ArcComparisonHeading.vue +29 -0
- package/components/comparisons/arc-comparison/ArcComparisonLeft.vue +268 -0
- package/components/comparisons/arc-comparison/ArcComparisonOrbit.vue +47 -0
- package/components/comparisons/arc-comparison/ArcComparisonRight.vue +271 -0
- package/components/comparisons/arc-comparison/ArcComparisonTitle.vue +29 -0
- package/components/{comparison-table/ComparisonTable.vue → comparisons/table-comparison/TableComparison.vue} +37 -27
- package/components/{comparison-table/ComparisonTableCell.vue → comparisons/table-comparison/TableComparisonCell.vue} +8 -8
- package/components/{comparison-table/ComparisonTableCol.vue → comparisons/table-comparison/TableComparisonCol.vue} +10 -10
- package/components/{comparison-table/ComparisonTableCols.vue → comparisons/table-comparison/TableComparisonCols.vue} +11 -11
- package/components/{comparison-table/ComparisonTableRow.vue → comparisons/table-comparison/TableComparisonRow.vue} +9 -9
- package/components/{comparison-table/ComparisonTableRows.vue → comparisons/table-comparison/TableComparisonRows.vue} +3 -3
- package/components/core/ReflectedTitle.vue +2 -1
- package/components/core/arc-orbit/ArcOrbit.vue +468 -0
- package/components/{arc-compare/ArcCompareBadge.vue → core/arc-orbit/ArcOrbitBadge.vue} +11 -8
- package/components/core/arc-orbit/ArcOrbitCallout.vue +400 -0
- package/components/{arc-compare/ArcCompareContent.vue → core/arc-orbit/ArcOrbitContent.vue} +8 -8
- package/components/{arc-compare/ArcCompareContents.vue → core/arc-orbit/ArcOrbitContents.vue} +3 -3
- package/components/{arc-compare/ArcCompareHeading.vue → core/arc-orbit/ArcOrbitHeading.vue} +11 -8
- package/components/core/arc-orbit/ArcOrbitTitle.vue +50 -0
- package/components/{thanks → core/thanks}/ThanksOutlineSquare.vue +1 -1
- package/components/{thanks → core/thanks}/ThanksSquare.vue +1 -1
- package/components/{quad-hub/QuadHub.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetrad.vue} +71 -69
- package/components/{quad-hub/QuadHubCallout.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradCallout.vue} +45 -43
- package/components/{quad-hub/QuadHubCenter.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradCenter.vue} +4 -4
- package/components/{quad-hub/QuadHubContent.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradContent.vue} +6 -6
- package/components/{quad-hub/QuadHubHeading.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradHeading.vue} +6 -6
- package/components/{quad-hub/QuadHubIcon.vue → facets/tetrad/rect-orbit-tetrad/RectOrbitTetradIcon.vue} +6 -6
- package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriad.vue +3 -3
- package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadCallout.vue +1 -1
- package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadContent.vue +1 -1
- package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadHeading.vue +1 -1
- package/components/facets/triad/gear-triad/GearTriad.vue +591 -0
- package/components/facets/triad/gear-triad/GearTriadCallout.vue +285 -0
- package/components/facets/triad/gear-triad/GearTriadCenter.vue +41 -0
- package/components/facets/triad/gear-triad/GearTriadContent.vue +58 -0
- package/components/facets/triad/gear-triad/GearTriadContents.vue +111 -0
- package/components/facets/triad/gear-triad/GearTriadDescription.vue +38 -0
- package/components/facets/triad/gear-triad/GearTriadHeading.vue +38 -0
- package/components/facets/triad/gear-triad/GearTriadIcon.vue +157 -0
- package/components/{hex-triad → facets/triad/hex-triad}/HexTriad.vue +2 -2
- package/components/{hex-triad → facets/triad/hex-triad}/HexTriadBadge.vue +1 -1
- package/components/{hex-triad → facets/triad/hex-triad}/HexTriadCallout.vue +1 -1
- package/components/{hex-triad → facets/triad/hex-triad}/HexTriadHeading.vue +1 -1
- package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcess.vue +2 -2
- package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessArrow.vue +1 -1
- package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessCallout.vue +1 -1
- package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessContent.vue +1 -1
- package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessHeading.vue +1 -1
- package/components/pyramid/circular-pyramid/CircularPyramid.vue +871 -0
- package/components/pyramid/circular-pyramid/CircularPyramidStack.vue +375 -0
- package/components/pyramid/circular-pyramid/CircularPyramidStackContent.vue +208 -0
- package/components/pyramid/circular-pyramid/CircularPyramidStackTitle.vue +138 -0
- package/components/pyramid/square-pyramid/SquarePyramid.vue +876 -0
- package/components/pyramid/square-pyramid/SquarePyramidStack.vue +366 -0
- package/components/pyramid/square-pyramid/SquarePyramidStackContent.vue +204 -0
- package/components/pyramid/square-pyramid/SquarePyramidStackTitle.vue +134 -0
- package/package.json +2 -1
- package/utils/arcComparison/context.ts +40 -0
- package/utils/arcComparison/createArcComparisonGeometry.ts +175 -0
- package/utils/arcComparison/index.ts +3 -0
- package/utils/{arcCompare/resolveArcCompareItems.ts → arcComparison/resolveArcComparisonItems.ts} +24 -45
- package/utils/arcOrbit/context.ts +38 -0
- package/utils/{arcCompare/createArcCompareGeometry.ts → arcOrbit/createArcOrbitGeometry.ts} +77 -108
- package/utils/arcOrbit/index.ts +3 -0
- package/utils/arcOrbit/resolveArcOrbitItems.ts +92 -0
- package/utils/gearTriad/context.ts +51 -0
- package/utils/gearTriad/createGearTriadGeometry.ts +210 -0
- package/utils/gearTriad/index.ts +3 -0
- package/utils/gearTriad/resolveGearTriadItems.ts +159 -0
- package/utils/pyramid/circular-pyramid/context.ts +46 -0
- package/utils/pyramid/circular-pyramid/createCircularPyramidGeometry.ts +143 -0
- package/utils/pyramid/circular-pyramid/index.ts +3 -0
- package/utils/pyramid/circular-pyramid/types.ts +34 -0
- package/utils/pyramid/index.ts +3 -0
- package/utils/pyramid/shared/colors.ts +92 -0
- package/utils/pyramid/shared/connector.ts +111 -0
- package/utils/pyramid/shared/constants.ts +8 -0
- package/utils/pyramid/shared/index.ts +5 -0
- package/utils/pyramid/shared/stacking.ts +76 -0
- package/utils/pyramid/shared/types.ts +36 -0
- package/utils/pyramid/square-pyramid/context.ts +46 -0
- package/utils/pyramid/square-pyramid/createSquarePyramidGeometry.ts +265 -0
- package/utils/pyramid/square-pyramid/index.ts +3 -0
- package/utils/pyramid/square-pyramid/types.ts +39 -0
- package/utils/rectOrbitTetrad/context.ts +31 -0
- package/utils/{quadHub/createQuadHubGeometry.ts → rectOrbitTetrad/createRectOrbitTetradGeometry.ts} +33 -28
- package/utils/rectOrbitTetrad/index.ts +3 -0
- package/utils/{quadHub/resolveQuadHubItems.ts → rectOrbitTetrad/resolveRectOrbitTetradItems.ts} +25 -25
- package/utils/{comparisonTable → tableComparison}/context.ts +6 -6
- package/utils/useDiagramAutoScale.ts +7 -9
- package/components/arc-compare/ArcCompare.vue +0 -443
- package/components/arc-compare/ArcCompareCallout.vue +0 -386
- package/components/arc-compare/ArcCompareLeft.vue +0 -183
- package/components/arc-compare/ArcCompareRight.vue +0 -183
- package/components/arc-compare/ArcCompareTitle.vue +0 -47
- package/utils/arcCompare/context.ts +0 -36
- package/utils/arcCompare/index.ts +0 -3
- package/utils/quadHub/context.ts +0 -26
- package/utils/quadHub/index.ts +0 -3
- /package/components/{shifting-heading → core}/TransitionHeading.vue +0 -0
- /package/components/{thanks → core/thanks}/ThanksContent.vue +0 -0
- /package/components/{arrow-triad → facets/triad/arrow-triad}/ArrowTriadIcon.vue +0 -0
- /package/components/{hex-triad → facets/triad/hex-triad}/HexTriadCenterBadge.vue +0 -0
- /package/components/{hex-triad → facets/triad/hex-triad}/HexTriadContent.vue +0 -0
- /package/components/{hex-triad → facets/triad/hex-triad}/HexTriadLayer.vue +0 -0
- /package/components/{arc-arrow-process → process/arc-arrow-process}/ArcArrowProcessIcon.vue +0 -0
- /package/utils/{comparisonTable → tableComparison}/constants.ts +0 -0
- /package/utils/{comparisonTable → tableComparison}/index.ts +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { IPyramidPalette } from '../shared';
|
|
2
|
+
|
|
3
|
+
export interface ICircularLayerGeometry {
|
|
4
|
+
readonly index: number;
|
|
5
|
+
readonly cx: number;
|
|
6
|
+
readonly cy: number;
|
|
7
|
+
readonly rx: number;
|
|
8
|
+
readonly ry: number;
|
|
9
|
+
readonly cylinderHeight: number;
|
|
10
|
+
readonly cylinderPath: string;
|
|
11
|
+
readonly bottomArcPath: string;
|
|
12
|
+
readonly topEllipsePath: string;
|
|
13
|
+
readonly slabCenterY?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ICircularPyramidGeometry {
|
|
17
|
+
readonly viewBoxWidth: number;
|
|
18
|
+
readonly viewBoxHeight: number;
|
|
19
|
+
readonly count: number;
|
|
20
|
+
readonly layers: readonly ICircularLayerGeometry[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ICreateCircularPyramidOptions {
|
|
24
|
+
readonly count?: number;
|
|
25
|
+
readonly viewBoxWidth?: number;
|
|
26
|
+
readonly viewBoxHeight?: number;
|
|
27
|
+
readonly topRadius?: number;
|
|
28
|
+
readonly bottomRadius?: number;
|
|
29
|
+
readonly cylinderHeight?: number;
|
|
30
|
+
readonly ratio?: number;
|
|
31
|
+
readonly cx?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ICircularPyramidPalette extends IPyramidPalette {}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { DEFAULT_PYRAMID_COLOR } from './constants';
|
|
2
|
+
import type { IColorModifierParams, IPyramidPalette } from './types';
|
|
3
|
+
|
|
4
|
+
export function lightenColor({
|
|
5
|
+
hex,
|
|
6
|
+
ratio = 0.5,
|
|
7
|
+
}: IColorModifierParams): string {
|
|
8
|
+
const raw = hex.trim().replace(/^#/, '');
|
|
9
|
+
const sanitized =
|
|
10
|
+
raw.length === 3
|
|
11
|
+
? raw
|
|
12
|
+
.split('')
|
|
13
|
+
.map((char) => char + char)
|
|
14
|
+
.join('')
|
|
15
|
+
: raw;
|
|
16
|
+
|
|
17
|
+
if (sanitized.length !== 6) {
|
|
18
|
+
return hex;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const r = Number.parseInt(sanitized.slice(0, 2), 16);
|
|
22
|
+
const g = Number.parseInt(sanitized.slice(2, 4), 16);
|
|
23
|
+
const b = Number.parseInt(sanitized.slice(4, 6), 16);
|
|
24
|
+
|
|
25
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
26
|
+
return hex;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const clamp = (val: number): number =>
|
|
30
|
+
Math.min(255, Math.max(0, Math.round(val)));
|
|
31
|
+
const lr = clamp(r + (255 - r) * ratio);
|
|
32
|
+
const lg = clamp(g + (255 - g) * ratio);
|
|
33
|
+
const lb = clamp(b + (255 - b) * ratio);
|
|
34
|
+
|
|
35
|
+
const toHex = (n: number): string => n.toString(16).padStart(2, '0');
|
|
36
|
+
|
|
37
|
+
return `#${toHex(lr)}${toHex(lg)}${toHex(lb)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function darkenColor({
|
|
41
|
+
hex,
|
|
42
|
+
ratio = 0.5,
|
|
43
|
+
}: IColorModifierParams): string {
|
|
44
|
+
const raw = hex.trim().replace(/^#/, '');
|
|
45
|
+
const sanitized =
|
|
46
|
+
raw.length === 3
|
|
47
|
+
? raw
|
|
48
|
+
.split('')
|
|
49
|
+
.map((char) => char + char)
|
|
50
|
+
.join('')
|
|
51
|
+
: raw;
|
|
52
|
+
|
|
53
|
+
if (sanitized.length !== 6) {
|
|
54
|
+
return hex;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const r = Number.parseInt(sanitized.slice(0, 2), 16);
|
|
58
|
+
const g = Number.parseInt(sanitized.slice(2, 4), 16);
|
|
59
|
+
const b = Number.parseInt(sanitized.slice(4, 6), 16);
|
|
60
|
+
|
|
61
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
62
|
+
return hex;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const clamp = (val: number): number =>
|
|
66
|
+
Math.min(255, Math.max(0, Math.round(val)));
|
|
67
|
+
const dr = clamp(r * (1 - ratio));
|
|
68
|
+
const dg = clamp(g * (1 - ratio));
|
|
69
|
+
const db = clamp(b * (1 - ratio));
|
|
70
|
+
|
|
71
|
+
const toHex = (n: number): string => n.toString(16).padStart(2, '0');
|
|
72
|
+
|
|
73
|
+
return `#${toHex(dr)}${toHex(dg)}${toHex(db)}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function resolvePyramidPalette(
|
|
77
|
+
baseColor: string = DEFAULT_PYRAMID_COLOR,
|
|
78
|
+
): IPyramidPalette {
|
|
79
|
+
return {
|
|
80
|
+
activeTopStart: lightenColor({ hex: baseColor, ratio: 0.82 }),
|
|
81
|
+
activeTopEnd: baseColor,
|
|
82
|
+
activeSideStart: darkenColor({ hex: baseColor, ratio: 0.28 }),
|
|
83
|
+
activeSideEnd: lightenColor({ hex: baseColor, ratio: 0.35 }),
|
|
84
|
+
activeStroke: lightenColor({ hex: baseColor, ratio: 0.7 }),
|
|
85
|
+
defaultTopStart: '#ffffff',
|
|
86
|
+
defaultTopEnd: lightenColor({ hex: baseColor, ratio: 0.88 }),
|
|
87
|
+
defaultSideStart: lightenColor({ hex: baseColor, ratio: 0.72 }),
|
|
88
|
+
defaultSideEnd: lightenColor({ hex: baseColor, ratio: 0.88 }),
|
|
89
|
+
defaultStroke: '#ffffff',
|
|
90
|
+
glow: baseColor,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export interface IPyramidConnectorPoint {
|
|
2
|
+
readonly x: number;
|
|
3
|
+
readonly y: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function createPyramidConnectorPath(
|
|
7
|
+
start: IPyramidConnectorPoint,
|
|
8
|
+
end: IPyramidConnectorPoint,
|
|
9
|
+
): string {
|
|
10
|
+
const startX = Number(start.x.toFixed(2));
|
|
11
|
+
const startY = Number(start.y.toFixed(2));
|
|
12
|
+
const endX = Number(end.x.toFixed(2));
|
|
13
|
+
const endY = Number(end.y.toFixed(2));
|
|
14
|
+
|
|
15
|
+
// If startY and endY are virtually aligned, return straight line
|
|
16
|
+
if (Math.abs(startY - endY) < 1) {
|
|
17
|
+
return `M ${startX} ${startY} L ${endX} ${endY}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const width = Math.max(0, endX - startX);
|
|
21
|
+
// Initial horizontal stub leaving the disc dot
|
|
22
|
+
const stubWidth = Math.min(14, width * 0.2);
|
|
23
|
+
const x1 = Number((startX + stubWidth).toFixed(2));
|
|
24
|
+
|
|
25
|
+
// Ramp/elbow transition to target card elevation
|
|
26
|
+
const rampWidth = Math.min(28, Math.max(14, (width - stubWidth) * 0.45));
|
|
27
|
+
const x2 = Number((x1 + rampWidth).toFixed(2));
|
|
28
|
+
|
|
29
|
+
// Connect: (startX, startY) -> (x1, startY) -> (x2, endY) -> (endX, endY)
|
|
30
|
+
return `M ${startX} ${startY} L ${x1} ${startY} L ${x2} ${endY} L ${endX} ${endY}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IResolvePyramidCardPositionsParams {
|
|
34
|
+
readonly targetYList: readonly number[];
|
|
35
|
+
readonly heights: readonly number[];
|
|
36
|
+
readonly gap?: number;
|
|
37
|
+
readonly minY?: number;
|
|
38
|
+
readonly maxY?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function resolvePyramidCardPositions(
|
|
42
|
+
params: IResolvePyramidCardPositionsParams,
|
|
43
|
+
): number[] {
|
|
44
|
+
const { targetYList, heights, gap = 10, minY = 16, maxY = 464 } = params;
|
|
45
|
+
const count = targetYList.length;
|
|
46
|
+
if (count <= 1) {
|
|
47
|
+
return [...targetYList];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const yList: number[] = [...targetYList];
|
|
51
|
+
|
|
52
|
+
// 1. Forward pass: push down any overlapping cards
|
|
53
|
+
for (let i = 0; i < count - 1; i++) {
|
|
54
|
+
const curY = yList[i] ?? 0;
|
|
55
|
+
const nextY = yList[i + 1] ?? 0;
|
|
56
|
+
const curH = heights[i] ?? 48;
|
|
57
|
+
const nextH = heights[i + 1] ?? 48;
|
|
58
|
+
const minCenterNext = curY + curH / 2 + nextH / 2 + gap;
|
|
59
|
+
if (nextY < minCenterNext) {
|
|
60
|
+
yList[i + 1] = minCenterNext;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 2. Center the shift by redistributing excess displacement
|
|
65
|
+
const avgShift =
|
|
66
|
+
yList.reduce((sum, y, i) => sum + (y - (targetYList[i] ?? y)), 0) / count;
|
|
67
|
+
const firstY = yList[0] ?? 0;
|
|
68
|
+
const firstH = heights[0] ?? 48;
|
|
69
|
+
const maxAllowableUpShift = firstY - firstH / 2 - minY;
|
|
70
|
+
const shiftUp = Math.max(0, Math.min(avgShift, maxAllowableUpShift));
|
|
71
|
+
for (let i = 0; i < count; i++) {
|
|
72
|
+
yList[i] = (yList[i] ?? 0) - shiftUp;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 3. Backward pass from bottom boundary
|
|
76
|
+
const lastY = yList[count - 1] ?? 0;
|
|
77
|
+
const lastH = heights[count - 1] ?? 48;
|
|
78
|
+
const bottomOverlap = lastY + lastH / 2 - maxY;
|
|
79
|
+
if (bottomOverlap > 0) {
|
|
80
|
+
yList[count - 1] = lastY - bottomOverlap;
|
|
81
|
+
for (let i = count - 2; i >= 0; i--) {
|
|
82
|
+
const curY = yList[i] ?? 0;
|
|
83
|
+
const nextY = yList[i + 1] ?? 0;
|
|
84
|
+
const curH = heights[i] ?? 48;
|
|
85
|
+
const nextH = heights[i + 1] ?? 48;
|
|
86
|
+
const maxCenterPrev = nextY - nextH / 2 - curH / 2 - gap;
|
|
87
|
+
if (curY > maxCenterPrev) {
|
|
88
|
+
yList[i] = maxCenterPrev;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 4. Final top boundary safety
|
|
94
|
+
const updatedFirstY = yList[0] ?? 0;
|
|
95
|
+
const topOverlap = minY - (updatedFirstY - firstH / 2);
|
|
96
|
+
if (topOverlap > 0) {
|
|
97
|
+
yList[0] = updatedFirstY + topOverlap;
|
|
98
|
+
for (let i = 0; i < count - 1; i++) {
|
|
99
|
+
const curY = yList[i] ?? 0;
|
|
100
|
+
const nextY = yList[i + 1] ?? 0;
|
|
101
|
+
const curH = heights[i] ?? 48;
|
|
102
|
+
const nextH = heights[i + 1] ?? 48;
|
|
103
|
+
const minCenterNext = curY + curH / 2 + nextH / 2 + gap;
|
|
104
|
+
if (nextY < minCenterNext) {
|
|
105
|
+
yList[i + 1] = minCenterNext;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return yList.map((y) => Number(y.toFixed(2)));
|
|
111
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const DEFAULT_PYRAMID_COLOR = '#3b82f6';
|
|
2
|
+
export const MIN_PYRAMID_COUNT = 1;
|
|
3
|
+
export const MAX_PYRAMID_COUNT = 6;
|
|
4
|
+
|
|
5
|
+
export const PYRAMID_TITLE_WIDTH_PX = 150;
|
|
6
|
+
export const PYRAMID_CONTENT_WIDTH_PX = 240;
|
|
7
|
+
export const PYRAMID_CONTENT_RIGHT_OFFSET_PX = 0;
|
|
8
|
+
export const PYRAMID_DOT_POSITION_X = 210;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { MAX_PYRAMID_COUNT, MIN_PYRAMID_COUNT } from './constants';
|
|
2
|
+
import type {
|
|
3
|
+
IComputePyramidStackingParams,
|
|
4
|
+
IPyramidStackingResult,
|
|
5
|
+
} from './types';
|
|
6
|
+
|
|
7
|
+
export function clampPyramidCount(count?: number): number {
|
|
8
|
+
if (count === undefined || Number.isNaN(count)) {
|
|
9
|
+
return 4;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return Math.min(
|
|
13
|
+
MAX_PYRAMID_COUNT,
|
|
14
|
+
Math.max(MIN_PYRAMID_COUNT, Math.round(count)),
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function computePyramidStacking({
|
|
19
|
+
count,
|
|
20
|
+
viewBoxHeight,
|
|
21
|
+
slabHeight,
|
|
22
|
+
radii,
|
|
23
|
+
}: IComputePyramidStackingParams): IPyramidStackingResult {
|
|
24
|
+
let sumAdjacentRy = 0;
|
|
25
|
+
for (let i = 0; i < count - 1; i++) {
|
|
26
|
+
const rCurrent = radii[i]?.ry ?? 0;
|
|
27
|
+
const rNext = radii[i + 1]?.ry ?? 0;
|
|
28
|
+
sumAdjacentRy += rCurrent + rNext;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const firstRy = radii[0]?.ry ?? 0;
|
|
32
|
+
const lastRy = radii[count - 1]?.ry ?? 0;
|
|
33
|
+
|
|
34
|
+
const baseOverlap =
|
|
35
|
+
count <= 2
|
|
36
|
+
? 0.24
|
|
37
|
+
: count === 3
|
|
38
|
+
? 0.26
|
|
39
|
+
: count === 4
|
|
40
|
+
? 0.28
|
|
41
|
+
: count === 5
|
|
42
|
+
? 0.34
|
|
43
|
+
: 0.38;
|
|
44
|
+
|
|
45
|
+
const maxAllowedHeight = viewBoxHeight - 60;
|
|
46
|
+
const naturalHeight =
|
|
47
|
+
firstRy + lastRy + count * slabHeight + (1 - baseOverlap) * sumAdjacentRy;
|
|
48
|
+
|
|
49
|
+
const effectiveOverlap =
|
|
50
|
+
naturalHeight > maxAllowedHeight && sumAdjacentRy > 0
|
|
51
|
+
? 1 -
|
|
52
|
+
(maxAllowedHeight - firstRy - lastRy - count * slabHeight) /
|
|
53
|
+
sumAdjacentRy
|
|
54
|
+
: baseOverlap;
|
|
55
|
+
|
|
56
|
+
const stepYList: number[] = [];
|
|
57
|
+
let totalStepY = 0;
|
|
58
|
+
for (let i = 0; i < count - 1; i++) {
|
|
59
|
+
const rCurrent = radii[i]?.ry ?? 0;
|
|
60
|
+
const rNext = radii[i + 1]?.ry ?? 0;
|
|
61
|
+
const step = (rCurrent + rNext) * (1 - effectiveOverlap) + slabHeight;
|
|
62
|
+
stepYList.push(step);
|
|
63
|
+
totalStepY += step;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const actualTotalHeight = firstRy + totalStepY + slabHeight + lastRy;
|
|
67
|
+
const startCy = Number(
|
|
68
|
+
(viewBoxHeight / 2 - actualTotalHeight / 2 + firstRy).toFixed(2),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
startCy,
|
|
73
|
+
stepYList,
|
|
74
|
+
actualTotalHeight,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface IColorModifierParams {
|
|
2
|
+
readonly hex: string;
|
|
3
|
+
readonly ratio?: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface IPyramidPalette {
|
|
7
|
+
readonly activeTopStart: string;
|
|
8
|
+
readonly activeTopEnd: string;
|
|
9
|
+
readonly activeSideStart: string;
|
|
10
|
+
readonly activeSideEnd: string;
|
|
11
|
+
readonly activeStroke: string;
|
|
12
|
+
readonly defaultTopStart: string;
|
|
13
|
+
readonly defaultTopEnd: string;
|
|
14
|
+
readonly defaultSideStart: string;
|
|
15
|
+
readonly defaultSideEnd: string;
|
|
16
|
+
readonly defaultStroke: string;
|
|
17
|
+
readonly glow: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface IPyramidRegistration {
|
|
21
|
+
readonly index: number;
|
|
22
|
+
readonly unregister: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface IComputePyramidStackingParams {
|
|
26
|
+
readonly count: number;
|
|
27
|
+
readonly viewBoxHeight: number;
|
|
28
|
+
readonly slabHeight: number;
|
|
29
|
+
readonly radii: readonly { readonly rx: number; readonly ry: number }[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IPyramidStackingResult {
|
|
33
|
+
readonly startCy: number;
|
|
34
|
+
readonly stepYList: readonly number[];
|
|
35
|
+
readonly actualTotalHeight: number;
|
|
36
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ComputedRef, InjectionKey, Ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
import type { IPyramidPalette, IPyramidRegistration } from '../shared';
|
|
4
|
+
import type { ISquareLayerGeometry, ISquarePyramidGeometry } from './types';
|
|
5
|
+
|
|
6
|
+
export interface ISquarePyramidRegistration extends IPyramidRegistration {}
|
|
7
|
+
|
|
8
|
+
export interface ISquarePyramidRootContext {
|
|
9
|
+
readonly count: ComputedRef<number>;
|
|
10
|
+
readonly activeIndex: Ref<number> | ComputedRef<number>;
|
|
11
|
+
readonly color: ComputedRef<string>;
|
|
12
|
+
readonly geo: ComputedRef<ISquarePyramidGeometry>;
|
|
13
|
+
readonly palette: ComputedRef<IPyramidPalette>;
|
|
14
|
+
readonly animation: ComputedRef<boolean>;
|
|
15
|
+
readonly interactive: ComputedRef<boolean>;
|
|
16
|
+
readonly titleWidthPx: ComputedRef<number>;
|
|
17
|
+
readonly contentWidthPx: ComputedRef<number>;
|
|
18
|
+
readonly contentRightOffsetPx: ComputedRef<number>;
|
|
19
|
+
readonly registerStack: (
|
|
20
|
+
id?: symbol,
|
|
21
|
+
color?: ComputedRef<string | undefined>,
|
|
22
|
+
) => {
|
|
23
|
+
index: ComputedRef<number>;
|
|
24
|
+
unregister: () => void;
|
|
25
|
+
};
|
|
26
|
+
readonly getLayerColor: (index: number) => string;
|
|
27
|
+
readonly setActiveIndex: (index: number) => void;
|
|
28
|
+
readonly getCardDeltaY?: (index: number) => number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ISquarePyramidStackContext {
|
|
32
|
+
readonly index: ComputedRef<number>;
|
|
33
|
+
readonly isActive: ComputedRef<boolean>;
|
|
34
|
+
readonly step: ComputedRef<string | number>;
|
|
35
|
+
readonly color?: ComputedRef<string>;
|
|
36
|
+
readonly layerGeo?: ComputedRef<ISquareLayerGeometry | undefined>;
|
|
37
|
+
readonly deltaY?: ComputedRef<number>;
|
|
38
|
+
readonly connectorWidth?: ComputedRef<number>;
|
|
39
|
+
readonly setActive?: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const SQUARE_PYRAMID_ROOT_KEY: InjectionKey<ISquarePyramidRootContext> =
|
|
43
|
+
Symbol('SQUARE_PYRAMID_ROOT_KEY');
|
|
44
|
+
|
|
45
|
+
export const SQUARE_PYRAMID_STACK_KEY: InjectionKey<ISquarePyramidStackContext> =
|
|
46
|
+
Symbol('SQUARE_PYRAMID_STACK_KEY');
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clampPyramidCount,
|
|
3
|
+
computePyramidStacking,
|
|
4
|
+
DEFAULT_PYRAMID_COLOR,
|
|
5
|
+
darkenColor,
|
|
6
|
+
lightenColor,
|
|
7
|
+
MAX_PYRAMID_COUNT,
|
|
8
|
+
MIN_PYRAMID_COUNT,
|
|
9
|
+
resolvePyramidPalette,
|
|
10
|
+
} from '../shared';
|
|
11
|
+
import type {
|
|
12
|
+
ICreateSquarePyramidOptions,
|
|
13
|
+
ISquareLayerGeometry,
|
|
14
|
+
ISquarePyramidGeometry,
|
|
15
|
+
ISquarePyramidPalette,
|
|
16
|
+
} from './types';
|
|
17
|
+
|
|
18
|
+
export const DEFAULT_SQUARE_PYRAMID_COLOR = DEFAULT_PYRAMID_COLOR;
|
|
19
|
+
export const DEFAULT_SQUARE_PYRAMID_STACK_COLORS: readonly string[] = [
|
|
20
|
+
'#ec4899', // 0: Pink (matches top slab)
|
|
21
|
+
'#8b5cf6', // 1: Purple (matches 2nd slab)
|
|
22
|
+
'#3b82f6', // 2: Blue (matches 3rd slab)
|
|
23
|
+
'#06b6d4', // 3: Cyan (matches 4th slab)
|
|
24
|
+
'#14b8a6', // 4: Teal (matches 5th slab)
|
|
25
|
+
'#f59e0b', // 5: Amber (warm accent)
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
export const MIN_SQUARE_COUNT = MIN_PYRAMID_COUNT;
|
|
29
|
+
export const MAX_SQUARE_COUNT = MAX_PYRAMID_COUNT;
|
|
30
|
+
|
|
31
|
+
export { darkenColor, lightenColor };
|
|
32
|
+
|
|
33
|
+
export function clampSquareCount(count?: number): number {
|
|
34
|
+
return clampPyramidCount(count);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveSquarePyramidPalette(
|
|
38
|
+
baseColor: string = DEFAULT_SQUARE_PYRAMID_COLOR,
|
|
39
|
+
): ISquarePyramidPalette {
|
|
40
|
+
return resolvePyramidPalette(baseColor);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildSquarePaths({
|
|
44
|
+
cx,
|
|
45
|
+
cy,
|
|
46
|
+
rx,
|
|
47
|
+
ry,
|
|
48
|
+
dx,
|
|
49
|
+
dy,
|
|
50
|
+
slabHeight,
|
|
51
|
+
}: {
|
|
52
|
+
cx: number;
|
|
53
|
+
cy: number;
|
|
54
|
+
rx: number;
|
|
55
|
+
ry: number;
|
|
56
|
+
dx: number;
|
|
57
|
+
dy: number;
|
|
58
|
+
slabHeight: number;
|
|
59
|
+
}) {
|
|
60
|
+
const topFacePath = [
|
|
61
|
+
`M ${cx - dx} ${cy - ry + dy}`,
|
|
62
|
+
`Q ${cx} ${cy - ry} ${cx + dx} ${cy - ry + dy}`,
|
|
63
|
+
`L ${cx + rx - dx} ${cy - dy}`,
|
|
64
|
+
`Q ${cx + rx} ${cy} ${cx + rx - dx} ${cy + dy}`,
|
|
65
|
+
`L ${cx + dx} ${cy + ry - dy}`,
|
|
66
|
+
`Q ${cx} ${cy + ry} ${cx - dx} ${cy + ry - dy}`,
|
|
67
|
+
`L ${cx - rx + dx} ${cy + dy}`,
|
|
68
|
+
`Q ${cx - rx} ${cy} ${cx - rx + dx} ${cy - dy}`,
|
|
69
|
+
'Z',
|
|
70
|
+
].join(' ');
|
|
71
|
+
|
|
72
|
+
const innerRatio = 0.65;
|
|
73
|
+
const rxInner = Number((rx * innerRatio).toFixed(2));
|
|
74
|
+
const ryInner = Number((ry * innerRatio).toFixed(2));
|
|
75
|
+
const dxInner = Number((dx * innerRatio).toFixed(2));
|
|
76
|
+
const dyInner = Number((dy * innerRatio).toFixed(2));
|
|
77
|
+
|
|
78
|
+
const innerFacePath = [
|
|
79
|
+
`M ${cx - dxInner} ${cy - ryInner + dyInner}`,
|
|
80
|
+
`Q ${cx} ${cy - ryInner} ${cx + dxInner} ${cy - ryInner + dyInner}`,
|
|
81
|
+
`L ${cx + rxInner - dxInner} ${cy - dyInner}`,
|
|
82
|
+
`Q ${cx + rxInner} ${cy} ${cx + rxInner - dxInner} ${cy + dyInner}`,
|
|
83
|
+
`L ${cx + dxInner} ${cy + ryInner - dyInner}`,
|
|
84
|
+
`Q ${cx} ${cy + ryInner} ${cx - dxInner} ${cy + ryInner - dyInner}`,
|
|
85
|
+
`L ${cx - rxInner + dxInner} ${cy + dyInner}`,
|
|
86
|
+
`Q ${cx - rxInner} ${cy} ${cx - rxInner + dxInner} ${cy - dyInner}`,
|
|
87
|
+
'Z',
|
|
88
|
+
].join(' ');
|
|
89
|
+
|
|
90
|
+
const sidePath = [
|
|
91
|
+
`M ${cx - rx + 0.5 * dx} ${cy}`,
|
|
92
|
+
`Q ${cx - rx + 0.5 * dx} ${cy + 0.5 * dy} ${cx - rx + dx} ${cy + dy}`,
|
|
93
|
+
`L ${cx - dx} ${cy + ry - dy}`,
|
|
94
|
+
`Q ${cx} ${cy + ry} ${cx + dx} ${cy + ry - dy}`,
|
|
95
|
+
`L ${cx + rx - dx} ${cy + dy}`,
|
|
96
|
+
`Q ${cx + rx - 0.5 * dx} ${cy + 0.5 * dy} ${cx + rx - 0.5 * dx} ${cy}`,
|
|
97
|
+
`L ${cx + rx - 0.5 * dx} ${cy + slabHeight}`,
|
|
98
|
+
`Q ${cx + rx - 0.5 * dx} ${cy + slabHeight + 0.5 * dy} ${cx + rx - dx} ${cy + slabHeight + dy}`,
|
|
99
|
+
`L ${cx + dx} ${cy + ry + slabHeight - dy}`,
|
|
100
|
+
`Q ${cx} ${cy + ry + slabHeight} ${cx - dx} ${cy + ry + slabHeight - dy}`,
|
|
101
|
+
`L ${cx - rx + dx} ${cy + slabHeight + dy}`,
|
|
102
|
+
`Q ${cx - rx + 0.5 * dx} ${cy + slabHeight + 0.5 * dy} ${cx - rx + 0.5 * dx} ${cy + slabHeight}`,
|
|
103
|
+
'Z',
|
|
104
|
+
].join(' ');
|
|
105
|
+
|
|
106
|
+
const leftSidePath = [
|
|
107
|
+
`M ${cx - rx + 0.5 * dx} ${cy}`,
|
|
108
|
+
`Q ${cx - rx + 0.5 * dx} ${cy + 0.5 * dy} ${cx - rx + dx} ${cy + dy}`,
|
|
109
|
+
`L ${cx - dx} ${cy + ry - dy}`,
|
|
110
|
+
`Q ${cx} ${cy + ry} ${cx} ${cy + ry - 0.5 * dy}`,
|
|
111
|
+
`L ${cx} ${cy + ry + slabHeight - 0.5 * dy}`,
|
|
112
|
+
`Q ${cx} ${cy + ry + slabHeight} ${cx - dx} ${cy + ry + slabHeight - dy}`,
|
|
113
|
+
`L ${cx - rx + dx} ${cy + slabHeight + dy}`,
|
|
114
|
+
`Q ${cx - rx + 0.5 * dx} ${cy + slabHeight + 0.5 * dy} ${cx - rx + 0.5 * dx} ${cy + slabHeight}`,
|
|
115
|
+
'Z',
|
|
116
|
+
].join(' ');
|
|
117
|
+
|
|
118
|
+
const rightSidePath = [
|
|
119
|
+
`M ${cx} ${cy + ry - 0.5 * dy}`,
|
|
120
|
+
`Q ${cx} ${cy + ry} ${cx + dx} ${cy + ry - dy}`,
|
|
121
|
+
`L ${cx + rx - dx} ${cy + dy}`,
|
|
122
|
+
`Q ${cx + rx - 0.5 * dx} ${cy + 0.5 * dy} ${cx + rx - 0.5 * dx} ${cy}`,
|
|
123
|
+
`L ${cx + rx - 0.5 * dx} ${cy + slabHeight}`,
|
|
124
|
+
`Q ${cx + rx - 0.5 * dx} ${cy + slabHeight + 0.5 * dy} ${cx + rx - dx} ${cy + slabHeight + dy}`,
|
|
125
|
+
`L ${cx + dx} ${cy + ry + slabHeight - dy}`,
|
|
126
|
+
`Q ${cx} ${cy + ry + slabHeight} ${cx} ${cy + ry + slabHeight - 0.5 * dy}`,
|
|
127
|
+
'Z',
|
|
128
|
+
].join(' ');
|
|
129
|
+
|
|
130
|
+
const bottomEdgePath = [
|
|
131
|
+
`M ${cx - rx + 0.5 * dx} ${cy + slabHeight}`,
|
|
132
|
+
`Q ${cx - rx + 0.5 * dx} ${cy + slabHeight + 0.5 * dy} ${cx - rx + dx} ${cy + slabHeight + dy}`,
|
|
133
|
+
`L ${cx - dx} ${cy + ry + slabHeight - dy}`,
|
|
134
|
+
`Q ${cx} ${cy + ry + slabHeight} ${cx + dx} ${cy + ry + slabHeight - dy}`,
|
|
135
|
+
`L ${cx + rx - dx} ${cy + slabHeight + dy}`,
|
|
136
|
+
`Q ${cx + rx - 0.5 * dx} ${cy + slabHeight + 0.5 * dy} ${cx + rx - 0.5 * dx} ${cy + slabHeight}`,
|
|
137
|
+
].join(' ');
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
topFacePath,
|
|
141
|
+
innerFacePath,
|
|
142
|
+
sidePath,
|
|
143
|
+
leftSidePath,
|
|
144
|
+
rightSidePath,
|
|
145
|
+
bottomEdgePath,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function createSquarePyramidGeometry(
|
|
150
|
+
options: ICreateSquarePyramidOptions = {},
|
|
151
|
+
): ISquarePyramidGeometry {
|
|
152
|
+
const viewBoxWidth = options.viewBoxWidth ?? 960;
|
|
153
|
+
const viewBoxHeight = options.viewBoxHeight ?? 440;
|
|
154
|
+
const count = clampSquareCount(options.count);
|
|
155
|
+
const ratio = options.ratio ?? 0.46;
|
|
156
|
+
const cx = options.cx ?? viewBoxWidth / 2;
|
|
157
|
+
const slabHeight = options.slabHeight ?? (count >= 5 ? 8 : 10);
|
|
158
|
+
const cornerRadiusRatio = options.cornerRadiusRatio ?? 0.16;
|
|
159
|
+
|
|
160
|
+
if (count === 1) {
|
|
161
|
+
const rx = options.topSize ?? 110;
|
|
162
|
+
const ry = Number((rx * ratio).toFixed(2));
|
|
163
|
+
const cy = Number((viewBoxHeight / 2 - slabHeight / 2).toFixed(2));
|
|
164
|
+
const cornerRadius = Number((rx * cornerRadiusRatio).toFixed(2));
|
|
165
|
+
const dx = cornerRadius;
|
|
166
|
+
const dy = Number((cornerRadius * ratio).toFixed(2));
|
|
167
|
+
|
|
168
|
+
const paths = buildSquarePaths({
|
|
169
|
+
cx,
|
|
170
|
+
cy,
|
|
171
|
+
rx,
|
|
172
|
+
ry,
|
|
173
|
+
dx,
|
|
174
|
+
dy,
|
|
175
|
+
slabHeight,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const slabCenterY = Number((cy + slabHeight / 2).toFixed(2));
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
viewBoxWidth,
|
|
182
|
+
viewBoxHeight,
|
|
183
|
+
count: 1,
|
|
184
|
+
layers: [
|
|
185
|
+
{
|
|
186
|
+
index: 0,
|
|
187
|
+
cx,
|
|
188
|
+
cy,
|
|
189
|
+
rx,
|
|
190
|
+
ry,
|
|
191
|
+
slabHeight,
|
|
192
|
+
cornerRadius,
|
|
193
|
+
slabCenterY,
|
|
194
|
+
...paths,
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const topSize =
|
|
201
|
+
options.topSize ?? (count <= 2 ? 110 : Math.max(75, 100 - (count - 3) * 7));
|
|
202
|
+
const bottomSize = options.bottomSize ?? 185;
|
|
203
|
+
|
|
204
|
+
const radii: { rx: number; ry: number }[] = [];
|
|
205
|
+
for (let i = 0; i < count; i++) {
|
|
206
|
+
const t = i / (count - 1);
|
|
207
|
+
const rx = Number((topSize + t * (bottomSize - topSize)).toFixed(2));
|
|
208
|
+
const ry = Number((rx * ratio).toFixed(2));
|
|
209
|
+
radii.push({ rx, ry });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const { startCy, stepYList } = computePyramidStacking({
|
|
213
|
+
count,
|
|
214
|
+
viewBoxHeight,
|
|
215
|
+
slabHeight,
|
|
216
|
+
radii,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const layers: ISquareLayerGeometry[] = [];
|
|
220
|
+
let currentCy = startCy;
|
|
221
|
+
|
|
222
|
+
for (let i = 0; i < count; i++) {
|
|
223
|
+
const rx = radii[i]?.rx ?? topSize;
|
|
224
|
+
const ry = radii[i]?.ry ?? topSize * ratio;
|
|
225
|
+
const cy = Number(currentCy.toFixed(2));
|
|
226
|
+
const cornerRadius = Number((rx * cornerRadiusRatio).toFixed(2));
|
|
227
|
+
const dx = cornerRadius;
|
|
228
|
+
const dy = Number((cornerRadius * ratio).toFixed(2));
|
|
229
|
+
|
|
230
|
+
const paths = buildSquarePaths({
|
|
231
|
+
cx,
|
|
232
|
+
cy,
|
|
233
|
+
rx,
|
|
234
|
+
ry,
|
|
235
|
+
dx,
|
|
236
|
+
dy,
|
|
237
|
+
slabHeight,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const slabCenterY = Number((cy + slabHeight / 2).toFixed(2));
|
|
241
|
+
|
|
242
|
+
layers.push({
|
|
243
|
+
index: i,
|
|
244
|
+
cx,
|
|
245
|
+
cy,
|
|
246
|
+
rx,
|
|
247
|
+
ry,
|
|
248
|
+
slabHeight,
|
|
249
|
+
cornerRadius,
|
|
250
|
+
slabCenterY,
|
|
251
|
+
...paths,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (i < count - 1) {
|
|
255
|
+
currentCy += stepYList[i] ?? 0;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
viewBoxWidth,
|
|
261
|
+
viewBoxHeight,
|
|
262
|
+
count,
|
|
263
|
+
layers,
|
|
264
|
+
};
|
|
265
|
+
}
|