diff --git a/pcbdevice/gcode/GcodeBuilder.py b/pcbdevice/gcode/GcodeBuilder.py new file mode 100644 index 0000000..4042389 --- /dev/null +++ b/pcbdevice/gcode/GcodeBuilder.py @@ -0,0 +1,26 @@ +def listToGCode(listIndex, pHeight, pWidth): + gcodeCommand = [] + toolUp = True + + if pHeight <= 0 or pWidth <= 0: + raise RuntimeError('Pixel dimension error') + + # HEADER + gcodeCommand.append('G28') + gcodeCommand.append('G90\n') + + for coord in listIndex: + if coord.getX() == -1 and coord.getY() == -1: + gcodeCommand.append('G0 Z0') + toolUp = True + else: + gcodeCommand.append('G0 X' + str(coord.getX()*pWidth) + ' Y' + str(coord.getY()*pHeight)) + if toolUp: + gcodeCommand.append('G0 Z3') + toolUp = False + + # FOOTER + gcodeCommand.append('\nG0 Z0') + gcodeCommand.append('G28') + + return gcodeCommand \ No newline at end of file diff --git a/pcbdevice/gcode/__init__.py b/pcbdevice/gcode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pcbdevice/main.py b/pcbdevice/main.py index 2cc7f6b..637ac52 100644 --- a/pcbdevice/main.py +++ b/pcbdevice/main.py @@ -1,15 +1,29 @@ from pcbdevice.utils.path import path from pcbdevice.utils.plotimg import plotPath from pcbdevice.utils.FileUtils import FileUtils +import argparse if __name__ == "__main__": - # Usage example + parser = argparse.ArgumentParser(prog = 'main.py') + parser.add_argument('-i', required = True, help = 'PCB image 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('-u', required = False, help = 'PCB dimension unit') + args = parser.parse_args() + + matrix, height, width = FileUtils.pbmToMatrix(args.i) + + if args.u: + 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.pbmToCsv(resourcesRawPath + 'test1ascii.pbm'), resourcesFormattedPath + 'test1.csv') + #FileUtils.saveMatrixToFile(FileUtils.pbmToMatrix(resourcesRawPath + 'test1ascii.pbm'), resourcesFormattedPath + 'test1.csv') - plotPath(path(FileUtils.pbmToCsv(resourcesRawPath + 'test100x100.pbm'), 5)) \ No newline at end of file + #plotPath(path(FileUtils.pbmToMatrix(resourcesRawPath + 'test100x100.pbm'), 5)) \ No newline at end of file diff --git a/pcbdevice/models/Coordinates.py b/pcbdevice/models/Coordinates.py new file mode 100644 index 0000000..1d9728e --- /dev/null +++ b/pcbdevice/models/Coordinates.py @@ -0,0 +1,19 @@ +class Coordinate: + _x = -1 + _y = -1 + + def __init__(self, x = -1, y = -1): + self._x = x + self._y = y + + def setX(self, x): + self._x = x + + def setY(self, y): + self._y = y + + def getX(self): + return self._x + + def getY(self): + return self._y \ No newline at end of file diff --git a/pcbdevice/models/__init__.py b/pcbdevice/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pcbdevice/resources/input/square.pbm b/pcbdevice/resources/input/square.pbm new file mode 100644 index 0000000..8b34f1c --- /dev/null +++ b/pcbdevice/resources/input/square.pbm @@ -0,0 +1,23 @@ +P1 +# Bob +20 20 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 0 +0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 +0 0 2 0 1 1 1 1 1 1 1 1 1 1 1 1 0 2 0 0 +0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 1 0 2 0 0 +0 0 2 0 1 0 2 2 2 2 2 2 2 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 0 0 0 0 0 0 2 0 1 0 2 0 0 +0 0 2 0 1 0 2 2 2 2 2 2 2 2 0 1 0 2 0 0 +0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 1 0 2 0 0 +0 0 2 0 1 1 1 1 1 1 1 1 1 1 1 1 0 2 0 0 +0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 +0 0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 0 diff --git a/pcbdevice/tests/gcode/__init__.py b/pcbdevice/tests/gcode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pcbdevice/tests/gcode/test_listToGcode.py b/pcbdevice/tests/gcode/test_listToGcode.py new file mode 100644 index 0000000..04571a2 --- /dev/null +++ b/pcbdevice/tests/gcode/test_listToGcode.py @@ -0,0 +1,84 @@ +from unittest import TestCase + +from pcbdevice.gcode.GcodeBuilder import listToGCode +from pcbdevice.models.Coordinates import Coordinate + + +class TestListToGCode(TestCase): + 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)) + + def test_listToGCodePixelSize(self): + xSize, ySize = 1, 4 + coords = whenSingleTrace() + self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize)) + + xSize, ySize = 4, 2 + coords = whenSingleTrace() + self.assertEqual(listToGCode(coords, ySize, xSize), getExpected(coords, ySize, xSize)) + + xSize, ySize = 8, -1 + coords = whenSingleTrace() + self.assertRaises(RuntimeError, lambda: listToGCode(coords, 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()), + 'G0 Z3', + ] + + for index, coord in enumerate(coords): + if index > 0: + if coord.getX() != -1 and coord.getY() != -1: + content.append('G0 X' + str(xSize * coord.getX()) + ' Y' + str(ySize * coord.getY())) + if coords[index - 1].getX() == -1 and coords[index - 1].getX() == -1: + content.append('G0 Z3') + 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 diff --git a/pcbdevice/tests/utils/test_fileUtils.py b/pcbdevice/tests/utils/test_fileUtils.py index de95ad3..2ecffa6 100644 --- a/pcbdevice/tests/utils/test_fileUtils.py +++ b/pcbdevice/tests/utils/test_fileUtils.py @@ -6,13 +6,21 @@ from pcbdevice.utils.FileUtils import FileUtils resources = './pcbdevice/tests/resources/' class TestFileUtils(TestCase): - def test_pbmToCsv(self): - actual = FileUtils.pbmToCsv(resources + 'raw/test1.pbm') + def test_pbmToMatrix(self): + actual, h, w = FileUtils.pbmToMatrix(resources + 'raw/test1.pbm') expected = TestUtils.readIntFile(resources + 'formatted/test1.csv') assert actual == expected def test_saveMatrixToFile(self): - actual = FileUtils.pbmToCsv(resources + 'raw/test1.pbm') + 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 + + 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 diff --git a/pcbdevice/utils/FileUtils.py b/pcbdevice/utils/FileUtils.py index 4072edf..08caf56 100644 --- a/pcbdevice/utils/FileUtils.py +++ b/pcbdevice/utils/FileUtils.py @@ -2,10 +2,10 @@ import math class FileUtils: @staticmethod - def pbmToCsv(pbmFile, dimensionLineIndex = 2): + def pbmToMatrix(pbmFilePath, dimensionLineIndex = 2): completeFile = [] - file = open(pbmFile, 'r') + file = open(pbmFilePath, 'r') lines = file.readlines() width, height = (int(val) for val in lines[dimensionLineIndex].split()) file.close() @@ -18,7 +18,7 @@ class FileUtils: for index, value in enumerate(completeFile): matrix[int(math.floor(index / width))][index % width] = value - return matrix + return matrix, height, width @staticmethod def saveMatrixToFile(matrix, filePath): @@ -27,4 +27,17 @@ class FileUtils: for y in x: f.write('%s ' % y ) f.write('\n') - f.close() \ No newline at end of file + f.close() + + @staticmethod + def getPixelSize(matHeight, matWidth, pcbHeight, pcbWidth, unit = 'mm'): + if unit == 'mm': + return pcbHeight / matHeight, pcbWidth / matWidth + elif unit == 'cm': + return pcbHeight / matHeight * 10, pcbWidth / matWidth * 10 + elif unit == 'm': + return pcbHeight / matHeight * 100, pcbWidth / matWidth * 100 + elif unit == 'in': + return pcbHeight / matHeight * 25.4, pcbWidth / matWidth * 25.4 + else: + raise RuntimeError('Unit not handle') \ No newline at end of file