def get_modules_bounding_box(modules): # get the pivot bounding box bounding_box = modules[0].mod.GetFootprintRect() top = bounding_box.GetTop() bottom = bounding_box.GetBottom() left = bounding_box.GetLeft() right = bounding_box.GetRight() for mod in modules: mod_box = mod.mod.GetFootprintRect() top = min(top, mod_box.GetTop()) bottom = max(bottom, mod_box.GetBottom()) left = min(left, mod_box.GetLeft()) right = max(right, mod_box.GetRight()) position = pcbnew.wxPoint(left, top) size = pcbnew.wxSize(right - left, bottom - top) bounding_box = pcbnew.EDA_RECT(position, size) return bounding_box
def getFootprintRect(module, upright=True): """ module.GetFootprintRect() includes edge items in calculating bounding rectangle instead of the just the pads. This function does the module rectangle using only the pads. By default it calculates the true footprint size of the module before it was rotated. """ area = PN.EDA_RECT() area.SetOrigin(module.GetPosition()) area.SetEnd(module.GetPosition()) rot_center = module.GetPosition() if upright: rot_angle = -(module.GetOrientation()) else: rot_angle = 0 for pad in module.Pads(): pad_bb = pad.GetBoundingBox().GetBoundingBoxRotated( rot_center, rot_angle) area.Merge(pad_bb) return area
def get_bounding_box(module_list): top = None bottom = None left = None right = None for mod in module_list: if top == None: bounding_box = mod.GetBoundingBox() top = bounding_box.GetTop() bottom = bounding_box.GetBottom() left = bounding_box.GetLeft() right = bounding_box.GetRight() else: mod_box = mod.GetBoundingBox() top = min(top, mod_box.GetTop()) bottom = max(bottom, mod_box.GetBottom()) left = min(left, mod_box.GetLeft()) right = max(right, mod_box.GetRight()) position = pcbnew.wxPoint(left, top) size = pcbnew.wxSize(right - left, bottom - top) bounding_box = pcbnew.EDA_RECT(position, size) return bounding_box
def get_bounding_box_of_modules(module_list): """ get bounding box encompasing all modules """ top = None bottom = None left = None right = None for mod in module_list: if top is None: bounding_box = mod.GetFootprintRect() top = bounding_box.GetTop() bottom = bounding_box.GetBottom() left = bounding_box.GetLeft() right = bounding_box.GetRight() else: mod_box = mod.GetFootprintRect() top = min(top, mod_box.GetTop()) bottom = max(bottom, mod_box.GetBottom()) left = min(left, mod_box.GetLeft()) right = max(right, mod_box.GetRight()) position = pcbnew.wxPoint(left, top) size = pcbnew.wxSize(right - left, bottom - top) bounding_box = pcbnew.EDA_RECT(position, size) return bounding_box
def checkTracks(self): ##Check vias collisions with all tracks #self.clearance = 0 #TBF self.clearance = self.boardObj.GetDesignSettings().GetDefault( ).GetClearance() #lboard = self.boardObj.ComputeBoundingBox(False) #origin = lboard.GetPosition() # Create an initial rectangle: all is set to "REASON_NO_SIGNAL" # get a margin to avoid out of range l_clearance = self.clearance + self.viaSize #+ self.size #wxLogDebug(str(l_clearance),True) #x_limit = int((lboard.GetWidth() + l_clearance) / l_clearance) + 1 #y_limit = int((lboard.GetHeight() + l_clearance) / l_clearance) + 1 viasToRemove = [] removed = False expansion = 2 # extra expansion to fix HitTest for track in self.boardObj.GetTracks(): #wx.LogMessage(str(self.viaPointsSafe)) #wx.LogMessage(str(pad.GetPosition())) #local_offset = max(pad.GetClearance(), self.clearance, max_target_area_clearance) + (self.size / 2) local_offset = max(track.GetClearance(), self.clearance) + (self.viaSize / 2) #wxLogDebug(str(max_size),True) #max_size = max(pad.GetSize().x, pad.GetSize().y) #start_x = int(floor(((pad.GetPosition().x - (max_size / 2.0 + local_offset)) - origin.x) / l_clearance)) #stop_x = int(ceil(((pad.GetPosition().x + (max_size / 2.0 + local_offset)) - origin.x) / l_clearance)) #start_y = int(floor(((pad.GetPosition().y - (max_size / 2.0 + local_offset)) - origin.y) / l_clearance)) #stop_y = int(ceil(((pad.GetPosition().y + (max_size / 2.0 + local_offset)) - origin.y) / l_clearance)) #for x in range(start_x, stop_x + 1): # for y in range(start_y, stop_y + 1): #wx.LogMessage(str(getTrackAngleRadians(track))) angle = abs(math.degrees(getTrackAngleRadians(track))) if (angle > 15 and angle < 75) or (angle > 105 and angle < 165) or ( angle > 195 and angle < 255) or (angle > 285 and angle < 345): expansion = 1.4 # extra expansion to fix HitTest #wx.LogMessage(str(angle)+'::'+str(expansion)) else: expansion = 2.0 # extra expansion to fix HitTest #wx.LogMessage(str(angle)+'::'+str(expansion)) for viaPos in self.viaPointsSafe: if 1: #try: #if isinstance(rectangle[x][y], ViaObject): #start_rect = wxPoint(origin.x + (l_clearance * x) - local_offset, # origin.y + (l_clearance * y) - local_offset) #start_rect = pcbnew.wxPoint(viaPos[0] + (l_clearance * viaPos[0]) - local_offset, # viaPos[1] + (l_clearance * viaPos[1]) - local_offset) start_rect = pcbnew.wxPoint( viaPos[0] - local_offset * expansion, viaPos[1] - local_offset * expansion) size_rect = pcbnew.wxSize(2 * expansion * local_offset, 2 * expansion * local_offset) wxLogDebug( str(pcbnew.ToMM(start_rect)) + '::' + str(pcbnew.ToMM(size_rect)), debug) #wxLogDebug(str(track.GetNetCode()),True) #wxLogDebug(str(self.viaNetId),True) #wxLogDebug(str(type(track)),True) if track.GetNetCode() != self.viaNetId or type( track) != pcbnew.TRACK: #PCB_VIA_T: #wxLogDebug('here',True) #if track.HitTest(pcbnew.EDA_RECT(start_rect, size_rect), False): aContained = False aAccuracy = 0 if track.HitTest( pcbnew.EDA_RECT(start_rect, size_rect), aContained, aAccuracy): #rectangle[x][y] = self.REASON_PAD wxLogDebug('Hit on Track: viaPos:' + str(viaPos), debug) #self.viaPointsSafe.pop(i) #self.viaPointsSafe.remove(viaPos) viasToRemove.append(viaPos) removed = True #else: # viaPSafe.append(viaPos) else: #except: wx.LogMessage("exception on Processing all tracks...") #i+=1 #self.viaPointSafe = viaPSafe #wx.LogMessage(str(viasToRemove)) newPoints = [p for p in self.viaPointsSafe if p not in viasToRemove] #wx.LogMessage(str(newPoints)) #wx.LogMessage(str(len(newPoints))) self.viaPointsSafe = newPoints return removed
def checkPads(self): ##Check vias collisions with all pads => all pads on all layers #wxPrint("Processing all pads...") #self.clearance = 0 #TBF self.clearance = self.boardObj.GetDesignSettings().GetDefault( ).GetClearance() #lboard = self.boardObj.ComputeBoundingBox(False) #origin = lboard.GetPosition() # Create an initial rectangle: all is set to "REASON_NO_SIGNAL" # get a margin to avoid out of range l_clearance = self.clearance + self.viaSize #+ self.size #x_limit = int((lboard.GetWidth() + l_clearance) / l_clearance) + 1 #y_limit = int((lboard.GetHeight() + l_clearance) / l_clearance) + 1 viasToRemove = [] removed = False expansion = 1.6 # extra expansion to fix HitTest for pad in self.boardObj.GetPads(): #wx.LogMessage(str(self.viaPointsSafe)) #wx.LogMessage(str(pad.GetPosition())) #local_offset = max(pad.GetClearance(), self.clearance, max_target_area_clearance) + (self.size / 2) local_offset = max(pad.GetClearance(), self.clearance) + (self.viaSize / 2) max_size = max(pad.GetSize().x, pad.GetSize().y) #start_x = int(floor(((pad.GetPosition().x - (max_size / 2.0 + local_offset)) - origin.x) / l_clearance)) #stop_x = int(ceil(((pad.GetPosition().x + (max_size / 2.0 + local_offset)) - origin.x) / l_clearance)) #start_y = int(floor(((pad.GetPosition().y - (max_size / 2.0 + local_offset)) - origin.y) / l_clearance)) #stop_y = int(ceil(((pad.GetPosition().y + (max_size / 2.0 + local_offset)) - origin.y) / l_clearance)) #for x in range(start_x, stop_x + 1): # for y in range(start_y, stop_y + 1): for viaPos in self.viaPointsSafe: if 1: #try: #if isinstance(rectangle[x][y], ViaObject): #start_rect = wxPoint(origin.x + (l_clearance * x) - local_offset, # origin.y + (l_clearance * y) - local_offset) #start_rect = pcbnew.wxPoint(viaPos[0] + (l_clearance * viaPos[0]) - local_offset, # viaPos[1] + (l_clearance * viaPos[1]) - local_offset) start_rect = pcbnew.wxPoint( viaPos[0] - local_offset * expansion, viaPos[1] - local_offset * expansion) size_rect = pcbnew.wxSize(2 * expansion * local_offset, 2 * expansion * local_offset) wxLogDebug( str(pcbnew.ToMM(start_rect)) + '::' + str(pcbnew.ToMM(size_rect)), debug) if pad.HitTest(pcbnew.EDA_RECT(start_rect, size_rect), False): #rectangle[x][y] = self.REASON_PAD wxLogDebug('Hit on Pad: viaPos:' + str(viaPos), debug) #self.viaPointsSafe.pop(i) #self.viaPointsSafe.remove(viaPos) viasToRemove.append(viaPos) removed = True #else: # viaPSafe.append(viaPos) else: #except: wx.LogMessage("exception on Processing all pads...") #i+=1 #self.viaPointSafe = viaPSafe #wx.LogMessage(str(viasToRemove)) newPoints = [p for p in self.viaPointsSafe if p not in viasToRemove] #wx.LogMessage(str(newPoints)) #wx.LogMessage(str(len(newPoints))) self.viaPointsSafe = newPoints return removed
def __init__(self, board, pivot_module_reference, only_within_boundingbox): """ initialize base object needed to replicate module layout, track layout and zone layout""" # take care of different APIs if hasattr(pcbnew, "LAYER_ID_COUNT"): pcbnew.PCB_LAYER_ID_COUNT = pcbnew.LAYER_ID_COUNT self.board = board self.only_within_bbox = only_within_boundingbox # load all modules self.modules = board.GetModules() # find pivodmodule self.pivot_mod = board.FindModuleByReference(pivot_module_reference) # find sheet ID on which the module is on self.pivot_sheet_id = get_sheet_id(self.pivot_mod) # while at it, get the pivot module ID self.pivot_mod_id = get_module_id(self.pivot_mod) # find all modules on the same sheet self.pivot_modules = [] self.pivot_modules_id = [] for mod in self.modules: module_id = get_module_id(mod) sheet_id = get_sheet_id(mod) if sheet_id == self.pivot_sheet_id: self.pivot_modules.append(mod) self.pivot_modules_id.append(module_id) # find all sheets to replicate self.sheets_to_clone = [] for mod in self.modules: module_id = get_module_id(mod) sheet_id = get_sheet_id(mod) if (module_id == self.pivot_mod_id) and (sheet_id != self.pivot_sheet_id) \ and (sheet_id not in self.sheets_to_clone): self.sheets_to_clone.append(sheet_id) pass # get bounding bounding box of all modules bounding_box = self.pivot_mod.GetBoundingBox() top = bounding_box.GetTop() bottom = bounding_box.GetBottom() left = bounding_box.GetLeft() right = bounding_box.GetRight() for mod in self.pivot_modules: mod_box = mod.GetBoundingBox() top = min(top, mod_box.GetTop()) bottom = max(bottom, mod_box.GetBottom()) left = min(left, mod_box.GetLeft()) right = max(right, mod_box.GetRight()) position = pcbnew.wxPoint(left, top) size = pcbnew.wxSize(right - left, bottom - top) self.pivot_bounding_box = pcbnew.EDA_RECT(position, size) # find all tracks within the pivot bounding box all_tracks = board.GetTracks() # keep only tracks that are within our bounding box self.pivot_tracks = [] for track in all_tracks: track_bb = track.GetBoundingBox() if only_within_boundingbox: if self.pivot_bounding_box.Contains(track_bb): self.pivot_tracks.append(track) else: if self.pivot_bounding_box.Intersects(track_bb): self.pivot_tracks.append(track) # get all zones all_zones = [] for zoneid in range(board.GetAreaCount()): all_zones.append(board.GetArea(zoneid)) # find all zones which are completely within the pivot bounding box self.pivot_zones = [] for zone in all_zones: zone_bb = zone.GetBoundingBox() if only_within_boundingbox: if self.pivot_bounding_box.Contains(zone_bb): self.pivot_zones.append(zone) else: if self.pivot_bounding_box.Intersects(zone_bb): self.pivot_zones.append(zone)
def clone(): """ Clone traces, zones, drawing, and location of equivalent components from selected components """ __helper = _helper() # PCB file specification sch_rootfile = '' #r"C:\Projects\svn\Cricket\ECG\Rev0\PCB_KiCad\ModECG.sch" sch_dir = '' sch_root = '' pcb_file = '' # clone Zone configuration # cloneZoneLoc help identify which clone zone amount many cloneZoneLayer = pcbnew.Cmts_User # one of pcbnew.LAYER_ID cloneZoneLoc = None # a pcbnew.wxPoint # Clone configuration cloneX = None cloneY = None cloneArrayXdim = None cloneArraydX = None cloneArraydY = None cloneHorMirror = None # True or False cloneVerMirror = None # True or False #if len(sys.argv)>=2: # sch_rootfile = sys.argv[1] #else: # s = raw_input("Please enter LIB_DIR [" + sch_rootfile + "] ") # if s: sch_rootfile = s if not sch_rootfile: board = pcbnew.GetBoard() cur_pcb_file = board.GetFileName() cur_pcb_file = cur_pcb_file.replace('/', os.sep) sch_rootfile = os.path.splitext(cur_pcb_file)[0] + '.sch' del board, cur_pcb_file else: sch_rootfile = sch_rootfile.replace('\\', os.sep) if not sch_dir: sch_dir = os.path.dirname(sch_rootfile) if not sch_root: sch_root = os.path.basename(sch_rootfile) if not pcb_file: pcb_file = os.path.splitext(sch_root)[0] + '.kicad_pcb' ToUnit = ToInch if pcbnew.GetAbbreviatedUnitsLabel() == u'in' else ToMM FromUnit = FromInch if pcbnew.GetAbbreviatedUnitsLabel( ) == u'in' else FromMM if cloneZoneLoc is not None: cloneZoneLoc = FromUnit(cloneZoneLoc) del sch_rootfile # # Figure out switch PCB file we going to work on, and load it if not # already loaded # board = pcbnew.GetBoard() cur_pcb_file = board.GetFileName() cur_pcb_file = cur_pcb_file.replace('/', os.sep) pcb_fullpath = sch_dir + os.sep + pcb_file if pcb_fullpath.lower() != cur_pcb_file.lower(): board = pcbnew.LoadBoard(pcb_file) else: pcb_file = os.path.basename(cur_pcb_file) sch_dir = os.path.dirname(cur_pcb_file) del pcb_fullpath, cur_pcb_file print 'This clone script will apply on', pcb_file # # Find source areas for cloning # print 'Finding Cmts.User Zones for clone source' srcZones = [] for i in range(0, board.GetAreaCount()): zone = board.GetArea(i) if zone.GetLayer() == cloneZoneLayer: srcZone = None if cloneZoneLoc is None: srcZone = zone elif zone.HitTestInsideZone(cloneZoneLoc): srcZone = zone if srcZone is not None: srcZones.append(srcZone) rect = srcZone.GetBoundingBox() print "Found source zone #" + str(len(srcZones)) \ , "within", ToUnit(rect.GetOrigin()) \ , "to", ToUnit(rect.GetEnd()) \ if len(srcZones) == 0: print "Can't find any source zone of %s Layer to clone" \ % pcbnew.BOARD_GetStandardLayerName(cloneZoneLayer) return if len(srcZones) > 1: i = int(raw_input("Please choose a zone #")) srcZone = srcZones[i] else: srcZone = srcZones[0] del srcZones srcRect = srcZone.GetBoundingBox() boardRect = board.ComputeBoundingBox(True) # Find components in srcRect and collect them into srcModules srcModules = {} # Dict of { Reference : pcbnew.MODULE } for module in board.GetModules(): if module.HitTest(srcRect): ref = module.GetReference() srcModules[ref] = module print "Found following %d components in clone zone:" % len(srcModules) print " " + ', '.join(srcModules.keys()) + "\n" # # Extract REFToPath from schematic for figure out equivalent component for replicate # print "Read schematic to find equivalent components for clone", sch_root eesch = eeschematic.schematic(sch_dir, True) eesch.LoadAllScheets(sch_root) #pp.pprint(eesch.GetSheets()) eesch.LinkSheets() #pp.pprint(eesch.GetSheets()) #eesch.GenREFToPathDict() #pp.pprint(eesch._REFToARPath) #pp.pprint(eesch._IDsToRefs) # # Figure out equivalent components # print "Figure out equivalent components and group them by channels" # Build AR Tree which contains equivalent REFs map arTree = eesch.BuildEqvRefsARTree( map(lambda x: x.encode(), srcModules.keys())) #pp.pprint(arTree._tree) # Group eqv REFs into channels channels = arTree.groupByChannel(srcModules.keys()) # Show WARNING to user if there is any for arPath, warnMsg in channels['WARN'].iteritems(): print "*** WARN ***:", eesch.convertARPathToUserPath(arPath), warnMsg channels = channels['MAP'] # Ask user what channel to clone ==> cloneChs (list of string) if len(channels) == 0: print "*** ERROR *** Cannot find equivalent components for cloning" # TODO: Get a customized REFtoREF map from user some how cloneChs = [] elif len(channels) > 1: channel_names = channels.keys() channel_names = map(lambda x: [x, eesch.convertARPathToUserPath(x)], channel_names) channel_names.sort(key=lambda x: x[1]) print "Found", len(channel_names), "channels: " for ch_idx, ch_name in enumerate(channel_names): print " ", ch_idx, " -- ", ch_name[1] tmp = __helper.getInputList( "Enter set of channels will be cloned [all channels if empty]: ") if tmp: cloneChs = [] for ch in tmp.split(' '): if ch: cloneChs.append(channel_names[int(ch)][0]) else: cloneChs = map(lambda x: x[0], channel_names) else: cloneChs = channels.keys() # ASK USER WHICH COMPONENT CAN BE USE AS CLONE ORIGIN ==> RefOfOrigin, # ModuleOfOrigin # (string) RefOfOrigin = raw_input( "Which component can be use as clone origin? [None]") if RefOfOrigin.lower() in ('none'): RefOfOrigin = '' ModuleOfOrigin = None else: RefOfOrigin = RefOfOrigin.decode() if not RefOfOrigin in srcModules: print "*** ERROR *** Cannot find " + RefOfOrigin + " in list of components for cloning" RefOfOrigin = '' ModuleOfOrigin = None return ModuleOfOrigin = board.FindModuleByReference(RefOfOrigin) cloneX = srcRect.GetPosition().x cloneY = srcRect.GetPosition().y if not RefOfOrigin: # ASK USER HOW TO PUT CLONE IN TO ARRAY ==> cloneArrayXdim, # cloneArraydX, cloneArraydY (int) ask = set() if cloneArrayXdim is None: ask.add('Xdim') cloneArrayXdim = round(math.sqrt(len(cloneChs))) if cloneArraydX is None: ask.add('dY') cloneArraydX = srcRect.GetWidth() + FromInch(0.1) if cloneArraydY is None: ask.add('dX') cloneArraydY = srcRect.GetHeight() + FromInch(0.1) if len(cloneChs) > 1: if 'Xdim' in ask: tmp = __helper.getInputList( "Enter number of clones in X direction [%d]:" \ % (cloneArrayXdim)) if tmp: cloneArrayXdim = int(tmp) tmp = ['', ''] if cloneArrayXdim < len(cloneChs): if cloneArrayXdim > 1: #Ask for X and Y spacing between clone if ('dX' in ask) or ('dY' in ask): tmp = __helper.getInputList( "X and Y spacing between clones [%f, %f]: " \ % (ToUnit(cloneArraydX), ToUnit(cloneArraydY)) ) tmp = tmp.split(' ') + ['', ''] else: #Ask for Y spacing between clone if ('dY' in ask): tmp = __helper.getInputList( "Y spacing between clones [%f]: " \ % (ToUnit(cloneArraydY)) ) tmp = ['', tmp] else: if cloneArrayXdim > 1: # Ask for X spacing between clone if ('dX' in ask): tmp = __helper.getInputList( "X spacing between clones [%f]: " % (ToUnit(cloneArraydX))) tmp = [tmp, ''] if tmp[0]: cloneArraydX = FromUnit(float(tmp[0])) if tmp[1]: cloneArraydY = FromUnit(float(tmp[1])) # ASK USER CLONE START LOCATION ==> cloneX, cloneY (int) ask = False if cloneX is None: ask = True cloneX = srcRect.GetPosition().x + cloneArraydX if cloneY is None: ask = True cloneY = srcRect.GetPosition().y if ask: tmp = __helper.getInputList("Clone start point (%f, %f): " % (ToUnit(cloneX), ToUnit(cloneY))) tmp = tmp.split(' ') + ['', ''] if tmp[0]: cloneX = FromUnit(float(tmp[0])) if tmp[1]: cloneY = FromUnit(float(tmp[1])) if (cloneVerMirror is None) or (cloneHorMirror is None): tmp = raw_input("Ver/Hor/Dia Mirror? (Ver, Hor, Dia, [None]) :") if not tmp: tmp = 'no' tmp2 = tmp.lower() if tmp2 in ('no', 'none'): cloneVerMirror = False cloneHorMirror = False elif tmp2 in ('ver', 'vertical'): cloneHorMirror = False cloneVerMirror = True elif tmp2 in ('hor', 'horizontal'): cloneHorMirror = True cloneVerMirror = False elif tmp2 in ('dia', 'diagonal'): cloneVerMirror = True cloneHorMirror = True else: print "*** ERROR *** Not recognized answer of", tmp return # ASK USER TO CLEANUP THE CLONING AREAS ==> CleanUp (True/False) CleanUp = raw_input( "Do you want to cleanup the clone target areas? (Yes/[No]/Cleanup Only) " ).lower() NoClone = False if not CleanUp: CleanUp = False elif CleanUp in ('no'): CleanUp = False elif CleanUp in ("yes"): CleanUp = True elif CleanUp in ("cleanup only"): CleanUp = True NoClone = True else: print "*** ERROR *** Not recognized answer of", CleanUp CleanUp = False return # Collecting tracks that belong clone zone tracks = board.GetTracks() allTracks = [] srcTracks = [] for track in tracks: if track.HitTest(srcRect): srcTracks.append(track) else: allTracks.append(track) print "Found %d track in clone zone" % len(srcTracks) # Collecting Zones that belong clone zone srcZoneIdx = board.GetAreaIndex(srcZone) allZones = [] srcZones = [] for i in range(0, board.GetAreaCount()): if i != srcZoneIdx: zone = board.GetArea(i) if zone != srcZone: if zone.HitTest(srcRect): srcZones.append(zone) else: allZones.append(zone) print "Found %d zones in clone zone" % len(srcZones) # Collecting Drawings that belong clone zone srcDrawings = [] allDraws = [] for drawing in board.GetDrawings(): if drawing.HitTest(srcRect): srcDrawings.append(drawing) else: allDraws.append(drawing) print "Found %d drawings in clone zone" % len(srcDrawings) # # Start to do cloning work # if not RefOfOrigin: curCloneX = cloneX - srcRect.GetPosition().x curCloneY = cloneY - cloneArraydY - srcRect.GetPosition().y srcSize = pcbnew.wxSize(srcRect.GetWidth(), srcRect.GetHeight()) srcSize90Deg = pcbnew.wxSize(srcRect.GetHeight(), srcRect.GetWidth()) #CloneLayers = set([pcbnew.B_Cu, pcbnew.F_Cu]) xCnt = 0 wxPointX0Y0 = pcbnew.wxPoint(0, 0) for cloneCh in cloneChs: print "Cloning channel", eesch.convertARPathToUserPath(cloneCh) REFtoREF = channels[cloneCh] # Figure out the clone origin if RefOfOrigin: # Using RefOfOrigin if supplied by user # cloneRefOfOrigin = REFtoREF.get(RefOfOrigin, None) if cloneRefOfOrigin is None: print " *** WARN *** Cannot find equivalent origin reference in channel", cloneCh continue cloneModuleOfOrigin = board.FindModuleByReference(cloneRefOfOrigin) rotation = round(cloneModuleOfOrigin.GetOrientation() - ModuleOfOrigin.GetOrientation()) while (rotation < -1800): rotation += 3600 while (rotation > +1800): rotation -= 3600 cloneRotOrigin = cloneModuleOfOrigin.GetPosition() curCloneOfs = cloneRotOrigin - ModuleOfOrigin.GetPosition() else: # Using CloneX and CloneY supplied by user # if xCnt == 0: xCnt = 0 curCloneX = cloneX - srcRect.GetPosition().x curCloneY += cloneArraydY else: curCloneX += cloneArraydX curCloneOfs = pcbnew.wxPoint(curCloneX, curCloneY) rotation = 0 cloneRotOrigin = srcRect.GetPosition() #EndOf if RefOfOrigin if CleanUp: cloneRect = pcbnew.EDA_RECT(srcRect.GetPosition() + curCloneOfs, srcSize) if rotation != 0: cloneRect = cloneRect.GetBoundingBoxRotated( cloneRotOrigin, rotation) # Clean the Traces in cloning target for idx in range(len(allTracks) - 1, -1, -1): track = allTracks[idx] if track.HitTest(cloneRect): #if track.GetLayer() in CloneLayers: allTracks.pop(idx) tracks.Remove(track) # Clean zones that belong cloning target for idx in range(len(allZones) - 1, -1, -1): zone = allZones[idx] if zone.HitTest(cloneRect): allZones.pop(idx) board.Delete(zone) # Clean drawings that belong cloning target for idx in range(len(allDraws) - 1, -1, -1): drawing = allDraws[idx] if drawing.HitTest(cloneRect): allDraws.pop(idx) board.Delete(drawing) #Endof if CleanUp if not NoClone: # Cloning the components print " Moved Eqv. Modules" pairs = [] for ref, module in srcModules.iteritems(): cloneRef = REFtoREF.get(ref, None) if cloneRef is None: print " *** WARN *** %s don't have equivalent components. Skip clone" \ % ref continue cloneModule = board.FindModuleByReference(cloneRef) if cloneModule is None: print " *** ERROR *** Cannot find module with reference of", cloneRef continue if cloneHorMirror: if cloneVerMirror: __helper.cloneComponentDiaMirror( module, cloneModule, curCloneOfs, srcRect) else: __helper.cloneComponentHorMirror( module, cloneModule, curCloneOfs, srcRect) elif cloneVerMirror: __helper.cloneComponentVerMirror(module, cloneModule, curCloneOfs, srcRect) else: __helper.cloneComponentNormal(module, cloneModule, curCloneOfs, rotation, cloneRotOrigin) pairs.append([module, cloneModule]) eqvNets = equivalentNetlist(pairs) # Cloning the Traces print " Clone traces" for track in srcTracks: cloneNetCode = eqvNets.getEqvNetCode(track.GetNetCode(), track.GetShortNetname()) if cloneNetCode is not None: #if track.GetLayer() in CloneLayers: cloneTrack = track.Duplicate() cloneTrack.SetNetCode(cloneNetCode) if cloneHorMirror: if cloneVerMirror: __helper.cloneTrackDiaMirror( cloneTrack, curCloneOfs, srcRect) else: __helper.cloneTrackHorMirror( cloneTrack, curCloneOfs, srcRect) elif cloneVerMirror: __helper.cloneTrackVerMirror(cloneTrack, curCloneOfs, srcRect) else: __helper.cloneTrackNormal(cloneTrack, curCloneOfs, rotation, cloneRotOrigin) tracks.Append(cloneTrack) # Cloning the drawing print " Clone drawings" for drawing in srcDrawings: cloneDrawing = drawing.Duplicate() cloneDrawing.Move(curCloneOfs) board.Add(cloneDrawing) # Cloning the Zones print " Clone zones" curPairsIdx = len(pairs) for zone in srcZones: # Figure out what is appropriate eqv netname for copper filled zone if zone.IsOnCopperLayer(): cloneNetCode = eqvNets.getEqvNetCode( zone.GetNetCode(), zone.GetShortNetname()) if cloneNetCode is not None: cloneZone = zone.Duplicate() cloneZone.SetNetCode(cloneNetCode) else: continue else: cloneZone = zone.Duplicate() if cloneHorMirror: if cloneVerMirror: __helper.cloneZoneDiaMirror(cloneZone, curCloneOfs, srcRect) else: __helper.cloneZoneHorMirror(cloneZone, curCloneOfs, srcRect) elif cloneVerMirror: __helper.cloneZoneVerMirror(cloneZone, curCloneOfs, srcRect) else: __helper.cloneZoneNormal(cloneZone, curCloneOfs, rotation, cloneRotOrigin) board.Add(cloneZone) #Endof if not NoClone # Advancing to next clone location xCnt += 1 if xCnt >= cloneArrayXdim: xCnt = 0
def __init__(self, board, pivot_module_reference): """ initialize base object needed to replicate module layout, track layout and zone layout""" # take care of different APIs if hasattr(pcbnew, "LAYER_ID_COUNT"): pcbnew.PCB_LAYER_ID_COUNT = pcbnew.LAYER_ID_COUNT self.board = board # load all modules self.modules = board.GetModules() # find pivodmodule self.pivot_mod = board.FindModuleByReference(pivot_module_reference) # find sheet ID on which the module is on self.pivot_sheet_id = get_sheet_id(self.pivot_mod) # while at it, get the pivot module ID self.pivot_mod_id = get_module_id(self.pivot_mod) # find all modules on the same sheet self.pivot_modules = [] self.pivot_modules_id = [] self.pivot_modules_ref = [] for mod in self.modules: module_id = get_module_id(mod) sheet_id = get_sheet_id(mod) if sheet_id == self.pivot_sheet_id: self.pivot_modules.append(mod) self.pivot_modules_id.append(module_id) self.pivot_modules_ref.append(mod.GetReference()) # find all local nets other_modules = [] other_modules_ref = [] for mod in self.modules: if mod.GetReference() not in self.pivot_modules_ref: other_modules.append(mod) other_modules_ref.append(mod.GetReference()) other_nets = self.get_nets_from_modules(other_modules) pivot_nets = self.get_nets_from_modules(self.pivot_modules) self.pivot_local_nets = [] for net in pivot_nets: if net not in other_nets: self.pivot_local_nets.append(net) # find all sheets to replicate self.sheets_to_clone = [] for mod in self.modules: module_id = get_module_id(mod) sheet_id = get_sheet_id(mod) if (module_id == self.pivot_mod_id) and (sheet_id != self.pivot_sheet_id) \ and (sheet_id not in self.sheets_to_clone): self.sheets_to_clone.append(sheet_id) pass # get bounding bounding box of all modules bounding_box = self.pivot_mod.GetFootprintRect() top = bounding_box.GetTop() bottom = bounding_box.GetBottom() left = bounding_box.GetLeft() right = bounding_box.GetRight() for mod in self.pivot_modules: mod_box = mod.GetFootprintRect() top = min(top, mod_box.GetTop()) bottom = max(bottom, mod_box.GetBottom()) left = min(left, mod_box.GetLeft()) right = max(right, mod_box.GetRight()) position = pcbnew.wxPoint(left, top) size = pcbnew.wxSize(right - left, bottom - top) self.pivot_bounding_box = pcbnew.EDA_RECT(position, size) # get corner points for the pivot bunding box - might need them for proper rotated bounding box # top_left = (self.pivot_bounding_box.GetLeft(), self.pivot_bounding_box.GetTop()) top_right = (self.pivot_bounding_box.GetRight(), self.pivot_bounding_box.GetTop()) bottom_right = (self.pivot_bounding_box.GetRight(), self.pivot_bounding_box.GetBottom()) bottom_left = (self.pivot_bounding_box.GetLeft(), self.pivot_bounding_box.GetBottom()) self.pivot_bounding_box_corners = (top_left, top_right, bottom_right, bottom_left) # get radius for polar replication middle = (right + left)/2 self.polar_center = (middle, bottom) # get minimal radius - used by GUI to autofill in case of polar replication # could be improved with proper formulae, this is just a rough estimae number_of_all_sheets = 1 + len(self.sheets_to_clone) width_of_sheet = (right - left) / SCALE circumference = number_of_all_sheets * width_of_sheet self.minimum_radius = circumference / (2 * math.pi) self.minimum_angle = 360.0 / number_of_all_sheets # get minimal width - GUI assumes horizontal replication self.minimum_width = (right - left) / SCALE self.pivot_tracks = [] self.pivot_zones = [] self.only_within_bbox = False