269 lines
7.6 KiB
TypeScript
269 lines
7.6 KiB
TypeScript
/** 控笔图形:100×100 坐标系,Y 轴向下 */
|
|
|
|
export type PenControlCategory = 'line' | 'curve' | 'shape' | 'combo';
|
|
|
|
export const PEN_CONTROL_CATEGORY_LABEL: Record<PenControlCategory, string> = {
|
|
line: '直线',
|
|
curve: '曲线',
|
|
shape: '形状',
|
|
combo: '组合',
|
|
};
|
|
|
|
export type PenControlRenderMode = 'stroke' | 'fill' | 'both';
|
|
|
|
export interface PenControlPattern {
|
|
id: string;
|
|
name: string;
|
|
category: PenControlCategory;
|
|
categoryLabel?: string;
|
|
tags: string[];
|
|
paths: string[];
|
|
render: PenControlRenderMode;
|
|
strokeScale?: number;
|
|
}
|
|
|
|
const RAW_PEN_CONTROL_PATTERNS: PenControlPattern[] = [
|
|
{
|
|
id: 'horizontal-lines',
|
|
name: '横线',
|
|
category: 'line',
|
|
tags: ['基础', '直线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 14 26 L 86 26',
|
|
'M 14 42 L 86 42',
|
|
'M 14 58 L 86 58',
|
|
'M 14 74 L 86 74',
|
|
],
|
|
},
|
|
{
|
|
id: 'vertical-lines',
|
|
name: '竖线',
|
|
category: 'line',
|
|
tags: ['基础', '直线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 26 14 L 26 86',
|
|
'M 42 14 L 42 86',
|
|
'M 58 14 L 58 86',
|
|
'M 74 14 L 74 86',
|
|
],
|
|
},
|
|
{
|
|
id: 'diagonal-lr',
|
|
name: '左斜线',
|
|
category: 'line',
|
|
tags: ['斜线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 14 14 L 86 86',
|
|
'M 14 32 L 68 86',
|
|
'M 32 14 L 86 68',
|
|
'M 14 50 L 50 86',
|
|
'M 50 14 L 86 50',
|
|
],
|
|
},
|
|
{
|
|
id: 'diagonal-rl',
|
|
name: '右斜线',
|
|
category: 'line',
|
|
tags: ['斜线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 86 14 L 14 86',
|
|
'M 86 32 L 32 86',
|
|
'M 68 14 L 14 68',
|
|
'M 86 50 L 50 86',
|
|
'M 50 14 L 14 50',
|
|
],
|
|
},
|
|
{
|
|
id: 'zigzag',
|
|
name: '锯齿线',
|
|
category: 'line',
|
|
tags: ['折线'],
|
|
render: 'stroke',
|
|
paths: ['M 14 18 L 26 82 L 38 18 L 50 82 L 62 18 L 74 82 L 86 18'],
|
|
},
|
|
{
|
|
id: 'loop-single',
|
|
name: '回字圈',
|
|
category: 'shape',
|
|
tags: ['形状'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 14 14 L 86 14 L 86 86 L 14 86 Z',
|
|
'M 28 28 L 72 28 L 72 72 L 28 72 Z',
|
|
'M 42 42 L 58 42 L 58 58 L 42 58 Z',
|
|
],
|
|
},
|
|
{
|
|
id: 'spiral',
|
|
name: '蜗牛线',
|
|
category: 'combo',
|
|
tags: ['组合'],
|
|
render: 'stroke',
|
|
strokeScale: 0.95,
|
|
paths: [
|
|
'M 88 50 Q 88 86, 50 86 Q 14 86, 14 50 Q 14 18, 50 18 Q 80 18, 80 50 Q 80 74, 50 74 Q 26 74, 26 50 Q 26 30, 50 30 Q 68 30, 68 50 Q 68 62, 50 62 Q 38 62, 38 50 Q 38 44, 50 44 Q 56 44, 56 50',
|
|
],
|
|
},
|
|
{
|
|
id: 'mountain',
|
|
name: '山峰线',
|
|
category: 'line',
|
|
tags: ['折线'],
|
|
render: 'stroke',
|
|
paths: ['M 14 76 L 32 28 L 50 64 L 68 28 L 86 76'],
|
|
},
|
|
{
|
|
id: 'stair',
|
|
name: '台阶线',
|
|
category: 'line',
|
|
tags: ['折线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 14 58 L 14 43 L 38 43 L 38 28 L 62 28 L 62 13 L 86 13',
|
|
'M 14 88 L 14 73 L 38 73 L 38 58 L 62 58 L 62 43 L 86 43',
|
|
],
|
|
},
|
|
{
|
|
id: 'corner-turn',
|
|
name: '转折线',
|
|
category: 'line',
|
|
tags: ['折线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 86 86 L 14 86 L 14 14 L 86 14 L 86 74 L 26 74 L 26 26 L 74 26 L 74 62 L 38 62 L 38 38 L 62 38 L 62 50',
|
|
],
|
|
},
|
|
{
|
|
id: 'wave',
|
|
name: '波浪线',
|
|
category: 'curve',
|
|
tags: ['曲线'],
|
|
render: 'stroke',
|
|
paths: ['M 14 50 Q 32 18, 50 50 Q 68 82, 86 50'],
|
|
},
|
|
{
|
|
id: 'horizontal-curve',
|
|
name: '横曲线',
|
|
category: 'curve',
|
|
tags: ['曲线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 14 24 Q 20 8, 26 24 Q 32 40, 38 24 Q 44 8, 50 24 Q 56 40, 62 24 Q 68 8, 74 24 Q 80 40, 86 24',
|
|
'M 14 50 Q 20 34, 26 50 Q 32 66, 38 50 Q 44 34, 50 50 Q 56 66, 62 50 Q 68 34, 74 50 Q 80 66, 86 50',
|
|
'M 14 76 Q 20 60, 26 76 Q 32 92, 38 76 Q 44 60, 50 76 Q 56 92, 62 76 Q 68 60, 74 76 Q 80 92, 86 76',
|
|
],
|
|
},
|
|
{
|
|
id: 'vertical-curve',
|
|
name: '竖曲线',
|
|
category: 'curve',
|
|
tags: ['曲线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 24 14 Q 8 20, 24 26 Q 40 32, 24 38 Q 8 44, 24 50 Q 40 56, 24 62 Q 8 68, 24 74 Q 40 80, 24 86',
|
|
'M 50 14 Q 34 20, 50 26 Q 66 32, 50 38 Q 34 44, 50 50 Q 66 56, 50 62 Q 34 68, 50 74 Q 66 80, 50 86',
|
|
'M 76 14 Q 60 20, 76 26 Q 92 32, 76 38 Q 60 44, 76 50 Q 92 56, 76 62 Q 60 68, 76 74 Q 92 80, 76 86',
|
|
],
|
|
},
|
|
{
|
|
id: 's-curve',
|
|
name: 'S 弯',
|
|
category: 'curve',
|
|
tags: ['曲线'],
|
|
render: 'stroke',
|
|
strokeScale: 1.1,
|
|
paths: ['M 80 16 Q 18 18, 50 50 Q 82 82, 20 84'],
|
|
},
|
|
{
|
|
id: 'arc-up',
|
|
name: '左弧线',
|
|
category: 'curve',
|
|
tags: ['弧线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 52 13 Q 13 13, 13 52',
|
|
'M 69 30 Q 30 30, 30 69',
|
|
'M 86 47 Q 47 47, 47 86',
|
|
],
|
|
},
|
|
{
|
|
id: 'arc-down',
|
|
name: '右弧线',
|
|
category: 'curve',
|
|
tags: ['弧线'],
|
|
render: 'stroke',
|
|
paths: [
|
|
'M 13 52 Q 52 52, 52 13',
|
|
'M 30 69 Q 69 69, 69 30',
|
|
'M 47 86 Q 86 86, 86 47',
|
|
],
|
|
},
|
|
|
|
{
|
|
id: 'figure-eight',
|
|
name: '长8线',
|
|
category: 'combo',
|
|
tags: ['组合'],
|
|
render: 'stroke',
|
|
strokeScale: 0.95,
|
|
paths: [
|
|
'M 20 50 C 10 41, 13 20, 27 13 C 41 6, 42 28, 34 40 C 30 45, 25 49, 20 50 C 30 59, 27 80, 14 87 C 5 92, 5 72, 8 60 C 11 55, 15 51, 20 50',
|
|
'M 50 50 C 40 41, 43 20, 57 13 C 71 6, 72 28, 64 40 C 60 45, 55 49, 50 50 C 60 59, 57 80, 43 87 C 32 92, 31 72, 36 60 C 40 55, 45 51, 50 50',
|
|
'M 80 50 C 70 41, 73 20, 87 13 C 99 7, 100 28, 94 40 C 90 45, 85 49, 80 50 C 90 59, 87 80, 73 87 C 62 92, 61 72, 66 60 C 70 55, 75 51, 80 50',
|
|
],
|
|
},
|
|
{
|
|
id: 'cross-hatch',
|
|
name: '小太阳',
|
|
category: 'combo',
|
|
tags: ['组合'],
|
|
render: 'stroke',
|
|
strokeScale: 0.9,
|
|
paths: [
|
|
'M 62 50 C 62 56.6, 56.6 62, 50 62 C 43.4 62, 38 56.6, 38 50 C 38 43.4, 43.4 38, 50 38 C 56.6 38, 62 43.4, 62 50 Z',
|
|
'M 50 30 L 50 12',
|
|
'M 50 70 L 50 88',
|
|
'M 30 50 L 12 50',
|
|
'M 70 50 L 88 50',
|
|
'M 36 36 L 22 22',
|
|
'M 64 36 L 78 22',
|
|
'M 36 64 L 22 78',
|
|
'M 64 64 L 78 78',
|
|
],
|
|
},
|
|
];
|
|
|
|
export const PEN_CONTROL_PATTERNS: PenControlPattern[] =
|
|
RAW_PEN_CONTROL_PATTERNS.map((p) => ({
|
|
...p,
|
|
categoryLabel: PEN_CONTROL_CATEGORY_LABEL[p.category],
|
|
}));
|
|
|
|
const PATTERN_BY_ID = Object.fromEntries(
|
|
PEN_CONTROL_PATTERNS.map((p) => [p.id, p]),
|
|
) as Record<string, PenControlPattern>;
|
|
|
|
export function getPenControlPattern(
|
|
id: string,
|
|
): PenControlPattern | undefined {
|
|
return PATTERN_BY_ID[id];
|
|
}
|
|
|
|
export const PEN_CONTROL_PATTERN_IDS = PEN_CONTROL_PATTERNS.map((p) => p.id);
|
|
|
|
export function pickRandomPatternIds(
|
|
count: number,
|
|
exclude: string[] = [],
|
|
): string[] {
|
|
const pool = PEN_CONTROL_PATTERNS.map((p) => p.id).filter(
|
|
(id) => !exclude.includes(id),
|
|
);
|
|
const n = Math.max(0, Math.min(count, pool.length));
|
|
const shuffled = [...pool].sort(() => Math.random() - 0.5);
|
|
return shuffled.slice(0, n);
|
|
}
|