640 lines
33 KiB
Python
Executable File
640 lines
33 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# Implementation of the action plugin derived from pcbnew.ActionPlugin
|
|
import pcbnew
|
|
import os
|
|
import sys
|
|
import re
|
|
import time
|
|
import json
|
|
import math
|
|
import wx
|
|
import uuid
|
|
|
|
from collections import OrderedDict
|
|
from .viafence import *
|
|
from .viafence_dialogs import *
|
|
|
|
debug = False
|
|
temporary_fix = True
|
|
|
|
def wxLogDebug(msg,show):
|
|
"""printing messages only if show is omitted or True"""
|
|
if show:
|
|
wx.LogMessage(msg)
|
|
#
|
|
def getTrackAngleRadians(track):
|
|
#return math.degrees(math.atan2((p1.y-p2.y),(p1.x-p2.x)))
|
|
return (math.atan2((track.GetEnd().y - track.GetStart().y), (track.GetEnd().x - track.GetStart().x)))
|
|
#
|
|
|
|
def distance (p1,p2):
|
|
return math.hypot(p1.y-p2.y,p1.x-p2.x)
|
|
|
|
class ViaFenceAction(pcbnew.ActionPlugin):
|
|
# ActionPlugin descriptive information
|
|
def defaults(self):
|
|
self.name = "Via Fence Generator\nversion 2.8"
|
|
self.category = "Modify PCB"
|
|
self.description = "Add a via fence to nets or tracks on the board"
|
|
self.icon_file_name = os.path.join(os.path.dirname(__file__), "resources/fencing-vias.png")
|
|
self.show_toolbar_button = True
|
|
|
|
def dumpJSON(self, file):
|
|
dict = {
|
|
'pathList': self.pathList,
|
|
'viaOffset': self.viaOffset,
|
|
'viaPitch': self.viaPitch,
|
|
'viaPoints': self.viaPoints if hasattr(self, 'viaPoints') else []
|
|
}
|
|
with open(file, 'w') as file:
|
|
json.dump(dict, file, indent=4, sort_keys=True)
|
|
|
|
# Return an ordered {layerId: layerName} dict of enabled layers
|
|
def getLayerMap(self):
|
|
layerMap = []
|
|
for i in list(range(pcbnew.PCB_LAYER_ID_COUNT)):
|
|
if self.boardObj.IsLayerEnabled(i):
|
|
layerMap += [[i, self.boardObj.GetLayerName(i)]]
|
|
return OrderedDict(layerMap)
|
|
|
|
# Return an ordered {netCode: netName} dict of nets in the board
|
|
def getNetMap(self):
|
|
netMap = OrderedDict(self.boardObj.GetNetsByNetcode())
|
|
netMap.pop(0) # TODO: What is Net 0?
|
|
return netMap
|
|
|
|
# Generates a list of net filter phrases using the local netMap
|
|
# Currently all nets are included as filter phrases
|
|
# Additionally, differential Nets get a special filter phrase
|
|
def createNetFilterSuggestions(self):
|
|
netFilterList = ['*']
|
|
netList = [self.netMap[item].GetNetname() for item in self.netMap]
|
|
diffMap = {'+': '-', 'P': 'N', '-': '+', 'N': 'P'}
|
|
regexMap = {'+': '[+-]', '-': '[+-]', 'P': '[PN]', 'N': '[PN]'}
|
|
invertDiffNet = lambda netName : netName[0:-1] + diffMap[netName[-1]]
|
|
isDiffNet = lambda netName : True if netName[-1] in diffMap.keys() else False
|
|
|
|
# Translate board nets into a filter list
|
|
for netName in netList:
|
|
if isDiffNet(netName) and invertDiffNet(netName) in netList:
|
|
# If we have a +/- or P/N pair, we insert a regex entry once into the filter list
|
|
filterText = netName[0:-1] + regexMap[netName[-1]]
|
|
if (filterText not in netFilterList): netFilterList += [filterText]
|
|
|
|
# Append every net to the filter list
|
|
netFilterList += [netName]
|
|
|
|
return netFilterList
|
|
|
|
# Generates a RegEx string from a SimpleEx (which is a proprietary invention ;-))
|
|
# The SimpleEx only supports [...] with single chars and * used as a wildcard
|
|
def regExFromSimpleEx(self, simpleEx):
|
|
# Escape the entire filter string. Unescape and remap specific characters that we want to allow
|
|
subsTable = {r'\[':'[', r'\]':']', r'\*':'.*'}
|
|
regEx = re.escape(simpleEx)
|
|
for subsFrom, subsTo in subsTable.items(): regEx = regEx.replace(subsFrom, subsTo)
|
|
return regEx
|
|
|
|
def createVias(self, viaPoints, viaDrill, viaSize, netCode):
|
|
newVias = []
|
|
for viaPoint in viaPoints:
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')):
|
|
newVia = pcbnew.PCB_VIA(self.boardObj)
|
|
else:
|
|
newVia = pcbnew.VIA(self.boardObj)
|
|
if hasattr(newVia, 'SetTimeStamp'):
|
|
ts = 55
|
|
newVia.SetTimeStamp(ts) # adding a unique number as timestamp to mark this via as generated by this script
|
|
self.boardObj.Add(newVia)
|
|
|
|
newVia.SetPosition(pcbnew.wxPoint(viaPoint[0], viaPoint[1]))
|
|
newVia.SetWidth(viaSize)
|
|
newVia.SetDrill(viaDrill)
|
|
if hasattr(pcbnew, 'VIA_THROUGH'):
|
|
newVia.SetViaType(pcbnew.VIA_THROUGH)
|
|
else:
|
|
newVia.SetViaType(pcbnew.VIATYPE_THROUGH)
|
|
newVia.SetNetCode(netCode)
|
|
newVias += [newVia]
|
|
|
|
return newVias
|
|
|
|
def onDeleteClick(self, event):
|
|
return self.mainDlg.EndModal(wx.ID_DELETE)
|
|
|
|
def checkPads(self):
|
|
##Check vias collisions with all pads => all pads on all layers
|
|
#wxPrint("Processing all pads...")
|
|
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')) and temporary_fix:
|
|
self.clearance = 0 #TBF
|
|
else:
|
|
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)
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')) and temporary_fix:
|
|
pad_clr = 0 # FIXME
|
|
local_offset = max(pad_clr, self.clearance) + (self.viaSize / 2)
|
|
else:
|
|
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 checkTracks(self):
|
|
##Check vias collisions with all tracks
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')) and temporary_fix:
|
|
self.clearance = 0 #TBF
|
|
else:
|
|
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)
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')) and temporary_fix:
|
|
trk_clr = 0 # FIXME
|
|
#trk_clr = track.GetClearance()
|
|
local_offset = max(trk_clr, self.clearance) + (self.viaSize / 2)
|
|
else:
|
|
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 (hasattr(pcbnew,'DRAWSEGMENT')):
|
|
trk_type = pcbnew.TRACK
|
|
else:
|
|
trk_type = pcbnew.PCB_TRACK
|
|
if track.GetNetCode() != self.viaNetId or type(track) != trk_type: #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 DoKeyPress(self, event):
|
|
if event.GetKeyCode() == wx.WXK_RETURN:
|
|
self.mainDlg.EndModal(wx.ID_OK)
|
|
else:
|
|
event.Skip()
|
|
|
|
def selfToMainDialog(self):
|
|
self.mainDlg.lstLayer.SetItems(list(self.layerMap.values())) #maui
|
|
self.mainDlg.lstLayer.SetSelection(self.layerId)
|
|
self.mainDlg.txtNetFilter.SetItems(self.netFilterList)
|
|
self.mainDlg.txtNetFilter.SetSelection(self.netFilterList.index(self.netFilter))
|
|
self.mainDlg.txtViaOffset.SetValue(str(pcbnew.ToMM(self.viaOffset)))
|
|
self.mainDlg.txtViaPitch.SetValue(str(pcbnew.ToMM(self.viaPitch)))
|
|
self.mainDlg.txtViaDrill.SetValue(str(pcbnew.ToMM(self.viaDrill)))
|
|
self.mainDlg.txtViaSize.SetValue(str(pcbnew.ToMM(self.viaSize)))
|
|
self.mainDlg.txtViaOffset.Bind(wx.EVT_KEY_DOWN, self.DoKeyPress)
|
|
#self.mainDlg.txtViaOffset.Bind(wx.EVT_TEXT_ENTER, self.mainDlg.EndModal(wx.ID_OK))
|
|
self.mainDlg.txtViaPitch.Bind(wx.EVT_KEY_DOWN, self.DoKeyPress)
|
|
self.mainDlg.txtViaDrill.Bind(wx.EVT_KEY_DOWN, self.DoKeyPress)
|
|
self.mainDlg.txtViaSize.Bind(wx.EVT_KEY_DOWN, self.DoKeyPress)
|
|
|
|
self.mainDlg.lstViaNet.SetItems([item.GetNetname() for item in self.netMap.values()])
|
|
for i, item in enumerate (self.netMap.values()):
|
|
if self.mainDlg.lstViaNet.GetString(i) in ["GND", "/GND"]:
|
|
self.mainDlg.lstViaNet.SetSelection(i)
|
|
break
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')) and temporary_fix: #temporary_fix!!!
|
|
self.mainDlg.chkNetFilter.Enable (False)
|
|
self.mainDlg.chkNetFilter.SetValue(self.isNetFilterChecked)
|
|
self.mainDlg.txtNetFilter.Enable(self.isNetFilterChecked)
|
|
self.mainDlg.chkLayer.SetValue(self.isLayerChecked)
|
|
self.mainDlg.lstLayer.Enable(self.isLayerChecked)
|
|
self.mainDlg.chkIncludeDrawing.SetValue(self.isIncludeDrawingChecked)
|
|
self.mainDlg.chkIncludeSelection.SetValue(self.isIncludeSelectionChecked)
|
|
self.mainDlg.chkDebugDump.SetValue(self.isDebugDumpChecked)
|
|
self.mainDlg.chkRemoveViasWithClearanceViolation.SetValue(self.isRemoveViasWithClearanceViolationChecked)
|
|
self.mainDlg.chkSameNetZoneViasOnly.SetValue(self.isSameNetZoneViasOnlyChecked)
|
|
self.mainDlg.m_buttonDelete.Bind(wx.EVT_BUTTON, self.onDeleteClick)
|
|
# hiding unimplemented controls
|
|
#self.mainDlg.chkRemoveViasWithClearanceViolation.Hide()
|
|
self.mainDlg.chkSameNetZoneViasOnly.Hide()
|
|
|
|
def mainDialogToSelf(self):
|
|
self.netFilter = self.mainDlg.txtNetFilter.GetValue()
|
|
if len(list(self.layerMap.keys())) > 0:
|
|
self.layerId = list(self.layerMap.keys())[self.mainDlg.lstLayer.GetSelection()] #maui
|
|
self.viaOffset = pcbnew.FromMM(float(self.mainDlg.txtViaOffset.GetValue().replace(',','.')))
|
|
self.viaPitch = pcbnew.FromMM(float(self.mainDlg.txtViaPitch.GetValue().replace(',','.')))
|
|
self.viaDrill = pcbnew.FromMM(float(self.mainDlg.txtViaDrill.GetValue().replace(',','.')))
|
|
self.viaSize = pcbnew.FromMM(float(self.mainDlg.txtViaSize.GetValue().replace(',','.')))
|
|
if len(list(self.netMap.keys())) > 0:
|
|
self.viaNetId = list(self.netMap.keys())[self.mainDlg.lstViaNet.GetSelection()] #maui
|
|
self.isNetFilterChecked = self.mainDlg.chkNetFilter.GetValue()
|
|
self.isLayerChecked = self.mainDlg.chkLayer.GetValue()
|
|
self.isIncludeDrawingChecked = self.mainDlg.chkIncludeDrawing.GetValue()
|
|
self.isIncludeSelectionChecked = self.mainDlg.chkIncludeSelection.GetValue()
|
|
self.isDebugDumpChecked = self.mainDlg.chkDebugDump.GetValue()
|
|
self.isSameNetZoneViasOnlyChecked = self.mainDlg.chkSameNetZoneViasOnly.GetValue()
|
|
self.isRemoveViasWithClearanceViolationChecked = self.mainDlg.chkRemoveViasWithClearanceViolation.GetValue()
|
|
|
|
def Run(self):
|
|
#check for pyclipper lib
|
|
pyclip = False
|
|
try:
|
|
import pyclipper
|
|
pyclip = True
|
|
# import pyclipper; pyclipper.__file__
|
|
except:
|
|
#error exception if pyclipper lib is missing
|
|
import sys, os
|
|
from sys import platform as _platform
|
|
if sys.version_info.major == 2 and sys.version_info.minor == 7:
|
|
if _platform == "linux" or _platform == "linux2":
|
|
# linux
|
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),'python-pyclipper','py2-7-linux-64'))
|
|
elif _platform == "darwin":
|
|
#osx
|
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),'python-pyclipper','py2-7-mac-64'))
|
|
else:
|
|
#win
|
|
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),'python-pyclipper','py2-7-win-64'))
|
|
try:
|
|
import pyclipper
|
|
pyclip = True
|
|
except:
|
|
msg = u"\u2718 ERROR Missing KiCAD \'pyclipper\' python module:\nplease install it using pip\nin your KiCAD python environment.\n[You may need administrative rights]"
|
|
wdlg = wx.MessageDialog(None, msg,'ERROR message',wx.OK | wx.ICON_WARNING)# wx.ICON_ERROR)
|
|
result = wdlg.ShowModal()
|
|
if pyclip:
|
|
#import pyclipper
|
|
import os
|
|
#self.mainDlg = MainDialog(None)
|
|
#self.selfToMainDialog()
|
|
|
|
self.boardObj = pcbnew.GetBoard()
|
|
self.boardDesignSettingsObj = self.boardObj.GetDesignSettings()
|
|
self.boardPath = os.path.dirname(os.path.realpath(self.boardObj.GetFileName()))
|
|
self.layerMap = self.getLayerMap()
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')) and temporary_fix:
|
|
self.highlightedNetId = -1
|
|
# Nasty work around... GetHighLightNetCodes returns a swig object >_<
|
|
# hl_nets = filter(lambda x: x.IsSelected(), self.boardObj.GetTracks())
|
|
# hl_net_codes = set(net.GetNetCode() for net in hl_nets)
|
|
# if len(hl_net_codes) == 1:
|
|
# self.highlightedNetId = hl_net_codes.pop()
|
|
# else:
|
|
# wdlg = wx.MessageDialog(None, u"\u2718 ERROR Please only select one net",'ERROR message',wx.OK | wx.ICON_WARNING)# wx.ICON_ERROR)
|
|
# result = wdlg.ShowModal()
|
|
# return
|
|
else:
|
|
self.highlightedNetId = self.boardObj.GetHighLightNetCode()
|
|
self.netMap = self.getNetMap()
|
|
self.netFilterList = self.createNetFilterSuggestions()
|
|
self.netFilter = self.netMap[self.highlightedNetId].GetNetname() if self.highlightedNetId != -1 else self.netFilterList[0]
|
|
self.viaSize = self.boardDesignSettingsObj.GetCurrentViaSize()
|
|
self.layerId = 0 #TODO: How to get currently selected layer?
|
|
self.viaDrill = self.boardDesignSettingsObj.GetCurrentViaDrill()
|
|
self.viaPitch = pcbnew.FromMM(1.3)
|
|
self.viaOffset = pcbnew.FromMM(1.3)
|
|
self.viaNetId = 0 #TODO: Maybe a better init value here. Try to find "GND" maybe?
|
|
self.isNetFilterChecked = 1 if self.highlightedNetId != -1 else 0
|
|
#if len(list(self.netMap.keys())) > 0:
|
|
# self.viaNetId = list(self.netMap.keys())[self.mainDlg.lstViaNet.GetSelection()] #maui
|
|
|
|
self.isLayerChecked = 0
|
|
self.isIncludeDrawingChecked = 0
|
|
self.isIncludeSelectionChecked = 1
|
|
self.isDebugDumpChecked = 0
|
|
self.isRemoveViasWithClearanceViolationChecked = 1
|
|
self.isSameNetZoneViasOnlyChecked = 0
|
|
|
|
self.mainDlg = MainDialog(None)
|
|
self.selfToMainDialog()
|
|
if hasattr(self.boardObj, 'm_Uuid'):
|
|
self.mainDlg.m_buttonDelete.Disable()
|
|
self.mainDlg.m_buttonDelete.SetToolTip( u"fencing vias are placed in a group,\nto delete fencing vias, just delete the group" )
|
|
reply = self.mainDlg.ShowModal()
|
|
if (reply == wx.ID_OK):
|
|
# User pressed OK.
|
|
# Assemble a list of pcbnew.BOARD_ITEMs derived objects that support GetStart/GetEnd and IsOnLayer
|
|
self.mainDialogToSelf()
|
|
lineObjects = []
|
|
arcObjects = []
|
|
|
|
# creating a unique group for vias
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')): #creating a group of fencing vias
|
|
groupName = uuid.uuid4() #randomword(5)
|
|
pcb_group = pcbnew.PCB_GROUP(None)
|
|
pcb_group.SetName(groupName)
|
|
self.boardObj.Add(pcb_group)
|
|
|
|
|
|
# Do we want to include net tracks?
|
|
if (self.isNetFilterChecked):
|
|
# Find nets that match the generated regular expression and add their tracks to the list
|
|
netRegex = self.regExFromSimpleEx(self.netFilter)
|
|
for netId in self.netMap:
|
|
if re.match(netRegex, self.netMap[netId].GetNetname()):
|
|
for trackObject in self.boardObj.TracksInNet(netId):
|
|
#wx.LogMessage('type '+str(trackObject.GetStart()))
|
|
#wx.LogMessage('type '+str(trackObject.GetMid()))
|
|
if (hasattr(pcbnew,'DRAWSEGMENT')):
|
|
trk_type = pcbnew.TRACK
|
|
else:
|
|
trk_type = pcbnew.PCB_TRACK
|
|
trk_arc = pcbnew.PCB_ARC
|
|
if hasattr(trackObject,'GetMid()'):
|
|
arcObjects += [trackObject]
|
|
elif type(trackObject) is trk_type:
|
|
lineObjects += [trackObject]
|
|
|
|
# wx.LogMessage('net selected '+str(lineObjects))
|
|
# wx.LogMessage('net selected '+str(arcObjects))
|
|
|
|
# Do we want to include drawing segments?
|
|
if (self.isIncludeDrawingChecked):
|
|
boardItem = self.boardObj.GetDrawings().GetFirst()
|
|
while boardItem is not None:
|
|
if pcbnew.DRAWSEGMENT.ClassOf(boardItem):
|
|
# A drawing segment (not a text or something else)
|
|
drawingObject = boardItem.Cast()
|
|
if drawingObject.GetShape() == pcbnew.S_SEGMENT:
|
|
# A straight line
|
|
lineObjects += [drawingObject]
|
|
|
|
boardItem = boardItem.Next()
|
|
|
|
# Do we want to include track segments?
|
|
if (self.isIncludeSelectionChecked):
|
|
if (hasattr(pcbnew,'DRAWSEGMENT')):
|
|
trk_type = pcbnew.TRACK
|
|
else:
|
|
trk_type = pcbnew.PCB_TRACK
|
|
trk_arc = pcbnew.PCB_ARC
|
|
for item in self.boardObj.GetTracks():
|
|
#wx.LogMessage('type track: %s' % str(type(item)))
|
|
if not (hasattr(pcbnew,'DRAWSEGMENT')):
|
|
if type(item) is trk_arc and item.IsSelected():
|
|
wx.LogMessage('type track: %s' % str(type(item)))
|
|
arcObjects += [item]
|
|
if type(item) is trk_type and item.IsSelected():
|
|
lineObjects += [item]
|
|
|
|
# Do we want to filter the generated lines by layer?
|
|
if (self.isLayerChecked):
|
|
# Filter by layer
|
|
# TODO: Make layer selection also a regex
|
|
lineObjects = [lineObject for lineObject in lineObjects if lineObject.IsOnLayer(self.layerId)]
|
|
arcObjects = [arcObject for arcObject in arcObjects if arcObject.IsOnLayer(self.layerId)]
|
|
#wx.LogMessage('arcs: %s' % str(arcObjects))
|
|
|
|
for arc in arcObjects:
|
|
segNBR = 16
|
|
start = arc.GetStart()
|
|
end = arc.GetEnd()
|
|
md = arc.GetMid()
|
|
width = arc.GetWidth()
|
|
layer = arc.GetLayerSet()
|
|
netName = None
|
|
cnt, rad = getCircleCenterRadius(start,end,md)
|
|
pts = create_round_pts(start,end,cnt,rad,layer,width,netName,segNBR)
|
|
self.pathListArcs = [[ [p.x, p.y],
|
|
[pts[i+1].x, pts[i+1].y] ]
|
|
for i,p in enumerate(pts[:-1])]
|
|
# Generate via fence
|
|
try:
|
|
viaPointsArcs = []
|
|
if len (arcObjects) > 0:
|
|
viaPointsArcs = generateViaFence(self.pathListArcs, self.viaOffset, self.viaPitch)
|
|
viaObjListArcs = self.createVias(viaPointsArcs, self.viaDrill, self.viaSize, self.viaNetId)
|
|
for v in viaObjListArcs:
|
|
pcb_group.AddItem(v)
|
|
except Exception as exc:
|
|
wx.LogMessage('exception on via fence generation: %s' % str(exc))
|
|
import traceback
|
|
wx.LogMessage(traceback.format_exc())
|
|
viaPoints = []
|
|
#wx.LogMessage('pL: %s' % str(self.pathListArcs))
|
|
#wx.LogMessage('pts: %s' % str(pts))
|
|
|
|
# Generate a path list from the pcbnew.BOARD_ITEM objects
|
|
self.pathList = [[ [lineObject.GetStart()[0], lineObject.GetStart()[1]],
|
|
[lineObject.GetEnd()[0], lineObject.GetEnd()[1]] ]
|
|
for lineObject in lineObjects]
|
|
#wx.LogMessage('pL: %s' % str(self.pathList))
|
|
|
|
|
|
# Generate via fence
|
|
try:
|
|
viaPointsArcs = []
|
|
viaPoints = generateViaFence(self.pathList, self.viaOffset, self.viaPitch)
|
|
#if len (arcObjects) > 0:
|
|
# viaPointsArcs = generateViaFence(self.pathListArcs, self.viaOffset, self.viaPitch)
|
|
except Exception as exc:
|
|
wx.LogMessage('exception on via fence generation: %s' % str(exc))
|
|
import traceback
|
|
wx.LogMessage(traceback.format_exc())
|
|
viaPoints = []
|
|
|
|
if (self.isDebugDumpChecked):
|
|
self.viaPoints = viaPoints
|
|
self.dumpJSON(os.path.join(self.boardPath, time.strftime("viafence-%Y%m%d-%H%M%S.json")))
|
|
|
|
removed = False
|
|
if (self.isRemoveViasWithClearanceViolationChecked):
|
|
#if self.mainDlg.chkRemoveViasWithClearanceViolation.GetValue():
|
|
# Remove Vias that violate clearance to other things
|
|
# Check against other tracks
|
|
#wx.LogMessage('hereIam')
|
|
# removing generated & colliding vias
|
|
viaPointsSafe = []
|
|
for i,v in enumerate(viaPoints):
|
|
#clearance = v.GetClearance()
|
|
collision_found = False
|
|
tolerance = 1 + 0.2
|
|
# This should be handled with Net Clearance
|
|
for j, vn in enumerate(viaPoints[i+1:]):
|
|
if distance (pcbnew.wxPoint(v[0], v[1]),pcbnew.wxPoint(vn[0], vn[1])) < int(self.viaSize*tolerance): # +clearance viasize+20%:
|
|
collision_found = True
|
|
if not collision_found:
|
|
viaPointsSafe.append(v)
|
|
self.viaPointsSafe = viaPointsSafe
|
|
#wx.LogMessage(str(len(self.viaPointsSafe)))
|
|
removed = self.checkPads()
|
|
remvd = self.checkTracks()
|
|
removed = removed or remvd
|
|
else:
|
|
self.viaPointsSafe = viaPoints
|
|
#wx.LogMessage(str(len(self.viaPointsSafe)))
|
|
#self.checkPads()
|
|
#wx.LogMessage(str(len(self.viaPointsSafe)))
|
|
viaObjList = self.createVias(self.viaPointsSafe, self.viaDrill, self.viaSize, self.viaNetId)
|
|
# viaObjListArcs = []
|
|
# if len (arcObjects) > 0:
|
|
# viaObjListArcs = self.createVias(viaPointsArcs, self.viaDrill, self.viaSize, self.viaNetId)
|
|
if not(hasattr(pcbnew,'DRAWSEGMENT')): #creating a group of fencing vias
|
|
# groupName = uuid.uuid4() #randomword(5)
|
|
# pcb_group = pcbnew.PCB_GROUP(None)
|
|
# pcb_group.SetName(groupName)
|
|
# self.boardObj.Add(pcb_group)
|
|
for v in viaObjList:
|
|
pcb_group.AddItem(v)
|
|
#for v in viaObjListArcs:
|
|
# pcb_group.AddItem(v)
|
|
via_nbr = len(self.viaPointsSafe)
|
|
via_nbr += len(viaPointsArcs)
|
|
msg = u'Placed {0:} Fencing Vias.\n\u26A0 Please run a DRC check on your board.'.format(str(via_nbr))
|
|
if removed:
|
|
msg += u'\n\u281B Removed DRC colliding vias.'
|
|
wx.LogMessage(msg)
|
|
#viaObjList = self.createVias(viaPoints, self.viaDrill, self.viaSize, self.viaNetId)
|
|
#via_nbr = len(viaPoints)
|
|
|
|
|
|
elif (reply == wx.ID_DELETE):
|
|
#user clicked ('Delete Fence Vias')
|
|
target_tracks = filter(lambda x: ((x.Type() == pcbnew.PCB_VIA_T)and (x.GetTimeStamp() == 55)), self.boardObj.GetTracks())
|
|
#wx.LogMessage(str(len(target_tracks)))
|
|
target_tracks_cp = list(target_tracks)
|
|
l = len (target_tracks_cp)
|
|
for i in range(l):
|
|
#if type(target_tracks_cp[i]) is PCB_TRACK and target_tracks_cp[i].IsSelected(): #item.GetNetname() == net_name:
|
|
self.boardObj.RemoveNative(target_tracks_cp[i]) #removing via
|
|
#for via in target_tracks:
|
|
# #if via.GetTimeStamp() == 55:
|
|
# self.boardObj.RemoveNative(via)
|
|
# #wx.LogMessage('removing via')
|
|
#pcbnew.Refresh()
|
|
self.mainDlg.Destroy() #the Dlg needs to be destroyed to release pcbnew
|
|
|
|
# TODO: Implement
|
|
# if (self.isRemoveViasWithClearanceViolationChecked):
|
|
# # Remove Vias that violate clearance to other things
|
|
# # Check against other tracks
|
|
# for viaObj in viaObjList:
|
|
# for track in self.boardObj.GetTracks():
|
|
# clearance = track.GetClearance(viaObj)
|
|
# if track.HitTest(False, clearance):
|
|
# self.boardObj.RemoveNative(viaObj)
|
|
|
|
# TODO: Implement
|
|
# if (self.isSameNetZoneViasOnlyChecked):
|
|
# # Keep via only if it is in a filled zone with the same net
|
|
|
|
# import numpy as np
|
|
# import matplotlib.pyplot as plt
|
|
|
|
# for path in self.pathList:
|
|
# plt.plot(np.array(path).T[0], np.array(path).T[1], linewidth=2)
|
|
# for via in viaPoints:
|
|
# plt.plot(via[0], via[1], 'o', markersize=10)
|
|
|
|
|
|
# plt.ylim(plt.ylim()[::-1])
|
|
# plt.axes().set_aspect('equal','box')
|
|
# plt.show()
|
|
|