add bt_player
9
kicad_plugins/via_fence_generator/__init__.py
Normal 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()
|
||||
|
||||
|
||||
|
||||
120
kicad_plugins/via_fence_generator/__main__.py
Normal 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()
|
||||
BIN
kicad_plugins/via_fence_generator/python-pyclipper/py2-7-linux-64/pyclipper.so
Executable file
BIN
kicad_plugins/via_fence_generator/resources/fencing-vias.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
204
kicad_plugins/via_fence_generator/resources/fencing-vias.svg
Normal 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 |
BIN
kicad_plugins/via_fence_generator/resources/viafence.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
536
kicad_plugins/via_fence_generator/resources/viafence.svg
Normal 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 |
1623
kicad_plugins/via_fence_generator/resources/viafence_dialogs.fbp
Normal file
140
kicad_plugins/via_fence_generator/tests/diffms.json
Normal 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
|
||||
]
|
||||
]
|
||||
}
|
||||
BIN
kicad_plugins/via_fence_generator/tests/diffms.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
140
kicad_plugins/via_fence_generator/tests/diffms2.json
Normal 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
|
||||
]
|
||||
]
|
||||
}
|
||||
BIN
kicad_plugins/via_fence_generator/tests/diffms2.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
252
kicad_plugins/via_fence_generator/tests/non-diffms.json
Normal 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
|
||||
]
|
||||
]
|
||||
}
|
||||
BIN
kicad_plugins/via_fence_generator/tests/non-diffms.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
434
kicad_plugins/via_fence_generator/tests/simple-test.json
Normal 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
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
BIN
kicad_plugins/via_fence_generator/tests/simple-test.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
386
kicad_plugins/via_fence_generator/viafence.py
Normal 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)
|
||||
639
kicad_plugins/via_fence_generator/viafence_action.py
Executable 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()
|
||||
|
||||
226
kicad_plugins/via_fence_generator/viafence_basedialogs.py
Normal 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()
|
||||
|
||||
|
||||
30
kicad_plugins/via_fence_generator/viafence_dialogs.py
Normal 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())
|
||||
|
||||