feat: 格子仿画修改

This commit is contained in:
R524809
2025-12-12 17:19:36 +08:00
parent 3648ccc494
commit 62ba977a45
13 changed files with 401 additions and 141 deletions
+138 -13
View File
@@ -1,11 +1,12 @@
import GridDraw from '../shared/service/gridDraw';
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 { getRandom3x3Shape } from '../shared/shapes/gridShapes3x3';
import { getRandom5x5Shape } from '../shared/shapes/gridShapes5x5';
import { getRandom7x7Shape } from '../shared/shapes/gridShapes7x7';
import { ShapeTemplate } from '../shared/types/gridTypes';
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,
@@ -79,34 +80,158 @@ createFocusPage({
}
},
/**
* 检查两个图案是否相同(通过比较格子的位置和颜色)
*/
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 shape: ShapeTemplate;
let allShapes: ShapeTemplate[];
let config: GridConfig;
let groupsPerRow: number;
let totalRows: number;
// 根据模式选择形状和配置
if (mode === '3x3') {
shape = getRandom3x3Shape();
allShapes = ALL_3X3_SHAPES;
config = { rows: 3, cols: 3 };
groupsPerRow = 2; // 每行2组
totalRows = 3; // 总共3行
} else if (mode === '5x5') {
shape = getRandom5x5Shape();
allShapes = ALL_5X5_SHAPES;
config = { rows: 5, cols: 5 };
groupsPerRow = 2;
totalRows = 2; // 5x5可以展示2行
} else {
// 7x7
shape = getRandom7x7Shape();
allShapes = ALL_7X7_SHAPES;
config = { rows: 7, cols: 7 };
groupsPerRow = 1; // 7x7每行1组
totalRows = 2; // 总共2行
}
// 生成完整的格子数据
const cells = generateCompletePattern(shape);
// 生成不重复的图案组
const totalGroups = groupsPerRow * totalRows;
const groups = this.generateUniqueGroups(
allShapes,
totalGroups,
config,
);
this.gridData = {
config,
cells,
name: shape.name,
groups,
groupsPerRow,
totalRows,
};
this.drawCanvas();
@@ -0,0 +1 @@
@import '../../base/baseDrawPage.wxss';
@@ -6,13 +6,27 @@
import { BaseDrawService } from '../../../service/baseDraw';
import { GridCell, GridConfig } from '../types/gridTypes';
/**
* 格子组数据(一组包含一个填充的网格和一个空的网格)
*/
export interface GridGroup {
/** 填充的格子数据 */
filledCells: GridCell[];
/** 空的格子(用于仿画) */
emptyCells: GridCell[]; // 始终为空数组,仅用于标识
}
/**
* 格子仿画数据
*/
export interface GridDrawingData {
config: GridConfig;
cells: GridCell[];
name?: string;
/** 格子组数组,每组包含一个填充的网格和一个空的网格 */
groups: GridGroup[];
/** 每行展示的组数 */
groupsPerRow: number;
/** 总行数 */
totalRows: number;
}
/**
@@ -33,7 +47,7 @@ class GridDraw extends BaseDrawService {
* 绘制格子仿画内容
*/
async draw(gridData: GridDrawingData) {
if (!gridData || !gridData.cells) {
if (!gridData || !gridData.groups || gridData.groups.length === 0) {
return;
}
@@ -71,19 +85,139 @@ class GridDraw extends BaseDrawService {
startY: number;
}): Promise<void> {
const { ctx, gridData, canvasWidth, startY } = params;
const { config, cells } = gridData;
const { config, groups, groupsPerRow, totalRows } = gridData;
const { rows, cols } = config;
// 计算网格尺寸
const margin = 40; // 左右边距
const groupSpacing = 20; // 组之间的间距
const rowSpacing = 30; // 行之间的间距
const dividerWidth = 2; // 分割线宽度
// 计算每组网格的尺寸
const availableWidth = canvasWidth - margin * 2;
const cellSize = Math.floor(availableWidth / cols);
const totalGroupsPerRow = groupsPerRow * 2; // 每组包含填充和空两个网格
const groupWidth =
(availableWidth -
(groupsPerRow - 1) * dividerWidth -
(totalGroupsPerRow - 1) * groupSpacing) /
totalGroupsPerRow;
const cellSize = Math.floor(groupWidth / cols);
const gridWidth = cellSize * cols;
const gridHeight = cellSize * rows;
// 计算网格起始位置(居中)
const gridStartX = margin + (availableWidth - gridWidth) / 2;
const gridStartY = startY + 30; // 顶部间距
let currentY = startY + 30; // 顶部间距
// 遍历每一行
for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) {
const rowStartIndex = rowIndex * groupsPerRow;
const rowGroups = groups.slice(
rowStartIndex,
rowStartIndex + groupsPerRow,
);
// 计算当前行的起始X位置(居中)
const rowTotalWidth =
totalGroupsPerRow * gridWidth +
(totalGroupsPerRow - 1) * groupSpacing +
(groupsPerRow - 1) * dividerWidth;
const rowStartX = margin + (availableWidth - rowTotalWidth) / 2;
let currentX = rowStartX;
// 遍历当前行的每一组
for (
let groupIndex = 0;
groupIndex < rowGroups.length;
groupIndex++
) {
const group = rowGroups[groupIndex];
// 绘制填充的网格(左侧)
this.drawSingleGrid({
ctx,
gridStartX: currentX,
gridStartY: currentY,
gridWidth,
gridHeight,
cellSize,
rows,
cols,
cells: group.filledCells,
config,
});
currentX += gridWidth + groupSpacing;
// 绘制空的网格(右侧)
this.drawSingleGrid({
ctx,
gridStartX: currentX,
gridStartY: currentY,
gridWidth,
gridHeight,
cellSize,
rows,
cols,
cells: [], // 空网格
config,
});
currentX += gridWidth;
// 如果不是最后一组,绘制垂直分割线
if (groupIndex < rowGroups.length - 1) {
ctx.strokeStyle = '#000000';
ctx.lineWidth = dividerWidth;
ctx.beginPath();
ctx.moveTo(currentX, currentY);
ctx.lineTo(currentX, currentY + gridHeight);
ctx.stroke();
currentX += dividerWidth;
}
}
currentY += gridHeight + rowSpacing;
// 如果不是最后一行,绘制水平分割线
if (rowIndex < totalRows - 1) {
ctx.strokeStyle = '#000000';
ctx.lineWidth = dividerWidth;
ctx.beginPath();
ctx.moveTo(margin, currentY - rowSpacing / 2);
ctx.lineTo(canvasWidth - margin, currentY - rowSpacing / 2);
ctx.stroke();
}
}
}
/**
* 绘制单个网格
*/
private drawSingleGrid(params: {
ctx: RenderingContext;
gridStartX: number;
gridStartY: number;
gridWidth: number;
gridHeight: number;
cellSize: number;
rows: number;
cols: number;
cells: GridCell[];
config: GridConfig;
}): void {
const {
ctx,
gridStartX,
gridStartY,
gridWidth,
gridHeight,
cellSize,
rows,
cols,
cells,
config,
} = params;
// 绘制网格线
ctx.strokeStyle = config.gridLineColor || '#000000';