返回 presentation-ai
snake-shared.ts
1 /**
2 * Shared constants and SVG helpers for the snake diagram component.
3 *
4 * Layout: CSS Grid with N columns x SNAKE_GRID_ROWS rows.
5 * Each column holds one item. Even-indexed items leave row 1 open for the
6 * upper arrow, odd-indexed items leave row 5 open for the lower arrow.
7 */
8
9 /** Number of rows in the snake grid */
10 export const SNAKE_GRID_ROWS = 5;
11
12 /** Logical height of one cell in the SVG coordinate system */
13 const SNAKE_CELL_HEIGHT = 60;
14
15 /** Total SVG height = SNAKE_GRID_ROWS * SNAKE_CELL_HEIGHT */
16 export const SNAKE_SVG_HEIGHT = SNAKE_GRID_ROWS * SNAKE_CELL_HEIGHT;
17
18 /** Width allocated per column in SVG units */
19 export const SNAKE_COL_WIDTH = 220;
20
21 /** Fixed layout height in real pixels */
22 export const SNAKE_LAYOUT_HEIGHT_PX = 280;
23
24 export const SNAKE_EDGE_PADDING_X = 48;
25 const SNAKE_BASELINE_Y = SNAKE_SVG_HEIGHT / 2;
26 export const SNAKE_START_DOT_RADIUS = 8;
27 export const SNAKE_START_DOT_GAP = 18;
28 export const SNAKE_END_ARROW_LENGTH = 38;
29 const SNAKE_SEMICIRCLE_START_CUT = 0;
30 const SNAKE_ROW_GAP_Y = 24;
31
32 /**
33 * Returns the first drawable X position for a column semicircle.
34 */
35 export function getSnakeArrowStartX(index: number): number {
36 return index * SNAKE_COL_WIDTH + SNAKE_SEMICIRCLE_START_CUT;
37 }
38
39 /**
40 * Returns the last drawable X position for a column semicircle.
41 */
42 export function getSnakeArrowEndX(index: number): number {
43 return (index + 1) * SNAKE_COL_WIDTH;
44 }
45
46 /**
47 * Returns the centerline Y position for each alternating semicircle lane.
48 */
49 export function getSnakeArrowBaselineY(index: number): number {
50 return index % 2 === 0
51 ? SNAKE_BASELINE_Y
52 : SNAKE_BASELINE_Y + SNAKE_ROW_GAP_Y;
53 }
54
55 /**
56 * Total SVG width based on total number of items.
57 */
58 export function getSvgWidth(total: number): number {
59 return Math.max(total, 1) * SNAKE_COL_WIDTH;
60 }
61
62 /**
63 * Builds the alternating top/bottom semicircle path for a snake item.
64 */
65 export function buildSnakeArrowPath(index: number): string {
66 const startX = getSnakeArrowStartX(index);
67 const endX = getSnakeArrowEndX(index);
68 const baselineY = getSnakeArrowBaselineY(index);
69 const radius = (endX - startX) / 2;
70 const sweepFlag = index % 2 === 0 ? 1 : 0;
71
72 return [
73 `M ${startX.toFixed(1)} ${baselineY.toFixed(1)}`,
74 `A ${radius.toFixed(1)} ${radius.toFixed(1)}`,
75 `0 0 ${sweepFlag}`,
76 `${endX.toFixed(1)} ${baselineY.toFixed(1)}`,
77 ].join(" ");
78 }
79
80 /**
81 * Returns CSS gridRow value for a snake item.
82 * Even items leave row 1 for the upper arrow; odd items leave row 5.
83 */
84 export function getSnakeGridRow(index: number): string {
85 return index % 2 === 0 ? "2 / 6" : "1 / 5";
86 }
87
88 /**
89 * Returns CSS gridColumn value for a snake item.
90 */
91 export function getSnakeGridColumn(index: number): string {
92 return `${index + 1}`;
93 }
94
94 lines TYPESCRIPT