From 8aee76eee2625598007790085a48608412682e6b Mon Sep 17 00:00:00 2001 From: Marc-Antoine Lafreniere Date: Wed, 20 Feb 2019 13:48:37 -0500 Subject: [PATCH 1/5] Convert matrix index to gcode #39 Save GCode in file Convert a list of index to gcode string in an array --- pcbdevice/tests/gcode/test_listToGcode.py | 84 ++++++++------------ pcbdevice/tests/resources/expected/text1.txt | 5 ++ pcbdevice/tests/resources/expected/text2.txt | 5 ++ pcbdevice/tests/utils/test_fileUtils.py | 34 ++++++-- pcbdevice/utils/FileUtils.py | 7 ++ pcbdevice/utils/TestUtils.py | 9 ++- 6 files changed, 86 insertions(+), 58 deletions(-) create mode 100644 pcbdevice/tests/resources/expected/text1.txt create mode 100644 pcbdevice/tests/resources/expected/text2.txt diff --git a/pcbdevice/tests/gcode/test_listToGcode.py b/pcbdevice/tests/gcode/test_listToGcode.py index 04571a2..b72d3f8 100644 --- a/pcbdevice/tests/gcode/test_listToGcode.py +++ b/pcbdevice/tests/gcode/test_listToGcode.py @@ -5,31 +5,50 @@ 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 - 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)) + 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)) + 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)) - + self.assertRaises(RuntimeError, lambda: listToGCode(self.oneTrace, ySize, xSize)) def getExpected(coords, ySize, xSize): header = ['G28', 'G90\n'] @@ -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)] \ No newline at end of file + return header + content + footer \ No newline at end of file diff --git a/pcbdevice/tests/resources/expected/text1.txt b/pcbdevice/tests/resources/expected/text1.txt new file mode 100644 index 0000000..8e78941 --- /dev/null +++ b/pcbdevice/tests/resources/expected/text1.txt @@ -0,0 +1,5 @@ +This +is +a + +test diff --git a/pcbdevice/tests/resources/expected/text2.txt b/pcbdevice/tests/resources/expected/text2.txt new file mode 100644 index 0000000..c7cf44c --- /dev/null +++ b/pcbdevice/tests/resources/expected/text2.txt @@ -0,0 +1,5 @@ +G28 +G90 +G0 Z3 + +G0 X15 Y45 diff --git a/pcbdevice/tests/utils/test_fileUtils.py b/pcbdevice/tests/utils/test_fileUtils.py index 2ecffa6..f87f846 100644 --- a/pcbdevice/tests/utils/test_fileUtils.py +++ b/pcbdevice/tests/utils/test_fileUtils.py @@ -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) \ No newline at end of file + 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((10, 10), FileUtils.getPixelSize(10, 10, 1, 1, unit = 'm')) + self.assertEqual((25.4, 25.4), FileUtils.getPixelSize(10, 10, 10, 10, unit = 'in')) + self.assertEqual((1, 2), FileUtils.getPixelSize(10, 10, 10, 20)) \ No newline at end of file diff --git a/pcbdevice/utils/FileUtils.py b/pcbdevice/utils/FileUtils.py index 08caf56..3014aa9 100644 --- a/pcbdevice/utils/FileUtils.py +++ b/pcbdevice/utils/FileUtils.py @@ -29,6 +29,13 @@ class FileUtils: f.write('\n') f.close() + @staticmethod + def saveStringListToFile(stringList, filePath): + 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'): if unit == 'mm': diff --git a/pcbdevice/utils/TestUtils.py b/pcbdevice/utils/TestUtils.py index f5904f4..384e50d 100644 --- a/pcbdevice/utils/TestUtils.py +++ b/pcbdevice/utils/TestUtils.py @@ -11,4 +11,11 @@ def readIntFile(filePath): completeFile.append(tempArray) - return completeFile \ No newline at end of file + return completeFile + +def readStringFile(filePath): + file = open(filePath, 'r') + lines = file.readlines() + file.close() + + return lines \ No newline at end of file From f5599ce4b70070854e385fa812e5ea092647c762 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Lafreniere Date: Thu, 21 Feb 2019 13:41:08 -0500 Subject: [PATCH 2/5] Create gcode file from image with arguments #38-Create GCode from image closes #38 --- .gitignore | 3 +++ pcbdevice/gcode/GcodeBuilder.py | 2 +- pcbdevice/main.py | 26 +++++++++++++------------ pcbdevice/tests/utils/test_fileUtils.py | 3 ++- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 454ad24..583c4a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ *.pyc \.idea/ + +pcbdevice/resources/output/ +pcbdevice/tests/resources/output/ diff --git a/pcbdevice/gcode/GcodeBuilder.py b/pcbdevice/gcode/GcodeBuilder.py index 4042389..ce28059 100644 --- a/pcbdevice/gcode/GcodeBuilder.py +++ b/pcbdevice/gcode/GcodeBuilder.py @@ -14,7 +14,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 diff --git a/pcbdevice/main.py b/pcbdevice/main.py index 637ac52..637e385 100644 --- a/pcbdevice/main.py +++ b/pcbdevice/main.py @@ -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,9 @@ 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)) \ No newline at end of file + + rTool = int(math.ceil(args.t * pxHeight if pxHeight > pxWidth else pxWidth)) + matrixUpdated = path(matrix, rTool) + listIndexes = createSequence(matrixUpdated) + gcode = listToGCode(listIndexes, pxHeight, pxWidth) + FileUtils.saveStringListToFile(gcode, args.o) \ No newline at end of file diff --git a/pcbdevice/tests/utils/test_fileUtils.py b/pcbdevice/tests/utils/test_fileUtils.py index f87f846..c3bc4f3 100644 --- a/pcbdevice/tests/utils/test_fileUtils.py +++ b/pcbdevice/tests/utils/test_fileUtils.py @@ -41,4 +41,5 @@ class TestFileUtils(TestCase): self.assertEqual((10, 10), FileUtils.getPixelSize(10, 10, 10, 10, unit = 'cm')) self.assertEqual((10, 10), FileUtils.getPixelSize(10, 10, 1, 1, unit = 'm')) self.assertEqual((25.4, 25.4), FileUtils.getPixelSize(10, 10, 10, 10, unit = 'in')) - self.assertEqual((1, 2), FileUtils.getPixelSize(10, 10, 10, 20)) \ No newline at end of file + self.assertEqual((1, 2), FileUtils.getPixelSize(10, 10, 10, 20)) + self.assertRaises(RuntimeError, lambda: FileUtils.getPixelSize(10, 10, 10, 10, 'ft')) \ No newline at end of file From 1b4d729e87fc93d3b4ecc379dd807977dab92742 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Lafreniere Date: Thu, 21 Feb 2019 13:54:44 -0500 Subject: [PATCH 3/5] Build fix --- pcbdevice/tests/gcode/test_listToGcode.py | 10 +++++----- pcbdevice/tests/utils/test_fileUtils.py | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pcbdevice/tests/gcode/test_listToGcode.py b/pcbdevice/tests/gcode/test_listToGcode.py index b72d3f8..3ca9a51 100644 --- a/pcbdevice/tests/gcode/test_listToGcode.py +++ b/pcbdevice/tests/gcode/test_listToGcode.py @@ -34,27 +34,27 @@ class TestListToGCode(TestCase): Coordinate(1, 10)] def test_listToGCodeMultipleTrace(self): - xSize, ySize = 2, 3 + xSize, ySize = 2.0, 3.0 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 + xSize, ySize = 1.0, 4.0 self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, ySize, xSize)) - xSize, ySize = 4, 2 + xSize, ySize = 4.0, 2.0 self.assertEqual(listToGCode(self.oneTrace, ySize, xSize), getExpected(self.oneTrace, ySize, xSize)) - xSize, ySize = 8, -1 + 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', ] diff --git a/pcbdevice/tests/utils/test_fileUtils.py b/pcbdevice/tests/utils/test_fileUtils.py index c3bc4f3..69054b2 100644 --- a/pcbdevice/tests/utils/test_fileUtils.py +++ b/pcbdevice/tests/utils/test_fileUtils.py @@ -39,7 +39,6 @@ class TestFileUtils(TestCase): 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((10, 10), FileUtils.getPixelSize(10, 10, 1, 1, unit = 'm')) 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')) \ No newline at end of file From 46a3a02b2ac7ada3b24e81e0bde6a7971eb56fc2 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Lafreniere Date: Tue, 26 Feb 2019 17:51:53 -0500 Subject: [PATCH 4/5] Add functions description Add python documentation --- pcbdevice/gcode/GcodeBuilder.py | 8 ++++++++ pcbdevice/utils/FileUtils.py | 36 +++++++++++++++++++++++++++++++++ pcbdevice/utils/TestUtils.py | 12 +++++++++++ 3 files changed, 56 insertions(+) diff --git a/pcbdevice/gcode/GcodeBuilder.py b/pcbdevice/gcode/GcodeBuilder.py index ce28059..603bc16 100644 --- a/pcbdevice/gcode/GcodeBuilder.py +++ b/pcbdevice/gcode/GcodeBuilder.py @@ -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 diff --git a/pcbdevice/utils/FileUtils.py b/pcbdevice/utils/FileUtils.py index 3014aa9..80648fb 100644 --- a/pcbdevice/utils/FileUtils.py +++ b/pcbdevice/utils/FileUtils.py @@ -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: @@ -31,6 +48,13 @@ class FileUtils: @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) @@ -38,6 +62,18 @@ class FileUtils: @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': diff --git a/pcbdevice/utils/TestUtils.py b/pcbdevice/utils/TestUtils.py index 384e50d..a8f1dce 100644 --- a/pcbdevice/utils/TestUtils.py +++ b/pcbdevice/utils/TestUtils.py @@ -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() @@ -14,6 +20,12 @@ def readIntFile(filePath): 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() From 921e04f88c91b8903af0998fa662692246839415 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Lafreniere Date: Tue, 26 Feb 2019 20:54:19 -0500 Subject: [PATCH 5/5] Fix issue with tool size --- pcbdevice/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pcbdevice/main.py b/pcbdevice/main.py index 637e385..9959568 100644 --- a/pcbdevice/main.py +++ b/pcbdevice/main.py @@ -24,7 +24,11 @@ if __name__ == "__main__": else: pxHeight, pxWidth = FileUtils.getPixelSize(height, width, args.he, args.wi) - rTool = int(math.ceil(args.t * pxHeight if pxHeight > pxWidth else pxWidth)) + 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)