@ry-krystal/kicad-converter
Version:
专业的KiCad符号文件与JSON互转工具
376 lines (375 loc) • 12.9 kB
JavaScript
/**
* 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;
}
}