UNPKG

kipm

Version:

KiCad component package manager

362 lines (317 loc) 12.9 kB
const { EasyedaPinType, EeSymbol, EeSymbolArc, EeSymbolBbox, EeSymbolCircle, EeSymbolEllipse, EeSymbolPath, EeSymbolPin, EeSymbolPolygon, EeSymbolPolyline, EeSymbolRectangle } = require("../easyeda/parametersEasyeda"); const { SvgPathEllipticalArc, SvgPathMoveTo } = require("../easyeda/svgPathParser"); const { getMiddleArcPos } = require("../arcUtils"); const { computeArc } = require("./exportKicadFootprint"); const { KiPinType, KiPinStyle, KiSymbolPin, KiSymbolRectangle, KiSymbolCircle, KiSymbolArc, KiSymbolPolygon, KiSymbolInfo, KiSymbol, KicadVersion } = require("./parametersKicadSymbol"); const eePinTypeToKiPinType = { [EasyedaPinType.unspecified]: KiPinType.unspecified, [EasyedaPinType._input]: KiPinType._input, [EasyedaPinType.output]: KiPinType.output, [EasyedaPinType.bidirectional]: KiPinType.bidirectional, [EasyedaPinType.power]: KiPinType.power_in, }; function pxToMil(dim) { // Multiply by 10 and truncate to an integer (like Python's int()) return Math.floor(10 * dim); } function pxToMm(dim) { return 10.0 * dim * 0.0254; } function convertEePins(eePins, eeBbox, kicadVersion) { const toKi = kicadVersion === KicadVersion.v5 ? pxToMil : pxToMm; const kicadPins = []; eePins.forEach(eePin => { // Parse the pin length from the pin_path string. const pathParts = eePin.pin_path.path.split("h"); const pinLength = Math.abs(parseInt(parseFloat(pathParts[pathParts.length - 1]))); const kiPin = new KiSymbolPin({ name: eePin.name.text.replace(" ", ""), number: eePin.settings.spice_pin_number.replace(" ", ""), style: KiPinStyle.line, length: toKi(pinLength), type: eePinTypeToKiPinType[eePin.settings.type], orientation: eePin.settings.rotation, pos_x: toKi(parseInt(eePin.settings.pos_x) - parseInt(eeBbox.x)), pos_y: -toKi(parseInt(eePin.settings.pos_y) - parseInt(eeBbox.y)), }); if (+eePin.dot.is_displayed && +eePin.clock.is_displayed) { kiPin.style = KiPinStyle.inverted_clock; } else if (+eePin.dot.is_displayed) { kiPin.style = KiPinStyle.inverted; } else if (+eePin.clock.is_displayed) { kiPin.style = KiPinStyle.clock; } // (The Python code contained commented-out adjustments based on rotation.) kicadPins.push(kiPin); }); return kicadPins; } function convertEeRectangles(eeRectangles, eeBbox, kicadVersion) { const toKi = kicadVersion === KicadVersion.v5 ? pxToMil : pxToMm; const kicadRectangles = []; eeRectangles.forEach(eeRect => { const kiRect = new KiSymbolRectangle({ pos_x0: toKi(parseInt(eeRect.pos_x) - parseInt(eeBbox.x)), pos_y0: -toKi(parseInt(eeRect.pos_y) - parseInt(eeBbox.y)), }); kiRect.pos_x1 = toKi(parseInt(eeRect.width)) + kiRect.pos_x0; kiRect.pos_y1 = -toKi(parseInt(eeRect.height)) + kiRect.pos_y0; kicadRectangles.push(kiRect); }); return kicadRectangles; } function convertEeCircles(eeCircles, eeBbox, kicadVersion) { const toKi = kicadVersion === KicadVersion.v5 ? pxToMil : pxToMm; return eeCircles.map(eeCircle => new KiSymbolCircle({ pos_x: toKi(parseInt(eeCircle.center_x) - parseInt(eeBbox.x)), pos_y: -toKi(parseInt(eeCircle.center_y) - parseInt(eeBbox.y)), radius: toKi(eeCircle.radius), background_filling: eeCircle.fill_color, })); } function convertEeEllipses(eeEllipses, eeBbox, kicadVersion) { const toKi = kicadVersion === KicadVersion.v5 ? pxToMil : pxToMm; // Ellipses are not supported in KiCad; only process circles. return eeEllipses .filter(ellipse => ellipse.radius_x === ellipse.radius_y) .map(ellipse => new KiSymbolCircle({ pos_x: toKi(parseInt(ellipse.center_x) - parseInt(eeBbox.x)), pos_y: -toKi(parseInt(ellipse.center_y) - parseInt(eeBbox.y)), radius: toKi(ellipse.radius_x), })); } function convertEeArcs(eeArcs, eeBbox, kicadVersion) { const toKi = kicadVersion === KicadVersion.v5 ? pxToMil : pxToMm; const kicadArcs = []; eeArcs.forEach(eeArc => { if ( !(eeArc.path[0] instanceof SvgPathMoveTo || eeArc.path[1] instanceof SvgPathEllipticalArc) ) { console.error("Can't convert this arc"); } else { const kiArc = new KiSymbolArc({ radius: toKi(Math.max(eeArc.path[1].radius_x, eeArc.path[1].radius_y)), angle_start: eeArc.path[1].x_axis_rotation, start_x: toKi(eeArc.path[0].start_x - eeBbox.x), start_y: toKi(eeArc.path[0].start_y - eeBbox.y), end_x: toKi(eeArc.path[1].end_x - eeBbox.x), end_y: toKi(eeArc.path[1].end_y - eeBbox.y), }); const { center_x, center_y, angle_end } = computeArc({ start_x: kiArc.start_x, start_y: kiArc.start_y, radius_x: toKi(eeArc.path[1].radius_x), radius_y: toKi(eeArc.path[1].radius_y), angle: kiArc.angle_start, large_arc_flag: eeArc.path[1].flag_large_arc, sweep_flag: eeArc.path[1].flag_sweep, end_x: kiArc.end_x, end_y: kiArc.end_y, }); kiArc.center_x = center_x; kiArc.center_y = eeArc.path[1].flag_large_arc ? center_y : -center_y; kiArc.angle_end = eeArc.path[1].flag_large_arc ? (360 - angle_end) : angle_end; const { x: middleX, y: middleY } = getMiddleArcPos({ center_x: kiArc.center_x, center_y: kiArc.center_y, radius: kiArc.radius, angle_start: kiArc.angle_start, angle_end: kiArc.angle_end, }); kiArc.middle_x = middleX; kiArc.middle_y = middleY; kiArc.start_y = eeArc.path[1].flag_large_arc ? kiArc.start_y : -kiArc.start_y; kiArc.end_y = eeArc.path[1].flag_large_arc ? kiArc.end_y : -kiArc.end_y; kicadArcs.push(kiArc); } }); return kicadArcs; } function convertEePolylines(eePolylines, eeBbox, kicadVersion) { const toKi = kicadVersion === KicadVersion.v5 ? pxToMil : pxToMm; const kicadPolygons = []; eePolylines.forEach(eePolyline => { const rawPts = eePolyline.points.split(" "); const xPoints = []; const yPoints = []; // Process points (assuming even number of items) for (let i = 0; i < rawPts.length; i += 2) { xPoints.push(toKi(parseInt(parseFloat(rawPts[i])) - parseInt(eeBbox.x))); yPoints.push(-toKi(parseInt(parseFloat(rawPts[i + 1])) - parseInt(eeBbox.y))); } // If the polyline is a polygon or has a fill color, close the shape. if (eePolyline instanceof EeSymbolPolygon || eePolyline.fill_color) { xPoints.push(xPoints[0]); yPoints.push(yPoints[0]); } if (xPoints.length > 0 && yPoints.length > 0) { const points = []; const numPoints = Math.min(xPoints.length, yPoints.length); for (let i = 0; i < numPoints; i++) { points.push([xPoints[i], yPoints[i]]); } const isClosed = xPoints[0] === xPoints[xPoints.length - 1] && yPoints[0] === yPoints[yPoints.length - 1]; const kicadPolygon = new KiSymbolPolygon({ points, points_number: numPoints, is_closed: isClosed, }); kicadPolygons.push(kicadPolygon); } else { console.warn("Skipping polygon with no parseable points"); } }); return kicadPolygons; } function convertEePolygons(eePolygons, eeBbox, kicadVersion) { // Reuse polyline conversion for polygons. return convertEePolylines(eePolygons, eeBbox, kicadVersion); } function convertEePaths(eePaths, eeBbox, kicadVersion) { const toKi = kicadVersion === KicadVersion.v5 ? pxToMil : pxToMm; const kicadPolygons = []; const kicadBeziers = []; eePaths.forEach(eePath => { const rawPts = eePath.paths.split(" "); const xPoints = []; const yPoints = []; let i = 0; while (i < rawPts.length) { const cmd = rawPts[i]; if (cmd === "M" || cmd === "L") { const x = toKi(parseInt(parseFloat(rawPts[i + 1])) - parseInt(eeBbox.x)); const y = -toKi(parseInt(parseFloat(rawPts[i + 2])) - parseInt(eeBbox.y)); xPoints.push(x); yPoints.push(y); i += 3; } else if (cmd === "Z") { if (xPoints.length > 0 && yPoints.length > 0) { xPoints.push(xPoints[0]); yPoints.push(yPoints[0]); } i++; } else if (cmd === "C") { // TODO: Add bezier support. i += 7; } else { i++; } } if (xPoints.length > 0 && yPoints.length > 0) { const points = []; const numPoints = Math.min(xPoints.length, yPoints.length); for (let j = 0; j < numPoints; j++) { points.push([xPoints[j], yPoints[j]]); } const isClosed = xPoints[0] === xPoints[xPoints.length - 1] && yPoints[0] === yPoints[yPoints.length - 1]; const kiPolygon = new KiSymbolPolygon({ points, points_number: numPoints, is_closed: isClosed, }); kicadPolygons.push(kiPolygon); } else { console.warn("Skipping path with no parseable points"); } }); return [kicadPolygons, kicadBeziers]; } function convertToKicad(eeSymbol, kicadVersion) { const kiInfo = new KiSymbolInfo({ name: eeSymbol.info.name, prefix: eeSymbol.info.prefix.replace("?", ""), package: eeSymbol.info.package, manufacturer: eeSymbol.info.manufacturer, datasheet: eeSymbol.info.datasheet, lcsc_id: eeSymbol.info.lcsc_id, jlc_id: eeSymbol.info.jlc_id, }); const kicadSymbol = new KiSymbol({ info: kiInfo, pins: convertEePins(eeSymbol.pins, eeSymbol.bbox, kicadVersion), rectangles: convertEeRectangles(eeSymbol.rectangles, eeSymbol.bbox, kicadVersion), circles: convertEeCircles(eeSymbol.circles, eeSymbol.bbox, kicadVersion), arcs: convertEeArcs(eeSymbol.arcs, eeSymbol.bbox, kicadVersion), }); // Append ellipses (converted to circles) kicadSymbol.circles = kicadSymbol.circles.concat( convertEeEllipses(eeSymbol.ellipses, eeSymbol.bbox, kicadVersion) ); // Process paths (polygons and beziers) const [polygonsFromPaths, beziers] = convertEePaths(eeSymbol.paths, eeSymbol.bbox, kicadVersion); kicadSymbol.polygons = polygonsFromPaths; kicadSymbol.beziers = beziers; // Process additional polylines and polygons kicadSymbol.polygons = kicadSymbol.polygons.concat( convertEePolylines(eeSymbol.polylines, eeSymbol.bbox, kicadVersion), convertEePolygons(eeSymbol.polygons, eeSymbol.bbox, kicadVersion) ); return kicadSymbol; } function tuneFootprintRefPath(kiSymbol, footprintLibName) { // Adjust the package reference by prepending the library name. kiSymbol.info.package = `${footprintLibName}:${kiSymbol.info.package}`; } class ExporterSymbolKicad { constructor(symbol, kicadVersion) { this.input = symbol; this.version = kicadVersion; if (this.input instanceof EeSymbol) { this.output = convertToKicad(this.input, kicadVersion); } else { console.error("Unknown input symbol format"); } } export(footprintLibName) { tuneFootprintRefPath(this.output, footprintLibName); // Assuming the export method of KiSymbol accepts an object with the version. return this.output.export(this.version); } } // === Usage Example === // const exporter = new ExporterSymbolKicad(myEeSymbol, KicadVersion.v6); // const kicadSymbolString = exporter.export("MyFootprintLib"); // console.log(kicadSymbolString); module.exports = { pxToMil, pxToMm, convertEePins, convertEeRectangles, convertEeCircles, convertEeEllipses, convertEeArcs, convertEePolylines, convertEePolygons, convertEePaths, convertToKicad, tuneFootprintRefPath, ExporterSymbolKicad, };