251 lines
7.9 KiB
TypeScript
251 lines
7.9 KiB
TypeScript
import GridDraw, { GridGroup } from '../shared/service/gridDraw';
|
||
import { createFocusPage } from '../shared/common/focusPageMixin';
|
||
import { GridDrawingData } from '../shared/service/gridDraw';
|
||
import { generateCompletePattern } from '../shared/utils/gridUtils';
|
||
import { ALL_3X3_SHAPES } from '../shared/shapes/gridShapes3x3';
|
||
import { ALL_5X5_SHAPES } from '../shared/shapes/gridShapes5x5';
|
||
import { ALL_7X7_SHAPES } from '../shared/shapes/gridShapes7x7';
|
||
import { ShapeTemplate, GridCell } from '../shared/types/gridTypes';
|
||
import { GridConfig } from '../shared/types/gridTypes';
|
||
|
||
createFocusPage({
|
||
canvas: null as Canvas | null,
|
||
ctx: null as RenderingContext | null,
|
||
boxHeight: 0,
|
||
boxWidth: 0,
|
||
drawService: null as GridDraw | null,
|
||
gridData: null as GridDrawingData | null,
|
||
|
||
data: {
|
||
pageTitle: '格子仿画',
|
||
functionId: '',
|
||
hasContent: false,
|
||
showShareDialog: false,
|
||
currentMode: '3x3', // '3x3', '5x5', '7x7'
|
||
currentModeName: '3×3',
|
||
typeActions: [
|
||
{ name: '3×3', value: '3x3' },
|
||
{ name: '5×5', value: '5x5' },
|
||
{ name: '7×7', value: '7x7' },
|
||
],
|
||
},
|
||
|
||
onLoad(options: { id?: string }) {
|
||
const functionId = options.id || 'grid-drawing';
|
||
this.setData({
|
||
functionId,
|
||
});
|
||
this.initPageInfo(functionId, '格子仿画');
|
||
},
|
||
|
||
onReady() {
|
||
this.initCanvas({
|
||
createDrawService: (
|
||
canvas: Canvas,
|
||
ctx: RenderingContext,
|
||
options?: Record<string, any>,
|
||
) => {
|
||
return new GridDraw(canvas, ctx, options);
|
||
},
|
||
drawServiceOptions: {
|
||
subTitle: '在网格中填充颜色,形成各种形状',
|
||
functionId: this.data.functionId,
|
||
},
|
||
onCanvasReady: () => {
|
||
// 初始随机生成
|
||
this.onRandom();
|
||
},
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 绘制Canvas内容
|
||
*/
|
||
async drawCanvas() {
|
||
if (!this.ctx || !this.drawService || !this.gridData) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 更新 Header 的 Title
|
||
if (this.drawService) {
|
||
this.drawService.options.title = this.data.currentModeName;
|
||
}
|
||
|
||
await this.drawService.draw(this.gridData);
|
||
this.setData({ hasContent: true });
|
||
} catch (error) {
|
||
console.error('绘制失败:', error);
|
||
this.setData({ hasContent: false });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 检查两个图案是否相同(通过比较格子的位置和颜色)
|
||
*/
|
||
arePatternsEqual(cells1: GridCell[], cells2: GridCell[]): boolean {
|
||
if (cells1.length !== cells2.length) {
|
||
return false;
|
||
}
|
||
|
||
// 将格子转换为字符串键进行比较
|
||
const pattern1 = cells1
|
||
.map((c) => `${c.x},${c.y},${c.color}`)
|
||
.sort()
|
||
.join('|');
|
||
const pattern2 = cells2
|
||
.map((c) => `${c.x},${c.y},${c.color}`)
|
||
.sort()
|
||
.join('|');
|
||
|
||
return pattern1 === pattern2;
|
||
},
|
||
|
||
/**
|
||
* 生成不重复的图案组
|
||
*/
|
||
generateUniqueGroups(
|
||
allShapes: ShapeTemplate[],
|
||
count: number,
|
||
config: GridConfig,
|
||
): GridGroup[] {
|
||
const groups: GridGroup[] = [];
|
||
const usedPatterns = new Set<string>(); // 用于存储已使用的图案签名
|
||
|
||
// 如果需要的数量超过可用形状数量,允许重复使用
|
||
const maxUniqueShapes = Math.min(count, allShapes.length);
|
||
const availableShapes = [...allShapes]; // 复制数组以便打乱
|
||
|
||
// 打乱数组顺序
|
||
for (let i = availableShapes.length - 1; i > 0; i--) {
|
||
const j = Math.floor(Math.random() * (i + 1));
|
||
[availableShapes[i], availableShapes[j]] = [
|
||
availableShapes[j],
|
||
availableShapes[i],
|
||
];
|
||
}
|
||
|
||
// 生成不重复的图案
|
||
for (let i = 0; i < count; i++) {
|
||
const shapeIndex = i % availableShapes.length;
|
||
const shape = availableShapes[shapeIndex];
|
||
const cells = generateCompletePattern(shape);
|
||
|
||
// 生成图案签名
|
||
const patternSignature = cells
|
||
.map((c) => `${c.x},${c.y},${c.color}`)
|
||
.sort()
|
||
.join('|');
|
||
|
||
// 如果图案已存在,尝试下一个形状
|
||
if (usedPatterns.has(patternSignature)) {
|
||
// 如果所有形状都用过了,允许重复
|
||
if (usedPatterns.size >= maxUniqueShapes) {
|
||
// 使用当前形状,即使重复
|
||
groups.push({
|
||
filledCells: cells,
|
||
emptyCells: [],
|
||
});
|
||
continue;
|
||
} else {
|
||
// 尝试找到未使用的形状
|
||
let found = false;
|
||
for (let j = 0; j < availableShapes.length; j++) {
|
||
const nextShape = availableShapes[j];
|
||
const nextCells = generateCompletePattern(nextShape);
|
||
const nextSignature = nextCells
|
||
.map((c) => `${c.x},${c.y},${c.color}`)
|
||
.sort()
|
||
.join('|');
|
||
|
||
if (!usedPatterns.has(nextSignature)) {
|
||
groups.push({
|
||
filledCells: nextCells,
|
||
emptyCells: [],
|
||
});
|
||
usedPatterns.add(nextSignature);
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!found) {
|
||
// 如果找不到未使用的,使用当前形状
|
||
groups.push({
|
||
filledCells: cells,
|
||
emptyCells: [],
|
||
});
|
||
}
|
||
}
|
||
} else {
|
||
// 图案未使用,添加到结果中
|
||
groups.push({
|
||
filledCells: cells,
|
||
emptyCells: [],
|
||
});
|
||
usedPatterns.add(patternSignature);
|
||
}
|
||
}
|
||
|
||
return groups;
|
||
},
|
||
|
||
/**
|
||
* 随机生成
|
||
*/
|
||
onRandom() {
|
||
const mode = this.data.currentMode;
|
||
let allShapes: ShapeTemplate[];
|
||
let config: GridConfig;
|
||
let groupsPerRow: number;
|
||
let totalRows: number;
|
||
|
||
// 根据模式选择形状和配置
|
||
if (mode === '3x3') {
|
||
allShapes = ALL_3X3_SHAPES;
|
||
config = { rows: 3, cols: 3 };
|
||
groupsPerRow = 2; // 每行2组
|
||
totalRows = 3; // 总共3行
|
||
} else if (mode === '5x5') {
|
||
allShapes = ALL_5X5_SHAPES;
|
||
config = { rows: 5, cols: 5 };
|
||
groupsPerRow = 2;
|
||
totalRows = 2; // 5x5可以展示2行
|
||
} else {
|
||
// 7x7
|
||
allShapes = ALL_7X7_SHAPES;
|
||
config = { rows: 7, cols: 7 };
|
||
groupsPerRow = 1; // 7x7每行1组
|
||
totalRows = 2; // 总共2行
|
||
}
|
||
|
||
// 生成不重复的图案组
|
||
const totalGroups = groupsPerRow * totalRows;
|
||
const groups = this.generateUniqueGroups(
|
||
allShapes,
|
||
totalGroups,
|
||
config,
|
||
);
|
||
|
||
this.gridData = {
|
||
config,
|
||
groups,
|
||
groupsPerRow,
|
||
totalRows,
|
||
};
|
||
|
||
this.drawCanvas();
|
||
},
|
||
|
||
/** 选择类型 */
|
||
onSelectType(event: any) {
|
||
const { name, value } = event.detail;
|
||
this.setData({
|
||
currentMode: value,
|
||
currentModeName: name,
|
||
});
|
||
// 重新生成数据
|
||
this.onRandom();
|
||
},
|
||
});
|