@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,92 @@
|
|
|
1
|
+
export interface IArcOrbitItem {
|
|
2
|
+
readonly id?: string | number;
|
|
3
|
+
readonly title?: string;
|
|
4
|
+
readonly description?: string;
|
|
5
|
+
readonly color?: string;
|
|
6
|
+
readonly textColor?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface IResolvedArcOrbitItem {
|
|
10
|
+
readonly id: string | number;
|
|
11
|
+
readonly title: string;
|
|
12
|
+
readonly description: string;
|
|
13
|
+
readonly color: string;
|
|
14
|
+
readonly textColor?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const DEFAULT_ARC_ORBIT_COLOR = '#ea580c';
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_LOREM_TEXT =
|
|
20
|
+
'Lorem ipsum dolor sit amet, constitutor adipiscing elit. Nam vulputate augue vel ligula.';
|
|
21
|
+
|
|
22
|
+
export const DEFAULT_ARC_ORBIT_ITEMS: readonly [
|
|
23
|
+
IResolvedArcOrbitItem,
|
|
24
|
+
IResolvedArcOrbitItem,
|
|
25
|
+
IResolvedArcOrbitItem,
|
|
26
|
+
IResolvedArcOrbitItem,
|
|
27
|
+
] = [
|
|
28
|
+
{
|
|
29
|
+
id: '01',
|
|
30
|
+
title: 'Add Text Here',
|
|
31
|
+
description: DEFAULT_LOREM_TEXT,
|
|
32
|
+
color: DEFAULT_ARC_ORBIT_COLOR,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: '02',
|
|
36
|
+
title: 'Add Text Here',
|
|
37
|
+
description: DEFAULT_LOREM_TEXT,
|
|
38
|
+
color: DEFAULT_ARC_ORBIT_COLOR,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: '03',
|
|
42
|
+
title: 'Add Text Here',
|
|
43
|
+
description: DEFAULT_LOREM_TEXT,
|
|
44
|
+
color: DEFAULT_ARC_ORBIT_COLOR,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: '04',
|
|
48
|
+
title: 'Add Text Here',
|
|
49
|
+
description: DEFAULT_LOREM_TEXT,
|
|
50
|
+
color: DEFAULT_ARC_ORBIT_COLOR,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export interface IResolveArcOrbitItemsOptions {
|
|
55
|
+
readonly items?: readonly IArcOrbitItem[];
|
|
56
|
+
readonly defaultColor?: string;
|
|
57
|
+
readonly count?: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function resolveArcOrbitItems(
|
|
61
|
+
options: IResolveArcOrbitItemsOptions = {},
|
|
62
|
+
): readonly IResolvedArcOrbitItem[] {
|
|
63
|
+
const { items, defaultColor = DEFAULT_ARC_ORBIT_COLOR, count = 3 } = options;
|
|
64
|
+
|
|
65
|
+
const result: IResolvedArcOrbitItem[] = [];
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < count; i++) {
|
|
68
|
+
const custom = items?.[i];
|
|
69
|
+
const defaultItem = DEFAULT_ARC_ORBIT_ITEMS[i];
|
|
70
|
+
|
|
71
|
+
const id =
|
|
72
|
+
custom?.id !== undefined
|
|
73
|
+
? custom.id
|
|
74
|
+
: (defaultItem?.id ?? String(i + 1).padStart(2, '0'));
|
|
75
|
+
|
|
76
|
+
const title = custom?.title ?? defaultItem?.title ?? 'Add Text Here';
|
|
77
|
+
const description =
|
|
78
|
+
custom?.description ?? defaultItem?.description ?? DEFAULT_LOREM_TEXT;
|
|
79
|
+
const color = custom?.color ?? defaultColor;
|
|
80
|
+
const textColor = custom?.textColor ?? defaultItem?.textColor;
|
|
81
|
+
|
|
82
|
+
result.push({
|
|
83
|
+
id,
|
|
84
|
+
title,
|
|
85
|
+
description,
|
|
86
|
+
color,
|
|
87
|
+
textColor,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ComputedRef, InjectionKey } from 'vue';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
IGearTriadItem,
|
|
5
|
+
IResolvedGearTriadItem,
|
|
6
|
+
} from './resolveGearTriadItems';
|
|
7
|
+
|
|
8
|
+
export interface IGearTriadRegistration {
|
|
9
|
+
readonly index: number;
|
|
10
|
+
readonly item: ComputedRef<IResolvedGearTriadItem>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IGearTriadRootContext {
|
|
14
|
+
readonly animation: ComputedRef<boolean>;
|
|
15
|
+
readonly isShifted: ComputedRef<boolean>;
|
|
16
|
+
readonly currentStep: ComputedRef<number>;
|
|
17
|
+
readonly activeOptionIndex: ComputedRef<number>;
|
|
18
|
+
readonly hasContents: ComputedRef<boolean>;
|
|
19
|
+
readonly registerCallout: (
|
|
20
|
+
override?: ComputedRef<IGearTriadItem> | (() => IGearTriadItem),
|
|
21
|
+
) => IGearTriadRegistration;
|
|
22
|
+
readonly registerContents: () => void;
|
|
23
|
+
readonly items: ComputedRef<
|
|
24
|
+
readonly [
|
|
25
|
+
IResolvedGearTriadItem,
|
|
26
|
+
IResolvedGearTriadItem,
|
|
27
|
+
IResolvedGearTriadItem,
|
|
28
|
+
]
|
|
29
|
+
>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IGearTriadCalloutContext {
|
|
33
|
+
readonly item: ComputedRef<IResolvedGearTriadItem>;
|
|
34
|
+
readonly index: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface IGearTriadContentsContext {
|
|
38
|
+
readonly activeItem: ComputedRef<IResolvedGearTriadItem>;
|
|
39
|
+
readonly activeOptionIndex: ComputedRef<number>;
|
|
40
|
+
readonly registerContent: (option?: number) => { index: number };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const GEAR_TRIAD_ROOT_KEY: InjectionKey<IGearTriadRootContext> = Symbol(
|
|
44
|
+
'GEAR_TRIAD_ROOT_KEY',
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
export const GEAR_TRIAD_CALLOUT_KEY: InjectionKey<IGearTriadCalloutContext> =
|
|
48
|
+
Symbol('GEAR_TRIAD_CALLOUT_KEY');
|
|
49
|
+
|
|
50
|
+
export const GEAR_TRIAD_CONTENTS_KEY: InjectionKey<IGearTriadContentsContext> =
|
|
51
|
+
Symbol('GEAR_TRIAD_CONTENTS_KEY');
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
export interface IGearTriadPoint {
|
|
2
|
+
readonly x: number;
|
|
3
|
+
readonly y: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface IGearTriadPetalGeometry {
|
|
7
|
+
readonly facePath: string;
|
|
8
|
+
readonly valleyPath: string;
|
|
9
|
+
readonly leftFoldPath: string;
|
|
10
|
+
readonly rightFoldPath: string;
|
|
11
|
+
readonly iconPosition: IGearTriadPoint;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface IGearTriadSectorGeometry extends IGearTriadPetalGeometry {}
|
|
15
|
+
|
|
16
|
+
export interface IGearTriadGeometry {
|
|
17
|
+
readonly viewBoxWidth: number;
|
|
18
|
+
readonly viewBoxHeight: number;
|
|
19
|
+
readonly centerX: number;
|
|
20
|
+
readonly centerY: number;
|
|
21
|
+
readonly hubRadius: number;
|
|
22
|
+
readonly baseGearPath: string;
|
|
23
|
+
readonly valleyPath: string;
|
|
24
|
+
/**
|
|
25
|
+
* One canonical petal geometry (facing South, 270 deg).
|
|
26
|
+
* Reused for all 3 sectors via 0 deg (Pink), 120 deg (Cyan), and 240 deg (Orange).
|
|
27
|
+
*/
|
|
28
|
+
readonly petal: IGearTriadPetalGeometry;
|
|
29
|
+
readonly cyanSector: IGearTriadSectorGeometry;
|
|
30
|
+
readonly orangeSector: IGearTriadSectorGeometry;
|
|
31
|
+
readonly pinkSector: IGearTriadSectorGeometry;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Polar to Cartesian conversion in SVG screen space:
|
|
36
|
+
* - 0 deg = East (3 o'clock)
|
|
37
|
+
* - 90 deg = North (12 o'clock, straight UP)
|
|
38
|
+
* - 180 deg = West (9 o'clock)
|
|
39
|
+
* - 270 deg = South (6 o'clock, straight DOWN)
|
|
40
|
+
*/
|
|
41
|
+
function polarToCartesian(
|
|
42
|
+
cx: number,
|
|
43
|
+
cy: number,
|
|
44
|
+
radius: number,
|
|
45
|
+
angleInDegrees: number,
|
|
46
|
+
): IGearTriadPoint {
|
|
47
|
+
const radians = (angleInDegrees * Math.PI) / 180;
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
x: Number((cx + radius * Math.cos(radians)).toFixed(2)),
|
|
51
|
+
y: Number((cy - radius * Math.sin(radians)).toFixed(2)),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function rotatePoint(
|
|
56
|
+
p: IGearTriadPoint,
|
|
57
|
+
cx: number,
|
|
58
|
+
cy: number,
|
|
59
|
+
degrees: number,
|
|
60
|
+
): IGearTriadPoint {
|
|
61
|
+
const rad = (degrees * Math.PI) / 180;
|
|
62
|
+
const dx = p.x - cx;
|
|
63
|
+
const dy = p.y - cy;
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
x: Number((dx * Math.cos(rad) - dy * Math.sin(rad) + cx).toFixed(2)),
|
|
67
|
+
y: Number((dx * Math.sin(rad) + dy * Math.cos(rad) + cy).toFixed(2)),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function createGearTriadGeometry(): IGearTriadGeometry {
|
|
72
|
+
const viewBoxWidth = 540;
|
|
73
|
+
const viewBoxHeight = 480;
|
|
74
|
+
const centerX = 270;
|
|
75
|
+
const centerY = 220;
|
|
76
|
+
const rOut = 180;
|
|
77
|
+
const rNotch = 128;
|
|
78
|
+
const hubRadius = 54;
|
|
79
|
+
|
|
80
|
+
// Base 6-tooth gear with rounded valleys (6 teeth at 0, 60, 120, 180, 240, 300 deg)
|
|
81
|
+
const toothCenters = [0, 60, 120, 180, 240, 300];
|
|
82
|
+
let baseGearPath = '';
|
|
83
|
+
for (let i = 0; i < toothCenters.length; i++) {
|
|
84
|
+
const th = toothCenters[i] ?? 0;
|
|
85
|
+
const p1 = polarToCartesian(centerX, centerY, rOut, th - 10);
|
|
86
|
+
const p2 = polarToCartesian(centerX, centerY, rOut, th + 10);
|
|
87
|
+
const p3 = polarToCartesian(centerX, centerY, rNotch, th + 20);
|
|
88
|
+
const p4 = polarToCartesian(centerX, centerY, rNotch, th + 40);
|
|
89
|
+
if (i === 0) {
|
|
90
|
+
baseGearPath += `M ${p1.x} ${p1.y}`;
|
|
91
|
+
} else {
|
|
92
|
+
baseGearPath += ` L ${p1.x} ${p1.y}`;
|
|
93
|
+
}
|
|
94
|
+
baseGearPath += ` L ${p2.x} ${p2.y} L ${p3.x} ${p3.y} A ${rNotch} ${rNotch} 0 0 0 ${p4.x} ${p4.y}`;
|
|
95
|
+
}
|
|
96
|
+
baseGearPath += ' Z';
|
|
97
|
+
|
|
98
|
+
// ONE CANONICAL PETAL SHAPE (pointing South / 270 deg):
|
|
99
|
+
// Reused across all 3 sectors via 0 deg (Pink), 120 deg (Cyan), and 240 deg (Orange)
|
|
100
|
+
const p0 = polarToCartesian(centerX, centerY, hubRadius, 230);
|
|
101
|
+
const p1 = polarToCartesian(centerX, centerY, rOut, 230);
|
|
102
|
+
const p2 = polarToCartesian(centerX, centerY, rOut, 250);
|
|
103
|
+
const p3a = polarToCartesian(centerX, centerY, rNotch, 260);
|
|
104
|
+
const p3b = polarToCartesian(centerX, centerY, rNotch, 280);
|
|
105
|
+
const p4 = polarToCartesian(centerX, centerY, rOut, 290);
|
|
106
|
+
const p5 = polarToCartesian(centerX, centerY, rOut, 310);
|
|
107
|
+
const p6 = polarToCartesian(centerX, centerY, hubRadius, 310);
|
|
108
|
+
|
|
109
|
+
const petalFacePath = [
|
|
110
|
+
`M ${p0.x} ${p0.y}`,
|
|
111
|
+
`L ${p1.x} ${p1.y}`,
|
|
112
|
+
`L ${p2.x} ${p2.y}`,
|
|
113
|
+
`L ${p3a.x} ${p3a.y}`,
|
|
114
|
+
`A ${rNotch} ${rNotch} 0 0 0 ${p3b.x} ${p3b.y}`,
|
|
115
|
+
`L ${p4.x} ${p4.y}`,
|
|
116
|
+
`L ${p5.x} ${p5.y}`,
|
|
117
|
+
`L ${p6.x} ${p6.y}`,
|
|
118
|
+
`A ${hubRadius} ${hubRadius} 0 0 1 ${p0.x} ${p0.y}`,
|
|
119
|
+
'Z',
|
|
120
|
+
].join(' ');
|
|
121
|
+
|
|
122
|
+
// The unified canonical shape between the colored petals (centered at 210 deg):
|
|
123
|
+
// Reused across all 3 inter-sector positions via 0 deg, 120 deg, and 240 deg rotations.
|
|
124
|
+
const pvFlankPink = polarToCartesian(centerX, centerY, rOut, 230);
|
|
125
|
+
const pvNotchPink = polarToCartesian(centerX, centerY, rNotch, 220);
|
|
126
|
+
const pvNotchCyan = polarToCartesian(centerX, centerY, rNotch, 200);
|
|
127
|
+
const pvFlankCyan = polarToCartesian(centerX, centerY, rOut, 190);
|
|
128
|
+
const pvHubCyan = polarToCartesian(centerX, centerY, hubRadius, 190);
|
|
129
|
+
const pvHubPink = polarToCartesian(centerX, centerY, hubRadius, 230);
|
|
130
|
+
|
|
131
|
+
const valleyPath = [
|
|
132
|
+
`M ${pvFlankPink.x} ${pvFlankPink.y}`,
|
|
133
|
+
`L ${pvNotchPink.x} ${pvNotchPink.y}`,
|
|
134
|
+
`A ${rNotch} ${rNotch} 0 0 1 ${pvNotchCyan.x} ${pvNotchCyan.y}`,
|
|
135
|
+
`L ${pvFlankCyan.x} ${pvFlankCyan.y}`,
|
|
136
|
+
`L ${pvHubCyan.x} ${pvHubCyan.y}`,
|
|
137
|
+
`A ${hubRadius} ${hubRadius} 0 0 0 ${pvHubPink.x} ${pvHubPink.y}`,
|
|
138
|
+
'Z',
|
|
139
|
+
].join(' ');
|
|
140
|
+
|
|
141
|
+
// Legacy fold paths maintained for backwards compatibility
|
|
142
|
+
const pValley220 = polarToCartesian(centerX, centerY, rNotch, 220);
|
|
143
|
+
const pValley210 = polarToCartesian(centerX, centerY, rNotch, 210);
|
|
144
|
+
const pHub210 = polarToCartesian(centerX, centerY, hubRadius, 210);
|
|
145
|
+
|
|
146
|
+
const leftFoldPath = [
|
|
147
|
+
`M ${p1.x} ${p1.y}`,
|
|
148
|
+
`L ${pValley220.x} ${pValley220.y}`,
|
|
149
|
+
`A ${rNotch} ${rNotch} 0 0 0 ${pValley210.x} ${pValley210.y}`,
|
|
150
|
+
`L ${pHub210.x} ${pHub210.y}`,
|
|
151
|
+
`A ${hubRadius} ${hubRadius} 0 0 1 ${p0.x} ${p0.y}`,
|
|
152
|
+
'Z',
|
|
153
|
+
].join(' ');
|
|
154
|
+
|
|
155
|
+
const pValley320 = polarToCartesian(centerX, centerY, rNotch, 320);
|
|
156
|
+
const pValley330 = polarToCartesian(centerX, centerY, rNotch, 330);
|
|
157
|
+
const pHub330 = polarToCartesian(centerX, centerY, hubRadius, 330);
|
|
158
|
+
|
|
159
|
+
const rightFoldPath = [
|
|
160
|
+
`M ${p5.x} ${p5.y}`,
|
|
161
|
+
`L ${pValley320.x} ${pValley320.y}`,
|
|
162
|
+
`A ${rNotch} ${rNotch} 0 0 1 ${pValley330.x} ${pValley330.y}`,
|
|
163
|
+
`L ${pHub330.x} ${pHub330.y}`,
|
|
164
|
+
`A ${hubRadius} ${hubRadius} 0 0 0 ${p6.x} ${p6.y}`,
|
|
165
|
+
'Z',
|
|
166
|
+
].join(' ');
|
|
167
|
+
|
|
168
|
+
const canonicalIconPos = polarToCartesian(centerX, centerY, 105, 270);
|
|
169
|
+
|
|
170
|
+
const canonicalPetal: IGearTriadPetalGeometry = {
|
|
171
|
+
facePath: petalFacePath,
|
|
172
|
+
valleyPath,
|
|
173
|
+
leftFoldPath,
|
|
174
|
+
rightFoldPath,
|
|
175
|
+
iconPosition: canonicalIconPos,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// The 3 sectors reuse the canonical petal rotated by 120 deg intervals:
|
|
179
|
+
// - Pink (Option 3, Bottom): rotation 0 deg
|
|
180
|
+
// - Cyan (Option 1, Top-Left): rotation 120 deg
|
|
181
|
+
// - Orange (Option 2, Top-Right): rotation 240 deg
|
|
182
|
+
const pinkSector: IGearTriadSectorGeometry = {
|
|
183
|
+
...canonicalPetal,
|
|
184
|
+
iconPosition: canonicalIconPos,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const cyanSector: IGearTriadSectorGeometry = {
|
|
188
|
+
...canonicalPetal,
|
|
189
|
+
iconPosition: rotatePoint(canonicalIconPos, centerX, centerY, 120),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const orangeSector: IGearTriadSectorGeometry = {
|
|
193
|
+
...canonicalPetal,
|
|
194
|
+
iconPosition: rotatePoint(canonicalIconPos, centerX, centerY, 240),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
viewBoxWidth,
|
|
199
|
+
viewBoxHeight,
|
|
200
|
+
centerX,
|
|
201
|
+
centerY,
|
|
202
|
+
hubRadius,
|
|
203
|
+
baseGearPath,
|
|
204
|
+
valleyPath,
|
|
205
|
+
petal: canonicalPetal,
|
|
206
|
+
cyanSector,
|
|
207
|
+
orangeSector,
|
|
208
|
+
pinkSector,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
export type TGearTriadIcon =
|
|
2
|
+
| 'document'
|
|
3
|
+
| 'file'
|
|
4
|
+
| 'file-text'
|
|
5
|
+
| 'shield'
|
|
6
|
+
| 'shield-star'
|
|
7
|
+
| 'archive'
|
|
8
|
+
| 'box'
|
|
9
|
+
| 'package'
|
|
10
|
+
| 'gear'
|
|
11
|
+
| 'settings'
|
|
12
|
+
| string
|
|
13
|
+
| object
|
|
14
|
+
| false;
|
|
15
|
+
|
|
16
|
+
export interface IGearTriadItem {
|
|
17
|
+
readonly id?: string | number;
|
|
18
|
+
readonly color?: string;
|
|
19
|
+
readonly depthColor?: string;
|
|
20
|
+
readonly title?: string;
|
|
21
|
+
readonly description?: string;
|
|
22
|
+
readonly icon?: TGearTriadIcon;
|
|
23
|
+
readonly cardBg?: string;
|
|
24
|
+
readonly textColor?: string;
|
|
25
|
+
readonly titleColor?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface IResolvedGearTriadItem {
|
|
29
|
+
readonly id: string | number;
|
|
30
|
+
readonly color: string;
|
|
31
|
+
readonly depthColor: string;
|
|
32
|
+
readonly title: string;
|
|
33
|
+
readonly description: string;
|
|
34
|
+
readonly icon?: TGearTriadIcon;
|
|
35
|
+
readonly cardBg?: string;
|
|
36
|
+
readonly textColor: string;
|
|
37
|
+
readonly titleColor: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const DEFAULT_LOREM =
|
|
41
|
+
'There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration.';
|
|
42
|
+
|
|
43
|
+
const defaultItem0: IResolvedGearTriadItem = {
|
|
44
|
+
id: '01',
|
|
45
|
+
color: '#1fb5b5',
|
|
46
|
+
depthColor: '#126e7a',
|
|
47
|
+
title: 'Option 1',
|
|
48
|
+
description: DEFAULT_LOREM,
|
|
49
|
+
icon: undefined,
|
|
50
|
+
cardBg: undefined,
|
|
51
|
+
textColor: '#64748b',
|
|
52
|
+
titleColor: '#0f172a',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const defaultItem1: IResolvedGearTriadItem = {
|
|
56
|
+
id: '02',
|
|
57
|
+
color: '#f38942',
|
|
58
|
+
depthColor: '#aa521d',
|
|
59
|
+
title: 'Option 2',
|
|
60
|
+
description: DEFAULT_LOREM,
|
|
61
|
+
icon: undefined,
|
|
62
|
+
cardBg: undefined,
|
|
63
|
+
textColor: '#64748b',
|
|
64
|
+
titleColor: '#0f172a',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const defaultItem2: IResolvedGearTriadItem = {
|
|
68
|
+
id: '03',
|
|
69
|
+
color: '#f05367',
|
|
70
|
+
depthColor: '#a82c3c',
|
|
71
|
+
title: 'Option 3',
|
|
72
|
+
description: DEFAULT_LOREM,
|
|
73
|
+
icon: undefined,
|
|
74
|
+
cardBg: undefined,
|
|
75
|
+
textColor: '#64748b',
|
|
76
|
+
titleColor: '#0f172a',
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const DEFAULT_GEAR_TRIAD_ITEMS: readonly [
|
|
80
|
+
IResolvedGearTriadItem,
|
|
81
|
+
IResolvedGearTriadItem,
|
|
82
|
+
IResolvedGearTriadItem,
|
|
83
|
+
] = [defaultItem0, defaultItem1, defaultItem2];
|
|
84
|
+
|
|
85
|
+
export const GEAR_TRIAD_BASE_COLOR = '#11455c';
|
|
86
|
+
|
|
87
|
+
export function getDefaultGearTriadItem(index: number): IResolvedGearTriadItem {
|
|
88
|
+
const safe = Math.abs(index) % 3;
|
|
89
|
+
|
|
90
|
+
if (safe === 1) {
|
|
91
|
+
return defaultItem1;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (safe === 2) {
|
|
95
|
+
return defaultItem2;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return defaultItem0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function resolveSingleGearTriadItem(
|
|
102
|
+
item: IGearTriadItem | undefined,
|
|
103
|
+
fallbackIndex = 0,
|
|
104
|
+
): IResolvedGearTriadItem {
|
|
105
|
+
const fallback = getDefaultGearTriadItem(fallbackIndex);
|
|
106
|
+
|
|
107
|
+
if (!item) {
|
|
108
|
+
return fallback;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
id: item.id ?? fallback.id,
|
|
113
|
+
color: item.color ?? fallback.color,
|
|
114
|
+
depthColor: item.depthColor ?? fallback.depthColor,
|
|
115
|
+
title: item.title ?? fallback.title,
|
|
116
|
+
description: item.description ?? fallback.description,
|
|
117
|
+
icon: item.icon !== undefined ? item.icon : fallback.icon,
|
|
118
|
+
cardBg: item.cardBg !== undefined ? item.cardBg : fallback.cardBg,
|
|
119
|
+
textColor: item.textColor ?? fallback.textColor,
|
|
120
|
+
titleColor: item.titleColor ?? fallback.titleColor,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function resolveGearTriadItems(
|
|
125
|
+
items?: readonly IGearTriadItem[],
|
|
126
|
+
icons?: readonly (TGearTriadIcon | undefined)[],
|
|
127
|
+
): readonly [
|
|
128
|
+
IResolvedGearTriadItem,
|
|
129
|
+
IResolvedGearTriadItem,
|
|
130
|
+
IResolvedGearTriadItem,
|
|
131
|
+
] {
|
|
132
|
+
const item0 = items?.[0];
|
|
133
|
+
const item1 = items?.[1];
|
|
134
|
+
const item2 = items?.[2];
|
|
135
|
+
|
|
136
|
+
const merged0 = item0
|
|
137
|
+
? { icon: item0.icon ?? icons?.[0], ...item0 }
|
|
138
|
+
: icons?.[0] !== undefined
|
|
139
|
+
? { icon: icons[0] }
|
|
140
|
+
: undefined;
|
|
141
|
+
|
|
142
|
+
const merged1 = item1
|
|
143
|
+
? { icon: item1.icon ?? icons?.[1], ...item1 }
|
|
144
|
+
: icons?.[1] !== undefined
|
|
145
|
+
? { icon: icons[1] }
|
|
146
|
+
: undefined;
|
|
147
|
+
|
|
148
|
+
const merged2 = item2
|
|
149
|
+
? { icon: item2.icon ?? icons?.[2], ...item2 }
|
|
150
|
+
: icons?.[2] !== undefined
|
|
151
|
+
? { icon: icons[2] }
|
|
152
|
+
: undefined;
|
|
153
|
+
|
|
154
|
+
return [
|
|
155
|
+
resolveSingleGearTriadItem(merged0, 0),
|
|
156
|
+
resolveSingleGearTriadItem(merged1, 1),
|
|
157
|
+
resolveSingleGearTriadItem(merged2, 2),
|
|
158
|
+
];
|
|
159
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ComputedRef, InjectionKey, Ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
import type { IPyramidPalette, IPyramidRegistration } from '../shared';
|
|
4
|
+
import type { ICircularLayerGeometry, ICircularPyramidGeometry } from './types';
|
|
5
|
+
|
|
6
|
+
export interface ICircularPyramidRegistration extends IPyramidRegistration {}
|
|
7
|
+
|
|
8
|
+
export interface ICircularPyramidRootContext {
|
|
9
|
+
readonly count: ComputedRef<number>;
|
|
10
|
+
readonly activeIndex: Ref<number> | ComputedRef<number>;
|
|
11
|
+
readonly color: ComputedRef<string>;
|
|
12
|
+
readonly geo: ComputedRef<ICircularPyramidGeometry>;
|
|
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 ICircularPyramidStackContext {
|
|
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<ICircularLayerGeometry | undefined>;
|
|
37
|
+
readonly deltaY?: ComputedRef<number>;
|
|
38
|
+
readonly connectorWidth?: ComputedRef<number>;
|
|
39
|
+
readonly setActive?: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const CIRCULAR_PYRAMID_ROOT_KEY: InjectionKey<ICircularPyramidRootContext> =
|
|
43
|
+
Symbol('CIRCULAR_PYRAMID_ROOT_KEY');
|
|
44
|
+
|
|
45
|
+
export const CIRCULAR_PYRAMID_STACK_KEY: InjectionKey<ICircularPyramidStackContext> =
|
|
46
|
+
Symbol('CIRCULAR_PYRAMID_STACK_KEY');
|
|
@@ -0,0 +1,143 @@
|
|
|
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
|
+
ICircularLayerGeometry,
|
|
13
|
+
ICircularPyramidGeometry,
|
|
14
|
+
ICircularPyramidPalette,
|
|
15
|
+
ICreateCircularPyramidOptions,
|
|
16
|
+
} from './types';
|
|
17
|
+
|
|
18
|
+
export const DEFAULT_CIRCULAR_PYRAMID_COLOR = DEFAULT_PYRAMID_COLOR;
|
|
19
|
+
export const DEFAULT_CIRCULAR_PYRAMID_STACK_COLORS: readonly string[] = [
|
|
20
|
+
'#3b82f6', // 0: Blue
|
|
21
|
+
'#6366f1', // 1: Indigo
|
|
22
|
+
'#10b981', // 2: Emerald
|
|
23
|
+
'#f59e0b', // 3: Amber
|
|
24
|
+
'#ec4899', // 4: Pink
|
|
25
|
+
'#06b6d4', // 5: Cyan
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
export const MIN_CIRCULAR_COUNT = MIN_PYRAMID_COUNT;
|
|
29
|
+
export const MAX_CIRCULAR_COUNT = MAX_PYRAMID_COUNT;
|
|
30
|
+
|
|
31
|
+
export { darkenColor, lightenColor };
|
|
32
|
+
|
|
33
|
+
export function clampCircularCount(count?: number): number {
|
|
34
|
+
return clampPyramidCount(count);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveCircularPyramidPalette(
|
|
38
|
+
baseColor: string = DEFAULT_CIRCULAR_PYRAMID_COLOR,
|
|
39
|
+
): ICircularPyramidPalette {
|
|
40
|
+
return resolvePyramidPalette(baseColor);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createCircularPyramidGeometry(
|
|
44
|
+
options: ICreateCircularPyramidOptions = {},
|
|
45
|
+
): ICircularPyramidGeometry {
|
|
46
|
+
const viewBoxWidth = options.viewBoxWidth ?? 960;
|
|
47
|
+
const viewBoxHeight = options.viewBoxHeight ?? 440;
|
|
48
|
+
const count = clampCircularCount(options.count);
|
|
49
|
+
const ratio = options.ratio ?? 0.45;
|
|
50
|
+
|
|
51
|
+
const cx = options.cx ?? viewBoxWidth / 2;
|
|
52
|
+
const cylinderHeight = options.cylinderHeight ?? (count >= 5 ? 7 : 8);
|
|
53
|
+
|
|
54
|
+
if (count === 1) {
|
|
55
|
+
const rx = options.topRadius ?? 120;
|
|
56
|
+
const ry = Number((rx * ratio).toFixed(2));
|
|
57
|
+
const cy = Number((viewBoxHeight / 2 - cylinderHeight / 2).toFixed(2));
|
|
58
|
+
|
|
59
|
+
const topEllipsePath = `M ${cx - rx} ${cy} a ${rx} ${ry} 0 1 0 ${rx * 2} 0 a ${rx} ${ry} 0 1 0 ${-rx * 2} 0`;
|
|
60
|
+
const cylinderPath = `M ${cx - rx} ${cy} L ${cx - rx} ${cy + cylinderHeight} A ${rx} ${ry} 0 0 0 ${cx + rx} ${cy + cylinderHeight} L ${cx + rx} ${cy} A ${rx} ${ry} 0 0 1 ${cx - rx} ${cy} Z`;
|
|
61
|
+
const bottomArcPath = `M ${cx - rx} ${cy + cylinderHeight} A ${rx} ${ry} 0 0 0 ${cx + rx} ${cy + cylinderHeight}`;
|
|
62
|
+
|
|
63
|
+
const slabCenterY = Number((cy + cylinderHeight / 2).toFixed(2));
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
viewBoxWidth,
|
|
67
|
+
viewBoxHeight,
|
|
68
|
+
count: 1,
|
|
69
|
+
layers: [
|
|
70
|
+
{
|
|
71
|
+
index: 0,
|
|
72
|
+
cx,
|
|
73
|
+
cy,
|
|
74
|
+
rx,
|
|
75
|
+
ry,
|
|
76
|
+
cylinderHeight,
|
|
77
|
+
cylinderPath,
|
|
78
|
+
bottomArcPath,
|
|
79
|
+
topEllipsePath,
|
|
80
|
+
slabCenterY,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const topRadius =
|
|
87
|
+
options.topRadius ??
|
|
88
|
+
(count <= 2 ? 120 : Math.max(80, 115 - (count - 3) * 8));
|
|
89
|
+
const bottomRadius = options.bottomRadius ?? 215;
|
|
90
|
+
|
|
91
|
+
const radii: { rx: number; ry: number }[] = [];
|
|
92
|
+
for (let i = 0; i < count; i++) {
|
|
93
|
+
const t = i / (count - 1);
|
|
94
|
+
const rx = Number((topRadius + t * (bottomRadius - topRadius)).toFixed(2));
|
|
95
|
+
const ry = Number((rx * ratio).toFixed(2));
|
|
96
|
+
radii.push({ rx, ry });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const { startCy, stepYList } = computePyramidStacking({
|
|
100
|
+
count,
|
|
101
|
+
viewBoxHeight,
|
|
102
|
+
slabHeight: cylinderHeight,
|
|
103
|
+
radii,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const layers: ICircularLayerGeometry[] = [];
|
|
107
|
+
let currentCy = startCy;
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < count; i++) {
|
|
110
|
+
const rx = radii[i]?.rx ?? topRadius;
|
|
111
|
+
const ry = radii[i]?.ry ?? topRadius * ratio;
|
|
112
|
+
const cy = Number(currentCy.toFixed(2));
|
|
113
|
+
|
|
114
|
+
const topEllipsePath = `M ${cx - rx} ${cy} a ${rx} ${ry} 0 1 0 ${rx * 2} 0 a ${rx} ${ry} 0 1 0 ${-rx * 2} 0`;
|
|
115
|
+
const cylinderPath = `M ${cx - rx} ${cy} L ${cx - rx} ${cy + cylinderHeight} A ${rx} ${ry} 0 0 0 ${cx + rx} ${cy + cylinderHeight} L ${cx + rx} ${cy} A ${rx} ${ry} 0 0 1 ${cx - rx} ${cy} Z`;
|
|
116
|
+
const bottomArcPath = `M ${cx - rx} ${cy + cylinderHeight} A ${rx} ${ry} 0 0 0 ${cx + rx} ${cy + cylinderHeight}`;
|
|
117
|
+
const slabCenterY = Number((cy + cylinderHeight / 2).toFixed(2));
|
|
118
|
+
|
|
119
|
+
layers.push({
|
|
120
|
+
index: i,
|
|
121
|
+
cx,
|
|
122
|
+
cy,
|
|
123
|
+
rx,
|
|
124
|
+
ry,
|
|
125
|
+
cylinderHeight,
|
|
126
|
+
cylinderPath,
|
|
127
|
+
bottomArcPath,
|
|
128
|
+
topEllipsePath,
|
|
129
|
+
slabCenterY,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (i < count - 1) {
|
|
133
|
+
currentCy += stepYList[i] ?? 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
viewBoxWidth,
|
|
139
|
+
viewBoxHeight,
|
|
140
|
+
count,
|
|
141
|
+
layers,
|
|
142
|
+
};
|
|
143
|
+
}
|