UNPKG

@ry-krystal/kicad-converter

Version:

专业的KiCad符号文件与JSON互转工具

376 lines (375 loc) 12.9 kB
/** * KiCad文件生成器 - 核心版本 * 负责将JSON数据结构转换回KiCad S表达式格式 */ import { performance } from 'perf_hooks'; /** * KiCad生成器类 */ export class KiCadGenerator { indentSize; indentChar; currentIndent = 0; constructor(options = {}) { this.indentSize = options.indentSize || 1; // KiCad 使用单个制表符缩进 this.indentChar = options.useSpaces ? ' '.repeat(options.indentSize || 2) : '\t'; } /** * 将符号库数据生成为KiCad格式字符串 * @param symbolLib 符号库数据 * @param options 生成选项 * @returns 生成结果 */ generate(symbolLib, options = {}) { const startTime = performance.now(); try { // 验证输入数据 if (options.validateOutput) { const validation = this.validateInput(symbolLib); if (!validation.isValid) { return { success: false, errors: validation.errors, warnings: validation.warnings }; } } // 重置缩进 this.currentIndent = 0; // 生成KiCad文件内容 const lines = []; // 添加文件头注释 if (options.includeComments) { lines.push(';; KiCad符号库文件'); lines.push(`;; 生成时间: ${new Date().toISOString()}`); lines.push(';;'); } // 生成根节点 lines.push('(kicad_symbol_lib'); this.increaseIndent(); // 生成版本信息 lines.push(this.formatLine(`(version "${symbolLib.version}")`)); // 生成器信息 if (symbolLib.generator) { lines.push(this.formatLine(`(generator "${symbolLib.generator}")`)); } if (symbolLib.generatorVersion) { lines.push(this.formatLine(`(generator_version "${symbolLib.generatorVersion}")`)); } // 生成符号 symbolLib.symbols.forEach(symbol => { lines.push(''); if (options.includeComments) { lines.push(this.formatLine(`;; 符号: ${symbol.name}`)); } lines.push(this.generateSymbol(symbol, options)); }); this.decreaseIndent(); lines.push(')'); const result = lines.join('\n'); const endTime = performance.now(); return { success: true, data: result, errors: [], warnings: [], statistics: { processingTime: endTime - startTime, inputSize: this.calculateDataSize(symbolLib), outputSize: result.length, symbolCount: symbolLib.symbols.length, pinCount: symbolLib.symbols.reduce((sum, s) => sum + s.pins.length, 0), propertyCount: symbolLib.symbols.reduce((sum, s) => sum + s.properties.length, 0) } }; } catch (error) { return { success: false, data: undefined, errors: [`KiCad生成失败: ${error instanceof Error ? error.message : String(error)}`], warnings: [] }; } } /** * 生成符号 */ generateSymbol(symbol, _options) { const lines = []; lines.push(this.formatLine(`(symbol "${symbol.name}"`)); this.increaseIndent(); // 生成符号属性 if (symbol.excludeFromSim !== undefined) { lines.push(this.formatLine(`(exclude_from_sim ${symbol.excludeFromSim ? 'yes' : 'no'})`)); } if (symbol.excludeFromBom !== undefined) { lines.push(this.formatLine(`(exclude_from_bom ${symbol.excludeFromBom ? 'yes' : 'no'})`)); } if (symbol.excludeFromBoard !== undefined) { lines.push(this.formatLine(`(exclude_from_board ${symbol.excludeFromBoard ? 'yes' : 'no'})`)); } if (symbol.inBom !== undefined) { lines.push(this.formatLine(`(in_bom ${symbol.inBom ? 'yes' : 'no'})`)); } if (symbol.onBoard !== undefined) { lines.push(this.formatLine(`(on_board ${symbol.onBoard ? 'yes' : 'no'})`)); } // 生成属性 symbol.properties.forEach(property => { lines.push(this.generateProperty(property)); }); // 生成引脚 symbol.pins.forEach(pin => { lines.push(this.generatePin(pin)); }); // 生成图形元素 symbol.graphics.forEach(graphic => { lines.push(this.generateGraphic(graphic)); }); // 嵌入字体选项 if (symbol.embeddedFonts) { lines.push(this.formatLine('(embedded_fonts yes)')); } this.decreaseIndent(); lines.push(this.formatLine(')')); return lines.join('\n'); } /** * 生成属性 */ generateProperty(property) { const lines = []; // 属性开始行 lines.push(this.formatLine(`(property ${property.name} ${property.value}`)); this.increaseIndent(); // 位置信息 const pos = this.formatPosition(property.position); lines.push(this.formatLine(`(at ${pos})`)); // 效果信息 if (Object.keys(property.effects).length > 0) { lines.push(this.generateEffectsMultiline(property.effects)); } // 隐藏属性 if (property.hide) { lines.push(this.formatLine('(hide yes)')); } this.decreaseIndent(); lines.push(this.formatLine(')')); return lines.join('\n'); } /** * 生成引脚 */ generatePin(pin) { const parts = [ 'pin', pin.type, pin.shape ]; // 位置和长度 const pos = this.formatPosition(pin.position); parts.push(`(at ${pos})`); parts.push(`(length ${pin.length})`); // 引脚名称 if (pin.name.text) { const nameEffects = this.generateEffects(pin.name.effects); parts.push(`(name "${pin.name.text}" ${nameEffects})`); } // 引脚编号 if (pin.number.text) { const numberEffects = this.generateEffects(pin.number.effects); parts.push(`(number "${pin.number.text}" ${numberEffects})`); } // 隐藏选项 if (pin.hide) { parts.push('hide'); } return this.formatLine(`(${parts.join(' ')})`); } /** * 生成图形元素 */ generateGraphic(graphic) { // 这是简化实现,实际需要根据不同图形类型生成相应格式 const parts = [graphic.type]; // 根据图形类型添加特定参数 switch (graphic.type) { case 'rectangle': { const rect = graphic; if (rect.start && rect.end) { parts.push(`(start ${rect.start.x} ${rect.start.y})`); parts.push(`(end ${rect.end.x} ${rect.end.y})`); } break; } case 'circle': { const circle = graphic; if (circle.center && circle.radius) { parts.push(`(center ${circle.center.x} ${circle.center.y})`); parts.push(`(radius ${circle.radius})`); } break; } case 'polyline': { const poly = graphic; if (poly.points && Array.isArray(poly.points)) { const pointsStr = poly.points.map((p) => `(xy ${p.x} ${p.y})`).join(' '); parts.push(`(pts ${pointsStr})`); } break; } case 'text': { const text = graphic; if (text.text) { parts.push(`"${text.text}"`); } if (text.position) { parts.push(`(at ${this.formatPosition(text.position)})`); } break; } } // 添加描边信息 if (graphic.stroke) { parts.push(`(stroke (width ${graphic.stroke.width}) (type ${graphic.stroke.type || 'default'}))`); } // 添加填充信息 if (graphic.fill) { parts.push(`(fill (type ${graphic.fill.type}))`); } return this.formatLine(`(${parts.join(' ')})`); } /** * 生成文本效果(单行版本) */ generateEffects(effects) { const parts = ['effects']; if (effects.font) { const fontParts = ['font']; if (effects.font.size) { fontParts.push(`(size ${effects.font.size.x} ${effects.font.size.y})`); } if (effects.font.thickness) { fontParts.push(`(thickness ${effects.font.thickness})`); } if (effects.font.bold) { fontParts.push('bold'); } if (effects.font.italic) { fontParts.push('italic'); } parts.push(`(${fontParts.join(' ')})`); } if (effects.justify) { const justifyParts = ['justify']; if (effects.justify.horizontal) { justifyParts.push(effects.justify.horizontal); } if (effects.justify.vertical) { justifyParts.push(effects.justify.vertical); } parts.push(`(${justifyParts.join(' ')})`); } if (effects.hide) { parts.push('hide'); } return `(${parts.join(' ')})`; } /** * 生成文本效果(多行版本,按照原始 KiCad 格式) */ generateEffectsMultiline(effects) { const lines = []; lines.push(this.formatLine('(effects')); this.increaseIndent(); if (effects.font) { lines.push(this.formatLine('(font')); this.increaseIndent(); if (effects.font.size) { lines.push(this.formatLine(`(size ${effects.font.size.x} ${effects.font.size.y})`)); } if (effects.font.thickness) { lines.push(this.formatLine(`(thickness ${effects.font.thickness})`)); } if (effects.font.bold) { lines.push(this.formatLine('(bold yes)')); } if (effects.font.italic) { lines.push(this.formatLine('(italic yes)')); } this.decreaseIndent(); lines.push(this.formatLine(')')); } if (effects.justify) { const justifyParts = ['justify']; if (effects.justify.horizontal) { justifyParts.push(effects.justify.horizontal); } if (effects.justify.vertical) { justifyParts.push(effects.justify.vertical); } lines.push(this.formatLine(`(${justifyParts.join(' ')})`)); } if (effects.hide) { lines.push(this.formatLine('(hide yes)')); } this.decreaseIndent(); lines.push(this.formatLine(')')); return lines.join('\n'); } /** * 格式化位置信息 */ formatPosition(position) { const parts = [position.x.toString(), position.y.toString()]; if (position.rotation !== undefined && position.rotation !== 0) { parts.push(position.rotation.toString()); } return parts.join(' '); } /** * 格式化行输出 */ formatLine(content) { return this.getCurrentIndent() + content; } /** * 获取当前缩进字符串 */ getCurrentIndent() { return this.indentChar.repeat(this.currentIndent * this.indentSize); } /** * 增加缩进层级 */ increaseIndent() { this.currentIndent++; } /** * 减少缩进层级 */ decreaseIndent() { this.currentIndent = Math.max(0, this.currentIndent - 1); } /** * 验证输入数据 */ validateInput(symbolLib) { const errors = []; const warnings = []; if (!symbolLib.version) { errors.push('缺少版本信息'); } if (!Array.isArray(symbolLib.symbols)) { errors.push('符号数组无效'); } return { isValid: errors.length === 0, errors, warnings }; } /** * 计算数据大小 */ calculateDataSize(data) { return JSON.stringify(data).length; } }