Merge pull request #40 from marc4492/#38-CreateGCodefromimage
Convert matrix index to gcode
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,3 +2,6 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
\.idea/
|
\.idea/
|
||||||
|
|
||||||
|
pcbdevice/resources/output/
|
||||||
|
pcbdevice/tests/resources/output/
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
def listToGCode(listIndex, pHeight, pWidth):
|
def listToGCode(listIndex, pHeight, pWidth):
|
||||||
|
"""
|
||||||
|
Convert a list of matrix coordinate in a list of GCode commands
|
||||||
|
|
||||||
|
:param listIndex: List of coordinate
|
||||||
|
:param pHeight: Pixel height in mm
|
||||||
|
:param pWidth: Pixel width in mm
|
||||||
|
:return: List of all the GCode commands
|
||||||
|
"""
|
||||||
gcodeCommand = []
|
gcodeCommand = []
|
||||||
toolUp = True
|
toolUp = True
|
||||||
|
|
||||||
@@ -14,7 +22,7 @@ def listToGCode(listIndex, pHeight, pWidth):
|
|||||||
gcodeCommand.append('G0 Z0')
|
gcodeCommand.append('G0 Z0')
|
||||||
toolUp = True
|
toolUp = True
|
||||||
else:
|
else:
|
||||||
gcodeCommand.append('G0 X' + str(coord.getX()*pWidth) + ' Y' + str(coord.getY()*pHeight))
|
gcodeCommand.append('G0 X' + str(round(coord.getX()*pWidth, 2)) + ' Y' + str(round(coord.getY()*pHeight, 2)))
|
||||||
if toolUp:
|
if toolUp:
|
||||||
gcodeCommand.append('G0 Z3')
|
gcodeCommand.append('G0 Z3')
|
||||||
toolUp = False
|
toolUp = False
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
from pcbdevice.utils.path import path
|
import math
|
||||||
from pcbdevice.utils.plotimg import plotPath
|
|
||||||
|
from pcbdevice.gcode.GcodeBuilder import listToGCode
|
||||||
|
|
||||||
|
from pcbdevice.gcode.GcodeCreator import createSequence
|
||||||
|
from pcbdevice.gcode.path import path
|
||||||
from pcbdevice.utils.FileUtils import FileUtils
|
from pcbdevice.utils.FileUtils import FileUtils
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(prog = 'main.py')
|
parser = argparse.ArgumentParser(prog = 'main.py')
|
||||||
parser.add_argument('-i', required = True, help = 'PCB image path')
|
parser.add_argument('-i', required = True, help = 'PCB image path')
|
||||||
|
parser.add_argument('-o', required = True, help = 'Gcode output path')
|
||||||
parser.add_argument('-wi', required = True, type = int, help = 'Width of the PCB')
|
parser.add_argument('-wi', required = True, type = int, help = 'Width of the PCB')
|
||||||
parser.add_argument('-he', required = True, type = int, help = 'Height of the PCB')
|
parser.add_argument('-he', required = True, type = int, help = 'Height of the PCB')
|
||||||
|
parser.add_argument('-t', required = True, type = int, help = 'Tool\'s radius in mm')
|
||||||
parser.add_argument('-u', required = False, help = 'PCB dimension unit')
|
parser.add_argument('-u', required = False, help = 'PCB dimension unit')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -17,13 +23,13 @@ if __name__ == "__main__":
|
|||||||
pxHeight, pxWidth = FileUtils.getPixelSize(height, width, args.he, args.wi, unit = args.u)
|
pxHeight, pxWidth = FileUtils.getPixelSize(height, width, args.he, args.wi, unit = args.u)
|
||||||
else:
|
else:
|
||||||
pxHeight, pxWidth = FileUtils.getPixelSize(height, width, args.he, args.wi)
|
pxHeight, pxWidth = FileUtils.getPixelSize(height, width, args.he, args.wi)
|
||||||
|
|
||||||
|
if pxHeight > pxWidth:
|
||||||
resourcesRawPath = 'tests/resources/raw/'
|
rTool = int(math.ceil(args.t * pxHeight))
|
||||||
resourcesFormattedPath = 'tests/resources/formatted/'
|
else:
|
||||||
resourcesPathOutput = 'resources/pathoutput/'
|
rTool = int(math.ceil(args.t * pxWidth))
|
||||||
resourcesExpectedPath = 'tests/resources/expected/'
|
|
||||||
|
matrixUpdated = path(matrix, rTool)
|
||||||
#FileUtils.saveMatrixToFile(FileUtils.pbmToMatrix(resourcesRawPath + 'test1ascii.pbm'), resourcesFormattedPath + 'test1.csv')
|
listIndexes = createSequence(matrixUpdated)
|
||||||
|
gcode = listToGCode(listIndexes, pxHeight, pxWidth)
|
||||||
#plotPath(path(FileUtils.pbmToMatrix(resourcesRawPath + 'test100x100.pbm'), 5))
|
FileUtils.saveStringListToFile(gcode, args.o)
|
||||||
@@ -5,37 +5,56 @@ from pcbdevice.models.Coordinates import Coordinate
|
|||||||
|
|
||||||
|
|
||||||
class TestListToGCode(TestCase):
|
class TestListToGCode(TestCase):
|
||||||
|
|
||||||
|
oneTrace = [Coordinate(1, 2),
|
||||||
|
Coordinate(1, 5),
|
||||||
|
Coordinate(2, 5),
|
||||||
|
Coordinate(2, 8)]
|
||||||
|
|
||||||
|
twoTrace = [Coordinate(1, 2),
|
||||||
|
Coordinate(1, 5),
|
||||||
|
Coordinate(5, 5),
|
||||||
|
Coordinate(5, 2),
|
||||||
|
Coordinate(1, 2),
|
||||||
|
Coordinate(-1, -1),
|
||||||
|
Coordinate(5, 4),
|
||||||
|
Coordinate(8, 4)]
|
||||||
|
|
||||||
|
threeTrace = [Coordinate(1, 2),
|
||||||
|
Coordinate(1, 5),
|
||||||
|
Coordinate(5, 5),
|
||||||
|
Coordinate(5, 2),
|
||||||
|
Coordinate(1, 2),
|
||||||
|
Coordinate(-1, -1),
|
||||||
|
Coordinate(5, 4),
|
||||||
|
Coordinate(8, 4),
|
||||||
|
Coordinate(2, 9),
|
||||||
|
Coordinate(9, 45),
|
||||||
|
Coordinate(12, 12),
|
||||||
|
Coordinate(1, 10)]
|
||||||
|
|
||||||
def test_listToGCodeMultipleTrace(self):
|
def test_listToGCodeMultipleTrace(self):
|
||||||
xSize, ySize = 2, 3
|
xSize, ySize = 2.0, 3.0
|
||||||
|
|
||||||
coords = whenSingleTrace()
|
self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, ySize, xSize))
|
||||||
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize))
|
self.assertEqual(listToGCode(self.twoTrace, ySize, xSize), getExpected(self.twoTrace, ySize, xSize))
|
||||||
|
self.assertEqual(listToGCode(self.threeTrace, ySize, xSize), getExpected(self.threeTrace, ySize, xSize))
|
||||||
coords = whenTwoTrace()
|
|
||||||
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize))
|
|
||||||
|
|
||||||
coords = whenThreeTrace()
|
|
||||||
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize))
|
|
||||||
|
|
||||||
def test_listToGCodePixelSize(self):
|
def test_listToGCodePixelSize(self):
|
||||||
xSize, ySize = 1, 4
|
xSize, ySize = 1.0, 4.0
|
||||||
coords = whenSingleTrace()
|
self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, ySize, xSize))
|
||||||
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize))
|
|
||||||
|
|
||||||
xSize, ySize = 4, 2
|
xSize, ySize = 4.0, 2.0
|
||||||
coords = whenSingleTrace()
|
self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, ySize, xSize))
|
||||||
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize))
|
|
||||||
|
|
||||||
xSize, ySize = 8, -1
|
xSize, ySize = 8.0, -1.0
|
||||||
coords = whenSingleTrace()
|
self.assertRaises(RuntimeError, lambda: listToGCode(self.oneTrace, ySize, xSize))
|
||||||
self.assertRaises(RuntimeError, lambda: listToGCode(coords, ySize, xSize))
|
|
||||||
|
|
||||||
|
|
||||||
def getExpected(coords, ySize, xSize):
|
def getExpected(coords, ySize, xSize):
|
||||||
header = ['G28', 'G90\n']
|
header = ['G28', 'G90\n']
|
||||||
footer = ['\nG0 Z0', 'G28']
|
footer = ['\nG0 Z0', 'G28']
|
||||||
|
|
||||||
content = ['G0 X' + str(xSize * coords[0].getX()) + ' Y' + str(ySize * coords[0].getY()),
|
content = ['G0 X' + str(round(xSize * coords[0].getX(), 2)) + ' Y' + str(round(ySize * coords[0].getY(), 2)),
|
||||||
'G0 Z3',
|
'G0 Z3',
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -48,37 +67,4 @@ def getExpected(coords, ySize, xSize):
|
|||||||
else:
|
else:
|
||||||
content.append('G0 Z0')
|
content.append('G0 Z0')
|
||||||
|
|
||||||
return header + content + footer
|
return header + content + footer
|
||||||
|
|
||||||
|
|
||||||
def whenSingleTrace():
|
|
||||||
return [Coordinate(1, 2),
|
|
||||||
Coordinate(1, 5),
|
|
||||||
Coordinate(2, 5),
|
|
||||||
Coordinate(2, 8)]
|
|
||||||
|
|
||||||
|
|
||||||
def whenTwoTrace():
|
|
||||||
return [Coordinate(1, 2),
|
|
||||||
Coordinate(1, 5),
|
|
||||||
Coordinate(5, 5),
|
|
||||||
Coordinate(5, 2),
|
|
||||||
Coordinate(1, 2),
|
|
||||||
Coordinate(-1, -1),
|
|
||||||
Coordinate(5, 4),
|
|
||||||
Coordinate(8, 4)]
|
|
||||||
|
|
||||||
|
|
||||||
def whenThreeTrace():
|
|
||||||
return [Coordinate(1, 2),
|
|
||||||
Coordinate(1, 5),
|
|
||||||
Coordinate(5, 5),
|
|
||||||
Coordinate(5, 2),
|
|
||||||
Coordinate(1, 2),
|
|
||||||
Coordinate(-1, -1),
|
|
||||||
Coordinate(5, 4),
|
|
||||||
Coordinate(8, 4),
|
|
||||||
Coordinate(2, 9),
|
|
||||||
Coordinate(9, 45),
|
|
||||||
Coordinate(12, 12),
|
|
||||||
Coordinate(1, 10)]
|
|
||||||
5
pcbdevice/tests/resources/expected/text1.txt
Normal file
5
pcbdevice/tests/resources/expected/text1.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
This
|
||||||
|
is
|
||||||
|
a
|
||||||
|
|
||||||
|
test
|
||||||
5
pcbdevice/tests/resources/expected/text2.txt
Normal file
5
pcbdevice/tests/resources/expected/text2.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
G28
|
||||||
|
G90
|
||||||
|
G0 Z3
|
||||||
|
|
||||||
|
G0 X15 Y45
|
||||||
@@ -2,6 +2,7 @@ from unittest import TestCase
|
|||||||
|
|
||||||
from pcbdevice.utils import TestUtils
|
from pcbdevice.utils import TestUtils
|
||||||
from pcbdevice.utils.FileUtils import FileUtils
|
from pcbdevice.utils.FileUtils import FileUtils
|
||||||
|
from pcbdevice.utils.TestUtils import readStringFile
|
||||||
|
|
||||||
resources = './pcbdevice/tests/resources/'
|
resources = './pcbdevice/tests/resources/'
|
||||||
|
|
||||||
@@ -9,18 +10,35 @@ class TestFileUtils(TestCase):
|
|||||||
def test_pbmToMatrix(self):
|
def test_pbmToMatrix(self):
|
||||||
actual, h, w = FileUtils.pbmToMatrix(resources + 'raw/test1.pbm')
|
actual, h, w = FileUtils.pbmToMatrix(resources + 'raw/test1.pbm')
|
||||||
expected = TestUtils.readIntFile(resources + 'formatted/test1.csv')
|
expected = TestUtils.readIntFile(resources + 'formatted/test1.csv')
|
||||||
assert actual == expected
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
def test_saveMatrixToFile(self):
|
def test_saveMatrixToFile(self):
|
||||||
actual, h, w = FileUtils.pbmToMatrix(resources + 'raw/test1.pbm')
|
actual, h, w = FileUtils.pbmToMatrix(resources + 'raw/test1.pbm')
|
||||||
FileUtils.saveMatrixToFile(actual, resources + 'output/test1.csv')
|
FileUtils.saveMatrixToFile(actual, resources + 'output/test1.csv')
|
||||||
expected = TestUtils.readIntFile(resources + 'output/test1.csv')
|
expected = TestUtils.readIntFile(resources + 'output/test1.csv')
|
||||||
assert actual == expected
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test_saveStringListToFile(self):
|
||||||
|
stringList = ['This',
|
||||||
|
'is',
|
||||||
|
'a\n',
|
||||||
|
'test']
|
||||||
|
|
||||||
|
FileUtils.saveStringListToFile(stringList, resources + 'output/text1.txt')
|
||||||
|
self.assertEqual(readStringFile(resources + 'output/text1.txt'), readStringFile(resources+'expected/text1.txt'))
|
||||||
|
|
||||||
|
stringList = ['G28',
|
||||||
|
'G90',
|
||||||
|
'G0 Z3\n',
|
||||||
|
'G0 X15 Y45']
|
||||||
|
|
||||||
|
FileUtils.saveStringListToFile(stringList, resources + 'output/text2.txt')
|
||||||
|
self.assertEqual(readStringFile(resources + 'output/text2.txt'), readStringFile(resources + 'expected/text2.txt'))
|
||||||
|
|
||||||
def test_getPixelSize(self):
|
def test_getPixelSize(self):
|
||||||
assert 10, 10 == FileUtils.getPixelSize(10, 10, 100, 100)
|
self.assertEqual((10, 10), FileUtils.getPixelSize(10, 10, 100, 100))
|
||||||
assert 1, 1 == FileUtils.getPixelSize(100, 100, 100, 100)
|
self.assertEqual((1, 1), FileUtils.getPixelSize(100, 100, 100, 100))
|
||||||
assert 10, 10 == FileUtils.getPixelSize(10, 10, 10, 10, unit = 'cm')
|
self.assertEqual((10, 10), FileUtils.getPixelSize(10, 10, 10, 10, unit = 'cm'))
|
||||||
assert 10, 10 == FileUtils.getPixelSize(10, 10, 1, 1, unit = 'm')
|
self.assertEqual((25.4, 25.4), FileUtils.getPixelSize(10, 10, 10, 10, unit = 'in'))
|
||||||
assert 254, 254 == FileUtils.getPixelSize(10, 10, 10, 10, unit = 'in')
|
self.assertEqual((1, 2), FileUtils.getPixelSize(10, 10, 10, 20))
|
||||||
assert 10, 5 == FileUtils.getPixelSize(10, 10, 10, 20)
|
self.assertRaises(RuntimeError, lambda: FileUtils.getPixelSize(10, 10, 10, 10, 'ft'))
|
||||||
@@ -3,6 +3,16 @@ import math
|
|||||||
class FileUtils:
|
class FileUtils:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pbmToMatrix(pbmFilePath, dimensionLineIndex = 2):
|
def pbmToMatrix(pbmFilePath, dimensionLineIndex = 2):
|
||||||
|
"""
|
||||||
|
Read a pbm file and convert in an int matrix
|
||||||
|
|
||||||
|
:param str pbmFilePath: Path of the ascii pbm file to convert in a matrix
|
||||||
|
:param int dimensionLineIndex: Line index containing the dimension of the image
|
||||||
|
:return matrix: Matrix with image value
|
||||||
|
:return height: Height of the matrix
|
||||||
|
:return width: Width of the matrix
|
||||||
|
"""
|
||||||
|
|
||||||
completeFile = []
|
completeFile = []
|
||||||
|
|
||||||
file = open(pbmFilePath, 'r')
|
file = open(pbmFilePath, 'r')
|
||||||
@@ -22,6 +32,13 @@ class FileUtils:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def saveMatrixToFile(matrix, filePath):
|
def saveMatrixToFile(matrix, filePath):
|
||||||
|
"""
|
||||||
|
Save a matrix in a plain text file
|
||||||
|
|
||||||
|
:param matrix: Matrix to save
|
||||||
|
:param filePath: Path of the output file
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
with open(filePath, 'w') as f:
|
with open(filePath, 'w') as f:
|
||||||
for x in matrix:
|
for x in matrix:
|
||||||
for y in x:
|
for y in x:
|
||||||
@@ -29,8 +46,34 @@ class FileUtils:
|
|||||||
f.write('\n')
|
f.write('\n')
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def saveStringListToFile(stringList, filePath):
|
||||||
|
"""
|
||||||
|
Save a string list into a plain text file with a carriage return after each entry
|
||||||
|
|
||||||
|
:param stringList: List of string to write
|
||||||
|
:param filePath: File path to write the text
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
with open(filePath, 'w') as f:
|
||||||
|
for line in stringList:
|
||||||
|
f.write('%s\n' % line)
|
||||||
|
f.close()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getPixelSize(matHeight, matWidth, pcbHeight, pcbWidth, unit = 'mm'):
|
def getPixelSize(matHeight, matWidth, pcbHeight, pcbWidth, unit = 'mm'):
|
||||||
|
"""
|
||||||
|
Get pixel width and height with the real image size
|
||||||
|
|
||||||
|
:param matHeight: Height of the image matrix (Nb pixels)
|
||||||
|
:param matWidth: Width of the image matrix (Nb pixels)
|
||||||
|
:param pcbHeight: True height of the image (PCB)
|
||||||
|
:param pcbWidth: True width of the image (PCB)
|
||||||
|
:param unit: Unit of the size of the image, default in mm
|
||||||
|
:return pixelHeight: Pixel height in mm
|
||||||
|
:return pixelWidth: Pixel width in mm
|
||||||
|
|
||||||
|
"""
|
||||||
if unit == 'mm':
|
if unit == 'mm':
|
||||||
return pcbHeight / matHeight, pcbWidth / matWidth
|
return pcbHeight / matHeight, pcbWidth / matWidth
|
||||||
elif unit == 'cm':
|
elif unit == 'cm':
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
def readIntFile(filePath):
|
def readIntFile(filePath):
|
||||||
|
"""
|
||||||
|
Read a matrix file
|
||||||
|
|
||||||
|
:param filePath: File path to read from
|
||||||
|
:return: The matrix in int
|
||||||
|
"""
|
||||||
completeFile = []
|
completeFile = []
|
||||||
file = open(filePath, 'r')
|
file = open(filePath, 'r')
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
@@ -11,4 +17,17 @@ def readIntFile(filePath):
|
|||||||
|
|
||||||
completeFile.append(tempArray)
|
completeFile.append(tempArray)
|
||||||
|
|
||||||
return completeFile
|
return completeFile
|
||||||
|
|
||||||
|
def readStringFile(filePath):
|
||||||
|
"""
|
||||||
|
Read all lines of a file
|
||||||
|
|
||||||
|
:param filePath: File path to read from
|
||||||
|
:return: Array of all lines in the file
|
||||||
|
"""
|
||||||
|
file = open(filePath, 'r')
|
||||||
|
lines = file.readlines()
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
return lines
|
||||||
Reference in New Issue
Block a user