/** * Writing/Tracing Worksheet Generator * Generates consistent dotted letters and numbers for tracing practice */ // Letter path definitions (school-style uppercase) const letterPaths = { 'A': 'M 0 80 L 25 0 L 50 80 M 12 50 L 38 50', 'B': 'M 0 0 L 0 80 L 35 80 Q 50 80 50 60 Q 50 40 35 40 L 0 40 M 0 0 L 35 0 Q 50 0 50 20 Q 50 40 35 40', 'C': 'M 50 15 Q 50 0 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 65', 'D': 'M 0 0 L 0 80 L 25 80 Q 50 80 50 40 Q 50 0 25 0 L 0 0', 'E': 'M 50 0 L 0 0 L 0 80 L 50 80 M 0 40 L 35 40', 'F': 'M 50 0 L 0 0 L 0 80 M 0 40 L 35 40', 'G': 'M 50 15 Q 50 0 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 50 L 30 50', 'H': 'M 0 0 L 0 80 M 50 0 L 50 80 M 0 40 L 50 40', 'I': 'M 10 0 L 40 0 M 25 0 L 25 80 M 10 80 L 40 80', 'J': 'M 10 0 L 40 0 M 30 0 L 30 60 Q 30 80 15 80 Q 0 80 0 65', 'K': 'M 0 0 L 0 80 M 50 0 L 0 45 L 50 80', 'L': 'M 0 0 L 0 80 L 50 80', 'M': 'M 0 80 L 0 0 L 25 40 L 50 0 L 50 80', 'N': 'M 0 80 L 0 0 L 50 80 L 50 0', 'O': 'M 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 40 Q 50 0 25 0', 'P': 'M 0 80 L 0 0 L 35 0 Q 50 0 50 20 Q 50 40 35 40 L 0 40', 'Q': 'M 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 40 Q 50 0 25 0 M 35 60 L 55 85', 'R': 'M 0 80 L 0 0 L 35 0 Q 50 0 50 20 Q 50 40 35 40 L 0 40 M 30 40 L 50 80', 'S': 'M 50 15 Q 50 0 25 0 Q 0 0 0 20 Q 0 40 25 40 Q 50 40 50 60 Q 50 80 25 80 Q 0 80 0 65', 'T': 'M 0 0 L 50 0 M 25 0 L 25 80', 'U': 'M 0 0 L 0 60 Q 0 80 25 80 Q 50 80 50 60 L 50 0', 'V': 'M 0 0 L 25 80 L 50 0', 'W': 'M 0 0 L 12 80 L 25 30 L 38 80 L 50 0', 'X': 'M 0 0 L 50 80 M 50 0 L 0 80', 'Y': 'M 0 0 L 25 40 L 50 0 M 25 40 L 25 80', 'Z': 'M 0 0 L 50 0 L 0 80 L 50 80' }; // Number path definitions const numberPaths = { '1': 'M 15 15 L 25 0 L 25 80 M 10 80 L 40 80', '2': 'M 0 20 Q 0 0 25 0 Q 50 0 50 20 Q 50 40 0 80 L 50 80', '3': 'M 0 10 Q 0 0 25 0 Q 50 0 50 20 Q 50 40 25 40 Q 50 40 50 60 Q 50 80 25 80 Q 0 80 0 70', '4': 'M 40 80 L 40 0 L 0 55 L 50 55', '5': 'M 50 0 L 0 0 L 0 35 Q 0 35 25 35 Q 50 35 50 57 Q 50 80 25 80 Q 0 80 0 65', '6': 'M 45 10 Q 45 0 25 0 Q 0 0 0 40 L 0 55 Q 0 80 25 80 Q 50 80 50 57 Q 50 35 25 35 Q 0 35 0 55', '7': 'M 0 0 L 50 0 L 20 80', '8': 'M 25 40 Q 0 40 0 20 Q 0 0 25 0 Q 50 0 50 20 Q 50 40 25 40 Q 0 40 0 60 Q 0 80 25 80 Q 50 80 50 60 Q 50 40 25 40', '9': 'M 5 70 Q 5 80 25 80 Q 50 80 50 40 L 50 25 Q 50 0 25 0 Q 0 0 0 23 Q 0 45 25 45 Q 50 45 50 25' }; /** * Generate a dotted path for a single character */ function generateDottedChar(char, x, y, scale = 1, color = '#000000') { const path = letterPaths[char.toUpperCase()] || numberPaths[char]; if (!path) return ''; // Transform the path to the correct position and scale const transformedPath = path.replace(/(\d+)/g, (match) => { return match; // Keep original values, we'll use transform }); return ``; } /** * Generate a row of dotted characters centered on the page with dynamic scaling */ function generateCharacterRow(text, y, baseScale = 1, color = '#000000') { const baseCharWidth = 50; const baseSpacing = 20; const maxWidth = 460; // Leave 20px margin on each side // Calculate required width with base scale const requiredWidth = text.length * (baseCharWidth + baseSpacing) * baseScale - baseSpacing * baseScale; // Scale down if content is too wide let finalScale = baseScale; if (requiredWidth > maxWidth) { finalScale = maxWidth / (text.length * (baseCharWidth + baseSpacing) - baseSpacing); } const charWidth = baseCharWidth * finalScale; const spacing = baseSpacing * finalScale; const totalWidth = text.length * charWidth + (text.length - 1) * spacing; let startX = (500 - totalWidth) / 2; let svg = ''; for (let i = 0; i < text.length; i++) { const char = text[i]; svg += generateDottedChar(char, startX + i * (charWidth + spacing), y, finalScale, color); } return svg; } /** * Generate baseline guide */ function generateBaseline(y, width = 400) { const startX = (500 - width) / 2; return ``; } /** * Generate complete writing worksheet * @param {Object} params * @param {string} params.word - Word to trace (if tracing letters) * @param {number[]} params.numbers - Numbers to trace (if tracing numbers) * @param {boolean} params.isNumbers - Whether to trace numbers * @param {number} params.age - Child's age (affects complexity) */ export function generateWritingWorksheet({ word, numbers, isNumbers, age }) { const content = isNumbers ? numbers.join('') : word.toUpperCase(); const title = isNumbers ? 'Trace the Numbers' : `Trace: ${word.toUpperCase()}`; const svg = ` ${generateBaseline(130)} ${generateCharacterRow(content, 50, 1, '#000000')} ${generateBaseline(280)} ${generateCharacterRow(content, 200, 1, '#888888')} ${generateBaseline(430)} ${generateCharacterRow(content, 350, 1, '#CCCCCC')} ${generateBaseline(495)} `; return { title: title, subtitle: isNumbers ? 'Practice writing your numbers!' : 'Practice writing letters!', sections: [{ id: 'writing-main', type: 'writing', content_svg: svg }] }; } export default { generateWritingWorksheet };