/** * 方格推理绘制服务 */ import { BaseDrawService } from '../../../service/baseDraw'; /** * 3x3网格中的位置坐标 */ export interface GridPosition { x: number; // 0-2 y: number; // 0-2 } /** * 方格推理数据 */ export interface GridReasoningData { /** 每行的数据 */ rows: Array<{ /** 第一个方格涂色位置 */ grid1: GridPosition[]; /** 第二个方格涂色位置 */ grid2: GridPosition[]; /** 运算符:+ 或 - */ operator: '+' | '-'; /** 结果方格涂色位置 */ result: GridPosition[]; }>; /** 右侧答案选项(随机排列的方格) */ answerOptions: Array<{ /** 方格涂色位置 */ positions: GridPosition[]; /** 对应的行索引(用于匹配答案) */ rowIndex: number; }>; /** 使用的颜色 */ color: string; } /** * 方格推理绘制服务 */ class GridReasoningDraw extends BaseDrawService { gridData: GridReasoningData | null = null; constructor( canvas: Canvas, ctx: RenderingContext, options?: Record, ) { super(canvas, ctx, options); } /** * 绘制方格推理内容 */ async draw(gridData: GridReasoningData) { if (!gridData || !gridData.rows || !gridData.answerOptions) { return; } this.setPrintConfig(); this.gridData = gridData; this.clear(); this.setPaper(); // 绘制Header if (this.headerType !== 'minimal') { await this.drawHeader(); } else { this.drawMiniHeader(); } // 绘制内容区域 this.drawDivider(); await this.drawContent({ canvas: this.canvas, ctx: this.ctx, gridData: this.gridData, canvasWidth: this.canvasWidth, startY: this.currentY, }); } /** * 绘制内容区域 */ private async drawContent(params: { canvas: WechatMiniprogram.Canvas; ctx: RenderingContext; gridData: GridReasoningData; canvasWidth: number; startY: number; }): Promise { const { ctx, gridData, canvasWidth } = params; let { startY } = params; startY = startY + 30; const margin = 40; // 左右边距 const gridSize = 3; // 3x3网格 const cellSize = 32; // 每个格子的宽度 const gridWidth = cellSize * gridSize; const gridHeight = cellSize * gridSize; // 左侧区域宽度(两个网格 + 运算符 + 间距) const gridSpacing = 15; // 两个网格之间的间距 const operatorWidth = 40; // 运算符宽度 const dotRadius = 7; // 圆点半径 const dotSpacing = 20; // 圆点与网格的间距 // 右侧区域宽度(答案选项网格) const rightAreaWidth = gridWidth; const rightAreaStartX = canvasWidth - margin - rightAreaWidth; // 行间距 const rowSpacing = 38; // 打乱答案选项的顺序,实现随机乱序 const shuffledAnswers = [...gridData.answerOptions].sort( () => Math.random() - 0.5, ); // 绘制5行 for (let rowIndex = 0; rowIndex < 5; rowIndex++) { const rowData = gridData.rows[rowIndex]; if (!rowData) continue; const rowY = startY + rowIndex * (gridHeight + rowSpacing); // 绘制左侧第一个网格 const grid1X = margin; this.drawGrid( ctx, grid1X, rowY, cellSize, gridSize, rowData.grid1, gridData.color, ); // 绘制运算符 const operatorStartX = grid1X + gridWidth + gridSpacing; const operatorX = operatorStartX + operatorWidth / 2; const operatorY = rowY + gridHeight / 2; const operatorSize = 30; // 运算符大小 this.drawSymbol( operatorX, operatorY, rowData.operator, operatorSize, ); // 绘制左侧第二个网格 const grid2X = operatorStartX + operatorWidth + gridSpacing; this.drawGrid( ctx, grid2X, rowY, cellSize, gridSize, rowData.grid2, gridData.color, ); // 绘制左侧圆点(垂直居中) const leftDotX = grid2X + gridWidth + dotSpacing; const leftDotY = rowY + gridHeight / 2; this.drawDot(ctx, leftDotX, leftDotY, dotRadius); // 绘制右侧答案选项(随机乱序) const answerData = shuffledAnswers[rowIndex]; if (answerData) { // 绘制右侧圆点(垂直居中) const rightDotX = rightAreaStartX - dotSpacing - dotRadius * 2; const rightDotY = rowY + gridHeight / 2; this.drawDot(ctx, rightDotX, rightDotY, dotRadius); // 绘制答案网格 this.drawGrid( ctx, rightAreaStartX, rowY, cellSize, gridSize, answerData.positions, gridData.color, ); } } } /** * 绘制3x3网格 */ private drawGrid( ctx: RenderingContext, x: number, y: number, cellSize: number, gridSize: number, filledPositions: GridPosition[], fillColor: string, ) { // 绘制网格线 this.drawGridLines(x, y, cellSize, cellSize, gridSize, gridSize); // 绘制填充的格子 ctx.fillStyle = fillColor; for (const pos of filledPositions) { const cellX = x + pos.x * cellSize; const cellY = y + pos.y * cellSize; ctx.fillRect(cellX, cellY, cellSize, cellSize); } // 重新绘制网格线(确保在填充色之上) ctx.strokeStyle = '#000'; ctx.lineWidth = 1; ctx.setLineDash([]); this.drawGridLines(x, y, cellSize, cellSize, gridSize, gridSize); } } export default GridReasoningDraw;