├── src ├── pygcode │ ├── dialects │ │ ├── reprap.py │ │ ├── utils.py │ │ ├── __init__.py │ │ ├── mapping.py │ │ └── linuxcnc.py │ ├── exceptions.py │ ├── line.py │ ├── dialect.py │ ├── comment.py │ ├── utils.py │ ├── block.py │ └── words.py └── pygcode.egg-info │ ├── not-zip-safe │ ├── dependency_links.txt │ ├── top_level.txt │ ├── requires.txt │ ├── SOURCES.txt │ └── PKG-INFO ├── dist ├── pygcode-0.2.1.tar.gz ├── pygcode-0.2.1-py2.py3-none-any.whl └── README.md ├── tests ├── runtests.sh ├── test-files │ └── linuxcnc │ │ ├── random-sample-1.gcode │ │ ├── vertical-slot.ngc │ │ ├── Tweakie's CNC Cube2.nc │ │ ├── Circular Pocket.tap │ │ ├── unsupported │ │ ├── Circular Pocket.tap │ │ ├── Mach4 write.tap │ │ ├── Mach3 write.tap │ │ ├── README.md │ │ ├── Trispokedovetiles(laser).tap │ │ ├── DB25.tap │ │ ├── Disc3.tap │ │ ├── plug6a.tap │ │ ├── Cross2.tap │ │ ├── Horse2.tap │ │ ├── Snow White.tap │ │ └── Horse.tap │ │ ├── Mach4 write.tap │ │ ├── Mach3 write.tap │ │ ├── Trispokedovetiles(laser).tap │ │ ├── Smiley001.nc │ │ ├── DB25.tap │ │ ├── Smiley face.tap │ │ ├── plug6a.tap │ │ ├── Disc3.tap │ │ ├── Alien.tap │ │ ├── Cross2.tap │ │ ├── Horse2.tap │ │ ├── Tweakie's CNC Cube.nc │ │ ├── Smiley002.nc │ │ ├── Snow White.tap │ │ ├── Letter A (V-Carve).tap │ │ ├── Scorpion.tap │ │ ├── Clamp.nc │ │ └── Horse.tap ├── testutils.py ├── test_utils.py ├── test_line.py ├── test_parsing.py ├── test_words.py ├── test_machine.py └── test_gcodes.py ├── scripts ├── .gitignore └── pygcode-crop ├── media ├── arc-linearize-method-mid.png ├── arc-linearize-method-inside.png ├── arc-linearize-method-mixed.png ├── arc-linearize-method-outside.png └── README.md ├── setup.cfg ├── .gitignore ├── README.rst ├── setup.py └── deployment ├── README.md └── deploy.sh /src/pygcode/dialects/reprap.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pygcode.egg-info/not-zip-safe: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/pygcode.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/pygcode.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | pygcode 2 | -------------------------------------------------------------------------------- /src/pygcode.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | argparse 2 | euclid3 3 | six 4 | -------------------------------------------------------------------------------- /dist/pygcode-0.2.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fragmuffin/pygcode/HEAD/dist/pygcode-0.2.1.tar.gz -------------------------------------------------------------------------------- /tests/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | python -m unittest discover -s . -p 'test_*.py' --verbose 4 | -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | # GCode files (dropped into this folder during development) 2 | *.gcode 3 | *.ngc 4 | *.g 5 | -------------------------------------------------------------------------------- /media/arc-linearize-method-mid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fragmuffin/pygcode/HEAD/media/arc-linearize-method-mid.png -------------------------------------------------------------------------------- /media/arc-linearize-method-inside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fragmuffin/pygcode/HEAD/media/arc-linearize-method-inside.png -------------------------------------------------------------------------------- /media/arc-linearize-method-mixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fragmuffin/pygcode/HEAD/media/arc-linearize-method-mixed.png -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | [metadata] 5 | description-file = README.rst 6 | license_file = LICENSE 7 | -------------------------------------------------------------------------------- /dist/pygcode-0.2.1-py2.py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fragmuffin/pygcode/HEAD/dist/pygcode-0.2.1-py2.py3-none-any.whl -------------------------------------------------------------------------------- /media/arc-linearize-method-outside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fragmuffin/pygcode/HEAD/media/arc-linearize-method-outside.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # python cache / compilations 2 | *__pycache__ 3 | *.pyc 4 | 5 | # editor backups 6 | *.swp 7 | 8 | # build 9 | build/* 10 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/random-sample-1.gcode: -------------------------------------------------------------------------------- 1 | G17 G20 G90 G94 G54 2 | G0 Z0.25 3 | X-0.5 Y0. 4 | Z0.1 5 | G01 Z0. F5. 6 | G02 X0. Y0.5 I0.5 J0. F2.5 7 | X0.5 Y0. I0. J-0.5 8 | X0. Y-0.5 I-0.5 J0. 9 | X-0.5 Y0. I0. J0.5 10 | G01 Z0.1 F5. 11 | G00 X0. Y0. Z0.25 12 | -------------------------------------------------------------------------------- /src/pygcode/dialects/utils.py: -------------------------------------------------------------------------------- 1 | 2 | # Data Classes 3 | 4 | class WordType(object): 5 | def __init__(self, cls, value_regex, description, clean_value): 6 | self.cls = cls 7 | self.value_regex = value_regex 8 | self.description = description 9 | self.clean_value = clean_value 10 | -------------------------------------------------------------------------------- /media/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## `arc-linearize-method-*.png` 3 | 4 | Screenshots of arc linearizing methods for the `pygcode-norm` script. 5 | 6 | credit to _nraynaud_ for [this awesome online gcode interpreter](https://nraynaud.github.io/webgcode/) 7 | 8 | ## `grbl-stream-*.png` 9 | 10 | Screenshots of the `grbl-stream` script at work 11 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | pygcode 3 | ======= 4 | 5 | GCODE Parser for Python 6 | 7 | Currently in development, ``pygcode`` is a low-level GCode interpreter 8 | for python. 9 | 10 | 11 | Installation 12 | ============ 13 | 14 | Install using ``pip`` 15 | 16 | ``pip install pygcode`` 17 | 18 | or `download directly from PyPi `__ 19 | 20 | 21 | Documentation 22 | ============= 23 | 24 | `Check out the wiki `__ for documentation. 25 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/vertical-slot.ngc: -------------------------------------------------------------------------------- 1 | T5 M06 G43 2 | G17 G90 G21 3 | G00 Z5 S7000 M03 4 | X9.60 Y48.74 5 | Z2 6 | G01 Z-0.5 F100 7 | G02 X10.75 Y47.44 I-0.11 J-1.26 F70 8 | G01 X10.75 Y-47.44 9 | G02 X9.51 Y-48.69 I-1.25 J0 10 | G02 X8.25 Y-47.44 I0 J1.25 11 | G01 X8.25 Y-47.44 12 | X8.25 Y47.44 13 | G02 X9.6 Y48.74 I1.25 J0.05 14 | G00 Z5 15 | Z1.5 16 | G01 Z-1 F100 17 | G02 X10.75 Y47.44 I-0.11 J-1.26 F70 18 | G01 X10.75 Y-47.44 19 | G02 X9.51 Y-48.69 I-1.25 J0 20 | G02 X8.25 Y-47.44 I0 J1.25 21 | G01 X8.25 Y-47.44 22 | X8.25 Y47.44 23 | G02 X9.60 Y48.74 I1.25 J0.05 24 | G00 Z5 25 | T0 M06 M02 26 | 27 | -------------------------------------------------------------------------------- /src/pygcode.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | README.rst 2 | setup.cfg 3 | setup.py 4 | scripts/pygcode-crop 5 | scripts/pygcode-norm 6 | src/pygcode/__init__.py 7 | src/pygcode/block.py 8 | src/pygcode/comment.py 9 | src/pygcode/exceptions.py 10 | src/pygcode/gcodes.py 11 | src/pygcode/line.py 12 | src/pygcode/machine.py 13 | src/pygcode/transform.py 14 | src/pygcode/utils.py 15 | src/pygcode/words.py 16 | src/pygcode.egg-info/PKG-INFO 17 | src/pygcode.egg-info/SOURCES.txt 18 | src/pygcode.egg-info/dependency_links.txt 19 | src/pygcode.egg-info/not-zip-safe 20 | src/pygcode.egg-info/requires.txt 21 | src/pygcode.egg-info/top_level.txt -------------------------------------------------------------------------------- /src/pygcode/exceptions.py: -------------------------------------------------------------------------------- 1 | 2 | # ===================== Parsing Exceptions ===================== 3 | class GCodeBlockFormatError(Exception): 4 | """Raised when errors encountered while parsing block text""" 5 | 6 | class GCodeParameterError(Exception): 7 | """Raised for conflicting / invalid / badly formed parameters""" 8 | 9 | class GCodeWordStrError(Exception): 10 | """Raised when issues found while parsing a word string""" 11 | 12 | # ===================== Machine Exceptions ===================== 13 | class MachineInvalidAxis(Exception): 14 | """Raised if an axis is invalid""" 15 | # For example: for axes X/Y/Z, set the value of "Q"; wtf? 16 | 17 | class MachineInvalidState(Exception): 18 | """Raised if a machine state is set incorrectly, or in conflict""" 19 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Tweakie's CNC Cube2.nc: -------------------------------------------------------------------------------- 1 | (Tweakie's CNC Cube 2) 2 | (Tool = 2mm dia.) 3 | N1 T1 M3 M6 G21 G91.1 F80 4 | N2 G0 Z2 5 | N3 G00 X45.932 Y27.932 6 | N4 G1 Z-2 7 | N5 G03 X45.932 Y27.932 I-18 J0 F400 8 | N6 G0 Z2 9 | N7 G0 X2.54 Y54.864 10 | N8 G1 Z-2 11 | N9 G1 X28.956 Y54.864 12 | N10 G1 X28.956 Y52.324 13 | N11 G1 X28.956 Y53.34 14 | N12 G1 X54.864 Y53.34 15 | N13 G1 X54.864 Y26.924 16 | N14 G1 X52.324 Y26.924 17 | N15 G1 X53.34 Y26.924 18 | N16 G1 X53.34 Y1.016 19 | N17 G1 X26.924 Y1.016 20 | N18 G1 X26.924 Y3.556 21 | N19 G1 X26.924 Y2.54 22 | N20 G1 X1.016 Y2.54 23 | N21 G1 X1.016 Y28.956 24 | N22 G1 X3.556 Y28.956 25 | N23 G1 X2.54 Y28.956 26 | N24 G1 X2.54 Y54.864 27 | N25 G0 Z2 28 | N26 G0 X0 Y0 29 | N27 M30 30 | N28 % 31 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Circular Pocket.tap: -------------------------------------------------------------------------------- 1 | (Code by Newfangled Wizard, 15/01/2017) 2 | (Version 2.86) 3 | (Program Posted for Aluminum ) 4 | G0 G49 G17 5 | G80 G90 G98 6 | G21 (mm) 7 | (***** Circular Pocket *****) 8 | (Xcen=0 Ycen=0 Dia=10) 9 | (CW-climb mill) 10 | M6 T7(TOOL DIA. 4) 11 | M03 S4000 12 | M7 (Mist On) 13 | G00 G43 H7 Z2 14 | X0 Y0 15 | G01 Z-1 F50 16 | G2 Y0 X1.60 R0.80 F100 17 | Y0 X-1.6 R1.6 18 | Y0 X3 R2.3 19 | Y0 X-3 R3 20 | X3 Y0 R3 21 | Y-1.5 X1.5 R1.5 22 | G00 Z2 23 | X0 Y0 24 | G01 Z-2 F50 25 | G2 Y0 X1.60 R0.80 F100 26 | Y0 X-1.6 R1.6 27 | Y0 X3 R2.3 28 | Y0 X-3 R3 29 | X3 Y0 R3 30 | Y-1.5 X1.5 R1.5 31 | G00 Z2 32 | X0 Y0 33 | G01 Z-3 F50 34 | G2 Y0 X1.60 R0.80 F100 35 | Y0 X-1.6 R1.6 36 | Y0 X3 R2.3 37 | Y0 X-3 R3 38 | X3 Y0 R3 39 | Y-1.5 X1.5 R1.5 40 | G00 Z2 41 | M5 M9 42 | M30 43 | % 44 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Circular Pocket.tap: -------------------------------------------------------------------------------- 1 | (Code by Newfangled Wizard, 15/01/2017) 2 | (Version 2.86) 3 | (Program Posted for Aluminum ) 4 | G0 G49 G40.1 G17 5 | G80 G50 G90 G98 6 | G21 (mm) 7 | (***** Circular Pocket *****) 8 | (Xcen=0 Ycen=0 Dia=10) 9 | (CW-climb mill) 10 | M6 T7(TOOL DIA. 4) 11 | M03 S4000 12 | M7 (Mist On) 13 | G00 G43 H7 Z2 14 | X0 Y0 15 | G01 Z-1 F50 16 | G2 Y0 X1.60 R0.80 F100 17 | Y0 X-1.6 R1.6 18 | Y0 X3 R2.3 19 | Y0 X-3 R3 20 | X3 Y0 R3 21 | Y-1.5 X1.5 R1.5 22 | G00 Z2 23 | X0 Y0 24 | G01 Z-2 F50 25 | G2 Y0 X1.60 R0.80 F100 26 | Y0 X-1.6 R1.6 27 | Y0 X3 R2.3 28 | Y0 X-3 R3 29 | X3 Y0 R3 30 | Y-1.5 X1.5 R1.5 31 | G00 Z2 32 | X0 Y0 33 | G01 Z-3 F50 34 | G2 Y0 X1.60 R0.80 F100 35 | Y0 X-1.6 R1.6 36 | Y0 X3 R2.3 37 | Y0 X-3 R3 38 | X3 Y0 R3 39 | Y-1.5 X1.5 R1.5 40 | G00 Z2 41 | M5 M9 42 | M30 43 | % -------------------------------------------------------------------------------- /tests/testutils.py: -------------------------------------------------------------------------------- 1 | # utilities for the testing suite (as opposed to the tests for utils.py) 2 | import sys 3 | import os 4 | import inspect 5 | 6 | import re 7 | 8 | 9 | # Units Under Test 10 | _pygcode_in_path = False 11 | def add_pygcode_to_path(): 12 | global _pygcode_in_path 13 | if not _pygcode_in_path: 14 | if os.environ.get('PYGCODE_TESTSCOPE', 'local') == 'installed': 15 | # Run tests on the `pip install` installed libray 16 | pass # nothing to be done 17 | else: 18 | # Run tests explicitly on files in ../src (ignore any installed libs) 19 | # Add pygcode (relative to this test-path) to the system path 20 | _this_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 21 | sys.path.insert(0, os.path.join(_this_path, '..', 'src')) 22 | 23 | _pygcode_in_path = True 24 | 25 | add_pygcode_to_path() 26 | 27 | 28 | # String Utilities 29 | def str_lines(text): 30 | """Split given string into lines (ignore blank lines, and automagically strip)""" 31 | for match in re.finditer(r'\s*(?P.*?)\s*\n', text): 32 | yield match.group('content') 33 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import re 3 | 4 | # Add relative pygcode to path 5 | from testutils import add_pygcode_to_path, str_lines 6 | add_pygcode_to_path() 7 | 8 | # Units under test 9 | from pygcode.utils import omit_redundant_modes 10 | from pygcode import text2gcodes, Line 11 | 12 | class UtilityTests(unittest.TestCase): 13 | def test_omit_redundant_modes(self): 14 | lines = [ 15 | Line(line_str) 16 | for line_str in re.split(r'\s*\n\s*', ''' 17 | g1 x0 y0 ; yes 18 | g1 x10 y-20 ; no 19 | g0 x-3 y2 ; yes 20 | g0 x0 y0 ; no 21 | g0 x1 y1 ; no 22 | g1 x20 y20 z5 ; yes 23 | ''') 24 | if line_str 25 | ] 26 | gcodes = [l.gcodes[0] for l in lines] 27 | comments = [l.comment for l in lines] 28 | for (i, g) in enumerate(omit_redundant_modes(gcodes)): 29 | comment = comments[i].text if comments[i] else None 30 | if comment == 'no': 31 | self.assertIsNotNone(re.search(r'^\s', str(g))) 32 | elif comment == 'yes': 33 | self.assertIsNone(re.search(r'^\s', str(g))) 34 | -------------------------------------------------------------------------------- /src/pygcode/dialects/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'DEFAULT', 3 | 4 | # registration decorators 5 | 'gcode_dialect', 6 | 'word_dialect', 7 | 8 | # dialects 9 | 'linuxcnc', 10 | 'reprap', 11 | ] 12 | 13 | 14 | # Registration decorators 15 | from .mapping import gcode_dialect 16 | from .mapping import word_dialect 17 | 18 | # Dialects 19 | from . import linuxcnc 20 | from . import reprap 21 | 22 | 23 | _DEFAULT = 'linuxcnc' 24 | 25 | 26 | def get_default(): 27 | """ 28 | Get the default gcode interpreter dialect. 29 | (see :meth:`set_default` for details) 30 | """ 31 | return _DEFAULT 32 | 33 | 34 | def set_default(name): 35 | """ 36 | Set the default gcode interpreter dialect. 37 | This dialect will be used if no other is specified for particular function 38 | calls. 39 | 40 | :param name: name of dialect 41 | :type name: :class:`str` 42 | 43 | .. doctest:: 44 | 45 | >>> from pygcode import dialect 46 | >>> dialect.get_default() 47 | 'linuxcnc' 48 | >>> dialect.set_default('reprap') 49 | >>> dialect.get_default() 50 | 'reprap' 51 | 52 | """ 53 | 54 | # TODO: verify valid name 55 | _DEFAULT = name 56 | -------------------------------------------------------------------------------- /src/pygcode/line.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from .comment import split_line 4 | from .block import Block 5 | 6 | class Line(object): 7 | 8 | line_regex = re.compile(r'^(?P.*?)?(?P%.*%?)?\s*$') 9 | 10 | def __init__(self, text=None): 11 | self._text = text 12 | 13 | # Initialize 14 | self.block = None 15 | self.comment = None 16 | self.macro = None 17 | 18 | # Split line into block text, and comments 19 | if text is not None: 20 | match = self.line_regex.search(text) 21 | 22 | block_and_comment = match.group('block_and_comment') 23 | self.macro = match.group('macro') 24 | 25 | (block_str, comment) = split_line(block_and_comment) 26 | self.block = Block(block_str) 27 | if comment: 28 | self.comment = comment 29 | 30 | @property 31 | def text(self): 32 | if self._text is None: 33 | return str(self) 34 | return self._text 35 | 36 | @property 37 | def gcodes(self): 38 | """self.block.gcodes passthrough""" 39 | return self.block.gcodes 40 | 41 | def __str__(self): 42 | return ' '.join([str(x) for x in [self.block, self.comment, self.macro] if x]) 43 | -------------------------------------------------------------------------------- /src/pygcode/dialect.py: -------------------------------------------------------------------------------- 1 | 2 | def gcode_dialect(*names): 3 | """ 4 | GCode class dialect registration decorator 5 | 6 | :param names: name of relevant dialects 7 | :type names: :class:`list` of :class:`str` instances 8 | 9 | For example:: 10 | 11 | from pygcode.dialect import gcode_dialect as dialect 12 | from pygcode.gcodes import GCode 13 | 14 | @dialect('linuxcnc') 15 | class GCodeRapidMove(GCode): 16 | word_key = Word('G', 0) 17 | 18 | def _process(self, machine): 19 | params = self.get_param_dict(letters=machine.axes) 20 | machine.move_to(rapid=True, **params) 21 | 22 | @dialect('reprap') 23 | clsas GCodeRapidMove2(GCode): # name changed because scope is the same 24 | word_key = Word('G', 0) 25 | 26 | def _process(self, machine): 27 | params = self.get_param_dict(letters=machine.axes) 28 | params = {k: -v for (k, v) in params.items()} # negate parameters 29 | machine.move_to(rapid=True, **params) 30 | 31 | When processing a ``linuxcnc`` dialect, the machine coordintes would be 32 | positive. Conversely the coordintes would be negated if processed in the 33 | ``reprap`` dialect. 34 | 35 | """ 36 | # TODO 37 | 38 | def word_dialect(*names): 39 | """ 40 | """ 41 | -------------------------------------------------------------------------------- /src/pygcode/dialects/mapping.py: -------------------------------------------------------------------------------- 1 | 2 | # ---------------- Registration Decorators ---------------- 3 | def gcode_dialect(*names): 4 | """ 5 | GCode class dialect registration decorator 6 | 7 | :param names: name of relevant dialects 8 | :type names: :class:`list` of :class:`str` instances 9 | 10 | For example:: 11 | 12 | from pygcode.dialect import gcode_dialect as dialect 13 | from pygcode.gcodes import GCode 14 | 15 | @dialect('linuxcnc') 16 | class GCodeRapidMove(GCode): 17 | word_key = Word('G', 0) 18 | 19 | def _process(self, machine): 20 | params = self.get_param_dict(letters=machine.axes) 21 | machine.move_to(rapid=True, **params) 22 | 23 | @dialect('reprap') 24 | clsas GCodeRapidMove2(GCode): # name changed because scope is the same 25 | word_key = Word('G', 0) 26 | 27 | def _process(self, machine): 28 | params = self.get_param_dict(letters=machine.axes) 29 | params = {k: -v for (k, v) in params.items()} # negate parameters 30 | machine.move_to(rapid=True, **params) 31 | 32 | When processing a ``linuxcnc`` dialect, the machine coordintes would be 33 | positive. Conversely the coordintes would be negated if processed in the 34 | ``reprap`` dialect. 35 | 36 | """ 37 | # TODO 38 | 39 | def word_dialect(*names): 40 | """ 41 | 42 | """ 43 | 44 | # TODO 45 | 46 | 47 | # ---------------- Dialect ---------------- 48 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Mach4 write.tap: -------------------------------------------------------------------------------- 1 | G49 G40 G17 G80 G90 G21 2 | M04 S12000 3 | G90 G90.1 4 | G00 Z6 5 | G00 X0 Y0 6 | F600 7 | G01 Z-0.4 8 | G01 X1.8945 Y8.9334 9 | G02 X3.2091 Y9.9985 I3.2091 J8.6546 10 | G01 X4.8089 Y9.9985 11 | G02 X5.5538 Y9.0494 I4.8089 J9.2316 12 | G00 Z2 13 | G00 X4.4691 Y4.6164 14 | G01 Z-0.4 15 | G01 X5.5572 Y9.04 16 | G02 X6.7833 Y9.9997 I6.7815 J8.7389 17 | G01 X8.3784 Y9.9974 18 | G02 X9.3057 Y8.8497 I8.377 J9.0479 19 | G01 X7.4175 Y-0.0001 20 | G00 Z2 21 | G00 X12.5737 Y0 22 | G01 Z-0.4 23 | G01 X14.1923 Y7.7724 24 | G02 X16.9591 Y10 I16.9317 J7.2019 25 | G01 X19.3014 Y9.9771 26 | G02 X20.9532 Y7.9109 I19.2847 J8.2704 27 | G01 X19.2484 Y0.0001 28 | G00 Z2 29 | G00 X13.3612 Y3.7815 30 | G01 Z-0.4 31 | G01 X20.0633 Y3.7815 32 | G00 Z2 33 | G00 X31.1752 Y1.6931 34 | G01 Z-0.4 35 | G02 X29.1577 Y-0.0232 I29.1718 J2.0042 36 | G01 X25.8234 Y0 37 | G02 X24.5528 Y1.5916 I25.8325 J1.3103 38 | G01 X26.0337 Y8.3291 39 | G02 X28.1285 Y10.0011 I28.1131 J7.872 40 | G01 X31.2929 Y9.9782 41 | G02 X32.5962 Y8.3801 I31.2832 J8.6398 42 | G00 Z2 43 | G00 X35.8642 Y0 44 | G01 Z-0.4 45 | G01 X37.9902 Y10 46 | G00 Z2 47 | G00 X36.9272 Y5 48 | G01 Z-0.4 49 | G01 X43.5272 Y5 50 | G00 Z2 51 | G00 X44.5902 Y10 52 | G01 Z-0.4 53 | G01 X42.4642 Y0 54 | G00 Z2 55 | G00 X59.3067 Y10 56 | G01 Z-0.4 57 | G01 X57.9242 Y4.4966 58 | G03 X59.286 Y2.7504 I59.286 J4.1545 59 | G01 X64.5422 Y2.7504 60 | G00 Z2 61 | G00 X64.2003 Y6.2539 62 | G01 Z-0.4 63 | G01 X62.6294 Y0.0001 64 | G00 Z6 65 | G00 X0 Y0 66 | M05 67 | M30 68 | % -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Mach4 write.tap: -------------------------------------------------------------------------------- 1 | G49 G40 G17 G80 G50 G90 G21 2 | M04 S12000 3 | G90 G90.1 4 | G00 Z6 5 | G00 X0 Y0 6 | F600 7 | G01 Z-0.4 8 | G01 X1.8945 Y8.9334 9 | G02 X3.2091 Y9.9985 I3.2091 J8.6546 10 | G01 X4.8089 Y9.9985 11 | G02 X5.5538 Y9.0494 I4.8089 J9.2316 12 | G00 Z2 13 | G00 X4.4691 Y4.6164 14 | G01 Z-0.4 15 | G01 X5.5572 Y9.04 16 | G02 X6.7833 Y9.9997 I6.7815 J8.7389 17 | G01 X8.3784 Y9.9974 18 | G02 X9.3057 Y8.8497 I8.377 J9.0479 19 | G01 X7.4175 Y-0.0001 20 | G00 Z2 21 | G00 X12.5737 Y0 22 | G01 Z-0.4 23 | G01 X14.1923 Y7.7724 24 | G02 X16.9591 Y10 I16.9317 J7.2019 25 | G01 X19.3014 Y9.9771 26 | G02 X20.9532 Y7.9109 I19.2847 J8.2704 27 | G01 X19.2484 Y0.0001 28 | G00 Z2 29 | G00 X13.3612 Y3.7815 30 | G01 Z-0.4 31 | G01 X20.0633 Y3.7815 32 | G00 Z2 33 | G00 X31.1752 Y1.6931 34 | G01 Z-0.4 35 | G02 X29.1577 Y-0.0232 I29.1718 J2.0042 36 | G01 X25.8234 Y0 37 | G02 X24.5528 Y1.5916 I25.8325 J1.3103 38 | G01 X26.0337 Y8.3291 39 | G02 X28.1285 Y10.0011 I28.1131 J7.872 40 | G01 X31.2929 Y9.9782 41 | G02 X32.5962 Y8.3801 I31.2832 J8.6398 42 | G00 Z2 43 | G00 X35.8642 Y0 44 | G01 Z-0.4 45 | G01 X37.9902 Y10 46 | G00 Z2 47 | G00 X36.9272 Y5 48 | G01 Z-0.4 49 | G01 X43.5272 Y5 50 | G00 Z2 51 | G00 X44.5902 Y10 52 | G01 Z-0.4 53 | G01 X42.4642 Y0 54 | G00 Z2 55 | G00 X59.3067 Y10 56 | G01 Z-0.4 57 | G01 X57.9242 Y4.4966 58 | G03 X59.286 Y2.7504 I59.286 J4.1545 59 | G01 X64.5422 Y2.7504 60 | G00 Z2 61 | G00 X64.2003 Y6.2539 62 | G01 Z-0.4 63 | G01 X62.6294 Y0.0001 64 | G00 Z6 65 | G00 X0 Y0 66 | M05 67 | M30 68 | % -------------------------------------------------------------------------------- /src/pygcode.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.1 2 | Name: pygcode 3 | Version: 0.2.1 4 | Summary: Basic g-code parser, interpreter, and encoder library. 5 | Home-page: https://github.com/fragmuffin/pygcode 6 | Author: Peter Boin 7 | Author-email: peter.boin@gmail.com 8 | License: GPLv3 9 | Description-Content-Type: UNKNOWN 10 | Description: ======= 11 | pygcode 12 | ======= 13 | 14 | GCODE Parser for Python 15 | 16 | Currently in development, ``pygcode`` is a low-level GCode interpreter 17 | for python. 18 | 19 | 20 | Installation 21 | ============ 22 | 23 | Install using ``pip`` 24 | 25 | ``pip install pygcode`` 26 | 27 | or `download directly from PyPi `__ 28 | 29 | 30 | Documentation 31 | ============= 32 | 33 | `Check out the wiki `__ for documentation. 34 | 35 | Keywords: gcode,cnc,parser,interpreter 36 | Platform: UNKNOWN 37 | Classifier: Development Status :: 3 - Alpha 38 | Classifier: Intended Audience :: Developers 39 | Classifier: Intended Audience :: Manufacturing 40 | Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) 41 | Classifier: Natural Language :: English 42 | Classifier: Operating System :: OS Independent 43 | Classifier: Programming Language :: Python 44 | Classifier: Programming Language :: Python :: 2 45 | Classifier: Programming Language :: Python :: 3 46 | Classifier: Topic :: Scientific/Engineering 47 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Mach3 write.tap: -------------------------------------------------------------------------------- 1 | G00 G49 G40 G17 G80 G90 G21 2 | M04 S12000 3 | G90 G90.1 4 | G00 Z6 5 | G00 X0 Y0 6 | F600 7 | G01 Z-0.4 8 | G01 X1.8945 Y8.9334 9 | G02 X3.2091 Y9.9985 I3.2091 J8.6546 10 | G01 X4.8089 Y9.9985 11 | G02 X5.5538 Y9.0494 I4.8089 J9.2316 12 | G00 Z2 13 | G00 X4.4691 Y4.6164 14 | G01 Z-0.4 15 | G01 X5.5572 Y9.04 16 | G02 X6.7833 Y9.9997 I6.7815 J8.7389 17 | G01 X8.3784 Y9.9974 18 | G02 X9.3057 Y8.8497 I8.377 J9.0479 19 | G01 X7.4175 Y-0.0001 20 | G00 Z2 21 | G00 X12.5737 Y0 22 | G01 Z-0.4 23 | G01 X14.1923 Y7.7724 24 | G02 X16.9591 Y10 I16.9317 J7.2019 25 | G01 X19.3014 Y9.9771 26 | G02 X20.9532 Y7.9109 I19.2847 J8.2704 27 | G01 X19.2484 Y0.0001 28 | G00 Z2 29 | G00 X13.3612 Y3.7815 30 | G01 Z-0.4 31 | G01 X20.0633 Y3.7815 32 | G00 Z2 33 | G00 X31.1752 Y1.6931 34 | G01 Z-0.4 35 | G02 X29.1577 Y-0.0232 I29.1718 J2.0042 36 | G01 X25.8234 Y0 37 | G02 X24.5528 Y1.5916 I25.8325 J1.3103 38 | G01 X26.0337 Y8.3291 39 | G02 X28.1285 Y10.0011 I28.1131 J7.872 40 | G01 X31.2929 Y9.9782 41 | G02 X32.5962 Y8.3801 I31.2832 J8.6398 42 | G00 Z2 43 | G00 X35.8642 Y0 44 | G01 Z-0.4 45 | G01 X37.9902 Y10 46 | G00 Z2 47 | G00 X36.9272 Y5 48 | G01 Z-0.4 49 | G01 X43.5272 Y5 50 | G00 Z2 51 | G00 X44.5902 Y10 52 | G01 Z-0.4 53 | G01 X42.4642 Y0 54 | G00 Z2 55 | G00 X59.2553 Y8.7284 56 | G01 Z-0.4 57 | G02 X60.5458 Y10 I60.5458 J8.7094 58 | G01 X64.4085 Y10 59 | G02 X65.6721 Y8.4332 I64.4085 J8.707 60 | G01 X65.3189 Y6.8035 61 | G02 X63.8438 Y5.6149 I63.8451 J7.1229 62 | G00 Z2 63 | G00 X61.3493 Y5.6171 64 | G01 Z-0.4 65 | G01 X63.8438 Y5.6149 66 | G02 X64.808 Y4.4157 I63.8429 J4.627 67 | G01 X64.17 Y1.502 68 | G02 X62.3035 Y0 I62.3035 J1.9107 69 | G01 X58.7792 Y0 70 | G02 X57.4544 Y1.3515 I58.7792 J1.3251 71 | G00 Z6 72 | G00 X0 Y0 73 | M05 74 | M30 75 | % 76 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Mach3 write.tap: -------------------------------------------------------------------------------- 1 | G00 G49 G40 G17 G80 G50 G90 G21 2 | M04 S12000 3 | G90 G90.1 4 | G00 Z6 5 | G00 X0 Y0 6 | F600 7 | G01 Z-0.4 8 | G01 X1.8945 Y8.9334 9 | G02 X3.2091 Y9.9985 I3.2091 J8.6546 10 | G01 X4.8089 Y9.9985 11 | G02 X5.5538 Y9.0494 I4.8089 J9.2316 12 | G00 Z2 13 | G00 X4.4691 Y4.6164 14 | G01 Z-0.4 15 | G01 X5.5572 Y9.04 16 | G02 X6.7833 Y9.9997 I6.7815 J8.7389 17 | G01 X8.3784 Y9.9974 18 | G02 X9.3057 Y8.8497 I8.377 J9.0479 19 | G01 X7.4175 Y-0.0001 20 | G00 Z2 21 | G00 X12.5737 Y0 22 | G01 Z-0.4 23 | G01 X14.1923 Y7.7724 24 | G02 X16.9591 Y10 I16.9317 J7.2019 25 | G01 X19.3014 Y9.9771 26 | G02 X20.9532 Y7.9109 I19.2847 J8.2704 27 | G01 X19.2484 Y0.0001 28 | G00 Z2 29 | G00 X13.3612 Y3.7815 30 | G01 Z-0.4 31 | G01 X20.0633 Y3.7815 32 | G00 Z2 33 | G00 X31.1752 Y1.6931 34 | G01 Z-0.4 35 | G02 X29.1577 Y-0.0232 I29.1718 J2.0042 36 | G01 X25.8234 Y0 37 | G02 X24.5528 Y1.5916 I25.8325 J1.3103 38 | G01 X26.0337 Y8.3291 39 | G02 X28.1285 Y10.0011 I28.1131 J7.872 40 | G01 X31.2929 Y9.9782 41 | G02 X32.5962 Y8.3801 I31.2832 J8.6398 42 | G00 Z2 43 | G00 X35.8642 Y0 44 | G01 Z-0.4 45 | G01 X37.9902 Y10 46 | G00 Z2 47 | G00 X36.9272 Y5 48 | G01 Z-0.4 49 | G01 X43.5272 Y5 50 | G00 Z2 51 | G00 X44.5902 Y10 52 | G01 Z-0.4 53 | G01 X42.4642 Y0 54 | G00 Z2 55 | G00 X59.2553 Y8.7284 56 | G01 Z-0.4 57 | G02 X60.5458 Y10 I60.5458 J8.7094 58 | G01 X64.4085 Y10 59 | G02 X65.6721 Y8.4332 I64.4085 J8.707 60 | G01 X65.3189 Y6.8035 61 | G02 X63.8438 Y5.6149 I63.8451 J7.1229 62 | G00 Z2 63 | G00 X61.3493 Y5.6171 64 | G01 Z-0.4 65 | G01 X63.8438 Y5.6149 66 | G02 X64.808 Y4.4157 I63.8429 J4.627 67 | G01 X64.17 Y1.502 68 | G02 X62.3035 Y0 I62.3035 J1.9107 69 | G01 X58.7792 Y0 70 | G02 X57.4544 Y1.3515 I58.7792 J1.3251 71 | G00 Z6 72 | G00 X0 Y0 73 | M05 74 | M30 75 | % -------------------------------------------------------------------------------- /tests/test_line.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | # Add relative pygcode to path 4 | from testutils import add_pygcode_to_path, str_lines 5 | add_pygcode_to_path() 6 | 7 | # Units under test 8 | from pygcode.line import Line 9 | 10 | 11 | class LineCommentTests(unittest.TestCase): 12 | def test_line_comment_semicolon(self): 13 | line = Line('G02 X10.75 Y47.44 I-0.11 J-1.26 F70 ; blah blah') 14 | self.assertEqual(line.comment.text, 'blah blah') 15 | self.assertEqual(len(line.block.words), 6) 16 | 17 | def test_line_comment_brackets(self): 18 | line = Line('G02 X10.75 Y47.44 I-0.11 J-1.26 F70 (blah blah)') 19 | self.assertEqual(line.comment.text, 'blah blah') 20 | self.assertEqual(len(line.block.words), 6) 21 | 22 | def test_line_comment_brackets_multi(self): 23 | line = Line('G02 X10.75 (x coord) Y47.44 (y coord) I-0.11 J-1.26 F70 (eol)') 24 | self.assertEqual(line.comment.text, 'x coord. y coord. eol') 25 | self.assertEqual(len(line.block.words), 6) 26 | 27 | def test_line_macros(self): 28 | # (blank) 29 | line = Line('') 30 | self.assertIsNone(line.macro) 31 | 32 | # (no macro) 33 | line = Line('G02 X10.75 Y47.44 I-0.11 J-1.26 F70 (blah blah)') 34 | self.assertIsNone(line.macro) 35 | 36 | # % 37 | line = Line('%') 38 | self.assertEqual(str(line.macro), '%') 39 | 40 | # %% 41 | line = Line('%%') 42 | self.assertEqual(str(line.macro), '%%') 43 | 44 | # % blah blah % 45 | line = Line('% blah blah %') 46 | self.assertEqual(str(line.macro), '% blah blah %') 47 | 48 | # Combined at end of line (not sure if this is legit) 49 | line = Line('G02 X10.75 Y2 ; abc %something%') 50 | self.assertEqual(line.comment.text.strip(), 'abc') 51 | self.assertEqual(line.macro, '%something%') 52 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/README.md: -------------------------------------------------------------------------------- 1 | # Unsupported Files 2 | 3 | Files containing unsupported gcodes 4 | 5 | We can however still deal with these, workarounds shown below 6 | 7 | ## Unsupported GCodes 8 | 9 | When attempting to process unsupported gcode(s) via a `Machine` the following error (or similar) will be raised 10 | 11 | MachineInvalidState: unsupported gcode(s): 'P1 M10' (machine mode: ) 12 | 13 | 14 | These codes are not currently supported by this library, but you may introduce 15 | them for your project. with the following workaround 16 | 17 | **Workaround** 18 | 19 | Any class inheriting `GCode` is used to parse each gcode string. 20 | 21 | Look to the root `GCode` class definition in `gcodes.py` for more details. 22 | 23 | So, create the following class(es) (anywhere in your codebase, as long as it's 24 | imported) 25 | 26 | 27 | ### `M10` / `M11` : Pallet Clamp 28 | 29 | import pygcode 30 | 31 | class GCodePalletClampOn(pygcode.GCode): 32 | """M10: Pallet clamp on""" 33 | word_key = pygcode.Word('M', 10) 34 | word_letter = 'M' 35 | param_letters = set('P') 36 | 37 | class GCodePalletClampOff(pygcode.GCode): 38 | """M10: Pallet clamp off""" 39 | word_key = pygcode.Word('M', 11) 40 | word_letter = 'M' 41 | param_letters = set('P') 42 | 43 | 44 | ### `G70` / `G71` : Fixed cycle, multiple repetitive cycle 45 | 46 | import pygcode 47 | 48 | class GCodeFixedCycleMultiRepCycleRough(pygcode.GCode): 49 | """G70: Fixed cycle, multiple repetitive cycle, for finishing (including contours)""" 50 | word_key = pygcode.Word('G', 70) 51 | word_letter = 'G' 52 | 53 | class GCodeFixedCycleMultiRepCycleRough(pygcode.GCode): 54 | """G71: Fixed cycle, multiple repetitive cycle, for roughing (Z-axis emphasis)""" 55 | word_key = pygcode.Word('G', 71) 56 | word_letter = 'G' 57 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Trispokedovetiles(laser).tap: -------------------------------------------------------------------------------- 1 | (Laser cut, interlocking, Trispokedovetiles by Peter Dow) 2 | 3 | G90 (set absolute distance mode) 4 | G90.1 (set absolute distance mode for arc centers) 5 | G17 (set active plane to XY) 6 | G21 (set units to mm) 7 | F600 (set feed-rate) 8 | (turns laser OFF) 9 | G0 X 65.8393 Y 49.0906 10 | (turns laser ON) 11 | G3 Y 61.7398 I 65.8393 J 55.4203 12 | G3 Y 49.0906 I 65.8393 J 55.4101 13 | 14 | G0 X 93.3399 Y 36.7792 15 | 16 | G3 Y 42.2783 I 93.3399 J 39.5300 17 | G3 Y 36.7792 I 93.3399 J 39.5300 18 | 19 | G0 X 38.3388 20 | 21 | G3 Y 42.2783 I 38.3388 J 39.5300 22 | G3 Y 36.7792 I 38.3388 J 39.5300 23 | 24 | G0 X 65.8393 Y 84.4093 25 | 26 | G3 Y 89.9084 I 65.8393 J 87.1601 27 | G3 Y 84.4093 I 65.8393 J 87.1601 28 | 29 | G0 X 93.3399 Y 103.0402 30 | 31 | G1 X 86.9010 32 | G3 X 82.5094 Y 98.3285 I 86.9010 J 98.6384 33 | G3 X 111.3409 Y 48.3895 I 148.3411 J 103.0402 34 | G3 X 117.6198 Y 49.8297 I 113.8098 J 52.0294 35 | G1 X 120.8405 Y 55.4101 36 | G1 X 127.2794 37 | G2 X 131.6711 Y 50.6984 I 127.2794 J 51.0108 38 | G2 X 102.8395 Y 0.7595 I 65.8393 J 55.4101 39 | G2 X 96.5606 Y 2.1996 I 100.3706 J 4.3993 40 | G1 X 93.3399 Y 7.7800 41 | G1 X 96.5606 Y 13.3604 42 | G3 X 94.6709 Y 19.5199 I 92.7506 J 15.5600 43 | G3 X 37.0103 I 65.8393 J-39.8501 44 | G3 X 35.1206 Y 13.3604 I 38.9306 J 15.5600 45 | G1 X 38.3388 Y 7.7800 46 | G1 X 35.1206 Y 2.1996 47 | G2 X 28.8392 Y 0.7595 I 31.3106 J 4.3993 48 | G2 X 0.0102 Y 50.6984 I 65.8393 J 55.4101 49 | G2 X 4.3993 Y 55.4101 I 4.3993 J 51.0108 50 | G1 X 10.8407 51 | G1 X 14.0589 Y 49.8297 52 | G3 X 20.3403 Y 48.3895 I 17.8689 J 52.0294 53 | G3 X 49.1693 Y 98.3285 I-16.6599 J 103.0402 54 | G3 X 44.7802 Y 103.0402 I 44.7802 J 98.6384 55 | G1 X 38.3388 56 | G1 X 35.1206 Y 108.6206 57 | G2 X 37.0103 Y 114.7801 I 38.9306 J 110.8202 58 | G2 X 94.6709 I 65.8393 J 55.4101 59 | G2 X 96.5606 Y 108.6206 I 92.7506 J 110.8202 60 | G1 X 93.3399 Y 103.0402 61 | 62 | G0 X 0.0000 Y 0.0000 63 | M30 64 | % 65 | -------------------------------------------------------------------------------- /tests/test_parsing.py: -------------------------------------------------------------------------------- 1 | import os 2 | import inspect 3 | import re 4 | import glob 5 | import unittest 6 | 7 | # Add relative pygcode to path 8 | from testutils import add_pygcode_to_path, str_lines 9 | add_pygcode_to_path() 10 | 11 | # Units under test 12 | from pygcode.machine import Position, Machine 13 | from pygcode.line import Line 14 | from pygcode.exceptions import MachineInvalidAxis 15 | from pygcode.gcodes import ( 16 | GCodeAbsoluteDistanceMode, GCodeIncrementalDistanceMode, 17 | GCodeAbsoluteArcDistanceMode, GCodeIncrementalArcDistanceMode, 18 | GCodeCannedCycleReturnPrevLevel, GCodeCannedCycleReturnToR, 19 | ) 20 | 21 | # Local paths 22 | _this_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 23 | _test_files_dir = os.path.join(_this_path, 'test-files') 24 | 25 | class FileParsingTest(unittest.TestCase): 26 | filename = os.path.join(_test_files_dir, 'linuxcnc', 'random-sample-1.gcode') 27 | 28 | def test_file(self): 29 | m = Machine() 30 | with open(self.filename, 'r') as fh: 31 | for line_str in fh.readlines(): 32 | line = Line(line_str) 33 | m.process_block(line.block) 34 | 35 | 36 | # Get list of test files 37 | _filetype_regex = re.compile(r'\.(tap|nc|ngc|gcode)$', re.IGNORECASE) 38 | _test_files = set() 39 | for dialect in ['linuxcnc']: # FIXME: get list of all dialects 40 | for filename in glob.glob(os.path.join(_test_files_dir, dialect, '*')): 41 | if _filetype_regex.search(filename): 42 | _test_files.add(filename) 43 | 44 | # remove default test file: 45 | _test_files.discard(FileParsingTest.filename) 46 | 47 | # Create inheriting class for each gcode file in the _test_files_dir directory 48 | for (i, filename) in enumerate(sorted(_test_files)): 49 | basename = os.path.basename(filename) 50 | class_name = "FileParsingTest_" + re.sub(r"""[^a-zA-Z0-9]""", '_', basename) 51 | globals()[class_name] = type(class_name, (FileParsingTest,), { 52 | 'filename': filename, 53 | }) 54 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Trispokedovetiles(laser).tap: -------------------------------------------------------------------------------- 1 | (Laser cut, interlocking, Trispokedovetiles by Peter Dow) 2 | 3 | G90 (set absolute distance mode) 4 | G90.1 (set absolute distance mode for arc centers) 5 | G17 (set active plane to XY) 6 | G21 (set units to mm) 7 | F600 (set feed-rate) 8 | M10 P1 (turns laser OFF) 9 | G0 X 65.8393 Y 49.0906 10 | M11 P1 (turns laser ON) 11 | G3 Y 61.7398 I 65.8393 J 55.4203 12 | G3 Y 49.0906 I 65.8393 J 55.4101 13 | M10 P1 14 | G0 X 93.3399 Y 36.7792 15 | M11 P1 16 | G3 Y 42.2783 I 93.3399 J 39.5300 17 | G3 Y 36.7792 I 93.3399 J 39.5300 18 | M10 P1 19 | G0 X 38.3388 20 | M11 P1 21 | G3 Y 42.2783 I 38.3388 J 39.5300 22 | G3 Y 36.7792 I 38.3388 J 39.5300 23 | M10 P1 24 | G0 X 65.8393 Y 84.4093 25 | M11 P1 26 | G3 Y 89.9084 I 65.8393 J 87.1601 27 | G3 Y 84.4093 I 65.8393 J 87.1601 28 | M10 P1 29 | G0 X 93.3399 Y 103.0402 30 | M11 P1 31 | G1 X 86.9010 32 | G3 X 82.5094 Y 98.3285 I 86.9010 J 98.6384 33 | G3 X 111.3409 Y 48.3895 I 148.3411 J 103.0402 34 | G3 X 117.6198 Y 49.8297 I 113.8098 J 52.0294 35 | G1 X 120.8405 Y 55.4101 36 | G1 X 127.2794 37 | G2 X 131.6711 Y 50.6984 I 127.2794 J 51.0108 38 | G2 X 102.8395 Y 0.7595 I 65.8393 J 55.4101 39 | G2 X 96.5606 Y 2.1996 I 100.3706 J 4.3993 40 | G1 X 93.3399 Y 7.7800 41 | G1 X 96.5606 Y 13.3604 42 | G3 X 94.6709 Y 19.5199 I 92.7506 J 15.5600 43 | G3 X 37.0103 I 65.8393 J-39.8501 44 | G3 X 35.1206 Y 13.3604 I 38.9306 J 15.5600 45 | G1 X 38.3388 Y 7.7800 46 | G1 X 35.1206 Y 2.1996 47 | G2 X 28.8392 Y 0.7595 I 31.3106 J 4.3993 48 | G2 X 0.0102 Y 50.6984 I 65.8393 J 55.4101 49 | G2 X 4.3993 Y 55.4101 I 4.3993 J 51.0108 50 | G1 X 10.8407 51 | G1 X 14.0589 Y 49.8297 52 | G3 X 20.3403 Y 48.3895 I 17.8689 J 52.0294 53 | G3 X 49.1693 Y 98.3285 I-16.6599 J 103.0402 54 | G3 X 44.7802 Y 103.0402 I 44.7802 J 98.6384 55 | G1 X 38.3388 56 | G1 X 35.1206 Y 108.6206 57 | G2 X 37.0103 Y 114.7801 I 38.9306 J 110.8202 58 | G2 X 94.6709 I 65.8393 J 55.4101 59 | G2 X 96.5606 Y 108.6206 I 92.7506 J 110.8202 60 | G1 X 93.3399 Y 103.0402 61 | M10 P1 62 | G0 X 0.0000 Y 0.0000 63 | M30 64 | % 65 | -------------------------------------------------------------------------------- /src/pygcode/comment.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class CommentBase(object): 5 | ORDER = 0 6 | MULTICOMMENT_JOINER = ". " # joiner if multiple comments are found on the same line 7 | def __init__(self, text): 8 | self.text = text 9 | 10 | def __repr__(self): 11 | return "<{class_name}: '{comment}'>".format( 12 | class_name=self.__class__.__name__, 13 | comment=str(self), 14 | ) 15 | 16 | 17 | class CommentSemicolon(CommentBase): 18 | "Comments of the format: 'G00 X1 Y2 ; something profound'" 19 | ORDER = 1 20 | AUTO_REGEX = re.compile(r'\s*;\s*(?P.*)$') 21 | 22 | def __str__(self): 23 | return "; {text}".format(text=self.text) 24 | 25 | 26 | class CommentBrackets(CommentBase): 27 | "Comments of the format: 'G00 X1 Y2 (something profound)" 28 | ORDER = 2 29 | AUTO_REGEX = re.compile(r'\((?P[^\)]*)\)') 30 | 31 | def __str__(self): 32 | return "({text})".format(text=self.text) 33 | 34 | 35 | Comment = CommentBrackets # default comment type 36 | 37 | 38 | def split_line(line_text): 39 | """ 40 | Split functional block content from comments 41 | :param line_text: line from gcode file 42 | :return: tuple of (str(), CommentBase()) 43 | """ 44 | comments_class = None 45 | 46 | # Auto-detect comment type if I can 47 | comments = [] 48 | block_str = line_text.rstrip("\n") # to remove potential return carriage from comment body 49 | 50 | for cls in sorted(CommentBase.__subclasses__(), key=lambda c: c.ORDER): 51 | matches = list(cls.AUTO_REGEX.finditer(block_str)) 52 | if matches: 53 | for match in reversed(matches): 54 | # Build list of comment text 55 | comments.insert(0, match.group('text')) # prepend 56 | # Remove comments from given block_str 57 | block_str = block_str[:match.start()] + block_str[match.end():] 58 | comments_class = cls 59 | break 60 | 61 | # Create comment instance if content was found 62 | comment_obj = None 63 | if comments_class: 64 | comment_text = comments_class.MULTICOMMENT_JOINER.join(comments) 65 | comment_obj = comments_class(comment_text) 66 | 67 | return (block_str, comment_obj) 68 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Smiley001.nc: -------------------------------------------------------------------------------- 1 | G20 G40 G90 2 | G91.1 3 | G0 Z0.250 4 | G0 X-0.305 Y-0.127 Z0.250 5 | G1 X-0.305 Y-0.127 Z-0.010 F50 6 | G3 X0.304 Y-0.127 Z-0.010 I0.305 J0.115 F50 7 | G2 X-0.305 Y-0.127 Z-0.010 I-0.305 J0.172 8 | G0 X-0.305 Y-0.127 Z0.250 9 | G0 X-0.372 Y-0.145 Z0.250 10 | G1 X-0.372 Y-0.145 Z-0.010 F50 11 | G3 X-0.267 Y-0.070 Z-0.010 I0.010 J0.097 F50 12 | G0 X-0.267 Y-0.070 Z0.250 13 | G0 X0.266 Y-0.070 Z0.250 14 | G1 X0.266 Y-0.070 Z-0.010 F50 15 | G3 X0.371 Y-0.145 Z-0.010 I0.095 J0.021 F50 16 | G0 X0.371 Y-0.145 Z0.250 17 | G0 X0.174 Y0.083 Z0.250 18 | G1 X0.174 Y0.083 Z-0.010 F50 19 | G2 X0.151 Y0.077 Z-0.010 I-0.023 J0.037 F50 20 | G2 X0.128 Y0.083 Z-0.010 I0.000 J0.044 21 | G2 X0.105 Y0.105 Z-0.010 I0.039 J0.063 22 | G2 X0.092 Y0.135 Z-0.010 I0.087 J0.058 23 | G2 X0.086 Y0.174 Z-0.010 I0.140 J0.039 24 | G2 X0.092 Y0.213 Z-0.010 I0.145 J0.000 25 | G2 X0.105 Y0.243 Z-0.010 I0.100 J-0.028 26 | G2 X0.128 Y0.264 Z-0.010 I0.062 J-0.041 27 | G2 X0.151 Y0.271 Z-0.010 I0.023 J-0.037 28 | G2 X0.174 Y0.264 Z-0.010 I0.000 J-0.044 29 | G2 X0.197 Y0.243 Z-0.010 I-0.039 J-0.063 30 | G2 X0.211 Y0.213 Z-0.010 I-0.087 J-0.058 31 | G2 X0.216 Y0.174 Z-0.010 I-0.140 J-0.039 32 | G2 X0.211 Y0.135 Z-0.010 I-0.145 J0.000 33 | G2 X0.197 Y0.105 Z-0.010 I-0.100 J0.028 34 | G2 X0.174 Y0.083 Z-0.010 I-0.062 J0.041 35 | G0 X0.174 Y0.083 Z0.250 36 | G0 X-0.174 Y0.083 Z0.250 37 | G1 X-0.174 Y0.083 Z-0.010 F50 38 | G3 X-0.151 Y0.077 Z-0.010 I0.023 J0.037 F50 39 | G3 X-0.128 Y0.083 Z-0.010 I0.000 J0.044 40 | G3 X-0.105 Y0.105 Z-0.010 I-0.039 J0.063 41 | G3 X-0.092 Y0.135 Z-0.010 I-0.087 J0.058 42 | G3 X-0.086 Y0.174 Z-0.010 I-0.140 J0.039 43 | G3 X-0.092 Y0.213 Z-0.010 I-0.145 J0.000 44 | G3 X-0.105 Y0.243 Z-0.010 I-0.100 J-0.028 45 | G3 X-0.128 Y0.264 Z-0.010 I-0.062 J-0.041 46 | G3 X-0.151 Y0.271 Z-0.010 I-0.023 J-0.037 47 | G3 X-0.174 Y0.264 Z-0.010 I0.000 J-0.044 48 | G3 X-0.197 Y0.243 Z-0.010 I0.039 J-0.063 49 | G3 X-0.211 Y0.213 Z-0.010 I0.087 J-0.058 50 | G3 X-0.216 Y0.174 Z-0.010 I0.140 J-0.039 51 | G3 X-0.211 Y0.135 Z-0.010 I0.145 J0.000 52 | G3 X-0.197 Y0.105 Z-0.010 I0.100 J0.028 53 | G3 X-0.174 Y0.083 Z-0.010 I0.062 J0.041 54 | G0 X-0.174 Y0.083 Z0.250 55 | G0 X0.000 Y0.500 Z0.250 56 | G1 X0.000 Y0.500 Z-0.010 F50 57 | G2 X0.000 Y0.500 Z-0.010 I0.000 J-0.500 F50 58 | G0 X0.000 Y0.500 Z0.250 59 | M30 60 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/DB25.tap: -------------------------------------------------------------------------------- 1 | (Code by Newfangled Wizard, 15/01/2017) 2 | (Version 2.86) 3 | (Program Posted for Aluminum ) 4 | G0 G49 G17 5 | G80 G90 G98 6 | G21 (mm) 7 | (****DB 25 Pin****) 8 | M6 T7(TOOL DIA. 3) 9 | G43 H7 10 | M03 S4000 11 | M7 (Mist On) 12 | G0 X18.6665 Y-3.1674 13 | X18.6665 Y-3.1674 Z2 14 | G1 X18.6665 Y-3.1674 Z-1 F39.6 15 | G41 D1.5 X20.4013 Y-3.9116 F79.2 16 | G3 X21.0007 Y-2.3038 R2.46126 17 | G1 X21.0007 Y-1.6002 18 | X23.3959 Y-1.6002 19 | G3 X24.9961 Y0 R1.6002 20 | X23.3959 Y1.6002 R1.6002 21 | G1 X21.0007 Y1.6002 22 | X21.0007 Y2.3241 23 | G3 X17.8257 Y5.4991 R3.175 24 | G1 X-17.8257 Y5.4991 25 | G3 X-21.0007 Y2.3241 R3.175 26 | G1 X-21.0007 Y1.6002 27 | X-23.3959 Y1.6002 28 | G3 X-24.9961 Y0 R1.6002 29 | X-23.3959 Y-1.6002 R1.6002 30 | G1 X-21.0007 Y-1.6002 31 | X-21.0007 Y-2.3241 32 | G3 X-17.8257 Y-5.4991 R3.175 33 | G1 X17.8257 Y-5.4991 34 | G3 X21.0007 Y-2.3241 R3.175 35 | G1 X21.0007 Y-1.6002 36 | G3 X21.8491 Y-0.7772 R1.69672 37 | G1 G40 X20.4241 Y-0.0178 38 | G0 X20.4241 Y-0.0178 Z2 39 | G0 X18.6665 Y-3.1674 40 | X18.6665 Y-3.1674 Z2 41 | G1 X18.6665 Y-3.1674 Z-2 F39.6 42 | G41 D1.5 X20.4013 Y-3.9116 F79.2 43 | G3 X21.0007 Y-2.3038 R2.46126 44 | G1 X21.0007 Y-1.6002 45 | X23.3959 Y-1.6002 46 | G3 X24.9961 Y0 R1.6002 47 | X23.3959 Y1.6002 R1.6002 48 | G1 X21.0007 Y1.6002 49 | X21.0007 Y2.3241 50 | G3 X17.8257 Y5.4991 R3.175 51 | G1 X-17.8257 Y5.4991 52 | G3 X-21.0007 Y2.3241 R3.175 53 | G1 X-21.0007 Y1.6002 54 | X-23.3959 Y1.6002 55 | G3 X-24.9961 Y0 R1.6002 56 | X-23.3959 Y-1.6002 R1.6002 57 | G1 X-21.0007 Y-1.6002 58 | X-21.0007 Y-2.3241 59 | G3 X-17.8257 Y-5.4991 R3.175 60 | G1 X17.8257 Y-5.4991 61 | G3 X21.0007 Y-2.3241 R3.175 62 | G1 X21.0007 Y-1.6002 63 | G3 X21.8491 Y-0.7772 R1.69672 64 | G1 G40 X20.4241 Y-0.0178 65 | G0 X20.4241 Y-0.0178 Z2 66 | G0 X18.6665 Y-3.1674 67 | X18.6665 Y-3.1674 Z2 68 | G1 X18.6665 Y-3.1674 Z-3 F39.6 69 | G41 D1.5 X20.4013 Y-3.9116 F79.2 70 | G3 X21.0007 Y-2.3038 R2.46126 71 | G1 X21.0007 Y-1.6002 72 | X23.3959 Y-1.6002 73 | G3 X24.9961 Y0 R1.6002 74 | X23.3959 Y1.6002 R1.6002 75 | G1 X21.0007 Y1.6002 76 | X21.0007 Y2.3241 77 | G3 X17.8257 Y5.4991 R3.175 78 | G1 X-17.8257 Y5.4991 79 | G3 X-21.0007 Y2.3241 R3.175 80 | G1 X-21.0007 Y1.6002 81 | X-23.3959 Y1.6002 82 | G3 X-24.9961 Y0 R1.6002 83 | X-23.3959 Y-1.6002 R1.6002 84 | G1 X-21.0007 Y-1.6002 85 | X-21.0007 Y-2.3241 86 | G3 X-17.8257 Y-5.4991 R3.175 87 | G1 X17.8257 Y-5.4991 88 | G3 X21.0007 Y-2.3241 R3.175 89 | G1 X21.0007 Y-1.6002 90 | G3 X21.8491 Y-0.7772 R1.69672 91 | G1 G40 X20.4241 Y-0.0178 92 | G0 X20.4241 Y-0.0178 Z2 93 | M5 M9 94 | M30 95 | % 96 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/DB25.tap: -------------------------------------------------------------------------------- 1 | (Code by Newfangled Wizard, 15/01/2017) 2 | (Version 2.86) 3 | (Program Posted for Aluminum ) 4 | G0 G49 G40.1 G17 5 | G80 G50 G90 G98 6 | G21 (mm) 7 | (****DB 25 Pin****) 8 | M6 T7(TOOL DIA. 3) 9 | G43 H7 10 | M03 S4000 11 | M7 (Mist On) 12 | G0 X18.6665 Y-3.1674 13 | X18.6665 Y-3.1674 Z2 14 | G1 X18.6665 Y-3.1674 Z-1 F39.6 15 | G41 P1.5 X20.4013 Y-3.9116 F79.2 16 | G3 X21.0007 Y-2.3038 R2.46126 17 | G1 X21.0007 Y-1.6002 18 | X23.3959 Y-1.6002 19 | G3 X24.9961 Y0 R1.6002 20 | X23.3959 Y1.6002 R1.6002 21 | G1 X21.0007 Y1.6002 22 | X21.0007 Y2.3241 23 | G3 X17.8257 Y5.4991 R3.175 24 | G1 X-17.8257 Y5.4991 25 | G3 X-21.0007 Y2.3241 R3.175 26 | G1 X-21.0007 Y1.6002 27 | X-23.3959 Y1.6002 28 | G3 X-24.9961 Y0 R1.6002 29 | X-23.3959 Y-1.6002 R1.6002 30 | G1 X-21.0007 Y-1.6002 31 | X-21.0007 Y-2.3241 32 | G3 X-17.8257 Y-5.4991 R3.175 33 | G1 X17.8257 Y-5.4991 34 | G3 X21.0007 Y-2.3241 R3.175 35 | G1 X21.0007 Y-1.6002 36 | G3 X21.8491 Y-0.7772 R1.69672 37 | G1 G40 X20.4241 Y-0.0178 38 | G0 X20.4241 Y-0.0178 Z2 39 | G0 X18.6665 Y-3.1674 40 | X18.6665 Y-3.1674 Z2 41 | G1 X18.6665 Y-3.1674 Z-2 F39.6 42 | G41 P1.5 X20.4013 Y-3.9116 F79.2 43 | G3 X21.0007 Y-2.3038 R2.46126 44 | G1 X21.0007 Y-1.6002 45 | X23.3959 Y-1.6002 46 | G3 X24.9961 Y0 R1.6002 47 | X23.3959 Y1.6002 R1.6002 48 | G1 X21.0007 Y1.6002 49 | X21.0007 Y2.3241 50 | G3 X17.8257 Y5.4991 R3.175 51 | G1 X-17.8257 Y5.4991 52 | G3 X-21.0007 Y2.3241 R3.175 53 | G1 X-21.0007 Y1.6002 54 | X-23.3959 Y1.6002 55 | G3 X-24.9961 Y0 R1.6002 56 | X-23.3959 Y-1.6002 R1.6002 57 | G1 X-21.0007 Y-1.6002 58 | X-21.0007 Y-2.3241 59 | G3 X-17.8257 Y-5.4991 R3.175 60 | G1 X17.8257 Y-5.4991 61 | G3 X21.0007 Y-2.3241 R3.175 62 | G1 X21.0007 Y-1.6002 63 | G3 X21.8491 Y-0.7772 R1.69672 64 | G1 G40 X20.4241 Y-0.0178 65 | G0 X20.4241 Y-0.0178 Z2 66 | G0 X18.6665 Y-3.1674 67 | X18.6665 Y-3.1674 Z2 68 | G1 X18.6665 Y-3.1674 Z-3 F39.6 69 | G41 P1.5 X20.4013 Y-3.9116 F79.2 70 | G3 X21.0007 Y-2.3038 R2.46126 71 | G1 X21.0007 Y-1.6002 72 | X23.3959 Y-1.6002 73 | G3 X24.9961 Y0 R1.6002 74 | X23.3959 Y1.6002 R1.6002 75 | G1 X21.0007 Y1.6002 76 | X21.0007 Y2.3241 77 | G3 X17.8257 Y5.4991 R3.175 78 | G1 X-17.8257 Y5.4991 79 | G3 X-21.0007 Y2.3241 R3.175 80 | G1 X-21.0007 Y1.6002 81 | X-23.3959 Y1.6002 82 | G3 X-24.9961 Y0 R1.6002 83 | X-23.3959 Y-1.6002 R1.6002 84 | G1 X-21.0007 Y-1.6002 85 | X-21.0007 Y-2.3241 86 | G3 X-17.8257 Y-5.4991 R3.175 87 | G1 X17.8257 Y-5.4991 88 | G3 X21.0007 Y-2.3241 R3.175 89 | G1 X21.0007 Y-1.6002 90 | G3 X21.8491 Y-0.7772 R1.69672 91 | G1 G40 X20.4241 Y-0.0178 92 | G0 X20.4241 Y-0.0178 Z2 93 | M5 M9 94 | M30 95 | % 96 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Smiley face.tap: -------------------------------------------------------------------------------- 1 | (Created 7:48:52 AM 5/1/2011 from Smiley face.dxf) 2 | (Post = Mikes Mach3 Router) 3 | (Tool 8 = .250 Vee bit) 4 | N0001 G90 5 | N0002 G20 G91.1 M3 6 | N0003 G00 X0.1250 Y0.0000 Z0.5000 7 | N0004 G00 X0.1250 Y0.0000 Z0.5000 8 | N0005 G01 X0.1250 Y0.0000 Z-0.0500 F10.00 9 | N0006 G03 X0.1250 Y0.0000 I-0.1250 J0.0000 F40.00 10 | N0007 G00 X0.1250 Y0.0000 Z0.5000 11 | N0008 G00 X1.8215 Y4.2445 Z0.5000 12 | N0009 G01 X1.8215 Y4.2445 Z-0.0500 F10.00 13 | N0010 G03 X1.8215 Y4.2445 I-0.1250 J0.0000 F40.00 14 | N0011 G00 X1.8215 Y4.2445 Z0.5000 15 | N0012 G00 X2.2583 Y5.1186 Z0.5000 16 | N0013 G01 X2.2583 Y5.1186 Z-0.0500 F10.00 17 | N0014 G01 X2.2583 Y5.9313 Z-0.0500 F40.00 18 | N0015 G00 X2.2583 Y5.9313 Z0.5000 19 | N0016 G00 X3.0000 Y6.0244 Z0.5000 20 | N0017 G01 X3.0000 Y6.0244 Z-0.0500 F10.00 21 | N0018 G01 X3.0000 Y5.1186 Z-0.0500 F40.00 22 | N0019 G00 X3.0000 Y5.1186 Z0.5000 23 | N0020 G00 X3.7417 Y5.1186 Z0.5000 24 | N0021 G01 X3.7417 Y5.1186 Z-0.0500 F10.00 25 | N0022 G01 X3.7417 Y5.9313 Z-0.0500 F40.00 26 | N0023 G00 X3.7417 Y5.9313 Z0.5000 27 | N0024 G00 X4.4835 Y5.1186 Z0.5000 28 | N0025 G01 X4.4835 Y5.1186 Z-0.0500 F10.00 29 | N0026 G01 X4.4835 Y5.6320 Z-0.0500 F40.00 30 | N0027 G00 X4.4835 Y5.6320 Z0.5000 31 | N0028 G00 X4.7885 Y4.2445 Z0.5000 32 | N0029 G01 X4.7885 Y4.2445 Z-0.0500 F10.00 33 | N0030 G03 X4.7885 Y4.2445 I-0.1250 J0.0000 F40.00 34 | N0031 G00 X4.7885 Y4.2445 Z0.5000 35 | N0032 G00 X1.5165 Y1.8003 Z0.5000 36 | N0033 G01 X1.5165 Y1.8003 Z-0.0500 F10.00 37 | N0034 G03 X4.4835 Y1.8003 I1.4835 J0.8852 F40.00 38 | N0035 G02 X1.5165 Y1.8003 I-1.4835 J1.2241 39 | N0036 G00 X1.5165 Y1.8003 Z0.5000 40 | N0037 G00 X2.4130 Y2.6855 Z0.5000 41 | N0038 G01 X2.4130 Y2.6855 Z-0.0500 F10.00 42 | N0039 G01 X3.5870 Y2.6855 Z-0.0500 F40.00 43 | N0040 G01 X3.0000 Y3.7022 Z-0.0500 44 | N0041 G01 X2.4130 Y2.6855 Z-0.0500 45 | N0042 G00 X2.4130 Y2.6855 Z0.5000 46 | N0043 G00 X2.0165 Y4.3945 Z0.5000 47 | N0044 G01 X2.0165 Y4.3945 Z-0.0500 F10.00 48 | N0045 G03 X2.0165 Y4.3945 I-0.5000 J0.0000 F40.00 49 | N0046 G00 X2.0165 Y4.3945 Z0.5000 50 | N0047 G00 X4.9835 Y4.3945 Z0.5000 51 | N0048 G01 X4.9835 Y4.3945 Z-0.0500 F10.00 52 | N0049 G03 X4.9835 Y4.3945 I-0.5000 J0.0000 F40.00 53 | N0050 G00 X4.9835 Y4.3945 Z0.5000 54 | N0051 G00 X6.0000 Y3.0244 Z0.5000 55 | N0052 G01 X6.0000 Y3.0244 Z-0.0500 F10.00 56 | N0053 G03 X6.0000 Y3.0244 I-3.0000 J0.0000 F40.00 57 | N0054 G00 X6.0000 Y3.0244 Z0.5000 58 | N0055 G00 X1.5165 Y5.1186 Z0.5000 59 | N0056 G01 X1.5165 Y5.1186 Z-0.0500 F10.00 60 | N0057 G01 X1.5165 Y5.6320 Z-0.0500 F40.00 61 | N0058 G00 X1.5165 Y5.6320 Z0.5000 62 | N0059 G00 X0.8518 Y5.1186 Z0.5000 63 | N0060 G01 X0.8518 Y5.1186 Z-0.0500 F10.00 64 | N0061 G01 X5.1482 Y5.1186 Z-0.0500 F40.00 65 | N0062 G00 X5.1482 Y5.1186 Z0.5000 66 | N0063 M5 67 | N0064 M30 68 | N0065 % 69 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # Change History 2 | 3 | ---- 4 | ## 0.2.1 5 | 6 | ### Improvements 7 | 8 | **`pygcode-norm` script:** 9 | 10 | Added "Final Machine Actions:" 11 | 12 | Final Machine Actions: 13 | standardize what's done at the end of a gcode program. 14 | 15 | --zero_xy, -zxy On completion, move straight up to 16 | rapid_safety_height, then across to X0 Y0. 17 | --zero_z, -zz On completion, move down to Z0 (done after zero_xy, if 18 | set). 19 | --rapid_safety_height RAPID_SAFETY_HEIGHT, -rsh RAPID_SAFETY_HEIGHT 20 | Z value to move to before traversing workpiece (if not 21 | set, max value will be attempted). 22 | --spindle_off, -so On completion, turn spindle off. 23 | 24 | 25 | Added ability to remove all codes & parameters that cannot be parsed. 26 | 27 | --rm_invalid_modal, -rmim 28 | Simply remove everything that isn't understood. Use 29 | with caution. 30 | 31 | **Library Improvements** 32 | 33 | * `Machine.abs2work()` and `Machine.work2abs()` position 34 | converters, apply machine's offset to the given position without effecting 35 | machine's current position. 36 | * `Machine.clean_block()` removes content from a block that's not parsable (use with caution) 37 | * `Machine.ignore_invalid_modal` bool class parameter, if set, will continue on merrily while ignoring 38 | anything not parsable (similarly to `clean_block`) 39 | * deployment version category validation in `setup.py` (ie: alpha, beta, and so on) 40 | 41 | ### Bugfixes 42 | 43 | (none) 44 | 45 | ---- 46 | ## 0.2.0 47 | 48 | Moved to `alpha` 49 | 50 | Improvements to read more versatile formats 51 | 52 | ### Improvements 53 | 54 | * Tests include a sample-set of gcode files from varying CAM software and authors. 55 | * `lsenv` in deployment script. 56 | * added `GCodeLineNumber` and `GCodeProgramName` (in response to [#5](https://github.com/fragmuffin/pygcode/issues/5)). 57 | * `GCodeCancelCannedCycle` sets machine mode to `None`, and is run first in list of `motion` gcodes. 58 | * Error message for unsupported gcodes is more helpful / relevant. 59 | * Optional whitespace in `Word`, (eg: `X-1.2` and `X -1.2` are now both valid) 60 | 61 | ### Bugfixes 62 | 63 | * [#5](https://github.com/fragmuffin/pygcode/issues/5) Line number in program 64 | 65 | 66 | ---- 67 | ## 0.1.2 68 | 69 | Changes to accommodate implementation of [grbl-stream](https://github.com/fragmuffin/grbl-stream) 70 | 71 | ### Improvements 72 | 73 | - added `NullMachine`, `NullState`, and `NullMode` (not assuming any machine state) 74 | - `Block` length is the number of gcodes + 1 if modal parameters exists 75 | 76 | ### Bugfixes 77 | 78 | - `%` enclosed lines are considered to be _macros_ when parsing 79 | - added axes `ABCXYZ` as valid parameters for `G10` (eg: `G10 L20 X0 Y0 Z0`) 80 | -------------------------------------------------------------------------------- /tests/test_words.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | # Add relative pygcode to path 4 | from testutils import add_pygcode_to_path, str_lines 5 | add_pygcode_to_path() 6 | 7 | # Units under test 8 | from pygcode import words 9 | from pygcode import dialects 10 | 11 | 12 | class WordIterTests(unittest.TestCase): 13 | def test_iter1(self): 14 | block_str = 'G01 Z-0.5 F100' 15 | w = list(words.text2words(block_str)) 16 | # word length 17 | self.assertEqual(len(w), 3) 18 | # word values 19 | self.assertEqual(w[0], words.Word('G', 1)) 20 | self.assertEqual(w[1], words.Word('Z', -0.5)) 21 | self.assertEqual(w[2], words.Word('F', 100)) 22 | 23 | def test_iter2(self): 24 | block_str = 'G02 X10.75 Y47.44 I-0.11 J-1.26 F70' 25 | w = list(words.text2words(block_str)) 26 | # word length 27 | self.assertEqual(len(w), 6) 28 | # word values 29 | self.assertEqual([w[0].letter, w[0].value], ['G', 2]) 30 | self.assertEqual([w[1].letter, w[1].value], ['X', 10.75]) 31 | self.assertEqual([w[2].letter, w[2].value], ['Y', 47.44]) 32 | self.assertEqual([w[3].letter, w[3].value], ['I', -0.11]) 33 | self.assertEqual([w[4].letter, w[4].value], ['J', -1.26]) 34 | self.assertEqual([w[5].letter, w[5].value], ['F', 70]) 35 | 36 | 37 | class WordValueMatchTest(unittest.TestCase): 38 | def regex_assertions(self, regex, positive_list, negative_list): 39 | # Assert all elements of positive_list match regex 40 | for (value_str, expected_match) in positive_list: 41 | match = regex.search(value_str) 42 | self.assertIsNotNone(match, "failed to match '%s'" % value_str) 43 | self.assertEqual(match.group(), expected_match) 44 | 45 | # Asesrt all elements of negative_list do not match regex 46 | for value_str in negative_list: 47 | match = regex.search(value_str) 48 | self.assertIsNone(match, "matched for '%s'" % value_str) 49 | 50 | 51 | class WordTests_LinuxCNC(WordValueMatchTest): 52 | def test_float(self): 53 | self.regex_assertions( 54 | regex=dialects.linuxcnc.REGEX_FLOAT, 55 | positive_list=[ 56 | ('1.2', '1.2'), ('1', '1'), ('200', '200'), ('0092', '0092'), 57 | ('1.', '1.'), ('.2', '.2'), ('-1.234', '-1.234'), 58 | ('-1.', '-1.'), ('-.289', '-.289'), 59 | (' 1.2', ' 1.2'), # leading whitespace 60 | # error cases (only detectable in gcode context) 61 | ('1.2e3', '1.2'), 62 | ], 63 | negative_list=['.'] 64 | ) 65 | 66 | def test_code(self): 67 | self.regex_assertions( 68 | regex=dialects.linuxcnc.REGEX_CODE, 69 | positive_list=[ 70 | ('1.2', '1.2'), ('1', '1'), ('10', '10'), 71 | ('02', '02'), ('02.3', '02.3'), 72 | ('1.', '1'), ('03 ', '03'), 73 | (' 2', ' 2'), # leading whitespace 74 | # error cases (only detectable in gcode context) 75 | ('30.12', '30.1'), 76 | ], 77 | negative_list=['.2', '.'] 78 | ) 79 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/plug6a.tap: -------------------------------------------------------------------------------- 1 | G00G21G17G90G40G49G80 2 | G91.1 3 | 4 | S12000M03 5 | G94 F600.0 6 | X0.000Y0.000 7 | G00X76.918Y-10.288 8 | G1X76.918Y-10.288 9 | G1X83.317Y-10.288 10 | G1X83.317Y-14.187 11 | G1X76.918Y-14.187 12 | G1X76.918Y-10.288 13 | G00X76.918Y-10.288 14 | G00X98.918Y-10.288 15 | G1X98.918Y-10.288 16 | G1X105.317Y-10.288 17 | G1X105.317Y-14.187 18 | G1X98.918Y-14.187 19 | G1X98.918Y-10.288 20 | G00X98.918Y-10.288 21 | G00X93.022Y5.812 22 | G1X93.022Y5.812 23 | G1X89.122Y5.812 24 | G1X89.122Y13.712 25 | G1X93.022Y13.712 26 | G1X93.022Y5.812 27 | G00X93.022Y5.812 28 | G00X-75.607Y-10.288 29 | G1X-75.607Y-10.288 30 | G1X-75.607Y-14.187 31 | G1X-82.006Y-14.187 32 | G1X-82.006Y-10.288 33 | G1X-75.607Y-10.288 34 | G00X-75.607Y-10.288 35 | G00X-97.607Y-10.288 36 | G1X-97.607Y-10.288 37 | G1X-97.607Y-14.187 38 | G1X-104.006Y-14.187 39 | G1X-104.006Y-10.288 40 | G1X-97.607Y-10.288 41 | G00X-97.607Y-10.288 42 | G00X-91.836Y5.812 43 | G1X-91.836Y5.812 44 | G1X-91.836Y13.712 45 | G1X-87.937Y13.712 46 | G1X-87.937Y5.812 47 | G1X-91.836Y5.812 48 | G00X-91.836Y5.812 49 | 50 | G00X-15.556Y7.900 51 | G1X-15.556Y7.900 52 | G2X-36.650Y9.539I13.488J310.094 53 | G2X-49.156Y11.364I21.153J188.675 54 | G2X-59.846Y13.733I24.394J135.416 55 | G2X-66.307Y15.887I13.581J51.503 56 | G1X-69.715Y17.476 57 | G2X-72.492Y19.421I6.409J12.108 58 | G1X-73.368Y20.224 59 | G1X-73.972Y20.718 60 | G3X-76.569Y22.055I-5.802J-8.079 61 | G3X-79.360Y22.552I-3.021J-8.872 62 | G1X-79.361Y22.552 63 | 64 | G1X-99.112Y22.555 65 | G3X-102.657Y21.875I0.233J-10.791 66 | G3X-107.639Y17.790I3.489J-9.335 67 | G3X-108.710Y15.360I8.094J-5.017 68 | G3X-109.131Y12.506I10.825J-3.055 69 | G1X-109.131Y12.505 70 | 71 | G1X-109.131Y-12.496 72 | G3X-108.453Y-16.055I10.908J0.233 73 | G3X-104.382Y-21.048I9.343J3.463 74 | G3X-101.954Y-22.123I5.022J8.056 75 | G3X-99.111Y-22.545I3.043J10.709 76 | G1X-79.424Y-22.540 77 | G1X-79.388Y-22.535 78 | G1X-79.168Y-22.520 79 | G3X-75.571Y-21.598I-0.501J9.434 80 | G3X-73.102Y-19.977I-4.851J10.075 81 | G1X-72.307Y-19.259 82 | G1X-71.489Y-18.581 83 | G2X-68.981Y-17.071I10.154J-14.026 84 | G2X-63.518Y-14.835I18.951J-38.502 85 | G2X-55.994Y-12.757I24.388J-73.663 86 | G2X-40.945Y-10.070I39.192J-176.018 87 | G2X-22.523Y-8.256I34.302J-253.893 88 | G2X16.846Y-7.894I23.114J-372.188 89 | G2X35.881Y-9.294I-13.948J-319.663 90 | G2X50.651Y-11.397I-20.156J-194.491 91 | G2X61.189Y-13.743I-24.422J-134.524 92 | G2X67.555Y-15.868I-13.517J-51.105 93 | G1X70.871Y-17.420 94 | G2X73.696Y-19.397I-6.781J-12.701 95 | G1X74.410Y-20.057 96 | G1X75.162Y-20.684 97 | G3X77.822Y-22.036I5.562J7.648 98 | G3X80.964Y-22.555I3.308J10.258 99 | 100 | G1X100.290Y-22.545 101 | G3X103.641Y-21.937I-0.239J10.842 102 | G3X108.492Y-18.272I-3.303J9.416 103 | G3X109.799Y-15.631I-7.810J5.509 104 | G3X110.309Y-12.496I-10.600J3.334 105 | G1X110.309Y-12.495 106 | 107 | G1X110.309Y12.506 108 | G3X109.631Y16.065I-10.907J-0.233 109 | G3X105.560Y21.057I-9.343J-3.464 110 | G3X103.131Y22.134I-5.025J-8.060 111 | G3X100.290Y22.555I-3.037J-10.689 112 | G1X100.289Y22.555 113 | 114 | G1X80.686Y22.540 115 | G3X77.158Y21.806I0.259J-10.091 116 | G3X74.187Y19.909I3.623J-8.951 117 | G1X73.261Y19.069 118 | G1X72.879Y18.756 119 | G2X69.069Y16.529I-11.097J14.613 120 | G2X64.340Y14.738I-21.639J50.004 121 | G2X54.729Y12.209I-26.464J81.059 122 | G2X39.610Y9.752I-40.016J198.475 123 | G2X21.814Y8.147I-32.998J266.303 124 | G2X-15.556Y7.900I-21.148J372.705 125 | G00X-15.556Y7.900 126 | 127 | G00X0.000Y0.000 128 | M09 129 | M30 130 | % 131 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Disc3.tap: -------------------------------------------------------------------------------- 1 | (Tweakie.CNC) 2 | G00G21G17G90G40G49G80 3 | G91.1 4 | T1M06 5 | G00G43Z10H1 6 | S12000M03 7 | G94 8 | X0.000Y0.000F300.0 9 | G00X17.477Y7.782Z6.000 10 | G1X17.477Y7.782Z-2.000F100.0 11 | G1X17.510Y7.801Z-2.000F300.0 12 | G1X17.687Y7.912Z-2.000 13 | G1X17.802Y7.990Z-2.000 14 | G3X18.687Y13.577I-2.351J3.236 15 | G3X13.100Y14.462I-3.236J-2.351 16 | G1X12.988Y14.374Z-2.000 17 | G1X12.802Y14.218Z-2.000 18 | G1X12.827Y14.253Z-2.000 19 | G1X12.941Y14.424Z-2.000 20 | G1X13.013Y14.540Z-2.000 21 | G3X11.549Y20.004I-3.464J2.000 22 | G3X6.085Y18.540I-2.000J-3.464 23 | G1X6.020Y18.416Z-2.000 24 | G1X5.913Y18.198Z-2.000 25 | G1X5.921Y18.243Z-2.000 26 | G1X5.956Y18.443Z-2.000 27 | G1X5.974Y18.576Z-2.000 28 | G3X2.414Y22.972I-3.978J0.418 29 | G3X-1.982Y19.412I-0.418J-3.978 30 | G1X-1.991Y19.275Z-2.000 31 | G1X-2.000Y19.029Z-2.000 32 | G1X-2.009Y19.067Z-2.000 33 | G1X-2.060Y19.268Z-2.000 34 | G1X-2.098Y19.400Z-2.000 35 | G3X-7.138Y21.968I-3.804J-1.236 36 | G3X-9.706Y16.928I1.236J-3.804 37 | G1X-9.659Y16.799Z-2.000 38 | G1X-9.567Y16.571Z-2.000 39 | G1X-9.590Y16.600Z-2.000 40 | G1X-9.718Y16.764Z-2.000 41 | G1X-9.807Y16.870Z-2.000 42 | G3X-15.456Y17.166I-2.973J-2.677 43 | G3X-15.752Y11.517I2.677J-2.973 44 | G1X-15.653Y11.414Z-2.000 45 | G1X-15.478Y11.245Z-2.000 46 | G1X-15.513Y11.265Z-2.000 47 | G1X-15.697Y11.362Z-2.000 48 | G1X-15.820Y11.422Z-2.000 49 | G3X-21.102Y9.395I-1.627J-3.654 50 | G3X-19.074Y4.114I3.654J-1.627 51 | G1X-18.943Y4.061Z-2.000 52 | G1X-18.715Y3.978Z-2.000 53 | G1X-18.765Y3.982Z-2.000 54 | G1X-18.966Y3.996Z-2.000 55 | G1X-19.099Y4.000Z-2.000 56 | G3X-23.099Y0.000I0.000J-4.000 57 | G3X-19.099Y-4.000I4.000J0.000 58 | G1X-18.962Y-3.995Z-2.000 59 | G1X-18.717Y-3.979Z-2.000 60 | G1X-18.754Y-3.992Z-2.000 61 | G1X-18.948Y-4.063Z-2.000 62 | G1X-19.074Y-4.114Z-2.000 63 | G3X-21.102Y-9.395I1.627J-3.654 64 | G3X-15.820Y-11.422I3.654J1.627 65 | G1X-15.692Y-11.360Z-2.000 66 | G1X-15.478Y-11.245Z-2.000 67 | G1X-15.508Y-11.274Z-2.000 68 | G1X-15.657Y-11.418Z-2.000 69 | G1X-15.752Y-11.517Z-2.000 70 | G3X-15.456Y-17.166I2.973J-2.677 71 | G3X-9.807Y-16.870I2.677J2.973 72 | G1X-9.717Y-16.762Z-2.000 73 | G1X-9.566Y-16.570Z-2.000 74 | G1X-9.582Y-16.606Z-2.000 75 | G1X-9.659Y-16.799Z-2.000 76 | G1X-9.706Y-16.928Z-2.000 77 | G3X-7.138Y-21.968I3.804J-1.236 78 | G3X-2.098Y-19.400I1.236J3.804 79 | G1X-2.059Y-19.265Z-2.000 80 | G1X-2.000Y-19.029Z-2.000 81 | G1X-1.999Y-19.071Z-2.000 82 | G1X-1.991Y-19.277Z-2.000 83 | G1X-1.982Y-19.412Z-2.000 84 | G3X2.414Y-22.972I3.978J0.418 85 | G3X5.974Y-18.576I-0.418J3.978 86 | G1X5.955Y-18.435Z-2.000 87 | G1X5.912Y-18.197Z-2.000 88 | G1X5.933Y-18.241Z-2.000 89 | G1X6.022Y-18.422Z-2.000 90 | G1X6.085Y-18.540Z-2.000 91 | G3X11.549Y-20.004I3.464J2.000 92 | G3X13.013Y-14.540I-2.000J3.464 93 | G1X12.939Y-14.421Z-2.000 94 | G1X12.802Y-14.218Z-2.000 95 | G1X12.831Y-14.242Z-2.000 96 | G1X12.990Y-14.376Z-2.000 97 | G1X13.100Y-14.462Z-2.000 98 | G3X18.687Y-13.577I2.351J3.236 99 | G3X17.802Y-7.990I-3.236J2.351 100 | G1X17.684Y-7.910Z-2.000 101 | G1X17.478Y-7.782Z-2.000 102 | G1X17.521Y-7.795Z-2.000 103 | G1X17.718Y-7.851Z-2.000 104 | G1X17.850Y-7.883Z-2.000 105 | G3X22.594Y-4.802I0.832J3.913 106 | G3X19.513Y-0.058I-3.913J0.832 107 | G1X19.374Y-0.034Z-2.000 108 | G1X19.133Y0.000Z-2.000 109 | G1X19.179Y0.006Z-2.000 110 | G1X19.380Y0.035Z-2.000 111 | G1X19.513Y0.058Z-2.000 112 | G3X22.594Y4.802I-0.832J3.913 113 | G3X17.850Y7.883I-3.913J-0.832 114 | G1X17.712Y7.849Z-2.000 115 | G1X17.477Y7.782Z-2.000 116 | G00X17.477Y7.782Z6.000 117 | G00Z10 118 | G00X0.000Y0.000 119 | M09 120 | M30 121 | % 122 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Disc3.tap: -------------------------------------------------------------------------------- 1 | (Tweakie.CNC) 2 | G00G21G17G90G40G49G80 3 | G71G91.1 4 | T1M06 5 | G00G43Z10H1 6 | S12000M03 7 | G94 8 | X0.000Y0.000F300.0 9 | G00X17.477Y7.782Z6.000 10 | G1X17.477Y7.782Z-2.000F100.0 11 | G1X17.510Y7.801Z-2.000F300.0 12 | G1X17.687Y7.912Z-2.000 13 | G1X17.802Y7.990Z-2.000 14 | G3X18.687Y13.577I-2.351J3.236 15 | G3X13.100Y14.462I-3.236J-2.351 16 | G1X12.988Y14.374Z-2.000 17 | G1X12.802Y14.218Z-2.000 18 | G1X12.827Y14.253Z-2.000 19 | G1X12.941Y14.424Z-2.000 20 | G1X13.013Y14.540Z-2.000 21 | G3X11.549Y20.004I-3.464J2.000 22 | G3X6.085Y18.540I-2.000J-3.464 23 | G1X6.020Y18.416Z-2.000 24 | G1X5.913Y18.198Z-2.000 25 | G1X5.921Y18.243Z-2.000 26 | G1X5.956Y18.443Z-2.000 27 | G1X5.974Y18.576Z-2.000 28 | G3X2.414Y22.972I-3.978J0.418 29 | G3X-1.982Y19.412I-0.418J-3.978 30 | G1X-1.991Y19.275Z-2.000 31 | G1X-2.000Y19.029Z-2.000 32 | G1X-2.009Y19.067Z-2.000 33 | G1X-2.060Y19.268Z-2.000 34 | G1X-2.098Y19.400Z-2.000 35 | G3X-7.138Y21.968I-3.804J-1.236 36 | G3X-9.706Y16.928I1.236J-3.804 37 | G1X-9.659Y16.799Z-2.000 38 | G1X-9.567Y16.571Z-2.000 39 | G1X-9.590Y16.600Z-2.000 40 | G1X-9.718Y16.764Z-2.000 41 | G1X-9.807Y16.870Z-2.000 42 | G3X-15.456Y17.166I-2.973J-2.677 43 | G3X-15.752Y11.517I2.677J-2.973 44 | G1X-15.653Y11.414Z-2.000 45 | G1X-15.478Y11.245Z-2.000 46 | G1X-15.513Y11.265Z-2.000 47 | G1X-15.697Y11.362Z-2.000 48 | G1X-15.820Y11.422Z-2.000 49 | G3X-21.102Y9.395I-1.627J-3.654 50 | G3X-19.074Y4.114I3.654J-1.627 51 | G1X-18.943Y4.061Z-2.000 52 | G1X-18.715Y3.978Z-2.000 53 | G1X-18.765Y3.982Z-2.000 54 | G1X-18.966Y3.996Z-2.000 55 | G1X-19.099Y4.000Z-2.000 56 | G3X-23.099Y0.000I0.000J-4.000 57 | G3X-19.099Y-4.000I4.000J0.000 58 | G1X-18.962Y-3.995Z-2.000 59 | G1X-18.717Y-3.979Z-2.000 60 | G1X-18.754Y-3.992Z-2.000 61 | G1X-18.948Y-4.063Z-2.000 62 | G1X-19.074Y-4.114Z-2.000 63 | G3X-21.102Y-9.395I1.627J-3.654 64 | G3X-15.820Y-11.422I3.654J1.627 65 | G1X-15.692Y-11.360Z-2.000 66 | G1X-15.478Y-11.245Z-2.000 67 | G1X-15.508Y-11.274Z-2.000 68 | G1X-15.657Y-11.418Z-2.000 69 | G1X-15.752Y-11.517Z-2.000 70 | G3X-15.456Y-17.166I2.973J-2.677 71 | G3X-9.807Y-16.870I2.677J2.973 72 | G1X-9.717Y-16.762Z-2.000 73 | G1X-9.566Y-16.570Z-2.000 74 | G1X-9.582Y-16.606Z-2.000 75 | G1X-9.659Y-16.799Z-2.000 76 | G1X-9.706Y-16.928Z-2.000 77 | G3X-7.138Y-21.968I3.804J-1.236 78 | G3X-2.098Y-19.400I1.236J3.804 79 | G1X-2.059Y-19.265Z-2.000 80 | G1X-2.000Y-19.029Z-2.000 81 | G1X-1.999Y-19.071Z-2.000 82 | G1X-1.991Y-19.277Z-2.000 83 | G1X-1.982Y-19.412Z-2.000 84 | G3X2.414Y-22.972I3.978J0.418 85 | G3X5.974Y-18.576I-0.418J3.978 86 | G1X5.955Y-18.435Z-2.000 87 | G1X5.912Y-18.197Z-2.000 88 | G1X5.933Y-18.241Z-2.000 89 | G1X6.022Y-18.422Z-2.000 90 | G1X6.085Y-18.540Z-2.000 91 | G3X11.549Y-20.004I3.464J2.000 92 | G3X13.013Y-14.540I-2.000J3.464 93 | G1X12.939Y-14.421Z-2.000 94 | G1X12.802Y-14.218Z-2.000 95 | G1X12.831Y-14.242Z-2.000 96 | G1X12.990Y-14.376Z-2.000 97 | G1X13.100Y-14.462Z-2.000 98 | G3X18.687Y-13.577I2.351J3.236 99 | G3X17.802Y-7.990I-3.236J2.351 100 | G1X17.684Y-7.910Z-2.000 101 | G1X17.478Y-7.782Z-2.000 102 | G1X17.521Y-7.795Z-2.000 103 | G1X17.718Y-7.851Z-2.000 104 | G1X17.850Y-7.883Z-2.000 105 | G3X22.594Y-4.802I0.832J3.913 106 | G3X19.513Y-0.058I-3.913J0.832 107 | G1X19.374Y-0.034Z-2.000 108 | G1X19.133Y0.000Z-2.000 109 | G1X19.179Y0.006Z-2.000 110 | G1X19.380Y0.035Z-2.000 111 | G1X19.513Y0.058Z-2.000 112 | G3X22.594Y4.802I-0.832J3.913 113 | G3X17.850Y7.883I-3.913J-0.832 114 | G1X17.712Y7.849Z-2.000 115 | G1X17.477Y7.782Z-2.000 116 | G00X17.477Y7.782Z6.000 117 | G00Z10 118 | G00X0.000Y0.000 119 | M09 120 | M30 121 | % 122 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/plug6a.tap: -------------------------------------------------------------------------------- 1 | G00G21G17G90G40G49G80 2 | G71G91.1 3 | 4 | S12000M03 5 | G94 M10P1 F600.0 6 | X0.000Y0.000 7 | G00X76.918Y-10.288M10P1 8 | G1X76.918Y-10.288M11P1 9 | G1X83.317Y-10.288 10 | G1X83.317Y-14.187 11 | G1X76.918Y-14.187 12 | G1X76.918Y-10.288 13 | G00X76.918Y-10.288M10P1 14 | G00X98.918Y-10.288 15 | G1X98.918Y-10.288M11P1 16 | G1X105.317Y-10.288 17 | G1X105.317Y-14.187 18 | G1X98.918Y-14.187 19 | G1X98.918Y-10.288 20 | G00X98.918Y-10.288M10P1 21 | G00X93.022Y5.812 22 | G1X93.022Y5.812M11P1 23 | G1X89.122Y5.812 24 | G1X89.122Y13.712 25 | G1X93.022Y13.712 26 | G1X93.022Y5.812 27 | G00X93.022Y5.812M10P1 28 | G00X-75.607Y-10.288 29 | G1X-75.607Y-10.288M11P1 30 | G1X-75.607Y-14.187 31 | G1X-82.006Y-14.187 32 | G1X-82.006Y-10.288 33 | G1X-75.607Y-10.288 34 | G00X-75.607Y-10.288M10P1 35 | G00X-97.607Y-10.288 36 | G1X-97.607Y-10.288M11P1 37 | G1X-97.607Y-14.187 38 | G1X-104.006Y-14.187 39 | G1X-104.006Y-10.288 40 | G1X-97.607Y-10.288 41 | G00X-97.607Y-10.288M10P1 42 | G00X-91.836Y5.812 43 | G1X-91.836Y5.812M11P1 44 | G1X-91.836Y13.712 45 | G1X-87.937Y13.712 46 | G1X-87.937Y5.812 47 | G1X-91.836Y5.812 48 | G00X-91.836Y5.812M10P1 49 | 50 | G00X-15.556Y7.900 51 | G1X-15.556Y7.900M11P1 52 | G2X-36.650Y9.539I13.488J310.094 53 | G2X-49.156Y11.364I21.153J188.675 54 | G2X-59.846Y13.733I24.394J135.416 55 | G2X-66.307Y15.887I13.581J51.503 56 | G1X-69.715Y17.476 57 | G2X-72.492Y19.421I6.409J12.108 58 | G1X-73.368Y20.224 59 | G1X-73.972Y20.718 60 | G3X-76.569Y22.055I-5.802J-8.079 61 | G3X-79.360Y22.552I-3.021J-8.872 62 | G1X-79.361Y22.552 63 | 64 | G1X-99.112Y22.555 65 | G3X-102.657Y21.875I0.233J-10.791 66 | G3X-107.639Y17.790I3.489J-9.335 67 | G3X-108.710Y15.360I8.094J-5.017 68 | G3X-109.131Y12.506I10.825J-3.055 69 | G1X-109.131Y12.505 70 | 71 | G1X-109.131Y-12.496 72 | G3X-108.453Y-16.055I10.908J0.233 73 | G3X-104.382Y-21.048I9.343J3.463 74 | G3X-101.954Y-22.123I5.022J8.056 75 | G3X-99.111Y-22.545I3.043J10.709 76 | G1X-79.424Y-22.540 77 | G1X-79.388Y-22.535 78 | G1X-79.168Y-22.520 79 | G3X-75.571Y-21.598I-0.501J9.434 80 | G3X-73.102Y-19.977I-4.851J10.075 81 | G1X-72.307Y-19.259 82 | G1X-71.489Y-18.581 83 | G2X-68.981Y-17.071I10.154J-14.026 84 | G2X-63.518Y-14.835I18.951J-38.502 85 | G2X-55.994Y-12.757I24.388J-73.663 86 | G2X-40.945Y-10.070I39.192J-176.018 87 | G2X-22.523Y-8.256I34.302J-253.893 88 | G2X16.846Y-7.894I23.114J-372.188 89 | G2X35.881Y-9.294I-13.948J-319.663 90 | G2X50.651Y-11.397I-20.156J-194.491 91 | G2X61.189Y-13.743I-24.422J-134.524 92 | G2X67.555Y-15.868I-13.517J-51.105 93 | G1X70.871Y-17.420 94 | G2X73.696Y-19.397I-6.781J-12.701 95 | G1X74.410Y-20.057 96 | G1X75.162Y-20.684 97 | G3X77.822Y-22.036I5.562J7.648 98 | G3X80.964Y-22.555I3.308J10.258 99 | 100 | G1X100.290Y-22.545 101 | G3X103.641Y-21.937I-0.239J10.842 102 | G3X108.492Y-18.272I-3.303J9.416 103 | G3X109.799Y-15.631I-7.810J5.509 104 | G3X110.309Y-12.496I-10.600J3.334 105 | G1X110.309Y-12.495 106 | 107 | G1X110.309Y12.506 108 | G3X109.631Y16.065I-10.907J-0.233 109 | G3X105.560Y21.057I-9.343J-3.464 110 | G3X103.131Y22.134I-5.025J-8.060 111 | G3X100.290Y22.555I-3.037J-10.689 112 | G1X100.289Y22.555 113 | 114 | G1X80.686Y22.540 115 | G3X77.158Y21.806I0.259J-10.091 116 | G3X74.187Y19.909I3.623J-8.951 117 | G1X73.261Y19.069 118 | G1X72.879Y18.756 119 | G2X69.069Y16.529I-11.097J14.613 120 | G2X64.340Y14.738I-21.639J50.004 121 | G2X54.729Y12.209I-26.464J81.059 122 | G2X39.610Y9.752I-40.016J198.475 123 | G2X21.814Y8.147I-32.998J266.303 124 | G2X-15.556Y7.900I-21.148J372.705 125 | G00X-15.556Y7.900M10P1 126 | M10P1 127 | G00X0.000Y0.000 128 | M09 129 | M30 130 | % 131 | -------------------------------------------------------------------------------- /src/pygcode/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from copy import copy, deepcopy 3 | 4 | from euclid3 import Vector3, Quaternion 5 | 6 | 7 | # ==================== Geometric Utilities ==================== 8 | def quat2align(to_align, with_this, normalize=True): 9 | """ 10 | Calculate Quaternion that will rotate a given vector to align with another 11 | can accumulate with perpendicular alignemnt vectors like so: 12 | (x_axis, z_axis) = (Vector3(1, 0, 0), Vector3(0, 0, 1)) 13 | q1 = quat2align(v1, z_axis) 14 | q2 = quat2align(q1 * v2, x_axis) 15 | # assuming v1 is perpendicular to v2 16 | q3 = q2 * q1 # application of q3 to any vector will re-orient it to the 17 | # coordinate system defined by (v1,v2), as (z,x) respectively. 18 | :param to_align: Vector3 instance to be rotated 19 | :param with_this: Vector3 instance as target for alignment 20 | :result: Quaternion such that: q * to_align == with_this 21 | """ 22 | # Normalize Vectors 23 | if normalize: 24 | to_align = to_align.normalized() 25 | with_this = with_this.normalized() 26 | # Calculate Quaternion 27 | return Quaternion.new_rotate_axis( 28 | angle=to_align.angle(with_this), 29 | axis=to_align.cross(with_this), 30 | ) 31 | 32 | 33 | def quat2coord_system(origin1, origin2, align1, align2): 34 | """ 35 | Calculate Quaternion to apply to any vector to re-orientate it to another 36 | (target) coordinate system. 37 | (note: both origin and align coordinate systems must use right-hand-rule) 38 | :param origin1: origin(1|2) are perpendicular vectors in the original coordinate system 39 | :param origin2: see origin1 40 | :param align1: align(1|2) are 2 perpendicular vectors in the target coordinate system 41 | :param align2: see align1 42 | :return: Quaternion such that q * origin1 = align1, and q * origin2 = align2 43 | """ 44 | # Normalize Vectors 45 | origin1 = origin1.normalized() 46 | origin2 = origin2.normalized() 47 | align1 = align1.normalized() 48 | align2 = align2.normalized() 49 | # Calculate Quaternion 50 | q1 = quat2align(origin1, align1, normalize=False) 51 | q2 = quat2align(q1 * origin2, align2, normalize=False) 52 | return q2 * q1 53 | 54 | 55 | def plane_projection(vect, normal): 56 | """ 57 | Project vect to a plane represented by normal 58 | :param vect: vector to be projected (Vector3) 59 | :param normal: normal of plane to project on to (Vector3) 60 | :return: vect projected onto plane represented by normal 61 | """ 62 | # ref: https://en.wikipedia.org/wiki/Vector_projection 63 | n = normal.normalized() 64 | return vect - (n * vect.dot(n)) 65 | 66 | 67 | # ==================== GCode Utilities ==================== 68 | def omit_redundant_modes(gcode_iter): 69 | """ 70 | Replace redundant machine motion modes with whitespace, 71 | :param gcode_iter: iterable to return with modifications 72 | """ 73 | 74 | from .machine import Machine, Mode 75 | from .gcodes import MODAL_GROUP_MAP 76 | class NullModeMachine(Machine): 77 | MODE_CLASS = type('NullMode', (Mode,), {'default_mode': ''}) 78 | m = NullModeMachine() 79 | 80 | for g in gcode_iter: 81 | if (g.modal_group is not None) and (m.mode.modal_groups[g.modal_group] is not None): 82 | # g-code has a modal groups, and the machine's mode 83 | # (of the same modal group) is not None 84 | if m.mode.modal_groups[g.modal_group].word == g.word: 85 | # machine's mode & g-code's mode match (no machine change) 86 | if g.modal_group == MODAL_GROUP_MAP['motion']: 87 | # finally: g-code sets a motion mode in the machine 88 | g = copy(g) # duplicate gcode object 89 | # stop redundant g-code word from being printed 90 | g._whitespace_prefix = True 91 | 92 | m.process_gcodes(g) 93 | yield g 94 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Alien.tap: -------------------------------------------------------------------------------- 1 | (for 1/8"dia cutter or 'V' cutter) 2 | G20 3 | G90 G91.1 4 | G64 5 | G00 Z0.125 6 | M06 T0 7 | M03 8 | G00 X-0.299635 Y-2.672109 9 | G01 F15 Z-0.050 10 | G01 F30 X0.242283 11 | G00 Z0.125 12 | G00 X0.281184 Y-0.761984 13 | G01 F15 Z-0.050 14 | G03 F30 X0.090095 Y-0.953059 I-0.022534 J-0.168553 15 | G00 Z0.125 16 | G00 X-0.324529 Y-0.753181 17 | G01 F15 Z-0.050 18 | G02 F30 X-0.132317 Y-0.945379 I0.022666 J-0.169545 19 | G00 Z0.125 20 | G00 X-0.475024 Y0.265282 21 | G01 F15 Z-0.050 22 | G02 F30 X-0.600539 Y0.198916 I-0.145025 J0.122407 23 | G02 X-0.83879 Y0.218446 I-0.069565 J0.614393 24 | G01 X-0.8388 Y0.218448 25 | G02 X-1.17323 Y0.343422 I0.452748 J1.721514 26 | G02 X-1.479371 Y0.527113 I0.95037 J1.93085 27 | G02 X-1.768868 Y0.851146 I0.606555 J0.833245 28 | G02 X-1.839332 Y1.06599 I0.491952 J0.280324 29 | G03 X-1.839333 Y1.066 I-0.000497 J-0.000058 30 | G02 X-1.78474 Y1.285138 I0.301344 J0.041296 31 | G02 X-1.587205 Y1.397775 I0.243525 J-0.197547 32 | G03 X-1.587193 Y1.397777 I-0.000073 J0.000495 33 | G02 X-1.419527 Y1.40102 I0.094434 J-0.54653 34 | G02 X-1.17848 Y1.332849 I-0.115339 J-0.868079 35 | G02 X-0.962004 Y1.206631 I-0.44674 J-1.014955 36 | G02 X-0.760908 Y1.019047 I-0.766042 J-1.022801 37 | G02 X-0.525644 Y0.676937 I-1.265297 J-1.122075 38 | G02 X-0.450756 Y0.477071 I-0.79209 J-0.410751 39 | G02 X-0.440417 Y0.368565 I-0.342239 J-0.087354 40 | G03 X-0.440418 Y0.368546 I0.000499 J-0.00003 41 | G02 X-0.475024 Y0.265282 I-0.183129 J0.00394 42 | G00 Z0.125 43 | G00 X0.457507 Y0.266706 44 | G01 F15 Z-0.050 45 | G02 F30 X0.422901 Y0.36997 I0.148522 J0.107204 46 | G03 X0.4229 Y0.369989 I-0.0005 J-0.000011 47 | G02 X0.433238 Y0.478489 I0.352559 J0.021153 48 | G02 X0.508127 Y0.678362 I0.867003 J-0.210888 49 | G02 X0.743386 Y1.020466 I1.500537 J-0.77995 50 | G02 X0.944487 Y1.208056 I0.967165 J-0.835237 51 | G02 X1.160961 Y1.334272 I0.663209 J-0.888726 52 | G02 X1.40201 Y1.402444 I0.356389 J-0.799915 53 | G02 X1.569676 Y1.399201 I0.073232 J-0.549773 54 | G03 X1.569688 Y1.399199 I0.000085 J0.000493 55 | G02 X1.767223 Y1.286562 I-0.04599 J-0.310184 56 | G02 X1.821816 Y1.067424 I-0.246751 J-0.177841 57 | G03 X1.821814 Y1.067414 I0.000495 J-0.000068 58 | G02 X1.751352 Y0.852571 I-0.562414 J0.06548 59 | G02 X1.461851 Y0.528535 I-0.896059 J0.509216 60 | G02 X1.155713 Y0.344846 I-1.256498 J1.747145 61 | G02 X0.821283 Y0.219872 I-0.78718 J1.596542 62 | G01 X0.821273 Y0.21987 63 | G02 X0.583027 Y0.20034 I-0.168685 J0.594851 64 | G02 X0.457507 Y0.266706 I0.019508 J0.188779 65 | G00 Z0.125 66 | G00 X2.460439 Y0.585937 67 | G01 F15 Z-0.050 68 | G02 F30 X2.446297 Y0.275659 I-3.294169 J-0.005312 69 | G02 X2.353243 Y-0.298841 I-4.012489 J0.35513 70 | G02 X2.027545 Y-1.264386 I-5.219711 J1.223007 71 | G02 X1.226249 Y-2.57332 I-5.185175 J2.2745 72 | G02 X0.961742 Y-2.861901 I-2.578692 J2.098069 73 | G01 X0.961736 Y-2.861906 74 | G02 X0.742197 Y-3.058791 I-1.898965 J1.896629 75 | G01 X0.74219 Y-3.058796 76 | G02 X0.371109 Y-3.30128 I-1.243094 J1.497168 77 | G02 X0.16307 Y-3.38167 I-0.54818 J1.109229 78 | G02 X-0.057563 Y-3.413565 I-0.231363 J0.821398 79 | G02 X-0.369146 Y-3.351629 I0.011233 J0.871233 80 | G02 X-0.629013 Y-3.218586 I0.486391 J1.270358 81 | G02 X-1.047155 Y-2.872594 I1.371483 J2.083143 82 | G02 X-1.626308 Y-2.125388 I3.153785 J3.042526 83 | G02 X-2.222722 Y-0.841132 I4.913442 J3.062441 84 | G02 X-2.395533 Y-0.152417 I4.918815 J1.600253 85 | G02 X-2.460446 Y0.554692 I4.223296 J0.744236 86 | G02 X-2.297429 Y1.565427 I3.14925 J0.010585 87 | G02 X-1.438033 Y2.831134 I2.723838 J-0.924834 88 | G02 X-0.78046 Y3.243225 I1.730883 J-2.031278 89 | G02 X-0.410567 Y3.366616 I0.959272 J-2.259514 90 | G02 X-0.023439 Y3.413565 I0.383777 J-1.54498 91 | G02 X0.490695 Y3.335659 I-0.020852 J-1.873067 92 | G02 X1.297896 Y2.930895 I-0.75152 J-2.505977 93 | G02 X2.163515 Y1.899368 I-1.681057 J-2.289642 94 | G02 X2.460439 Y0.585937 I-2.662859 J-1.292264 95 | G00 Z0.125 96 | G00 X0 Y0 97 | M05 98 | M30 99 | % 100 | 101 | -------------------------------------------------------------------------------- /src/pygcode/block.py: -------------------------------------------------------------------------------- 1 | import re 2 | from .words import text2words 3 | from .gcodes import words2gcodes 4 | from . import dialects 5 | 6 | 7 | class Block(object): 8 | """GCode block (effectively any gcode file line that defines any )""" 9 | 10 | def __init__(self, text=None, dialect=None, verify=True): 11 | """ 12 | Block Constructor 13 | :param text: gcode line content (including comments) as string 14 | :type text: :class:`str` 15 | :param verify: verify given codes (modal & non-modal are not repeated) 16 | :type verify: :class:`bool` 17 | 18 | .. note:: 19 | 20 | State & machine specific codes cannot be verified at this point; 21 | they must be processed by a virtual machine to be fully verified. 22 | 23 | """ 24 | 25 | self._raw_text = None 26 | self._text = None 27 | self.words = [] 28 | self.gcodes = [] 29 | self.modal_params = [] 30 | 31 | if dialect is None: 32 | dialect = dialects.get_default() 33 | self.dialect = dialect 34 | 35 | self._word_map = getattr(getattr(dialects, dialect), 'WORD_MAP') 36 | 37 | # clean up block string 38 | if text: 39 | self._raw_text = text # unaltered block content (before alteration) 40 | text = re.sub(r'(^\s+|\s+$)', '', text) # remove whitespace padding 41 | text = re.sub(r'\s+', ' ', text) # remove duplicate whitespace with ' ' 42 | self._text = text # cleaned up block content 43 | 44 | # Get words from text, and group into gcodes 45 | self.words = list(text2words(self._text)) 46 | (self.gcodes, self.modal_params) = words2gcodes(self.words) 47 | 48 | # Verification 49 | if verify: 50 | self._assert_gcodes() 51 | 52 | @property 53 | def text(self): 54 | if self._text: 55 | return self._text 56 | return str(self) 57 | 58 | def _assert_gcodes(self): 59 | modal_groups = set() 60 | code_words = set() 61 | 62 | for gc in self.gcodes: 63 | 64 | # Assert all gcodes are not repeated in the same block 65 | if gc.word in code_words: 66 | raise AssertionError("%s cannot be in the same block" % ([ 67 | x for x in self.gcodes 68 | if x.modal_group == gc.modal_group 69 | ])) 70 | code_words.add(gc.word) 71 | 72 | # Assert all gcodes are from different modal groups 73 | if gc.modal_group is not None: 74 | if gc.modal_group in modal_groups: 75 | raise AssertionError("%s cannot be in the same block" % ([ 76 | x for x in self.gcodes 77 | if x.modal_group == gc.modal_group 78 | ])) 79 | modal_groups.add(gc.modal_group) 80 | 81 | def __getattr__(self, k): 82 | if k in self._word_map: 83 | for w in self.words: 84 | if w.letter == k: 85 | return w 86 | # if word is not in this block: 87 | return None 88 | 89 | else: 90 | raise AttributeError("'{cls}' object has no attribute '{key}'".format( 91 | cls=self.__class__.__name__, 92 | key=k 93 | )) 94 | 95 | def __len__(self): 96 | """ 97 | Block length = number of identified gcodes (+ 1 if any modal parameters are defined) 98 | :return: block length 99 | """ 100 | length = len(self.gcodes) 101 | if self.modal_params: 102 | length += 1 103 | return length 104 | 105 | def __bool__(self): 106 | return bool(self.words) 107 | 108 | __nonzero__ = __bool__ # python < 3 compatability 109 | 110 | def __str__(self): 111 | return ' '.join(str(x) for x in (self.gcodes + self.modal_params)) 112 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Cross2.tap: -------------------------------------------------------------------------------- 1 | ( Cross2 ) 2 | ( Material Size ) 3 | ( X=96 mm, Y=120 mm, Z=6 mm ) 4 | ( Tools used in this file: ) 5 | ( T1 = End Mill 4 mm ) 6 | () 7 | N100G00G21G17G90G40G49G80 8 | N110G91.1 9 | N120T1M06 10 | N130 11 | N140G00G43Z6.001H1 12 | N150S12000M03 13 | N160 14 | N170 15 | N180G94 16 | N190X0.000Y0.000F300.0 17 | N200G00X19.365Y10.204 18 | N210G1X19.365Y10.204F100.0Z-6.250 19 | N220G3X21.516Y8.215I3.342J1.456F300.0 20 | N230G3X34.429Y4.911I26.451J76.500 21 | N240G1X39.329Y4.240 22 | N250G1X40.693Y4.106 23 | N260G1X41.226Y2.568 24 | N270G3X45.677Y-1.360I6.769J3.185 25 | N280G1X46.900Y-1.639 26 | N290G1X47.924Y-1.727 27 | N300G1X48.076Y-1.727 28 | N310G1X49.100Y-1.639 29 | N320G1X50.323Y-1.360 30 | N330G3X54.774Y2.568I-2.318J7.113 31 | N340G1X55.307Y4.106 32 | N350G1X56.671Y4.240 33 | N360G1X61.571Y4.911 34 | N370G3X74.484Y8.215I-13.538J79.804 35 | N380G3X76.635Y10.204I-1.191J3.445 36 | N390G3X76.934Y11.837I-3.337J1.454 37 | N400G3X76.476Y13.433I-3.637J-0.179 38 | N410G1X76.387Y13.582 39 | N420G1X76.279Y13.746 40 | N430G1X75.550Y14.804 41 | N440G1X72.855Y18.835 42 | N450G2X60.886Y44.862I91.386J57.794 43 | N460G2X56.171Y73.119I103.357J31.767 44 | N470G1X56.124Y74.803 45 | N480G1X56.123Y74.969 46 | N490G1X57.117Y74.965 47 | N500G1X60.431Y74.897 48 | N510G1X64.581Y74.650 49 | N520G2X74.700Y72.963I-6.338J-69.199 50 | N530G2X84.461Y69.804I-16.458J-67.516 51 | N540G1X85.953Y69.181 52 | N550G1X86.868Y68.927 53 | N560G3X89.334Y69.473I0.494J3.607 54 | N570G3X90.862Y71.480I-2.004J3.111 55 | N580G1X91.035Y72.102 56 | N590G1X91.308Y73.289 57 | N600G1X91.347Y73.474 58 | N610G3X93.056Y73.960I-1.143J7.266 59 | N620G3X97.553Y82.081I-2.910J6.917 60 | N630G3X91.345Y88.286I-7.408J-1.204 61 | N640G1X91.234Y88.787 62 | N650G1X90.775Y90.562 63 | N660G1X90.576Y91.205 64 | N670G3X89.881Y92.426I-3.393J-1.123 65 | N680G3X88.118Y93.544I-2.750J-2.389 66 | N690G3X86.033Y93.514I-0.993J-3.526 67 | N700G1X85.430Y93.282 68 | N710G1X84.091Y92.688 69 | N720G2X58.689Y87.223I-27.064J64.027 70 | N730G1X56.527Y87.219 71 | N740G1X56.532Y87.237 72 | N750G1X56.726Y88.030 73 | N760G2X66.598Y104.001I30.363J-7.731 74 | N770G1X67.446Y104.720 75 | N780G3X68.587Y106.437I-2.308J2.772 76 | N790G3X68.532Y108.707I-3.443J1.053 77 | N800G3X67.160Y110.507I-3.529J-1.268 78 | N810G1X66.617Y110.866 79 | N820G1X65.603Y111.480 80 | N830G3X60.338Y113.988I-17.052J-29.028 81 | N840G3X55.464Y115.399I-11.723J-31.363 82 | N850G3X53.114Y119.751I-7.439J-1.206 83 | N860G3X48.560Y121.710I-5.090J-5.558 84 | N870G1X48.236Y121.724 85 | N880G1X48.065Y121.728 86 | N890G1X47.935Y121.728 87 | N900G1X47.764Y121.724 88 | N910G1X47.440Y121.710 89 | N920G3X42.886Y119.751I0.535J-7.517 90 | N930G3X40.536Y115.399I5.089J-5.557 91 | N940G3X35.662Y113.988I6.852J-32.786 92 | N950G3X30.397Y111.480I11.786J-31.535 93 | N960G1X29.383Y110.866 94 | N970G1X28.840Y110.507 95 | N980G3X27.468Y108.707I2.157J-3.067 96 | N990G3X27.413Y106.437I3.388J-1.218 97 | N1000G3X28.554Y104.720I3.449J1.055 98 | N1010G1X29.402Y104.001 99 | N1020G2X39.274Y88.030I-20.492J-23.702 100 | N1030G1X39.468Y87.237 101 | N1040G1X39.473Y87.219 102 | N1050G1X37.311Y87.223 103 | N1060G2X11.909Y92.688I1.662J69.492 104 | N1070G1X10.570Y93.282 105 | N1080G1X9.967Y93.514 106 | N1090G3X7.882Y93.544I-1.092J-3.496 107 | N1100G3X6.119Y92.426I0.988J-3.506 108 | N1110G3X5.424Y91.205I2.698J-2.344 109 | N1120G1X5.225Y90.562 110 | N1130G1X4.766Y88.787 111 | N1140G1X4.655Y88.286 112 | N1150G3X-1.553Y82.081I1.201J-7.409 113 | N1160G3X2.944Y73.960I7.407J-1.204 114 | N1170G3X4.653Y73.474I2.843J6.748 115 | N1180G1X4.692Y73.289 116 | N1190G1X4.965Y72.102 117 | N1200G1X5.138Y71.480 118 | N1210G3X6.666Y69.473I3.532J1.104 119 | N1220G3X9.132Y68.927I1.972J3.061 120 | N1230G1X10.047Y69.181 121 | N1240G1X11.539Y69.804 122 | N1250G2X21.300Y72.963I26.219J-64.357 123 | N1260G2X31.419Y74.650I16.457J-67.512 124 | N1270G1X35.569Y74.897 125 | N1280G1X38.883Y74.965 126 | N1290G1X39.877Y74.969 127 | N1300G1X39.876Y74.803 128 | N1310G1X39.829Y73.119 129 | N1320G2X35.114Y44.862I-108.072J3.510 130 | N1330G2X23.145Y18.835I-103.356J31.767 131 | N1340G1X20.450Y14.804 132 | N1350G1X19.721Y13.746 133 | N1360G1X19.613Y13.582 134 | N1370G1X19.524Y13.433 135 | N1380G3X19.066Y11.837I3.179J-1.775 136 | N1390G3X19.365Y10.204I3.636J-0.179 137 | N1400G00X19.365Y10.204Z6.000 138 | N1410G00Z6.001M05 139 | N1420G00X0.000Y0.000 140 | N1430M30 141 | % 142 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Cross2.tap: -------------------------------------------------------------------------------- 1 | ( Cross2 ) 2 | ( Material Size ) 3 | ( X=96 mm, Y=120 mm, Z=6 mm ) 4 | ( Tools used in this file: ) 5 | ( T1 = End Mill 4 mm ) 6 | () 7 | N100G00G21G17G90G40G49G80 8 | N110G71G91.1 9 | N120T1M06 10 | N130 11 | N140G00G43Z6.001H1 12 | N150S12000M03 13 | N160 14 | N170 15 | N180G94 16 | N190X0.000Y0.000F300.0 17 | N200G00X19.365Y10.204 18 | N210G1X19.365Y10.204F100.0Z-6.250 19 | N220G3X21.516Y8.215I3.342J1.456F300.0 20 | N230G3X34.429Y4.911I26.451J76.500 21 | N240G1X39.329Y4.240 22 | N250G1X40.693Y4.106 23 | N260G1X41.226Y2.568 24 | N270G3X45.677Y-1.360I6.769J3.185 25 | N280G1X46.900Y-1.639 26 | N290G1X47.924Y-1.727 27 | N300G1X48.076Y-1.727 28 | N310G1X49.100Y-1.639 29 | N320G1X50.323Y-1.360 30 | N330G3X54.774Y2.568I-2.318J7.113 31 | N340G1X55.307Y4.106 32 | N350G1X56.671Y4.240 33 | N360G1X61.571Y4.911 34 | N370G3X74.484Y8.215I-13.538J79.804 35 | N380G3X76.635Y10.204I-1.191J3.445 36 | N390G3X76.934Y11.837I-3.337J1.454 37 | N400G3X76.476Y13.433I-3.637J-0.179 38 | N410G1X76.387Y13.582 39 | N420G1X76.279Y13.746 40 | N430G1X75.550Y14.804 41 | N440G1X72.855Y18.835 42 | N450G2X60.886Y44.862I91.386J57.794 43 | N460G2X56.171Y73.119I103.357J31.767 44 | N470G1X56.124Y74.803 45 | N480G1X56.123Y74.969 46 | N490G1X57.117Y74.965 47 | N500G1X60.431Y74.897 48 | N510G1X64.581Y74.650 49 | N520G2X74.700Y72.963I-6.338J-69.199 50 | N530G2X84.461Y69.804I-16.458J-67.516 51 | N540G1X85.953Y69.181 52 | N550G1X86.868Y68.927 53 | N560G3X89.334Y69.473I0.494J3.607 54 | N570G3X90.862Y71.480I-2.004J3.111 55 | N580G1X91.035Y72.102 56 | N590G1X91.308Y73.289 57 | N600G1X91.347Y73.474 58 | N610G3X93.056Y73.960I-1.143J7.266 59 | N620G3X97.553Y82.081I-2.910J6.917 60 | N630G3X91.345Y88.286I-7.408J-1.204 61 | N640G1X91.234Y88.787 62 | N650G1X90.775Y90.562 63 | N660G1X90.576Y91.205 64 | N670G3X89.881Y92.426I-3.393J-1.123 65 | N680G3X88.118Y93.544I-2.750J-2.389 66 | N690G3X86.033Y93.514I-0.993J-3.526 67 | N700G1X85.430Y93.282 68 | N710G1X84.091Y92.688 69 | N720G2X58.689Y87.223I-27.064J64.027 70 | N730G1X56.527Y87.219 71 | N740G1X56.532Y87.237 72 | N750G1X56.726Y88.030 73 | N760G2X66.598Y104.001I30.363J-7.731 74 | N770G1X67.446Y104.720 75 | N780G3X68.587Y106.437I-2.308J2.772 76 | N790G3X68.532Y108.707I-3.443J1.053 77 | N800G3X67.160Y110.507I-3.529J-1.268 78 | N810G1X66.617Y110.866 79 | N820G1X65.603Y111.480 80 | N830G3X60.338Y113.988I-17.052J-29.028 81 | N840G3X55.464Y115.399I-11.723J-31.363 82 | N850G3X53.114Y119.751I-7.439J-1.206 83 | N860G3X48.560Y121.710I-5.090J-5.558 84 | N870G1X48.236Y121.724 85 | N880G1X48.065Y121.728 86 | N890G1X47.935Y121.728 87 | N900G1X47.764Y121.724 88 | N910G1X47.440Y121.710 89 | N920G3X42.886Y119.751I0.535J-7.517 90 | N930G3X40.536Y115.399I5.089J-5.557 91 | N940G3X35.662Y113.988I6.852J-32.786 92 | N950G3X30.397Y111.480I11.786J-31.535 93 | N960G1X29.383Y110.866 94 | N970G1X28.840Y110.507 95 | N980G3X27.468Y108.707I2.157J-3.067 96 | N990G3X27.413Y106.437I3.388J-1.218 97 | N1000G3X28.554Y104.720I3.449J1.055 98 | N1010G1X29.402Y104.001 99 | N1020G2X39.274Y88.030I-20.492J-23.702 100 | N1030G1X39.468Y87.237 101 | N1040G1X39.473Y87.219 102 | N1050G1X37.311Y87.223 103 | N1060G2X11.909Y92.688I1.662J69.492 104 | N1070G1X10.570Y93.282 105 | N1080G1X9.967Y93.514 106 | N1090G3X7.882Y93.544I-1.092J-3.496 107 | N1100G3X6.119Y92.426I0.988J-3.506 108 | N1110G3X5.424Y91.205I2.698J-2.344 109 | N1120G1X5.225Y90.562 110 | N1130G1X4.766Y88.787 111 | N1140G1X4.655Y88.286 112 | N1150G3X-1.553Y82.081I1.201J-7.409 113 | N1160G3X2.944Y73.960I7.407J-1.204 114 | N1170G3X4.653Y73.474I2.843J6.748 115 | N1180G1X4.692Y73.289 116 | N1190G1X4.965Y72.102 117 | N1200G1X5.138Y71.480 118 | N1210G3X6.666Y69.473I3.532J1.104 119 | N1220G3X9.132Y68.927I1.972J3.061 120 | N1230G1X10.047Y69.181 121 | N1240G1X11.539Y69.804 122 | N1250G2X21.300Y72.963I26.219J-64.357 123 | N1260G2X31.419Y74.650I16.457J-67.512 124 | N1270G1X35.569Y74.897 125 | N1280G1X38.883Y74.965 126 | N1290G1X39.877Y74.969 127 | N1300G1X39.876Y74.803 128 | N1310G1X39.829Y73.119 129 | N1320G2X35.114Y44.862I-108.072J3.510 130 | N1330G2X23.145Y18.835I-103.356J31.767 131 | N1340G1X20.450Y14.804 132 | N1350G1X19.721Y13.746 133 | N1360G1X19.613Y13.582 134 | N1370G1X19.524Y13.433 135 | N1380G3X19.066Y11.837I3.179J-1.775 136 | N1390G3X19.365Y10.204I3.636J-0.179 137 | N1400G00X19.365Y10.204Z6.000 138 | N1410G00Z6.001M05 139 | N1420G00X0.000Y0.000 140 | N1430M30 141 | % 142 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import os 3 | import re 4 | from distutils.version import LooseVersion 5 | 6 | from setuptools import setup, find_packages 7 | 8 | 9 | # Setup template thanks to: Hynek Schlawack 10 | # https://hynek.me/articles/sharing-your-labor-of-love-pypi-quick-and-dirty/ 11 | ################################################################### 12 | 13 | NAME = "pygcode" 14 | PACKAGES = find_packages(where="src") 15 | META_PATH = os.path.join("src", NAME, "__init__.py") 16 | KEYWORDS = ['gcode', 'cnc', 'parser', 'interpreter'] 17 | CLASSIFIERS = [ 18 | "Development Status :: 3 - Alpha", # see src/pygcode/__init__.py 19 | "Intended Audience :: Developers", 20 | "Intended Audience :: Manufacturing", 21 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 22 | "Natural Language :: English", 23 | "Operating System :: OS Independent", 24 | "Programming Language :: Python", 25 | "Programming Language :: Python :: 2", 26 | "Programming Language :: Python :: 3", 27 | "Topic :: Scientific/Engineering", 28 | ] 29 | INSTALL_REQUIRES = [ 30 | 'argparse', # Python command-line parsing library 31 | 'euclid3', # 2D and 3D vector, matrix, quaternion and geometry module. 32 | 'six', # Python 2 and 3 compatibility utilities 33 | ] 34 | SCRIPTS = [ 35 | 'scripts/pygcode-norm', 36 | 'scripts/pygcode-crop', 37 | ] 38 | 39 | ################################################################### 40 | 41 | HERE = os.path.abspath(os.path.dirname(__file__)) 42 | 43 | 44 | def read(*parts): 45 | """ 46 | Build an absolute path from *parts* and and return the contents of the 47 | resulting file. Assume UTF-8 encoding. 48 | """ 49 | with codecs.open(os.path.join(HERE, *parts), "rb", "utf-8") as f: 50 | return f.read() 51 | 52 | 53 | META_FILE = read(META_PATH) 54 | 55 | 56 | def find_meta(meta): 57 | """ 58 | Extract __*meta*__ from META_FILE. 59 | """ 60 | meta_match = re.search( 61 | r"^(?P__{meta}__)\s*=\s*['\"](?P[^'\"]*)['\"](\s*#.*)?$".format(meta=meta), 62 | META_FILE, re.M 63 | ) 64 | if meta_match: 65 | return meta_match.group('value') 66 | raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta)) 67 | 68 | 69 | def assert_version_classifier(version_str): 70 | """ 71 | Verify version consistency: 72 | version number must correspond to the correct "Development Status" classifier 73 | :raises: ValueError if error found, but ideally this function does nothing 74 | """ 75 | V = lambda v: LooseVersion(v) 76 | # cast version 77 | version = V(version_str) 78 | 79 | # get "Development Status" classifier 80 | dev_status_list = [x for x in CLASSIFIERS if x.startswith("Development Status ::")] 81 | if len(dev_status_list) != 1: 82 | raise ValueError("must be 1 'Development Status' in CLASSIFIERS") 83 | classifier = dev_status_list.pop() 84 | 85 | version_map = [ 86 | (V('0.1'), "Development Status :: 2 - Pre-Alpha"), 87 | (V('0.2'), "Development Status :: 3 - Alpha"), 88 | (V('0.3'), "Development Status :: 4 - Beta"), 89 | (V('1.0'), "Development Status :: 5 - Production/Stable"), 90 | ] 91 | 92 | for (test_ver, test_classifier) in reversed(sorted(version_map, key=lambda x: x[0])): 93 | if version >= test_ver: 94 | if classifier == test_classifier: 95 | return # all good, now forget any of this ever happened 96 | else: 97 | raise ValueError("for version {ver} classifier should be \n'{good}'\nnot\n'{bad}'".format( 98 | ver=str(version), good=test_classifier, bad=classifier 99 | )) 100 | 101 | 102 | if __name__ == "__main__": 103 | version = find_meta("version") 104 | assert_version_classifier(version) 105 | 106 | setup( 107 | name=NAME, 108 | description=find_meta("description"), 109 | license=find_meta("license"), 110 | url=find_meta("url"), 111 | version=version, 112 | author=find_meta("author"), 113 | author_email=find_meta("email"), 114 | maintainer=find_meta("author"), 115 | maintainer_email=find_meta("email"), 116 | keywords=KEYWORDS, 117 | long_description=read("README.rst"), 118 | packages=PACKAGES, 119 | package_dir={"": "src"}, 120 | zip_safe=False, 121 | classifiers=CLASSIFIERS, 122 | install_requires=INSTALL_REQUIRES, 123 | scripts=SCRIPTS, 124 | ) 125 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Horse2.tap: -------------------------------------------------------------------------------- 1 | ( Horse2 ) 2 | N100G00G21G17G90G40G49G80 3 | N110G91.1 4 | N120T1M06 5 | N130(ENGRAVING - 20 DEG TIP 0.1) 6 | N140G00G43Z6H1 7 | N150S12000M03 8 | N160 9 | N170 10 | N180G94 11 | N190X0Y0F600 12 | N200G00X44.05Y21.75 13 | N210G1Z-0.2 14 | N220G3X50.4Y15.75I39.9J36 15 | N230G3X50.65Y15.85I.1J.15 16 | N240G3X50.75Y16.7I-1.6J.65 17 | N250G2X51.35Y17.6I.8J.1 18 | N260G2X55.2Y18.05I3.05J-10.25 19 | N270G3X60.3Y18.75I.9J12.3 20 | N280G3X65.8Y21.85I-6.35J17.5 21 | N290G3X67.15Y26.55I-2.75J3.35 22 | N300G2X66.15Y31.4I18.55J6.2 23 | N310G3X65.35Y33.45I-3.65J-.25 24 | N320G1X62.6Y36.55 25 | N330G3X54.55Y39.5I-7.15J-6.95 26 | N340G2X45.95Y39.55I-4.15J46.35 27 | N350G2X42.65Y40.5I.85J9.15 28 | N360G3X41.8Y40.15I-.25J-.5 29 | N370G3X41.75Y38.45I3.55J-.9 30 | N380G2X41.65Y31.6I-14.1J-3.15 31 | N390G3X44.05Y21.75I10.3J-2.7 32 | N400G00Z6 33 | N410G00X8.35Y73.5 34 | N420G1Z-0.2 35 | N430G3X6.95Y75.75I-3.3J-.5 36 | N440G3X6Y75.1I-.35J-.5 37 | N450G3X7.65Y73I3.25J.85 38 | N460G3X8.35Y73.5I.2J.4 39 | N470G00Z6 40 | N480G00X10.6Y74.8 41 | N490G1Z-0.2 42 | N500G1X13.45Y68.6 43 | N510G2X14.05Y64.9I-6.9J-3 44 | N520G3X14.8Y64.25I.6J-.05 45 | N530G3X16.9Y65.75I-1.25J4 46 | N540G2X25.45Y71.45I12.4J-9.3 47 | N550G2X42.75Y72.55I11.45J-44.45 48 | N560G3X52.1Y72.6I4.4J34.3 49 | N570G3X69.15Y79.15I-5.85J40.65 50 | N580G2X87Y82.7I13.15J-19.6 51 | N590G2X97.5Y79.05I-8.55J-41.65 52 | N600G2X102Y75.8I-7.8J-15.55 53 | N610G1X101.95Y76.5 54 | N620G1X101.85Y77.15 55 | N630G1X102.05Y76.55 56 | N640G3X102.5Y76.05I.55J.05 57 | N650G1X102.55Y76.1 58 | N660G1X102.55Y76.15 59 | N670G1X102.6Y76.2 60 | N680G2X103.3Y76.25I.35J-.05 61 | N690G3X103.7Y75.65I.9J.2 62 | N700G1X103.75Y75.7 63 | N710G1X103.8Y76.1 64 | N720G2X104.4Y76.3I.35J.05 65 | N730G3X106.95Y76I1.4J.9 66 | N740G2X108.8Y75.25I.75J-.8 67 | N750G3X109.55Y71.8I11J.6 68 | N760G2X111.15Y65.9I-27.2J-10.75 69 | N770G2X110.7Y64.8I-1.15J-.2 70 | N780G3X110.05Y63.5I.95J-1.3 71 | N790G3X110.3Y61.1I13.35J.15 72 | N800G2X110.3Y59.4I-4.7J-.9 73 | N810G1X109.85Y57.55 74 | N820G3X107.8Y44.45I38.4J-12.7 75 | N830G2X106.7Y41.3I-4.9J-.05 76 | N840G2X101.7Y39.35I-4.25J3.45 77 | N850G2X97.25Y42.65I.8J5.75 78 | N860G2X97.7Y45.1I1.95J.9 79 | N870G3X97.9Y45.7I-.5J.5 80 | N880G3X97.4Y47.75I-10.3J-1.35 81 | N890G3X93.85Y57.3I-229.55J-80.15 82 | N900G3X88.85Y59.25I-3.4J-1.35 83 | N910G3X82.15Y51.65I5.75J-11.85 84 | N920G2X78.15Y43.5I-32.5J11.05 85 | N930G2X74.3Y39.95I-9.5J6.4 86 | N940G1X74.2Y38.05 87 | N950G1X73.8Y36.35 88 | N960G3X74.65Y32.7I3.6J-1.1 89 | N970G2X75.3Y25.75I-4.05J-3.85 90 | N980G3X72.55Y20.7I28.95J-19.05 91 | N990G1X71.15Y18 92 | N1000G2X67.55Y14.8I-6.8J4.05 93 | N1010G3X65.3Y13.5I5.4J-12 94 | N1020G2X57.4Y12.1I-5.4J7.6 95 | N1030G3X53.1Y11.75I-1.7J-6.05 96 | N1040G2X55.75Y8.2I-7.55J-8.4 97 | N1050G2X56.35Y4.9I-5.75J-2.7 98 | N1060G3X53.1Y4.7I0J-25.3 99 | N1070G2X51.5Y5.4I-.2J1.65 100 | N1080G3X49.8Y6.9I-3.25J-2.1 101 | N1090G2X49.1Y7.85I.45J1.1 102 | N1100G3X47.35Y11.55I-5.55J-.4 103 | N1110G2X43Y15.9I52.5J56.35 104 | N1120G3X35.25Y22.9I-40.5J-37.05 105 | N1130G2X33.9Y25.2I2J2.7 106 | N1140G2X34.4Y30.55I12.7J1.55 107 | N1150G3X31.6Y40I-8.5J2.65 108 | N1160G3X32.4Y27.15I323.3J13.5 109 | N1170G3X33Y22.6I40.95J3.35 110 | N1180G3X36.3Y16I13.7J2.7 111 | N1190G2X40.75Y10.15I-37.35J-33.25 112 | N1200G3X44.55Y6.05I14J9.2 113 | N1210G2X45.3Y4.85I-1.2J-1.55 114 | N1220G3X46.45Y2.65I4.2J.8 115 | N1230G2X47.1Y1.45I-1.6J-1.6 116 | N1240G2X46.25Y.25I-1J-.2 117 | N1250G2X43.35Y0I-3.05J18.6 118 | N1260G2X39.45Y4.6I-.05J3.95 119 | N1270G3X38.7Y5.95I-1.2J.2 120 | N1280G2X35.95Y8.5I1.85J4.7 121 | N1290G3X27.5Y20.7I-40.8J-19.25 122 | N1300G2X26Y24.2I3.85J3.75 123 | N1310G1X25.6Y29.65 124 | N1320G3X18.25Y42.25I-17.65J-1.9 125 | N1330G2X13.25Y48.25I9.05J12.6 126 | N1340G2X11.3Y56.8I18.55J8.7 127 | N1350G2X12.05Y59.25I4.25J.05 128 | N1360G3X12.05Y59.55I-.2J.15 129 | N1370G1X11.55Y59.85 130 | N1380G2X7.8Y63.05I1J4.9 131 | N1390G2X6.5Y67.55I38.3J13.6 132 | N1400G3X5.45Y68.4I-1.1J-.25 133 | N1410G1X5.15Y68.55 134 | N1420G2X-.35Y76I6.85J10.8 135 | N1430G2X.2Y79.2I3.75J1 136 | N1440G1X.1Y79.15 137 | N1450G3X.4Y77.35I3.9J-.35 138 | N1460G3X4.05Y71.1I24.4J10 139 | N1470G3X4.4Y71.05I.2J.15 140 | N1480G3X4.5Y71.4I-.2J.25 141 | N1490G2X2.75Y79.3I24.85J9.6 142 | N1500G2X3.45Y87.55I28J1.85 143 | N1510G1X4Y83.2 144 | N1520G1X5.25Y76.9 145 | N1530G1X6.45Y77.05 146 | N1540G3X7.5Y77.6I-.05J1.4 147 | N1550G1X8.2Y78.7 148 | N1560G2X10.95Y83.15I32.05J-16.5 149 | N1570G1X10.3Y81.15 150 | N1580G1X10Y79.95 151 | N1590G3X10.6Y74.8I7.85J-1.7 152 | N1600G00X10.6Y74.8 153 | N1610G00Z6 154 | N1620G00X0Y0 155 | N1630M05 156 | N1640M30 157 | N1650% 158 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Horse2.tap: -------------------------------------------------------------------------------- 1 | ( Horse2 ) 2 | N100G00G21G17G90G40G49G80 3 | N110G71G91.1 4 | N120T1M06 5 | N130(ENGRAVING - 20 DEG TIP 0.1) 6 | N140G00G43Z6H1 7 | N150S12000M03 8 | N160 9 | N170 10 | N180G94 11 | N190X0Y0F600 12 | N200G00X44.05Y21.75 13 | N210G1Z-0.2 14 | N220G3X50.4Y15.75I39.9J36 15 | N230G3X50.65Y15.85I.1J.15 16 | N240G3X50.75Y16.7I-1.6J.65 17 | N250G2X51.35Y17.6I.8J.1 18 | N260G2X55.2Y18.05I3.05J-10.25 19 | N270G3X60.3Y18.75I.9J12.3 20 | N280G3X65.8Y21.85I-6.35J17.5 21 | N290G3X67.15Y26.55I-2.75J3.35 22 | N300G2X66.15Y31.4I18.55J6.2 23 | N310G3X65.35Y33.45I-3.65J-.25 24 | N320G1X62.6Y36.55 25 | N330G3X54.55Y39.5I-7.15J-6.95 26 | N340G2X45.95Y39.55I-4.15J46.35 27 | N350G2X42.65Y40.5I.85J9.15 28 | N360G3X41.8Y40.15I-.25J-.5 29 | N370G3X41.75Y38.45I3.55J-.9 30 | N380G2X41.65Y31.6I-14.1J-3.15 31 | N390G3X44.05Y21.75I10.3J-2.7 32 | N400G00Z6 33 | N410G00X8.35Y73.5 34 | N420G1Z-0.2 35 | N430G3X6.95Y75.75I-3.3J-.5 36 | N440G3X6Y75.1I-.35J-.5 37 | N450G3X7.65Y73I3.25J.85 38 | N460G3X8.35Y73.5I.2J.4 39 | N470G00Z6 40 | N480G00X10.6Y74.8 41 | N490G1Z-0.2 42 | N500G1X13.45Y68.6 43 | N510G2X14.05Y64.9I-6.9J-3 44 | N520G3X14.8Y64.25I.6J-.05 45 | N530G3X16.9Y65.75I-1.25J4 46 | N540G2X25.45Y71.45I12.4J-9.3 47 | N550G2X42.75Y72.55I11.45J-44.45 48 | N560G3X52.1Y72.6I4.4J34.3 49 | N570G3X69.15Y79.15I-5.85J40.65 50 | N580G2X87Y82.7I13.15J-19.6 51 | N590G2X97.5Y79.05I-8.55J-41.65 52 | N600G2X102Y75.8I-7.8J-15.55 53 | N610G1X101.95Y76.5 54 | N620G1X101.85Y77.15 55 | N630G1X102.05Y76.55 56 | N640G3X102.5Y76.05I.55J.05 57 | N650G1X102.55Y76.1 58 | N660G1X102.55Y76.15 59 | N670G1X102.6Y76.2 60 | N680G2X103.3Y76.25I.35J-.05 61 | N690G3X103.7Y75.65I.9J.2 62 | N700G1X103.75Y75.7 63 | N710G1X103.8Y76.1 64 | N720G2X104.4Y76.3I.35J.05 65 | N730G3X106.95Y76I1.4J.9 66 | N740G2X108.8Y75.25I.75J-.8 67 | N750G3X109.55Y71.8I11J.6 68 | N760G2X111.15Y65.9I-27.2J-10.75 69 | N770G2X110.7Y64.8I-1.15J-.2 70 | N780G3X110.05Y63.5I.95J-1.3 71 | N790G3X110.3Y61.1I13.35J.15 72 | N800G2X110.3Y59.4I-4.7J-.9 73 | N810G1X109.85Y57.55 74 | N820G3X107.8Y44.45I38.4J-12.7 75 | N830G2X106.7Y41.3I-4.9J-.05 76 | N840G2X101.7Y39.35I-4.25J3.45 77 | N850G2X97.25Y42.65I.8J5.75 78 | N860G2X97.7Y45.1I1.95J.9 79 | N870G3X97.9Y45.7I-.5J.5 80 | N880G3X97.4Y47.75I-10.3J-1.35 81 | N890G3X93.85Y57.3I-229.55J-80.15 82 | N900G3X88.85Y59.25I-3.4J-1.35 83 | N910G3X82.15Y51.65I5.75J-11.85 84 | N920G2X78.15Y43.5I-32.5J11.05 85 | N930G2X74.3Y39.95I-9.5J6.4 86 | N940G1X74.2Y38.05 87 | N950G1X73.8Y36.35 88 | N960G3X74.65Y32.7I3.6J-1.1 89 | N970G2X75.3Y25.75I-4.05J-3.85 90 | N980G3X72.55Y20.7I28.95J-19.05 91 | N990G1X71.15Y18 92 | N1000G2X67.55Y14.8I-6.8J4.05 93 | N1010G3X65.3Y13.5I5.4J-12 94 | N1020G2X57.4Y12.1I-5.4J7.6 95 | N1030G3X53.1Y11.75I-1.7J-6.05 96 | N1040G2X55.75Y8.2I-7.55J-8.4 97 | N1050G2X56.35Y4.9I-5.75J-2.7 98 | N1060G3X53.1Y4.7I0J-25.3 99 | N1070G2X51.5Y5.4I-.2J1.65 100 | N1080G3X49.8Y6.9I-3.25J-2.1 101 | N1090G2X49.1Y7.85I.45J1.1 102 | N1100G3X47.35Y11.55I-5.55J-.4 103 | N1110G2X43Y15.9I52.5J56.35 104 | N1120G3X35.25Y22.9I-40.5J-37.05 105 | N1130G2X33.9Y25.2I2J2.7 106 | N1140G2X34.4Y30.55I12.7J1.55 107 | N1150G3X31.6Y40I-8.5J2.65 108 | N1160G3X32.4Y27.15I323.3J13.5 109 | N1170G3X33Y22.6I40.95J3.35 110 | N1180G3X36.3Y16I13.7J2.7 111 | N1190G2X40.75Y10.15I-37.35J-33.25 112 | N1200G3X44.55Y6.05I14J9.2 113 | N1210G2X45.3Y4.85I-1.2J-1.55 114 | N1220G3X46.45Y2.65I4.2J.8 115 | N1230G2X47.1Y1.45I-1.6J-1.6 116 | N1240G2X46.25Y.25I-1J-.2 117 | N1250G2X43.35Y0I-3.05J18.6 118 | N1260G2X39.45Y4.6I-.05J3.95 119 | N1270G3X38.7Y5.95I-1.2J.2 120 | N1280G2X35.95Y8.5I1.85J4.7 121 | N1290G3X27.5Y20.7I-40.8J-19.25 122 | N1300G2X26Y24.2I3.85J3.75 123 | N1310G1X25.6Y29.65 124 | N1320G3X18.25Y42.25I-17.65J-1.9 125 | N1330G2X13.25Y48.25I9.05J12.6 126 | N1340G2X11.3Y56.8I18.55J8.7 127 | N1350G2X12.05Y59.25I4.25J.05 128 | N1360G3X12.05Y59.55I-.2J.15 129 | N1370G1X11.55Y59.85 130 | N1380G2X7.8Y63.05I1J4.9 131 | N1390G2X6.5Y67.55I38.3J13.6 132 | N1400G3X5.45Y68.4I-1.1J-.25 133 | N1410G1X5.15Y68.55 134 | N1420G2X-.35Y76I6.85J10.8 135 | N1430G2X.2Y79.2I3.75J1 136 | N1440G1X.1Y79.15 137 | N1450G3X.4Y77.35I3.9J-.35 138 | N1460G3X4.05Y71.1I24.4J10 139 | N1470G3X4.4Y71.05I.2J.15 140 | N1480G3X4.5Y71.4I-.2J.25 141 | N1490G2X2.75Y79.3I24.85J9.6 142 | N1500G2X3.45Y87.55I28J1.85 143 | N1510G1X4Y83.2 144 | N1520G1X5.25Y76.9 145 | N1530G1X6.45Y77.05 146 | N1540G3X7.5Y77.6I-.05J1.4 147 | N1550G1X8.2Y78.7 148 | N1560G2X10.95Y83.15I32.05J-16.5 149 | N1570G1X10.3Y81.15 150 | N1580G1X10Y79.95 151 | N1590G3X10.6Y74.8I7.85J-1.7 152 | N1600G00X10.6Y74.8 153 | N1610G00Z6 154 | N1620G00X0Y0 155 | N1630M05 156 | N1640M30 157 | N1650% 158 | -------------------------------------------------------------------------------- /src/pygcode/words.py: -------------------------------------------------------------------------------- 1 | import re 2 | import itertools 3 | import six 4 | 5 | from . import dialects 6 | from .exceptions import GCodeBlockFormatError, GCodeWordStrError 7 | 8 | class Word(object): 9 | def __init__(self, *args, **kwargs): 10 | # Parameters (listed) 11 | args_count = len(args) 12 | if args_count == 1: 13 | # Word('G90') 14 | letter = args[0][0] # first letter 15 | value = args[0][1:] # rest of string 16 | elif args_count == 2: 17 | # Word('G', 90) 18 | (letter, value) = args 19 | else: 20 | raise AssertionError("input arguments either: (letter, value) or (word_str)") 21 | 22 | # Parameters (keyword) 23 | dialect = kwargs.pop('dialect', dialects.get_default()) 24 | 25 | letter = letter.upper() 26 | 27 | self._word_map = getattr(getattr(dialects, dialect), 'WORD_MAP') 28 | self._value_class = self._word_map[letter].cls 29 | self._value_clean = self._word_map[letter].clean_value 30 | 31 | self.letter = letter 32 | self.value = value 33 | 34 | def __str__(self): 35 | return "{letter}{value}".format( 36 | letter=self.letter, 37 | value=self.value_str, 38 | ) 39 | 40 | def __repr__(self): 41 | return "<{class_name}: {string}>".format( 42 | class_name=self.__class__.__name__, 43 | string=str(self), 44 | ) 45 | 46 | # Sorting 47 | def __lt__(self, other): 48 | return (self.letter, self.value) < (other.letter, other.value) 49 | 50 | def __gt__(self, other): 51 | return (self.letter, self.value) > (other.letter, other.value) 52 | 53 | def __le__(self, other): 54 | return (self.letter, self.value) <= (other.letter, other.value) 55 | 56 | def __ge__(self, other): 57 | return (self.letter, self.value) >= (other.letter, other.value) 58 | 59 | # Equality 60 | def __eq__(self, other): 61 | if isinstance(other, six.string_types): 62 | other = str2word(other) 63 | return (self.letter == other.letter) and (self.value == other.value) 64 | 65 | def __ne__(self, other): 66 | return not self.__eq__(other) 67 | 68 | # Hashing 69 | def __hash__(self): 70 | return hash((self.letter, self.value)) 71 | 72 | @property 73 | def value_str(self): 74 | """Clean string representation, for consistent file output""" 75 | return self._value_clean(self.value) 76 | 77 | # Value Properties 78 | @property 79 | def value(self): 80 | return self._value 81 | 82 | @value.setter 83 | def value(self, new_value): 84 | self._value = self._value_class(new_value) 85 | 86 | @property 87 | def description(self): 88 | return "%s: %s" % (self.letter, self._word_map[self.letter].description) 89 | 90 | 91 | def text2words(block_text, dialect=None): 92 | """ 93 | Iterate through block text yielding Word instances 94 | :param block_text: text for given block with comments removed 95 | """ 96 | if dialect is None: 97 | dialect = dialects.get_default() 98 | word_map = getattr(getattr(dialects, dialect), 'WORD_MAP') 99 | 100 | next_word = re.compile(r'^.*?(?P[%s])' % ''.join(word_map.keys()), re.IGNORECASE) 101 | 102 | index = 0 103 | while True: 104 | letter_match = next_word.search(block_text[index:]) 105 | if letter_match: 106 | # Letter 107 | letter = letter_match.group('letter').upper() 108 | index += letter_match.end() # propogate index to start of value 109 | 110 | # Value 111 | value_regex = word_map[letter].value_regex 112 | value_match = value_regex.search(block_text[index:]) 113 | if value_match is None: 114 | raise GCodeWordStrError("word '%s' value invalid" % letter) 115 | value = value_match.group() # matched text 116 | 117 | yield Word(letter, value) 118 | 119 | index += value_match.end() # propogate index to end of value 120 | else: 121 | break 122 | 123 | remainder = block_text[index:] 124 | if remainder and re.search(r'\S', remainder): 125 | raise GCodeWordStrError("block code remaining '%s'" % remainder) 126 | 127 | 128 | def str2word(word_str): 129 | words = list(text2words(word_str)) 130 | if words: 131 | if len(words) > 1: 132 | raise GCodeWordStrError("more than one word given") 133 | return words[0] 134 | return None 135 | 136 | 137 | def words2dict(word_list, limit_word_letters=None): 138 | """ 139 | Represent a list of words as a dict 140 | :param limit_word_letters: iterable containing a white-list of word letters (None allows all) 141 | :return: dict of the form: {: , ... } 142 | """ 143 | # Remember: duplicate word letters cannot be represented as a dict 144 | return dict( 145 | (w.letter, w.value) for w in word_list 146 | if (limit_word_letters is None) or (w.letter in limit_word_letters) 147 | ) 148 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Tweakie's CNC Cube.nc: -------------------------------------------------------------------------------- 1 | (Tweakie's CNC Cube) 2 | (Tool 2mm dia.) 3 | N1 T1 M3 M6 G21 F80 4 | N2 G0 Z2 5 | N3 G0 X12.751 Y23.165 6 | N4 G1 Z-2 7 | N5 G1 X14.122 Y21.819 8 | N6 G1 X14.122 Y21.819 9 | N7 G1 X13.411 Y21.514 10 | N8 G1 X12.751 Y21.133 11 | N9 G1 X12.167 Y20.676 12 | N10 G1 X11.963 Y20.472 13 | N11 G1 X11.963 Y20.472 14 | N12 G1 X11.125 Y19.406 15 | N13 G1 X10.617 Y18.237 16 | N14 G1 X10.389 Y16.916 17 | N15 G1 X10.389 Y16.459 18 | N16 G1 X10.592 Y15.088 19 | N17 G1 X11.049 Y13.868 20 | N18 G1 X11.836 Y12.751 21 | N19 G1 X12.141 Y12.421 22 | N20 G1 X12.141 Y12.421 23 | N21 G1 X13.233 Y11.557 24 | N22 G1 X14.427 Y10.973 25 | N23 G1 X15.723 Y10.693 26 | N24 G1 X16.205 Y10.643 27 | N25 G1 X17.551 Y10.744 28 | N26 G1 X18.771 Y11.176 29 | N27 G1 X19.888 Y11.938 30 | N28 G1 X20.244 Y12.243 31 | N29 G1 X20.244 Y12.243 32 | N30 G1 X20.752 Y12.852 33 | N31 G1 X21.158 Y13.462 34 | N32 G1 X21.438 Y14.173 35 | N33 G1 X21.488 Y14.402 36 | N34 G1 X22.809 Y13.106 37 | N35 G1 X22.809 Y13.106 38 | N36 G1 X22.504 Y12.446 39 | N37 G1 X22.098 Y11.811 40 | N38 G1 X21.615 Y11.227 41 | N39 G1 X21.438 Y11.024 42 | N40 G1 X21.438 Y11.024 43 | N41 G1 X20.041 Y9.906 44 | N42 G1 X18.466 Y9.195 45 | N43 G1 X16.789 Y8.915 46 | N44 G1 X16.18 Y8.915 47 | N45 G1 X14.427 Y9.195 48 | N46 G1 X12.802 Y9.804 49 | N47 G1 X11.328 Y10.846 50 | N48 G1 X10.897 Y11.278 51 | N49 G1 X10.897 Y11.278 52 | N50 G1 X10.16 Y12.141 53 | N51 G1 X9.576 Y13.056 54 | N52 G1 X9.119 Y14.122 55 | N53 G1 X8.992 Y14.503 56 | N54 G1 X8.788 Y15.646 57 | N55 G1 X8.687 Y16.789 58 | N56 G1 X8.788 Y17.907 59 | N57 G1 X8.89 Y18.288 60 | N58 G1 X9.22 Y19.38 61 | N59 G1 X9.779 Y20.396 62 | N60 G1 X10.516 Y21.387 63 | N61 G1 X10.82 Y21.692 64 | N62 G1 X11.024 Y21.869 65 | N63 G1 X11.024 Y21.869 66 | N64 G1 X11.506 Y22.327 67 | N65 G1 X12.04 Y22.733 68 | N66 G1 X12.573 Y23.063 69 | N67 G1 X12.751 Y23.165 70 | N68 G1 X12.751 Y23.165 71 | N69 G0 Z2 72 | N70 G0 X29.591 Y19.558 73 | N71 G1 Z-2 74 | N72 G1 X28.27 Y18.237 75 | N73 G1 X18.034 Y28.448 76 | N74 G1 X30.099 Y27.864 77 | N75 G1 X30.099 Y27.864 78 | N76 G1 X30.226 Y27.889 79 | N77 G1 X30.328 Y27.915 80 | N78 G1 X30.404 Y27.991 81 | N79 G1 X30.429 Y27.991 82 | N80 G1 X30.429 Y27.991 83 | N81 G1 X30.48 Y28.067 84 | N82 G1 X30.48 Y28.118 85 | N83 G1 X30.48 Y28.194 86 | N84 G1 X30.48 Y28.219 87 | N85 G1 X30.48 Y28.219 88 | N86 G1 X30.353 Y28.397 89 | N87 G1 X30.175 Y28.626 90 | N88 G1 X29.896 Y28.931 91 | N89 G1 X29.794 Y29.032 92 | N90 G1 X24.155 Y34.671 93 | N91 G1 X25.476 Y35.992 94 | N92 G1 X35.814 Y25.654 95 | N93 G1 X24.333 Y26.391 96 | N94 G1 X24.333 Y26.391 97 | N95 G1 X23.927 Y26.416 98 | N96 G1 X23.647 Y26.391 99 | N97 G1 X23.47 Y26.34 100 | N98 G1 X23.419 Y26.314 101 | N99 G1 X23.419 Y26.314 102 | N100 G1 X23.317 Y26.111 103 | N101 G1 X23.368 Y25.857 104 | N102 G1 X23.571 Y25.578 105 | N103 G1 X23.647 Y25.476 106 | N104 G1 X29.591 Y19.558 107 | N105 G0 Z2 108 | N106 G0 X37.77 Y48.184 109 | N107 G1 Z-2 110 | N108 G1 X39.141 Y46.838 111 | N109 G1 X39.141 Y46.838 112 | N110 G1 X38.43 Y46.533 113 | N111 G1 X37.77 Y46.152 114 | N112 G1 X37.186 Y45.669 115 | N113 G1 X36.982 Y45.491 116 | N114 G1 X36.982 Y45.491 117 | N115 G1 X36.144 Y44.425 118 | N116 G1 X35.611 Y43.231 119 | N117 G1 X35.408 Y41.935 120 | N118 G1 X35.408 Y41.478 121 | N119 G1 X35.611 Y40.107 122 | N120 G1 X36.068 Y38.887 123 | N121 G1 X36.83 Y37.77 124 | N122 G1 X37.16 Y37.44 125 | N123 G1 X37.16 Y37.44 126 | N124 G1 X38.252 Y36.576 127 | N125 G1 X39.446 Y35.992 128 | N126 G1 X40.742 Y35.712 129 | N127 G1 X41.199 Y35.662 130 | N128 G1 X42.57 Y35.763 131 | N129 G1 X43.79 Y36.195 132 | N130 G1 X44.907 Y36.932 133 | N131 G1 X45.263 Y37.262 134 | N132 G1 X45.263 Y37.262 135 | N133 G1 X45.771 Y37.846 136 | N134 G1 X46.177 Y38.481 137 | N135 G1 X46.457 Y39.167 138 | N136 G1 X46.507 Y39.421 139 | N137 G1 X47.803 Y38.125 140 | N138 G1 X47.803 Y38.125 141 | N139 G1 X47.498 Y37.44 142 | N140 G1 X47.092 Y36.83 143 | N141 G1 X46.634 Y36.22 144 | N142 G1 X46.457 Y36.043 145 | N143 G1 X46.457 Y36.043 146 | N144 G1 X45.06 Y34.925 147 | N145 G1 X43.485 Y34.214 148 | N146 G1 X41.808 Y33.934 149 | N147 G1 X41.199 Y33.934 150 | N148 G1 X39.446 Y34.188 151 | N149 G1 X37.821 Y34.823 152 | N150 G1 X36.347 Y35.865 153 | N151 G1 X35.916 Y36.297 154 | N152 G1 X35.916 Y36.297 155 | N153 G1 X35.179 Y37.135 156 | N154 G1 X34.595 Y38.075 157 | N155 G1 X34.138 Y39.141 158 | N156 G1 X34.011 Y39.522 159 | N157 G1 X33.782 Y40.665 160 | N158 G1 X33.706 Y41.808 161 | N159 G1 X33.807 Y42.926 162 | N160 G1 X33.884 Y43.307 163 | N161 G1 X34.239 Y44.399 164 | N162 G1 X34.798 Y45.415 165 | N163 G1 X35.535 Y46.38 166 | N164 G1 X35.814 Y46.685 167 | N165 G1 X36.017 Y46.888 168 | N166 G1 X36.017 Y46.888 169 | N167 G1 X36.525 Y47.346 170 | N168 G1 X37.033 Y47.752 171 | N169 G1 X37.592 Y48.082 172 | N170 G1 X37.77 Y48.184 173 | N171 G1 X37.77 Y48.184 174 | N172 G0 Z2 175 | N173 G0 X2.54 Y54.864 176 | N174 G1 Z-2 177 | N175 G1 X28.956 Y54.864 178 | N176 G1 X28.956 Y52.324 179 | N177 G1 X28.956 Y53.34 180 | N178 G1 X54.864 Y53.34 181 | N179 G1 X54.864 Y26.924 182 | N180 G1 X52.324 Y26.924 183 | N181 G1 X53.34 Y26.924 184 | N182 G1 X53.34 Y1.016 185 | N183 G1 X26.924 Y1.016 186 | N184 G1 X26.924 Y3.556 187 | N185 G1 X26.924 Y2.54 188 | N186 G1 X1.016 Y2.54 189 | N187 G1 X1.016 Y28.956 190 | N188 G1 X3.556 Y28.956 191 | N189 G1 X2.54 Y28.956 192 | N190 G1 X2.54 Y54.864 193 | N191 G0 Z2 194 | N192 G0 X0 Y0 195 | N193 M30 196 | N194 % 197 | -------------------------------------------------------------------------------- /deployment/README.md: -------------------------------------------------------------------------------- 1 | # Notes on deployment 2 | 3 | How I deploy this package. 4 | 5 | For anyone reading, this readme and all files in this folder are mainly just 6 | notes for myself; they have little to do with pygcode itself. 7 | However, if you're interested in deploying your own PyPi package, then hopefully 8 | this can help. 9 | 10 | Method based on the articles: 11 | 12 | * http://peterdowns.com/posts/first-time-with-pypi.html and 13 | * https://hynek.me/articles/sharing-your-labor-of-love-pypi-quick-and-dirty/ 14 | 15 | 16 | Deployment also heavily uses the `./deploy.sh` script in this folder. 17 | At this time, running `./deploy.sh --help` displays: 18 | 19 | Usage: ./deploy.sh {build|test|and so on ...} 20 | 21 | This script is to maintain a consistent method of deployment and testing. 22 | 23 | Deployment target: pygcode 0.2.0 24 | 25 | Arguments: 26 | Setup: 27 | setup: Installs packages & sets up environment (requires sudo) 28 | 29 | Compiling: 30 | build: Execute setup to build packages (populates ../dist/) 31 | creates both 'sdist' and 'wheel' distrobutions. 32 | 33 | Virtual Environments: 34 | lsenv List created virtual environments for this lib 35 | rmenv py# Remove virtual environment 36 | remkenv py# Remove, then create re-create virtual environment 37 | envprereq py# install environment prerequisites (official PyPi) 38 | 39 | Deploy: 40 | deploy test Upload to PyPi test server 41 | deploy prod Upload to PyPi (official) 42 | 43 | Install: 44 | install sdist py# Install from local sdist 45 | install wheel py# Install from local wheel 46 | install pypitest py# Install from PyPi test server 47 | install pypi py# Install from PyPi (official) 48 | 49 | Testing: 50 | test dev py# Run tests on local dev in a virtual env 51 | test installed py# Run tests on installed library in virtual env 52 | 53 | Help: 54 | -h | --help display this help message 55 | 56 | py#: when referenced above means 57 | 'py2' for Python 2.7.12 58 | 'py3' for Python 3.5.2 59 | 60 | # PyPi deployment 61 | 62 | ## Install Required Tools 63 | 64 | `./deploy.sh setup` 65 | 66 | ## PyPi rc 67 | 68 | `cat ~/.pypirc` 69 | 70 | [distutils] 71 | index-servers = 72 | prod 73 | test 74 | 75 | [prod] 76 | repository = https://upload.pypi.org/legacy/ 77 | username=FraggaMuffin 78 | password=secret 79 | 80 | [test] 81 | repository=https://test.pypi.org/legacy/ 82 | username=FraggaMuffin 83 | password=secret 84 | 85 | `chmod 600 ~/.pypirc` 86 | 87 | 88 | ## Build and Test `sdist` and `wheel` 89 | 90 | **Build** 91 | 92 | ./deploy.sh build 93 | 94 | **Test `sdist`** 95 | 96 | # Python 2.x 97 | ./deploy.sh remkenv py2 98 | ./deploy.sh envprereq py2 99 | ./deploy.sh install sdist py2 100 | ./deploy.sh test installed py2 101 | 102 | # Python 3.x 103 | ./deploy.sh remkenv py3 104 | ./deploy.sh envprereq py3 105 | ./deploy.sh install sdist py3 106 | ./deploy.sh test installed py3 107 | 108 | **Test `wheel`** 109 | 110 | # Python 2.x 111 | ./deploy.sh remkenv py2 112 | ./deploy.sh install wheel py2 113 | ./deploy.sh test installed py2 114 | 115 | # Python 3.x 116 | ./deploy.sh remkenv py3 117 | ./deploy.sh install wheel py3 118 | ./deploy.sh test installed py3 119 | 120 | 121 | 122 | ## Upload to PyPi Test server 123 | 124 | 125 | ./deploy.sh deploy test 126 | 127 | 128 | **Test** 129 | 130 | # Python 2.x 131 | ./deploy.sh remkenv py2 132 | ./deploy.sh envprereq py2 133 | ./deploy.sh install pypitest py2 134 | ./deploy.sh test installed py2 135 | 136 | # Python 3.x 137 | ./deploy.sh remkenv py3 138 | ./deploy.sh envprereq py3 139 | ./deploy.sh install pypitest py3 140 | ./deploy.sh test installed py3 141 | 142 | have a look at: 143 | https://testpypi.python.org/pypi/pygcode 144 | to make sure it's sane 145 | 146 | 147 | ## Upload to PyPy server 148 | 149 | all good!? sweet :+1: time to upload to 'production' 150 | 151 | ./deploy.sh deploy prod 152 | 153 | **Test** 154 | 155 | # Python 2.x 156 | ./deploy.sh remkenv py2 157 | ./deploy.sh install pypi py2 158 | ./deploy.sh test installed py2 159 | 160 | # Python 3.x 161 | ./deploy.sh remkenv py3 162 | ./deploy.sh install pypi py3 163 | ./deploy.sh test installed py3 164 | 165 | have a look at: 166 | https://pypi.python.org/pypi/pygcode 167 | to make sure it's sane 168 | 169 | 170 | # Deployment in Git 171 | 172 | create pull request `develop` -> `master` 173 | 174 | ## Update change log 175 | 176 | update [change log](../dist/README.md) 177 | 178 | ## Push Release to Git (aka `tag`) 179 | 180 | merge `develop` branch to `master`, then... 181 | 182 | Switch local to `master` and tag with relevant version 183 | 184 | git checkout master 185 | git pull 186 | git tag ${version} -m "" 187 | git push --tags origin master 188 | 189 | have a look at the 190 | [releases page](https://github.com/fragmuffin/pygcode/releases) 191 | and it should be there. 192 | 193 | edit release and copy relevant content from [change log](../dist/README.md) 194 | 195 | tadaaaaaa!... go to sleep; it's probably late 196 | 197 | # Cleanup 198 | 199 | ./deploy.sh rmenv py2 200 | ./deploy.sh rmenv py3 201 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Smiley002.nc: -------------------------------------------------------------------------------- 1 | G20 G40 G90 2 | G91.1 3 | G0 Z0.250 4 | G0 X0.000 Y-0.366 Z0.250 5 | G1 X0.000 Y-0.366 Z-0.010 F50 6 | G2 X-0.056 Y-0.363 Z-0.010 I-0.006 J0.435 F50 7 | G2 X-0.088 Y-0.347 Z-0.010 I0.013 J0.066 8 | G2 X-0.082 Y-0.311 Z-0.010 I0.015 J0.016 9 | G2 X-0.041 Y-0.308 Z-0.010 I0.025 J-0.054 10 | G2 X-0.006 Y-0.326 Z-0.010 I-0.025 J-0.087 11 | G1 X0.000 Y-0.332 Z-0.010 12 | G1 X0.006 Y-0.326 Z-0.010 13 | G2 X0.041 Y-0.308 Z-0.010 I0.060 J-0.069 14 | G2 X0.082 Y-0.311 Z-0.010 I0.016 J-0.057 15 | G2 X0.088 Y-0.347 Z-0.010 I-0.009 J-0.020 16 | G2 X0.056 Y-0.363 Z-0.010 I-0.045 J0.049 17 | G2 X0.000 Y-0.366 Z-0.010 I-0.050 J0.432 18 | G0 X0.000 Y-0.366 Z0.250 19 | G0 X-0.251 Y-0.117 Z0.250 20 | G1 X-0.251 Y-0.117 Z-0.010 F50 21 | G1 X-0.197 Y-0.278 Z-0.010 F50 22 | G3 X-0.145 Y-0.349 Z-0.010 I0.139 J0.047 23 | G3 X-0.114 Y-0.364 Z-0.010 I0.052 J0.071 24 | G3 X0.000 Y-0.377 Z-0.010 I0.108 J0.433 25 | G3 X0.114 Y-0.364 Z-0.010 I0.006 J0.446 26 | G3 X0.145 Y-0.349 Z-0.010 I-0.021 J0.085 27 | G3 X0.197 Y-0.278 Z-0.010 I-0.088 J0.118 28 | G1 X0.251 Y-0.117 Z-0.010 29 | G0 X0.251 Y-0.117 Z0.250 30 | G0 X-0.228 Y-0.117 Z0.250 31 | G1 X-0.228 Y-0.117 Z-0.010 F50 32 | G3 X0.228 Y-0.117 Z-0.010 I0.228 J0.270 F50 33 | G0 X0.228 Y-0.117 Z0.250 34 | G0 X0.359 Y-0.037 Z0.250 35 | G1 X0.359 Y-0.037 Z-0.010 F50 36 | G3 X0.269 Y-0.031 Z-0.010 I-0.051 J-0.089 F50 37 | G0 X0.269 Y-0.031 Z0.250 38 | G0 X-0.314 Y-0.023 Z0.250 39 | G1 X-0.314 Y-0.023 Z-0.010 F50 40 | G3 X-0.258 Y-0.117 Z-0.010 I0.055 J-0.031 F50 41 | G1 X0.258 Y-0.117 Z-0.010 42 | G3 X0.314 Y-0.023 Z-0.010 I0.000 J0.063 43 | G0 X0.314 Y-0.023 Z0.250 44 | G0 X-0.269 Y-0.031 Z0.250 45 | G1 X-0.269 Y-0.031 Z-0.010 F50 46 | G3 X-0.359 Y-0.037 Z-0.010 I-0.040 J-0.095 F50 47 | G0 X-0.359 Y-0.037 Z0.250 48 | G0 X-0.225 Y0.286 Z0.250 49 | G1 X-0.225 Y0.286 Z-0.010 F50 50 | G3 X-0.252 Y0.217 Z-0.010 I0.249 J-0.134 F50 51 | G3 X-0.259 Y0.143 Z-0.010 I0.275 J-0.065 52 | G3 X-0.248 Y0.083 Z-0.010 I0.199 J0.006 53 | G3 X-0.217 Y0.033 Z-0.010 I0.130 J0.046 54 | G3 X-0.179 Y0.011 Z-0.010 I0.057 J0.055 55 | G3 X-0.134 Y0.013 Z-0.010 I0.018 J0.077 56 | G3 X-0.084 Y0.044 Z-0.010 I-0.046 J0.131 57 | G3 X-0.047 Y0.093 Z-0.010 I-0.138 J0.144 58 | G3 X-0.021 Y0.162 Z-0.010 I-0.249 J0.134 59 | G3 X-0.013 Y0.235 Z-0.010 I-0.275 J0.065 60 | G3 X-0.024 Y0.296 Z-0.010 I-0.199 J-0.006 61 | G3 X-0.055 Y0.346 Z-0.010 I-0.130 J-0.046 62 | G3 X-0.094 Y0.368 Z-0.010 I-0.057 J-0.055 63 | G3 X-0.138 Y0.366 Z-0.010 I-0.018 J-0.077 64 | G3 X-0.188 Y0.335 Z-0.010 I0.046 J-0.131 65 | G3 X-0.225 Y0.286 Z-0.010 I0.138 J-0.144 66 | G0 X-0.225 Y0.286 Z0.250 67 | G0 X-0.092 Y0.154 Z0.250 68 | G1 X-0.092 Y0.154 Z-0.010 F50 69 | G3 X-0.094 Y0.175 Z-0.010 I-0.067 J0.004 F50 70 | G3 X-0.102 Y0.192 Z-0.010 I-0.044 J-0.011 71 | G3 X-0.114 Y0.200 Z-0.010 I-0.018 J-0.015 72 | G3 X-0.127 Y0.200 Z-0.010 I-0.007 J-0.023 73 | G3 X-0.144 Y0.190 Z-0.010 I0.013 J-0.043 74 | G3 X-0.157 Y0.175 Z-0.010 I0.044 J-0.051 75 | G3 X-0.167 Y0.153 Z-0.010 I0.084 J-0.052 76 | G3 X-0.172 Y0.129 Z-0.010 I0.094 J-0.029 77 | G3 X-0.170 Y0.108 Z-0.010 I0.067 J-0.004 78 | G3 X-0.161 Y0.091 Z-0.010 I0.044 J0.011 79 | G3 X-0.150 Y0.084 Z-0.010 I0.018 J0.015 80 | G3 X-0.136 Y0.083 Z-0.010 I0.007 J0.023 81 | G3 X-0.119 Y0.093 Z-0.010 I-0.013 J0.043 82 | G3 X-0.106 Y0.108 Z-0.010 I-0.044 J0.051 83 | G3 X-0.096 Y0.130 Z-0.010 I-0.084 J0.052 84 | G3 X-0.092 Y0.154 Z-0.010 I-0.094 J0.029 85 | G0 X-0.092 Y0.154 Z0.250 86 | G0 X-0.089 Y0.413 Z0.250 87 | G1 X-0.089 Y0.413 Z-0.010 F50 88 | G3 X-0.168 Y0.414 Z-0.010 I-0.040 J-0.055 F50 89 | G3 X-0.241 Y0.335 Z-0.010 I0.141 J-0.204 90 | G2 X-0.193 Y0.418 Z-0.010 I0.126 J-0.018 91 | G2 X-0.089 Y0.413 Z-0.010 I0.049 J-0.064 92 | G0 X-0.089 Y0.413 Z0.250 93 | G0 X0.089 Y0.413 Z0.250 94 | G1 X0.089 Y0.413 Z-0.010 F50 95 | G2 X0.168 Y0.414 Z-0.010 I0.040 J-0.055 F50 96 | G2 X0.241 Y0.335 Z-0.010 I-0.141 J-0.204 97 | G3 X0.193 Y0.418 Z-0.010 I-0.126 J-0.018 98 | G3 X0.089 Y0.413 Z-0.010 I-0.049 J-0.064 99 | G0 X0.089 Y0.413 Z0.250 100 | G0 X0.225 Y0.286 Z0.250 101 | G1 X0.225 Y0.286 Z-0.010 F50 102 | G2 X0.252 Y0.217 Z-0.010 I-0.249 J-0.134 F50 103 | G2 X0.259 Y0.143 Z-0.010 I-0.275 J-0.065 104 | G2 X0.248 Y0.083 Z-0.010 I-0.199 J0.006 105 | G2 X0.217 Y0.033 Z-0.010 I-0.130 J0.046 106 | G2 X0.179 Y0.011 Z-0.010 I-0.057 J0.055 107 | G2 X0.134 Y0.013 Z-0.010 I-0.018 J0.077 108 | G2 X0.084 Y0.044 Z-0.010 I0.046 J0.131 109 | G2 X0.047 Y0.093 Z-0.010 I0.138 J0.144 110 | G2 X0.021 Y0.162 Z-0.010 I0.249 J0.134 111 | G2 X0.013 Y0.235 Z-0.010 I0.275 J0.065 112 | G2 X0.024 Y0.296 Z-0.010 I0.199 J-0.006 113 | G2 X0.055 Y0.346 Z-0.010 I0.130 J-0.046 114 | G2 X0.094 Y0.368 Z-0.010 I0.057 J-0.055 115 | G2 X0.138 Y0.366 Z-0.010 I0.018 J-0.077 116 | G2 X0.188 Y0.335 Z-0.010 I-0.046 J-0.131 117 | G2 X0.225 Y0.286 Z-0.010 I-0.138 J-0.144 118 | G0 X0.225 Y0.286 Z0.250 119 | G0 X0.092 Y0.154 Z0.250 120 | G1 X0.092 Y0.154 Z-0.010 F50 121 | G2 X0.094 Y0.175 Z-0.010 I0.067 J0.004 F50 122 | G2 X0.102 Y0.192 Z-0.010 I0.044 J-0.011 123 | G2 X0.114 Y0.200 Z-0.010 I0.018 J-0.015 124 | G2 X0.127 Y0.200 Z-0.010 I0.007 J-0.023 125 | G2 X0.144 Y0.190 Z-0.010 I-0.013 J-0.043 126 | G2 X0.157 Y0.175 Z-0.010 I-0.044 J-0.051 127 | G2 X0.167 Y0.153 Z-0.010 I-0.084 J-0.052 128 | G2 X0.172 Y0.129 Z-0.010 I-0.094 J-0.029 129 | G2 X0.170 Y0.108 Z-0.010 I-0.067 J-0.004 130 | G2 X0.161 Y0.091 Z-0.010 I-0.044 J0.011 131 | G2 X0.150 Y0.084 Z-0.010 I-0.018 J0.015 132 | G2 X0.136 Y0.083 Z-0.010 I-0.007 J0.023 133 | G2 X0.119 Y0.093 Z-0.010 I0.013 J0.043 134 | G2 X0.106 Y0.108 Z-0.010 I0.044 J0.051 135 | G2 X0.096 Y0.130 Z-0.010 I0.084 J0.052 136 | G2 X0.092 Y0.154 Z-0.010 I0.094 J0.029 137 | G0 X0.092 Y0.154 Z0.250 138 | G0 X0.000 Y0.500 Z0.250 139 | G1 X0.000 Y0.500 Z-0.010 F50 140 | G2 X0.000 Y0.500 Z-0.010 I0.000 J-0.500 F50 141 | G0 X0.000 Y0.500 Z0.250 142 | M30 143 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Snow White.tap: -------------------------------------------------------------------------------- 1 | ( Snow White ) 2 | N100G20G17G90G40G49G80 3 | N110G91.1 4 | N120T1M06 5 | N140G00G43Z0.5H1 6 | N150S12000M03 7 | N180G94 8 | N190 (Engrave or Vinyl Cut) 9 | N200G00X2.445Y1.5325Z0.2 10 | N210G1Z-.01F10.0 11 | N220G2X2.57Y1.6325I1.52J-1.7725F20.0 12 | N230G3X2.6975Y1.7325I-1.925J2.5875 13 | N240G3X2.835Y1.9175I-.305J.37 14 | N250G3X2.8975Y2.22I-.78J.3225 15 | N260G3X2.755Y2.7925I-1.1075J.0275 16 | N270G3X2.48Y3.235I-5.635J-3.1975 17 | N280G3X2.26Y2.4375I1.0375J-.7125 18 | N290G3X2.445Y1.5325I3.4725J.2325 19 | N300G00Z0.2 20 | N310G00X2.9225Y1.46 21 | N320G1Z-.01F10.0 22 | N330G3X3.3075Y1.635I-.075J.67F20.0 23 | N340G3X3.4675Y1.965I-.345J.37 24 | N350G3X3.425Y2.3I-.8125J.065 25 | N360G3X3.26Y2.5625I-.6625J-.2325 26 | N370G2X3.3425Y2.1325I-7.26J-1.65 27 | N380G2X3.35Y2.0075I-.61J-.1025 28 | N390G2X3.2575Y1.7525I-.4425J.0175 29 | N400G2X2.9225Y1.46I-.9325J.73 30 | N410G00Z0.2 31 | N470G00X3.42Y6.7575 32 | N480G1Z-.01F10.0 33 | N490G3X3.8575Y6.9375I-.17J1.0425F20.0 34 | N500G3X4.1425Y7.265I-.515J.73 35 | N510G2X3.8375Y7.0275I-2.375J2.705 36 | N520G2X3.42Y6.7575I-3.39J4.815 37 | N530G00Z0.2 38 | N540G00X3.18Y5.915 39 | N550G1Z-.01F10.0 40 | N560G1X3.28Y5.79F20.0 41 | N570G2X3.375Y5.665I-2.155J-1.76 42 | N580G2X3.455Y5.385I-.3375J-.245 43 | N590G2X3.325Y5.04I-.6675J.055 44 | N600G2X2.865Y4.875I-.355J.2625 45 | N610G2X2.3925Y5.0125I1.3875J5.6575 46 | N620G2X2.7925Y4.81I-4.5375J-9.455 47 | N630G2X2.9575Y4.71I-.94J-1.76 48 | N640G2X3.485Y4.0025I-.725J-1.0875 49 | N650G2X3.6925Y2.89I-5.035J-1.525 50 | N660G3X3.265Y3.3025I-20.3225J-20.755 51 | N670G3X3.1825Y3.37I-.6325J-.675 52 | N680G3X2.465Y3.7675I-1.825J-2.4525 53 | N690G3X2.0975Y3.8375I-.37J-.9525 54 | N700G2X3.0425Y3.3475I-3.105J-7.16 55 | N710G2X3.4725Y3.0025I-1.0825J-1.78 56 | N720G2X4.0375Y2.3525I-7.0775J-6.7275 57 | N730G2X4.31Y1.95I-2.4375J-1.9375 58 | N740G2X4.4675Y1.0975I-1.155J-.655 59 | N750G2X4.315Y.615I-1.6075J.24 60 | N760G2X4.0125Y.0975I-3.8775J1.925 61 | N770G2X3.93Y.05I-.09J.0625 62 | N780G3X3.7125Y.53I-.765J-.0575 63 | N790G3X2.925Y1.0775I-1.8125J-1.775 64 | N800G3X2.435Y1.17I-.4425J-1 65 | N810G2X1.505Y1.1875I-.3175J7.0825 66 | N820G2X1.3825Y1.24I.02J.2225 67 | N830G2X1.2425Y1.405I.38J.4575 68 | N840G1X1.2825Y1.435 69 | N850G1X1.3225Y1.46 70 | N860G2X1.845Y1.5475I.375J-.635 71 | N870G3X2.5275Y1.4225I3.695J18.2125 72 | N880G1X2.2225Y1.515 73 | N890G2X2.0475Y1.73I.07J.235 74 | N900G3X1.9475Y2.7025I-22.19J-1.765 75 | N910G3X1.79Y3.1625I-1.1675J-.145 76 | N920G1X1.6725Y2.85 77 | N930G3X1.56Y2.5375I5.2225J-2.0525 78 | N940G3X1.6525Y1.98I.58J-.19 79 | N950G3X1.9925Y1.585I3.48J2.6375 80 | N960G1X1.85Y1.605 81 | N970G1X1.6725Y1.62 82 | N980G2X1.46Y1.7225I.02J.31 83 | N990G2X1.1875Y2.165I1.12J.9975 84 | N1000G2X1.1675Y2.4225I.4J.16 85 | N1010G2X1.2925Y2.8525I4.3875J-1.03 86 | N1020G1X1.2525Y2.8325 87 | N1030G1X1.235Y2.8225 88 | N1040G3X.9575Y2.5725I1.245J-1.6725 89 | N1050G3X.8575Y2.415I.3725J-.3475 90 | N1060G3X.94Y1.98I.3775J-.1525 91 | N1070G3X1.435Y1.6025I1.51J1.4575 92 | N1080G3X1.3325Y1.5225I.375J-.59 93 | N1090G2X1.2075Y1.485I-.1025J.11 94 | N1100G2X.8575Y1.66I.095J.63 95 | N1110G2X.5575Y2.2375I.73J.7425 96 | N1120G2X.6875Y2.74I.6425J.1025 97 | N1130G1X1.06Y3.2175 98 | N1140G3X1.1725Y3.44I-.505J.3925 99 | N1150G3X1.1575Y3.5725I-.1775J.05 100 | N1160G3X.935Y3.7675I-.36J-.185 101 | N1170G2X.5025Y4.1075I.2975J.8275 102 | N1180G2X.15Y4.7175I4.4125J2.9475 103 | N1190G2X.055Y5.02I.97J.475 104 | N1200G2X.07Y5.735I1.9375J.315 105 | N1210G2X.465Y6.215I.65J-.135 106 | N1220G3Y5.9825I.49J-.1175 107 | N1230G3X.5675Y5.7725I.505J.115 108 | N1240G3X.6625Y5.685I.3825J.3075 109 | N1250G3X1.05Y5.425I3.6875J5.11 110 | N1260G2X.5925Y5.91I2.8075J3.1 111 | N1270G2X.515Y6.0925I.2875J.23 112 | N1280G2X.6175Y6.3275I.2475J.0325 113 | N1290G2X.8Y6.4325I.6025J-.825 114 | N1300G3X.8575Y6.5125I-.04J.09 115 | N1310G1X.9075Y6.89 116 | N1320G2X1.1475Y7.5225I1.5125J-.215 117 | N1330G2X1.7275Y8.1225I1.9475J-1.295 118 | N1340G2X2.1125Y8.2625I.4325J-.6 119 | N1350G2X2.905Y8.1325I.12J-1.765 120 | N1360G3X2.57Y7.6775I2.885J-2.4825 121 | N1370G3X2.42Y7.2525I.8525J-.5375 122 | N1380G2X2.7075Y7.7925I4.635J-2.13 123 | N1390G2X2.9325Y8.09I1.6275J-.9975 124 | N1400G3X3.0325Y8.0025I1.355J1.4325 125 | N1410G3X3.0725Y7.975I.1725J.21 126 | N1420G2X2.885Y8.3875I.6875J.5625 127 | N1430G2X2.9575Y8.645I.3025J.0525 128 | N1440G2X3.22Y8.6125I.12J-.1075 129 | N1450G3X3.3825Y8.3275I4.395J2.325 130 | N1460G1X3.4675Y8.315 131 | N1470G1X3.51Y8.3075 132 | N1480G2X3.5525Y8.2275I-.02J-.0625 133 | N1490G2X3.445Y8.07I-.2775J.0725 134 | N1500G3X3Y7.675I1.875J-2.57 135 | N1510G3X2.6925Y7.2675I1.73J-1.625 136 | N1520G1X2.865Y7.4625 137 | N1530G2X3.04Y7.6575I3.15J-2.6975 138 | N1540G2X3.4875Y8.06I3.24J-3.1475 139 | N1550G2X4.115Y8.0675I.3175J-.405 140 | N1560G2X4.2575Y7.5625I-.2575J-.345 141 | N1570G3X4.26Y7.28I.3525J-.1375 142 | N1580G2X4.3025Y6.4175I-1.195J-.49 143 | N1590G3X4.2725Y6.2025I.67J-.2025 144 | N1600G3X4.355Y6.235I-.0975J.37 145 | N1610G3X4.4975Y6.3225I-.6425J1.2 146 | N1620G2X4.4125Y6.2025I-.325J.1375 147 | N1630G2X4.2875Y6.1275I-.24J.2575 148 | N1640G3X4.23Y5.9625I.035J-.105 149 | N1650G3X4.4425Y5.69I1.725J1.135 150 | N1660G2X4.4475Y5.52I-.095J-.0875 151 | N1670G1X4.325Y5.38 152 | N1680G3Y5.2725I.06J-.055 153 | N1690G2X4.3175Y5.0725I-.105J-.095 154 | N1700G3X4.27Y4.94I.1075J-.115 155 | N1710G2X4.23Y4.805I-.1775J-.0225 156 | N1720G3X4.1525Y4.67I.355J-.29 157 | N1730G2X4.0925Y4.53I-1.1525J.4225 158 | N1740G2X3.8075Y4.35I-.2925J.15 159 | N1750G3X3.475Y4.3025I.035J-1.4125 160 | N1760G3X3.3125Y4.51I-3.0125J-2.2075 161 | N1770G3X3.105Y4.74I-3.9825J-3.3775 162 | N1780G3X3.4075Y5.0225I-.6225J.9775 163 | N1790G3X3.55Y5.335I-.65J.4875 164 | N1800G3X3.4425Y5.6875I-.37J.08 165 | N1810G3X3.18Y5.915I-2.1425J-2.2275 166 | N1830G00Z0.5 167 | N1840G00X0Y0 168 | N1850M05 169 | N1860M30 170 | N1870% 171 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Snow White.tap: -------------------------------------------------------------------------------- 1 | ( Snow White ) 2 | N100G20G17G90G40G49G80 3 | N110G70G91.1 4 | N120T1M06 5 | N140G00G43Z0.5H1 6 | N150S12000M03 7 | N180G94 8 | N190 (Engrave or Vinyl Cut) 9 | N200G00X2.445Y1.5325Z0.2 10 | N210G1Z-.01F10.0 11 | N220G2X2.57Y1.6325I1.52J-1.7725F20.0 12 | N230G3X2.6975Y1.7325I-1.925J2.5875 13 | N240G3X2.835Y1.9175I-.305J.37 14 | N250G3X2.8975Y2.22I-.78J.3225 15 | N260G3X2.755Y2.7925I-1.1075J.0275 16 | N270G3X2.48Y3.235I-5.635J-3.1975 17 | N280G3X2.26Y2.4375I1.0375J-.7125 18 | N290G3X2.445Y1.5325I3.4725J.2325 19 | N300G00Z0.2 20 | N310G00X2.9225Y1.46 21 | N320G1Z-.01F10.0 22 | N330G3X3.3075Y1.635I-.075J.67F20.0 23 | N340G3X3.4675Y1.965I-.345J.37 24 | N350G3X3.425Y2.3I-.8125J.065 25 | N360G3X3.26Y2.5625I-.6625J-.2325 26 | N370G2X3.3425Y2.1325I-7.26J-1.65 27 | N380G2X3.35Y2.0075I-.61J-.1025 28 | N390G2X3.2575Y1.7525I-.4425J.0175 29 | N400G2X2.9225Y1.46I-.9325J.73 30 | N410G00Z0.2 31 | N470G00X3.42Y6.7575 32 | N480G1Z-.01F10.0 33 | N490G3X3.8575Y6.9375I-.17J1.0425F20.0 34 | N500G3X4.1425Y7.265I-.515J.73 35 | N510G2X3.8375Y7.0275I-2.375J2.705 36 | N520G2X3.42Y6.7575I-3.39J4.815 37 | N530G00Z0.2 38 | N540G00X3.18Y5.915 39 | N550G1Z-.01F10.0 40 | N560G1X3.28Y5.79F20.0 41 | N570G2X3.375Y5.665I-2.155J-1.76 42 | N580G2X3.455Y5.385I-.3375J-.245 43 | N590G2X3.325Y5.04I-.6675J.055 44 | N600G2X2.865Y4.875I-.355J.2625 45 | N610G2X2.3925Y5.0125I1.3875J5.6575 46 | N620G2X2.7925Y4.81I-4.5375J-9.455 47 | N630G2X2.9575Y4.71I-.94J-1.76 48 | N640G2X3.485Y4.0025I-.725J-1.0875 49 | N650G2X3.6925Y2.89I-5.035J-1.525 50 | N660G3X3.265Y3.3025I-20.3225J-20.755 51 | N670G3X3.1825Y3.37I-.6325J-.675 52 | N680G3X2.465Y3.7675I-1.825J-2.4525 53 | N690G3X2.0975Y3.8375I-.37J-.9525 54 | N700G2X3.0425Y3.3475I-3.105J-7.16 55 | N710G2X3.4725Y3.0025I-1.0825J-1.78 56 | N720G2X4.0375Y2.3525I-7.0775J-6.7275 57 | N730G2X4.31Y1.95I-2.4375J-1.9375 58 | N740G2X4.4675Y1.0975I-1.155J-.655 59 | N750G2X4.315Y.615I-1.6075J.24 60 | N760G2X4.0125Y.0975I-3.8775J1.925 61 | N770G2X3.93Y.05I-.09J.0625 62 | N780G3X3.7125Y.53I-.765J-.0575 63 | N790G3X2.925Y1.0775I-1.8125J-1.775 64 | N800G3X2.435Y1.17I-.4425J-1 65 | N810G2X1.505Y1.1875I-.3175J7.0825 66 | N820G2X1.3825Y1.24I.02J.2225 67 | N830G2X1.2425Y1.405I.38J.4575 68 | N840G1X1.2825Y1.435 69 | N850G1X1.3225Y1.46 70 | N860G2X1.845Y1.5475I.375J-.635 71 | N870G3X2.5275Y1.4225I3.695J18.2125 72 | N880G1X2.2225Y1.515 73 | N890G2X2.0475Y1.73I.07J.235 74 | N900G3X1.9475Y2.7025I-22.19J-1.765 75 | N910G3X1.79Y3.1625I-1.1675J-.145 76 | N920G1X1.6725Y2.85 77 | N930G3X1.56Y2.5375I5.2225J-2.0525 78 | N940G3X1.6525Y1.98I.58J-.19 79 | N950G3X1.9925Y1.585I3.48J2.6375 80 | N960G1X1.85Y1.605 81 | N970G1X1.6725Y1.62 82 | N980G2X1.46Y1.7225I.02J.31 83 | N990G2X1.1875Y2.165I1.12J.9975 84 | N1000G2X1.1675Y2.4225I.4J.16 85 | N1010G2X1.2925Y2.8525I4.3875J-1.03 86 | N1020G1X1.2525Y2.8325 87 | N1030G1X1.235Y2.8225 88 | N1040G3X.9575Y2.5725I1.245J-1.6725 89 | N1050G3X.8575Y2.415I.3725J-.3475 90 | N1060G3X.94Y1.98I.3775J-.1525 91 | N1070G3X1.435Y1.6025I1.51J1.4575 92 | N1080G3X1.3325Y1.5225I.375J-.59 93 | N1090G2X1.2075Y1.485I-.1025J.11 94 | N1100G2X.8575Y1.66I.095J.63 95 | N1110G2X.5575Y2.2375I.73J.7425 96 | N1120G2X.6875Y2.74I.6425J.1025 97 | N1130G1X1.06Y3.2175 98 | N1140G3X1.1725Y3.44I-.505J.3925 99 | N1150G3X1.1575Y3.5725I-.1775J.05 100 | N1160G3X.935Y3.7675I-.36J-.185 101 | N1170G2X.5025Y4.1075I.2975J.8275 102 | N1180G2X.15Y4.7175I4.4125J2.9475 103 | N1190G2X.055Y5.02I.97J.475 104 | N1200G2X.07Y5.735I1.9375J.315 105 | N1210G2X.465Y6.215I.65J-.135 106 | N1220G3Y5.9825I.49J-.1175 107 | N1230G3X.5675Y5.7725I.505J.115 108 | N1240G3X.6625Y5.685I.3825J.3075 109 | N1250G3X1.05Y5.425I3.6875J5.11 110 | N1260G2X.5925Y5.91I2.8075J3.1 111 | N1270G2X.515Y6.0925I.2875J.23 112 | N1280G2X.6175Y6.3275I.2475J.0325 113 | N1290G2X.8Y6.4325I.6025J-.825 114 | N1300G3X.8575Y6.5125I-.04J.09 115 | N1310G1X.9075Y6.89 116 | N1320G2X1.1475Y7.5225I1.5125J-.215 117 | N1330G2X1.7275Y8.1225I1.9475J-1.295 118 | N1340G2X2.1125Y8.2625I.4325J-.6 119 | N1350G2X2.905Y8.1325I.12J-1.765 120 | N1360G3X2.57Y7.6775I2.885J-2.4825 121 | N1370G3X2.42Y7.2525I.8525J-.5375 122 | N1380G2X2.7075Y7.7925I4.635J-2.13 123 | N1390G2X2.9325Y8.09I1.6275J-.9975 124 | N1400G3X3.0325Y8.0025I1.355J1.4325 125 | N1410G3X3.0725Y7.975I.1725J.21 126 | N1420G2X2.885Y8.3875I.6875J.5625 127 | N1430G2X2.9575Y8.645I.3025J.0525 128 | N1440G2X3.22Y8.6125I.12J-.1075 129 | N1450G3X3.3825Y8.3275I4.395J2.325 130 | N1460G1X3.4675Y8.315 131 | N1470G1X3.51Y8.3075 132 | N1480G2X3.5525Y8.2275I-.02J-.0625 133 | N1490G2X3.445Y8.07I-.2775J.0725 134 | N1500G3X3Y7.675I1.875J-2.57 135 | N1510G3X2.6925Y7.2675I1.73J-1.625 136 | N1520G1X2.865Y7.4625 137 | N1530G2X3.04Y7.6575I3.15J-2.6975 138 | N1540G2X3.4875Y8.06I3.24J-3.1475 139 | N1550G2X4.115Y8.0675I.3175J-.405 140 | N1560G2X4.2575Y7.5625I-.2575J-.345 141 | N1570G3X4.26Y7.28I.3525J-.1375 142 | N1580G2X4.3025Y6.4175I-1.195J-.49 143 | N1590G3X4.2725Y6.2025I.67J-.2025 144 | N1600G3X4.355Y6.235I-.0975J.37 145 | N1610G3X4.4975Y6.3225I-.6425J1.2 146 | N1620G2X4.4125Y6.2025I-.325J.1375 147 | N1630G2X4.2875Y6.1275I-.24J.2575 148 | N1640G3X4.23Y5.9625I.035J-.105 149 | N1650G3X4.4425Y5.69I1.725J1.135 150 | N1660G2X4.4475Y5.52I-.095J-.0875 151 | N1670G1X4.325Y5.38 152 | N1680G3Y5.2725I.06J-.055 153 | N1690G2X4.3175Y5.0725I-.105J-.095 154 | N1700G3X4.27Y4.94I.1075J-.115 155 | N1710G2X4.23Y4.805I-.1775J-.0225 156 | N1720G3X4.1525Y4.67I.355J-.29 157 | N1730G2X4.0925Y4.53I-1.1525J.4225 158 | N1740G2X3.8075Y4.35I-.2925J.15 159 | N1750G3X3.475Y4.3025I.035J-1.4125 160 | N1760G3X3.3125Y4.51I-3.0125J-2.2075 161 | N1770G3X3.105Y4.74I-3.9825J-3.3775 162 | N1780G3X3.4075Y5.0225I-.6225J.9775 163 | N1790G3X3.55Y5.335I-.65J.4875 164 | N1800G3X3.4425Y5.6875I-.37J.08 165 | N1810G3X3.18Y5.915I-2.1425J-2.2275 166 | N1830G00Z0.5 167 | N1840G00X0Y0 168 | N1850M05 169 | N1860M30 170 | N1870% 171 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Letter A (V-Carve).tap: -------------------------------------------------------------------------------- 1 | (Letter 'A' F-Engrave, V-Carve.) 2 | (60deg. 1/2" V bit) 3 | G90 4 | G20 5 | G17 G64 P0.001 M3 S3000 6 | F5.00 7 | G0 Z0.2500 8 | G0 X1.8221 Y0.0000 9 | G1 Z0.0000 10 | G1 X1.8221 Y0.0000 Z0.0000 11 | G1 X1.5484 Y0.1983 Z-0.3435 12 | G1 X1.5383 Y0.2002 Z-0.3468 13 | G1 X1.3862 Y0.0000 Z0.0000 14 | G1 X1.5386 Y0.2007 Z-0.3476 15 | G1 X1.5387 Y0.2112 Z-0.3526 16 | G1 X1.4414 Y0.5321 Z-0.3396 17 | G1 X1.4280 Y0.5719 Z-0.3402 18 | G1 X1.4113 Y0.6088 Z-0.3479 19 | G1 X1.3945 Y0.6381 Z-0.3596 20 | G1 X1.3743 Y0.6678 Z-0.3769 21 | G1 X1.3564 Y0.6904 Z-0.3942 22 | G1 X1.3479 Y0.6940 Z-0.3932 23 | G1 X1.3062 Y0.6824 Z-0.3485 24 | G1 X1.2723 Y0.6729 Z-0.3205 25 | G1 X1.2571 Y0.6687 Z-0.3111 26 | G1 X1.2296 Y0.6616 Z-0.2986 27 | G1 X1.2095 Y0.6579 Z-0.2920 28 | G1 X1.1893 Y0.6553 Z-0.2876 29 | G1 X1.1590 Y0.6538 Z-0.2850 30 | G1 X0.6646 Y0.6538 Z-0.2850 31 | G1 X0.6444 Y0.6545 Z-0.2862 32 | G1 X0.6141 Y0.6579 Z-0.2920 33 | G1 X0.5939 Y0.6616 Z-0.2986 34 | G1 X0.5665 Y0.6687 Z-0.3111 35 | G1 X0.5433 Y0.6752 Z-0.3263 36 | G1 X0.5264 Y0.6799 Z-0.3402 37 | G1 X0.4978 Y0.6879 Z-0.3683 38 | G1 X0.4757 Y0.6940 Z-0.3932 39 | G1 X0.4670 Y0.6908 Z-0.3951 40 | G1 X0.4436 Y0.6607 Z-0.3729 41 | G1 X0.4242 Y0.6311 Z-0.3570 42 | G1 X0.4082 Y0.6017 Z-0.3466 43 | G1 X0.3923 Y0.5646 Z-0.3404 44 | G1 X0.3817 Y0.5323 Z-0.3405 45 | G1 X0.2843 Y0.2114 Z-0.3537 46 | G1 X0.2850 Y0.2007 Z-0.3476 47 | G1 X0.4374 Y0.0000 Z0.0000 48 | G1 X0.2848 Y0.2009 Z-0.3480 49 | G1 X0.2746 Y0.1989 Z-0.3444 50 | G1 X0.0000 Y0.0000 Z0.0000 51 | G1 X0.2807 Y0.2033 Z-0.3520 52 | G1 X0.2847 Y0.2125 Z-0.3536 53 | G1 X0.3836 Y0.5387 Z-0.3402 54 | G1 X0.3932 Y0.5672 Z-0.3406 55 | G1 X0.4050 Y0.5950 Z-0.3449 56 | G1 X0.4190 Y0.6221 Z-0.3533 57 | G1 X0.4410 Y0.6570 Z-0.3706 58 | G1 X0.4669 Y0.6907 Z-0.3950 59 | G1 X0.4703 Y0.7002 Z-0.3955 60 | G1 X0.4673 Y0.7434 Z-0.3673 61 | G1 X0.4684 Y0.7852 Z-0.3465 62 | G1 X0.4735 Y0.8257 Z-0.3330 63 | G1 X0.4800 Y0.8552 Z-0.3278 64 | G1 X0.6616 Y1.4498 Z-0.3053 65 | G1 X0.7179 Y1.6423 Z-0.2938 66 | G1 X0.7556 Y1.7776 Z-0.2828 67 | G1 X0.7622 Y1.7966 Z-0.2834 68 | G1 X0.7744 Y1.8242 Z-0.2885 69 | G1 X0.7771 Y1.8339 Z-0.2877 70 | G1 X0.6568 Y2.0000 Z0.0000 71 | G1 X0.7768 Y1.8343 Z-0.2870 72 | G1 X0.7868 Y1.8361 Z-0.2838 73 | G1 X0.8168 Y1.8484 Z-0.2626 74 | G1 X0.8468 Y1.8573 Z-0.2472 75 | G1 X0.8768 Y1.8628 Z-0.2376 76 | G1 X0.9068 Y1.8650 Z-0.2338 77 | G1 X0.9368 Y1.8639 Z-0.2357 78 | G1 X0.9668 Y1.8595 Z-0.2434 79 | G1 X0.9968 Y1.8517 Z-0.2569 80 | G1 X1.0168 Y1.8447 Z-0.2691 81 | G1 X1.0368 Y1.8361 Z-0.2838 82 | G1 X1.0468 Y1.8344 Z-0.2868 83 | G1 X1.1668 Y2.0000 Z0.0000 84 | G1 X1.0465 Y1.8340 Z-0.2874 85 | G1 X1.0491 Y1.8243 Z-0.2884 86 | G1 X1.0575 Y1.8060 Z-0.2845 87 | G1 X1.0679 Y1.7778 Z-0.2827 88 | G1 X1.0862 Y1.7099 Z-0.2891 89 | G1 X1.1386 Y1.5267 Z-0.3016 90 | G1 X1.3431 Y0.8555 Z-0.3271 91 | G1 X1.3496 Y0.8260 Z-0.3323 92 | G1 X1.3547 Y0.7855 Z-0.3457 93 | G1 X1.3558 Y0.7437 Z-0.3664 94 | G1 X1.3528 Y0.7005 Z-0.3946 95 | G1 X1.3559 Y0.6909 Z-0.3947 96 | G1 X1.3819 Y0.6573 Z-0.3701 97 | G1 X1.3988 Y0.6312 Z-0.3564 98 | G1 X1.4135 Y0.6044 Z-0.3466 99 | G1 X1.4221 Y0.5861 Z-0.3423 100 | G1 X1.4297 Y0.5675 Z-0.3398 101 | G1 X1.4393 Y0.5390 Z-0.3393 102 | G1 X1.5415 Y0.2034 Z-0.3522 103 | G1 X1.8121 Y0.0073 Z-0.0126 104 | G1 X1.8221 Y0.0000 Z0.0000 105 | G0 Z0.2500 106 | G0 X0.9118 Y1.7302 107 | G1 Z0.0000 108 | G1 X0.9118 Y1.7302 109 | G1 X0.7511 Y1.7621 Z-0.2838 110 | G1 X0.7188 Y1.6456 Z-0.2936 111 | G1 X0.6592 Y1.4417 Z-0.3057 112 | G1 X0.4801 Y0.8553 Z-0.3278 113 | G1 X0.4722 Y0.8176 Z-0.3352 114 | G1 X0.4679 Y0.7772 Z-0.3499 115 | G1 X0.4677 Y0.7333 Z-0.3733 116 | G1 X0.4698 Y0.7046 Z-0.3924 117 | G1 X0.4719 Y0.6951 Z-0.3978 118 | G1 X0.4942 Y0.6889 Z-0.3722 119 | G1 X0.5231 Y0.6808 Z-0.3432 120 | G1 X0.5481 Y0.6738 Z-0.3228 121 | G1 X0.5706 Y0.6675 Z-0.3088 122 | G1 X0.5987 Y0.6606 Z-0.2969 123 | G1 X0.6258 Y0.6562 Z-0.2892 124 | G1 X0.6525 Y0.6541 Z-0.2855 125 | G1 X0.6757 Y0.6538 Z-0.2850 126 | G1 X1.1478 127 | G1 X1.1844 Y0.6549 Z-0.2869 128 | G1 X1.2045 Y0.6571 Z-0.2907 129 | G1 X1.2249 Y0.6606 Z-0.2969 130 | G1 X1.2458 Y0.6656 Z-0.3054 131 | G1 X1.2677 Y0.6717 Z-0.3175 132 | G1 X1.2918 Y0.6784 Z-0.3356 133 | G1 X1.3096 Y0.6834 Z-0.3518 134 | G1 X1.3517 Y0.6951 Z-0.3978 135 | G1 X1.3555 Y0.7335 Z-0.3725 136 | G1 X1.3553 Y0.7773 Z-0.3492 137 | G1 X1.3510 Y0.8176 Z-0.3345 138 | G1 X1.3431 Y0.8553 Z-0.3271 139 | G1 X1.1400 Y1.5219 Z-0.3019 140 | G1 X1.0868 Y1.7079 Z-0.2892 141 | G1 X1.0688 Y1.7750 Z-0.2827 142 | G1 X1.0575 Y1.8060 Z-0.2845 143 | G1 X1.0460 Y1.8304 Z-0.2902 144 | G1 X1.0403 Y1.8345 Z-0.2867 145 | G1 X1.0083 Y1.8478 Z-0.2636 146 | G1 X0.9903 Y1.8537 Z-0.2534 147 | G1 X0.9671 Y1.8594 Z-0.2435 148 | G1 X0.9503 Y1.8623 Z-0.2384 149 | G1 X0.9283 Y1.8646 Z-0.2345 150 | G1 X0.9064 Y1.8650 Z-0.2338 151 | G1 X0.8846 Y1.8637 Z-0.2361 152 | G1 X0.8567 Y1.8595 Z-0.2434 153 | G1 X0.8335 Y1.8537 Z-0.2533 154 | G1 X0.8030 Y1.8432 Z-0.2716 155 | G1 X0.7835 Y1.8346 Z-0.2865 156 | G1 X0.7776 Y1.8307 Z-0.2904 157 | G1 X0.7688 Y1.8124 Z-0.2857 158 | G1 X0.7612 Y1.7940 Z-0.2832 159 | G1 X0.7511 Y1.7621 Z-0.2838 160 | G1 X1.0725 Y1.7618 Z-0.2837 161 | G1 X1.0624 Y1.7937 Z-0.2831 162 | G1 X1.0548 Y1.8122 Z-0.2856 163 | G1 X1.0460 Y1.8304 Z-0.2902 164 | G1 X1.0403 Y1.8345 Z-0.2867 165 | G1 X1.0083 Y1.8478 Z-0.2636 166 | G1 X0.9902 Y1.8537 Z-0.2534 167 | G1 X0.9671 Y1.8594 Z-0.2435 168 | G1 X0.9503 Y1.8623 Z-0.2384 169 | G1 X0.9282 Y1.8646 Z-0.2345 170 | G1 X0.9064 Y1.8650 Z-0.2338 171 | G1 X0.8846 Y1.8637 Z-0.2361 172 | G1 X0.8567 Y1.8595 Z-0.2434 173 | G1 X0.8335 Y1.8537 Z-0.2533 174 | G1 X0.8030 Y1.8432 Z-0.2717 175 | G1 X0.7835 Y1.8346 Z-0.2865 176 | G1 X0.7776 Y1.8307 Z-0.2904 177 | G1 X0.7688 Y1.8124 Z-0.2857 178 | G1 X0.7612 Y1.7940 Z-0.2832 179 | G1 X0.7511 Y1.7621 Z-0.2838 180 | G0 Z0.2500 181 | M5 182 | M30 183 | % 184 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Scorpion.tap: -------------------------------------------------------------------------------- 1 | G21 2 | T1M6 3 | G0Z10.000 4 | G0X0.000Y0.000S10000M3 5 | G0X40.094Y235.389Z5.000 6 | G1Z-3.000F600.0 7 | G1X39.594Y234.826F1800.0 8 | X33.059Y225.446 9 | X29.088Y209.687 10 | X29.338Y187.488 11 | X32.027Y174.419 12 | X36.905Y168.040 13 | X38.969Y159.598 14 | X46.473Y148.999 15 | X55.665Y143.089 16 | X61.856Y141.870 17 | X68.047Y138.962 18 | X78.027Y141.964 19 | X88.870Y151.907 20 | X91.496Y155.096 21 | X100.877Y152.845 22 | X97.312Y145.903 23 | X90.934Y143.277 24 | X83.993Y136.523 25 | X68.047Y135.023 26 | X63.544Y134.710 27 | X56.884Y136.617 28 | X47.223Y137.461 29 | X40.845Y139.150 30 | X35.404Y139.400 31 | X32.965Y139.712 32 | X32.965Y136.242 33 | X46.848Y134.835 34 | X62.606Y131.458 35 | X74.881Y129.957 36 | X82.679Y131.833 37 | X91.497Y135.023 38 | X97.312Y140.275 39 | X99.563Y141.588 40 | X101.252Y138.587 41 | X100.501Y134.835 42 | X95.624Y134.272 43 | X91.403Y129.957 44 | X76.864Y121.984 45 | X62.231Y120.859 46 | X47.926Y121.328 47 | X43.283Y122.547 48 | X33.528Y127.331 49 | X31.277Y128.644 50 | X29.401Y123.016 51 | X28.557Y121.140 52 | X39.344Y119.639 53 | X55.477Y116.450 54 | X73.675Y116.075 55 | X89.433Y121.328 56 | X96.499Y126.768 57 | X100.501Y127.894 58 | X99.845Y123.298 59 | X92.435Y120.015 60 | X88.589Y113.824 61 | X79.115Y104.256 62 | X75.926Y96.752 63 | X58.979Y96.002 64 | X53.977Y96.189 65 | X44.221Y98.159 66 | X33.716Y102.380 67 | X26.212Y105.569 68 | X24.336Y100.504 69 | X33.153Y99.941 70 | X39.907Y96.940 71 | X50.225Y92.437 72 | X58.104Y92.062 73 | X65.561Y91.030 74 | X75.644Y92.906 75 | X83.430Y95.251 76 | X89.433Y102.755 77 | X92.059Y110.447 78 | X97.875Y114.574 79 | X96.562Y105.382 80 | X93.842Y105.007 81 | X92.622Y104.631 82 | X89.621Y97.221 83 | X88.964Y89.248 84 | X83.805Y78.930 85 | X81.929Y73.865 86 | X75.082Y68.190 87 | X67.296Y58.857 88 | X58.760Y63.172 89 | X49.287Y67.862 90 | X40.845Y71.332 91 | X36.155Y71.239 92 | X34.279Y68.049 93 | X44.409Y67.862 94 | X53.977Y62.609 95 | X58.667Y58.669 96 | X63.169Y54.917 97 | X70.298Y52.479 98 | X85.118Y66.361 99 | X86.807Y71.801 100 | X91.497Y82.119 101 | X92.059Y92.062 102 | X93.185Y93.188 103 | Y79.743 104 | X97.312Y66.173 105 | X101.252Y58.763 106 | X104.159Y51.165 107 | X111.070Y41.285 108 | X121.513Y30.154 109 | X132.206Y19.273 110 | X146.088Y11.957 111 | X160.909Y7.924 112 | X173.478Y7.079 113 | X183.045Y9.143 114 | X193.739Y15.146 115 | X197.960Y21.525 116 | X196.928Y30.529 117 | X187.226Y42.589 118 | X180.419Y52.854 119 | X178.355Y61.296 120 | X174.979Y54.355 121 | X176.479Y36.533 122 | X183.608Y20.211 123 | X182.389Y17.397 124 | X169.726Y18.898 125 | X162.972Y16.741 126 | X156.312Y22.181 127 | X144.775Y23.776 128 | X138.584Y31.749 129 | X132.018Y34.281 130 | X128.266Y39.440 131 | X123.326Y42.223 132 | X121.325Y50.040 133 | X129.767Y67.862 134 | X134.457Y84.183 135 | Y90.937 136 | X138.115Y86.997 137 | X140.835Y73.677 138 | X153.029Y60.358 139 | X157.907Y59.232 140 | X165.786Y64.110 141 | X173.290Y72.427 142 | X182.670Y76.491 143 | X190.268Y76.116 144 | X191.487Y79.493 145 | X176.542Y77.680 146 | X166.255Y70.394 147 | X160.533Y64.297 148 | X153.092Y67.987 149 | X147.214Y73.302 150 | X144.400Y83.620 151 | X141.586Y89.623 152 | X140.460Y95.627 153 | X136.521Y99.379 154 | X134.082Y100.598 155 | X133.144Y110.259 156 | X133.707Y111.385 157 | X138.959Y105.194 158 | X143.274Y100.879 159 | X147.120Y98.816 160 | X151.341Y96.752 161 | X162.785Y94.970 162 | X171.508Y92.719 163 | X178.824Y94.782 164 | X190.174Y95.251 165 | X198.241Y90.374 166 | X199.929Y95.064 167 | X185.484Y97.878 168 | X176.198Y97.315 169 | X164.379Y97.409 170 | X153.874Y102.286 171 | X148.339Y102.568 172 | X141.211Y111.385 173 | X137.771Y115.012 174 | X133.144Y118.326 175 | X130.893Y121.140 176 | X130.330Y124.329 177 | X134.832Y121.515 178 | X139.428Y119.827 179 | X149.840Y113.261 180 | X155.281Y111.885 181 | X161.873Y110.447 182 | X172.602Y111.823 183 | X179.997Y111.338 184 | X184.921Y112.323 185 | X193.739Y112.573 186 | X201.055Y110.447 187 | X201.430Y115.325 188 | X191.112Y114.387 189 | X180.794Y115.512 190 | X172.399Y115.887 191 | X159.327Y117.763 192 | X154.718 193 | X147.589Y123.673 194 | X140.554Y126.768 195 | X135.020Y132.209 196 | X130.142Y133.522 197 | X129.017Y142.996 198 | X134.082Y143.089 199 | X138.021Y139.713 200 | X146.839Y138.212 201 | X155.281Y133.147 202 | X161.284Y133.522 203 | X168.975Y130.145 204 | X174.228Y127.456 205 | X180.794Y120.953 206 | X183.608Y123.766 207 | X174.979Y131.646 208 | X164.098Y134.741 209 | X157.719Y137.555 210 | X150.122Y140.651 211 | X139.710Y144.872 212 | X134.269Y145.340 213 | X130.142Y150.781 214 | X137.021Y152.782 215 | X142.149Y150.593 216 | X149.277Y143.465 217 | X162.972Y140.088 218 | X171.039Y142.151 219 | X180.044Y145.903 220 | X191.300Y156.597 221 | X193.832Y165.226 222 | X201.055Y174.981 223 | X206.120Y189.802 224 | X205.772Y207.597 225 | X200.867Y223.945 226 | X195.427Y232.762 227 | X194.489Y216.253 228 | X191.300Y200.120 229 | X192.050Y229.010 230 | X188.157Y202.652 231 | X187.735Y192.991 232 | X182.952Y186.144 233 | X183.233Y177.795 234 | X189.987Y168.228 235 | X186.891Y164.570 236 | X175.729Y160.349 237 | X170.851Y155.283 238 | X170.101Y149.468 239 | X165.724Y150.030 240 | X155.281Y157.159 241 | X142.524Y158.379 242 | X137.083Y162.037 243 | X136.333Y164.007 244 | X126.203Y156.221 245 | X123.107Y155.846 246 | X122.638Y155.096 247 | X120.293Y160.818 248 | X117.761Y161.943 249 | X117.010Y155.096 250 | X113.258Y156.034 251 | X114.196Y161.287 252 | X111.945 253 | X107.818Y153.970 254 | X102.040Y159.973 255 | X93.935Y165.320 256 | X92.247Y165.414 257 | X91.403Y161.849 258 | X86.181Y159.223 259 | X68.234Y154.158 260 | X59.980Y150.687 261 | X57.541Y153.220 262 | X54.633Y159.786 263 | X44.484Y166.127 264 | X40.469Y172.355 265 | X48.818Y182.861 266 | X51.350Y191.115 267 | X44.972Y203.872 268 | X40.845Y231.637 269 | X40.657Y213.439 270 | X39.391Y224.695 271 | X40.094Y235.389 272 | G0Z5.000 273 | G0X0.000Y0.000Z10.000 274 | G0Z10.000 275 | G0X0Y0 276 | M30 277 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Clamp.nc: -------------------------------------------------------------------------------- 1 | N005 G91.1 2 | N015 G90 G17 G94 G49 G40 G80 3 | N020 G20 (Inch) 4 | N025 M06 T1 (Endmill Dia. 0.250) 5 | N030 M03 S2500 6 | N035 G00 Z1.0000 7 | N040 M08 8 | N045 G43 H1 Z0.250 9 | N050 G00 X3.5851 Y0.0000 10 | N055 G01 Z-0.0250 F1.0 11 | N060 F5.0 12 | N065 G01 X2.7632 Y0.6910 13 | N070 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 14 | N075 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 15 | N080 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 16 | N085 G01 X-0.1158 Y0.3567 17 | N090 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 18 | N095 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 19 | N100 G01 X2.2526 Y-0.2500 20 | N105 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 21 | N110 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 22 | N115 G03 X3.2421 Y0.2864 I0.2744 J0.2523 23 | N120 G01 X3.5851 Y0.0000 24 | N125 G00 Z0.2500 25 | N130 G00 X3.5851 Y0.0000 26 | N135 G01 Z-0.0500 F1.0 27 | N140 F5.0 28 | N145 G01 X2.7632 Y0.6910 29 | N150 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 30 | N155 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 31 | N160 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 32 | N165 G01 X-0.1158 Y0.3567 33 | N170 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 34 | N175 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 35 | N180 G01 X2.2526 Y-0.2500 36 | N185 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 37 | N190 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 38 | N195 G03 X3.2421 Y0.2864 I0.2744 J0.2523 39 | N200 G01 X3.5851 Y0.0000 40 | N205 G00 Z0.2500 41 | N210 G00 X3.5851 Y0.0000 42 | N215 G01 Z-0.0750 F1.0 43 | N220 F5.0 44 | N225 G01 X2.7632 Y0.6910 45 | N230 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 46 | N235 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 47 | N240 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 48 | N245 G01 X-0.1158 Y0.3567 49 | N250 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 50 | N255 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 51 | N260 G01 X2.2526 Y-0.2500 52 | N265 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 53 | N270 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 54 | N275 G03 X3.2421 Y0.2864 I0.2744 J0.2523 55 | N280 G01 X3.5851 Y0.0000 56 | N285 G00 Z0.2500 57 | N290 G00 X3.5851 Y0.0000 58 | N295 G01 Z-0.1000 F1.0 59 | N300 F5.0 60 | N305 G01 X2.7632 Y0.6910 61 | N310 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 62 | N315 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 63 | N320 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 64 | N325 G01 X-0.1158 Y0.3567 65 | N330 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 66 | N335 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 67 | N340 G01 X2.2526 Y-0.2500 68 | N345 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 69 | N350 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 70 | N355 G03 X3.2421 Y0.2864 I0.2744 J0.2523 71 | N360 G01 X3.5851 Y0.0000 72 | N365 G00 Z0.2500 73 | N370 G00 X3.5851 Y0.0000 74 | N375 G01 Z-0.1250 F1.0 75 | N380 F5.0 76 | N385 G01 X2.7632 Y0.6910 77 | N390 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 78 | N395 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 79 | N400 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 80 | N405 G01 X-0.1158 Y0.3567 81 | N410 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 82 | N415 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 83 | N420 G01 X2.2526 Y-0.2500 84 | N425 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 85 | N430 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 86 | N435 G03 X3.2421 Y0.2864 I0.2744 J0.2523 87 | N440 G01 X3.5851 Y0.0000 88 | N445 G00 Z0.2500 89 | N450 G00 X3.5851 Y0.0000 90 | N455 G01 Z-0.1500 F1.0 91 | N460 F5.0 92 | N465 G01 X2.7632 Y0.6910 93 | N470 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 94 | N475 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 95 | N480 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 96 | N485 G01 X-0.1158 Y0.3567 97 | N490 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 98 | N495 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 99 | N500 G01 X2.2526 Y-0.2500 100 | N505 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 101 | N510 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 102 | N515 G03 X3.2421 Y0.2864 I0.2744 J0.2523 103 | N520 G01 X3.5851 Y0.0000 104 | N525 G00 Z0.2500 105 | N530 G00 X3.5851 Y0.0000 106 | N535 G01 Z-0.1750 F1.0 107 | N540 F5.0 108 | N545 G01 X2.7632 Y0.6910 109 | N550 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 110 | N555 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 111 | N560 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 112 | N565 G01 X-0.1158 Y0.3567 113 | N570 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 114 | N575 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 115 | N580 G01 X2.2526 Y-0.2500 116 | N585 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 117 | N590 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 118 | N595 G03 X3.2421 Y0.2864 I0.2744 J0.2523 119 | N600 G01 X3.5851 Y0.0000 120 | N605 G00 Z0.2500 121 | N610 G00 X3.5851 Y0.0000 122 | N615 G01 Z-0.2000 F1.0 123 | N620 F5.0 124 | N625 G01 X2.7632 Y0.6910 125 | N630 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 126 | N635 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 127 | N640 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 128 | N645 G01 X-0.1158 Y0.3567 129 | N650 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 130 | N655 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 131 | N660 G01 X2.2526 Y-0.2500 132 | N665 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 133 | N670 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 134 | N675 G03 X3.2421 Y0.2864 I0.2744 J0.2523 135 | N680 G01 X3.5851 Y0.0000 136 | N685 G00 Z0.2500 137 | N690 G00 X3.5851 Y0.0000 138 | N695 G01 Z-0.2250 F1.0 139 | N700 F5.0 140 | N705 G01 X2.7632 Y0.6910 141 | N710 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 142 | N715 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 143 | N720 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 144 | N725 G01 X-0.1158 Y0.3567 145 | N730 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 146 | N735 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 147 | N740 G01 X2.2526 Y-0.2500 148 | N745 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 149 | N750 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 150 | N755 G03 X3.2421 Y0.2864 I0.2744 J0.2523 151 | N760 G01 X3.5851 Y0.0000 152 | N765 G00 Z0.2500 153 | N770 G00 X3.5851 Y0.0000 154 | N775 G01 Z-0.2500 F1.0 155 | N780 F5.0 156 | N785 G01 X2.7632 Y0.6910 157 | N790 G03 X2.2084 Y0.6120 I-0.2421 J-0.2873 158 | N795 G02 X1.7769 Y0.6373 I-0.2084 J0.1380 159 | N800 G03 X1.3261 Y0.8253 I-0.3383 J-0.1766 160 | N805 G01 X-0.1158 Y0.3567 161 | N810 G03 X0.2251 Y-0.3000 I0.1158 J-0.3567 162 | N815 G02 X0.3751 Y-0.2500 I0.1499 J-0.2001 163 | N820 G01 X2.2526 Y-0.2500 164 | N825 G03 X2.4276 Y-0.2067 I0.0077 J0.3441 165 | N830 G02 X2.7279 Y-0.2513 I0.1391 J-0.0969 166 | N835 G03 X3.2421 Y0.2864 I0.2744 J0.2523 167 | N840 G01 X3.5851 Y0.0000 168 | N845 G00 Z0.2500 169 | N850 G00 X0.0000 Y0.0000 170 | N855 G01 Z1.0000 F1.0 171 | N860 F5.0 172 | N865 G01 X0.0000 Y0.0000 173 | N870 G00 Z0.2500 174 | N875 M09 175 | N880 M05 176 | N885 M30 177 | N890 % 178 | -------------------------------------------------------------------------------- /deployment/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source $(which virtualenvwrapper.sh) 3 | 4 | function pyver() { 5 | # write python version to stdout 6 | $1 -c 'import sys; print("{}.{}.{}".format(*sys.version_info))' 7 | } 8 | 9 | function lib_ver() { 10 | # Get library vesion number 11 | pushd ../src > /dev/null 12 | python -c 'import pygcode; print(pygcode.__version__)' 13 | popd > /dev/null 14 | } 15 | 16 | function lib_prereq() { 17 | pushd .. > /dev/null 18 | python -c 'from setup import INSTALL_REQUIRES ; print(" ".join(INSTALL_REQUIRES))' 19 | popd > /dev/null 20 | } 21 | 22 | # ============ Local Parameters 23 | LIB_NAME=pygcode 24 | LIB_VER=$(lib_ver) 25 | 26 | # ============ Help Text 27 | function show_help() { 28 | [ "$@" ] && echo "$@" 29 | cat << EOF 30 | Usage: ./${0##*/} {build|test|and so on ...} 31 | 32 | This script is to maintain a consistent method of deployment and testing. 33 | 34 | Deployment target: ${LIB_NAME} ${LIB_VER} 35 | 36 | Arguments: 37 | Setup: 38 | setup: Installs packages & sets up environment (requires sudo) 39 | 40 | Compiling: 41 | build: Execute setup to build packages (populates ../dist/) 42 | creates both 'sdist' and 'wheel' distrobutions. 43 | 44 | Virtual Environments: 45 | lsenv List created virtual environments for this lib 46 | rmenv py# Remove virtual environment 47 | remkenv py# Remove, then create re-create virtual environment 48 | envprereq py# install environment prerequisites (official PyPi) 49 | 50 | Deploy: 51 | deploy test Upload to PyPi test server 52 | deploy prod Upload to PyPi (official) 53 | 54 | Install: 55 | install sdist py# Install from local sdist 56 | install wheel py# Install from local wheel 57 | install pypitest py# Install from PyPi test server 58 | install pypi py# Install from PyPi (official) 59 | 60 | Testing: 61 | test dev py# Run tests on local dev in a virtual env 62 | test installed py# Run tests on installed library in virtual env 63 | 64 | Help: 65 | -h | --help display this help message 66 | 67 | py#: when referenced above means 68 | 'py2' for Python $(pyver python2) 69 | 'py3' for Python $(pyver python3) 70 | EOF 71 | } 72 | 73 | # ============ Commands 74 | function setup() { 75 | # Install pre-requisite tooling 76 | sudo pip install -U "pip>=1.4" "setuptools>=0.9" "wheel>=0.21" twine 77 | } 78 | 79 | function build() { 80 | # Run setup.py to build sdist and wheel distrobutions 81 | pushd .. 82 | rm -rf build/ 83 | python setup.py sdist bdist_wheel 84 | popd 85 | } 86 | 87 | function lsenv() { 88 | lsvirtualenv -b | grep ^${LIB_NAME}_ 89 | } 90 | 91 | function rmenv() { 92 | # Remove virtual environment 93 | set_venv_variables $1 94 | rmvirtualenv ${env_name} 95 | } 96 | 97 | function remkenv() { 98 | # Remove virtual environment, then re-create it from scratch 99 | set_venv_variables $1 100 | rmvirtualenv ${env_name} 101 | mkvirtualenv --python=${python_bin} ${env_name} 102 | } 103 | 104 | function envprereq() { 105 | set_venv_variables $1 106 | workon ${env_name} 107 | ${env_pip_bin} install $(lib_prereq) 108 | deactivate 109 | } 110 | 111 | function deploy() { 112 | # Deploy compiled distributions to the test|prod server 113 | _deployment_env=$1 114 | pushd .. 115 | twine upload -r ${_deployment_env} dist/${LIB_NAME}-${LIB_VER}* 116 | popd 117 | } 118 | 119 | function install() { 120 | # Install library from a variety of sources 121 | _install_type=$1 122 | _env_key=$2 123 | 124 | set_venv_variables ${_env_key} 125 | workon ${env_name} 126 | 127 | case "${_install_type}" in 128 | sdist) 129 | # Install from locally compiled 'sdist' file 130 | ${env_pip_bin} install ../dist/${LIB_NAME}-${LIB_VER}.tar.gz 131 | ;; 132 | wheel) 133 | # Install from locally compiled 'wheel' file 134 | ${env_pip_bin} install ../dist/${LIB_NAME}-${LIB_VER}-py2.py3-none-any.whl 135 | ;; 136 | pypitest) 137 | # Install from PyPi test server 138 | ${env_pip_bin} install -i https://testpypi.python.org/pypi ${LIB_NAME} 139 | ;; 140 | pypi) 141 | # Install from official PyPi server 142 | ${env_pip_bin} install ${LIB_NAME} 143 | ;; 144 | *) 145 | echo invalid install type: \"${_install_type}\" >&2 146 | exit 1 147 | ;; 148 | esac 149 | 150 | deactivate 151 | } 152 | 153 | function run_test() { 154 | # Run tests 155 | 156 | _test_scope=$1 157 | _env_key=$2 158 | 159 | set_venv_variables ${_env_key} 160 | 161 | case "${_test_scope}" in 162 | dev) 163 | export PYGCODE_TESTSCOPE=local 164 | ;; 165 | installed) 166 | export PYGCODE_TESTSCOPE=installed 167 | ;; 168 | *) 169 | echo invalid test scope: \"${_test_scope}\" >&2 170 | exit 1 171 | ;; 172 | esac 173 | 174 | pushd ../tests 175 | workon ${env_name} 176 | ${env_python_bin} -m unittest discover -s . -p 'test_*.py' 177 | deactivate 178 | popd 179 | } 180 | 181 | # ============ Utilities 182 | function set_venv_variables() { 183 | # on successful exit, environment variables are set: 184 | # env_name = virtual environment's name 185 | # env_pip_bin = environment's pip binary 186 | # env_python_bin = environment's python binary 187 | # python_bin = python binary in host environment 188 | _env_key=$1 189 | 190 | env_name=${LIB_NAME}_${_env_key} 191 | env_pip_bin=${WORKON_HOME}/${env_name}/bin/pip 192 | env_python_bin=${WORKON_HOME}/${env_name}/bin/python 193 | case "${_env_key}" in 194 | py2) 195 | python_bin=$(which python2) 196 | ;; 197 | py3) 198 | python_bin=$(which python3) 199 | ;; 200 | *) 201 | echo invalid environment key: \"${_env_key}\" >&2 202 | exit 1 203 | ;; 204 | esac 205 | } 206 | 207 | # ============ Option parsing 208 | case "$1" in 209 | # Show help on request 210 | -h|--help) 211 | show_help 212 | exit 0 213 | ;; 214 | 215 | # Valid Commands 216 | setup) setup ;; 217 | build) build ;; 218 | lsenv) lsenv ;; 219 | rmenv) rmenv $2 ;; 220 | remkenv) remkenv $2 ;; 221 | envprereq) envprereq $2 ;; 222 | deploy) deploy $2 ;; 223 | install) install $2 $3 ;; 224 | test) run_test $2 $3 ;; 225 | 226 | # otherwise... show help 227 | *) 228 | show_help >&2 229 | exit 1 230 | ;; 231 | esac 232 | 233 | #echo ./${0##*/} completed successfully 234 | -------------------------------------------------------------------------------- /src/pygcode/dialects/linuxcnc.py: -------------------------------------------------------------------------------- 1 | """ 2 | LinuxCNC 3 | 4 | The linuxcnc gcode dialect is typically used for subtractive fabrication, such 5 | as milling. 6 | 7 | This dialect is the basis for all other dialects; GCodes and Words in other 8 | dialects either inherit, or directly reference these classes. 9 | 10 | **Specification:** http://www.linuxcnc.org 11 | 12 | TODO: verify above info before publishing 13 | """ 14 | 15 | import re 16 | 17 | from .utils import WordType 18 | 19 | # ======================== WORDS ======================== 20 | 21 | REGEX_FLOAT = re.compile(r'^\s*-?(\d+\.?\d*|\.\d+)') # testcase: ..tests.test_words.WordValueMatchTests.test_float 22 | REGEX_INT = re.compile(r'^\s*-?\d+') 23 | REGEX_POSITIVEINT = re.compile(r'^\s*\d+') 24 | REGEX_CODE = re.compile(r'^\s*\d+(\.\d)?') # float, but can't be negative 25 | 26 | # Value cleaning functions 27 | def _clean_codestr(value): 28 | if value < 10: 29 | return "0%g" % value 30 | return "%g" % value 31 | 32 | CLEAN_NONE = lambda v: v 33 | CLEAN_FLOAT = lambda v: "{0:g}".format(round(v, 3)) 34 | CLEAN_CODE = _clean_codestr 35 | CLEAN_INT = lambda v: "%g" % v 36 | 37 | WORD_MAP = { 38 | # Descriptions copied from wikipedia: 39 | # https://en.wikipedia.org/wiki/G-code#Letter_addresses 40 | 41 | # Rotational Axes 42 | 'A': WordType( 43 | cls=float, 44 | value_regex=REGEX_FLOAT, 45 | description="Absolute or incremental position of A axis (rotational axis around X axis)", 46 | clean_value=CLEAN_FLOAT, 47 | ), 48 | 'B': WordType( 49 | cls=float, 50 | value_regex=REGEX_FLOAT, 51 | description="Absolute or incremental position of B axis (rotational axis around Y axis)", 52 | clean_value=CLEAN_FLOAT, 53 | ), 54 | 'C': WordType( 55 | cls=float, 56 | value_regex=REGEX_FLOAT, 57 | description="Absolute or incremental position of C axis (rotational axis around Z axis)", 58 | clean_value=CLEAN_FLOAT, 59 | ), 60 | 'D': WordType( 61 | cls=float, 62 | value_regex=REGEX_FLOAT, 63 | description="Defines diameter or radial offset used for cutter compensation. D is used for depth of cut on lathes. It is used for aperture selection and commands on photoplotters.", 64 | clean_value=CLEAN_FLOAT, 65 | ), 66 | # Feed Rates 67 | 'E': WordType( 68 | cls=float, 69 | value_regex=REGEX_FLOAT, 70 | description="Precision feedrate for threading on lathes", 71 | clean_value=CLEAN_FLOAT, 72 | ), 73 | 'F': WordType( 74 | cls=float, 75 | value_regex=REGEX_FLOAT, 76 | description="Feedrate", 77 | clean_value=CLEAN_FLOAT, 78 | ), 79 | # G-Codes 80 | 'G': WordType( 81 | cls=float, 82 | value_regex=REGEX_CODE, 83 | description="Address for preparatory commands", 84 | clean_value=CLEAN_CODE, 85 | ), 86 | # Tool Offsets 87 | 'H': WordType( 88 | cls=float, 89 | value_regex=REGEX_FLOAT, 90 | description="Defines tool length offset; Incremental axis corresponding to C axis (e.g., on a turn-mill)", 91 | clean_value=CLEAN_FLOAT, 92 | ), 93 | # Arc radius center coords 94 | 'I': WordType( 95 | cls=float, 96 | value_regex=REGEX_FLOAT, 97 | description="Defines arc center in X axis for G02 or G03 arc commands. Also used as a parameter within some fixed cycles.", 98 | clean_value=CLEAN_FLOAT, 99 | ), 100 | 'J': WordType( 101 | cls=float, 102 | value_regex=REGEX_FLOAT, 103 | description="Defines arc center in Y axis for G02 or G03 arc commands. Also used as a parameter within some fixed cycles.", 104 | clean_value=CLEAN_FLOAT, 105 | ), 106 | 'K': WordType( 107 | cls=float, 108 | value_regex=REGEX_FLOAT, 109 | description="Defines arc center in Z axis for G02 or G03 arc commands. Also used as a parameter within some fixed cycles, equal to L address.", 110 | clean_value=CLEAN_FLOAT, 111 | ), 112 | # Loop Count 113 | 'L': WordType( 114 | cls=int, 115 | value_regex=REGEX_POSITIVEINT, 116 | description="Fixed cycle loop count; Specification of what register to edit using G10", 117 | clean_value=CLEAN_INT, 118 | ), 119 | # Miscellaneous Function 120 | 'M': WordType( 121 | cls=float, 122 | value_regex=REGEX_CODE, 123 | description="Miscellaneous function", 124 | clean_value=CLEAN_CODE, 125 | ), 126 | # Line Number 127 | 'N': WordType( 128 | cls=int, 129 | value_regex=REGEX_POSITIVEINT, 130 | description="Line (block) number in program; System parameter number to change using G10", 131 | clean_value=CLEAN_INT, 132 | ), 133 | # Program Name 134 | 'O': WordType( 135 | cls=str, 136 | value_regex=re.compile(r'^.+$'), # all the way to the end 137 | description="Program name", 138 | clean_value=CLEAN_NONE, 139 | ), 140 | # Parameter (arbitrary parameter) 141 | 'P': WordType( 142 | cls=float, # parameter is often an integer, but can be a float 143 | value_regex=REGEX_FLOAT, 144 | description="Serves as parameter address for various G and M codes", 145 | clean_value=CLEAN_FLOAT, 146 | ), 147 | # Peck increment 148 | 'Q': WordType( 149 | cls=float, 150 | value_regex=REGEX_FLOAT, 151 | description="Depth to increase on each peck; Peck increment in canned cycles", 152 | clean_value=CLEAN_FLOAT, 153 | ), 154 | # Arc Radius 155 | 'R': WordType( 156 | cls=float, 157 | value_regex=REGEX_FLOAT, 158 | description="Defines size of arc radius, or defines retract height in milling canned cycles", 159 | clean_value=CLEAN_FLOAT, 160 | ), 161 | # Spindle speed 162 | 'S': WordType( 163 | cls=float, 164 | value_regex=REGEX_FLOAT, 165 | description="Defines speed, either spindle speed or surface speed depending on mode", 166 | clean_value=CLEAN_FLOAT, 167 | ), 168 | # Tool Selecton 169 | 'T': WordType( 170 | cls=str, 171 | value_regex=REGEX_POSITIVEINT, # tool string may have leading '0's, but is effectively an index (integer) 172 | description="Tool selection", 173 | clean_value=CLEAN_NONE, 174 | ), 175 | # Incremental axes 176 | 'U': WordType( 177 | cls=float, 178 | value_regex=REGEX_FLOAT, 179 | description="Incremental axis corresponding to X axis (typically only lathe group A controls) Also defines dwell time on some machines (instead of 'P' or 'X').", 180 | clean_value=CLEAN_FLOAT, 181 | ), 182 | 'V': WordType( 183 | cls=float, 184 | value_regex=REGEX_FLOAT, 185 | description="Incremental axis corresponding to Y axis", 186 | clean_value=CLEAN_FLOAT, 187 | ), 188 | 'W': WordType( 189 | cls=float, 190 | value_regex=REGEX_FLOAT, 191 | description="Incremental axis corresponding to Z axis (typically only lathe group A controls)", 192 | clean_value=CLEAN_FLOAT, 193 | ), 194 | # Linear Axes 195 | 'X': WordType( 196 | cls=float, 197 | value_regex=REGEX_FLOAT, 198 | description="Absolute or incremental position of X axis.", 199 | clean_value=CLEAN_FLOAT, 200 | ), 201 | 'Y': WordType( 202 | cls=float, 203 | value_regex=REGEX_FLOAT, 204 | description="Absolute or incremental position of Y axis.", 205 | clean_value=CLEAN_FLOAT, 206 | ), 207 | 'Z': WordType( 208 | cls=float, 209 | value_regex=REGEX_FLOAT, 210 | description="Absolute or incremental position of Z axis.", 211 | clean_value=CLEAN_FLOAT, 212 | ), 213 | } 214 | 215 | 216 | # ======================== G-CODES ======================== 217 | -------------------------------------------------------------------------------- /tests/test_machine.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | # Add relative pygcode to path 4 | from testutils import add_pygcode_to_path, str_lines 5 | add_pygcode_to_path() 6 | 7 | # Units under test 8 | from pygcode.machine import Position, Machine 9 | from pygcode.line import Line 10 | from pygcode.exceptions import MachineInvalidAxis 11 | from pygcode.gcodes import ( 12 | GCodeAbsoluteDistanceMode, GCodeIncrementalDistanceMode, 13 | GCodeAbsoluteArcDistanceMode, GCodeIncrementalArcDistanceMode, 14 | GCodeCannedCycleReturnPrevLevel, GCodeCannedCycleReturnToR, 15 | ) 16 | 17 | 18 | class PositionTests(unittest.TestCase): 19 | def test_basics(self): 20 | p = Position() 21 | # 22 | 23 | def test_default_axes(self): 24 | p = Position() # no instantiation parameters 25 | # all initialized to zero 26 | for axis in 'XYZABCUVW': 27 | self.assertEqual(getattr(p, axis), 0) 28 | 29 | for axis in 'XYZABCUVW': 30 | # set to 100 31 | setattr(p, axis, 100) 32 | self.assertEqual(getattr(p, axis), 100) 33 | for inner_axis in set('XYZABCUVW') - {axis}: # no other axis has changed 34 | self.assertEqual(getattr(p, inner_axis), 0), "axis '%s'" % inner_axis 35 | # revert back to zero 36 | setattr(p, axis, 0) 37 | self.assertEqual(getattr(p, axis), 0) 38 | 39 | # Equality 40 | def test_equality(self): 41 | p1 = Position(axes='XYZ', X=1, Y=2) 42 | p2 = Position(axes='XYZ', X=1, Y=2, Z=0) 43 | p3 = Position(axes='XYZ', X=1, Y=2, Z=1000) 44 | p4 = Position(axes='XYZA', X=1, Y=2, Z=0) 45 | 46 | # p1 <--> p2 47 | self.assertTrue(p1 == p2) 48 | self.assertFalse(p1 != p2) # negative case 49 | 50 | # p2 <--> p3 51 | self.assertTrue(p2 != p3) 52 | self.assertFalse(p2 == p3) # negative case 53 | 54 | # p2 <--> p4 55 | self.assertTrue(p2 != p4) 56 | self.assertFalse(p2 == p4) # negative case 57 | 58 | # Arithmetic 59 | def test_arithmetic_add(self): 60 | p1 = Position(axes='XYZ', X=1, Y=2) 61 | p2 = Position(axes='XYZ', Y=10, Z=-20) 62 | self.assertEqual(p1 + p2, Position(axes='XYZ', X=1, Y=12, Z=-20)) 63 | 64 | p3 = Position(axes='XYZA') 65 | with self.assertRaises(MachineInvalidAxis): 66 | p1 + p3 # mismatched axes 67 | with self.assertRaises(MachineInvalidAxis): 68 | p3 + p1 # mismatched axes 69 | 70 | def test_arithmetic_sub(self): 71 | p1 = Position(axes='XYZ', X=1, Y=2) 72 | p2 = Position(axes='XYZ', Y=10, Z=-20) 73 | self.assertEqual(p1 - p2, Position(axes='XYZ', X=1, Y=-8, Z=20)) 74 | 75 | p3 = Position(axes='XYZA') 76 | p3 - p1 # fine 77 | with self.assertRaises(MachineInvalidAxis): 78 | p1 - p3 # mismatched axes 79 | 80 | def test_arithmetic_multiply(self): 81 | p = Position(axes='XYZ', X=2, Y=10) 82 | self.assertEqual(p * 2, Position(axes='XYZ', X=4, Y=20)) 83 | 84 | def test_arithmetic_divide(self): 85 | p = Position(axes='XYZ', X=2, Y=10) 86 | self.assertEqual(p / 2, Position(axes='XYZ', X=1, Y=5)) 87 | 88 | 89 | class MachineGCodeProcessingTests(unittest.TestCase): 90 | def assert_processed_lines(self, line_data, machine): 91 | """ 92 | Process lines & assert machine's position 93 | :param line_data: list of tuples [('g1 x2', {'X':2}), ... ] 94 | """ 95 | for (i, (line_str, expected_pos)) in enumerate(line_data): 96 | line = Line(line_str) 97 | if line.block: 98 | machine.process_block(line.block) 99 | # Assert possition change correct 100 | if expected_pos is not None: 101 | p1 = machine.pos 102 | p2 = machine.Position(**expected_pos) 103 | self.assertEqual(p1, p2, "index:%i '%s': %r != %r" % (i, line_str, p1, p2)) 104 | 105 | # Rapid Movement 106 | def test_rapid_abs(self): 107 | m = Machine() 108 | m.process_gcodes(GCodeAbsoluteDistanceMode()) 109 | line_data = [ 110 | ('', {}), # start @ 0,0,0 111 | ('g0 x0 y10', {'X':0, 'Y':10}), 112 | (' x10 y10', {'X':10, 'Y':10}), 113 | (' x10 y0', {'X':10, 'Y':0}), 114 | (' x0 y0', {'X':0, 'Y':0}), 115 | ] 116 | self.assert_processed_lines(line_data, m) 117 | 118 | def test_rapid_inc(self): 119 | m = Machine() 120 | m.process_gcodes(GCodeIncrementalDistanceMode()) 121 | line_data = [ 122 | ('', {}), # start @ 0,0,0 123 | ('g0 y10', {'X':0, 'Y':10}), 124 | (' x10', {'X':10, 'Y':10}), 125 | (' y-10', {'X':10, 'Y':0}), 126 | (' x-10', {'X':0, 'Y':0}), 127 | ] 128 | self.assert_processed_lines(line_data, m) 129 | 130 | # Linearly Interpolated Movement 131 | def test_linear_abs(self): 132 | m = Machine() 133 | m.process_gcodes(GCodeAbsoluteDistanceMode()) 134 | line_data = [ 135 | ('g1 x0 y10', {'X':0, 'Y':10}), 136 | (' x10 y10', {'X':10, 'Y':10}), 137 | (' x10 y0', {'X':10, 'Y':0}), 138 | (' x0 y0', {'X':0, 'Y':0}), 139 | ] 140 | self.assert_processed_lines(line_data, m) 141 | 142 | def test_linear_inc(self): 143 | m = Machine() 144 | m.process_gcodes(GCodeIncrementalDistanceMode()) 145 | line_data = [ 146 | ('g1 y10', {'X':0, 'Y':10}), 147 | (' x10', {'X':10, 'Y':10}), 148 | (' y-10', {'X':10, 'Y':0}), 149 | (' x-10', {'X':0, 'Y':0}), 150 | ] 151 | self.assert_processed_lines(line_data, m) 152 | 153 | # Arc Movement 154 | def test_arc_abs(self): 155 | m = Machine() 156 | m.process_gcodes( 157 | GCodeAbsoluteDistanceMode(), 158 | GCodeIncrementalArcDistanceMode(), 159 | ) 160 | line_data = [ 161 | # Clockwise circle in 4 segments 162 | ('g2 x0 y10 i5 j5', {'X':0, 'Y':10}), 163 | (' x10 y10 i5 j-5', {'X':10, 'Y':10}), 164 | (' x10 y0 i-5 j-5', {'X':10, 'Y':0}), 165 | (' x0 y0 i-5 j5', {'X':0, 'Y':0}), 166 | # Counter-clockwise circle in 4 segments 167 | ('g3 x10 y0 i5 j5', {'X':10, 'Y':0}), 168 | (' x10 y10 i-5 j5', {'X':10, 'Y':10}), 169 | (' x0 y10 i-5 j-5', {'X':0, 'Y':10}), 170 | (' x0 y0 i5 j-5', {'X':0, 'Y':0}), 171 | ] 172 | self.assert_processed_lines(line_data, m) 173 | 174 | def test_arc_inc(self): 175 | m = Machine() 176 | m.process_gcodes( 177 | GCodeIncrementalDistanceMode(), 178 | GCodeIncrementalArcDistanceMode(), 179 | ) 180 | line_data = [ 181 | # Clockwise circle in 4 segments 182 | ('g2 y10 i5 j5', {'X':0, 'Y':10}), 183 | (' x10 i5 j-5', {'X':10, 'Y':10}), 184 | (' y-10 i-5 j-5', {'X':10, 'Y':0}), 185 | (' x-10 i-5 j5', {'X':0, 'Y':0}), 186 | # Counter-clockwise circle in 4 segments 187 | ('g3 x10 i5 j5', {'X':10, 'Y':0}), 188 | (' y10 i-5 j5', {'X':10, 'Y':10}), 189 | (' x-10 i-5 j-5', {'X':0, 'Y':10}), 190 | (' y-10 i5 j-5', {'X':0, 'Y':0}), 191 | ] 192 | self.assert_processed_lines(line_data, m) 193 | 194 | # Canned Drilling Cycles 195 | def test_canned_return2oldz(self): 196 | m = Machine() 197 | m.process_gcodes( 198 | GCodeAbsoluteDistanceMode(), 199 | GCodeCannedCycleReturnPrevLevel(), 200 | ) 201 | line_data = [ 202 | ('g0 z5', {'Z':5}), 203 | ('g81 x10 y20 z-2 r1', {'X':10, 'Y':20, 'Z':5}), 204 | ] 205 | self.assert_processed_lines(line_data, m) 206 | 207 | def test_canned_return2r(self): 208 | m = Machine() 209 | m.process_gcodes( 210 | GCodeAbsoluteDistanceMode(), 211 | GCodeCannedCycleReturnToR(), 212 | ) 213 | line_data = [ 214 | ('g0 z5', {'Z':5}), 215 | ('g81 x10 y20 z-2 r1', {'X':10, 'Y':20, 'Z':1}), 216 | ] 217 | self.assert_processed_lines(line_data, m) 218 | 219 | def test_canned_loops(self): 220 | m = Machine() 221 | m.process_gcodes( 222 | GCodeAbsoluteDistanceMode(), 223 | GCodeCannedCycleReturnPrevLevel(), 224 | ) 225 | line_data = [ 226 | ('g0 z5', None), 227 | ('g81 x10 y20 z-2 r1 l2', {'X':10, 'Y':20, 'Z':5}), 228 | ('g91', None), # switch to incremental mode 229 | ('g81 x10 y20 z-2 r1 l2', {'X':30, 'Y':60, 'Z':5}), 230 | ] 231 | self.assert_processed_lines(line_data, m) 232 | -------------------------------------------------------------------------------- /tests/test_gcodes.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import inspect 4 | import re 5 | import unittest 6 | 7 | # Add relative pygcode to path 8 | from testutils import add_pygcode_to_path, str_lines 9 | add_pygcode_to_path() 10 | 11 | # Units under test 12 | from pygcode import gcodes 13 | from pygcode import words 14 | from pygcode import machine 15 | 16 | from pygcode.exceptions import GCodeWordStrError 17 | 18 | class GCodeWordMappingTests(unittest.TestCase): 19 | def test_word_map_integrity(self): 20 | 21 | gcodes.build_maps() 22 | for (word_maches, fn_class) in gcodes._gcode_function_list: 23 | for (word, key_class) in gcodes._gcode_word_map.items(): 24 | # Verify that no mapped word will yield a True result 25 | # from any of the 'word_maches' functions 26 | self.assertFalse( 27 | word_maches(word), 28 | "conflict with %s and %s" % (fn_class, key_class) 29 | ) 30 | 31 | class GCodeModalGroupTests(unittest.TestCase): 32 | def test_modal_groups(self): 33 | # Modal groups taken (and slightly modified) from LinuxCNC documentation: 34 | # link: http://linuxcnc.org/docs/html/gcode/overview.html#_modal_groups 35 | table_rows = '' 36 | # Table 5. G-Code Modal Groups 37 | # MODAL GROUP MEANING MEMBER WORDS 38 | table_rows += ''' 39 | Non-modal codes (Group 0) G4,G10,G28,G30,G53,G92,G92.1,G92.2,G92.3 40 | Motion (Group 1) G0,G1,G2,G3,G33,G38.2,G38.3,G38.4 41 | Motion (Group 1) G38.5,G73,G76,G81,G82,G83,G85,G89 42 | Plane selection (Group 2) G17, G18, G19, G17.1, G18.1, G19.1 43 | Distance Mode (Group 3) G90, G91 44 | Arc IJK Distance Mode (Group 4) G90.1, G91.1 45 | Feed Rate Mode (Group 5) G93, G94, G95 46 | Units (Group 6) G20, G21 47 | Cutter Diameter Compensation (Group 7) G40, G41, G42, G41.1, G42.1 48 | Tool Length Offset (Group 8) G43, G43.1, G49 49 | Canned Cycles Return Mode (Group 10) G98 50 | Coordinate System (Group 12) G54,G55,G56,G57,G58,G59,G59.1,G59.2,G59.3 51 | Control Mode (Group 13) G61, G61.1, G64 52 | Spindle Speed Mode (Group 14) G96, G97 53 | Lathe Diameter Mode (Group 15) G7,G8 54 | ''' 55 | 56 | # Table 6. M-Code Modal Groups 57 | # MODAL GROUP MEANING MEMBER WORDS 58 | table_rows += re.sub(r'\(Group (\d+)\)', r'(Group 10\1)', ''' 59 | Stopping (Group 4) M0, M1, M2, M30, M60 60 | Spindle (Group 7) M3, M4, M5 61 | Coolant (Group 8) M7, M8, M9 62 | Override Switches (Group 9) M48, M49 63 | ''') # groups += 100 (to distinguish "M" GCodes from "G" GCodes) 64 | 65 | for row in table_rows.split('\n'): 66 | match = re.search(r'^\s*(?P.*)\s*\(Group (?P<group>\d+)\)\s*(?P<words>.*)$', row, re.I) 67 | if match: 68 | for word_str in re.split(r'\s*,\s*', match.group('words')): 69 | word = list(words.text2words(word_str))[0] 70 | gcode_class = gcodes.word_gcode_class(word) 71 | # GCode class found for each word in the table 72 | self.assertIsNotNone(gcode_class) 73 | # GCode's modal group equals that defined in the table 74 | expected_group = int(match.group('group')) 75 | if expected_group == 0: 76 | self.assertIsNone( 77 | gcode_class.modal_group, 78 | "%s modal_group: %s is not None" % (gcode_class, gcode_class.modal_group) 79 | ) 80 | else: 81 | self.assertEqual( 82 | gcode_class.modal_group, expected_group, 83 | "%s != %s (%r)" % (gcode_class.modal_group, expected_group, word) 84 | ) 85 | 86 | 87 | class Words2GCodesTests(unittest.TestCase): 88 | def test_stuff(self): # FIXME: function name 89 | line = 'G1 X82.6892 Y-38.6339 F1500' 90 | word_list = list(words.text2words(line)) 91 | result = gcodes.words2gcodes(word_list) 92 | # result form 93 | self.assertIsInstance(result, tuple) 94 | self.assertEqual(len(result), 2) 95 | # result content 96 | (gcode_list, unused_words) = result 97 | self.assertEqual(len(gcode_list), 2) 98 | self.assertEqual(unused_words, []) 99 | # Parsed GCodes 100 | # G1 101 | self.assertEqual(gcode_list[0].word, words.Word('G', 1)) 102 | self.assertEqual(gcode_list[0].X, 82.6892) 103 | self.assertEqual(gcode_list[0].Y, -38.6339) 104 | # F1500 105 | self.assertEqual(gcode_list[1].word, words.Word('F', 1500)) 106 | 107 | 108 | class Text2GCodesTests(unittest.TestCase): 109 | def test_basic(self): 110 | gcs = gcodes.text2gcodes('G1 X1 Y2 G90') 111 | self.assertEqual(len(gcs), 2) 112 | # G1 X1 Y2 113 | self.assertEqual(gcs[0].word, words.Word('G', 1)) 114 | self.assertEqual(gcs[0].X, 1) 115 | self.assertEqual(gcs[0].Y, 2) 116 | # G90 117 | self.assertEqual(gcs[1].word, words.Word('G', 90)) 118 | 119 | def test_modal_params(self): 120 | with self.assertRaises(GCodeWordStrError): 121 | gcodes.text2gcodes('X1 Y2') 122 | 123 | 124 | class GCodeSplitTests(unittest.TestCase): 125 | 126 | def test_split(self): 127 | g_list = gcodes.text2gcodes('G91 S1000 G1 X1 Y2 M3') 128 | split = gcodes.split_gcodes(g_list, gcodes.GCodeStartSpindle) 129 | self.assertEqual([len(x) for x in split], [1, 1, 2]) 130 | self.assertTrue(any(isinstance(g, gcodes.GCodeSpindleSpeed) for g in split[0])) 131 | self.assertTrue(isinstance(split[1][0], gcodes.GCodeStartSpindle)) 132 | self.assertTrue(any(isinstance(g, gcodes.GCodeDistanceMode) for g in split[2])) 133 | self.assertTrue(any(isinstance(g, gcodes.GCodeMotion) for g in split[2])) 134 | 135 | def test_split_unsorted(self): 136 | g_list = gcodes.text2gcodes('G91 G1 X1 Y2 M3 S1000') 137 | split = gcodes.split_gcodes(g_list, gcodes.GCodeStartSpindle, sort_list=False) 138 | self.assertEqual([len(x) for x in split], [2, 1, 1]) 139 | self.assertTrue(any(isinstance(g, gcodes.GCodeDistanceMode) for g in split[0])) 140 | self.assertTrue(any(isinstance(g, gcodes.GCodeMotion) for g in split[0])) 141 | self.assertTrue(isinstance(split[1][0], gcodes.GCodeStartSpindle)) 142 | self.assertTrue(any(isinstance(g, gcodes.GCodeSpindleSpeed) for g in split[2])) 143 | 144 | 145 | class GCodeAbsoluteToRelativeDecoratorTests(unittest.TestCase): 146 | 147 | def test_gcodes_abs2rel(self): 148 | # setup gcode testlist 149 | L = gcodes.GCodeLinearMove 150 | R = gcodes.GCodeRapidMove 151 | args = lambda x, y, z: dict(a for a in zip('XYZ', [x,y,z]) if a[1] is not None) 152 | gcode_list = [ 153 | # GCode instances Expected incremental output 154 | (L(**args(0, 0, 0)), L(**args(-10, -20, -30))), 155 | (L(**args(1, 2, 0)), L(**args(1, 2, None))), 156 | (L(**args(3, 4, 0)), L(**args(2, 2, None))), 157 | (R(**args(1, 2, 0)), R(**args(-2, -2, None))), 158 | (R(**args(3, 4, 0)), R(**args(2, 2, None))), 159 | (L(**args(3, 4, 0)), None), 160 | (L(**args(3, 4, 8)), L(**args(None, None, 8))), 161 | ] 162 | 163 | m = machine.Machine() 164 | 165 | # Incremental Output 166 | m.set_mode(gcodes.GCodeAbsoluteDistanceMode()) 167 | m.move_to(X=10, Y=20, Z=30) # initial position (absolute) 168 | m.set_mode(gcodes.GCodeIncrementalDistanceMode()) 169 | 170 | @gcodes._gcodes_abs2rel(m.pos, dist_mode=m.mode.distance, axes=m.axes) 171 | def expecting_rel(): 172 | return [g[0] for g in gcode_list] 173 | 174 | trimmed_expecting_list = [x[1] for x in gcode_list if x[1] is not None] 175 | for (i, g) in enumerate(expecting_rel()): 176 | expected = trimmed_expecting_list[i] 177 | self.assertEqual(type(g), type(expected)) 178 | self.assertEqual(g.word, expected.word) 179 | self.assertEqual(g.params, expected.params) 180 | 181 | # Absolute Output 182 | m.set_mode(gcodes.GCodeAbsoluteDistanceMode()) 183 | m.move_to(X=10, Y=20, Z=30) # initial position 184 | 185 | @gcodes._gcodes_abs2rel(m.pos, dist_mode=m.mode.distance, axes=m.axes) 186 | def expecting_abs(): 187 | return [g[0] for g in gcode_list] 188 | 189 | for (i, g) in enumerate(expecting_abs()): 190 | expected = gcode_list[i][0] # expecting passthrough 191 | self.assertEqual(type(g), type(expected)) 192 | self.assertEqual(g.word, expected.word) 193 | self.assertEqual(g.params, expected.params) 194 | -------------------------------------------------------------------------------- /scripts/pygcode-crop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Script to remove commands of a gcode file before and after the given range. 4 | # All gcodes before the given line will be replaced with equivalent rapid 5 | # movements to get to the right position. Initial rapid movement will first move 6 | # to the maximum Z value used in the removed portion, then move to XY, 7 | # then move down to the correct Z. 8 | 9 | import argparse 10 | import re 11 | from copy import copy 12 | 13 | for pygcode_lib_type in ('installed_lib', 'relative_lib'): 14 | try: 15 | # pygcode 16 | from pygcode import Machine, Mode 17 | from pygcode import Line, Comment 18 | from pygcode import GCodePlaneSelect, GCodeSelectXYPlane 19 | from pygcode import GCodeRapidMove 20 | 21 | except ImportError: 22 | import sys, os, inspect 23 | # Add pygcode (relative to this test-path) to the system path 24 | _this_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 25 | sys.path.insert(0, os.path.join(_this_path, '..', 'src')) 26 | if pygcode_lib_type == 'installed_lib': 27 | continue # import was attempted before sys.path addition. retry import 28 | raise # otherwise the raised ImportError is a genuine problem 29 | break 30 | 31 | 32 | # =================== Command Line Arguments =================== 33 | # --- Types 34 | def range_type(value): 35 | """ 36 | Return (is_first, is_last) such that is_first(n, pos) will return True if 37 | the gcode's current line is the first to be cropped, and similarly for the 38 | last line. 39 | :param value: string given as argument 40 | :return: (is_first, is_last) callables 41 | """ 42 | # All files are cropped from one line, to another, identifying these lines is 43 | # done via the [first] and [last] cropping criteria. 44 | # - Line numbers (parameters: n) 45 | # - Machine position (parameters: a,b,c,x,y,z) 46 | # Comparisons, all with <letter><comparison><number> 47 | # - = (or ==) equal to 48 | # - != not equal to 49 | # - < less than 50 | # - <= less than or equal to 51 | # - > greater than 52 | # - >= greater than or equal to 53 | match = re.search(r'^(?P<first>[^:]*):(?P<last>[^:]*)$', value) 54 | if not match: 55 | raise argparse.ArgumentTypeError("'%s' range invalid format" % value) 56 | 57 | def _cmp(cmp_str): 58 | """ 59 | Convert strings like 60 | 'x>10.3' 61 | into a callable equivalent to: 62 | lambda n, pos: pos.X > 10.3 63 | where: 64 | n is the file's line number 65 | pos is the machine's position (Position) instance 66 | :param cmp_str: comparison string of the form: '<param><cmp><value>' 67 | :return: callable 68 | """ 69 | CMP_MAP = { 70 | '=': lambda a, b: a == b, 71 | '==': lambda a, b: a == b, 72 | '!=': lambda a, b: a != b, 73 | '<': lambda a, b: a < b, 74 | '<=': lambda a, b: a <= b, 75 | '>': lambda a, b: a > b, 76 | '>=': lambda a, b: a >= b, 77 | } 78 | # split comparison into (param, cmp, value) 79 | m = re.search( 80 | r'''^\s* 81 | ( 82 | (?P<param>[abcnxyz])?\s* # parameter 83 | (?P<cmp>(==?|!=|<=?|>=?)) # comparison 84 | )?\s* # parameter & comparison defaults to "n=" 85 | (?P<value>-?\d+(\.\d+)?)\s* 86 | $''', 87 | cmp_str, re.IGNORECASE | re.MULTILINE | re.VERBOSE 88 | ) 89 | if not m: 90 | raise argparse.ArgumentTypeError("'%s' range comparison invalid" % cmp_str) 91 | (param, cmp, val) = ( 92 | (m.group('param') or 'N').upper(), # default to 'N' 93 | m.group('cmp') or '=', # default to '=' 94 | m.group('value') 95 | ) 96 | 97 | # convert to lambda 98 | if param == 'N': 99 | if float(val) % 1: 100 | raise argparse.ArgumentTypeError("'%s' line number must be an integer" % cmp_str) 101 | return lambda n, pos: CMP_MAP[cmp](n, float(val)) 102 | else: 103 | return lambda n, pos: CMP_MAP[cmp](getattr(pos, param), float(val)) 104 | 105 | def _cmp_group(group_str, default): 106 | """ 107 | Split given group_str by ',' and return callable that will return True 108 | only if all comparisons are true. 109 | So if group_str is: 110 | x>=10.4,z>1 111 | return will be a callable equivalent to: 112 | lambda n, pos: (pos.X >= 10.4) and (pos.Z > 1) 113 | (see _cmp for more detail) 114 | :param group_str: string of _cmp valid strings delimited by ','s 115 | :param default: default callable if group_str is falsey 116 | :return: callable that returns True if all cmp's are true 117 | """ 118 | if not group_str: 119 | return default 120 | cmp_list = [] 121 | for cmp_str in group_str.split(','): 122 | cmp_list.append(_cmp(cmp_str)) 123 | return lambda n, pos: all(x(n, pos) for x in cmp_list) 124 | 125 | 126 | is_first = _cmp_group(match.group('first'), lambda n, pos: True) 127 | is_last = _cmp_group(match.group('last'), lambda n, pos: False) 128 | 129 | return (is_first, is_last) 130 | 131 | 132 | # --- Defaults 133 | 134 | 135 | # --- Create Parser 136 | parser = argparse.ArgumentParser( 137 | description="Remove gcode before and after given 'from' and 'to' conditions.", 138 | epilog="Range Format:" 139 | """ 140 | range must be of the format: 141 | [condition[,condition...]]:[condition[,condition...]] 142 | the first condition(s) are true for the first line included in the cropped area 143 | the second set are true for the first line excluded after the cropped area 144 | 145 | Conditions: 146 | each condition is of the format: 147 | {variable}{operation}{number} 148 | or, more specifically: 149 | [[{a,b,c,n,x,y,z}]{=,!=,<,<=,>,>=}]{number} 150 | 151 | Condition Variables: 152 | n - file's line number 153 | a|b|c - machine's angular axes 154 | x|y|z - machine's linear axes 155 | 156 | Example Ranges: 157 | "100:200" will crop lines 100-199 (inclusive) 158 | "z<=-2:" will isolate everything after the machine crosses z=-2 159 | "x>10,y>10:n>=123" starts cropped area where both x and y exceed 10, 160 | but only before line 123 161 | 162 | Limitations: 163 | Only takes points from start and finish of a gcode operation, so a line 164 | through a condition region, or an arc that crosses a barrier will NOT 165 | trigger the start or stop of cropping. 166 | Probe alignment operations will not change virtual machine's position. 167 | """, 168 | formatter_class=argparse.RawTextHelpFormatter, 169 | ) 170 | parser.add_argument( 171 | 'infile', type=argparse.FileType('r'), 172 | help="gcode file to crop", 173 | ) 174 | parser.add_argument( 175 | 'range', type=range_type, 176 | help="file range to crop, format [from]:[to] (details below)", 177 | ) 178 | 179 | 180 | # --- Parse Arguments 181 | args = parser.parse_args() 182 | 183 | 184 | # =================== Cropping File =================== 185 | 186 | # --- Machine 187 | class NullMachine(Machine): 188 | MODE_CLASS = type('NullMode', (Mode,), {'default_mode': ''}) 189 | 190 | machine = NullMachine() 191 | 192 | pre_crop = True 193 | post_crop = False 194 | 195 | (is_first, is_last) = args.range 196 | 197 | for (i, line_str) in enumerate(args.infile.readlines()): 198 | line = Line(line_str) 199 | 200 | # remember machine's state before processing the current line 201 | old_machine = copy(machine) 202 | machine.process_block(line.block) 203 | 204 | if pre_crop: 205 | if is_first(i + 1, machine.pos): 206 | # First line inside cropping range 207 | pre_crop = False 208 | 209 | # Set machine's accumulated mode (from everything that's been cut) 210 | mode_str = str(old_machine.mode) 211 | if mode_str: 212 | print(Comment("machine mode before cropping")) 213 | print(mode_str) 214 | 215 | # Getting machine's current (modal) selected plane 216 | plane = old_machine.mode.plane_selection 217 | if not isinstance(plane, GCodePlaneSelect): 218 | plane = GCodeSelectXYPlane() # default to XY plane 219 | 220 | # --- position machine before first cropped line 221 | print(Comment("traverse into position, up, over, and down")) 222 | # rapid move to Z (maximum Z the machine has experienced thus far) 223 | print(GCodeRapidMove(**{ 224 | plane.normal_axis: getattr(old_machine.abs_range_max, plane.normal_axis), 225 | })) 226 | # rapid move to X,Y 227 | print(GCodeRapidMove(**dict( 228 | (k, v) for (k, v) in old_machine.pos.values.items() 229 | if k in plane.plane_axes 230 | ))) 231 | # rapid move to Z (machine.pos.Z) 232 | print(GCodeRapidMove(**{ 233 | plane.normal_axis: getattr(old_machine.pos, plane.normal_axis), 234 | })) 235 | print('') 236 | 237 | if (pre_crop, post_crop) == (False, False): 238 | if is_last(i + 1, machine.pos): 239 | # First line **outside** the area being cropped 240 | # (ie: this line won't be output) 241 | post_crop = True # although, irrelevant because... 242 | break 243 | else: 244 | # inside cropping area 245 | print(line) 246 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/Horse.tap: -------------------------------------------------------------------------------- 1 | ( Horse ) 2 | N100G00G21G17G90G40G49G80 3 | N110G91.1 4 | N120T1M06 5 | N130(ENGRAVING - 20 DEG TIP 0.1) 6 | N140G00G43Z6H1 7 | N150S12000M03 8 | N160 9 | N170 10 | N180G94 11 | N190X0Y0F600.0 12 | N200G00X.25Y1.75 13 | N210G1Z-0.2 14 | N220G2X.2Y2.5I.3J.4 15 | N230G3X1.85Y4.5I-9J9 16 | N240G3X.5Y6.75I-1.25J.8 17 | N250G2X-.3Y7.6I-.05J.75 18 | N260G2X1.4Y13.3I19.05J-2.6 19 | N270G3X2.35Y16.05I-12.45J5.95 20 | N280G3X4.05Y24.7I-120.15J28.25 21 | N290G1X4.3Y25.2 22 | N300G3X4.15Y25.8I-.25J.25 23 | N310G3X3.65Y25.4I-.1J-.35 24 | N320G2X3.35Y23.15I-8.95J.05 25 | N330G1X2.95Y21.6 26 | N340G2X2.55Y21.4I-.3J.05 27 | N350G1X3.05Y22.85 28 | N360G1X2.4Y21.7 29 | N370G1X2.15Y21.5 30 | N380G1X1.95Y21.55 31 | N390G1X2.75Y22.75 32 | N400G1X2.9Y23.1 33 | N410G1X2.8Y23.2 34 | N420G3X2.5Y22.95I-.05J-.25 35 | N430G2X1.75Y21.65I-2.15J.3 36 | N440G2X1.45Y21.95I-.15J.15 37 | N450G3X2.25Y23.2I-1.6J1.85 38 | N460G2X3.4Y26I9.15J-2.25 39 | N470G3X3.1Y26.7I-.4J.25 40 | N480G1X2.95Y26.65 41 | N490G1X2.9Y26.1 42 | N500G2X2Y24.25I-5.8J1.6 43 | N510G2X1.05Y23.5I-1.65J1.15 44 | N520G1X.95Y23.5 45 | N530G1X.9Y23.6 46 | N540G3X1.8Y24.6I-1.65J2.45 47 | N550G3X3.05Y27.65I-10.35J5.95 48 | N560G2X3.8Y29.1I3.3J-.8 49 | N570G3X3.75Y29.45I-.2J.15 50 | N580G3X3.4Y29.35I-.15J-.2 51 | N590G3X3.05Y28.15I3.65J-1.7 52 | N600G1X2.8Y27.5 53 | N610G1X2.7Y27.55 54 | N620G1X2.8Y27.9 55 | N630G1X3.4Y30.3 56 | N640G3X3.5Y31.6I-2.7J.9 57 | N650G3X3.35Y31.65I-.1J0 58 | N660G1X3.2Y31.2 59 | N670G1X2.9Y30.1 60 | N680G2X2.6Y30.65I.05J.4 61 | N690G3X3.15Y32.85I-10.65J3.85 62 | N700G1X3.75Y35.6 63 | N710G1X5.1Y39.7 64 | N720G2X5.5Y40.25I1.1J-.4 65 | N730G3X5.45Y40.95I-.25J.35 66 | N740G1X4.65Y39.25 67 | N750G2X4.45Y40.2I.15J.5 68 | N760G3X6.2Y43.4I-2.85J3.65 69 | N770G1X6.4Y44.55 70 | N780G1X6.55Y45.7 71 | N790G1X6.55Y45.95 72 | N800G2X6.75Y45.95I.1J0 73 | N810G1X6.65Y45.85 74 | N820G1X6.55Y45.75 75 | N830G1X6.55Y45.7 76 | N840G1X6.4Y44.45 77 | N850G2X6.15Y44.25I-.2J0 78 | N860G2X6Y44.5I0J.2 79 | N870G1X6.25Y45.75 80 | N880G1X6.65Y47 81 | N890G1X7.3Y48.6 82 | N900G3X6.8Y48.85I-.25J.1 83 | N910G1X6.55Y48.05 84 | N920G1X6.25Y47.25 85 | N930G1X6Y46.9 86 | N940G1X5.95Y46.85 87 | N950G1X5.95Y46.7 88 | N960G1X5.95Y46.5 89 | N970G1X5.9Y46.3 90 | N980G1X5.8Y46.3 91 | N990G1X5.8Y46.35 92 | N1000G1X5.85Y46.4 93 | N1010G1X5.95Y46.75 94 | N1020G1X5.95Y47.05 95 | N1030G1X6.25Y48.35 96 | N1040G1X7Y50.1 97 | N1050G3X6.9Y50.4I-.2J.1 98 | N1060G3X6.6Y50.3I-.1J-.2 99 | N1070G1X5.9Y49.1 100 | N1080G3X5.85Y48.4I.55J-.4 101 | N1090G1X5.75Y48.25 102 | N1100G1X5.65Y48.35 103 | N1110G2X6.25Y50.6I6.3J-.55 104 | N1120G3X7.25Y54.8I-8.5J4.2 105 | N1130G1X7.15Y54.95 106 | N1140G1X7Y54.8 107 | N1150G1X6.95Y54.45 108 | N1160G1X6.85Y53.95 109 | N1170G2X6.65Y53.8I-.2J0 110 | N1180G2X6.5Y54I0J.2 111 | N1190G1X6.6Y54.5 112 | N1200G3X7.05Y56.35I-4.7J2.05 113 | N1210G2X7.9Y59.45I7.1J-.3 114 | N1220G3X7.6Y60.2I-.5J.25 115 | N1230G1X7.45Y60.15 116 | N1240G1X7.15Y59.4 117 | N1250G2X6.9Y59.35I-.15J.1 118 | N1260G2X6.85Y59.6I.1J.15 119 | N1270G1X7.45Y60.55 120 | N1280G3X8.25Y64.85I-7.3J3.6 121 | N1290G2X12.15Y71.9I7.25J.6 122 | N1300G2X23.2Y75.55I16.65J-32.2 123 | N1310G2X36.7Y75.75I7.5J-48.2 124 | N1320G3X58.4Y74.4I22.25J180.2 125 | N1330G3X66.05Y75.65I.05J23 126 | N1340G2X73.8Y76.55I5.9J-17 127 | N1350G2X78.25Y74.4I-.75J-7.15 128 | N1360G3X78.5Y74.5I.1J.1 129 | N1370G3X78.4Y74.95I-.45J.1 130 | N1380G1X78.1Y75.15 131 | N1390G1X77.85Y75.4 132 | N1400G1X77.25Y75.85 133 | N1410G2X78Y75.95I.4J-.2 134 | N1420G3X80.05Y73.9I10.6J8.55 135 | N1430G2X83.6Y70.7I-28.3J-34.85 136 | N1440G2X90.6Y63.3I-143J-141.9 137 | N1450G2X92Y61.4I-9.1J-8.15 138 | N1460G3X93.15Y60.9I.95J.55 139 | N1470G2X93.55Y60.35I.1J-.35 140 | N1480G3X93.65Y59.75I.45J-.25 141 | N1490G1X94.9Y58.5 142 | N1500G3X95.3Y58.4I.3J.25 143 | N1510G1X95.3Y58.8 144 | N1520G2X95.55Y58.6I0J-.2 145 | N1530G3X95.95Y57.55I1.75J.1 146 | N1540G2X99.7Y52.6I-38.9J-33.25 147 | N1550G2X100.9Y50.1I-8.7J-5.75 148 | N1560G2X101.75Y45.9I-16.3J-5.3 149 | N1570G3X104.2Y38.45I15.75J1.1 150 | N1580G2X105Y36.4I-4.5J-2.9 151 | N1590G3X106.15Y36.05I.7J.1 152 | N1600G2X107.8Y36.95I2.1J-1.8 153 | N1610G2X108.15Y36.35I.05J-.35 154 | N1620G1X107.8Y36 155 | N1630G3X107.3Y35.25I.85J-1.1 156 | N1640G1X107.3Y34.75 157 | N1650G1X107.45Y34.7 158 | N1660G1X110.25Y37.15 159 | N1670G2X111.15Y36.4I.4J-.45 160 | N1680G1X110.75Y35.8 161 | N1690G3X109.35Y33.3I5.25J-4.5 162 | N1700G2X108.2Y32.05I-1.85J.55 163 | N1710G3X107.35Y30.6I.5J-1.3 164 | N1720G2X109.15Y9.65I-310J-37.3 165 | N1730G2X108.65Y6.15I-8.85J-.45 166 | N1740G2X107.3Y5.1I-1.55J.55 167 | N1750G2X101Y5.15I-2.95J21.8 168 | N1760G2X100.15Y5.65I.2J1.35 169 | N1770G2X98.9Y8.15I5.35J4.2 170 | N1780G2X98.8Y9.7I4.1J1.1 171 | N1790G3X98.4Y11.25I-2.05J.25 172 | N1800G2X95.8Y14.85I37.3J29.25 173 | N1810G3X93.2Y18.5I-34.35J-22.05 174 | N1820G2X92.65Y23.6I3.65J2.95 175 | N1830G3X92.3Y33.15I-8.65J4.45 176 | N1840G1X86.4Y42.65 177 | N1850G3X83.9Y44.05I-2.5J-1.6 178 | N1860G3X83.2Y43.55I0J-.7 179 | N1870G2X81.45Y40.9I-5.35J1.6 180 | N1880G1X79.55Y39.45 181 | N1890G3X77.9Y37.75I3J-4.5 182 | N1900G2X76.9Y36.6I-3.55J2.15 183 | N1910G3X76.05Y35.4I1.6J-2.1 184 | N1920G3X75.95Y32.5I4.65J-1.6 185 | N1930G2X76.7Y20.9I-27.25J-7.55 186 | N1940G3X77Y12.95I21.6J-3.15 187 | N1950G3X78.75Y9I10.8J2.4 188 | N1960G3X80.5Y7.35I4.95J3.55 189 | N1970G2X82.1Y5.6I-2.5J-3.95 190 | N1980G1X83Y4.1 191 | N1990G2X82.45Y2.75I-.75J-.45 192 | N2000G2X79.9Y2.35I-3.45J13.3 193 | N2010G2X77.5Y4I-.15J2.35 194 | N2020G3X77.1Y4.2I-.3J-.1 195 | N2030G2X76.5Y5.75I-.15J.85 196 | N2040G3X76.05Y7I-.4J.6 197 | N2050G2X73.7Y7.4I-.45J4.5 198 | N2060G2X73.25Y8.05I.3J.7 199 | N2070G1X73.1Y11.1 200 | N2080G1X72.45Y17.85 201 | N2090G1X71.3Y26.75 202 | N2100G1X71.35Y27.45 203 | N2110G3X70.95Y28I-.45J.1 204 | N2120G3X70.35Y27.55I-.05J-.55 205 | N2130G2X69.5Y24.7I-12.95J2.25 206 | N2140G1X63.65Y10.85 207 | N2150G3X64.05Y5.55I5.35J-2.25 208 | N2160G2X65.9Y2.15I-27.8J-17.15 209 | N2170G2X64.95Y.55I-1J-.45 210 | N2180G2X60.45Y1I-.95J14.2 211 | N2190G2X59.65Y1.95I.25J1 212 | N2200G3X58.8Y2.8I-.85J0 213 | N2210G2X58.45Y3.3I0J.4 214 | N2220G2X59.6Y5.7I9.55J-3 215 | N2230G3X59.5Y8.35I-1.95J1.25 216 | N2240G1X59.25Y8.55 217 | N2250G2X58.55Y9.5I.15J.85 218 | N2260G2X59.6Y13.1I13J-1.9 219 | N2270G3X64.75Y27.1I-85.8J39.55 220 | N2280G2X68.5Y36.9I57.75J-16.2 221 | N2290G3X67.2Y39.4I-1.6J.75 222 | N2300G3X62.85Y39.4I-2.05J-12.45 223 | N2310G2X45.6Y40.2I-6.85J37.85 224 | N2320G2X39Y42.9I8.25J29.4 225 | N2330G2X33.85Y46.75I12.1J21.8 226 | N2340G3X32.05Y46.1I-.75J-.75 227 | N2350G2X30.7Y42.95I-5.95J.7 228 | N2360G3X28.7Y39.45I8.85J-7.4 229 | N2370G3X24.2Y22.45I91.3J-33.1 230 | N2380G3X24.35Y15.65I17.85J-3 231 | N2390G3X27.25Y8.25I24.15J5.2 232 | N2400G3X30.85Y4.75I8.2J4.85 233 | N2410G2X33.6Y.55I-2.6J-4.65 234 | N2420G2X32.75Y-.25I-.75J-.05 235 | N2430G3X28.3Y.05I-4.3J-29.6 236 | N2440G2X25.85Y.35I-.05J10.15 237 | N2450G2X25.55Y.75I.1J.4 238 | N2460G2X25.7Y1.7I2.45J.1 239 | N2470G3X25.6Y2.7I-1.05J.4 240 | N2480G1X25.55Y3.1 241 | N2490G3X24.75Y4.5I-1J.35 242 | N2500G1X22.55Y4.95 243 | N2510G2X20.9Y6.45I.55J2.25 244 | N2520G2X19.75Y13.5I18.15J6.5 245 | N2530G3X17.15Y26.45I-29.6J.8 246 | N2540G1X16.8Y26.9 247 | N2550G2X16.6Y27.45I.35J.45 248 | N2560G2X17.45Y29.6I6.3J-1.25 249 | N2570G3X20Y38.6I-14.1J8.8 250 | N2580G2X20.2Y39.7I3.1J.05 251 | N2590G3X19.6Y40I-.3J.1 252 | N2600G1X18.45Y38.7 253 | N2610G3X17.7Y37.15I1.8J-1.85 254 | N2620G3X18.25Y36.45I.65J-.05 255 | N2630G2X18.4Y35.55I-.05J-.45 256 | N2640G2X17.8Y36.3I-.25J.45 257 | N2650G3X17.55Y36.75I-.25J.2 258 | N2660G3X17.05Y36.15I.05J-.5 259 | N2670G1X17.75Y33.35 260 | N2680G1X18.15Y32.45 261 | N2690G2X18.1Y32.2I-.15J-.1 262 | N2700G1X17.35Y33.45 263 | N2710G1X17.8Y31.9 264 | N2720G1X17.75Y30.85 265 | N2730G2X17.5Y31.05I0J.2 266 | N2740G1X17.3Y32.2 267 | N2750G1X17.15Y33.2 268 | N2760G3X16.85Y34.15I-1.95J-.1 269 | N2770G3X16.6Y34.1I-.1J-.05 270 | N2780G3X16.65Y32.7I3.8J-.5 271 | N2790G1X17.3Y30.65 272 | N2800G1X17.4Y30.15 273 | N2810G1X17.3Y29.65 274 | N2820G1X17.05Y29.15 275 | N2830G1X16.85Y28.6 276 | N2840G1X16.8Y28.7 277 | N2850G1X16.8Y28.85 278 | N2860G3X16.05Y33I-11.55J0 279 | N2870G3X15.5Y33.25I-.4J-.15 280 | N2880G3X15.2Y32.6I.15J-.45 281 | N2890G2X16.05Y29.95I-11.5J-5.15 282 | N2900G1X16.4Y29.1 283 | N2910G1X16.7Y28.25 284 | N2920G1X16.6Y27.85 285 | N2930G3X16.4Y26.35I1.35J-.95 286 | N2940G1X16.6Y26.05 287 | N2950G1X16.8Y25.7 288 | N2960G1X17.55Y21.8 289 | N2970G1X16.65Y25.2 290 | N2980G3X16.3Y24.6I.1J-.45 291 | N2990G2X16.85Y22I-9.2J-3.25 292 | N3000G1X16.9Y21.75 293 | N3010G1X16.95Y21.5 294 | N3020G1X16.95Y21.3 295 | N3030G1X17Y21.25 296 | N3040G1X17.1Y21.3 297 | N3050G1X17.05Y21.35 298 | N3060G1X17Y21.45 299 | N3070G1X16.95Y21.5 300 | N3080G2X17.05Y22.75I2.95J.4 301 | N3090G3X16.6Y23.3I-.4J.1 302 | N3100G1X16.9Y21.05 303 | N3110G2X16.5Y21.4I-.1J.3 304 | N3120G3X16.2Y23.5I-4.85J.45 305 | N3130G2X16.2Y24.15I1J.35 306 | N3140G3X15.75Y24.6I-.35J.1 307 | N3150G1X16.1Y21.75 308 | N3160G2X15.65Y22.15I-.05J.35 309 | N3170G3X15.45Y24.35I-6.55J.55 310 | N3180G2X15Y28.05I14.1J3.75 311 | N3190G3X14.45Y28.8I-.85J0 312 | N3200G3X13.85Y28.3I-.15J-.4 313 | N3210G1X14.35Y25.6 314 | N3220G1X14.55Y24.75 315 | N3230G1X15.45Y21.35 316 | N3240G2X15.2Y21.3I-.1J-.05 317 | N3250G2X14.7Y23.05I6J2.8 318 | N3260G1X14.3Y24.35 319 | N3270G1X14.1Y24.45 320 | N3280G1X14.2Y21.9 321 | N3290G1X14Y22.75 322 | N3300G1X14.05Y21.9 323 | N3310G1X13.95Y21.6 324 | N3320G1X13.9Y21.6 325 | N3330G1X13.05Y26.75 326 | N3340G1X12.65Y28.55 327 | N3350G3X12.15Y28.5I-.25J-.05 328 | N3360G1X11.85Y26.95 329 | N3370G1X11.3Y25.75 330 | N3380G3X10.8Y24.55I2.55J-1.7 331 | N3390G1X10.85Y24.05 332 | N3400G1X11.25Y22.1 333 | N3410G1X11.55Y21.55 334 | N3420G2X11.4Y20.95I-.25J-.25 335 | N3430G2X11.1Y21.1I-.05J.25 336 | N3440G2X10.7Y22.6I5.25J2.2 337 | N3450G2X10.75Y24.05I5.15J.65 338 | N3460G3X10.55Y24.4I-.3J.05 339 | N3470G3X9.8Y24.1I-.2J-.6 340 | N3480G3X8.1Y20I16.95J-9.45 341 | N3490G3X6.35Y10.3I47.2J-13.5 342 | N3500G3X8Y2.25I14.85J-1.15 343 | N3510G2X8.05Y.8I-1.4J-.75 344 | N3520G2X7Y.1I-1.15J.55 345 | N3530G1X3.6Y-.2 346 | N3540G2X1.45Y1.8I-.15J2 347 | N3550G3X.95Y1.9I-.25J0 348 | N3560G2X.25Y1.75I-.4J.25 349 | N3570G00X.25Y1.75 350 | N3580G00Z6 351 | N3590G00X0Y0 352 | N3600M05 353 | N3610M30 354 | N3620% 355 | -------------------------------------------------------------------------------- /tests/test-files/linuxcnc/unsupported/Horse.tap: -------------------------------------------------------------------------------- 1 | ( Horse ) 2 | N100G00G21G17G90G40G49G80 3 | N110G71G91.1 4 | N120T1M06 5 | N130(ENGRAVING - 20 DEG TIP 0.1) 6 | N140G00G43Z6H1 7 | N150S12000M03 8 | N160 9 | N170 10 | N180G94 11 | N190X0Y0F600.0 12 | N200G00X.25Y1.75 13 | N210G1Z-0.2 14 | N220G2X.2Y2.5I.3J.4 15 | N230G3X1.85Y4.5I-9J9 16 | N240G3X.5Y6.75I-1.25J.8 17 | N250G2X-.3Y7.6I-.05J.75 18 | N260G2X1.4Y13.3I19.05J-2.6 19 | N270G3X2.35Y16.05I-12.45J5.95 20 | N280G3X4.05Y24.7I-120.15J28.25 21 | N290G1X4.3Y25.2 22 | N300G3X4.15Y25.8I-.25J.25 23 | N310G3X3.65Y25.4I-.1J-.35 24 | N320G2X3.35Y23.15I-8.95J.05 25 | N330G1X2.95Y21.6 26 | N340G2X2.55Y21.4I-.3J.05 27 | N350G1X3.05Y22.85 28 | N360G1X2.4Y21.7 29 | N370G1X2.15Y21.5 30 | N380G1X1.95Y21.55 31 | N390G1X2.75Y22.75 32 | N400G1X2.9Y23.1 33 | N410G1X2.8Y23.2 34 | N420G3X2.5Y22.95I-.05J-.25 35 | N430G2X1.75Y21.65I-2.15J.3 36 | N440G2X1.45Y21.95I-.15J.15 37 | N450G3X2.25Y23.2I-1.6J1.85 38 | N460G2X3.4Y26I9.15J-2.25 39 | N470G3X3.1Y26.7I-.4J.25 40 | N480G1X2.95Y26.65 41 | N490G1X2.9Y26.1 42 | N500G2X2Y24.25I-5.8J1.6 43 | N510G2X1.05Y23.5I-1.65J1.15 44 | N520G1X.95Y23.5 45 | N530G1X.9Y23.6 46 | N540G3X1.8Y24.6I-1.65J2.45 47 | N550G3X3.05Y27.65I-10.35J5.95 48 | N560G2X3.8Y29.1I3.3J-.8 49 | N570G3X3.75Y29.45I-.2J.15 50 | N580G3X3.4Y29.35I-.15J-.2 51 | N590G3X3.05Y28.15I3.65J-1.7 52 | N600G1X2.8Y27.5 53 | N610G1X2.7Y27.55 54 | N620G1X2.8Y27.9 55 | N630G1X3.4Y30.3 56 | N640G3X3.5Y31.6I-2.7J.9 57 | N650G3X3.35Y31.65I-.1J0 58 | N660G1X3.2Y31.2 59 | N670G1X2.9Y30.1 60 | N680G2X2.6Y30.65I.05J.4 61 | N690G3X3.15Y32.85I-10.65J3.85 62 | N700G1X3.75Y35.6 63 | N710G1X5.1Y39.7 64 | N720G2X5.5Y40.25I1.1J-.4 65 | N730G3X5.45Y40.95I-.25J.35 66 | N740G1X4.65Y39.25 67 | N750G2X4.45Y40.2I.15J.5 68 | N760G3X6.2Y43.4I-2.85J3.65 69 | N770G1X6.4Y44.55 70 | N780G1X6.55Y45.7 71 | N790G1X6.55Y45.95 72 | N800G2X6.75Y45.95I.1J0 73 | N810G1X6.65Y45.85 74 | N820G1X6.55Y45.75 75 | N830G1X6.55Y45.7 76 | N840G1X6.4Y44.45 77 | N850G2X6.15Y44.25I-.2J0 78 | N860G2X6Y44.5I0J.2 79 | N870G1X6.25Y45.75 80 | N880G1X6.65Y47 81 | N890G1X7.3Y48.6 82 | N900G3X6.8Y48.85I-.25J.1 83 | N910G1X6.55Y48.05 84 | N920G1X6.25Y47.25 85 | N930G1X6Y46.9 86 | N940G1X5.95Y46.85 87 | N950G1X5.95Y46.7 88 | N960G1X5.95Y46.5 89 | N970G1X5.9Y46.3 90 | N980G1X5.8Y46.3 91 | N990G1X5.8Y46.35 92 | N1000G1X5.85Y46.4 93 | N1010G1X5.95Y46.75 94 | N1020G1X5.95Y47.05 95 | N1030G1X6.25Y48.35 96 | N1040G1X7Y50.1 97 | N1050G3X6.9Y50.4I-.2J.1 98 | N1060G3X6.6Y50.3I-.1J-.2 99 | N1070G1X5.9Y49.1 100 | N1080G3X5.85Y48.4I.55J-.4 101 | N1090G1X5.75Y48.25 102 | N1100G1X5.65Y48.35 103 | N1110G2X6.25Y50.6I6.3J-.55 104 | N1120G3X7.25Y54.8I-8.5J4.2 105 | N1130G1X7.15Y54.95 106 | N1140G1X7Y54.8 107 | N1150G1X6.95Y54.45 108 | N1160G1X6.85Y53.95 109 | N1170G2X6.65Y53.8I-.2J0 110 | N1180G2X6.5Y54I0J.2 111 | N1190G1X6.6Y54.5 112 | N1200G3X7.05Y56.35I-4.7J2.05 113 | N1210G2X7.9Y59.45I7.1J-.3 114 | N1220G3X7.6Y60.2I-.5J.25 115 | N1230G1X7.45Y60.15 116 | N1240G1X7.15Y59.4 117 | N1250G2X6.9Y59.35I-.15J.1 118 | N1260G2X6.85Y59.6I.1J.15 119 | N1270G1X7.45Y60.55 120 | N1280G3X8.25Y64.85I-7.3J3.6 121 | N1290G2X12.15Y71.9I7.25J.6 122 | N1300G2X23.2Y75.55I16.65J-32.2 123 | N1310G2X36.7Y75.75I7.5J-48.2 124 | N1320G3X58.4Y74.4I22.25J180.2 125 | N1330G3X66.05Y75.65I.05J23 126 | N1340G2X73.8Y76.55I5.9J-17 127 | N1350G2X78.25Y74.4I-.75J-7.15 128 | N1360G3X78.5Y74.5I.1J.1 129 | N1370G3X78.4Y74.95I-.45J.1 130 | N1380G1X78.1Y75.15 131 | N1390G1X77.85Y75.4 132 | N1400G1X77.25Y75.85 133 | N1410G2X78Y75.95I.4J-.2 134 | N1420G3X80.05Y73.9I10.6J8.55 135 | N1430G2X83.6Y70.7I-28.3J-34.85 136 | N1440G2X90.6Y63.3I-143J-141.9 137 | N1450G2X92Y61.4I-9.1J-8.15 138 | N1460G3X93.15Y60.9I.95J.55 139 | N1470G2X93.55Y60.35I.1J-.35 140 | N1480G3X93.65Y59.75I.45J-.25 141 | N1490G1X94.9Y58.5 142 | N1500G3X95.3Y58.4I.3J.25 143 | N1510G1X95.3Y58.8 144 | N1520G2X95.55Y58.6I0J-.2 145 | N1530G3X95.95Y57.55I1.75J.1 146 | N1540G2X99.7Y52.6I-38.9J-33.25 147 | N1550G2X100.9Y50.1I-8.7J-5.75 148 | N1560G2X101.75Y45.9I-16.3J-5.3 149 | N1570G3X104.2Y38.45I15.75J1.1 150 | N1580G2X105Y36.4I-4.5J-2.9 151 | N1590G3X106.15Y36.05I.7J.1 152 | N1600G2X107.8Y36.95I2.1J-1.8 153 | N1610G2X108.15Y36.35I.05J-.35 154 | N1620G1X107.8Y36 155 | N1630G3X107.3Y35.25I.85J-1.1 156 | N1640G1X107.3Y34.75 157 | N1650G1X107.45Y34.7 158 | N1660G1X110.25Y37.15 159 | N1670G2X111.15Y36.4I.4J-.45 160 | N1680G1X110.75Y35.8 161 | N1690G3X109.35Y33.3I5.25J-4.5 162 | N1700G2X108.2Y32.05I-1.85J.55 163 | N1710G3X107.35Y30.6I.5J-1.3 164 | N1720G2X109.15Y9.65I-310J-37.3 165 | N1730G2X108.65Y6.15I-8.85J-.45 166 | N1740G2X107.3Y5.1I-1.55J.55 167 | N1750G2X101Y5.15I-2.95J21.8 168 | N1760G2X100.15Y5.65I.2J1.35 169 | N1770G2X98.9Y8.15I5.35J4.2 170 | N1780G2X98.8Y9.7I4.1J1.1 171 | N1790G3X98.4Y11.25I-2.05J.25 172 | N1800G2X95.8Y14.85I37.3J29.25 173 | N1810G3X93.2Y18.5I-34.35J-22.05 174 | N1820G2X92.65Y23.6I3.65J2.95 175 | N1830G3X92.3Y33.15I-8.65J4.45 176 | N1840G1X86.4Y42.65 177 | N1850G3X83.9Y44.05I-2.5J-1.6 178 | N1860G3X83.2Y43.55I0J-.7 179 | N1870G2X81.45Y40.9I-5.35J1.6 180 | N1880G1X79.55Y39.45 181 | N1890G3X77.9Y37.75I3J-4.5 182 | N1900G2X76.9Y36.6I-3.55J2.15 183 | N1910G3X76.05Y35.4I1.6J-2.1 184 | N1920G3X75.95Y32.5I4.65J-1.6 185 | N1930G2X76.7Y20.9I-27.25J-7.55 186 | N1940G3X77Y12.95I21.6J-3.15 187 | N1950G3X78.75Y9I10.8J2.4 188 | N1960G3X80.5Y7.35I4.95J3.55 189 | N1970G2X82.1Y5.6I-2.5J-3.95 190 | N1980G1X83Y4.1 191 | N1990G2X82.45Y2.75I-.75J-.45 192 | N2000G2X79.9Y2.35I-3.45J13.3 193 | N2010G2X77.5Y4I-.15J2.35 194 | N2020G3X77.1Y4.2I-.3J-.1 195 | N2030G2X76.5Y5.75I-.15J.85 196 | N2040G3X76.05Y7I-.4J.6 197 | N2050G2X73.7Y7.4I-.45J4.5 198 | N2060G2X73.25Y8.05I.3J.7 199 | N2070G1X73.1Y11.1 200 | N2080G1X72.45Y17.85 201 | N2090G1X71.3Y26.75 202 | N2100G1X71.35Y27.45 203 | N2110G3X70.95Y28I-.45J.1 204 | N2120G3X70.35Y27.55I-.05J-.55 205 | N2130G2X69.5Y24.7I-12.95J2.25 206 | N2140G1X63.65Y10.85 207 | N2150G3X64.05Y5.55I5.35J-2.25 208 | N2160G2X65.9Y2.15I-27.8J-17.15 209 | N2170G2X64.95Y.55I-1J-.45 210 | N2180G2X60.45Y1I-.95J14.2 211 | N2190G2X59.65Y1.95I.25J1 212 | N2200G3X58.8Y2.8I-.85J0 213 | N2210G2X58.45Y3.3I0J.4 214 | N2220G2X59.6Y5.7I9.55J-3 215 | N2230G3X59.5Y8.35I-1.95J1.25 216 | N2240G1X59.25Y8.55 217 | N2250G2X58.55Y9.5I.15J.85 218 | N2260G2X59.6Y13.1I13J-1.9 219 | N2270G3X64.75Y27.1I-85.8J39.55 220 | N2280G2X68.5Y36.9I57.75J-16.2 221 | N2290G3X67.2Y39.4I-1.6J.75 222 | N2300G3X62.85Y39.4I-2.05J-12.45 223 | N2310G2X45.6Y40.2I-6.85J37.85 224 | N2320G2X39Y42.9I8.25J29.4 225 | N2330G2X33.85Y46.75I12.1J21.8 226 | N2340G3X32.05Y46.1I-.75J-.75 227 | N2350G2X30.7Y42.95I-5.95J.7 228 | N2360G3X28.7Y39.45I8.85J-7.4 229 | N2370G3X24.2Y22.45I91.3J-33.1 230 | N2380G3X24.35Y15.65I17.85J-3 231 | N2390G3X27.25Y8.25I24.15J5.2 232 | N2400G3X30.85Y4.75I8.2J4.85 233 | N2410G2X33.6Y.55I-2.6J-4.65 234 | N2420G2X32.75Y-.25I-.75J-.05 235 | N2430G3X28.3Y.05I-4.3J-29.6 236 | N2440G2X25.85Y.35I-.05J10.15 237 | N2450G2X25.55Y.75I.1J.4 238 | N2460G2X25.7Y1.7I2.45J.1 239 | N2470G3X25.6Y2.7I-1.05J.4 240 | N2480G1X25.55Y3.1 241 | N2490G3X24.75Y4.5I-1J.35 242 | N2500G1X22.55Y4.95 243 | N2510G2X20.9Y6.45I.55J2.25 244 | N2520G2X19.75Y13.5I18.15J6.5 245 | N2530G3X17.15Y26.45I-29.6J.8 246 | N2540G1X16.8Y26.9 247 | N2550G2X16.6Y27.45I.35J.45 248 | N2560G2X17.45Y29.6I6.3J-1.25 249 | N2570G3X20Y38.6I-14.1J8.8 250 | N2580G2X20.2Y39.7I3.1J.05 251 | N2590G3X19.6Y40I-.3J.1 252 | N2600G1X18.45Y38.7 253 | N2610G3X17.7Y37.15I1.8J-1.85 254 | N2620G3X18.25Y36.45I.65J-.05 255 | N2630G2X18.4Y35.55I-.05J-.45 256 | N2640G2X17.8Y36.3I-.25J.45 257 | N2650G3X17.55Y36.75I-.25J.2 258 | N2660G3X17.05Y36.15I.05J-.5 259 | N2670G1X17.75Y33.35 260 | N2680G1X18.15Y32.45 261 | N2690G2X18.1Y32.2I-.15J-.1 262 | N2700G1X17.35Y33.45 263 | N2710G1X17.8Y31.9 264 | N2720G1X17.75Y30.85 265 | N2730G2X17.5Y31.05I0J.2 266 | N2740G1X17.3Y32.2 267 | N2750G1X17.15Y33.2 268 | N2760G3X16.85Y34.15I-1.95J-.1 269 | N2770G3X16.6Y34.1I-.1J-.05 270 | N2780G3X16.65Y32.7I3.8J-.5 271 | N2790G1X17.3Y30.65 272 | N2800G1X17.4Y30.15 273 | N2810G1X17.3Y29.65 274 | N2820G1X17.05Y29.15 275 | N2830G1X16.85Y28.6 276 | N2840G1X16.8Y28.7 277 | N2850G1X16.8Y28.85 278 | N2860G3X16.05Y33I-11.55J0 279 | N2870G3X15.5Y33.25I-.4J-.15 280 | N2880G3X15.2Y32.6I.15J-.45 281 | N2890G2X16.05Y29.95I-11.5J-5.15 282 | N2900G1X16.4Y29.1 283 | N2910G1X16.7Y28.25 284 | N2920G1X16.6Y27.85 285 | N2930G3X16.4Y26.35I1.35J-.95 286 | N2940G1X16.6Y26.05 287 | N2950G1X16.8Y25.7 288 | N2960G1X17.55Y21.8 289 | N2970G1X16.65Y25.2 290 | N2980G3X16.3Y24.6I.1J-.45 291 | N2990G2X16.85Y22I-9.2J-3.25 292 | N3000G1X16.9Y21.75 293 | N3010G1X16.95Y21.5 294 | N3020G1X16.95Y21.3 295 | N3030G1X17Y21.25 296 | N3040G1X17.1Y21.3 297 | N3050G1X17.05Y21.35 298 | N3060G1X17Y21.45 299 | N3070G1X16.95Y21.5 300 | N3080G2X17.05Y22.75I2.95J.4 301 | N3090G3X16.6Y23.3I-.4J.1 302 | N3100G1X16.9Y21.05 303 | N3110G2X16.5Y21.4I-.1J.3 304 | N3120G3X16.2Y23.5I-4.85J.45 305 | N3130G2X16.2Y24.15I1J.35 306 | N3140G3X15.75Y24.6I-.35J.1 307 | N3150G1X16.1Y21.75 308 | N3160G2X15.65Y22.15I-.05J.35 309 | N3170G3X15.45Y24.35I-6.55J.55 310 | N3180G2X15Y28.05I14.1J3.75 311 | N3190G3X14.45Y28.8I-.85J0 312 | N3200G3X13.85Y28.3I-.15J-.4 313 | N3210G1X14.35Y25.6 314 | N3220G1X14.55Y24.75 315 | N3230G1X15.45Y21.35 316 | N3240G2X15.2Y21.3I-.1J-.05 317 | N3250G2X14.7Y23.05I6J2.8 318 | N3260G1X14.3Y24.35 319 | N3270G1X14.1Y24.45 320 | N3280G1X14.2Y21.9 321 | N3290G1X14Y22.75 322 | N3300G1X14.05Y21.9 323 | N3310G1X13.95Y21.6 324 | N3320G1X13.9Y21.6 325 | N3330G1X13.05Y26.75 326 | N3340G1X12.65Y28.55 327 | N3350G3X12.15Y28.5I-.25J-.05 328 | N3360G1X11.85Y26.95 329 | N3370G1X11.3Y25.75 330 | N3380G3X10.8Y24.55I2.55J-1.7 331 | N3390G1X10.85Y24.05 332 | N3400G1X11.25Y22.1 333 | N3410G1X11.55Y21.55 334 | N3420G2X11.4Y20.95I-.25J-.25 335 | N3430G2X11.1Y21.1I-.05J.25 336 | N3440G2X10.7Y22.6I5.25J2.2 337 | N3450G2X10.75Y24.05I5.15J.65 338 | N3460G3X10.55Y24.4I-.3J.05 339 | N3470G3X9.8Y24.1I-.2J-.6 340 | N3480G3X8.1Y20I16.95J-9.45 341 | N3490G3X6.35Y10.3I47.2J-13.5 342 | N3500G3X8Y2.25I14.85J-1.15 343 | N3510G2X8.05Y.8I-1.4J-.75 344 | N3520G2X7Y.1I-1.15J.55 345 | N3530G1X3.6Y-.2 346 | N3540G2X1.45Y1.8I-.15J2 347 | N3550G3X.95Y1.9I-.25J0 348 | N3560G2X.25Y1.75I-.4J.25 349 | N3570G00X.25Y1.75 350 | N3580G00Z6 351 | N3590G00X0Y0 352 | N3600M05 353 | N3610M30 354 | N3620% 355 | --------------------------------------------------------------------------------