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
\.idea/
pcbdevice/resources/output/
pcbdevice/tests/resources/output/

View File

@@ -1,4 +1,12 @@
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 = []
toolUp = True
@@ -14,7 +22,7 @@ def listToGCode(listIndex, pHeight, pWidth):
gcodeCommand.append('G0 Z0')
toolUp = True
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:
gcodeCommand.append('G0 Z3')
toolUp = False

View File

@@ -1,13 +1,19 @@
from pcbdevice.utils.path import path
from pcbdevice.utils.plotimg import plotPath
import math
from pcbdevice.gcode.GcodeBuilder import listToGCode
from pcbdevice.gcode.GcodeCreator import createSequence
from pcbdevice.gcode.path import path
from pcbdevice.utils.FileUtils import FileUtils
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog = 'main.py')
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('-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')
args = parser.parse_args()
@@ -17,13 +23,13 @@ if __name__ == "__main__":
pxHeight, pxWidth = FileUtils.getPixelSize(height, width, args.he, args.wi, unit = args.u)
else:
pxHeight, pxWidth = FileUtils.getPixelSize(height, width, args.he, args.wi)
resourcesRawPath = 'tests/resources/raw/'
resourcesFormattedPath = 'tests/resources/formatted/'
resourcesPathOutput = 'resources/pathoutput/'
resourcesExpectedPath = 'tests/resources/expected/'
#FileUtils.saveMatrixToFile(FileUtils.pbmToMatrix(resourcesRawPath + 'test1ascii.pbm'), resourcesFormattedPath + 'test1.csv')
#plotPath(path(FileUtils.pbmToMatrix(resourcesRawPath + 'test100x100.pbm'), 5))
if pxHeight > pxWidth:
rTool = int(math.ceil(args.t * pxHeight))
else:
rTool = int(math.ceil(args.t * pxWidth))
matrixUpdated = path(matrix, rTool)
listIndexes = createSequence(matrixUpdated)
gcode = listToGCode(listIndexes, pxHeight, pxWidth)
FileUtils.saveStringListToFile(gcode, args.o)

View File

@@ -5,37 +5,56 @@ from pcbdevice.models.Coordinates import Coordinate
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):
xSize, ySize = 2, 3
xSize, ySize = 2.0, 3.0
coords = whenSingleTrace()
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, 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))
self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, 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))
def test_listToGCodePixelSize(self):
xSize, ySize = 1, 4
coords = whenSingleTrace()
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize))
xSize, ySize = 1.0, 4.0
self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, ySize, xSize))
xSize, ySize = 4, 2
coords = whenSingleTrace()
self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize))
xSize, ySize = 4.0, 2.0
self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, ySize, xSize))
xSize, ySize = 8, -1
coords = whenSingleTrace()
self.assertRaises(RuntimeError, lambda: listToGCode(coords, ySize, xSize))
xSize, ySize = 8.0, -1.0
self.assertRaises(RuntimeError, lambda: listToGCode(self.oneTrace, ySize, xSize))
def getExpected(coords, ySize, xSize):
header = ['G28', 'G90\n']
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',
]
@@ -48,37 +67,4 @@ def getExpected(coords, ySize, xSize):
else:
content.append('G0 Z0')
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)]
return header + content + footer

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.FileUtils import FileUtils
from pcbdevice.utils.TestUtils import readStringFile
resources = './pcbdevice/tests/resources/'
@@ -9,18 +10,35 @@ class TestFileUtils(TestCase):
def test_pbmToMatrix(self):
actual, h, w = FileUtils.pbmToMatrix(resources + 'raw/test1.pbm')
expected = TestUtils.readIntFile(resources + 'formatted/test1.csv')
assert actual == expected
self.assertEqual(actual, expected)
def test_saveMatrixToFile(self):
actual, h, w = FileUtils.pbmToMatrix(resources + 'raw/test1.pbm')
FileUtils.saveMatrixToFile(actual, 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):
assert 10, 10 == FileUtils.getPixelSize(10, 10, 100, 100)
assert 1, 1 == FileUtils.getPixelSize(100, 100, 100, 100)
assert 10, 10 == FileUtils.getPixelSize(10, 10, 10, 10, unit = 'cm')
assert 10, 10 == FileUtils.getPixelSize(10, 10, 1, 1, unit = 'm')
assert 254, 254 == FileUtils.getPixelSize(10, 10, 10, 10, unit = 'in')
assert 10, 5 == FileUtils.getPixelSize(10, 10, 10, 20)
self.assertEqual((10, 10), FileUtils.getPixelSize(10, 10, 100, 100))
self.assertEqual((1, 1), FileUtils.getPixelSize(100, 100, 100, 100))
self.assertEqual((10, 10), FileUtils.getPixelSize(10, 10, 10, 10, unit = 'cm'))
self.assertEqual((25.4, 25.4), FileUtils.getPixelSize(10, 10, 10, 10, unit = 'in'))
self.assertEqual((1, 2), 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:
@staticmethod
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 = []
file = open(pbmFilePath, 'r')
@@ -22,6 +32,13 @@ class FileUtils:
@staticmethod
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:
for x in matrix:
for y in x:
@@ -29,8 +46,34 @@ class FileUtils:
f.write('\n')
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
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':
return pcbHeight / matHeight, pcbWidth / matWidth
elif unit == 'cm':

View File

@@ -1,4 +1,10 @@
def readIntFile(filePath):
"""
Read a matrix file
:param filePath: File path to read from
:return: The matrix in int
"""
completeFile = []
file = open(filePath, 'r')
lines = file.readlines()
@@ -11,4 +17,17 @@ def readIntFile(filePath):
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