From ad7bc25dfb82b7e233e87febb8447aa0fd73d2ae Mon Sep 17 00:00:00 2001 From: Wannes Sels Date: Thu, 16 Jun 2022 23:01:55 +0200 Subject: [PATCH 1/6] LayerToPlot enum for plot_plan --- kikit/export.py | 144 ++++++++++++++++++++++++------------------- kikit/fab/oshpark.py | 4 +- kikit/stencil.py | 18 +++--- 3 files changed, 91 insertions(+), 75 deletions(-) diff --git a/kikit/export.py b/kikit/export.py index 32bb5513..80f66d0d 100644 --- a/kikit/export.py +++ b/kikit/export.py @@ -1,21 +1,41 @@ # Based on https://github.com/KiCad/kicad-source-mirror/blob/master/demos/python_scripts_examples/gen_gerber_and_drill_files_board.py import sys import os +from enum import Enum + from pcbnewTransition import pcbnew from pcbnew import * + +class LayerToPlot(Enum): + CuTop = (pcbnew.F_Cu, "Top layer") + CuBottom = (pcbnew.B_Cu, "Bottom layer") + PasteTop = (pcbnew.F_Paste, "Paste top") + PasteBottom = (pcbnew.B_Paste, "Paste bottom") + SilkTop = (pcbnew.F_SilkS, "Silk top") + SilkBottom = (pcbnew.B_SilkS, "Silk bottom") + MaskTop = (pcbnew.F_Mask, "Mask top") + MaskBottom = (pcbnew.B_Mask, "Mask bottom") + EdgeCuts = (pcbnew.Edge_Cuts, "Edges") + CmtUser = (pcbnew.Cmts_User, "V-CUT") + AdhesiveTop = (pcbnew.F_Adhes, "Adhesive top") + AdhesiveBottom = (pcbnew.B_Adhes, "Adhesive bottom") + + def __init__(self, id: int, description: str): + self.id = id + self.description = description + fullGerberPlotPlan = [ - # name, id, comment - ("CuTop", F_Cu, "Top layer"), - ("CuBottom", B_Cu, "Bottom layer"), - ("PasteBottom", B_Paste, "Paste Bottom"), - ("PasteTop", F_Paste, "Paste top"), - ("SilkTop", F_SilkS, "Silk top"), - ("SilkBottom", B_SilkS, "Silk top"), - ("MaskBottom", B_Mask, "Mask bottom"), - ("MaskTop", F_Mask, "Mask top"), - ("EdgeCuts", Edge_Cuts, "Edges"), - ("CmtUser", Cmts_User, "V-CUT") + LayerToPlot.CuTop, + LayerToPlot.CuBottom, + LayerToPlot.PasteBottom, + LayerToPlot.PasteTop, + LayerToPlot.SilkTop, + LayerToPlot.SilkBottom, + LayerToPlot.MaskBottom, + LayerToPlot.MaskTop, + LayerToPlot.EdgeCuts, + LayerToPlot.CmtUser ] exportSettingsJlcpcb = { @@ -50,13 +70,14 @@ } -def hasCopper(plotPlan): - for _, layer, _ in plotPlan: - if layer in [F_Cu, B_Cu]: +def hasCopper(plotPlan: list[LayerToPlot]): + for layer_to_plot in plotPlan: + if layer_to_plot.id in [F_Cu, B_Cu]: return True return False -def gerberImpl(boardfile, outputdir, plot_plan=fullGerberPlotPlan, drilling=True, settings=exportSettingsJlcpcb): + +def gerberImpl(boardfile, outputdir, plot_plan: list[LayerToPlot]=fullGerberPlotPlan, drilling=True, settings=exportSettingsJlcpcb): """ Export board to gerbers. @@ -71,62 +92,62 @@ def gerberImpl(boardfile, outputdir, plot_plan=fullGerberPlotPlan, drilling=True board = LoadBoard(boardfile) - pctl = PLOT_CONTROLLER(board) - popt = pctl.GetPlotOptions() - - popt.SetOutputDirectory(plotDir) - - popt.SetPlotFrameRef(False) - popt.SetSketchPadLineWidth(FromMM(0.35)) - popt.SetAutoScale(False) - popt.SetScale(1) - popt.SetMirror(False) - popt.SetUseGerberAttributes(False) - popt.SetIncludeGerberNetlistInfo(True) - popt.SetCreateGerberJobFile(True) - popt.SetUseGerberProtelExtensions(settings["UseGerberProtelExtensions"]) - popt.SetExcludeEdgeLayer(settings["ExcludeEdgeLayer"]) - popt.SetScale(1) - popt.SetUseAuxOrigin(settings["UseAuxOrigin"]) - popt.SetUseGerberX2format(False) - popt.SetDrillMarksType(0) # NO_DRILL_SHAPE + plot_controller = PLOT_CONTROLLER(board) + plot_options = plot_controller.GetPlotOptions() + + plot_options.SetOutputDirectory(plotDir) + + plot_options.SetPlotFrameRef(False) + plot_options.SetSketchPadLineWidth(FromMM(0.35)) + plot_options.SetAutoScale(False) + plot_options.SetScale(1) + plot_options.SetMirror(False) + plot_options.SetUseGerberAttributes(False) + plot_options.SetIncludeGerberNetlistInfo(True) + plot_options.SetCreateGerberJobFile(True) + plot_options.SetUseGerberProtelExtensions(settings["UseGerberProtelExtensions"]) + plot_options.SetExcludeEdgeLayer(settings["ExcludeEdgeLayer"]) + plot_options.SetScale(1) + plot_options.SetUseAuxOrigin(settings["UseAuxOrigin"]) + plot_options.SetUseGerberX2format(False) + plot_options.SetDrillMarksType(0) # NO_DRILL_SHAPE # This by gerbers only - popt.SetSubtractMaskFromSilk(False) - popt.SetDrillMarksType(PCB_PLOT_PARAMS.NO_DRILL_SHAPE) - popt.SetSkipPlotNPTH_Pads(False) + plot_options.SetSubtractMaskFromSilk(False) + plot_options.SetDrillMarksType(PCB_PLOT_PARAMS.NO_DRILL_SHAPE) + plot_options.SetSkipPlotNPTH_Pads(False) # prepare the gerber job file jobfile_writer = GERBER_JOBFILE_WRITER(board) - for name, id, comment in plot_plan: - if id <= B_Cu: - popt.SetSkipPlotNPTH_Pads(True) + for layer_to_plot in plot_plan: + if layer_to_plot.id <= B_Cu: + plot_options.SetSkipPlotNPTH_Pads(True) else: - popt.SetSkipPlotNPTH_Pads(False) + plot_options.SetSkipPlotNPTH_Pads(False) - pctl.SetLayer(id) - suffix = "" if settings["NoSuffix"] else name - pctl.OpenPlotfile(suffix, PLOT_FORMAT_GERBER, comment) - jobfile_writer.AddGbrFile(id, os.path.basename(pctl.GetPlotFileName())) - if pctl.PlotLayer() == False: + plot_controller.SetLayer(layer_to_plot.id) + suffix = "" if settings["NoSuffix"] else layer_to_plot.name + plot_controller.OpenPlotfile(suffix, PLOT_FORMAT_GERBER, layer_to_plot.description) + jobfile_writer.AddGbrFile(layer_to_plot.id, os.path.basename(plot_controller.GetPlotFileName())) + if plot_controller.PlotLayer() == False: print("plot error") if hasCopper(plot_plan): #generate internal copper layers, if any lyrcnt = board.GetCopperLayerCount() for innerlyr in range (1, lyrcnt - 1): - popt.SetSkipPlotNPTH_Pads(True) - pctl.SetLayer(innerlyr) + plot_options.SetSkipPlotNPTH_Pads(True) + plot_controller.SetLayer(innerlyr) lyrname = "" if settings["NoSuffix"] else 'inner{}'.format(innerlyr) - pctl.OpenPlotfile(lyrname, PLOT_FORMAT_GERBER, "inner") - jobfile_writer.AddGbrFile(innerlyr, os.path.basename(pctl.GetPlotFileName())) - if pctl.PlotLayer() == False: + plot_controller.OpenPlotfile(lyrname, PLOT_FORMAT_GERBER, "inner") + jobfile_writer.AddGbrFile(innerlyr, os.path.basename(plot_controller.GetPlotFileName())) + if plot_controller.PlotLayer() == False: print("plot error") # At the end you have to close the last plot, otherwise you don't know when # the object will be recycled! - pctl.ClosePlot() + plot_controller.ClosePlot() if drilling: # Fabricators need drill files. @@ -152,13 +173,13 @@ def gerberImpl(boardfile, outputdir, plot_plan=fullGerberPlotPlan, drilling=True drlwriter.SetFormat(metricFmt, zerosFmt) genDrl = True genMap = True - drlwriter.CreateDrillandMapFilesSet(pctl.GetPlotDirName(), genDrl, genMap) + drlwriter.CreateDrillandMapFilesSet(plot_controller.GetPlotDirName(), genDrl, genMap) # One can create a text file to report drill statistics - rptfn = pctl.GetPlotDirName() + 'drill_report.rpt' + rptfn = plot_controller.GetPlotDirName() + 'drill_report.rpt' drlwriter.GenDrillReportFile(rptfn) - job_fn=os.path.dirname(pctl.GetPlotFileName()) + '/' + os.path.basename(boardfile) + job_fn=os.path.dirname(plot_controller.GetPlotFileName()) + '/' + os.path.basename(boardfile) job_fn=os.path.splitext(job_fn)[0] + '.gbrjob' jobfile_writer.CreateJobFile(job_fn) @@ -176,16 +197,15 @@ def pasteDxfExport(board, plotDir): popt.SetDXFPlotPolygonMode(False) plot_plan = [ - # name, id, comment - ("PasteBottom", B_Paste, "Paste Bottom"), - ("PasteTop", F_Paste, "Paste top"), - ("EdgeCuts", Edge_Cuts, "Edges"), + LayerToPlot.PasteBottom, + LayerToPlot.PasteTop, + LayerToPlot.EdgeCuts ] output = [] - for name, id, comment in plot_plan: - pctl.SetLayer(id) - pctl.OpenPlotfile(name, PLOT_FORMAT_DXF, comment) + for layer_to_plot in plot_plan: + pctl.SetLayer(layer_to_plot.id) + pctl.OpenPlotfile(layer_to_plot.name, PLOT_FORMAT_DXF, layer_to_plot.description) output.append(pctl.GetPlotFileName()) if pctl.PlotLayer() == False: print("plot error") diff --git a/kikit/fab/oshpark.py b/kikit/fab/oshpark.py index 118eebe7..74f672de 100644 --- a/kikit/fab/oshpark.py +++ b/kikit/fab/oshpark.py @@ -2,10 +2,10 @@ import os import shutil from pathlib import Path -from kikit.export import gerberImpl, exportSettingsOSHPark, fullGerberPlotPlan +from kikit.export import gerberImpl, exportSettingsOSHPark, fullGerberPlotPlan, LayerToPlot from kikit.fab.common import ensurePassingDrc -plotPlanNoVCuts = [(name, id, comment) for name, id, comment in fullGerberPlotPlan if name != "CmtUser"] +plotPlanNoVCuts = [layer_to_plot for layer_to_plot in fullGerberPlotPlan if layer_to_plot is not LayerToPlot.CmtUser] def exportOSHPark(board, outputdir, nametemplate, drc): """ diff --git a/kikit/stencil.py b/kikit/stencil.py index ca1de245..0ffedfbf 100644 --- a/kikit/stencil.py +++ b/kikit/stencil.py @@ -1,3 +1,5 @@ +import shutil + from pcbnewTransition import pcbnew from pcbnew import wxPoint import numpy as np @@ -7,7 +9,7 @@ from kikit.common import * from kikit.defs import * from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad -from kikit.export import gerberImpl, pasteDxfExport +from kikit.export import gerberImpl, pasteDxfExport, LayerToPlot from kikit.export import exportSettingsJlcpcb import solid import solid.utils @@ -370,22 +372,16 @@ def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness, stencilFile = os.path.join(outputdir, "stencil.kicad_pcb") board.Save(stencilFile) - setStencilLayerVisibility(stencilFile) + setStencilLayerVisibility(inputboard) + plot_plan = [LayerToPlot.PasteTop, LayerToPlot.PasteBottom] - plotPlan = [ - # name, id, comment - ("PasteBottom", pcbnew.B_Paste, "Paste Bottom"), - ("PasteTop", pcbnew.F_Paste, "Paste top"), - ] # get a copy of exportSettingsJlcpcb dictionary and # exclude the Edge.Cuts layer for creation of stencil gerber files exportSettings = exportSettingsJlcpcb.copy() exportSettings["ExcludeEdgeLayer"] = True gerberDir = os.path.join(outputdir, "gerber") - gerberImpl(stencilFile, gerberDir, plotPlan, False, exportSettings) - gerbers = [os.path.join(gerberDir, x) for x in os.listdir(gerberDir)] - subprocess.check_call(["zip", "-j", - os.path.join(outputdir, "gerbers.zip")] + gerbers) + gerberImpl(stencilFile, gerberDir, plot_plan, False, exportSettings) + shutil.make_archive(os.path.join(outputdir, "gerbers"), "zip", gerberDir) jigthickness = fromMm(jigthickness) pcbthickness = fromMm(pcbthickness) From a778e72ff7b6ea45eb3caf73717e4d6cc4ee78ba Mon Sep 17 00:00:00 2001 From: Wannes Sels Date: Thu, 16 Jun 2022 23:25:33 +0200 Subject: [PATCH 2/6] StencilType: SolderPaste or Adhesive --- kikit/stencil.py | 125 ++++++++++++++++++++++++-------------------- kikit/stencil_ui.py | 45 ++++++++++------ 2 files changed, 97 insertions(+), 73 deletions(-) diff --git a/kikit/stencil.py b/kikit/stencil.py index 0ffedfbf..87bf21cb 100644 --- a/kikit/stencil.py +++ b/kikit/stencil.py @@ -25,12 +25,22 @@ MOUNTING_HOLE_R = fromMm(1) HOLE_SPACING = fromMm(20) -def addBottomCounterpart(board, item): + +class StencilType(Enum): + SolderPaste = (LayerToPlot.PasteTop, LayerToPlot.PasteBottom) + Adhesive = (LayerToPlot.AdhesiveTop, LayerToPlot.AdhesiveBottom) + + def __init__(self, top_layer: LayerToPlot, bottom_layer: LayerToPlot): + self.top_layer = top_layer + self.bottom_layer = bottom_layer + + +def addBottomCounterpart(board, item, stencil_type: StencilType): item = item.Duplicate() - item.SetLayer(Layer.B_Paste) + item.SetLayer(stencil_type.bottom_layer.id) board.Add(item) -def addRoundedCorner(board, center, start, end, thickness): +def addRoundedCorner(board, center, start, end, thickness, stencil_type: StencilType): corner = pcbnew.PCB_SHAPE() corner.SetShape(STROKE_T.S_ARC) corner.SetCenter(wxPoint(center[0], center[1])) @@ -50,21 +60,21 @@ def addRoundedCorner(board, center, start, end, thickness): else: corner.SetAngle(fromDegrees(-90)) corner.SetWidth(thickness) - corner.SetLayer(Layer.F_Paste) + corner.SetLayer(stencil_type.top_layer.id) board.Add(corner) - addBottomCounterpart(board, corner) + addBottomCounterpart(board, corner, stencil_type) -def addLine(board, start, end, thickness): +def addLine(board, start, end, thickness, stencil_type: StencilType): line = pcbnew.PCB_SHAPE() line.SetShape(STROKE_T.S_SEGMENT) line.SetStart(wxPoint(start[0], start[1])) line.SetEnd(wxPoint(end[0], end[1])) line.SetWidth(thickness) - line.SetLayer(Layer.F_Paste) + line.SetLayer(stencil_type.top_layer.id) board.Add(line) - addBottomCounterpart(board, line) + addBottomCounterpart(board, line, stencil_type) -def addBite(board, origin, direction, normal, thickness): +def addBite(board, origin, direction, normal, thickness, stencil_type: StencilType): """ Adds a bite to the stencil, direction points to the bridge, normal points inside the stencil @@ -75,7 +85,7 @@ def addBite(board, origin, direction, normal, thickness): start = origin end = center + wxPoint(direction[0], direction[1]) # addLine(board, end, end + normal / 2, thickness) - addRoundedCorner(board, center, start, end, thickness) + addRoundedCorner(board, center, start, end, thickness, stencil_type) def numberOfCuts(length, bridgeWidth, bridgeSpacing): """ @@ -85,7 +95,8 @@ def numberOfCuts(length, bridgeWidth, bridgeSpacing): cutLength = (length - (count - 1) * bridgeWidth) / count return count, cutLength -def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance): + +def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance, stencil_type: StencilType): """ Add rectangular frame to the board """ @@ -98,7 +109,7 @@ def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance): (bl(rect), wxPoint(0, -R), wxPoint(R, 0)) # BL ] for c, sOffset, eOffset in corners: - addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance) + addRoundedCorner(board, c + sOffset + eOffset, c + sOffset, c + eOffset, clearance, stencil_type) count, cutLength = numberOfCuts(rect.GetWidth() - 2 * R, bridgeWidth, bridgeSpacing) for i in range(count): @@ -106,16 +117,16 @@ def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance): end = start + cutLength y1, y2 = rect.GetY(), rect.GetY() + rect.GetHeight() - addLine(board, wxPoint(start, y1), wxPoint(end, y1), clearance) + addLine(board, wxPoint(start, y1), wxPoint(end, y1), clearance, stencil_type) if i != 0: - addBite(board, wxPoint(start, y1), wxPoint(-1, 0), wxPoint(0, 1), clearance) + addBite(board, wxPoint(start, y1), wxPoint(-1, 0), wxPoint(0, 1), clearance, stencil_type) if i != count - 1: - addBite(board, wxPoint(end, y1), wxPoint(1, 0), wxPoint(0, 1), clearance) - addLine(board, wxPoint(start, y2), wxPoint(end, y2), clearance) + addBite(board, wxPoint(end, y1), wxPoint(1, 0), wxPoint(0, 1), clearance, stencil_type) + addLine(board, wxPoint(start, y2), wxPoint(end, y2), clearance, stencil_type) if i != 0: - addBite(board, wxPoint(start, y2), wxPoint(-1, 0), wxPoint(0, -1), clearance) + addBite(board, wxPoint(start, y2), wxPoint(-1, 0), wxPoint(0, -1), clearance, stencil_type) if i != count - 1: - addBite(board, wxPoint(end, y2), wxPoint(1, 0), wxPoint(0, -1), clearance) + addBite(board, wxPoint(end, y2), wxPoint(1, 0), wxPoint(0, -1), clearance, stencil_type) count, cutLength = numberOfCuts(rect.GetHeight() - 2 * R, bridgeWidth, bridgeSpacing) for i in range(count): @@ -123,18 +134,19 @@ def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance): end = start + cutLength x1, x2 = rect.GetX(), rect.GetX() + rect.GetWidth() - addLine(board, wxPoint(x1, start), wxPoint(x1, end), clearance) + addLine(board, wxPoint(x1, start), wxPoint(x1, end), clearance, stencil_type) if i != 0: - addBite(board, wxPoint(x1, start), wxPoint(0, -1), wxPoint(1, 0), clearance) + addBite(board, wxPoint(x1, start), wxPoint(0, -1), wxPoint(1, 0), clearance, stencil_type) if i != count - 1: - addBite(board, wxPoint(x1, end), wxPoint(0, 1), wxPoint(1, 0), clearance) - addLine(board, wxPoint(x2, start), wxPoint(x2, end), clearance) + addBite(board, wxPoint(x1, end), wxPoint(0, 1), wxPoint(1, 0), clearance, stencil_type) + addLine(board, wxPoint(x2, start), wxPoint(x2, end), clearance, stencil_type) if i != 0: - addBite(board, wxPoint(x2, start), wxPoint(0, -1), wxPoint(-1, 0), clearance) + addBite(board, wxPoint(x2, start), wxPoint(0, -1), wxPoint(-1, 0), clearance, stencil_type) if i != count - 1: - addBite(board, wxPoint(x2, end), wxPoint(0, 1), wxPoint(-1, 0), clearance) + addBite(board, wxPoint(x2, end), wxPoint(0, 1), wxPoint(-1, 0), clearance, stencil_type) + -def addHole(board, position, radius): +def addHole(board, position, radius, stencil_type: StencilType): circle = pcbnew.PCB_SHAPE() circle.SetShape(STROKE_T.S_CIRCLE) circle.SetCenter(wxPoint(position[0], position[1])) @@ -144,12 +156,12 @@ def addHole(board, position, radius): else: circle.SetArcStart(wxPoint(position[0], position[1]) + wxPoint(radius/2, 0)) circle.SetWidth(radius) - circle.SetLayer(Layer.F_Paste) + circle.SetLayer(stencil_type.top_layer.id) board.Add(circle) - addBottomCounterpart(board, circle) + addBottomCounterpart(board, circle, stencil_type) def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2), - bridgeSpacing=fromMm(10), clearance=fromMm(0.5)): + bridgeSpacing=fromMm(10), clearance=fromMm(0.5), stencil_type: StencilType = StencilType.SolderPaste): """ Given a Pcbnew board finds the board outline and creates a stencil for KiKit's stencil jig. @@ -165,22 +177,22 @@ def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2), cutSize = rectByCenter(rectCenter(bBox), jigFrameSize[0] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1), jigFrameSize[1] + 2 * (OUTER_BORDER + INNER_BORDER) - fromMm(1)) - addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance) + addFrame(board, cutSize, bridgeWidth, bridgeSpacing, clearance, stencil_type) for i in range(MOUNTING_HOLES_COUNT): x = frameSize.GetX() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetWidth() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1) - addHole(board, wxPoint(x, OUTER_BORDER / 2 + frameSize.GetY()), MOUNTING_HOLE_R) - addHole(board, wxPoint(x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight()), MOUNTING_HOLE_R) + addHole(board, wxPoint(x, OUTER_BORDER / 2 + frameSize.GetY()), MOUNTING_HOLE_R, stencil_type) + addHole(board, wxPoint(x, - OUTER_BORDER / 2 +frameSize.GetY() + frameSize.GetHeight()), MOUNTING_HOLE_R, stencil_type) for i in range(MOUNTING_HOLES_COUNT): y = frameSize.GetY() + OUTER_BORDER / 2 + (i + 1) * (frameSize.GetHeight() - OUTER_BORDER) / (MOUNTING_HOLES_COUNT + 1) - addHole(board, wxPoint(OUTER_BORDER / 2 + frameSize.GetX(), y), MOUNTING_HOLE_R) - addHole(board, wxPoint(- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y), MOUNTING_HOLE_R) + addHole(board, wxPoint(OUTER_BORDER / 2 + frameSize.GetX(), y), MOUNTING_HOLE_R, stencil_type) + addHole(board, wxPoint(- OUTER_BORDER / 2 +frameSize.GetX() + frameSize.GetWidth(), y), MOUNTING_HOLE_R, stencil_type) PIN_TOLERANCE = fromMm(0.05) - addHole(board, tl(frameSize) + wxPoint(OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE) - addHole(board, tr(frameSize) + wxPoint(-OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE) - addHole(board, br(frameSize) + wxPoint(-OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE) - addHole(board, bl(frameSize) + wxPoint(OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE) + addHole(board, tl(frameSize) + wxPoint(OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE, stencil_type) + addHole(board, tr(frameSize) + wxPoint(-OUTER_BORDER / 2, OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE, stencil_type) + addHole(board, br(frameSize) + wxPoint(-OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE, stencil_type) + addHole(board, bl(frameSize) + wxPoint(OUTER_BORDER / 2, -OUTER_BORDER / 2), MOUNTING_HOLE_R + PIN_TOLERANCE, stencil_type) def jigMountingHoles(jigFrameSize, origin=wxPoint(0, 0)): """ Get list of all mounting holes in a jig of given size """ @@ -299,20 +311,20 @@ def shapelyToSHAPE_POLY_SET(polygon): p.AddOutline(linestringToKicad(polygon.exterior)) return p -def cutoutComponents(board, components): - topCutout = extractComponentPolygons(components, "F.CrtYd") +def cutoutComponents(board, components, stencil_type: StencilType): + topCutout = extractComponentPolygons(components, Layer.F_CrtYd) for polygon in topCutout: zone = pcbnew.PCB_SHAPE() zone.SetShape(STROKE_T.S_POLYGON) zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon)) - zone.SetLayer(Layer.F_Paste) + zone.SetLayer(stencil_type.top_layer.id) board.Add(zone) - bottomCutout = extractComponentPolygons(components, "B.CrtYd") + bottomCutout = extractComponentPolygons(components, Layer.B_CrtYd) for polygon in bottomCutout: zone = pcbnew.PCB_SHAPE() zone.SetShape(STROKE_T.S_POLYGON) zone.SetPolyShape(shapelyToSHAPE_POLY_SET(polygon)) - zone.SetLayer(Layer.B_Paste) + zone.SetLayer(stencil_type.bottom_layer.id) board.Add(zone) def setStencilLayerVisibility(boardName): @@ -358,22 +370,21 @@ def setStencilLayerVisibility(boardName): import os def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness, - registerborder, tolerance, ignore, cutout): + registerborder, tolerance, ignore, cutout, type: StencilType = StencilType.SolderPaste): board = pcbnew.LoadBoard(inputboard) - refs = parseReferences(ignore) - removeComponents(board, refs) + removeComponents(board, parseReferences(ignore)) + cutoutComponents(board, getComponents(board, parseReferences(cutout)), type) Path(outputdir).mkdir(parents=True, exist_ok=True) jigsize = (fromMm(jigsize[0]), fromMm(jigsize[1])) - addJigFrame(board, jigsize) - cutoutComponents(board, getComponents(board, parseReferences(cutout))) + addJigFrame(board, jigsize, stencil_type=type) stencilFile = os.path.join(outputdir, "stencil.kicad_pcb") board.Save(stencilFile) setStencilLayerVisibility(inputboard) - plot_plan = [LayerToPlot.PasteTop, LayerToPlot.PasteBottom] + plot_plan = [type.top_layer, type.bottom_layer] # get a copy of exportSettingsJlcpcb dictionary and # exclude the Edge.Cuts layer for creation of stencil gerber files @@ -387,10 +398,8 @@ def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness, pcbthickness = fromMm(pcbthickness) outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1]) tolerance = fromMm(tolerance) - topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness, - outerBorder, innerBorder, tolerance) - bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness, - outerBorder, innerBorder, tolerance) + topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness, outerBorder, innerBorder, tolerance) + bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness, outerBorder, innerBorder, tolerance) topRegisterFile = os.path.join(outputdir, "topRegister.scad") solid.scad_render_to_file(topRegister, topRegisterFile) @@ -416,13 +425,13 @@ def getComponents(board, references): """ return [f for f in board.GetFootprints() if f.GetReference() in references] -def collectFootprintEdges(footprint, layerName): +def collectFootprintEdges(footprint, layer: Layer): """ Return all edges on given layer in given footprint """ - return [e for e in footprint.GraphicalItems() if e.GetLayerName() == layerName] + return [e for e in footprint.GraphicalItems() if e.GetLayer() == layer] -def extractComponentPolygons(footprints, srcLayer): +def extractComponentPolygons(footprints, layer: Layer): """ Return a list of shapely polygons with holes for already placed components. The source layer defines the geometry on which the cutout is computed. @@ -430,7 +439,7 @@ def extractComponentPolygons(footprints, srcLayer): """ polygons = [] for f in footprints: - edges = collectFootprintEdges(f, srcLayer) + edges = collectFootprintEdges(f, layer) for ring in extractRings(edges): polygons.append(toShapely(ring, edges)) return polygons @@ -474,8 +483,8 @@ def createPrinted(inputboard, outputdir, pcbthickness, thickness, framewidth, topPaste = topPaste.replace("\\", "/") outline = outline.replace("\\", "/") - topCutout = extractComponentPolygons(cutoutComponents, "F.CrtYd") - bottomCutout = extractComponentPolygons(cutoutComponents, "B.CrtYd") + topCutout = extractComponentPolygons(cutoutComponents, Layer.F_CrtYd) + bottomCutout = extractComponentPolygons(cutoutComponents, Layer.B_CrtYd) topStencil = printedStencil(outline, topPaste, topCutout, thickness, height, framewidth, frameclearance, enlargeholes, True) bottomStencil = printedStencil(outline, bottomPaste, bottomCutout, thickness, diff --git a/kikit/stencil_ui.py b/kikit/stencil_ui.py index 023064fc..8b2d97e2 100644 --- a/kikit/stencil_ui.py +++ b/kikit/stencil_ui.py @@ -1,5 +1,26 @@ -import click +import enum import sys +import typing + +import click +from click import Choice +from kikit.stencil import StencilType + + +# from https://github.com/pallets/click/pull/2210/files#diff-dcb534e6a7591b92836537d4655ddbd2f18e3b293c3420144c30a9ca08f65c4e +class EnumChoice(Choice): + def __init__(self, enum_type: typing.Type[enum.Enum], case_sensitive: bool = True): + super().__init__( + choices=[element.name for element in enum_type], + case_sensitive=case_sensitive, + ) + self.enum_type = enum_type + + def convert(self, value: typing.Any, param: typing.Optional["Parameter"], ctx: typing.Optional["Context"]) -> typing.Any: + value = super().convert(value=value, param=param, ctx=ctx) + if value is None: + return None + return self.enum_type[value] @click.command() @@ -32,20 +53,14 @@ def createPrinted(**kwargs): @click.command() @click.argument("inputBoard", type=click.Path(dir_okay=False)) @click.argument("outputDir", type=click.Path(dir_okay=True)) -@click.option("--jigsize", type=(int, int), default=(100, 100), - help="Jig frame size in mm: ") -@click.option("--jigthickness", type=float, default=3, - help="Jig thickness in mm") -@click.option("--pcbthickness", type=float, default=1.6, - help="PCB thickness in mm") -@click.option("--registerborder", type=(float, float), default=(3, 1), - help="Register borders in mm: ") -@click.option("--tolerance", type=float, default=0.05, - help="Enlarges the register by the tolerance value") -@click.option("--ignore", type=str, default="", - help="Comma separated list of components references to exclude from the stencil") -@click.option("--cutout", type=str, default="", - help="Comma separated list of components references to cutout from the stencil based on the courtyard") +@click.option("--jigsize", type=(int, int), default=(100, 100), help="Jig frame size in mm: ") +@click.option("--jigthickness", type=float, default=3, help="Jig thickness in mm") +@click.option("--pcbthickness", type=float, default=1.6, help="PCB thickness in mm") +@click.option("--registerborder", type=(float, float), default=(3, 1), help="Register borders in mm: ") +@click.option("--tolerance", type=float, default=0.05, help="Enlarges the register by the tolerance value") +@click.option("--ignore", type=str, default="", help="Comma separated list of components references to exclude from the stencil") +@click.option("--cutout", type=str, default="", help="Comma separated list of components references to cutout from the stencil based on the courtyard") +@click.option("--type", type=EnumChoice(StencilType, case_sensitive=False), default="solderpaste", help="Stencil for SolderPaste or Adhesive") def create(**kwargs): """ Create stencil and register elements for manual paste dispensing jig. From 0ae49e9ee3fc20ab4a365b6e5f0757f525d1abc6 Mon Sep 17 00:00:00 2001 From: Wannes Sels Date: Sat, 24 Sep 2022 16:31:50 +0200 Subject: [PATCH 3/6] PR comments: revert formatting change for click.option help --- kikit/stencil_ui.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/kikit/stencil_ui.py b/kikit/stencil_ui.py index 8b2d97e2..e4c65e26 100644 --- a/kikit/stencil_ui.py +++ b/kikit/stencil_ui.py @@ -53,14 +53,22 @@ def createPrinted(**kwargs): @click.command() @click.argument("inputBoard", type=click.Path(dir_okay=False)) @click.argument("outputDir", type=click.Path(dir_okay=True)) -@click.option("--jigsize", type=(int, int), default=(100, 100), help="Jig frame size in mm: ") -@click.option("--jigthickness", type=float, default=3, help="Jig thickness in mm") -@click.option("--pcbthickness", type=float, default=1.6, help="PCB thickness in mm") -@click.option("--registerborder", type=(float, float), default=(3, 1), help="Register borders in mm: ") -@click.option("--tolerance", type=float, default=0.05, help="Enlarges the register by the tolerance value") -@click.option("--ignore", type=str, default="", help="Comma separated list of components references to exclude from the stencil") -@click.option("--cutout", type=str, default="", help="Comma separated list of components references to cutout from the stencil based on the courtyard") -@click.option("--type", type=EnumChoice(StencilType, case_sensitive=False), default="solderpaste", help="Stencil for SolderPaste or Adhesive") +@click.option("--jigsize", type=(int, int), default=(100, 100), + help="Jig frame size in mm: ") +@click.option("--jigthickness", type=float, default=3, + help="Jig thickness in mm") +@click.option("--pcbthickness", type=float, default=1.6, + help="PCB thickness in mm") +@click.option("--registerborder", type=(float, float), default=(3, 1), + help="Register borders in mm: ") +@click.option("--tolerance", type=float, default=0.05, + help="Enlarges the register by the tolerance value") +@click.option("--ignore", type=str, default="", + help="Comma separated list of components references to exclude from the stencil") +@click.option("--cutout", type=str, default="", + help="Comma separated list of components references to cutout from the stencil based on the courtyard") +@click.option("--type", type=EnumChoice(StencilType, case_sensitive=False), default="solderpaste", + help="Stencil for SolderPaste or Adhesive") def create(**kwargs): """ Create stencil and register elements for manual paste dispensing jig. From d89a272841ae57a0cde8f5650c5eb084f642bc1c Mon Sep 17 00:00:00 2001 From: Wannes Sels Date: Sat, 24 Sep 2022 16:55:09 +0200 Subject: [PATCH 4/6] PR comments: @dataclass instead of enum for LayerToPlot; revert renaming plot_controller and plot_options --- kikit/export.py | 137 ++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 69 deletions(-) diff --git a/kikit/export.py b/kikit/export.py index c6ce0464..1e0587db 100644 --- a/kikit/export.py +++ b/kikit/export.py @@ -1,41 +1,40 @@ # Based on https://github.com/KiCad/kicad-source-mirror/blob/master/demos/python_scripts_examples/gen_gerber_and_drill_files_board.py -import sys import os -from enum import Enum +from dataclasses import dataclass from pcbnewTransition import pcbnew from pcbnew import * -class LayerToPlot(Enum): - CuTop = (pcbnew.F_Cu, "Top layer") - CuBottom = (pcbnew.B_Cu, "Bottom layer") - PasteTop = (pcbnew.F_Paste, "Paste top") - PasteBottom = (pcbnew.B_Paste, "Paste bottom") - SilkTop = (pcbnew.F_SilkS, "Silk top") - SilkBottom = (pcbnew.B_SilkS, "Silk bottom") - MaskTop = (pcbnew.F_Mask, "Mask top") - MaskBottom = (pcbnew.B_Mask, "Mask bottom") - EdgeCuts = (pcbnew.Edge_Cuts, "Edges") - CmtUser = (pcbnew.Cmts_User, "V-CUT") - AdhesiveTop = (pcbnew.F_Adhes, "Adhesive top") - AdhesiveBottom = (pcbnew.B_Adhes, "Adhesive bottom") - - def __init__(self, id: int, description: str): - self.id = id - self.description = description +@dataclass +class LayerToPlot: + name: str + id: int + description: str + + +CuTop = LayerToPlot("CuTop", F_Cu, "Top layer") +CuBottom = LayerToPlot("CuBottom", B_Cu, "Bottom layer") +PasteBottom = LayerToPlot("PasteBottom", B_Paste, "Paste Bottom") +PasteTop = LayerToPlot("PasteTop", F_Paste, "Paste top") +SilkTop = LayerToPlot("SilkTop", F_SilkS, "Silk top") +SilkBottom = LayerToPlot("SilkBottom", B_SilkS, "Silk top") +MaskBottom = LayerToPlot("MaskBottom", B_Mask, "Mask bottom") +MaskTop = LayerToPlot("MaskTop", F_Mask, "Mask top") +EdgeCuts = LayerToPlot("EdgeCuts", Edge_Cuts, "Edges") +CmtUser = LayerToPlot("CmtUser", Cmts_User, "V-CUT") fullGerberPlotPlan = [ - LayerToPlot.CuTop, - LayerToPlot.CuBottom, - LayerToPlot.PasteBottom, - LayerToPlot.PasteTop, - LayerToPlot.SilkTop, - LayerToPlot.SilkBottom, - LayerToPlot.MaskBottom, - LayerToPlot.MaskTop, - LayerToPlot.EdgeCuts, - LayerToPlot.CmtUser + CuTop, + CuBottom, + PasteBottom, + PasteTop, + SilkTop, + SilkBottom, + MaskBottom, + MaskTop, + EdgeCuts, + CmtUser ] exportSettingsJlcpcb = { @@ -92,62 +91,62 @@ def gerberImpl(boardfile, outputdir, plot_plan: list[LayerToPlot]=fullGerberPlot board = LoadBoard(boardfile) - plot_controller = PLOT_CONTROLLER(board) - plot_options = plot_controller.GetPlotOptions() - - plot_options.SetOutputDirectory(plotDir) - - plot_options.SetPlotFrameRef(False) - plot_options.SetSketchPadLineWidth(FromMM(0.35)) - plot_options.SetAutoScale(False) - plot_options.SetScale(1) - plot_options.SetMirror(False) - plot_options.SetUseGerberAttributes(False) - plot_options.SetIncludeGerberNetlistInfo(True) - plot_options.SetCreateGerberJobFile(True) - plot_options.SetUseGerberProtelExtensions(settings["UseGerberProtelExtensions"]) - plot_options.SetExcludeEdgeLayer(settings["ExcludeEdgeLayer"]) - plot_options.SetScale(1) - plot_options.SetUseAuxOrigin(settings["UseAuxOrigin"]) - plot_options.SetUseGerberX2format(False) - plot_options.SetDrillMarksType(0) # NO_DRILL_SHAPE + pctl = PLOT_CONTROLLER(board) + popt = pctl.GetPlotOptions() + + popt.SetOutputDirectory(plotDir) + + popt.SetPlotFrameRef(False) + popt.SetSketchPadLineWidth(FromMM(0.35)) + popt.SetAutoScale(False) + popt.SetScale(1) + popt.SetMirror(False) + popt.SetUseGerberAttributes(False) + popt.SetIncludeGerberNetlistInfo(True) + popt.SetCreateGerberJobFile(True) + popt.SetUseGerberProtelExtensions(settings["UseGerberProtelExtensions"]) + popt.SetExcludeEdgeLayer(settings["ExcludeEdgeLayer"]) + popt.SetScale(1) + popt.SetUseAuxOrigin(settings["UseAuxOrigin"]) + popt.SetUseGerberX2format(False) + popt.SetDrillMarksType(0) # NO_DRILL_SHAPE # This by gerbers only - plot_options.SetSubtractMaskFromSilk(False) - plot_options.SetDrillMarksType(PCB_PLOT_PARAMS.NO_DRILL_SHAPE) - plot_options.SetSkipPlotNPTH_Pads(False) + popt.SetSubtractMaskFromSilk(False) + popt.SetDrillMarksType(PCB_PLOT_PARAMS.NO_DRILL_SHAPE) + popt.SetSkipPlotNPTH_Pads(False) # prepare the gerber job file jobfile_writer = GERBER_JOBFILE_WRITER(board) for layer_to_plot in plot_plan: if layer_to_plot.id <= B_Cu: - plot_options.SetSkipPlotNPTH_Pads(True) + popt.SetSkipPlotNPTH_Pads(True) else: - plot_options.SetSkipPlotNPTH_Pads(False) + popt.SetSkipPlotNPTH_Pads(False) - plot_controller.SetLayer(layer_to_plot.id) + pctl.SetLayer(layer_to_plot.id) suffix = "" if settings["NoSuffix"] else layer_to_plot.name - plot_controller.OpenPlotfile(suffix, PLOT_FORMAT_GERBER, layer_to_plot.description) - jobfile_writer.AddGbrFile(layer_to_plot.id, os.path.basename(plot_controller.GetPlotFileName())) - if plot_controller.PlotLayer() == False: + pctl.OpenPlotfile(suffix, PLOT_FORMAT_GERBER, layer_to_plot.description) + jobfile_writer.AddGbrFile(layer_to_plot.id, os.path.basename(popt.GetPlotFileName())) + if pctl.PlotLayer() == False: print("plot error") if hasCopper(plot_plan): #generate internal copper layers, if any lyrcnt = board.GetCopperLayerCount() for innerlyr in range (1, lyrcnt - 1): - plot_options.SetSkipPlotNPTH_Pads(True) - plot_controller.SetLayer(innerlyr) + popt.SetSkipPlotNPTH_Pads(True) + pctl.SetLayer(innerlyr) lyrname = "" if settings["NoSuffix"] else 'inner{}'.format(innerlyr) - plot_controller.OpenPlotfile(lyrname, PLOT_FORMAT_GERBER, "inner") - jobfile_writer.AddGbrFile(innerlyr, os.path.basename(plot_controller.GetPlotFileName())) - if plot_controller.PlotLayer() == False: + pctl.OpenPlotfile(lyrname, PLOT_FORMAT_GERBER, "inner") + jobfile_writer.AddGbrFile(innerlyr, os.path.basename(pctl.GetPlotFileName())) + if pctl.PlotLayer() == False: print("plot error") # At the end you have to close the last plot, otherwise you don't know when # the object will be recycled! - plot_controller.ClosePlot() + pctl.ClosePlot() if drilling: # Fabricators need drill files. @@ -173,13 +172,13 @@ def gerberImpl(boardfile, outputdir, plot_plan: list[LayerToPlot]=fullGerberPlot drlwriter.SetFormat(metricFmt, zerosFmt) genDrl = True genMap = True - drlwriter.CreateDrillandMapFilesSet(plot_controller.GetPlotDirName(), genDrl, genMap) + drlwriter.CreateDrillandMapFilesSet(pctl.GetPlotDirName(), genDrl, genMap) # One can create a text file to report drill statistics - rptfn = plot_controller.GetPlotDirName() + 'drill_report.rpt' + rptfn = pctl.GetPlotDirName() + 'drill_report.rpt' drlwriter.GenDrillReportFile(rptfn) - job_fn=os.path.dirname(plot_controller.GetPlotFileName()) + '/' + os.path.basename(boardfile) + job_fn=os.path.dirname(pctl.GetPlotFileName()) + '/' + os.path.basename(boardfile) job_fn=os.path.splitext(job_fn)[0] + '.gbrjob' jobfile_writer.CreateJobFile(job_fn) @@ -197,9 +196,9 @@ def pasteDxfExport(board, plotDir): popt.SetDXFPlotPolygonMode(False) plot_plan = [ - LayerToPlot.PasteBottom, - LayerToPlot.PasteTop, - LayerToPlot.EdgeCuts + PasteBottom, + PasteTop, + EdgeCuts ] output = [] From f8fc5c898dda1e856fdf781eaaf3237253e5d224 Mon Sep 17 00:00:00 2001 From: Wannes Sels Date: Sat, 24 Sep 2022 17:41:35 +0200 Subject: [PATCH 5/6] PR comments: type hints for all modified methods --- kikit/export.py | 24 +++++++++++++----------- kikit/fab/oshpark.py | 4 ++-- kikit/stencil.py | 40 +++++++++++++++++++++------------------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/kikit/export.py b/kikit/export.py index 1e0587db..0c16d585 100644 --- a/kikit/export.py +++ b/kikit/export.py @@ -13,16 +13,18 @@ class LayerToPlot: description: str -CuTop = LayerToPlot("CuTop", F_Cu, "Top layer") -CuBottom = LayerToPlot("CuBottom", B_Cu, "Bottom layer") -PasteBottom = LayerToPlot("PasteBottom", B_Paste, "Paste Bottom") -PasteTop = LayerToPlot("PasteTop", F_Paste, "Paste top") -SilkTop = LayerToPlot("SilkTop", F_SilkS, "Silk top") -SilkBottom = LayerToPlot("SilkBottom", B_SilkS, "Silk top") -MaskBottom = LayerToPlot("MaskBottom", B_Mask, "Mask bottom") -MaskTop = LayerToPlot("MaskTop", F_Mask, "Mask top") -EdgeCuts = LayerToPlot("EdgeCuts", Edge_Cuts, "Edges") -CmtUser = LayerToPlot("CmtUser", Cmts_User, "V-CUT") +CuTop = LayerToPlot("CuTop", pcbnew.F_Cu, "Top layer") +CuBottom = LayerToPlot("CuBottom", pcbnew.B_Cu, "Bottom layer") +PasteBottom = LayerToPlot("PasteBottom", pcbnew.B_Paste, "Paste Bottom") +PasteTop = LayerToPlot("PasteTop", pcbnew.F_Paste, "Paste top") +SilkTop = LayerToPlot("SilkTop", pcbnew.F_SilkS, "Silk top") +SilkBottom = LayerToPlot("SilkBottom", pcbnew.B_SilkS, "Silk top") +MaskBottom = LayerToPlot("MaskBottom", pcbnew.B_Mask, "Mask bottom") +MaskTop = LayerToPlot("MaskTop", pcbnew.F_Mask, "Mask top") +EdgeCuts = LayerToPlot("EdgeCuts", pcbnew.Edge_Cuts, "Edges") +CmtUser = LayerToPlot("CmtUser", pcbnew.Cmts_User, "V-CUT") +AdhesiveTop = LayerToPlot("AdhesiveTop", pcbnew.F_Adhes, "Adhesive top") +AdhesiveBottom = LayerToPlot("AdhesiveBottom", pcbnew.B_Adhes, "Adhesive bottom") fullGerberPlotPlan = [ CuTop, @@ -76,7 +78,7 @@ def hasCopper(plotPlan: list[LayerToPlot]): return False -def gerberImpl(boardfile, outputdir, plot_plan: list[LayerToPlot]=fullGerberPlotPlan, drilling=True, settings=exportSettingsJlcpcb): +def gerberImpl(boardfile: str, outputdir: str, plot_plan: list[LayerToPlot]=fullGerberPlotPlan, drilling=True, settings=exportSettingsJlcpcb): """ Export board to gerbers. diff --git a/kikit/fab/oshpark.py b/kikit/fab/oshpark.py index 74f672de..662f8143 100644 --- a/kikit/fab/oshpark.py +++ b/kikit/fab/oshpark.py @@ -2,10 +2,10 @@ import os import shutil from pathlib import Path -from kikit.export import gerberImpl, exportSettingsOSHPark, fullGerberPlotPlan, LayerToPlot +from kikit.export import gerberImpl, exportSettingsOSHPark, fullGerberPlotPlan, CmtUser from kikit.fab.common import ensurePassingDrc -plotPlanNoVCuts = [layer_to_plot for layer_to_plot in fullGerberPlotPlan if layer_to_plot is not LayerToPlot.CmtUser] +plotPlanNoVCuts = [layer_to_plot for layer_to_plot in fullGerberPlotPlan if layer_to_plot.id is not CmtUser.id] def exportOSHPark(board, outputdir, nametemplate, drc): """ diff --git a/kikit/stencil.py b/kikit/stencil.py index 0eb6c932..26d5c2a7 100644 --- a/kikit/stencil.py +++ b/kikit/stencil.py @@ -1,7 +1,7 @@ import shutil from pcbnewTransition import pcbnew -from pcbnew import wxPoint +from pcbnew import wxPoint, PCB_SHAPE, BOARD import numpy as np import json from collections import OrderedDict @@ -9,7 +9,7 @@ from kikit.common import * from kikit.defs import * from kikit.substrate import Substrate, extractRings, toShapely, linestringToKicad -from kikit.export import gerberImpl, pasteDxfExport, LayerToPlot +from kikit.export import gerberImpl, pasteDxfExport, LayerToPlot, PasteTop, PasteBottom, AdhesiveTop, AdhesiveBottom from kikit.export import exportSettingsJlcpcb import solid import solid.utils @@ -27,20 +27,20 @@ class StencilType(Enum): - SolderPaste = (LayerToPlot.PasteTop, LayerToPlot.PasteBottom) - Adhesive = (LayerToPlot.AdhesiveTop, LayerToPlot.AdhesiveBottom) + SolderPaste = (PasteTop, PasteBottom) + Adhesive = (AdhesiveTop, AdhesiveBottom) def __init__(self, top_layer: LayerToPlot, bottom_layer: LayerToPlot): self.top_layer = top_layer self.bottom_layer = bottom_layer -def addBottomCounterpart(board, item, stencil_type: StencilType): +def addBottomCounterpart(board: BOARD, item: PCB_SHAPE, stencil_type: StencilType = StencilType.SolderPaste): item = item.Duplicate() item.SetLayer(stencil_type.bottom_layer.id) board.Add(item) -def addRoundedCorner(board, center, start, end, thickness, stencil_type: StencilType): +def addRoundedCorner(board: BOARD, center: wxPoint, start: wxPoint, end: wxPoint, thickness, stencil_type: StencilType = StencilType.SolderPaste): corner = pcbnew.PCB_SHAPE() corner.SetShape(STROKE_T.S_ARC) corner.SetCenter(wxPoint(center[0], center[1])) @@ -64,7 +64,7 @@ def addRoundedCorner(board, center, start, end, thickness, stencil_type: Stencil board.Add(corner) addBottomCounterpart(board, corner, stencil_type) -def addLine(board, start, end, thickness, stencil_type: StencilType): +def addLine(board: BOARD, start: wxPoint, end: wxPoint, thickness: int, stencil_type: StencilType = StencilType.SolderPaste): line = pcbnew.PCB_SHAPE() line.SetShape(STROKE_T.S_SEGMENT) line.SetStart(wxPoint(start[0], start[1])) @@ -74,7 +74,7 @@ def addLine(board, start, end, thickness, stencil_type: StencilType): board.Add(line) addBottomCounterpart(board, line, stencil_type) -def addBite(board, origin, direction, normal, thickness, stencil_type: StencilType): +def addBite(board: BOARD, origin: wxPoint, direction: wxPoint, normal: wxPoint, thickness: int, stencil_type: StencilType = StencilType.SolderPaste): """ Adds a bite to the stencil, direction points to the bridge, normal points inside the stencil @@ -96,7 +96,7 @@ def numberOfCuts(length, bridgeWidth, bridgeSpacing): return count, cutLength -def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance, stencil_type: StencilType): +def addFrame(board: BOARD, rect: wxRect, bridgeWidth: int, bridgeSpacing: int, clearance: int, stencil_type: StencilType = StencilType.SolderPaste): """ Add rectangular frame to the board """ @@ -146,7 +146,7 @@ def addFrame(board, rect, bridgeWidth, bridgeSpacing, clearance, stencil_type: S addBite(board, wxPoint(x2, end), wxPoint(0, 1), wxPoint(-1, 0), clearance, stencil_type) -def addHole(board, position, radius, stencil_type: StencilType): +def addHole(board: BOARD, position: wxPoint, radius: int, stencil_type: StencilType = StencilType.SolderPaste): circle = pcbnew.PCB_SHAPE() circle.SetShape(STROKE_T.S_CIRCLE) circle.SetCenter(wxPoint(position[0], position[1])) @@ -160,8 +160,8 @@ def addHole(board, position, radius, stencil_type: StencilType): board.Add(circle) addBottomCounterpart(board, circle, stencil_type) -def addJigFrame(board, jigFrameSize, bridgeWidth=fromMm(2), - bridgeSpacing=fromMm(10), clearance=fromMm(0.5), stencil_type: StencilType = StencilType.SolderPaste): +def addJigFrame(board: BOARD, jigFrameSize: (int, int), bridgeWidth: int=fromMm(2), + bridgeSpacing: int=fromMm(10), clearance: int=fromMm(0.5), stencil_type: StencilType = StencilType.SolderPaste): """ Given a Pcbnew board finds the board outline and creates a stencil for KiKit's stencil jig. @@ -311,7 +311,7 @@ def shapelyToSHAPE_POLY_SET(polygon): p.AddOutline(linestringToKicad(polygon.exterior)) return p -def cutoutComponents(board, components, stencil_type: StencilType): +def cutoutComponents(board: BOARD, components: list[str], stencil_type: StencilType = StencilType.SolderPaste): topCutout = extractComponentPolygons(components, pcbnew.F_CrtYd) for polygon in topCutout: zone = pcbnew.PCB_SHAPE() @@ -370,8 +370,8 @@ def setStencilLayerVisibility(boardName): from pathlib import Path import os -def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness, - registerborder, tolerance, ignore, cutout, type: StencilType = StencilType.SolderPaste): +def create(inputboard: str, outputdir: str, jigsize: (int, int), jigthickness: float, pcbthickness: float, + registerborder: (float, float), tolerance: float, ignore: str, cutout: str, type: StencilType = StencilType.SolderPaste): board = pcbnew.LoadBoard(inputboard) removeComponents(board, parseReferences(ignore)) cutoutComponents(board, getComponents(board, parseReferences(cutout)), type) @@ -399,8 +399,10 @@ def create(inputboard, outputdir, jigsize, jigthickness, pcbthickness, pcbthickness = fromMm(pcbthickness) outerBorder, innerBorder = fromMm(registerborder[0]), fromMm(registerborder[1]) tolerance = fromMm(tolerance) - topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness, outerBorder, innerBorder, tolerance) - bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness, outerBorder, innerBorder, tolerance) + topRegister = makeTopRegister(board, jigsize,jigthickness, pcbthickness, + outerBorder, innerBorder, tolerance) + bottomRegister = makeBottomRegister(board, jigsize,jigthickness, pcbthickness, + outerBorder, innerBorder, tolerance) topRegisterFile = os.path.join(outputdir, "topRegister.scad") solid.scad_render_to_file(topRegister, topRegisterFile) @@ -432,7 +434,7 @@ def collectFootprintEdges(footprint, layer): """ return [e for e in footprint.GraphicalItems() if e.GetLayer() == layer] -def extractComponentPolygons(footprints, layer: Layer): +def extractComponentPolygons(footprints, srcLayer): """ Return a list of shapely polygons with holes for already placed components. The source layer defines the geometry on which the cutout is computed. @@ -440,7 +442,7 @@ def extractComponentPolygons(footprints, layer: Layer): """ polygons = [] for f in footprints: - edges = collectFootprintEdges(f, layer) + edges = collectFootprintEdges(f, srcLayer) for ring in extractRings(edges): polygons.append(toShapely(ring, edges)) return polygons From 5f6119d99eecf9dd821f20b214dbd537aa06cb4c Mon Sep 17 00:00:00 2001 From: Wannes Sels Date: Mon, 3 Jul 2023 20:35:06 +0200 Subject: [PATCH 6/6] fix imports (needs improvement) --- kikit/export.py | 2 +- kikit/panelize.py | 9 ++++++--- kikit/stencil.py | 17 ++++++++--------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/kikit/export.py b/kikit/export.py index 7429cf3f..5aa38bfd 100644 --- a/kikit/export.py +++ b/kikit/export.py @@ -140,7 +140,7 @@ def gerberImpl(boardfile: str, outputdir: str, plot_plan: list[LayerToPlot]=full pctl.SetLayer(layer_to_plot.id) suffix = "" if settings["NoSuffix"] else layer_to_plot.name pctl.OpenPlotfile(suffix, PLOT_FORMAT_GERBER, layer_to_plot.description) - jobfile_writer.AddGbrFile(layer_to_plot.id, os.path.basename(popt.GetPlotFileName())) + jobfile_writer.AddGbrFile(layer_to_plot.id, os.path.basename(pctl.GetPlotFileName())) if pctl.PlotLayer() == False: print("plot error") diff --git a/kikit/panelize.py b/kikit/panelize.py index 4492cfa2..0143d350 100644 --- a/kikit/panelize.py +++ b/kikit/panelize.py @@ -1505,7 +1505,7 @@ def makeCutsToLayer(self, cuts, layer=Layer.Cmts_User, prolongation=fromMm(0)): self.board.Add(segment) def addNPTHole(self, position: VECTOR2I, diameter: KiLength, - paste: bool=False, ref: Optional[str]=None) -> None: + paste: bool=False, adhesive: bool=False, ref: Optional[str]=None) -> None: """ Add a drilled non-plated hole to the position (`VECTOR2I`) with given diameter. The paste option allows to place the hole on the paste layers. @@ -1515,11 +1515,14 @@ def addNPTHole(self, position: VECTOR2I, diameter: KiLength, for pad in footprint.Pads(): pad.SetDrillSize(toKiCADPoint((diameter, diameter))) pad.SetSize(toKiCADPoint((diameter, diameter))) + layerSet = pad.GetLayerSet() if paste: - layerSet = pad.GetLayerSet() layerSet.AddLayer(Layer.F_Paste) layerSet.AddLayer(Layer.B_Paste) - pad.SetLayerSet(layerSet) + if adhesive: + layerSet.AddLayer(Layer.F_Adhes) + layerSet.AddLayer(Layer.B_Adhes) + pad.SetLayerSet(layerSet) if ref is not None: footprint.SetReference(ref) self.board.Add(footprint) diff --git a/kikit/stencil.py b/kikit/stencil.py index 380f8bcb..b1d93615 100644 --- a/kikit/stencil.py +++ b/kikit/stencil.py @@ -1,6 +1,5 @@ from pcbnewTransition import pcbnew -from pcbnew import wxRect, wxPoint, PCB_SHAPE, BOARD import numpy as np import json from collections import OrderedDict @@ -34,12 +33,12 @@ def __init__(self, top_layer: LayerToPlot, bottom_layer: LayerToPlot): self.bottom_layer = bottom_layer -def addBottomCounterpart(board: BOARD, item: PCB_SHAPE, stencil_type: StencilType = StencilType.SolderPaste): +def addBottomCounterpart(board, item, stencil_type: StencilType = StencilType.SolderPaste): item = item.Duplicate() item.SetLayer(stencil_type.bottom_layer.id) board.Add(item) -def addRoundedCorner(board: BOARD, center: wxPoint, start: wxPoint, end: wxPoint, thickness, stencil_type: StencilType = StencilType.SolderPaste): +def addRoundedCorner(board, center, start, end, thickness, stencil_type: StencilType = StencilType.SolderPaste): corner = pcbnew.PCB_SHAPE() corner.SetShape(STROKE_T.S_ARC) corner.SetCenter(toKiCADPoint((center[0], center[1]))) @@ -54,7 +53,7 @@ def addRoundedCorner(board: BOARD, center: wxPoint, start: wxPoint, end: wxPoint board.Add(corner) addBottomCounterpart(board, corner, stencil_type) -def addLine(board: BOARD, start: wxPoint, end: wxPoint, thickness: int, stencil_type: StencilType = StencilType.SolderPaste): +def addLine(board, start, end, thickness: int, stencil_type: StencilType = StencilType.SolderPaste): line = pcbnew.PCB_SHAPE() line.SetShape(STROKE_T.S_SEGMENT) line.SetStart(toKiCADPoint((start[0], start[1]))) @@ -64,7 +63,7 @@ def addLine(board: BOARD, start: wxPoint, end: wxPoint, thickness: int, stencil_ board.Add(line) addBottomCounterpart(board, line, stencil_type) -def addBite(board: BOARD, origin: wxPoint, direction: wxPoint, normal: wxPoint, thickness: int, stencil_type: StencilType = StencilType.SolderPaste): +def addBite(board, origin, direction, normal, thickness: int, stencil_type: StencilType = StencilType.SolderPaste): """ Adds a bite to the stencil, direction points to the bridge, normal points inside the stencil @@ -86,7 +85,7 @@ def numberOfCuts(length, bridgeWidth, bridgeSpacing): return count, cutLength -def addFrame(board: BOARD, rect: wxRect, bridgeWidth: int, bridgeSpacing: int, clearance: int, stencil_type: StencilType = StencilType.SolderPaste): +def addFrame(board, rect, bridgeWidth: int, bridgeSpacing: int, clearance: int, stencil_type: StencilType = StencilType.SolderPaste): """ Add rectangular frame to the board """ @@ -136,7 +135,7 @@ def addFrame(board: BOARD, rect: wxRect, bridgeWidth: int, bridgeSpacing: int, c addBite(board, toKiCADPoint((x2, end)), toKiCADPoint((0, 1)), toKiCADPoint((-1, 0)), clearance, stencil_type) -def addHole(board: BOARD, position: wxPoint, radius: int, stencil_type: StencilType = StencilType.SolderPaste): +def addHole(board, position, radius: int, stencil_type: StencilType = StencilType.SolderPaste): circle = pcbnew.PCB_SHAPE() circle.SetShape(STROKE_T.S_CIRCLE) circle.SetCenter(toKiCADPoint((position[0], position[1]))) @@ -148,7 +147,7 @@ def addHole(board: BOARD, position: wxPoint, radius: int, stencil_type: StencilT board.Add(circle) addBottomCounterpart(board, circle, stencil_type) -def addJigFrame(board: BOARD, jigFrameSize: (int, int), bridgeWidth: int=fromMm(2), +def addJigFrame(board, jigFrameSize: (int, int), bridgeWidth: int=fromMm(2), bridgeSpacing: int=fromMm(10), clearance: int=fromMm(0.5), stencil_type: StencilType = StencilType.SolderPaste): """ Given a Pcbnew board finds the board outline and creates a stencil for @@ -299,7 +298,7 @@ def shapelyToSHAPE_POLY_SET(polygon): p.AddOutline(linestringToKicad(polygon.exterior)) return p -def cutoutComponents(board: BOARD, components: list[str], stencil_type: StencilType = StencilType.SolderPaste): +def cutoutComponents(board, components: list[str], stencil_type: StencilType = StencilType.SolderPaste): topCutout = extractComponentPolygons(components, pcbnew.F_CrtYd) for polygon in topCutout: zone = pcbnew.PCB_SHAPE()