def build(self, board: pcbnew.BOARD, external_signals: dict[str, pcbnew.NETINFO_ITEM] = {}, path: list[str] = []): # Populate signal map creating new signals where required local_signals = {} for name in self.public_signals: if name in external_signals: local_signals[name] = external_signals[name] else: net = pcbnew.NETINFO_ITEM(board, "/".join( (*path, self.id, name))) board.Add(net) local_signals[name] = net for name in self.private_signals: net = pcbnew.NETINFO_ITEM(board, "/".join((*path, self.id, name))) board.Add(net) local_signals[name] = net # TODO: Warn about unused signals? for component, signal_mapping in self.components: component.build( board, { inner_name: local_signals[signal_name] for (inner_name, signal_name) in signal_mapping.items() }, (*path, self.id))
def build(self, board: pcbnew.BOARD, external_signals: dict[str, pcbnew.NETINFO_ITEM] = {}, path: list[str] = []): def load_footprint(ref: str): """Load a footprint otherwise raise exception""" library, name = ref.split(":") footprint = pcbnew.FootprintLoad( os.path.join(os.environ.get("RUNFILES_DIR", "external/"), library), name) if not footprint: raise ValueError( f"can not load footprint {name} from library {library}") return footprint fp = load_footprint(self.ref) fp.SetValue(self.value) set_pcb_position(fp, self.pose) fp.Value().SetVisible(self.value_visible) fp.Reference().SetVisible(self.reference_visible) fp.Reference().SetKeepUpright(self.keep_upright) if self.clear_top: for g in fp.GraphicalItems(): layer = g.GetLayer() if layer == 37: if isinstance(g, pcbnew.EDA_TEXT): g.SetVisible(False) else: g.DeleteStructure() other = [x.GetReference() for x in board.GetFootprints()] x = 1 while f"{self.reference_prefix}{x}" in other: x = x + 1 fp.SetReference(f"{self.reference_prefix}{x}") board.Add(fp) for i, pin_name in enumerate(self.pins, start=1): if pin_name in external_signals: fp.FindPadByName(i).SetNet(external_signals[pin_name]) for name in external_signals.keys(): if name not in self.pins: raise RuntimeError( f"no pin '{name}' in {path} ({self.reference_prefix}{x})")
def build(self, board: pcbnew.BOARD, external_signals: dict[str, pcbnew.NETINFO_ITEM] = {}, path: list[str] = []): """Create and return a KiCad circle centered at x, y.""" item = pcbnew.PCB_SHAPE() item.SetShape(pcbnew.S_CIRCLE) item.SetStart(pcbnew.wxPointMM(self.x, self.y)) item.SetEnd(pcbnew.wxPointMM(self.x + self.diameter / 2, self.y)) item.SetLayer(self.layer) board.Add(item)
def build(self, board: pcbnew.BOARD, external_signals: dict[str, pcbnew.NETINFO_ITEM] = {}, path: list[str] = []): o = pcbnew.PCB_TEXT(board) o.SetText(self.text) o.SetHorizJustify(pcbnew.GR_TEXT_HJUSTIFY_CENTER) o.SetTextSize( pcbnew.wxSize(pcbnew.FromMM(self.size), pcbnew.FromMM(self.size))) o.SetTextThickness(pcbnew.FromMM(self.size * self.thickness * 0.15)) o.SetLayer(self.layer) set_pcb_position(o, self.pcbpos) board.Add(o)
def build(self, board: pcbnew.BOARD, external_signals: dict[str, pcbnew.NETINFO_ITEM] = {}, path: list[str] = []): """Yield a sequence of KiCad segments from the provided Shapely polygon. A Shapely polygon may have interior polygons representing holes in the larger polygon. The hole polygons are emitted as additional segments so they appear as pcb cutouts. """ # coerce to polygon in case input is a ring geom = shapely.geometry.polygon.Polygon(self.geom) def make_point(p): x, y = p return pcbnew.wxPointMM(x, y) def make_seg(p1, p2): seg = pcbnew.PCB_SHAPE() seg.SetShape(pcbnew.S_SEGMENT) seg.SetStart(make_point(p1)) seg.SetEnd(make_point(p2)) seg.SetLayer(self.layer) return seg points = geom.exterior.coords for p1, p2 in [(points[i], points[(i + 1) % len(points)]) for i in range(len(points))]: board.Add(make_seg(p1, p2)) for interior in geom.interiors: points = interior.coords for p1, p2 in [(points[i], points[(i + 1) % len(points)]) for i in range(len(points))]: board.Add(make_seg(p1, p2))
def make_bom(board: pcbnew.BOARD, field_names: List[str], component_info: ComponentInfo = {}): fp = io.StringIO() writer = csv.DictWriter(fp, fieldnames=field_names) writer.writeheader() for footprint in board.GetFootprints(): bom_info = get_builtin_fields(footprint) key = tuple(bom_info[f] for f in key_fields) component_file_fields = {} if component_info: if key in component_info: # This is a hack, but treat Rotation as a special # column and add it's value to the value from KiCAD. for offset_field in ["Rotation"]: bom_info[offset_field] = str(float(bom_info[offset_field]) + float(component_info[key].pop(offset_field,0))) bom_info.update(component_info[key]) else: logging.warning(f"missing part information for {key}") continue row = OrderedDict() # Copy requested fields to CSV file for field_name in field_names: if field_name in bom_info: row[field_name] = bom_info[field_name] else: raise ValueError( f"unknown field '{field_name}' must be one of {list(bom_info.keys())}" ) writer.writerow(row) fp.seek(0) return fp.read()
plugin.FootprintLibDelete(lib_path1) except: pass # ignore, new may not exist if first run try: plugin.FootprintLibDelete(lib_path2) except: pass # ignore, new may not exist if first run plugin.FootprintLibCreate(lib_path1) # Verify that the same plugin instance can edge trigger on a lib_path change # for a FootprintLibCreate() plugin.FootprintLibCreate(lib_path2) board = BOARD() # The only way to construct a MODULE is to pass it a BOARD? Yep. module = MODULE(board) fpid = FPID('mine') module.SetFPID(fpid) plugin.FootprintSave(lib_path2, module) # Verify that the same plugin instance can edge trigger on a lib_path change # for a FootprintSave() plugin.FootprintSave(lib_path1, module) # create a disparity between the library's name ("footprint"),
def place_component(pcb: pcbnew.BOARD, ref: str, pos: Position): module = pcb.FindModuleByReference(ref) module.SetPosition(to_pcbnew_position(pos)) module.SetOrientation(to_pcbnew_angle(pos.angle))