Merge pull request #40 from marc4492/#38-CreateGCodefromimage

Convert matrix index to gcode
This commit is contained in:
Marc-Antoine
2019-02-26 21:56:06 -05:00
committed by GitHub
9 changed files with 169 additions and 76 deletions

3
.gitignore vendored
View File

@@ -2,3 +2,6 @@
*.pyc *.pyc
\.idea/ \.idea/
pcbdevice/resources/output/
pcbdevice/tests/resources/output/

View File

@@ -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

View File

@@ -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)

View File

@@ -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)]

View File

@@ -0,0 +1,5 @@
This
is
a
test

View File

@@ -0,0 +1,5 @@
G28
G90
G0 Z3
G0 X15 Y45

View File

@@ -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'))

View File

@@ -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':

View File

@@ -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