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
|
||||
|
||||
\.idea/
|
||||
|
||||
pcbdevice/resources/output/
|
||||
pcbdevice/tests/resources/output/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
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.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'))
|
||||
@@ -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':
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user