add bt_player

This commit is contained in:
2021-11-24 17:00:32 +01:00
parent 3bc6987c3b
commit 880e936dc3
92 changed files with 16476 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
# pcbnew loads this folder as a package using import
# thus __init__.py (this file) is executed
# We import the plugin class here and register it to pcbnew
from .viafence_action import ViaFenceAction
ViaFenceAction().register()

View File

@@ -0,0 +1,120 @@
from .viafence import *
from .viafence_dialogs import *
import os
import argparse
import json
import numpy as np
import matplotlib.pyplot as plt
import wx
import copy
argParser = argparse.ArgumentParser()
argParser.add_argument("--dialog", dest="dialog", metavar="DIALOGNAME", help="Show Dialog with <DIALOGNAME>")
argParser.add_argument("--runtests", dest="runtests", action="store_true", default=0, help="Execute testing all json test files in 'tests' subdirectory")
argParser.add_argument("--test", dest="test", metavar="TESTNAME", help="Loads <TESTNAME> from 'tests' directory, runs it and shows/stores the result into the test file")
argParser.add_argument("--store", dest="store", action="store_true", default=0, help="When running a test, stores the result as known-good")
argParser.add_argument("--verbose", dest="verbose", action="store_true", default=0, help="Verbose plotting the inner workings of the algorithm")
args = argParser.parse_args()
def compareTests(testDict, refDict):
testPts = testDict['viaPoints']
refPts = refDict['viaPoints']
matchedPts = [point for point in testPts if point in refPts]
return True if len(testPts) == len(refPts) == len(matchedPts) else False
def loadTest(testFilename):
with open(testFilename, 'r') as file:
return json.load(file)
def storeTest(testFilename, testDict):
with open(testFilename, 'w') as file:
json.dump(testDict, file, indent=4, sort_keys=True)
def runTest(testDict, verboseFunc):
viaOffset = testDict['viaOffset']
viaPitch = testDict['viaPitch']
pathList = testDict['pathList']
newDict = copy.deepcopy(testDict)
newDict['viaPoints'] = generateViaFence(pathList, viaOffset, viaPitch, verboseFunc)
return newDict
def printTestResult(testName, refDict, testDict):
print("{}: {} (Ref/Test Vias: {}/{})".format(
testName, "PASSED" if compareTests(refDict, testDict) else "FAILED",
len(refDict['viaPoints']), len(testDict['viaPoints']) ))
def verbosePlot(object, isPoints = False, isPaths = False, isPolygons = False):
import numpy as np
import matplotlib.pyplot as plt
for child in object:
data = np.array(child)
if isPolygons:
plt.fill(data.T[0], data.T[1], facecolor='grey', alpha=0.3, linestyle='--', linewidth=1)
elif isPaths:
plt.plot(data.T[0], data.T[1], linestyle='-', linewidth=3)
elif isPoints:
plt.plot(data.T[0], data.T[1], linestyle='', marker='x', markersize=10, mew=3)
def main():
testDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'tests')
verboseFunc = verbosePlot if args.verbose else lambda *args,**kwargs:None
if (args.dialog):
# Load and show dialog
# os.chdir(os.path.dirname(os.path.realpath(__file__)))
app = wx.App()
className = globals()[args.dialog]
className(None).Show()
print("Starting wxApp Now. Exit using Ctrl+C")
app.MainLoop()
elif (args.test):
# Load a test file, run the algorithm and show/store the result for later testing
testFile = os.path.join(testDir, args.test) + ".json"
ref = loadTest(testFile)
test = runTest(ref, verboseFunc)
printTestResult(args.test, ref, test)
if (args.store): storeTest(testFile, test)
for path in test['pathList']:
plt.plot(np.array(path).T[0], np.array(path).T[1], linewidth=5)
for via in test['viaPoints']:
plt.plot(via[0], via[1], 'o', markersize=10)
plt.axes().set_aspect('equal','box')
plt.ylim(plt.ylim()[::-1])
plt.savefig(os.path.join(testDir, args.test) + '.png')
plt.show()
elif (args.runtests):
# Run all tests in 'tests' subdirectory
scriptDir = os.path.dirname(os.path.realpath(__file__))
testDir = scriptDir + "/" + 'tests'
testsPassed = 0
testsTotal = 0
for file in os.listdir(testDir):
if file.endswith(".json"):
testName = os.path.basename(file)
ref = loadTest(os.path.join(testDir, file))
test = runTest(ref, verboseFunc)
printTestResult(testName, ref, test)
if compareTests(ref, test): testsPassed += 1
testsTotal += 1
print("----\n{}/{} tests PASSED".format(testsPassed, testsTotal))
assert testsPassed == testsTotal
if testsPassed == testsTotal: exit(0)
else: exit(1)
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="27.733334"
width="27.733334"
version="1.1"
id="svg2"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="shielding-vias.svg"
viewBox="0 0 26 26"
inkscape:export-filename="C:\Users\userC\AppData\Roaming\kicad\scripting\plugins\via_fence_generator\shielding-vias.png"
inkscape:export-xdpi="99.096771"
inkscape:export-ydpi="99.096771">
<metadata
id="metadata40">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1361"
id="namedview38"
showgrid="true"
inkscape:snap-to-guides="false"
inkscape:snap-grids="true"
inkscape:zoom="14.553175"
inkscape:cx="10.66825"
inkscape:cy="11.459509"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid3017"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5"
spacingy="0.5"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs4">
<filter
id="c"
height="1.3651"
width="1.2097"
y="-0.18257"
x="-0.10484"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
stdDeviation="1.5978799"
id="feGaussianBlur7" />
</filter>
<filter
id="d"
height="1.4696"
width="1.4809999"
y="-0.23481999"
x="-0.24049"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
stdDeviation="1.5978799"
id="feGaussianBlur10" />
</filter>
</defs>
<rect
style="fill:#006400;fill-opacity:1;stroke:none;stroke-width:0.9375;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.99997282;stroke-opacity:1"
id="rect4495"
width="26"
height="26"
x="-1.7193647e-008"
y="1.7193647e-008" />
<g
transform="matrix(1.6382539,0,0,1.5572263,1.2572207,0.36314149)"
id="g16"
inkscape:export-xdpi="82.105263"
inkscape:export-ydpi="82.105263">
<rect
height="16"
width="16"
y="0"
x="0"
id="rect18"
style="fill-opacity:0" />
</g>
<g
id="g4745"
transform="translate(0,6.625)">
<path
sodipodi:nodetypes="sssss"
inkscape:export-ydpi="82.105263"
inkscape:export-xdpi="82.105263"
inkscape:connector-curvature="0"
id="path3867-9-4"
style="opacity:1;fill:none;stroke:#ffcc00;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,0.37500002 C 10,2.307997 8.432997,3.8750001 6.5,3.8750001 4.567003,3.8750001 3,2.307997 3,0.37500002 3,-1.557997 4.567003,-3.125 6.5,-3.125 c 1.932997,0 3.5,1.567003 3.5,3.50000002 z" />
<path
sodipodi:nodetypes="sssss"
inkscape:export-ydpi="82.105263"
inkscape:export-xdpi="82.105263"
inkscape:connector-curvature="0"
id="path3867-9-4-7"
style="opacity:0.25;fill:none;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 10,0.37499992 C 10,2.3079969 8.4329971,3.8750001 6.5000001,3.8750001 4.5670031,3.8750001 3,2.3079969 3,0.37499992 3,-1.5579971 4.5670031,-3.1250001 6.5000001,-3.1250001 8.4329971,-3.1250001 10,-1.5579971 10,0.37499992 Z" />
</g>
<g
id="g4753">
<g
transform="translate(2.5,2.875)"
id="g4544">
<path
d="m 11.5,8 h 12 m -12,0 c -7.6666667,12 -3.8333333,6 0,0 z"
style="opacity:1;fill:none;stroke:#ffcc00;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3867"
inkscape:connector-curvature="0"
inkscape:export-xdpi="82.105263"
inkscape:export-ydpi="82.105263"
sodipodi:nodetypes="cccc" />
<path
d="m -2.5,15 h 9"
style="opacity:1;fill:none;stroke:#ffcc00;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3867-0"
inkscape:connector-curvature="0"
inkscape:export-xdpi="82.105263"
inkscape:export-ydpi="82.105263"
sodipodi:nodetypes="cc" />
</g>
<g
style="opacity:0.25;stroke:#ffffff"
transform="translate(2.5,2.8749995)"
id="g4544-5">
<path
d="m 11.5,8 h 12 m -12,0 c -7.6666667,12 -3.8333333,6 0,0 z"
style="opacity:1;fill:none;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3867-07"
inkscape:connector-curvature="0"
inkscape:export-xdpi="82.105263"
inkscape:export-ydpi="82.105263"
sodipodi:nodetypes="cccc" />
<path
d="m -2.5,15 h 9"
style="opacity:1;fill:none;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3867-0-3"
inkscape:connector-curvature="0"
inkscape:export-xdpi="82.105263"
inkscape:export-ydpi="82.105263"
sodipodi:nodetypes="cc" />
</g>
</g>
<g
id="g4741"
transform="translate(1,1)">
<path
sodipodi:nodetypes="sssss"
inkscape:export-ydpi="82.105263"
inkscape:export-xdpi="82.105263"
inkscape:connector-curvature="0"
id="path3867-9"
style="opacity:1;fill:none;stroke:#ffcc00;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 22,18 c 0,1.932997 -1.567003,3.5 -3.5,3.5 -1.932997,0 -3.5,-1.567003 -3.5,-3.5 0,-1.932997 1.567003,-3.5 3.5,-3.5 1.932997,0 3.5,1.567003 3.5,3.5 z" />
<path
sodipodi:nodetypes="sssss"
inkscape:export-ydpi="82.105263"
inkscape:export-xdpi="82.105263"
inkscape:connector-curvature="0"
id="path3867-9-1"
style="opacity:0.25;fill:none;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 22,18 c 0,1.932997 -1.567003,3.5 -3.5,3.5 -1.932997,0 -3.5,-1.567003 -3.5,-3.5 0,-1.932997 1.567003,-3.5 3.5,-3.5 1.932997,0 3.5,1.567003 3.5,3.5 z" />
</g>
<rect
style="opacity:0.25;fill:#ffffff;fill-opacity:0.85398233;stroke:#b3b3b3;stroke-width:2.30681324;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.99997282;stroke-opacity:1"
id="rect4737"
width="26.755686"
height="26.755686"
x="-0.37869588"
y="-0.37784326" />
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,536 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24mm"
height="40mm"
viewBox="0 0 23.999999 40.000001"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="viafence.svg"
inkscape:export-filename="viafence.png"
inkscape:export-xdpi="160.01999"
inkscape:export-ydpi="160.01999">
<defs
id="defs2">
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker7369"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend">
<path
inkscape:connector-curvature="0"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:#666666;fill-opacity:1;fill-rule:evenodd;stroke:#666666;stroke-width:1.00000003pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path7371" />
</marker>
<marker
inkscape:stockid="Arrow1Sstart"
orient="auto"
refY="0"
refX="0"
id="marker5457"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5459"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.2,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0"
refX="0"
id="marker5327"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path5329"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4910"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Send">
<path
inkscape:connector-curvature="0"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path4912" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4798"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Sstart">
<path
inkscape:connector-curvature="0"
transform="matrix(0.2,0,0,0.2,1.2,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path4800" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4722"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Sstart"
inkscape:collect="always">
<path
inkscape:connector-curvature="0"
transform="matrix(0.2,0,0,0.2,1.2,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path4724" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4573"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0"
refX="0"
id="Arrow1Send"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4579"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Sstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Sstart"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path4576"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.2,0,0,0.2,1.2,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4570"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#666666;fill-opacity:1;fill-rule:evenodd;stroke:#666666;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart-8"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path4570-7"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-6"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path4573-1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart-9"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4570-1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-7"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4573-4"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart-9-3"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4570-1-0"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-7-1"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path4573-4-7"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker4798-5"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Sstart">
<path
inkscape:connector-curvature="0"
transform="matrix(0.2,0,0,0.2,1.2,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path4800-4" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker5004-9"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Send">
<path
inkscape:connector-curvature="0"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path5006-2" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mstart-8-7"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path4570-7-0"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,4,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="4.0150521"
inkscape:cx="45.354331"
inkscape:cy="75.590551"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
units="mm"
inkscape:snap-bbox="true"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:bbox-paths="false"
inkscape:bbox-nodes="false"
inkscape:snap-bbox-edge-midpoints="false"
inkscape:snap-bbox-midpoints="false"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:window-width="1600"
inkscape:window-height="824"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:object-nodes="true"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid4485"
empspacing="8"
units="mm"
spacingx="0.49999998"
spacingy="0.49999998" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-256.99999)">
<rect
style="stroke-width:0.1;stroke-miterlimit:4;stroke-dasharray:none;fill:#006400;stroke:#000000;stroke-opacity:1"
id="rect4305"
width="23.999998"
height="39.999996"
x="0"
y="257" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffcc00;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 12.023009,260.99998 -0.02301,32.00001"
id="path4487"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.29999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Sstart);marker-end:url(#Arrow1Send)"
d="m 4.4999996,259.99999 7.0230094,-1e-5"
id="path4562"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 3.9999996,263.99999 0,-4.5"
id="path4910"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 12.023009,260.49998 0,-1"
id="path4910-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="12.7"
y="247.78749"
id="text5363"><tspan
sodipodi:role="line"
id="tspan5361"
x="12.7"
y="247.78749"
style="font-size:3.52777791px;line-height:1.25;stroke-width:0.26458332px"> </tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="7.9664097"
y="259.28909"
id="text5607"><tspan
sodipodi:role="line"
id="tspan5605"
x="7.9664097"
y="259.28909"
style="font-size:2.11666656px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:0.26458332px">Offset</tspan></text>
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6"
cx="4.0230093"
cy="267"
r="1.5230091" />
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6-6"
cx="3.9999993"
cy="273"
r="1.5230091" />
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6-2"
cx="4"
cy="285.02301"
r="1.5230091" />
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6-3"
cx="20.023008"
cy="267"
r="1.5230091" />
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6-6-5"
cx="19.999998"
cy="273"
r="1.5230091" />
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6-2-5"
cx="19.999998"
cy="285.02301"
r="1.5230091" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.29999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#marker4722);marker-end:url(#marker4910)"
d="m 16.999999,267.49999 0,5"
id="path4562-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 16.499999,266.99999 1,0"
id="path4910-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 16.499999,272.99999 1,0"
id="path4910-1-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="269.98706"
y="-14.210899"
id="text5607-1"
transform="rotate(90)"><tspan
sodipodi:role="line"
id="tspan5605-0"
x="269.98706"
y="-14.210899"
style="font-size:2.11666656px;line-height:1.25;text-align:center;text-anchor:middle;stroke-width:0.26458332px">Pitch</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="5.0834432"
y="289.84128"
id="text5110"><tspan
sodipodi:role="line"
id="tspan5112"
x="5.0834432"
y="289.84128"
style="font-size:2.11666656px;line-height:1.25;text-align:center;text-anchor:middle">Via Size</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mend);marker-end:url(#Arrow1Mstart-8)"
d="m 5.2022141,283.39636 -2.4224396,3.2534"
id="path5319"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#666666;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#marker7369)"
d="m 20.642371,285.69684 -1.317119,-1.33825"
id="path7343"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="18.987841"
y="289.84128"
id="text5110-9"><tspan
sodipodi:role="line"
id="tspan5112-9"
x="18.987841"
y="289.84128"
style="font-size:2.11666656px;line-height:1.25;text-align:center;text-anchor:middle">Via Drill</tspan></text>
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6-6-4"
cx="3.9999998"
cy="279"
r="1.5230091" />
<circle
style="fill:#1a1a1a;fill-opacity:1;stroke:#ffcc00;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4489-9-6-6-5-5"
cx="19.999998"
cy="279"
r="1.5230091" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
{
"pathList": [
[
[
-2000,
250
],
[
2000,
250
]
],
[
[
-2000,
-250
],
[
2000,
-250
]
]
],
"viaOffset": 1000,
"viaPitch": 300,
"viaPoints": [
[
-2000,
-1250
],
[
2000,
-1250
],
[
-1692.3076923076924,
-1250.0
],
[
-1384.6153846153848,
-1250.0
],
[
-1076.923076923077,
-1250.0
],
[
-769.2307692307693,
-1250.0
],
[
-461.53846153846143,
-1250.0
],
[
-153.8461538461538,
-1250.0
],
[
153.8461538461538,
-1250.0
],
[
461.53846153846143,
-1250.0
],
[
769.230769230769,
-1250.0
],
[
1076.9230769230771,
-1250.0
],
[
1384.6153846153848,
-1250.0
],
[
1692.3076923076924,
-1250.0
],
[
2000,
1250
],
[
-2000,
1250
],
[
1692.3076923076924,
1250.0
],
[
1384.6153846153848,
1250.0
],
[
1076.923076923077,
1250.0
],
[
769.2307692307693,
1250.0
],
[
461.53846153846143,
1250.0
],
[
153.8461538461538,
1250.0
],
[
-153.8461538461538,
1250.0
],
[
-461.53846153846143,
1250.0
],
[
-769.230769230769,
1250.0
],
[
-1076.9230769230771,
1250.0
],
[
-1384.6153846153848,
1250.0
],
[
-1692.3076923076924,
1250.0
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,140 @@
{
"pathList": [
[
[
-2000,
500
],
[
2000,
500
]
],
[
[
-2000,
-500
],
[
2000,
-500
]
]
],
"viaOffset": 500,
"viaPitch": 300,
"viaPoints": [
[
-2000,
-1000
],
[
2000,
-1000
],
[
-1692.3076923076924,
-1000.0
],
[
-1384.6153846153848,
-1000.0
],
[
-1076.923076923077,
-1000.0
],
[
-769.2307692307693,
-1000.0
],
[
-461.53846153846143,
-1000.0
],
[
-153.8461538461538,
-1000.0
],
[
153.8461538461538,
-1000.0
],
[
461.53846153846143,
-1000.0
],
[
769.230769230769,
-1000.0
],
[
1076.9230769230771,
-1000.0
],
[
1384.6153846153848,
-1000.0
],
[
1692.3076923076924,
-1000.0
],
[
2000,
1000
],
[
-2000,
1000
],
[
1692.3076923076924,
1000.0
],
[
1384.6153846153848,
1000.0
],
[
1076.923076923077,
1000.0
],
[
769.2307692307693,
1000.0
],
[
461.53846153846143,
1000.0
],
[
153.8461538461538,
1000.0
],
[
-153.8461538461538,
1000.0
],
[
-461.53846153846143,
1000.0
],
[
-769.230769230769,
1000.0
],
[
-1076.9230769230771,
1000.0
],
[
-1384.6153846153848,
1000.0
],
[
-1692.3076923076924,
1000.0
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,252 @@
{
"pathList": [
[
[
-2000,
501
],
[
2000,
501
]
],
[
[
-2000,
-501
],
[
2000,
-501
]
]
],
"viaOffset": 500,
"viaPitch": 300,
"viaPoints": [
[
-2000,
1
],
[
2000,
1
],
[
-1692.3076923076924,
1.0
],
[
-1384.6153846153848,
1.0
],
[
-1076.923076923077,
1.0
],
[
-769.2307692307693,
1.0
],
[
-461.53846153846143,
1.0
],
[
-153.8461538461538,
1.0
],
[
153.8461538461538,
1.0
],
[
461.53846153846143,
1.0
],
[
769.230769230769,
1.0
],
[
1076.9230769230771,
1.0
],
[
1384.6153846153848,
1.0
],
[
1692.3076923076924,
1.0
],
[
2000,
1001
],
[
-2000,
1001
],
[
1692.3076923076924,
1001.0
],
[
1384.6153846153848,
1001.0
],
[
1076.923076923077,
1001.0
],
[
769.2307692307693,
1001.0
],
[
461.53846153846143,
1001.0
],
[
153.8461538461538,
1001.0
],
[
-153.8461538461538,
1001.0
],
[
-461.53846153846143,
1001.0
],
[
-769.230769230769,
1001.0
],
[
-1076.9230769230771,
1001.0
],
[
-1384.6153846153848,
1001.0
],
[
-1692.3076923076924,
1001.0
],
[
-2000,
-1001
],
[
2000,
-1001
],
[
-1692.3076923076924,
-1001.0
],
[
-1384.6153846153848,
-1001.0
],
[
-1076.923076923077,
-1001.0
],
[
-769.2307692307693,
-1001.0
],
[
-461.53846153846143,
-1001.0
],
[
-153.8461538461538,
-1001.0
],
[
153.8461538461538,
-1001.0
],
[
461.53846153846143,
-1001.0
],
[
769.230769230769,
-1001.0
],
[
1076.9230769230771,
-1001.0
],
[
1384.6153846153848,
-1001.0
],
[
1692.3076923076924,
-1001.0
],
[
2000,
-1
],
[
-2000,
-1
],
[
1692.3076923076924,
-1.0
],
[
1384.6153846153848,
-1.0
],
[
1076.923076923077,
-1.0
],
[
769.2307692307693,
-1.0
],
[
461.53846153846143,
-1.0
],
[
153.8461538461538,
-1.0
],
[
-153.8461538461538,
-1.0
],
[
-461.53846153846143,
-1.0
],
[
-769.230769230769,
-1.0
],
[
-1076.9230769230771,
-1.0
],
[
-1384.6153846153848,
-1.0
],
[
-1692.3076923076924,
-1.0
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,434 @@
{
"viaOffset": 500,
"viaPitch": 300,
"viaPoints": [
[
2000,
4500
],
[
1500,
1792
],
[
1500,
500
],
[
309,
500
],
[
-2000,
2500
],
[
1694.7494045294795,
4500.0
],
[
1389.498809058959,
4500.0
],
[
1084.2482135884386,
4500.0
],
[
778.9976181179181,
4500.0
],
[
473.74702264739767,
4500.0
],
[
168.4964271768772,
4500.0
],
[
-134.97345240050308,
4481.007964279849
],
[
-386.4640053423498,
4316.612565323029
],
[
-498.26181546785324,
4037.810952514348
],
[
-430.1165855145392,
3745.243988758386
],
[
-226.75812814749332,
3518.758128147493
],
[
-10.91336212905685,
3302.9133621290566
],
[
204.93140388938002,
3087.06859611062
],
[
420.7761699078168,
2871.223830092183
],
[
636.6209359262533,
2655.3790640737466
],
[
852.4657019446897,
2439.5342980553105
],
[
1068.3104679631263,
2223.689532036874
],
[
1284.1552339815635,
2007.8447660184365
],
[
1500.0,
1469.0
],
[
1500.0,
1146.0
],
[
1500.0,
823.0
],
[
1103.0,
500.0
],
[
706.0,
500.0
],
[
167.49497119958997,
783.0100576008201
],
[
25.989942399179938,
1066.0201152016402
],
[
-115.51508640123012,
1349.0301728024601
],
[
-257.0201152016401,
1632.0402304032802
],
[
-398.5251440020502,
1915.0502880041004
],
[
-540.0301728024602,
2198.0603456049203
],
[
-746.9452985193672,
2430.745362085551
],
[
-1050.755409332653,
2500.0
],
[
-1367.1702728884356,
2500.0
],
[
-1683.5851364442178,
2500.0
],
[
-2000,
1500
],
[
-1309,
1500
],
[
-609,
99
],
[
-2354,
-1646
],
[
-1654.5,
1500.0
],
[
-1169.0,
1219.8
],
[
-1029.0,
939.6
],
[
-889.0,
659.4
],
[
-749.0,
379.20000000000005
],
[
-827.125,
-119.125
],
[
-1045.25,
-337.25
],
[
-1263.375,
-555.375
],
[
-1481.5,
-773.5
],
[
-1699.625,
-991.625
],
[
-1917.75,
-1209.75
],
[
-2135.875,
-1427.8750000000002
],
[
-1646,
-2354
],
[
208,
-500
],
[
1208,
3500
],
[
2000,
3500
],
[
-1414.25,
-2122.25
],
[
-1182.5,
-1890.5
],
[
-950.75,
-1658.75
],
[
-719.0,
-1427.0
],
[
-487.25,
-1195.25
],
[
-255.5,
-963.5
],
[
-23.75,
-731.75
],
[
521.8262211985277,
-500.0
],
[
835.6524423970556,
-500.0
],
[
1149.4786635955834,
-500.0
],
[
1463.304884794111,
-500.0
],
[
1777.1311059926388,
-500.0
],
[
2090.442225924456,
-491.57383452831357
],
[
2361.7526198249857,
-344.44044291024215
],
[
2495.659716859078,
-66.30975547371793
],
[
2500.0,
247.3274095922838
],
[
2500.0,
561.1536307908112
],
[
2500.0,
874.9798519893395
],
[
2500.0,
1188.806073187867
],
[
2500.0,
1502.632294386395
],
[
2500.0,
1816.4585155849227
],
[
2482.8669417451197,
2128.7768608496017
],
[
2317.5432456181425,
2390.4567543818575
],
[
2095.6345964945144,
2612.3654035054856
],
[
1873.7259473708855,
2834.2740526291145
],
[
1651.817298247257,
3056.182701752743
],
[
1429.908649123629,
3278.091350876371
],
[
1604.0,
3500.0
],
[
1000,
-2500
],
[
2000,
-2500
],
[
1333.3333333333333,
-2500.0
],
[
1666.6666666666665,
-2500.0
],
[
2000,
-1500
],
[
1000,
-1500
],
[
1666.6666666666667,
-1500.0
],
[
1333.3333333333335,
-1500.0
]
],
"pathList": [
[
[
-2000,
-2000
],
[
0,
0
],
[
2000,
0
],
[
2000,
2000
],
[
0,
4000
],
[
2000,
4000
]
],
[
[
0,
0
],
[
-1000,
2000
],
[
-2000,
2000
]
],
[
[
2000,
-2000
],
[
1000,
-2000
]
]
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,386 @@
#!/usr/bin/env python
# Copyright 2017 Simon Kuppers https://github.com/skuep/
# Copyright 2019 Maurice https://github.com/easyw/
# original plugin https://github.com/skuep/kicad-plugins
# some source tips @
# https://github.com/MitjaNemec/Kicad_action_plugins
# https://github.com/jsreynaud/kicad-action-scripts
# GNU GENERAL PUBLIC LICENSE
# Version 3, 29 June 2007
#
# Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
# Everyone is permitted to copy and distribute verbatim copies
# of this license document, but changing it is not allowed.
#
# 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.
#
# 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 math
#import pyclipper
from bisect import bisect_left
import wx
import pcbnew
def verbose(object, *args, **kwargs):
global verboseFunc
verboseFunc(object, *args, **kwargs)
# Returns the slope of a line
def getLineSlope(line):
return math.atan2(line[0][1]-line[1][1], line[0][0]-line[1][0])
# Returns the length of a line
def getLineLength(line):
return math.hypot(line[0][0]-line[1][0], line[0][1]-line[1][1])
# Returns a sub path in a path with a path specification (startIdx, stopIdx)
def getSubPath(path, pathSpec):
listModulus = len(path)
if (pathSpec[1] < pathSpec[0]): pathSpec[1] += listModulus
return [path[i % listModulus] for i in range(pathSpec[0], pathSpec[1]+1)]
# Returns a list of subpaths with a list of path specifications
def getSubPaths(path, pathSpecList):
return [getSubPath(path, pathSpec) for pathSpec in pathSpecList if (pathSpec[0] != pathSpec[1])]
# Splits a path using a list of indices representing points on the path
def splitPathByPoints(path, splitList):
pathSpecList = [[splitList[item], splitList[item+1]] for item in range(0, len(splitList)-1)]
return getSubPaths(path, pathSpecList)
# Splits a path around a list of list of indices representing a subpath within the original path
def splitPathByPaths(path, splitList):
pathSpecList = [[splitList[item][-1], splitList[(item+1)%len(splitList)][0]] for item in range(0, len(splitList))]
return getSubPaths(path, pathSpecList)
# Return a cumulative distance vector representing the distance travelled along
# the path at each path vertex
def getPathCumDist(path):
cumDist = [0.0]
for vertexId in range(1, len(path)):
cumDist += [cumDist[-1] + getLineLength([path[vertexId], path[vertexId-1]])]
return cumDist
# Return a list of all vertex indices where the angle between
# the two lines connected to the vertex deviate from a straight
# path more by the tolerance angle in degrees
# This function is used to find bends that are larger than a certain angle
def getPathVertices(path, angleTolerance):
angleTolerance = angleTolerance * math.pi / 180
vertices = []
# Look through all vertices except start and end vertex
# Calculate by how much the lines before and after the vertex
# deviate from a straight path.
# If the deviation angle exceeds the specification, store it
for vertexIdx in range(1, len(path)-1):
prevSlope = getLineSlope([path[vertexIdx+1], path[vertexIdx]])
nextSlope = getLineSlope([path[vertexIdx-1], path[vertexIdx]])
deviationAngle = abs(prevSlope - nextSlope) - math.pi
if (abs(deviationAngle) > angleTolerance):
vertices += [vertexIdx]
return vertices
# Uses the cross product to check if a point is on a line defined by two other points
def isPointOnLine(point, line):
cross = (line[1][1] - point[1]) * (line[0][0] - point[0]) - (line[1][0] - point[0]) * (line[0][1] - point[1])
if ( ((line[0][0] <= point[0] <= line[1][0]) or (line[1][0] <= point[0] <= line[0][0]))
and ((line[0][1] <= point[1] <= line[1][1]) or (line[1][1] <= point[1] <= line[0][1]))
and (cross == 0) ):
return True
return False
# Returns a list of path indices touching any item in a list of points
def getPathsThroughPoints(path, pointList):
touchingPaths = []
for vertexIdx in range(0, len(path)):
fromIdx = vertexIdx
toIdx = (vertexIdx+1) % len(path)
# If a point in the pointList is located on this line, store the line
for point in pointList:
if isPointOnLine(point, [ path[fromIdx], path[toIdx] ]):
touchingPaths += [[fromIdx, toIdx]]
break
return touchingPaths
# A small linear interpolation class so we don't rely on scipy or numpy here
class LinearInterpolator(object):
def __init__(self, x_list, y_list):
self.x_list, self.y_list = x_list, y_list
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
def __call__(self, x):
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
# Interpolate a path with (x,y) vertices using a third parameter t
class PathInterpolator:
def __init__(self, t, path):
# Quick and dirty transpose path so we get two list with x and y coords
# And set up two separate interpolators for them
x = [vertex[0] for vertex in path]
y = [vertex[1] for vertex in path]
self.xInterp = LinearInterpolator(t, x)
self.yInterp = LinearInterpolator(t, y)
def __call__(self, t):
# Return interpolated coordinates on the original path
return [self.xInterp(t), self.yInterp(t)]
# A small pyclipper wrapper class to expand a line to a polygon with a given offset
def expandPathsToPolygons(pathList, offset):
import pyclipper
# Use PyclipperOffset to generate polygons that surround the original
# paths with a constant offset all around
co = pyclipper.PyclipperOffset()
for path in pathList: co.AddPath(path, pyclipper.JT_ROUND, pyclipper.ET_OPENROUND)
return co.Execute(offset)
# A small pyclipper wrapper to trim parts of a polygon using another polygon
def clipPolygonWithPolygons(path, clipPathList):
import pyclipper
pc = pyclipper.Pyclipper()
pc.AddPath(path, pyclipper.PT_SUBJECT, True)
for clipPath in clipPathList: pc.AddPath(clipPath, pyclipper.PT_CLIP, True)
return pc.Execute(pyclipper.CT_DIFFERENCE)
def unionPolygons(pathList):
import pyclipper
pc = pyclipper.Pyclipper()
for path in pathList: pc.AddPath(path, pyclipper.PT_SUBJECT, True)
return pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO)
def isPointInPolygon(point, path):
import pyclipper
return True if (pyclipper.PointInPolygon(point, path) == 1) else False
def getPathsInsidePolygon(pathList, polygon):
filteredPathList = []
for path in pathList:
allVerticesInside = True
for vertex in path:
if not isPointInPolygon(vertex, polygon):
allVerticesInside = False
break
if (allVerticesInside): filteredPathList += [path]
return filteredPathList
# Distribute Points along a path with equal spacing to each other
# When the path length is not evenly dividable by the minimumSpacing,
# the actual spacing will be larger, but still smaller than 2*minimumSpacing
# The function does not return the start and end vertex of the path
def distributeAlongPath(path, minimumSpacing):
# Get cumulated distance vector for the path
# and determine the number of points that can fit to the path
distList = getPathCumDist(path)
nPoints = int(math.floor(distList[-1] / minimumSpacing))
ptInterp = PathInterpolator(distList, path)
return [ptInterp(ptIdx * distList[-1]/nPoints) for ptIdx in range(1, nPoints)]
# Find the leaf vertices in a list of paths,
# additionally it calculates the slope of the line connected to the leaf vertex
def getLeafVertices(pathList):
allVertices = [vertex for path in pathList for vertex in path]
leafVertices = []
leafVertexSlopes = []
for path in pathList:
for vertexIdx in [0,-1]:
if (allVertices.count(path[vertexIdx]) == 1):
# vertex appears only once in entire path list, store away
# Get neighbour vertex and also calculate the slope
leafVertex = path[vertexIdx]
neighbourVertex = path[ [1,-2][vertexIdx] ]
leafVertices += [leafVertex]
leafVertexSlopes += [getLineSlope([neighbourVertex, leafVertex])]
return leafVertices, leafVertexSlopes
# Rotate and Translate a list of vertices using a given angle and offset
def transformVertices(vertexList, offset, angle):
return [ [ round(offset[0] + math.cos(angle) * vertex[0] - math.sin(angle) * vertex[1]),
round(offset[1] + math.sin(angle) * vertex[0] + math.cos(angle) * vertex[1]) ]
for vertex in vertexList]
# Trims a polygon flush around the given vertices
def trimFlushPolygonAtVertices(path, vertexList, vertexSlopes, radius):
const = 0.414
trimPoly = [ [0, -radius], [0, 0], [0, radius], [-const*radius, radius], [-radius, const*radius],
[-radius, -const*radius], [-const*radius, -radius] ]
trimPolys = [transformVertices(trimPoly, vertexPos, vertexSlope)
for vertexPos, vertexSlope in zip(vertexList, vertexSlopes)]
trimPolys = unionPolygons(trimPolys)
verbose(trimPolys, isPolygons=True)
return clipPolygonWithPolygons(path, trimPolys)
######################
def generateViaFence(pathList, viaOffset, viaPitch, vFunc = lambda *args,**kwargs:None):
global verboseFunc
verboseFunc = vFunc
viaPoints = []
# Remove zero length tracks
pathList = [path for path in pathList if getLineLength(path) > 0]
# Expand the paths given as a parameter into one or more polygons
# using the offset parameter
for offsetPoly in expandPathsToPolygons(pathList, viaOffset):
verbose([offsetPoly], isPolygons=True)
# Filter the input path to only include paths inside this polygon
# Find all leaf vertices and use them to trim the expanded polygon
# around the leaf vertices so that we get a flush, flat end
# These butt lines are then found using the leaf vertices
# and used to split open the polygon into multiple separate open
# paths that envelop the original path
localPathList = getPathsInsidePolygon(pathList, offsetPoly)
if len(localPathList) == 0: continue # This might happen with very bad input paths
leafVertexList, leafVertexAngles = getLeafVertices(localPathList)
offsetPoly = trimFlushPolygonAtVertices(offsetPoly, leafVertexList, leafVertexAngles, 1.1*viaOffset)[0]
buttLineIdxList = getPathsThroughPoints(offsetPoly, leafVertexList)
fencePaths = splitPathByPaths(offsetPoly, buttLineIdxList)
verbose([offsetPoly], isPolygons=True)
verbose([leafVertexList], isPoints=True)
verbose(fencePaths, isPaths=True)
# With the now separated open paths we perform via placement on each one of them
for fencePath in fencePaths:
# For a nice via fence placement, we identify vertices that differ from a straight
# line by more than 10 degrees so we find all non-arc edges
# We combine these points with the start and end point of the path and use
# them to place fixed vias on their positions
tolerance_degree = 10
fixPointIdxList = [0] + getPathVertices(fencePath, tolerance_degree) + [-1]
fixPointList = [fencePath[idx] for idx in fixPointIdxList]
verbose(fixPointList, isPoints=True)
viaPoints += fixPointList
# Then we autoplace vias between the fixed via locations by satisfying the
# minimum via pitch given by the user
for subPath in splitPathByPoints(fencePath, fixPointIdxList):
viaPoints += distributeAlongPath(subPath, viaPitch)
return viaPoints
def create_round_pts(sp,ep,cntr,rad,layer,width,Nn,N_SEGMENTS):
start_point = sp
end_point = ep
pos = sp
next_pos = ep
a1 = getAngleRadians(cntr,sp)
a2 = getAngleRadians(cntr,ep)
#wxLogDebug('a1:'+str(math.degrees(a1))+' a2:'+str(math.degrees(a2))+' a2-a1:'+str(math.degrees(a2-a1)),debug)
if (a2-a1) > 0 and abs(a2-a1) > math.radians(180):
deltaA = -(math.radians(360)-(a2-a1))/N_SEGMENTS
#wxLogDebug('deltaA reviewed:'+str(math.degrees(deltaA)),debug)
elif (a2-a1) < 0 and abs(a2-a1) > math.radians(180):
deltaA = (math.radians(360)-abs(a2-a1))/N_SEGMENTS
#wxLogDebug('deltaA reviewed2:'+str(math.degrees(deltaA)),debug)
else:
deltaA = (a2-a1)/N_SEGMENTS
delta=deltaA
#wxLogDebug('delta:'+str(math.degrees(deltaA))+' radius:'+str(ToMM(rad)),debug)
points = []
#import round_trk; import importlib; importlib.reload(round_trk)
for ii in range (N_SEGMENTS+1): #+1):
points.append(pos)
#t = create_Track(pos,pos)
prv_pos = pos
#pos = pos + fraction_delta
#posPolar = cmath.polar(pos)
#(rad) * cmath.exp(math.radians(deltaA)*1j) #cmath.rect(r, phi) : Return the complex number x with polar coordinates r and phi.
#pos = wxPoint(posPolar.real+sp.x,posPolar.imag+sp.y)
pos = rotatePoint(rad,a1,delta,cntr)
delta=delta+deltaA
#wxLogDebug("pos:"+str(ToUnits(prv_pos.x))+":"+str(ToUnits(prv_pos.y))+";"+str(ToUnits(pos.x))+":"+str(ToUnits(pos.y)),debug)
return points
#if 0:
# for i, p in enumerate(points):
# #if i < len (points)-1:
# if i < len (points)-2:
# t = create_Solder(pcb,p,points[i+1],layer,width,Nn,True,pcbGroup) #adding ts code to segments
# t = create_Solder(pcb,points[-2],ep,layer,width,Nn,True,pcbGroup) #avoiding rounding on last segment
#
# Function to find the circle on
# which the given three points lie
def getCircleCenterRadius(sp,ep,ip):
# findCircle(x1, y1, x2, y2, x3, y3) :
# NB add always set float even if values are pcb internal Units!!!
x1 = float(sp.x); y1 = float(sp.y)
x2 = float(ep.x); y2 = float(ep.y)
x3 = float(ip.x); y3 = float(ip.y)
x12 = x1 - x2;
x13 = x1 - x3;
y12 = y1 - y2;
y13 = y1 - y3;
y31 = y3 - y1;
y21 = y2 - y1;
x31 = x3 - x1;
x21 = x2 - x1;
# x1^2 - x3^2
sx13 = math.pow(x1, 2) - math.pow(x3, 2);
# y1^2 - y3^2
sy13 = math.pow(y1, 2) - math.pow(y3, 2);
sx21 = math.pow(x2, 2) - math.pow(x1, 2);
sy21 = math.pow(y2, 2) - math.pow(y1, 2);
f = (((sx13) * (x12) + (sy13) *
(x12) + (sx21) * (x13) +
(sy21) * (x13)) // (2 *
((y31) * (x12) - (y21) * (x13))));
g = (((sx13) * (y12) + (sy13) * (y12) +
(sx21) * (y13) + (sy21) * (y13)) //
(2 * ((x31) * (y12) - (x21) * (y13))));
c = (-math.pow(x1, 2) - math.pow(y1, 2) - 2 * g * x1 - 2 * f * y1);
# eqn of circle be x^2 + y^2 + 2*g*x + 2*f*y + c = 0
# where centre is (h = -g, k = -f) and
# radius r as r^2 = h^2 + k^2 - c
h = -g;
k = -f;
sqr_of_r = h * h + k * k - c;
# r is the radius
r = round(math.sqrt(sqr_of_r), 5);
Cx = h
Cy = k
radius = r
return wx.Point(Cx,Cy), radius
#
def getAngleRadians(p1,p2):
#return math.degrees(math.atan2((p1.y-p2.y),(p1.x-p2.x)))
return (math.atan2((p1.y-p2.y),(p1.x-p2.x)))
#
def rotatePoint(r,sa,da,c):
# sa, da in radians
x = c.x - math.cos(sa+da) * r
y = c.y - math.sin(sa+da) * r
return wx.Point(x,y)

View File

@@ -0,0 +1,639 @@
#!/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()

View File

@@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version Oct 26 2018)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################
import wx
import wx.xrc
###########################################################################
## Class MainDialogBase
###########################################################################
class MainDialogBase ( wx.Dialog ):
def __init__( self, parent ):
wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Via Fence Generator", pos = wx.DefaultPosition, size = wx.Size( 503,567 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.RESIZE_BORDER )
import sys #maui
if sys.version_info[0] == 2:
self.SetSizeHintsSz( wx.Size( -1,-1 ), wx.Size( -1,-1 ) )
else:
self.SetSizeHints( wx.Size( -1,-1 ), wx.Size( -1,-1 ) )
mainSizer = wx.BoxSizer( wx.VERTICAL )
gbSizer4 = wx.GridBagSizer( 0, 0 )
gbSizer4.SetFlexibleDirection( wx.BOTH )
gbSizer4.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )
bSizer23 = wx.BoxSizer( wx.VERTICAL )
sbSizer2 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Via Settings" ), wx.VERTICAL )
fgSizer4 = wx.FlexGridSizer( 8, 2, 0, 0 )
fgSizer4.AddGrowableCol( 1 )
fgSizer4.SetFlexibleDirection( wx.BOTH )
fgSizer4.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )
self.m_staticText11 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, u"Offset (mm):", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText11.Wrap( -1 )
fgSizer4.Add( self.m_staticText11, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
self.txtViaOffset = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER|wx.TE_RIGHT )
fgSizer4.Add( self.txtViaOffset, 0, wx.ALL|wx.EXPAND, 5 )
self.m_staticText21 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, u"Pitch (mm):", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText21.Wrap( -1 )
fgSizer4.Add( self.m_staticText21, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
self.txtViaPitch = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER|wx.TE_RIGHT )
fgSizer4.Add( self.txtViaPitch, 0, wx.ALL|wx.EXPAND, 5 )
self.m_staticText13 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, u"Via Drill (mm):", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText13.Wrap( -1 )
fgSizer4.Add( self.m_staticText13, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
self.txtViaDrill = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER|wx.TE_RIGHT )
fgSizer4.Add( self.txtViaDrill, 0, wx.ALL|wx.EXPAND, 5 )
self.m_staticText14 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, u"Via Size (mm):", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText14.Wrap( -1 )
fgSizer4.Add( self.m_staticText14, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
self.txtViaSize = wx.TextCtrl( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_PROCESS_ENTER|wx.TE_RIGHT )
fgSizer4.Add( self.txtViaSize, 0, wx.EXPAND|wx.ALL, 5 )
self.m_staticText23 = wx.StaticText( sbSizer2.GetStaticBox(), wx.ID_ANY, u"Via Net:", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText23.Wrap( -1 )
fgSizer4.Add( self.m_staticText23, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 )
lstViaNetChoices = []
self.lstViaNet = wx.Choice( sbSizer2.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, lstViaNetChoices, 0 )
self.lstViaNet.SetSelection( 0 )
fgSizer4.Add( self.lstViaNet, 1, wx.EXPAND|wx.ALL, 5 )
sbSizer2.Add( fgSizer4, 1, wx.EXPAND, 5 )
bSizer23.Add( sbSizer2, 1, wx.ALL|wx.EXPAND, 5 )
gbSizer4.Add( bSizer23, wx.GBPosition( 0, 0 ), wx.GBSpan( 1, 1 ), wx.EXPAND, 5 )
bSizer21 = wx.BoxSizer( wx.VERTICAL )
bSizer21.Add( ( 0, 9), 0, 0, 5 )
self.bmpViafence = wx.StaticBitmap( self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer21.Add( self.bmpViafence, 1, wx.EXPAND, 5 )
gbSizer4.Add( bSizer21, wx.GBPosition( 0, 1 ), wx.GBSpan( 1, 1 ), wx.EXPAND|wx.ALL, 5 )
sbSizer411 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Input Tracks" ), wx.VERTICAL )
gSizer4 = wx.GridSizer( 3, 1, 0, 0 )
self.chkIncludeSelection = wx.CheckBox( sbSizer411.GetStaticBox(), wx.ID_ANY, u"Include Selected", wx.DefaultPosition, wx.DefaultSize, 0 )
self.chkIncludeSelection.SetValue(True)
gSizer4.Add( self.chkIncludeSelection, 0, wx.ALL, 5 )
self.chkIncludeDrawing = wx.CheckBox( sbSizer411.GetStaticBox(), wx.ID_ANY, u"Include Drawing Lines", wx.DefaultPosition, wx.DefaultSize, 0 )
gSizer4.Add( self.chkIncludeDrawing, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 5 )
fgSizer311 = wx.FlexGridSizer( 1, 2, 0, 10 )
fgSizer311.AddGrowableCol( 1 )
fgSizer311.SetFlexibleDirection( wx.HORIZONTAL )
fgSizer311.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )
self.chkNetFilter = wx.CheckBox( sbSizer411.GetStaticBox(), wx.ID_ANY, u"Net(s):", wx.DefaultPosition, wx.DefaultSize, 0 )
fgSizer311.Add( self.chkNetFilter, 0, wx.ALL|wx.EXPAND, 5 )
txtNetFilterChoices = []
self.txtNetFilter = wx.ComboBox( sbSizer411.GetStaticBox(), wx.ID_ANY, u"Combo!", wx.DefaultPosition, wx.DefaultSize, txtNetFilterChoices, 0 )
self.txtNetFilter.Enable( False )
fgSizer311.Add( self.txtNetFilter, 1, wx.ALL|wx.EXPAND, 5 )
gSizer4.Add( fgSizer311, 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5 )
fgSizer31 = wx.FlexGridSizer( 1, 2, 0, 10 )
fgSizer31.AddGrowableCol( 1 )
fgSizer31.SetFlexibleDirection( wx.HORIZONTAL )
fgSizer31.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )
self.chkLayer = wx.CheckBox( sbSizer411.GetStaticBox(), wx.ID_ANY, u"Layer:", wx.DefaultPosition, wx.DefaultSize, 0 )
fgSizer31.Add( self.chkLayer, 1, wx.ALL|wx.EXPAND, 5 )
lstLayerChoices = []
self.lstLayer = wx.Choice( sbSizer411.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, lstLayerChoices, 0 )
self.lstLayer.SetSelection( 0 )
self.lstLayer.Enable( False )
fgSizer31.Add( self.lstLayer, 1, wx.ALL|wx.EXPAND, 5 )
gSizer4.Add( fgSizer31, 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5 )
sbSizer411.Add( gSizer4, 0, wx.EXPAND, 5 )
gbSizer4.Add( sbSizer411, wx.GBPosition( 1, 0 ), wx.GBSpan( 1, 1 ), wx.ALL|wx.EXPAND, 5 )
sbSizer4 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Output VIAs" ), wx.VERTICAL )
gSizer2 = wx.GridSizer( 3, 1, 0, 0 )
self.chkSameNetZoneViasOnly = wx.CheckBox( sbSizer4.GetStaticBox(), wx.ID_ANY, u"Keep VIAs in \nVia Net zones only", wx.DefaultPosition, wx.DefaultSize, 0 )
gSizer2.Add( self.chkSameNetZoneViasOnly, 1, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 )
self.chkRemoveViasWithClearanceViolation = wx.CheckBox( sbSizer4.GetStaticBox(), wx.ID_ANY, u"Remove VIAs violating \nclearance rules", wx.DefaultPosition, wx.DefaultSize, 0 )
gSizer2.Add( self.chkRemoveViasWithClearanceViolation, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT|wx.LEFT, 5 )
self.m_buttonDelete = wx.Button( sbSizer4.GetStaticBox(), wx.ID_ANY, u"Delete Vias", wx.DefaultPosition, wx.DefaultSize, 0 )
gSizer2.Add( self.m_buttonDelete, 0, wx.ALIGN_CENTER|wx.ALL, 5 )
sbSizer4.Add( gSizer2, 1, wx.EXPAND, 5 )
gbSizer4.Add( sbSizer4, wx.GBPosition( 1, 1 ), wx.GBSpan( 1, 1 ), wx.EXPAND|wx.ALL, 5 )
gbSizer4.AddGrowableCol( 0 )
mainSizer.Add( gbSizer4, 1, wx.EXPAND, 5 )
bSizer5 = wx.BoxSizer( wx.HORIZONTAL )
self.chkDebugDump = wx.CheckBox( self, wx.ID_ANY, u"Debug Dump", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer5.Add( self.chkDebugDump, 0, wx.ALL|wx.EXPAND, 5 )
bSizer5.Add( ( 0, 0), 1, wx.EXPAND, 5 )
m_sdbSizer1 = wx.StdDialogButtonSizer()
self.m_sdbSizer1OK = wx.Button( self, wx.ID_OK )
m_sdbSizer1.AddButton( self.m_sdbSizer1OK )
self.m_sdbSizer1Cancel = wx.Button( self, wx.ID_CANCEL )
m_sdbSizer1.AddButton( self.m_sdbSizer1Cancel )
m_sdbSizer1.Realize();
bSizer5.Add( m_sdbSizer1, 0, wx.EXPAND|wx.ALL, 5 )
mainSizer.Add( bSizer5, 0, wx.EXPAND, 5 )
self.SetSizer( mainSizer )
self.Layout()
self.Centre( wx.BOTH )
# Connect Events
self.Bind( wx.EVT_INIT_DIALOG, self.OnInitDialog )
self.chkNetFilter.Bind( wx.EVT_CHECKBOX, self.OnNetFilterCheckBox )
self.chkLayer.Bind( wx.EVT_CHECKBOX, self.OnLayerCheckBox )
def __del__( self ):
pass
# Virtual event handlers, overide them in your derived class
def OnInitDialog( self, event ):
event.Skip()
def OnNetFilterCheckBox( self, event ):
event.Skip()
def OnLayerCheckBox( self, event ):
event.Skip()

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python
import os
import sys
from .viafence_basedialogs import *
class MainDialog(MainDialogBase):
def __init__(self, parent):
MainDialogBase.__init__(self, parent)
# Small workaround to fix the paths generated by wxFormBuilder
self.bmpViafence.SetBitmap(wx.Bitmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", "viafence.png") ) )
def OnInitDialog(self, event):
self.Layout()
self.GetSizer().Fit(self)
self.SetMinSize(self.GetSize())
#self.SetMaxSize(self.GetSize())
self.SetTitle(u"Via Fence Generator")
if sys.version_info[0] == 2:
self.chkDebugDump.SetToolTipString( u"Creates a json file in the same directory as the opened board file containing the tracks and settings" )
else:
self.chkDebugDump.SetToolTip( u"Creates a json file in the same directory as the opened board file containing the tracks and settings" )
def OnNetFilterCheckBox(self, event):
self.txtNetFilter.Enable(event.IsChecked())
def OnLayerCheckBox(self, event):
self.lstLayer.Enable(event.IsChecked())