UNPKG

ziwei-mcp

Version:

紫微斗数 MCP (Ziwei Doushu Model Context Protocol) - 基于 MCP 协议的紫微斗数命理分析工具

489 lines (414 loc) 15 kB
/** * 命盘可视化工具 * 生成紫微斗数命盘的SVG图表 */ class ChartVisualizer { /** * 生成命盘可视化图表 */ async generateVisualization(input) { const { chart, visualizationType, includeElements, colorScheme, outputFormat, } = input; try { let imageData; switch (visualizationType) { case "traditional_chart": imageData = this.generateTraditionalChart( chart, includeElements, colorScheme, ); break; case "modern_wheel": imageData = this.generateModernWheel( chart, includeElements, colorScheme, ); break; case "palace_grid": imageData = this.generatePalaceGrid( chart, includeElements, colorScheme, ); break; case "star_map": imageData = this.generateStarMap(chart, includeElements, colorScheme); break; default: imageData = this.generateTraditionalChart( chart, includeElements, colorScheme, ); } return { imageData, metadata: { format: outputFormat, dimensions: { width: 800, height: 800, }, elements: includeElements, }, }; } catch (error) { console.error("生成可视化图表失败:", error); throw new Error(`生成可视化图表失败: ${error.message}`); } } /** * 生成传统命盘图表 */ generateTraditionalChart(chart, includeElements, colorScheme) { const colors = this.getColorScheme(colorScheme); // 创建SVG画布 let svg = '<svg width="800" height="800" xmlns="http://www.w3.org/2000/svg">'; // 添加背景 svg += `<rect width="800" height="800" fill="${colors.background}" stroke="${colors.border}" stroke-width="2"/>`; // 添加标题 svg += `<text x="400" y="30" text-anchor="middle" font-size="24" font-weight="bold" fill="${colors.text}">紫微斗数命盘</text>`; // 添加基本信息 svg += this.addBasicInfo(chart, colors, 50, 60); // 绘制十二宫格 svg += this.drawPalaceGrid( chart, colors, includeElements, 100, 150, 600, 600, ); svg += "</svg>"; return svg; } /** * 生成现代轮盘图表 */ generateModernWheel(chart, includeElements, colorScheme) { const colors = this.getColorScheme(colorScheme); const centerX = 400; const centerY = 400; const radius = 300; let svg = '<svg width="800" height="800" xmlns="http://www.w3.org/2000/svg">'; // 添加背景 svg += `<rect width="800" height="800" fill="${colors.background}"/>`; // 添加标题 svg += `<text x="400" y="30" text-anchor="middle" font-size="24" font-weight="bold" fill="${colors.text}">紫微斗数命盘(轮盘式)</text>`; // 绘制圆形宫位 for (let i = 0; i < 12; i++) { const angle = ((i * 30 - 90) * Math.PI) / 180; // 从12点开始,顺时针 const palace = chart.palaces && chart.palaces[i]; if (palace) { // 绘制宫位扇形 svg += this.drawPalaceSector( centerX, centerY, radius, angle, palace, colors, includeElements, ); } } // 添加中心圆 svg += `<circle cx="${centerX}" cy="${centerY}" r="80" fill="${colors.center}" stroke="${colors.border}" stroke-width="2"/>`; svg += `<text x="${centerX}" y="${centerY - 10}" text-anchor="middle" font-size="16" font-weight="bold" fill="${colors.text}">命宫</text>`; svg += `<text x="${centerX}" y="${centerY + 10}" text-anchor="middle" font-size="14" fill="${colors.text}">紫微斗数</text>`; svg += "</svg>"; return svg; } /** * 生成宫位网格图表 */ generatePalaceGrid(chart, includeElements, colorScheme) { const colors = this.getColorScheme(colorScheme); let svg = '<svg width="800" height="800" xmlns="http://www.w3.org/2000/svg">'; // 添加背景 svg += `<rect width="800" height="800" fill="${colors.background}"/>`; // 添加标题 svg += `<text x="400" y="30" text-anchor="middle" font-size="24" font-weight="bold" fill="${colors.text}">紫微斗数宫位图</text>`; // 绘制4x3网格 const gridWidth = 600; const gridHeight = 600; const cellWidth = gridWidth / 4; const cellHeight = gridHeight / 3; const startX = 100; const startY = 100; // 宫位排列顺序(传统排法) const palaceOrder = [ [3, 2, 1, 0], // 第一行:辰巳午未 [4, -1, -1, 11], // 第二行:卯空空申 [5, 6, 7, 8], // 第三行:寅丑子亥 ]; for (let row = 0; row < 3; row++) { for (let col = 0; col < 4; col++) { const palaceIndex = palaceOrder[row][col]; const x = startX + col * cellWidth; const y = startY + row * cellHeight; if (palaceIndex >= 0 && chart.palaces[palaceIndex]) { const palace = chart.palaces[palaceIndex]; svg += this.drawPalaceCell( x, y, cellWidth, cellHeight, palace, colors, includeElements, ); } else { // 空白区域(中央) svg += `<rect x="${x}" y="${y}" width="${cellWidth}" height="${cellHeight}" fill="${colors.center}" stroke="${colors.border}" stroke-width="1"/>`; } } } svg += "</svg>"; return svg; } /** * 生成星曜分布图 */ generateStarMap(chart, includeElements, colorScheme) { const colors = this.getColorScheme(colorScheme); let svg = '<svg width="800" height="800" xmlns="http://www.w3.org/2000/svg">'; // 添加背景 svg += `<rect width="800" height="800" fill="${colors.background}"/>`; // 添加标题 svg += `<text x="400" y="30" text-anchor="middle" font-size="24" font-weight="bold" fill="${colors.text}">星曜分布图</text>`; // 绘制星曜分布 const centerX = 400; const centerY = 400; const radius = 250; // 绘制外圆 svg += `<circle cx="${centerX}" cy="${centerY}" r="${radius}" fill="none" stroke="${colors.border}" stroke-width="2"/>`; // 绘制宫位分割线 for (let i = 0; i < 12; i++) { const angle = (i * 30 * Math.PI) / 180; const x1 = centerX + Math.cos(angle) * (radius - 50); const y1 = centerY + Math.sin(angle) * (radius - 50); const x2 = centerX + Math.cos(angle) * radius; const y2 = centerY + Math.sin(angle) * radius; svg += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${colors.border}" stroke-width="1"/>`; } // 绘制星曜 chart.palaces.forEach((palace, index) => { const angle = ((index * 30 + 15) * Math.PI) / 180; // 宫位中心角度 const palaceX = centerX + Math.cos(angle) * (radius - 25); const palaceY = centerY + Math.sin(angle) * (radius - 25); // 宫位名称 svg += `<text x="${palaceX}" y="${palaceY - 20}" text-anchor="middle" font-size="12" font-weight="bold" fill="${colors.text}">${palace.name}</text>`; // 星曜 const mainStars = palace.stars .filter((s) => s.type === "main") .slice(0, 3); mainStars.forEach((star, starIndex) => { const starY = palaceY + starIndex * 15; svg += `<text x="${palaceX}" y="${starY}" text-anchor="middle" font-size="10" fill="${colors.star}">${star.name}</text>`; }); }); svg += "</svg>"; return svg; } /** * 添加基本信息 */ addBasicInfo(chart, colors, x, y) { let info = ""; info += `<text x="${x}" y="${y}" font-size="14" fill="${colors.text}">姓名: ${chart.info?.name || "未知"}</text>`; info += `<text x="${x}" y="${y + 20}" font-size="14" fill="${colors.text}">性别: ${chart.info?.gender === "male" ? "男" : "女"}</text>`; info += `<text x="${x}" y="${y + 40}" font-size="14" fill="${colors.text}">出生: ${chart.info?.birthDate} ${chart.info?.birthTime}</text>`; info += `<text x="${x}" y="${y + 60}" font-size="14" fill="${colors.text}">农历: ${chart.info?.lunarDate}</text>`; info += `<text x="${x + 300}" y="${y}" font-size="14" fill="${colors.text}">命宫: ${chart.palaces.find((p) => p.id === chart.info?.destinyPalace)?.name || ""}</text>`; info += `<text x="${x + 300}" y="${y + 20}" font-size="14" fill="${colors.text}">身宫: ${chart.palaces.find((p) => p.id === chart.info?.bodyPalace)?.name || ""}</text>`; info += `<text x="${x + 300}" y="${y + 40}" font-size="14" fill="${colors.text}">年龄: ${chart.info?.age}岁</text>`; return info; } /** * 绘制宫位网格 */ drawPalaceGrid(chart, colors, includeElements, x, y, width, height) { let grid = ""; const cellWidth = width / 4; const cellHeight = height / 3; // 宫位排列顺序 const palaceOrder = [ [3, 2, 1, 0], // 辰巳午未 [4, -1, -1, 11], // 卯空空申 [5, 6, 7, 8], // 寅丑子亥 ]; for (let row = 0; row < 3; row++) { for (let col = 0; col < 4; col++) { const palaceIndex = palaceOrder[row][col]; const cellX = x + col * cellWidth; const cellY = y + row * cellHeight; if (palaceIndex >= 0 && chart.palaces[palaceIndex]) { const palace = chart.palaces[palaceIndex]; grid += this.drawPalaceCell( cellX, cellY, cellWidth, cellHeight, palace, colors, includeElements, ); } else { // 中央空白区域 grid += `<rect x="${cellX}" y="${cellY}" width="${cellWidth}" height="${cellHeight}" fill="${colors.center}" stroke="${colors.border}" stroke-width="1"/>`; if (row === 1 && col === 1) { grid += `<text x="${cellX + cellWidth / 2}" y="${cellY + cellHeight / 2}" text-anchor="middle" font-size="16" font-weight="bold" fill="${colors.text}">紫微斗数</text>`; } } } } return grid; } /** * 绘制单个宫位格子 */ drawPalaceCell(x, y, width, height, palace, colors, includeElements) { let cell = ""; // 宫位背景 - 简化逻辑,不依赖chart引用 const bgColor = colors.palace; cell += `<rect x="${x}" y="${y}" width="${width}" height="${height}" fill="${bgColor}" stroke="${colors.border}" stroke-width="1"/>`; // 宫位名称 cell += `<text x="${x + 5}" y="${y + 15}" font-size="12" font-weight="bold" fill="${colors.text}">${palace.name}</text>`; // 地支 if (includeElements.includes("earthly_branches")) { cell += `<text x="${x + width - 5}" y="${y + 15}" text-anchor="end" font-size="10" fill="${colors.branch}">${palace.earthlyBranch || ""}</text>`; } // 主星 if (includeElements.includes("main_stars") && palace.stars) { const mainStars = palace.stars .filter((s) => s.type === "main") .slice(0, 4); mainStars.forEach((star, index) => { const starX = x + 5 + (index % 2) * (width / 2 - 10); const starY = y + 35 + Math.floor(index / 2) * 20; cell += `<text x="${starX}" y="${starY}" font-size="10" fill="${colors.star}">${star.name}</text>`; }); } // 辅星 if (includeElements.includes("auxiliary_stars") && palace.stars) { const auxStars = palace.stars .filter((s) => s.type === "auxiliary") .slice(0, 6); auxStars.forEach((star, index) => { const starX = x + 5 + (index % 3) * (width / 3 - 5); const starY = y + height - 40 + Math.floor(index / 3) * 15; cell += `<text x="${starX}" y="${starY}" font-size="8" fill="${colors.auxStar}">${star.name}</text>`; }); } return cell; } /** * 绘制宫位扇形(轮盘式) */ drawPalaceSector( centerX, centerY, radius, angle, palace, colors, includeElements, ) { let sector = ""; const startAngle = angle - Math.PI / 12; // -15度 const endAngle = angle + Math.PI / 12; // +15度 // 计算扇形路径 const x1 = centerX + Math.cos(startAngle) * radius; const y1 = centerY + Math.sin(startAngle) * radius; const x2 = centerX + Math.cos(endAngle) * radius; const y2 = centerY + Math.sin(endAngle) * radius; const largeArcFlag = 0; // 小于180度 const pathData = `M ${centerX} ${centerY} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2} Z`; sector += `<path d="${pathData}" fill="${colors.palace}" stroke="${colors.border}" stroke-width="1"/>`; // 宫位名称 const textX = centerX + Math.cos(angle) * (radius * 0.8); const textY = centerY + Math.sin(angle) * (radius * 0.8); sector += `<text x="${textX}" y="${textY}" text-anchor="middle" font-size="12" font-weight="bold" fill="${colors.text}">${palace.name}</text>`; // 主星 if (includeElements.includes("main_stars")) { const mainStars = palace.stars .filter((s) => s.type === "main") .slice(0, 2); mainStars.forEach((star, index) => { const starRadius = radius * (0.6 + index * 0.1); const starX = centerX + Math.cos(angle) * starRadius; const starY = centerY + Math.sin(angle) * starRadius; sector += `<text x="${starX}" y="${starY}" text-anchor="middle" font-size="10" fill="${colors.star}">${star.name}</text>`; }); } return sector; } /** * 获取配色方案 */ getColorScheme(scheme) { const schemes = { traditional: { background: "#f8f8f8", border: "#333333", text: "#000000", palace: "#ffffff", destinyPalace: "#ffe6e6", center: "#f0f0f0", star: "#cc0000", auxStar: "#666666", branch: "#0066cc", }, modern: { background: "#ffffff", border: "#cccccc", text: "#333333", palace: "#f9f9f9", destinyPalace: "#e6f3ff", center: "#f5f5f5", star: "#ff6600", auxStar: "#999999", branch: "#0099cc", }, colorful: { background: "#fafafa", border: "#666666", text: "#333333", palace: "#fff9e6", destinyPalace: "#ffe6cc", center: "#f0f8ff", star: "#ff3366", auxStar: "#6699cc", branch: "#33cc66", }, monochrome: { background: "#ffffff", border: "#000000", text: "#000000", palace: "#f8f8f8", destinyPalace: "#e8e8e8", center: "#f0f0f0", star: "#000000", auxStar: "#666666", branch: "#333333", }, }; return schemes[scheme] || schemes.traditional; } } module.exports = { ChartVisualizer, };