diff options
Diffstat (limited to 'pcbnew/scripting')
31 files changed, 3913 insertions, 0 deletions
diff --git a/pcbnew/scripting/TODO.txt b/pcbnew/scripting/TODO.txt new file mode 100644 index 0000000..c0f3160 --- /dev/null +++ b/pcbnew/scripting/TODO.txt @@ -0,0 +1,11 @@ + +* think about documentation, how to do it +* Action plugins: + right click hooks, + toolbar hooks, + menu hooks, +* IO plugins +* better footprint wizard (preview in footprint wizard list) + + + diff --git a/pcbnew/scripting/board.i b/pcbnew/scripting/board.i new file mode 100644 index 0000000..da0e10a --- /dev/null +++ b/pcbnew/scripting/board.i @@ -0,0 +1,117 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es> + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file board.i + * @brief Specific BOARD extensions and templates + */ + + +%extend BOARD +{ + %pythoncode + %{ + def GetModules(self): return self.m_Modules + def GetDrawings(self): return self.m_Drawings + def GetTracks(self): return self.m_Track + def GetFullRatsnest(self): return self.m_FullRatsnest + + def Save(self,filename): + return SaveBoard(filename,self,IO_MGR.KICAD) + + # + # add function, clears the thisown to avoid python from deleting + # the object in the garbage collector + # + + def Add(self,item): + item.thisown=0 + self.AddNative(item) + %} + +} + +// this is to help python with the * accessor of DLIST templates + +%rename(Get) operator BOARD_ITEM*; +%rename(Get) operator TRACK*; +%rename(Get) operator D_PAD*; +%rename(Get) operator MODULE*; + + +// we must translate C++ templates to scripting languages + +%template(BOARD_ITEM_List) DLIST<BOARD_ITEM>; +%template(MODULE_List) DLIST<MODULE>; +%template(TRACK_List) DLIST<TRACK>; +%template(PAD_List) DLIST<D_PAD>; + +// std::vector templates + +%template(VIA_DIMENSION_Vector) std::vector<VIA_DIMENSION>; +%template (RATSNEST_Vector) std::vector<RATSNEST_ITEM>; + +%extend BOARD +{ + %pythoncode + { + def GetNetClasses(self): + return self.GetDesignSettings().m_NetClasses + + def GetCurrentNetClassName(self): + return self.GetDesignSettings().m_CurrentNetClassName + + def GetViasDimensionsList(self): + return self.GetDesignSettings().m_ViasDimensionsList + + def GetTrackWidthList(self): + return self.GetDesignSettings().m_TrackWidthList + } +} + + +%extend DRAWSEGMENT +{ + %pythoncode + { + def GetShapeStr(self): + return self.ShowShape(self.GetShape()) + } +} + +%extend BOARD_ITEM +{ + %pythoncode + { + def SetPos(self,p): + self.SetPosition(p) + self.SetPos0(p) + + def SetStartEnd(self,start,end): + self.SetStart(start) + self.SetStart0(start) + self.SetEnd(end) + self.SetEnd0(end) + } +} diff --git a/pcbnew/scripting/board_item.i b/pcbnew/scripting/board_item.i new file mode 100644 index 0000000..93f3026 --- /dev/null +++ b/pcbnew/scripting/board_item.i @@ -0,0 +1,98 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Miguel Angel Ajo <miguelangel@nbee.es> + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file board_item.i + * @brief board_item helpers, mainly for casting down to all child classes + */ + + +/* Cast downs from EDA_ITEM/BOARD_ITEM to childs */ + +%inline +{ + BOARD_ITEM* Cast_to_BOARD_ITEM(EDA_ITEM* base) { return dynamic_cast<BOARD_ITEM*>(base); } +} + +%extend BOARD_ITEM +{ + TEXTE_PCB* Cast_to_TEXTE_PCB() { return dynamic_cast<TEXTE_PCB*>(self); } + DIMENSION* Cast_to_DIMENSION() { return dynamic_cast<DIMENSION*>(self); } + MODULE* Cast_to_MODULE() { return dynamic_cast<MODULE*>(self); } + TEXTE_MODULE* Cast_to_TEXTE_MODULE() { return dynamic_cast<TEXTE_MODULE*>(self); } + DRAWSEGMENT* Cast_to_DRAWSEGMENT() { return dynamic_cast<DRAWSEGMENT*>(self); } + MARKER_PCB* Cast_to_MARKER_PCB() { return dynamic_cast<MARKER_PCB*>(self); } + BOARD* Cast_to_BOARD() { return dynamic_cast<BOARD*>(self); } + EDGE_MODULE* Cast_to_EDGE_MODULE() { return dynamic_cast<EDGE_MODULE*>(self); } + D_PAD* Cast_to_D_PAD() { return dynamic_cast<D_PAD*>(self); } + TRACK* Cast_to_TRACK() { return dynamic_cast<TRACK*>(self); } + VIA* Cast_to_VIA() { return dynamic_cast<VIA*>(self); } + ZONE_CONTAINER* Cast_to_ZONE_CONTAINER() { return dynamic_cast<ZONE_CONTAINER*>(self);} + PCB_TARGET* Cast_to_PCB_TARGET() { return dynamic_cast<PCB_TARGET*>(self); } + + + %pythoncode + { + def Cast(self): + + ct = self.GetClass() + + if ct=="PTEXT": + return self.Cast_to_TEXTE_PCB() + elif ct=="BOARD": + return self.Cast_to_BOARD() + elif ct=="DIMENSION": + return self.Cast_to_DIMENSION() + elif ct=="DRAWSEGMENT": + return self.Cast_to_DRAWSEGMENT() + elif ct=="MGRAPHIC": + return self.Cast_to_EDGE_MODULE() + elif ct=="MODULE": + return self.Cast_to_MODULE() + elif ct=="PAD": + return self.Cast_to_D_PAD() + elif ct=="MTEXT": + return self.Cast_to_TEXTE_MODULE() + elif ct=="VIA": + return self.Cast_to_VIA() + elif ct=="TRACK": + return self.Cast_to_TRACK() + elif ct=="PCB_TARGET": + return self.Cast_to_PCB_TARGET() + elif ct=="ZONE_CONTAINER": + return self.Cast_to_ZONE_CONTAINER() + else: + return None + + + def Duplicate(self): + + ct = self.GetClass() + + if ct=="BOARD": + return None + else: + return Cast_to_BOARD_ITEM(self.Clone()).Cast() + } +} diff --git a/pcbnew/scripting/examples/createFPC40.py b/pcbnew/scripting/examples/createFPC40.py new file mode 100755 index 0000000..7c6568e --- /dev/null +++ b/pcbnew/scripting/examples/createFPC40.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python2.7 +from pcbnew import * + +size_025_160mm = wxSizeMM(0.25,1.6) +size_150_200mm = wxSizeMM(1.50,2.0) +pads = 40 + +# create a blank board +pcb = BOARD() + +pcb.m_NetClasses.GetDefault().SetClearance(FromMM(0.1)) + +# create a new module, it's parent is our previously created pcb +module = MODULE(pcb) +module.SetReference("FPC"+str(pads)) # give it a reference name +module.Reference().SetPos0(wxPointMM(-1,-1)) +pcb.Add(module) # add it to our pcb +m_pos = wxPointMM(50,50) +module.SetPosition(m_pos) + +# create a pad array and add it to the module + + +def smdRectPad(module,size,pos,name): + pad = D_PAD(module) + pad.SetSize(size) + pad.SetShape(PAD_RECT) + pad.SetAttribute(PAD_SMD) + pad.SetLayerMask(PAD_SMD_DEFAULT_LAYERS) + pad.SetPos0(pos) + pad.SetPadName(name) + return pad + +for n in range (0,pads): + pad = smdRectPad(module,size_025_160mm,wxPointMM(0.5*n,0),str(n+1)) + module.Add(pad) + + +pad_s0 = smdRectPad(module,size_150_200mm,wxPointMM(-1.6,1.3),"0") +pad_s1 = smdRectPad(module,size_150_200mm,wxPointMM((pads-1)*0.5+1.6,1.3),"0") +module.Add(pad_s0) +module.Add(pad_s1) + +e = EDGE_MODULE(module) +e.SetStart0(wxPointMM(-1,0)) +e.SetEnd0(wxPointMM(0,0)) +e.SetWidth(FromMM(0.2)) +e.SetLayer(EDGE_LAYER) +e.SetShape(S_SEGMENT) +module.Add(e) + +# save the PCB to disk +fpid = FPID("FPC"+str(pads)) #the name in library +module.SetFPID( fpid ) + +try: + FootprintLibCreate("fpc40.mod") +except: + pass # we try to create, but may be it exists already +FootprintSave("fpc40.mod",module) diff --git a/pcbnew/scripting/examples/createPcb.py b/pcbnew/scripting/examples/createPcb.py new file mode 100755 index 0000000..21c3803 --- /dev/null +++ b/pcbnew/scripting/examples/createPcb.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python2.7 +from pcbnew import * + +size_0_6mm = wxSizeMM(0.6,0.6) +size_1_0mm = wxSizeMM(1.0,1.0) + +# create a blank board +pcb = BOARD() + +pcb.m_NetClasses.GetDefault().SetClearance(FromMM(0.1)) + +# create a new module, it's parent is our previously created pcb +module = MODULE(pcb) +module.SetReference("M1") # give it a reference name +module.Reference().SetPos0(wxPointMM(-10,-10)) +pcb.Add(module) # add it to our pcb +m_pos = wxPointMM(50,50) +module.SetPosition(m_pos) + +# create a pad array and add it to the module +n = 1 +for y in range (0,10): + for x in range (0,10): + pad = D_PAD(module) + pad.SetDrillSize(size_0_6mm) + pad.SetSize(size_1_0mm) + pt = wxPointMM(1.27*x,1.27*y) + pad.SetPos0(pt); + #pad.SetPosition(pt) + pad.SetPadName(str(n)) + module.Add(pad) + n+=1 + + +# save the PCB to disk +pcb.Save("my2.kicad_pcb") +pcb.Save("my2.brd") + +pcb = LoadBoard("my2.kicad_pcb") + +print map( lambda x: x.GetReference() , list(pcb.GetModules())) + +for m in pcb.GetModules(): + for p in m.Pads(): + print p.GetPadName(), p.GetPosition(), p.GetOffset() + + +# pcb.GetDesignSettings() diff --git a/pcbnew/scripting/examples/hidePcbValuesShowReferences.py b/pcbnew/scripting/examples/hidePcbValuesShowReferences.py new file mode 100644 index 0000000..6b37842 --- /dev/null +++ b/pcbnew/scripting/examples/hidePcbValuesShowReferences.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +import sys +from pcbnew import * + +filename=sys.argv[1] + +pcb = LoadBoard(filename) + +for module in pcb.GetModules(): + print "* Module: %s"%module.GetReference() + module.Value().SetVisible(False) # set Value as Hidden + module.Reference().SetVisible(True) # set Reference as Visible + +pcb.Save("mod_"+filename) diff --git a/pcbnew/scripting/examples/listPcb.py b/pcbnew/scripting/examples/listPcb.py new file mode 100755 index 0000000..60ed093 --- /dev/null +++ b/pcbnew/scripting/examples/listPcb.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +import sys +from pcbnew import * + +filename=sys.argv[1] + +pcb = LoadBoard(filename) + +ToUnits = ToMM +FromUnits = FromMM +#ToUnits=ToMils +#FromUnits=FromMils + +print "LISTING VIAS:" + +for item in pcb.GetTracks(): + if type(item) is VIA: + + pos = item.GetPosition() + drill = item.GetDrillValue() + width = item.GetWidth() + print " * Via: %s - %f/%f "%(ToUnits(pos),ToUnits(drill),ToUnits(width)) + + elif type(item) is TRACK: + + start = item.GetStart() + end = item.GetEnd() + width = item.GetWidth() + + print " * Track: %s to %s, width %f" % (ToUnits(start),ToUnits(end),ToUnits(width)) + + else: + print "Unknown type %s" % type(item) + +print "" +print "LIST DRAWINGS:" + +for item in pcb.GetDrawings(): + if type(item) is TEXTE_PCB: + print "* Text: '%s' at %s"%(item.GetText(), item.GetPosition()) + elif type(item) is DRAWSEGMENT: + print "* Drawing: %s"%item.GetShapeStr() # dir(item) + else: + print type(item) + +print "" +print "LIST MODULES:" + +for module in pcb.GetModules(): + print "* Module: %s at %s"%(module.GetReference(),ToUnits(module.GetPosition())) + +print "" +print "Ratsnest cnt:",len(pcb.GetFullRatsnest()) +print "track w cnt:",len(pcb.GetTrackWidthList()) +print "via s cnt:",len(pcb.GetViasDimensionsList()) + +print "" +print "LIST ZONES:", pcb.GetAreaCount() + +for idx in range(0, pcb.GetAreaCount()): + zone=pcb.GetArea(idx) + print "zone:", idx, "priority:", zone.GetPriority(), "netname", zone.GetNetname() + +print "" +print "NetClasses:", pcb.GetNetClasses().GetCount(), + diff --git a/pcbnew/scripting/examples/listPcbLibrary.py b/pcbnew/scripting/examples/listPcbLibrary.py new file mode 100755 index 0000000..8262c17 --- /dev/null +++ b/pcbnew/scripting/examples/listPcbLibrary.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +from pcbnew import * + +lst = FootprintEnumerate("/usr/share/kicad/modules/sockets.mod") + +for name in lst: + m = FootprintLoad("/usr/share/kicad/modules/sockets.mod",name) + print name,"->",m.GetLibRef(), m.GetReference() + + for p in m.Pads(): + print "\t",p.GetPadName(),p.GetPosition(),p.GetPos0(), p.GetOffset() + diff --git a/pcbnew/scripting/module.i b/pcbnew/scripting/module.i new file mode 100644 index 0000000..21902bf --- /dev/null +++ b/pcbnew/scripting/module.i @@ -0,0 +1,110 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es> + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file module.i + * @brief Specific BOARD extensions and templates + */ + + +%extend MODULE +{ + %pythoncode + %{ + + #def SaveToLibrary(self,filename): + # return SaveModuleToLibrary(filename,self) + + # + # add function, clears the thisown to avoid python from deleting + # the object in the garbage collector + # + + def Add(self,item): + + itemC = item.Cast() + + if type(itemC) is D_PAD: + item.thisown=0 + self.Pads().PushBack(itemC) + elif type(itemC) in [ TEXTE_PCB, DIMENSION, TEXTE_MODULE, DRAWSEGMENT,EDGE_MODULE]: + item.thisown = 0 + self.GraphicalItems().PushBack(item) + %} + +} + +%pythoncode +{ + + def GetPluginForPath(lpath): + return IO_MGR.PluginFind(IO_MGR.LEGACY) + + def FootprintEnumerate(lpath): + plug = GetPluginForPath(lpath) + return plug.FootprintEnumerate(lpath) + + def FootprintLoad(lpath,name): + plug = GetPluginForPath(lpath) + return plug.FootprintLoad(lpath,name) + + def FootprintSave(lpath,module): + plug = GetPluginForPath(lpath) + return plug.FootprintSave(lpath,module) + + def FootprintDelete(lpath,name): + plug = GetPluginForPath(lpath) + plug.FootprintDelete(lpath,name) + + def FootprintLibCreate(lpath): + plug = GetPluginForPath(lpath) + plug.FootprintLibCreate(lpath) + + def FootprintLibDelete(lpath): + plug = GetPluginForPath(lpath) + plug.FootprintLibDelete(lpath) + + def FootprintIsWritable(lpath): + plug = GetPluginForPath(lpath) + plug.FootprintLibIsWritable(lpath) +} + +%{ + MODULE *PyModule_to_MODULE(PyObject *obj0) + { + void *argp; + int res1 = SWIG_ConvertPtr(obj0, &argp,SWIGTYPE_p_MODULE, 0 | 0 ); + if (!SWIG_IsOK(res1)) + { + SWIG_exception_fail(SWIG_ArgError(res1), "Converting object to MODULE*"); + } + + return (MODULE*)argp; + + fail: + return NULL; + + } + +%} diff --git a/pcbnew/scripting/pcbnew.i b/pcbnew/scripting/pcbnew.i new file mode 100644 index 0000000..4e8c7b4 --- /dev/null +++ b/pcbnew/scripting/pcbnew.i @@ -0,0 +1,169 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es> + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file pcbnew.i + * @brief Specific pcbnew wrappers + */ + + +%module pcbnew + +%feature("autodoc", "1"); +#ifdef ENABLE_DOCSTRINGS_FROM_DOXYGEN +%include "docstrings.i" +#endif + +%include "kicad.i" + +// ignore a couple of items that generate warnings from swig built code + +%ignore BOARD_ITEM::ZeroOffset; +%ignore D_PAD::m_PadSketchModePenSize; + +// rename the Add method of classes to Add native, so we will handle +// the Add method in python + +%rename(AddNative) *::Add; + +// fix method names conflicts +%rename(AddChild) MODULE::Add; +%rename(RemoveChild) MODULE::Remove; +%rename(DeleteChild) MODULE::Delete; + +%exception { + try{ + $action + } + catch( IO_ERROR e ) + { + std::string str = TO_UTF8( e.errorText ); + str += '\n'; + PyErr_SetString( PyExc_IOError, str.c_str() ); + return NULL; + } + catch( std::exception &e ) + { + std::string str = e.what(); + str += '\n'; + PyErr_SetString( PyExc_IOError, str.c_str() ); + return NULL; + } + catch( ... ) + { + SWIG_fail; + } +} +%include exception.i + + +// this is what it must be included in the wrapper .cxx code to compile + +%{ + #include <wx_python_helpers.h> + #include <class_board_item.h> + #include <class_board_connected_item.h> + #include <class_board_design_settings.h> + #include <class_board.h> + #include <class_module.h> + #include <class_track.h> + #include <class_zone.h> + #include <zones.h> + #include <layers_id_colors_and_visibility.h> + #include <class_pad.h> + #include <pad_shapes.h> + #include <class_netinfo.h> + #include <class_pcb_text.h> + #include <class_dimension.h> + #include <class_drawsegment.h> + #include <class_marker_pcb.h> + #include <class_mire.h> + #include <class_text_mod.h> + #include <class_edge_mod.h> + #include <dlist.h> + #include <class_zone_settings.h> + #include <class_netclass.h> + #include <class_netinfo.h> + #include <pcbnew_scripting_helpers.h> + + #include <plotcontroller.h> + #include <pcb_plot_params.h> + #include <exporters/gendrill_Excellon_writer.h> + #include <colors.h> + + BOARD *GetBoard(); /* get current editor board */ +%} + + +%{ + #include <io_mgr.h> + #include <kicad_plugin.h> +%} + +%include <class_board_item.h> +%include <class_board_connected_item.h> +%include <class_board_design_settings.h> +%include <class_board.h> +%include <class_module.h> +%include <class_track.h> +%include <class_zone.h> +%include <zones.h> +%include <layers_id_colors_and_visibility.h> +%include <class_pad.h> +%include <pad_shapes.h> +%include <class_netinfo.h> +%include <class_pcb_text.h> +%include <class_dimension.h> +%include <class_drawsegment.h> +%include <class_marker_pcb.h> +%include <class_mire.h> +%include <class_text_mod.h> +%include <class_edge_mod.h> +%include <dlist.h> +%include <class_zone_settings.h> +%include <class_netclass.h> +%include <class_netinfo.h> + +%include <plotcontroller.h> +%include <pcb_plot_params.h> +%include <plot_common.h> +%include <exporters/gendrill_Excellon_writer.h> +%include <colors.h> + +%include "board_item.i" + +%include <pcbnew_scripting_helpers.h> + + +// ignore RELEASER as nested classes are still unsupported by swig +%ignore IO_MGR::RELEASER; +%include <io_mgr.h> +%include <kicad_plugin.h> + +%include "board.i" +%include "module.i" +%include "plugins.i" +%include "units.i" + + diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.cpp b/pcbnew/scripting/pcbnew_footprint_wizards.cpp new file mode 100644 index 0000000..e0f3854 --- /dev/null +++ b/pcbnew/scripting/pcbnew_footprint_wizards.cpp @@ -0,0 +1,361 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@ajo.es> + * Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file pcbnew_footprint_wizards.cpp + * @brief Class PCBNEW_PYTHON_FOOTPRINT_WIZARDS + */ + +#include "pcbnew_footprint_wizards.h" +#include <python_scripting.h> +#include <stdio.h> +#include <macros.h> + + +PYTHON_FOOTPRINT_WIZARD::PYTHON_FOOTPRINT_WIZARD( PyObject* aWizard ) +{ + PyLOCK lock; + + this->m_PyWizard = aWizard; + Py_XINCREF( aWizard ); +} + + +PYTHON_FOOTPRINT_WIZARD::~PYTHON_FOOTPRINT_WIZARD() +{ + PyLOCK lock; + + Py_XDECREF( this->m_PyWizard ); +} + + +PyObject* PYTHON_FOOTPRINT_WIZARD::CallMethod( const char* aMethod, PyObject* aArglist ) +{ + PyLOCK lock; + + PyErr_Clear(); + // pFunc is a new reference to the desired method + PyObject* pFunc = PyObject_GetAttrString( this->m_PyWizard, aMethod ); + + if( pFunc && PyCallable_Check( pFunc ) ) + { + PyObject* result = PyObject_CallObject( pFunc, aArglist ); + + if( PyErr_Occurred() ) + { +#if 1 // defined(DEBUG) + wxMessageBox( PyErrStringWithTraceback(), + wxT( "Exception on python footprint wizard code" ), + wxICON_ERROR | wxOK ); +#endif + } + + if( result ) + { + Py_XDECREF( pFunc ); + return result; + } + } + else + { + printf( "method not found, or not callable: %s\n", aMethod ); + } + + if( pFunc ) + { + Py_XDECREF( pFunc ); + } + + return NULL; +} + + +wxString PYTHON_FOOTPRINT_WIZARD::CallRetStrMethod( const char* aMethod, PyObject* aArglist ) +{ + wxString ret; + PyLOCK lock; + + PyObject* result = CallMethod( aMethod, aArglist ); + + if( result ) + { + const char* str_res = PyString_AsString( result ); + ret = FROM_UTF8( str_res ); + Py_DECREF( result ); + } + + return ret; +} + + +wxArrayString PYTHON_FOOTPRINT_WIZARD::CallRetArrayStrMethod( const char* aMethod, + PyObject* aArglist ) +{ + wxArrayString ret; + wxString str_item; + PyLOCK lock; + + PyObject* result = CallMethod( aMethod, aArglist ); + + if( result ) + { + if( !PyList_Check( result ) ) + { + Py_DECREF( result ); + ret.Add( wxT( + "PYTHON_FOOTPRINT_WIZARD::CallRetArrayStrMethod, result is not a list" ), + 1 ); + return ret; + } + + ret = PyArrayStringToWx( result ); + + Py_DECREF( result ); + } + + return ret; +} + + +wxString PYTHON_FOOTPRINT_WIZARD::GetName() +{ + PyLOCK lock; + + return CallRetStrMethod( "GetName" ); +} + + +wxString PYTHON_FOOTPRINT_WIZARD::GetImage() +{ + PyLOCK lock; + + return CallRetStrMethod( "GetImage" ); +} + + +wxString PYTHON_FOOTPRINT_WIZARD::GetDescription() +{ + PyLOCK lock; + + return CallRetStrMethod( "GetDescription" ); +} + + +int PYTHON_FOOTPRINT_WIZARD::GetNumParameterPages() +{ + int ret = 0; + PyLOCK lock; + + // Time to call the callback + PyObject* result = CallMethod( "GetNumParameterPages", NULL ); + + if( result ) + { + if( !PyInt_Check( result ) ) + return -1; + + ret = PyInt_AsLong( result ); + Py_DECREF( result ); + } + + return ret; +} + + +wxString PYTHON_FOOTPRINT_WIZARD::GetParameterPageName( int aPage ) +{ + wxString ret; + PyLOCK lock; + + // Time to call the callback + PyObject* arglist = Py_BuildValue( "(i)", aPage ); + PyObject* result = CallMethod( "GetParameterPageName", arglist ); + + Py_DECREF( arglist ); + + if( result ) + { + const char* str_res = PyString_AsString( result ); + ret = FROM_UTF8( str_res ); + Py_DECREF( result ); + } + + return ret; +} + + +wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterNames( int aPage ) +{ + wxArrayString ret; + PyLOCK lock; + + PyObject* arglist = Py_BuildValue( "(i)", aPage ); + + ret = CallRetArrayStrMethod( "GetParameterNames", arglist ); + Py_DECREF( arglist ); + + for( unsigned i = 0; i < ret.GetCount(); i++ ) + { + wxString rest; + wxString item = ret[i]; + + if( item.StartsWith( wxT( "*" ), &rest ) ) + { + ret[i] = rest; + } + } + + return ret; +} + + +wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterTypes( int aPage ) +{ + wxArrayString ret; + PyLOCK lock; + + PyObject* arglist = Py_BuildValue( "(i)", aPage ); + + ret = CallRetArrayStrMethod( "GetParameterNames", arglist ); + Py_DECREF( arglist ); + + for( unsigned i = 0; i<ret.GetCount(); i++ ) + { + wxString rest; + wxString item = ret[i]; + + if( item.StartsWith( wxT( "*" ), &rest ) ) + { + ret[i] = wxT( "UNITS" ); // units + } + else + { + ret[i] = wxT( "IU" ); // internal units + } + } + + return ret; +} + + +wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterValues( int aPage ) +{ + PyLOCK lock; + + PyObject* arglist = Py_BuildValue( "(i)", aPage ); + wxArrayString ret = CallRetArrayStrMethod( "GetParameterValues", arglist ); + + Py_DECREF( arglist ); + + return ret; +} + + +wxArrayString PYTHON_FOOTPRINT_WIZARD::GetParameterErrors( int aPage ) +{ + PyLOCK lock; + + PyObject* arglist = Py_BuildValue( "(i)", aPage ); + wxArrayString ret = CallRetArrayStrMethod( "GetParameterErrors", arglist ); + + Py_DECREF( arglist ); + + return ret; +} + + +wxString PYTHON_FOOTPRINT_WIZARD::SetParameterValues( int aPage, wxArrayString& aValues ) +{ + int len = aValues.size(); + + PyLOCK lock; + + PyObject* py_list = PyList_New( len ); + + for( int i = 0; i < len; i++ ) + { + wxString& str = aValues[i]; + PyObject* py_str = PyString_FromString( (const char*) str.mb_str() ); + PyList_SetItem( py_list, i, py_str ); + } + + PyObject* arglist; + + arglist = Py_BuildValue( "(i,O)", aPage, py_list ); + wxString res = CallRetStrMethod( "SetParameterValues", arglist ); + Py_DECREF( arglist ); + + return res; +} + + +// this is a SWIG function declaration -from module.i +MODULE* PyModule_to_MODULE( PyObject* obj0 ); + + +MODULE* PYTHON_FOOTPRINT_WIZARD::GetFootprint( wxString * aMessages ) +{ + PyLOCK lock; + + PyObject* result = CallMethod( "GetFootprint", NULL ); + + if( aMessages ) + *aMessages = CallRetStrMethod( "GetBuildMessages", NULL ); + + if( !result ) + return NULL; + + PyObject* obj = PyObject_GetAttrString( result, "this" ); + + if( PyErr_Occurred() ) + { + PyErr_Print(); + PyErr_Clear(); + } + + MODULE* mod = PyModule_to_MODULE( obj ); + + return mod; +} + + +void* PYTHON_FOOTPRINT_WIZARD::GetObject() +{ + return (void*) m_PyWizard; +} + + +void PYTHON_FOOTPRINT_WIZARDS::register_wizard( PyObject* aPyWizard ) +{ + PYTHON_FOOTPRINT_WIZARD* fw = new PYTHON_FOOTPRINT_WIZARD( aPyWizard ); + + fw->register_wizard(); +} + + +void PYTHON_FOOTPRINT_WIZARDS::deregister_wizard( PyObject* aPyWizard ) +{ + // deregister also destroyes the previously created "PYTHON_FOOTPRINT_WIZARD object" + FOOTPRINT_WIZARDS::deregister_object( (void*) aPyWizard ); +} diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.h b/pcbnew/scripting/pcbnew_footprint_wizards.h new file mode 100644 index 0000000..ada111f --- /dev/null +++ b/pcbnew/scripting/pcbnew_footprint_wizards.h @@ -0,0 +1,71 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@ajo.es> + * Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file pcbnew_footprint_wizards.h + * @brief Class PCBNEW_FOOTPRINT_WIZARDS + */ + +#ifndef PCBNEW_FOOTPRINT_WIZARDS_H +#define PCBNEW_FOOTPRINT_WIZARDS_H +#include <Python.h> +#include <vector> +#include <class_footprint_wizard.h> + + +class PYTHON_FOOTPRINT_WIZARD : public FOOTPRINT_WIZARD +{ + PyObject* m_PyWizard; + PyObject* CallMethod( const char* aMethod, PyObject* aArglist = NULL ); + wxString CallRetStrMethod( const char* aMethod, PyObject* aArglist = NULL ); + wxArrayString CallRetArrayStrMethod( const char* aMethod, + PyObject* aArglist = NULL ); + +public: + PYTHON_FOOTPRINT_WIZARD( PyObject* wizard ); + ~PYTHON_FOOTPRINT_WIZARD(); + wxString GetName(); + wxString GetImage(); + wxString GetDescription(); + int GetNumParameterPages(); + wxString GetParameterPageName( int aPage ); + wxArrayString GetParameterNames( int aPage ); + wxArrayString GetParameterTypes( int aPage ); + wxArrayString GetParameterValues( int aPage ); + wxArrayString GetParameterErrors( int aPage ); + // must return an empty string or an error description + wxString SetParameterValues( int aPage, wxArrayString& aValues ); + MODULE* GetFootprint( wxString * aMessages ); + void* GetObject(); +}; + + +class PYTHON_FOOTPRINT_WIZARDS +{ +public: + static void register_wizard( PyObject* aPyWizard ); + static void deregister_wizard( PyObject* aPyWizard ); +}; + +#endif /* PCBNEW_FOOTPRINT_WIZARDS_H */ diff --git a/pcbnew/scripting/pcbnew_scripting_helpers.cpp b/pcbnew/scripting/pcbnew_scripting_helpers.cpp new file mode 100644 index 0000000..62b21b3 --- /dev/null +++ b/pcbnew/scripting/pcbnew_scripting_helpers.cpp @@ -0,0 +1,109 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es> + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file pcbnew_scripting_helpers.cpp + * @brief Scripting helper functions for pcbnew functionality + */ + +#include <Python.h> + +#include <pcbnew_scripting_helpers.h> +#include <pcbnew.h> +#include <pcbnew_id.h> +#include <build_version.h> +#include <class_board.h> +#include <kicad_string.h> +#include <io_mgr.h> +#include <macros.h> +#include <stdlib.h> + +static PCB_EDIT_FRAME* PcbEditFrame = NULL; + +BOARD* GetBoard() +{ + if( PcbEditFrame ) + return PcbEditFrame->GetBoard(); + else + return NULL; +} + + +void ScriptingSetPcbEditFrame( PCB_EDIT_FRAME* aPCBEdaFrame ) +{ + PcbEditFrame = aPCBEdaFrame; +} + + +BOARD* LoadBoard( wxString& aFileName ) +{ + if( aFileName.EndsWith( wxT( ".kicad_pcb" ) ) ) + return LoadBoard( aFileName, IO_MGR::KICAD ); + + else if( aFileName.EndsWith( wxT( ".brd" ) ) ) + return LoadBoard( aFileName, IO_MGR::LEGACY ); + + // as fall back for any other kind use the legacy format + return LoadBoard( aFileName, IO_MGR::LEGACY ); +} + + +BOARD* LoadBoard( wxString& aFileName, IO_MGR::PCB_FILE_T aFormat ) +{ + return IO_MGR::Load( aFormat, aFileName ); +} + + +bool SaveBoard( wxString& aFilename, BOARD* aBoard ) +{ + return SaveBoard( aFilename, aBoard, IO_MGR::KICAD ); +} + + +bool SaveBoard( wxString& aFileName, BOARD* aBoard, + IO_MGR::PCB_FILE_T aFormat ) +{ + aBoard->m_Status_Pcb &= ~CONNEXION_OK; + aBoard->SynchronizeNetsAndNetClasses(); + aBoard->GetDesignSettings().SetCurrentNetClass( NETCLASS::Default ); + +#if 0 + wxString header; + PROPERTIES props; + + if( aFormat==IO_MGR::LEGACY ) + { + header = wxString::Format( + wxT( "PCBNEW-BOARD Version %d date %s\n\n# Created by Pcbnew%s scripting\n\n" ), + LEGACY_BOARD_FILE_VERSION, DateAndTime().GetData(), + GetBuildVersion().GetData() ); + props["header"] = header; + } + + IO_MGR::Save( aFormat, aFileName, aBoard, &props ); +#else + IO_MGR::Save( aFormat, aFileName, aBoard, NULL ); +#endif + return true; +} diff --git a/pcbnew/scripting/pcbnew_scripting_helpers.h b/pcbnew/scripting/pcbnew_scripting_helpers.h new file mode 100644 index 0000000..bff3d0e --- /dev/null +++ b/pcbnew/scripting/pcbnew_scripting_helpers.h @@ -0,0 +1,47 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@ajo.es> + * Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __PCBNEW_SCRIPTING_HELPERS_H +#define __PCBNEW_SCRIPTING_HELPERS_H + +#include <wxPcbStruct.h> +#include <io_mgr.h> +/* we could be including all these methods as static in a class, but + * we want plain pcbnew.<method_name> access from python */ + +#ifndef SWIG +void ScriptingSetPcbEditFrame( PCB_EDIT_FRAME* aPCBEdaFrame ); + +#endif + +BOARD* GetBoard(); + +BOARD* LoadBoard( wxString& aFileName, IO_MGR::PCB_FILE_T aFormat ); +BOARD* LoadBoard( wxString& aFileName ); + +bool SaveBoard( wxString& aFileName, BOARD* aBoard, IO_MGR::PCB_FILE_T aFormat ); +bool SaveBoard( wxString& aFileName, BOARD* aBoard ); + + +#endif diff --git a/pcbnew/scripting/plugins.i b/pcbnew/scripting/plugins.i new file mode 100644 index 0000000..7e7dcb7 --- /dev/null +++ b/pcbnew/scripting/plugins.i @@ -0,0 +1,35 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@ajo.es> + * Copyright (C) 2013 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +%{ +#include <scripting/pcbnew_footprint_wizards.h> +%} + +class PYTHON_FOOTPRINT_WIZARDS +{ +public: + static void register_wizard(PyObject *wizard); + static void deregister_wizard(PyObject *wizard); + +}; diff --git a/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py b/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py new file mode 100644 index 0000000..8526b45 --- /dev/null +++ b/pcbnew/scripting/plugins/FPC_(SMD_type)_footprintwizard.py @@ -0,0 +1,156 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW + + +class FPC_FootprintWizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "FPC (SMT connector)" + + def GetDescription(self): + return "FPC (SMT connector) Footprint Wizard" + + def GetValue(self): + pins = self.parameters["Pads"]["*n"] + return "FPC_%d" % pins + + def GenerateParameterList(self): + self.AddParam( "Pads", "n", self.uNatural, 40 ) + self.AddParam( "Pads", "pitch", self.uMM, 0.5 ) + self.AddParam( "Pads", "width", self.uMM, 0.25 ) + self.AddParam( "Pads", "height", self.uMM, 1.6) + self.AddParam( "Shield", "shield_to_pad", self.uMM, 1.6 ) + self.AddParam( "Shield", "from_top", self.uMM, 1.3 ) + self.AddParam( "Shield", "width", self.uMM, 1.5 ) + self.AddParam( "Shield", "height", self.uMM, 2 ) + + + # build a rectangular pad + def smdRectPad(self,module,size,pos,name): + pad = pcbnew.D_PAD(module) + pad.SetSize(size) + pad.SetShape(pcbnew.PAD_SHAPE_RECT) + pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD) + pad.SetLayerSet( pad.SMDMask() ) + pad.SetPos0(pos) + pad.SetPosition(pos) + pad.SetPadName(name) + return pad + + def CheckParameters(self): + p = self.parameters + self.CheckParamInt( "Pads", "*n" ) # not internal units preceded by "*" + + + def BuildThisFootprint(self): + p = self.parameters + pad_count = int(p["Pads"]["*n"]) + pad_width = p["Pads"]["width"] + pad_height = p["Pads"]["height"] + pad_pitch = p["Pads"]["pitch"] + shl_width = p["Shield"]["width"] + shl_height = p["Shield"]["height"] + shl_to_pad = p["Shield"]["shield_to_pad"] + shl_from_top = p["Shield"]["from_top"] + + offsetX = pad_pitch * ( pad_count-1 ) / 2 + size_pad = pcbnew.wxSize( pad_width, pad_height ) + size_shld = pcbnew.wxSize(shl_width, shl_height) + size_text = self.GetTextSize() # IPC nominal + + # Gives a position and size to ref and value texts: + textposy = pad_height/2 + pcbnew.FromMM(1) + self.GetTextThickness() + self.draw.Reference( 0, textposy, size_text ) + + textposy = textposy + size_text + self.GetTextThickness() + self.draw.Value( 0, textposy, size_text ) + + # create a pad array and add it to the module + for n in range ( 0, pad_count ): + xpos = pad_pitch*n - offsetX + pad = self.smdRectPad(self.module,size_pad, pcbnew.wxPoint(xpos,0),str(n+1)) + self.module.Add(pad) + + + # Mechanical shield pads: left pad and right pad + xpos = -shl_to_pad-offsetX + pad_s0_pos = pcbnew.wxPoint(xpos,shl_from_top) + pad_s0 = self.smdRectPad(self.module, size_shld, pad_s0_pos, "0") + xpos = (pad_count-1) * pad_pitch+shl_to_pad - offsetX + pad_s1_pos = pcbnew.wxPoint(xpos,shl_from_top) + pad_s1 = self.smdRectPad(self.module, size_shld, pad_s1_pos, "0") + + self.module.Add(pad_s0) + self.module.Add(pad_s1) + + # add footprint outline + linewidth = self.draw.GetLineTickness() + margin = linewidth + + # upper line + posy = -pad_height/2 - linewidth/2 - margin + xstart = - pad_pitch*0.5-offsetX + xend = pad_pitch * pad_count + xstart; + self.draw.Line( xstart, posy, xend, posy ) + + # lower line + posy = pad_height/2 + linewidth/2 + margin + self.draw.Line(xstart, posy, xend, posy) + + # around left mechanical pad (the outline around right pad is mirrored/y axix) + yend = pad_s0_pos.y + shl_height/2 + margin + self.draw.Line(xstart, posy, xstart, yend) + self.draw.Line(-xstart, posy, -xstart, yend) + + posy = yend + xend = pad_s0_pos.x - (shl_width/2 + linewidth + margin*2) + self.draw.Line(xstart, posy, xend, posy) + + # right pad side + self.draw.Line(-xstart, posy, -xend, yend) + + # vertical segment at left of the pad + xstart = xend + yend = posy - (shl_height + linewidth + margin*2) + self.draw.Line(xstart, posy, xend, yend) + + # right pad side + self.draw.Line(-xstart, posy, -xend, yend) + + # horizontal segment above the pad + xstart = xend + xend = - pad_pitch*0.5-offsetX + posy = yend + self.draw.Line(xstart, posy, xend, yend) + + # right pad side + self.draw.Line(-xstart, posy,-xend, yend) + + # vertical segment above the pad + xstart = xend + yend = -pad_height/2 - linewidth/2 - margin + self.draw.Line(xstart, posy, xend, yend) + + # right pad side + self.draw.Line(-xstart, posy, -xend, yend) + + +FPC_FootprintWizard().register() diff --git a/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py b/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py new file mode 100644 index 0000000..058fe1e --- /dev/null +++ b/pcbnew/scripting/plugins/FootprintWizardDrawingAids.py @@ -0,0 +1,523 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division + +import pcbnew +import math + + +class FootprintWizardDrawingAids: + """ + Collection of handy functions to simplify drawing shapes from within + footprint wizards + + A "drawing context" is provided which can be used to set and retain + settings such as line tickness and layer + """ + + # directions (in degrees, compass-like) + dirN = 0 + dirNE = 45 + dirE = 90 + dirSE = 135 + dirS = 180 + dirSW = 225 + dirW = 270 + dirNW = 315 + + # flip constants + flipNone = 0 + flipX = 1 # flip X values, i.e. about Y + flipY = 2 # flip Y valuersabout X + flipBoth = 3 + + xfrmIDENTITY = [1, 0, 0, 0, 1, 0] # no transform + + # these values come from our KiCad Library Convention 0.11 + defaultLineThickness = pcbnew.FromMM(0.15) + + def DefaultGraphicLayer(self): + return pcbnew.F_SilkS + + def DefaultTextValueLayer(self): + return pcbnew.F_Fab + + def __init__(self, module): + self.module = module + # drawing context defaults + self.dc = { + 'layer': self.DefaultGraphicLayer(), + 'lineThickness': self.defaultLineThickness, + 'transforms': [], + 'transform': self.xfrmIDENTITY + } + + def PushTransform(self, mat): + """ + Add a transform to the top of the stack and recompute the + overall transform + """ + self.dc['transforms'].append(mat) + self.RecomputeTransforms() + + def PopTransform(self, num=1): + """ + Remove a transform from the top of the stack and recompute the + overall transform + """ + + for i in range(num): + mat = self.dc['transforms'].pop() + self.RecomputeTransforms() + return mat + + def ResetTransform(self): + """ + Reset the transform stack to the identity matrix + """ + self.dc['transforms'] = [] + self.RecomputeTransforms() + + def _ComposeMatricesWithIdentity(self, mats): + """ + Compose a sequence of matrices together by sequential + pre-mutiplciation with the identity matrix + """ + + x = self.xfrmIDENTITY + + for mat in mats: + #precompose with each transform in turn + x = [ + x[0] * mat[0] + x[1] * mat[3], + x[0] * mat[1] + x[1] * mat[4], + x[0] * mat[2] + x[1] * mat[5] + x[2], + x[3] * mat[0] + x[4] * mat[3], + x[3] * mat[1] + x[4] * mat[4], + x[3] * mat[2] + x[4] * mat[5] + x[5]] + + return x + + def RecomputeTransforms(self): + """ + Re-compute the transform stack into a single transform and + store in the DC + """ + self.dc['transform'] = self._ComposeMatricesWithIdentity( + self.dc['transforms']) + + def TransformTranslate(self, x, y, push=True): + """ + Set up and return a transform matrix representing a translartion + optionally pushing onto the stack + + ( 1 0 x ) + ( 0 1 y ) + """ + mat = [1, 0, x, 0, 1, y] + + if push: + self.PushTransform(mat) + return mat + + def TransformFlipOrigin(self, flip, push=True): + """ + Set up and return a transform matrix representing a horizontal, + vertical or both flip about the origin + """ + mat = None + if flip == self.flipX: + mat = [-1, 0, 0, 0, 1, 0] + elif flip == self.flipY: + mat = [1, 0, 0, 0, -1, 0] + elif flip == self.flipBoth: + mat = [-1, 0, 0, 0, -1, 0] + elif flip == self.flipNone: + mat = self.xfrmIDENTITY + else: + raise ValueError + + if push: + self.PushTransform(mat) + return mat + + def TransformFlip(self, x, y, flip=flipNone, push=True): + """ + Set up and return a transform matrix representing a horizontal, + vertical or both flip about a point (x,y) + + This is performed by a translate-to-origin, flip, translate- + back sequence + """ + mats = [self.TransformTranslate(x, y, push=False), + self.TransformFlipOrigin(flip, push=False), + self.TransformTranslate(-x, -y, push=False)] + + #distill into a single matrix + mat = self._ComposeMatricesWithIdentity(mats) + + if push: + self.PushTransform(mat) + return mat + + def TransformRotationOrigin(self, rot, push=True): + """ + Set up and return a transform matrix representing a rotation + about the origin, and optionally push onto the stack + + ( cos(t) -sin(t) 0 ) + ( sin(t) cos(t) 0 ) + """ + rads = rot * math.pi / 180 + mat = [math.cos(rads), -math.sin(rads), 0, + math.sin(rads), math.cos(rads), 0] + + if push: + self.PushTransform(mat) + return mat + + def TransformRotation(self, x, y, rot, push=True): + """ + Set up and return a transform matrix representing a rotation + about the point (x,y), and optionally push onto the stack + + This is performed by a translate-to-origin, rotate, translate- + back sequence + """ + + mats = [self.TransformTranslate(x, y, push=False), + self.TransformRotationOrigin(rot, push=False), + self.TransformTranslate(-x, -y, push=False)] + + #distill into a single matrix + mat = self._ComposeMatricesWithIdentity(mats) + + if push: + self.PushTransform(mat) + return mat + + def TransformScaleOrigin(self, sx, sy=None, push=True): + """ + Set up and return a transform matrix representing a scale about + the origin, and optionally push onto the stack + + ( sx 0 0 ) + ( 0 sy 0 ) + """ + + if sy is None: + sy = sx + + mat = [sx, 0, 0, 0, sy, 0] + + if push: + self.PushTransform(mat) + return mat + + def TransformPoint(self, x, y, mat=None): + """ + Return a point (x, y) transformed by the given matrix, or if + that is not given, the drawing context transform + """ + + if not mat: + mat = self.dc['transform'] + + return pcbnew.wxPoint(x * mat[0] + y * mat[1] + mat[2], + x * mat[3] + y * mat[4] + mat[5]) + + def SetLineTickness(self, lineThickness): + """ + Set the current pen lineThickness used for subsequent drawing + operations + """ + self.dc['lineThickness'] = lineThickness + + def GetLineTickness(self): + """ + Get the current drawing context line tickness + """ + return self.dc['lineThickness'] + + def SetLayer(self, layer): + """ + Set the current drawing layer, used for subsequent drawing + operations + """ + self.dc['layer'] = layer + + def GetLayer(self): + """ + return the current drawing layer, used drawing operations + """ + return self.dc['layer'] + + def Line(self, x1, y1, x2, y2): + """ + Draw a line from (x1, y1) to (x2, y2) + """ + outline = pcbnew.EDGE_MODULE(self.module) + outline.SetWidth(self.GetLineTickness()) + outline.SetLayer(self.GetLayer()) + outline.SetShape(pcbnew.S_SEGMENT) + start = self.TransformPoint(x1, y1) + end = self.TransformPoint(x2, y2) + outline.SetStartEnd(start, end) + self.module.Add(outline) + + def Circle(self, x, y, r, filled=False): + """ + Draw a circle at (x,y) of radius r + If filled is true, the thickness and radius of the line will be set + such that the circle appears filled + """ + circle = pcbnew.EDGE_MODULE(self.module) + start = self.TransformPoint(x, y) + + if filled: + circle.SetWidth(r) + end = self.TransformPoint(x, y + r/2) + else: + circle.SetWidth(self.dc['lineThickness']) + end = self.TransformPoint(x, y + r) + + circle.SetLayer(self.dc['layer']) + circle.SetShape(pcbnew.S_CIRCLE) + circle.SetStartEnd(start, end) + self.module.Add(circle) + + def Arc(self, cx, cy, sx, sy, a): + """ + Draw an arc based on centre, start and angle + + The transform matrix is applied + + Note that this won't work properly if the result is not a + circular arc (eg a horzontal scale) + """ + circle = pcbnew.EDGE_MODULE(self.module) + circle.SetWidth(self.dc['lineThickness']) + + center = self.TransformPoint(cx, cy) + start = self.TransformPoint(sx, sy) + + circle.SetLayer(self.dc['layer']) + circle.SetShape(pcbnew.S_ARC) + + # check if the angle needs to be reverse (a flip scaling) + if cmp(self.dc['transform'][0], 0) != cmp(self.dc['transform'][4], 0): + a = -a + + circle.SetAngle(a) + circle.SetStartEnd(center, start) + self.module.Add(circle) + + # extends from (x1,y1) right + def HLine(self, x, y, l): + """ + Draw a horizontal line from (x,y), rightwards + """ + self.Line(x, y, x + l, y) + + def VLine(self, x, y, l): + """ + Draw a vertical line from (x1,y1), downwards + """ + self.Line(x, y, x, y + l) + + def Polyline(self, pts, mirrorX=None, mirrorY=None): + """ + Draw a polyline, optinally mirroring around the given points + """ + + def _PolyLineInternal(pts): + if len(pts) < 2: + return + + for i in range(0, len(pts) - 1): + self.Line(pts[i][0], pts[i][1], pts[i+1][0], pts[i+1][1]) + + _PolyLineInternal(pts) # original + + if mirrorX is not None: + self.TransformFlip(mirrorX, 0, self.flipX) + _PolyLineInternal(pts) + self.PopTransform() + + if mirrorY is not None: + self.TransformFlipOrigin(0, mirrorY, self.flipY) + _PolyLineInternal(pts) + self.PopTransform() + + if mirrorX is not None and mirrorY is not None: + self.TransformFlip(mirrorX, mirrorY, self.flipBoth) # both + _PolyLineInternal(pts) + self.PopTransform() + + def Reference(self, x, y, size, orientation_degree = 0): + """ + Draw the module's reference as the given point. + + The actual setting of the reference is not done in this drawing + aid - that is up to the wizard + """ + + text_size = pcbnew.wxSize(size, size) + + self.module.Reference().SetPos0(self.TransformPoint(x, y)) + self.module.Reference().SetTextPosition( + self.module.Reference().GetPos0()) + self.module.Reference().SetSize(text_size) + self.module.Reference().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg + + def Value(self, x, y, size, orientation_degree = 0): + """ + As for references, draw the module's value + """ + text_size = pcbnew.wxSize(size, size) + + self.module.Value().SetPos0(self.TransformPoint(x, y)) + self.module.Value().SetTextPosition(self.module.Value().GetPos0()) + self.module.Value().SetSize(text_size) + self.module.Value().SetLayer(self.DefaultTextValueLayer()) + self.module.Value().SetOrientation(orientation_degree*10) # internal angles are in 0.1 deg + + def Box(self, x, y, w, h): + """ + Draw a rectangular box, centred at (x,y), with given width and + height + """ + + pts = [[x - w/2, y - h/2], # left + [x + w/2, y - h/2], # right + [x + w/2, y + h/2], # bottom + [x - w/2, y + h/2], # top + [x - w/2, y - h/2]] # close + + self.Polyline(pts) + + def NotchedCircle(self, x, y, r, notch_w, notch_h, rotate=0): + """ + Circle radus r centred at (x, y) with a raised or depressed notch + at the top + Notch height is measured from the top of the circle radius + """ + + self.TransformRotation(x, y, rotate) + + # find the angle where the notch vertical meets the circle + angle_intercept = math.asin(notch_w/(2 * r)) + + # and find the co-ords of this point + sx = math.sin(angle_intercept) * r + sy = -math.cos(angle_intercept) * r + + # NOTE: this may be out by a factor of ten one day + arc_angle = (math.pi * 2 - angle_intercept * 2) * (1800/math.pi) + + self.Arc(x,y, sx, sy, arc_angle) + + pts = [[sx, sy], + [sx, -r - notch_h], + [-sx, -r - notch_h], + [-sx, sy]] + + self.Polyline(pts) + self.PopTransform() + + def NotchedBox(self, x, y, w, h, notchW, notchH, rotate=0): + """ + Draw a box with a notch in the top edge + """ + + self.TransformRotation(x, y, rotate) + + # limit to half the overall width + notchW = min(x + w/2, notchW) + + # draw notch + self.Polyline([ # three sides of box + (x - w/2, y - h/2), + (x - w/2, y + h/2), + (x + w/2, y + h/2), + (x + w/2, y - h/2), + # the notch + (notchW/2, y - h/2), + (notchW/2, y - h/2 + notchH), + (-notchW/2, y - h/2 + notchH), + (-notchW/2, y - h/2), + (x - w/2, y - h/2) + ]) + + self.PopTransform() + + def BoxWithDiagonalAtCorner(self, x, y, w, h, + setback=pcbnew.FromMM(1.27), flip=flipNone): + """ + Draw a box with a diagonal at the top left corner + """ + + self.TransformFlip(x, y, flip, push=True) + + pts = [[x - w/2 + setback, y - h/2], + [x - w/2, y - h/2 + setback], + [x - w/2, y + h/2], + [x + w/2, y + h/2], + [x + w/2, y - h/2], + [x - w/2 + setback, y - h/2]] + + self.Polyline(pts) + + self.PopTransform() + + def BoxWithOpenCorner(self, x, y, w, h, + setback=pcbnew.FromMM(1.27), flip=flipNone): + """ + Draw a box with an opening at the top left corner + """ + + self.TransformTranslate(x, y) + self.TransformFlipOrigin(flip) + + pts = [[- w/2, - h/2 + setback], + [- w/2, + h/2], + [+ w/2, + h/2], + [+ w/2, - h/2], + [- w/2 + setback, - h/2]] + + self.Polyline(pts) + + self.PopTransform(num=2) + + def MarkerArrow(self, x, y, direction=dirN, width=pcbnew.FromMM(1)): + """ + Draw a marker arrow facing in the given direction, with the + point at (x,y) + + Direction of 0 is north + """ + + self.TransformTranslate(x, y) + self.TransformRotationOrigin(direction) + + pts = [[0, 0], + [width / 2, width / 2], + [-width / 2, width / 2], + [0, 0]] + + self.Polyline(pts) + self.PopTransform(2) diff --git a/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py b/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py new file mode 100644 index 0000000..90d2498 --- /dev/null +++ b/pcbnew/scripting/plugins/HelpfulFootprintWizardPlugin.py @@ -0,0 +1,330 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +import pcbnew +import math +import FootprintWizardDrawingAids + + +class FootprintWizardParameterManager: + """ + Functions for helpfully managing parameters to a KiCAD Footprint + Wizard. + + Abstracts away from whatever structure is used by pcbnew's footprint + wizard class + """ + + def __init__(self): + self.parameters = {} + self.GenerateParameterList() + + def GenerateParameterList(self): + """ + Construct parameters here, or leave out to have no parameters + """ + pass + + def CheckParameters(self): + """ + Implement this to make checks on parameter values, filling + parameter_errors (or using the checker routines) + + Subclasses can implment their own and override the parent + defaults and add new ones + """ + pass + + uMM = 1 + uMils = 2 + uNatural = 3 + uBool = 4 + uString = 5 + + def AddParam(self, section, param, unit, default, hint=''): + """ + Add a parameter with some properties. + + TODO: Hints are not supported, as there is as yet nowhere to + put them in the KiCAD interface + """ + error = "" + val = None + if unit == self.uMM: + val = pcbnew.FromMM(default) + elif unit == self.uMils: + val = pcbnew.FromMils(default) + elif unit == self.uNatural: + val = default + elif unit == self.uString: + val = str(default) + elif unit == self.uBool: + val = "True" if default else "False" # ugly stringing + else: + error = "Warning: Unknown unit type: %s" % unit + return error + + if unit in [self.uNatural, self.uBool, self.uString]: + param = "*%s" % param # star prefix for natural + + if section not in self.parameters: + if not hasattr(self, 'page_order'): + self.page_order = [] + self.page_order.append(section) + self.parameters[section] = {} + if not hasattr(self, 'parameter_order'): + self.parameter_order = {} + self.parameter_order[section] = [] + + self.parameters[section][param] = val + self.parameter_order[section].append(param) + + return error + + + def _PrintParameterTable(self): + """ + Pretty-print the parameters we have + """ + message = "" + + for name, section in self.parameters.iteritems(): + message += " %s:\n" % name + + for key, value in section.iteritems(): + unit = "" + if ((type(value) is int or type(value) is float) + and not "*" in key): + unit = "mm" + + if "*" in key: + key = key[1:] + else: + value = pcbnew.ToMM(value) + + message += " %s: %s%s\n" % (key, value, unit) + + return message + + + def _ParametersHaveErrors(self): + """ + Return true if we discovered errors during parameter processing + """ + + for name, section in self.parameter_errors.iteritems(): + for k, v in section.iteritems(): + if v: + return True + + return False + + def _PrintParameterErrors(self): + """ + Pretty-print parameters with errors + """ + errors = "" + + for name, section in self.parameter_errors.iteritems(): + printed_section = False + + for key, value in section.iteritems(): + if value: + if not printed_section: + errors += " %s:" % name + + errors += " %s: %s (have %s)\n" % ( + key, value, self.parameters[name][key]) + + return errors + + def ProcessParameters(self): + """ + Make sure the parameters we have meet whatever expectations the + footprint wizard has of them + """ + + self.ClearErrors() + self.CheckParameters() + + if self._ParametersHaveErrors(): + return False + + return True + + ################################################################# + # PARAMETER CHECKERS + ################################################################# + + def CheckParamInt(self, section, param, min_value=1, + max_value=None, is_multiple_of=1): + """ + Make sure a parameter can be made into an int, and enforce + limits if required + """ + + try: + self.parameters[section][param] = ( + int(self.parameters[section][param])) + except ValueError: + self.parameter_errors[section][param] = ( + "Must be a valid integer") + return + + if min_value is not None and ( + self.parameters[section][param] < min_value): + self.parameter_errors[section][param] = ( + "Must be greater than or equal to %d" % (min_value)) + return + + if max_value is not None and ( + self.parameters[section][param] > max_value): + self.parameter_errors[section][param] = ( + "Must be less than or equal to %d" % (max_value)) + return + + if is_multiple_of > 1 and ( + self.parameters[section][param] % is_multiple_of) > 0: + self.parameter_errors[section][param] = ( + "Must be a multiple of %d" % is_multiple_of) + return + + return + + def CheckParamBool(self, section, param): + """ + Make sure a parameter looks like a boolean, convert to native + boolean type if so + """ + if str(self.parameters[section][param]).lower() in [ + "true", "t", "y", "yes", "on", "1", "1.0"]: + self.parameters[section][param] = True + return + elif str(self.parameters[section][param]).lower() in [ + "false", "f", "n", "no", "off", "0", "0.0"]: + self.parameters[section][param] = False + return + + self.parameter_errors[section][param] = "Must be boolean (true/false)" + return + + +class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin, + FootprintWizardParameterManager): + """ + A class to simplify many aspects of footprint creation, leaving only + the foot-print specific routines to the wizards themselves + + Generally, you need to implement: + GetReference() + GetValue() + GenerateParameterList() + CheckParameters() + BuildThisFootprint() + GetName() + GetDescription() + """ + def __init__(self): + pcbnew.FootprintWizardPlugin.__init__(self) + FootprintWizardParameterManager.__init__(self) + + self.name = self.GetName() + self.decription = self.GetDescription() + self.image = self.GetImage() + + def GetValue(self): + raise NotImplementedError + + # this value come from our KiCad Library Convention 0.11 + def GetReferencePrefix(self): + return "REF" + + def GetImage(self): + return "" + + def GetTextSize(self): + """ + IPC nominal + """ + return pcbnew.FromMM(1.2) + + def GetTextThickness(self): + """ + Thicker than IPC guidelines (10% of text height = 0.12mm) + as 5 wires/mm is a common silk screen limitation + """ + return pcbnew.FromMM(0.15) + + def SetModule3DModel(self): + """ + Set a 3D model for the module + + Default is to do nothing, you need to implement this if you have + a model to set + + FIXME: This doesn't seem to be enabled yet? + """ + pass + + def BuildThisFootprint(self): + """ + Draw the footprint. + + This is specific to each footprint class, you need to implment + this to draw what you want + """ + raise NotImplementedError + + def BuildFootprint( self ): + """ + Actually make the footprint. We defer all but the setup to + the implementing class + """ + + self.buildmessages = "" + + self.module = pcbnew.MODULE(None) # create a new module + # do it first, so if we return early, we don't segfault KiCad + + if not self.ProcessParameters(): + self.buildmessages = "Cannot build footprint: Parameters have errors:\n" + self.buildmessages += self._PrintParameterErrors() + return + + self.buildmessages = ("Building new %s footprint with the following parameters:\n" + % self.name) + + self.buildmessages += self._PrintParameterTable() + + self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids( + self.module) + + self.module.SetValue(self.GetValue()) + self.module.SetReference("%s**" % self.GetReferencePrefix()) + + fpid = pcbnew.FPID(self.module.GetValue()) # the name in library + self.module.SetFPID(fpid) + + self.SetModule3DModel() # add a 3d module if specified + + thick = self.GetTextThickness() + + self.module.Reference().SetThickness(thick) + self.module.Value().SetThickness(thick) + + self.BuildThisFootprint() # implementer's build function + + return diff --git a/pcbnew/scripting/plugins/PadArray.py b/pcbnew/scripting/plugins/PadArray.py new file mode 100644 index 0000000..5908d38 --- /dev/null +++ b/pcbnew/scripting/plugins/PadArray.py @@ -0,0 +1,275 @@ +# PadArray.py +# +# Copyright 2014 john <john@johndev> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# + +from __future__ import division + +import math +import pcbnew + +class PadMaker: + """ + Useful construction functions for common types of pads + """ + + def __init__(self, module): + self.module = module + + def THPad(self, Vsize, Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL, rot_degree = 0): + pad = pcbnew.D_PAD(self.module) + pad.SetSize(pcbnew.wxSize(Hsize, Vsize)) + pad.SetShape(shape) + pad.SetAttribute(pcbnew.PAD_ATTRIB_STANDARD) + pad.SetLayerSet(pad.StandardMask()) + pad.SetDrillSize(pcbnew.wxSize(drill, drill)) + pad.SetOrientation(rot_degree*10) # rotation is in 0.1 degrees + + return pad + + def THRoundPad(self, size, drill): + pad = self.THPad(size, size, drill, shape=pcbnew.PAD_SHAPE_CIRCLE) + return pad + + def NPTHRoundPad(self, drill): + pad = pcbnew.D_PAD(self.module) + pad.SetSize(pcbnew.wxSize(drill, drill)) + pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE) + pad.SetAttribute(pcbnew.PAD_ATTRIB_HOLE_NOT_PLATED) + pad.SetLayerSet(pad.UnplatedHoleMask()) + pad.SetDrillSize(pcbnew.wxSize(drill, drill)) + return pad + + def SMDPad(self, Vsize, Hsize, shape=pcbnew.PAD_SHAPE_RECT, rot_degree=0): + pad = pcbnew.D_PAD(self.module) + pad.SetSize(pcbnew.wxSize(Hsize, Vsize)) + pad.SetShape(shape) + pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD) + pad.SetLayerSet(pad.SMDMask()) + pad.SetOrientation(rot_degree*10) # rotation is in 0.1 degrees + + return pad + + def SMTRoundPad(self, size): + pad = self.SMDPad(size, size, shape=pcbnew.PAD_SHAPE_CIRCLE) + return pad + + +class PadArray: + + def __init__(self): + self.firstPadNum = 1 + self.pinNames = None + self.firstPad = None + + def SetPinNames(self, pinNames): + """ + Set a name for all the pins + """ + self.pinNames = pinNames + + def SetFirstPadType(self, firstPad): + self.firstPad = firstPad + + def SetFirstPadInArray(self, fpNum): + self.firstPadNum = fpNum + + def AddPad(self, pad): + self.pad.GetParent().Add(pad) + + def GetPad(self, is_first_pad, pos): + if (self.firstPad and is_first_pad): + pad = self.firstPad + else: + pad = self.pad + + # create a new pad with same characteristics + pad = pad.Duplicate() + pad.SetPos0(pos) + pad.SetPosition(pos) + + return pad + + def GetName(self, *args, **kwargs): + + if self.pinNames is None: + return self.NamingFunction(*args, **kwargs) + + return self.pinNames + + def NamingFunction(self, *args, **kwargs): + """ + Implement this as needed for each array type + """ + raise NotImplementedError; + + +class PadGridArray(PadArray): + + def __init__(self, pad, nx, ny, px, py, centre=pcbnew.wxPoint(0, 0)): + PadArray.__init__(self) + # this pad is more of a "context", we will use it as a source of + # pad data, but not actually add it + self.pad = pad + self.nx = int(nx) + self.ny = int(ny) + self.px = px + self.py = py + self.centre = centre + + # handy utility function 1 - A, 2 - B, 26 - AA, etc + # aIndex = 0 for 0 - A + # alphabet = set of allowable chars if not A-Z, + # eg ABCDEFGHJKLMNPRTUVWY for BGA + def AlphaNameFromNumber(self, n, aIndex=1, + alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): + + div, mod = divmod(n - aIndex, len(alphabet)) + alpha = alphabet[mod] + + if div > 0: + return self.AlphaNameFromNumber(div, aIndex, alphabet) + alpha + + return alpha + + # right to left, top to bottom + def NamingFunction(self, x, y): + return self.firstPadNum + (self.nx * y + x) + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + + pin1posX = self.centre.x - self.px * (self.nx - 1) / 2 + pin1posY = self.centre.y - self.py * (self.ny - 1) / 2 + + for x in range(0, self.nx): + posX = pin1posX + (x * self.px) + + for y in range(self.ny): + posY = pin1posY + (self.py * y) + pos = dc.TransformPoint(posX, posY) + pad = self.GetPad(x == 0 and y == 0, pos) + pad.SetPadName(self.GetName(x,y)) + self.AddPad(pad) + + +class PadZGridArray(PadArray): + + def __init__(self, pad, pad_count, line_count, line_pitch, + pad_pitch, centre=pcbnew.wxPoint(0, 0)): + PadArray.__init__(self) + # this pad is more of a "context", we will use it as a source of + # pad data, but not actually add it + self.pad = pad + self.pad_count = int(pad_count) + self.line_count = int(line_count) + self.line_pitch = line_pitch + self.pad_pitch = pad_pitch + self.centre = centre + + + # right to left, top to bottom + def NamingFunction(self, pad_pos): + return self.firstPadNum + pad_pos + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + + pin1posX = self.centre.x - self.pad_pitch * (self.pad_count - 1) / 2 + pin1posY = self.centre.y + self.line_pitch * (self.line_count - 1) / 2 + line = 0 + + for padnum in range(0, self.pad_count): + posX = pin1posX + (padnum * self.pad_pitch) + posY = pin1posY - (self.line_pitch * line) + + pos = dc.TransformPoint(posX, posY) + pad = self.GetPad(padnum == 0, pos) + pad.SetPadName(self.GetName(padnum)) + self.AddPad(pad) + + line += 1 + + if line >= self.line_count: + line = 0 + +class PadLineArray(PadGridArray): + + def __init__(self, pad, n, pitch, isVertical, + centre=pcbnew.wxPoint(0, 0)): + + if isVertical: + PadGridArray.__init__(self, pad, 1, n, 0, pitch, centre) + else: + PadGridArray.__init__(self, pad, n, 1, pitch, 0, centre) + +class PadCircleArray(PadArray): + + def __init__(self, pad, n, r, angle_offset=0, centre=pcbnew.wxPoint(0, 0), + clockwise=True): + PadArray.__init__(self) + # this pad is more of a "context", we will use it as a source of + # pad data, but not actually add it + self.pad = pad + self.n = int(n) + self.r = r + self.angle_offset = angle_offset + self.centre = centre + self.clockwise = clockwise + + # around the circle, CW or CCW according to the flag + def NamingFunction(self, n): + return str(self.firstPadNum + n) + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + for pin in range(0, self.n): + angle = self.angle_offset + (360 / self.n) * pin + + if not self.clockwise: + angle = -angle + + pos_x = math.sin(angle * math.pi / 180) * self.r + pos_y = -math.cos(angle * math.pi / 180) * self.r + pos = dc.TransformPoint(pos_x, pos_y) + pad = self.GetPad(pin == 0, pos) + pad.SetPadName(self.GetName(pin)) + self.AddPad(pad) + +class PadCustomArray(PadArray): + """ + Layout pads according to a custom array of [x,y] data + """ + + def __init__(self, pad, array): + PadArray.__init__(self) + self.pad = pad + self.array = array + + def NamingFunction(self, n): + return str(self.firstPadNum + n) + + #relocate the pad and add it as many times as we need + def AddPadsToModule(self, dc): + + for i in range(len(self.array)): + pos = dc.TransformPoint(self.array[i][0], self.array[i][1]) + pad = self.GetPad(i == 0, pos) + pad.SetPadName(self.GetName(i)) + self.AddPad(pad) diff --git a/pcbnew/scripting/plugins/__init__.py b/pcbnew/scripting/plugins/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pcbnew/scripting/plugins/__init__.py @@ -0,0 +1 @@ + diff --git a/pcbnew/scripting/plugins/bga_wizard.py b/pcbnew/scripting/plugins/bga_wizard.py new file mode 100644 index 0000000..ed3500f --- /dev/null +++ b/pcbnew/scripting/plugins/bga_wizard.py @@ -0,0 +1,98 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class BGAPadGridArray(PA.PadGridArray): + + def NamingFunction(self, n_x, n_y): + return "%s%d" % ( + self.AlphaNameFromNumber(n_y + 1, alphabet="ABCDEFGHJKLMNPRTUVWY"), + n_x + 1) + + +class BGAWizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "BGA" + + def GetDescription(self): + return "Ball Grid Array Footprint Wizard" + + def GenerateParameterList(self): + self.AddParam("Pads", "pad pitch", self.uMM, 1) + self.AddParam("Pads", "pad size", self.uMM, 0.5) + self.AddParam("Pads", "row count", self.uNatural, 5) + self.AddParam("Pads", "column count", self.uNatural, 5) + self.AddParam("Pads", "outline x margin", self.uMM, 1) + self.AddParam("Pads", "outline y margin", self.uMM, 1) + + def CheckParameters(self): + self.CheckParamInt("Pads", "*row count") + self.CheckParamInt("Pads", "*column count") + + def GetValue(self): + pins = (self.parameters["Pads"]["*row count"] + * self.parameters["Pads"]["*column count"]) + + return "BGA_%d" % pins + + def BuildThisFootprint(self): + + pads = self.parameters["Pads"] + + rows = pads["*row count"] + cols = pads["*column count"] + pad_size = pads["pad size"] + pad_size = pcbnew.wxSize(pad_size, pad_size) + pad_pitch = pads["pad pitch"] + + # add in the pads + pad = PA.PadMaker(self.module).SMTRoundPad(pads["pad size"]) + + pin1_pos = pcbnew.wxPoint(-((cols - 1) * pad_pitch) / 2, + -((rows - 1) * pad_pitch) / 2) + + array = BGAPadGridArray(pad, cols, rows, pad_pitch, pad_pitch) + array.AddPadsToModule(self.draw) + + #box + ssx = -pin1_pos.x + pads["outline x margin"] + ssy = -pin1_pos.y + pads["outline y margin"] + + self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, + pads["outline x margin"]) + + # Courtyard + cmargin = self.draw.GetLineTickness() + self.draw.SetLayer(pcbnew.F_CrtYd) + sizex = (ssx + cmargin) * 2 + sizey = (ssy + cmargin) * 2 + self.draw.Box(0, 0, sizex, sizey) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + ypos = ssy + text_size + self.draw.Value(0, ypos, text_size) + self.draw.Reference(0, -ypos, text_size) + + +BGAWizard().register() diff --git a/pcbnew/scripting/plugins/circular_pad_array_wizard.py b/pcbnew/scripting/plugins/circular_pad_array_wizard.py new file mode 100644 index 0000000..0b2a280 --- /dev/null +++ b/pcbnew/scripting/plugins/circular_pad_array_wizard.py @@ -0,0 +1,83 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +from __future__ import division + +import math + +import pcbnew +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class circular_pad_array_wizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "Circular Pad Array" + + def GetDescription(self): + return "Circular array of pads" + + def GenerateParameterList(self): + + self.AddParam("Pads", "n", self.uNatural, 6) + self.AddParam("Pads", "pad width", self.uMM, 1.5) + self.AddParam("Pads", "drill", self.uMM, 1) + self.AddParam("Pads", "circle diameter", self.uMM, 5) + self.AddParam("Pads", "first pad angle", self.uNatural, 0) + self.AddParam("Pads", "number clockwise", self.uBool, True) + self.AddParam("Pads", "first pad number", self.uNatural, 1) + + def CheckParameters(self): + + self.CheckParamInt("Pads", "*n") + self.CheckParamInt("Pads", "*first pad number") + self.CheckParamBool("Pads", "*number clockwise") + + def GetValue(self): + pins = self.parameters["Pads"]["*n"] + return "CPA_%d" % pins + + def BuildThisFootprint(self): + + prm = self.parameters['Pads'] + + pad_size = prm['pad width'] + + pad = PA.PadMaker(self.module).THPad( + prm['pad width'], prm['pad width'], prm['drill']) + + array = PA.PadCircleArray( + pad, prm['*n'], prm['circle diameter'] / 2, + angle_offset=prm["*first pad angle"], + centre=pcbnew.wxPoint(0, 0), + clockwise=prm["*number clockwise"]) + + array.SetFirstPadInArray(prm["*first pad number"]) + + array.AddPadsToModule(self.draw) + + body_radius = (prm['circle diameter'] + prm['pad width'])/2 + self.draw.GetLineTickness() + self.draw.Circle(0, 0, body_radius) + + text_size = self.GetTextSize() # IPC nominal + thickness = self.GetTextThickness() + textposy = body_radius + self.draw.GetLineTickness()/2 + self.GetTextSize()/2 + thickness + self.draw.Value( 0, textposy, text_size ) + self.draw.Reference( 0, -textposy, text_size ) + + + +circular_pad_array_wizard().register() diff --git a/pcbnew/scripting/plugins/qfp_wizard.py b/pcbnew/scripting/plugins/qfp_wizard.py new file mode 100644 index 0000000..3cd1eda --- /dev/null +++ b/pcbnew/scripting/plugins/qfp_wizard.py @@ -0,0 +1,125 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin +import PadArray as PA + + +class QFPWizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): + + def GetName(self): + return "QFP" + + def GetDescription(self): + return "Quad Flat Package footprint wizard" + + def GenerateParameterList(self): + self.AddParam("Pads", "n", self.uNatural, 100) + self.AddParam("Pads", "pad pitch", self.uMM, 0.5) + self.AddParam("Pads", "pad width", self.uMM, 0.25) + self.AddParam("Pads", "pad length", self.uMM, 1.5) + self.AddParam("Pads", "vertical pitch", self.uMM, 15) + self.AddParam("Pads", "horizontal pitch", self.uMM, 15) + self.AddParam("Pads", "oval", self.uBool, True) + + self.AddParam("Package", "package width", self.uMM, 14) + self.AddParam("Package", "package height", self.uMM, 14) + self.AddParam("Package", "courtyard margin", self.uMM, 1) + + def CheckParameters(self): + self.CheckParamInt("Pads", "*n", is_multiple_of=4) + self.CheckParamBool("Pads", "*oval") + + def GetValue(self): + return "QFP_%d" % self.parameters["Pads"]["*n"] + + def BuildThisFootprint(self): + pads = self.parameters["Pads"] + + pad_pitch = pads["pad pitch"] + pad_length = self.parameters["Pads"]["pad length"] + pad_width = self.parameters["Pads"]["pad width"] + + v_pitch = pads["vertical pitch"] + h_pitch = pads["horizontal pitch"] + + pads_per_row = pads["*n"] // 4 + + row_len = (pads_per_row - 1) * pad_pitch + + pad_shape = pcbnew.PAD_SHAPE_OVAL if pads["*oval"] else pcbnew.PAD_SHAPE_RECT + + h_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width, + shape=pad_shape, rot_degree=90.0) + v_pad = PA.PadMaker(self.module).SMDPad( pad_length, pad_width, shape=pad_shape) + + #left row + pin1Pos = pcbnew.wxPoint(-h_pitch / 2, 0) + array = PA.PadLineArray(h_pad, pads_per_row, pad_pitch, True, pin1Pos) + array.SetFirstPadInArray(1) + array.AddPadsToModule(self.draw) + + #bottom row + pin1Pos = pcbnew.wxPoint(0, v_pitch / 2) + array = PA.PadLineArray(v_pad, pads_per_row, pad_pitch, False, pin1Pos) + array.SetFirstPadInArray(pads_per_row + 1) + array.AddPadsToModule(self.draw) + + #right row + pin1Pos = pcbnew.wxPoint(h_pitch / 2, 0) + array = PA.PadLineArray(h_pad, pads_per_row, -pad_pitch, True, + pin1Pos) + array.SetFirstPadInArray(2*pads_per_row + 1) + array.AddPadsToModule(self.draw) + + #top row + pin1Pos = pcbnew.wxPoint(0, -v_pitch / 2) + array = PA.PadLineArray(v_pad, pads_per_row, -pad_pitch, False, + pin1Pos) + array.SetFirstPadInArray(3*pads_per_row + 1) + array.AddPadsToModule(self.draw) + + lim_x = self.parameters["Package"]["package width"] / 2 + lim_y = self.parameters["Package"]["package height"] / 2 + inner = (row_len / 2) + pad_pitch + + #top left - diagonal + self.draw.Line(-lim_x, -inner, -inner, -lim_y) + # top right + self.draw.Polyline([(inner, -lim_y), (lim_x, -lim_y), (lim_x, -inner)]) + # bottom left + self.draw.Polyline([(-inner, lim_y), (-lim_x, lim_y), (-lim_x, inner)]) + # bottom right + self.draw.Polyline([(inner, lim_y), (lim_x, lim_y), (lim_x, inner)]) + + # Courtyard + cmargin = self.parameters["Package"]["courtyard margin"] + self.draw.SetLayer(pcbnew.F_CrtYd) + sizex = (lim_x + cmargin) * 2 + pad_length + sizey = (lim_y + cmargin) * 2 + pad_length + self.draw.Box(0, 0, sizex, sizey) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + text_offset = v_pitch / 2 + text_size + pad_length / 2 + + self.draw.Value(0, text_offset, text_size) + self.draw.Reference(0, -text_offset, text_size) + +QFPWizard().register() diff --git a/pcbnew/scripting/plugins/sdip_wizard.py b/pcbnew/scripting/plugins/sdip_wizard.py new file mode 100644 index 0000000..31f53cd --- /dev/null +++ b/pcbnew/scripting/plugins/sdip_wizard.py @@ -0,0 +1,237 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class RowedGridArray(PA.PadGridArray): + + def NamingFunction(self, x, y): + pad_cnt = self.nx*self.ny + + if self.ny == 1: + return x+1 + + if (y % 2) == 0: # upper row, count down + return pad_cnt-x + else: # lower row, count up + return x+1 + + +class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): + + pad_count_key = 'pas count' + row_count_key = 'row count' + row_spacing_key = 'row spacing' + pad_length_key = 'pad length' + pad_width_key = 'pad width' + pad_pitch_key = 'pad pitch' + + silkscreen_inside_key = 'silk screen inside' + outline_x_margin_key = 'outline x margin' + outline_y_margin_key = 'outline y margin' + + def GenerateParameterList(self): + # defaults for a DIP package + self.AddParam("Pads", self.pad_count_key, self.uNatural, 24) + self.AddParam("Pads", self.row_count_key, self.uNatural, 2) + + self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False) + self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5) + self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5) + + def CheckParameters(self): + self.CheckParamInt("Pads", '*' + self.row_count_key, min_value=1, max_value=2) + self.CheckParamInt( + "Pads", '*' + self.pad_count_key, + is_multiple_of=self.parameters["Pads"]['*' + self.row_count_key]) + + # can do this internally to parameter manager? + self.CheckParamBool("Body", '*' + self.silkscreen_inside_key) + + def BuildThisFootprint(self): + pads = self.parameters["Pads"] + body = self.parameters["Body"] + num_pads = pads['*' + self.pad_count_key] + pad_length = pads[self.pad_length_key] + pad_width = pads[self.pad_width_key] + row_pitch = pads[self.row_spacing_key] + pad_pitch = pads[self.pad_pitch_key] + num_rows = pads['*' + self.row_count_key] + + pads_per_row = num_pads // num_rows + + # add in the pads + pad = self.GetPad() + + array = RowedGridArray(pad, pads_per_row, num_rows, pad_pitch, row_pitch) + array.AddPadsToModule(self.draw) + + # draw the Silk Screen + Hsize = pad_pitch * (num_pads / num_rows - 1) + Vsize = row_pitch * (num_rows - 1) + pin1_posY = -Vsize / 2 + pin1_posX = -Hsize / 2 + + pad_length = pads[self.pad_length_key] + pad_width = pads[self.pad_width_key] + + ssx_offset = -pad_width / 2 - body[self.outline_x_margin_key] + ssy_offset = -pad_length / 2 - body[self.outline_y_margin_key] + + if body['*' + self.silkscreen_inside_key]: + ssy_offset *= -1 + + ssx = -pin1_posX - ssx_offset + ssy = -pin1_posY - ssy_offset + + self.DrawBox(ssx, ssy) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + + if num_rows == 1: + text_py = ssy + text_size + self.draw.Value(0, -text_py, text_size) + self.draw.Reference(0, text_py, text_size) + else: + text_px = ssx + text_size + # self.draw.Value(text_px, 0, text_size, orientation_degree=90) + self.draw.Value(0, 0, text_size) + self.draw.Reference(-text_px, 0, text_size, orientation_degree=90) + + +class SDIPWizard(RowedFootprint): + + def GetName(self): + return "S-DIP" + + def GetDescription(self): + return "Single/Dual Inline Package Footprint Wizard" + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 2.54) + self.AddParam("Pads", self.pad_width_key, self.uMM, 1.2) + self.AddParam("Pads", self.pad_length_key, self.uMM, 2) + self.AddParam("Pads", self.row_spacing_key, self.uMM, 7.52) + self.AddParam("Pads", "drill size", self.uMM, 0.8) + + def GetValue(self): + pads = self.parameters["Pads"] + rows = pads['*' + self.row_count_key] + pad_count = pads['*' + self.pad_count_key] + row_dist_mil = pcbnew.Iu2Mils(int(self.parameters["Pads"][self.row_spacing_key])) #int(self.parameters["Pads"][self.row_spacing_key] / 2.54 * 100) + pad_shape = "" + + if pads[self.pad_width_key] != pads[self.pad_length_key]: + pad_shape = '_ELL' + + if rows == 1: + name = "SIP" + return "%s-%d" % (name, pad_count) + + name = "DIP" + return "%s-%d_%d%s" % (name, pad_count, row_dist_mil, pad_shape) + + def GetPad(self): + pad_length = self.parameters["Pads"][self.pad_length_key] + pad_width = self.parameters["Pads"][self.pad_width_key] + drill = self.parameters["Pads"]["drill size"] + shape = pcbnew.PAD_SHAPE_CIRCLE + + if pad_length != pad_width: + shape = pcbnew.PAD_SHAPE_OVAL + + return PA.PadMaker(self.module).THPad( + pad_length, pad_width, drill, shape=shape) + + def DrawBox(self, ssx, ssy): + + if self.parameters["Pads"]['*' + self.row_count_key] == 2: + + # ---------- + # |8 7 6 5 | + # > | + # |1 2 3 4 | + # ---------- + + # draw the notch + notchWidth = ssy/1.5 + notchHeight = self.draw.GetLineTickness()*3 + + # NotchedBox draws the notch on top. Rotate the box 90 degrees + # to have it on the left + self.draw.NotchedBox(0, 0, ssy*2, ssx*2, notchWidth, notchHeight, -90) + else: + # ----------------- + # |1|2 3 4 5 6 7 8| + # ----------------- + self.draw.Box(0, 0, ssx*2, ssy*2) + + #line between pin1 and pin2 + pad_pitch = self.parameters["Pads"][self.pad_pitch_key] + pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key] + line_x = ( pad_cnt/2 - 1) * pad_pitch + self.draw.VLine(-line_x, -ssy, ssy * 2) + + return ssx, ssy + +SDIPWizard().register() + + +class SOICWizard(RowedFootprint): + + def GetName(self): + return "SOIC" + + def GetDescription(self): + return "SOIC, MSOP, SSOP, TSSOP, etc, footprint wizard" + + def GetValue(self): + pad_count = self.parameters["Pads"]['*' + self.pad_count_key] + return "%s-%d" % ("SOIC", pad_count) + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + #and override some of them + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27) + self.AddParam("Pads", self.pad_width_key, self.uMM, 0.6) + self.AddParam("Pads", self.pad_length_key, self.uMM, 2.2) + self.AddParam("Pads", self.row_spacing_key, self.uMM, 5.2) + + def GetPad(self): + pad_length = self.parameters["Pads"][self.pad_length_key] + pad_width = self.parameters["Pads"][self.pad_width_key] + return PA.PadMaker(self.module).SMDPad( + pad_length, pad_width, shape=pcbnew.PAD_SHAPE_RECT) + + def DrawBox(self, ssx, ssy): + + # ---------- + # |8 7 6 5 | + # |1 2 3 4 | + # \--------- + + self.draw.BoxWithDiagonalAtCorner(0, 0, ssx*2, ssy*2, pcbnew.FromMM(1)) + +SOICWizard().register() diff --git a/pcbnew/scripting/plugins/touch_slider_wizard.py b/pcbnew/scripting/plugins/touch_slider_wizard.py new file mode 100644 index 0000000..ee2ae53 --- /dev/null +++ b/pcbnew/scripting/plugins/touch_slider_wizard.py @@ -0,0 +1,205 @@ +#!/usr/bin/python + +# +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2012-2014 KiCad Developers, see change_log.txt for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# + +from pcbnew import * +import HelpfulFootprintWizardPlugin as HFPW + + +class TouchSliderWizard(HFPW.HelpfulFootprintWizardPlugin): + + def GetName(self): + """ + Return footprint name. + This is specific to each footprint class, you need to implement this + """ + return 'Touch Slider' + + def GetDescription(self): + """ + Return footprint description. + This is specific to each footprint class, you need to implement this + """ + return 'Capacitive Touch Slider wizard' + + def GetValue(self): + steps = int(self.parameters["Pads"]["*steps"]) + return "TS"+str(steps) + + def GenerateParameterList(self): + self.AddParam("Pads", "steps", self.uNatural, 4) + self.AddParam("Pads", "bands", self.uNatural, 2) + self.AddParam("Pads", "width", self.uMM, 10) + self.AddParam("Pads", "length", self.uMM, 50) + self.AddParam("Pads", "clearance", self.uMM, 1) + + # build a rectangular pad + def smdRectPad(self,module,size,pos,name): + pad = D_PAD(module) + pad.SetSize(size) + pad.SetShape(PAD_SHAPE_RECT) + pad.SetAttribute(PAD_ATTRIB_SMD) + pad.SetLayerSet(pad.ConnSMDMask()) + pad.SetPos0(pos) + pad.SetPosition(pos) + pad.SetPadName(name) + return pad + + + def smdTrianglePad(self,module,size,pos,name,up_down=1,left_right=0): + pad = D_PAD(module) + pad.SetSize(wxSize(size[0],size[1])) + pad.SetShape(PAD_SHAPE_TRAPEZOID) + pad.SetAttribute(PAD_ATTRIB_SMD) + pad.SetLayerSet(pad.ConnSMDMask()) + pad.SetPos0(pos) + pad.SetPosition(pos) + pad.SetPadName(name) + pad.SetDelta(wxSize(left_right*size[1],up_down*size[0])) + return pad + + + # This method checks the parameters provided to wizard and set errors + def CheckParameters(self): + prms = self.parameters["Pads"] + steps = prms["*steps"] + bands = prms["*bands"] + + if steps < 1: + self.parameter_errors["Pads"]["*steps"]="steps must be positive" + if bands < 1: + self.parameter_errors["Pads"]["*bands"]="bands must be positive" + + touch_width = prms["width"] + touch_length = prms["length"] + touch_clearance = prms["clearance"] + + # The start pad is made of a rectangular pad plus a couple of + # triangular pads facing tips on the middle/right of the first + # rectangular pad + def AddStartPad(self,position,touch_width,step_length,clearance,name): + module = self.module + step_length = step_length - clearance + size_pad = wxSize(step_length/2.0+(step_length/3),touch_width) + pad = self.smdRectPad(module,size_pad,position-wxPoint(step_length/6,0),name) + module.Add(pad) + + size_pad = wxSize(step_length/2.0,touch_width) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,size_pad[1]/4), + name) + module.Add(tp) + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,-size_pad[1]/4), + name + ,-1) + module.Add(tp) + + # compound a "start pad" shape plus a triangle on the left, pointing to + # the previous touch-pad + def AddMiddlePad(self,position,touch_width,step_length,clearance,name): + module = self.module + step_length = step_length - clearance + size_pad = wxSize(step_length/2.0,touch_width) + + size_pad = wxSize(step_length/2.0,touch_width) + pad = self.smdRectPad(module,size_pad,position,name) + module.Add(pad) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,size_pad[1]/4), + name) + module.Add(tp) + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(size_pad[0]/2,-size_pad[1]/4), + name + ,-1) + module.Add(tp) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(-size_pad[0],0), + name, + 0, + -1) + module.Add(tp) + + + def AddFinalPad(self,position,touch_width,step_length,clearance,name): + module = self.module + step_length = step_length - clearance + size_pad = wxSize(step_length/2.0,touch_width) + + pad = self.smdRectPad(module, + wxSize(size_pad[0]+(step_length/3),size_pad[1]), + position+wxPoint(step_length/6,0), + name) + module.Add(pad) + + tp = self.smdTrianglePad(module,wxSize(size_pad[0],size_pad[1]/2), + position+wxPoint(-size_pad[0],0), + name, + 0, + -1) + module.Add(tp) + + def AddStrip(self,pos,steps,touch_width,step_length,touch_clearance): + self.AddStartPad(pos,touch_width,step_length,touch_clearance,"1") + + for n in range(2,steps): + pos = pos + wxPoint(step_length,0) + self.AddMiddlePad(pos,touch_width,step_length,touch_clearance,str(n)) + + pos = pos + wxPoint(step_length,0) + self.AddFinalPad(pos,touch_width,step_length,touch_clearance,str(steps)) + + # build the footprint from parameters + # FIX ME: the X and Y position of the footprint can be better. + def BuildThisFootprint(self): + prm = self.parameters["Pads"] + steps = int(prm["*steps"]) + bands = int(prm["*bands"]) + touch_width = prm["width"] + touch_length = prm["length"] + touch_clearance = prm["clearance"] + + step_length = float(touch_length) / float(steps) + + t_size = self.GetTextSize() + w_text = self.draw.GetLineTickness() + ypos = touch_width/(bands*2) + t_size/2 + w_text + self.draw.Value(0, -ypos, t_size) + ypos += t_size + w_text*2 + self.draw.Reference(0, -ypos, t_size) + + # starting pad + pos = wxPointMM(0,0) + band_width = touch_width/bands + + for b in range(bands): + self.AddStrip(pos,steps,band_width,step_length,touch_clearance) + pos += wxPoint(0,band_width) + +TouchSliderWizard().register() + diff --git a/pcbnew/scripting/plugins/uss39_barcode.py b/pcbnew/scripting/plugins/uss39_barcode.py new file mode 100644 index 0000000..846a2bc --- /dev/null +++ b/pcbnew/scripting/plugins/uss39_barcode.py @@ -0,0 +1,149 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. + +from __future__ import division +import pcbnew as B + +import HelpfulFootprintWizardPlugin + +''' +Created on Jan 16, 2015 + +@author: ejohns +Rewritten by LordBlick +''' +ptd = { + '0': '000110100', '1': '100100001', '2': '001100001', '3': '101100000', + '4': '000110001', '5': '100110000', '6': '001110000', '7': '000100101', + '8': '100100100', '9': '001100100', 'A': '100001001', 'B': '001001001', + 'C': '101001000', 'D': '000011001', 'E': '100011000', 'F': '001011000', + 'G': '000001101', 'H': '100001100', 'I': '001001100', 'J': '000011100', + 'K': '100000011', 'L': '001000011', 'M': '101000010', 'N': '000010011', + 'O': '100010010', 'P': '001010010', 'Q': '000000111', 'R': '100000110', + 'S': '001000110', 'T': '000010110', 'U': '110000001', 'V': '011000001', + 'W': '111000000', 'X': '010010001', 'Y': '110010000', 'Z': '011010000', + '-': '010000101', '.': '110000100', ' ': '011000100', '*': '010010100', + '$': '010101000', '/': '010100010', '+': '010001010', '%': '000101010'} + +class Uss39: + def __init__(self, text): + self.Text = self.makePrintable(text) + + __str__ = lambda self: self.Text + makePrintable = lambda self, text: ''.join((c for c in text.upper() if ptd.has_key(c))) + + def getBarCodePattern(self, text = None): + text = text if not(text is None) else self.Text + # Reformated text with start and end characters + return reduce(lambda a1, a2: a1 + [0] + a2, [map(int, ptd[c]) for c in ("*%s*" % self.makePrintable(text))]) + +class Uss39Wizard(HelpfulFootprintWizardPlugin.HelpfulFootprintWizardPlugin): + GetName = lambda self: 'BARCODE USS-39' + GetDescription = lambda self: 'USS-39 Barcode' + GetReferencePrefix = lambda self: 'BARCODE' + GetValue = lambda self: self.module.Value().GetText() + + def GenerateParameterList(self): + # Silkscreen parameters + self.AddParam("Barcode", "Pixel Width", self.uMM, 0.20) + self.AddParam("Barcode", "Height", self.uMM, 3.0) + self.AddParam("Barcode", "Margin", self.uMM, 2.0) + self.AddParam("Barcode", "Contents", self.uString, 'BARCODE') + self.AddParam("Caption", "Enabled", self.uBool, True) + self.AddParam("Caption", "Height", self.uMM, 1.2) + self.AddParam("Caption", "Thickness", self.uMM, 0.12) + + def CheckParameters(self): + # Reset constants + self.CourtyardLineWidth = B.FromMM(0.05) + # Set bar height to the greater of 6.35mm or 0.15*L + # Set quiet width to 10*X + # User-defined parameters + # Create barcode object + self.Barcode = Uss39('=' + str(self.parameters['Barcode']['*Contents'])) + self.X = int(self.parameters['Barcode']['Pixel Width']) + self.module.Value().SetText( str(self.Barcode) ) + self.C = len(str(self.Barcode)) + # Inter-character gap + if self.X < 0.250: + self.I = B.FromMM(3.15) + else: + self.I = (2 * self.X) if (2*self.X) > B.FromMM(1.35) else B.FromMM(1.35) + # Wide to narrow ratio + if self.X >= B.FromMM(0.508): + self.N = B.FromMM(int((2.0+3.0)/2)) + else: + self.N = B.FromMM(int((2.2+3.0)/2)) + self.H = self.parameters['Barcode']['Height'] + self.Q = (10 * self.X) if (10 * self.X) > B.FromMM(6.35) else B.FromMM(6.35) + self.L = self.I * (1 + self.C) + (self.C + 2) * (6 * self.X + 3 * self.N * self.X) + 2 * self.Q + + + def __drawBar__(self, bit, x): + offset = (bit + 1) * self.X + return x + offset + + def __drawSpace__(self, bit, x): + self.draw.SetLayer(B.F_SilkS) + self.draw.SetLineTickness(self.X) + self.draw.Line(x, 0, x, self.H) + if (bit == 1): + self.draw.Line(x + self.X, 0, x + self.X, self.H) + self.draw.Line(x + self.X/2, 0, x + self.X/2, self.H) + self.draw.Line(x, 0, x + self.X, 0) + self.draw.Line(x, self.H, x + self.X, self.H) + offset = (bit + 1) * self.X + return x + offset + + def drawBars(self): + x = 0 + bars = self.Barcode.getBarCodePattern() + for index in range(0, len(bars), 2): + # Draw bar + barBit = bars[index] + x = self.__drawBar__(barBit, x) + # Draw space + if index < len(bars)-1: + spaceBit = bars[index + 1] + x = self.__drawSpace__(spaceBit, x) + return x + + def drawQuietZone(self, x0, y0, width, height): + self.draw.SetLayer(B.F_SilkS) + self.draw.SetLineTickness(self.X) + + for offset in range(0, int(self.Q), int(self.X/2)): + xoffset = offset + self.X + yoffset = offset + self.X/2 + self.draw.Line(x0 - xoffset, -yoffset, width + xoffset, -yoffset) + self.draw.Line(x0 - xoffset, self.H+yoffset, width + xoffset, self.H+yoffset) + self.draw.Line(x0 - xoffset, -yoffset, x0-xoffset, self.H+yoffset) + self.draw.Line(width + xoffset, -yoffset, width+xoffset, self.H+yoffset) + + def BuildThisFootprint(self): + # Draw bars + x = self.drawBars() + # Draw quiet zone + self.drawQuietZone(0, 0, x, self.H) + # Draw courtyard origin + self.draw.SetLayer(B.F_CrtYd) + self.draw.SetLineTickness(self.CourtyardLineWidth) + ch_lim = B.FromMM(0.35) + self.draw.Line(-ch_lim, 0, ch_lim, 0) + self.draw.Line(0, -ch_lim, 0, ch_lim) + self.draw.Circle(0, 0, B.FromMM(0.25)) + self.module.Value().SetLayer(B.F_Fab) + +Uss39Wizard().register() diff --git a/pcbnew/scripting/plugins/zip_wizard.py b/pcbnew/scripting/plugins/zip_wizard.py new file mode 100644 index 0000000..4e0f477 --- /dev/null +++ b/pcbnew/scripting/plugins/zip_wizard.py @@ -0,0 +1,201 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# + +from __future__ import division +import pcbnew + +import HelpfulFootprintWizardPlugin as HFPW +import PadArray as PA + + +class RowedFootprint(HFPW.HelpfulFootprintWizardPlugin): + + pad_count_key = '#pad count' + line_count_key = '#line count' + pad_vertical_size_key = 'pad vertical size' + pad_horizontal_size_key = 'pad horizontal size' + line_spacing_key = 'line spacing' + pad_pitch_key = 'pad pitch' + drill_size_key = 'drill size' + + courtyard_x_margin_key = 'courtyard x margin' + courtyard_y_margin_key = 'courtyard y margin' + outline_x_margin_key = 'outline x margin' + outline_y_margin_key = 'outline y margin' + silkscreen_inside_key = 'silk screen inside' + + def GenerateParameterList(self): + + # defaults for a ZIP package + self.AddParam("Pads", self.pad_count_key, self.uNatural, 24) + self.AddParam("Pads", self.line_count_key, self.uNatural, 2) + self.AddParam("Body", self.silkscreen_inside_key, self.uBool, False) + self.AddParam("Body", self.courtyard_x_margin_key, self.uMM, 1) + self.AddParam("Body", self.courtyard_y_margin_key, self.uMM, 1) + + def CheckParameters(self): + self.CheckParamInt("Pads", '*' + self.pad_count_key) + self.CheckParamInt("Pads", '*' + self.line_count_key) + + # can do this internally to parameter manager? + self.CheckParamBool("Body", '*' + self.silkscreen_inside_key) + + def BuildThisFootprint(self): + pads = self.parameters["Pads"] + body = self.parameters["Body"] + + pad_count = pads['*' + self.pad_count_key] + pad_Vsize = pads[self.pad_vertical_size_key] + pad_Hsize = pads[self.pad_horizontal_size_key] + line_pitch = pads[self.line_spacing_key] + pad_pitch = pads[self.pad_pitch_key] + line_count = pads['*' + self.line_count_key] + + if line_count == 1: + singleline = True + else: + singleline = False + + # add in the pads + pad = self.GetPad() + + array = PA.PadZGridArray(pad, pad_count, line_count, line_pitch, pad_pitch) + array.AddPadsToModule(self.draw) + + # draw the Silk Screen + pads_per_line = pad_count // line_count + row_length = pad_pitch * (pads_per_line - 1) # fenceposts + ssx_offset = pad_Hsize / 2 + body[self.outline_x_margin_key] + ssy_offset = pad_Vsize / 2 + body[self.outline_y_margin_key] + + pin1posX = pad_pitch * (pad_count - 1) / 2 + pin1posY = line_pitch * (line_count - 1) / 2 + leftx = pin1posX + ssx_offset + lowy = pin1posY + ssy_offset + + cornery = lowy + + # body inside pads is possible only for 2 rows. + # for other values, there is no room + linew = self.draw.GetLineTickness() + if body['*'+self.silkscreen_inside_key] and line_count == 2: + cornery = pin1posY - ssy_offset + if cornery < linew: + cornery = linew + + self.DrawBox(leftx*2, cornery*2) + + # Courtyard + cmarginx = body[self.courtyard_x_margin_key] + cmarginy = body[self.courtyard_y_margin_key] + self.draw.SetLayer(pcbnew.F_CrtYd) + sizex = (pin1posX + cmarginx) * 2 + pad_Hsize + sizey = (pin1posY + cmarginy) * 2 + pad_Vsize + self.draw.Box(0, 0, sizex, sizey) + + #reference and value + text_size = self.GetTextSize() # IPC nominal + t_posy = lowy + text_size + + self.draw.Value(0, t_posy, text_size) + self.draw.Reference(0, -t_posy, text_size) + + def DrawBox(self, sizex, sizey): + + # ---------- + # | 2 4 6 8| + # |1 3 5 7 | + # \--------- + setback = pcbnew.FromMM(1) + + if setback > sizey/2: + setback = sizey/2 + + self.draw.BoxWithDiagonalAtCorner(0, 0, sizex, sizey, setback, self.draw.flipY) + + +class ZIPWizard(RowedFootprint): + + def GetName(self): + return "ZIP" + + def GetDescription(self): + return "N lines Zip Package Footprint Wizard" + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 1.27) + self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 1.2) + self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 2) + self.AddParam("Pads", self.line_spacing_key, self.uMM, 2.54) + self.AddParam("Pads", self.drill_size_key, self.uMM, 0.8) + self.AddParam("Body", self.outline_x_margin_key, self.uMM, 1) + self.AddParam("Body", self.outline_y_margin_key, self.uMM, 0.5) + + def GetValue(self): + rows = self.parameters["Pads"]['*' + self.line_count_key] + pad_cnt = self.parameters["Pads"]['*' + self.pad_count_key] + + if rows == 1: + name = "SIP" + elif rows == 2: + name = "ZIP" + else: # triple and up aren't really a thing, but call it something! + name = "xIP" + + return "%s-%d" % (name, pad_cnt) + + def GetPad(self): + pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key] + pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key] + drill = self.parameters["Pads"][self.drill_size_key] + return PA.PadMaker(self.module).THPad( + pad_Vsize, pad_Hsize, drill, shape=pcbnew.PAD_SHAPE_OVAL) + +ZIPWizard().register() + + +class ZOICWizard(RowedFootprint): + + def GetName(self): + return "ZOIC" + + def GetDescription(self): + return "ZOIC, etc, Footprint Wizard" + + def GetValue(self): + return "%s-%d" % ("ZOIC", self.parameters["Pads"]['*' + self.pad_count_key]) + + def GenerateParameterList(self): + RowedFootprint.GenerateParameterList(self) + + #and override some of them + self.AddParam("Pads", self.pad_pitch_key, self.uMM, 0.6) + self.AddParam("Pads", self.pad_horizontal_size_key, self.uMM, 0.6) + self.AddParam("Pads", self.pad_vertical_size_key, self.uMM, 1.8) + self.AddParam("Pads", self.line_spacing_key, self.uMM, 5.2) + + self.AddParam("Body", self.outline_x_margin_key, self.uMM, 0.5) + self.AddParam("Body", self.outline_y_margin_key, self.uMM, 1) + + def GetPad(self): + pad_Vsize = self.parameters["Pads"][self.pad_vertical_size_key] + pad_Hsize = self.parameters["Pads"][self.pad_horizontal_size_key] + return PA.PadMaker(self.module).SMDPad( + pad_Vsize, pad_Hsize, shape=pcbnew.PAD_SHAPE_RECT) + +ZOICWizard().register() diff --git a/pcbnew/scripting/python_console_frame.h b/pcbnew/scripting/python_console_frame.h new file mode 100644 index 0000000..042e6b0 --- /dev/null +++ b/pcbnew/scripting/python_console_frame.h @@ -0,0 +1,105 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2004-2014 KiCad Developers, see change_log.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file python_console_frame.h + */ + +#ifndef PYTHON_CONSOLE_FRAME_H_ +#define PYTHON_CONSOLE_FRAME_H_ + +#if defined(KICAD_SCRIPTING) || defined(KICAD_SCRIPTING_WXPYTHON) +#include <python_scripting.h> +#endif + + +/** + * Class PYTHON_CONSOLE_FRAME is a simple derived class from wxMiniFrame + * to handle the scripting python console + */ +#define PC_STYLE wxCAPTION|wxCLOSE_BOX|wxRESIZE_BORDER + +class PYTHON_CONSOLE_FRAME : public wxMiniFrame +{ +private: + static wxSize m_frameSize; ///< The size of the frame, stored during a session + static wxPoint m_framePos; ///< The position of the frame, stored during a session + wxWindow * m_pythonPanel; ///< the window managed by the python shell + +public: + + PYTHON_CONSOLE_FRAME( wxWindow* aParent, const wxString& aFramenameId ) + : wxMiniFrame( aParent, wxID_ANY, wxT("Python console"), wxDefaultPosition, wxDefaultSize, + PC_STYLE | wxFRAME_FLOAT_ON_PARENT, aFramenameId ) + { + wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); + SetSizer( sizer ); + SetMinSize( wxSize( 500, 200 ) ); + +#if defined(KICAD_SCRIPTING_WXPYTHON) + m_pythonPanel = CreatePythonShellWindow( this ); + sizer->Add( m_pythonPanel, 1, wxEXPAND, 0 ); +#else + m_pythonPanel = NULL; +#endif + + if( m_frameSize.x <= 0 || m_frameSize.y <= 0 ) + SetSize( wxSize( 600, 300 ) ); + else + SetSize( m_frameSize ); + + if( m_framePos.x == 0 && m_framePos.y == 0 ) + Centre(); + else + SetPosition( m_framePos ); + + Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, + wxCloseEventHandler( PYTHON_CONSOLE_FRAME::OnClose ) ); + } + + ~PYTHON_CONSOLE_FRAME() + { + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, + wxCloseEventHandler( PYTHON_CONSOLE_FRAME::OnClose ) ); + } + +private: + + void OnClose( wxCloseEvent& event ) + { + if( !IsIconized() ) + { + m_frameSize = GetSize(); + m_framePos = GetPosition(); + } + + event.Skip(); + } +}; + +#endif // PYTHON_CONSOLE_FRAME_H_ diff --git a/pcbnew/scripting/tests/test1.py b/pcbnew/scripting/tests/test1.py new file mode 100644 index 0000000..b1f7cff --- /dev/null +++ b/pcbnew/scripting/tests/test1.py @@ -0,0 +1,11 @@ +import pcbnew + +pcb = pcbnew.GetBoard() + +for m in pcb.GetModules(): + print m.GetPosition() + for p in m.Pads(): + print "p=>",p.GetPosition(),p.GetPadName() + print p.GetPosition() + + diff --git a/pcbnew/scripting/tests/test2.py b/pcbnew/scripting/tests/test2.py new file mode 100644 index 0000000..e925f88 --- /dev/null +++ b/pcbnew/scripting/tests/test2.py @@ -0,0 +1,8 @@ +import pcbnew + +pcb = pcbnew.GetBoard() + +for m in pcb.GetModules(): + print m.GetReference(),"(",m.GetValue(),") at ", m.GetPosition() + for p in m.Pads(): + print " pad",p.GetPadName(), "at",p.GetPosition() diff --git a/pcbnew/scripting/units.i b/pcbnew/scripting/units.i new file mode 100644 index 0000000..56ea213 --- /dev/null +++ b/pcbnew/scripting/units.i @@ -0,0 +1,78 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es> + * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file units.i + * @brief unit conversion code + */ + +// Unit conversion, between internal units and mm or mils + +%pythoncode +{ + def ToMM(iu): + if type(iu) in [int,float]: + return float(iu) / float(IU_PER_MM) + elif type(iu) in [wxPoint,wxSize]: + return tuple(map(ToMM,iu)) + + def FromMM(mm): + if type(mm) in [int,float]: + return int(float(mm) * float(IU_PER_MM)) + elif type(mm) in [wxPoint,wxSize]: + return tuple(map(FromMM,mm)) + + def ToMils(iu): + if type(iu) in [int,float]: + return float(iu) / float(IU_PER_MILS) + elif type(iu) in [wxPoint,wxSize]: + return tuple(map(ToMils,iu)) + + def FromMils(mils): + if type(mils) in [int,float]: + return int(float(mils)*float(IU_PER_MILS)) + elif type(mils) in [wxPoint,wxSize]: + return tuple(map(FromMils,mils)) + + def wxSizeMM(mmx,mmy): return wxSize(FromMM(mmx),FromMM(mmy)) + def wxSizeMils(mmx,mmy): return wxSize(FromMils(mmx),FromMils(mmy)) + + def wxPointMM(mmx,mmy): return wxPoint(FromMM(mmx),FromMM(mmy)) + def wxPointMils(mmx,mmy): return wxPoint(FromMils(mmx),FromMils(mmy)) + + def wxRectMM(x,y,wx,wy): + x = int(FromMM(x)) + y = int(FromMM(y)) + wx = int(FromMM(wx)) + wy = int (FromMM(wy)) + return wxRect(x,y,wx,wy) + + def wxRectMils(x,y,wx,wy): + x = int(FromMils(x)) + y = int(FromMils(y)) + wx = int(FromMils(wx)) + wy = int (FromMils(wy)) + return wxRect(x,y,wx,wy) + +} |