plottertools / vpype-gcode

gcode extension for vpype
MIT License
37 stars 7 forks source link
gcode gcode-generation hacktoberfest plotter plotter-art

vpype-gcode

Vpype plugin to generate gcode and other text output. See: https://github.com/abey79/vpype

Gcode vpype plugin. Write gcode files for the vpype pipeline. The output format can be customized by the user heavily to an extent that you can also output non gcode ascii text files.

Installing

pipx-based vpype install:

$ pipx inject vpype vpype-gcode

Global or venv-based vpype install:

$ pip install vpype-gcode

Usage

Usage: vpype gwrite [OPTIONS] OUTPUT

Options:
  -p, --profile TEXT  gcode writer profile from the vpype configuration file
                      subsection 'gwrite'

  --help              Show this message and exit.

Goals

The goal of this project is to allow all hobbyists needing gcode writing to have everything they need to create the gcode their projects requires. The secondary goal is to get this project basically wholesale assumed into vpype proper. The writer is so open and modular that it can write everything from svg to json to gcode, the hope was to get this sort of thing added to vpype.

Philosophy

Breaking changes are fine. Anything that will make this code more likely to end up in vpype properly and this particular library getting deprecated will be highly likely to be done.

Profiles

This plugin supports different output profiles which configure the way the resulting output is formatted. Output profiles are flexible in a way that they can also be used to generate non gcode files, i.e. JSON or CSV files.

Predefined Profiles

There are several predefined output profiles as part of the release:

Check the source code for how these profiles are defined.

Defining Your Own Profiles

In case you want to define your own output format to make the suit your needs, it is possible to define your own profiles either in ~/.vpype.toml or any other file. In the latter case, you must instruct vpype to load the configuration using the --config global option.

Inside the configuration file you can specify a new output profile using the following format:

[gwrite.my_own_plotter]
unit = "mm"
document_start = "M3 G21\n"
layer_start = "(Start Layer)\n"
line_start = "(Start Block)\n"
segment_first = """G00 Z5
G00 X{x:.4f} Y{y:.4f}
M3 S1000
G4 P0.3
G01 Z1 F3500
"""
segment = """G01 X{x:.4f} Y{y:.4f} Z1\n"""
line_end = """G00 Z 5.0000
M5 S0
G4 P0.5\n"""
document_end = """M5
G00 X0.0000 Y0.0000
M2"""
invert_y = true

You can use the following options inside a profile. You only need to provide the options where you need to change the default. If you want a newline character in an option, you can either use escape sequences (\n) or you use TOML multi line strings wrapped in """".

Output Control

These parameters define the transformation between vpype's and the target's coordinate systems. vpype coordinate is based on CSS pixels (1/96th of an inch), has origin at the top-left corner with positive X value extending right and positive Y values extending downward.

Output Format

All of the options below default to an empty text which means no output is generated. However, if segment_first or segment_last is omitted the code from segment is used. If there is only one segment, segment_first takes priority over segment_last.

Segment formatting

gwrite uses .format() encoding which means that data elements must be encapsulated in {} brackets. This provides a particular syntax token which differs from between elements. For example every element accepts the value of index. You would encode that in the text as {index:d} the d denotes an integer value. If you need to have a { value in your text you would encode that as {{ likewise you would encode a } as }}.

Notes

Using properties

In addition to the variables described in the previous section, any existing property may be used for substitution. For example, the layer color is stored as a property named vp_color and can be added to the profile with the pattern {vp_color}. Since this is a layer property, it would be available for all options but document_start and document_end, for which no current layer is defined. Global properties are available to all options.

There is no guarantee that a given property is defined upon export time, even system properties such as vp_color. If a property used in the selected profile doesn't exist, an error will be generated. To avoid this, a default value for the property must be provided, which can be achieved in two ways: in the config file or using the --default option.

This is an example of profile configuration which includes a default value for the vp_color and vp_pen_width properties:

[gwrite.summary]
layer_start = "layer {layer_index1:d} (color = {vp_color}, pen_width = {vp_pen_width:0.2f})\n"
layer_join = "\n"
line_start = "line {index1} segment count: "
segment_last = "{index:d}\n"
default_values = {vp_color = "undefined", vp_pen_width = 0.1}

Here is an example of use:

$ vpype  rect 0 0 10cm 10cm  color red  random --layer 2  penwidth --layer 2 0.5mm  gwrite -p summary -
layer 1 (color = #ff0000, pen_width = 0.10)
line 1 segment count: 4

layer 2 (color = undefined, pen_width = 1.89)
line 1 segment count: 1
line 2 segment count: 1
line 3 segment count: 1
line 4 segment count: 1
line 5 segment count: 1
line 6 segment count: 1
line 7 segment count: 1
line 8 segment count: 1
line 9 segment count: 1
line 10 segment count: 1

Alternatively, default values for string-based variable may be provided using the command line using the --default option:

$ vpype [...] gwrite -p summary --default vp_color undefined output.txt

Information Control

Output structure

The gwrite command gives you access to write to a variety of formats that fit the given outline. We're writing generic ascii. Since gcode can have more flavors than a Baskin Robbins™, it's best to simply draw broad strokes as to what ascii output should look like. Here we define the various elements without any regard to the gcode it will largely be producing.

<document_start>
  <layer_start>
    <line_start>
      <segment_first>
      <segment>
      <segment>
      <segment>
      <segment>
      <segment_last>
    <line_end>
    <line_join>
    <line_start>
      <segment_first>
      <segment>
      <segment>
      <segment>
      <segment>
      <segment_last>
    <line_end>
    <line_join>
    <line_start>
      <segment_first>
      <segment>
      <segment>
      <segment>
      <segment>
      <segment_last>
    <line_end>
 <layer_end>
 <layer_join>
 <layer_start>
    <line_start>
      <segment_first>
      <segment>
      <segment>
      <segment>
      <segment>
      <segment_last>
    <line_end>
    <line_join>
    <line_start>
      <segment_first>
      <segment>
      <segment>
      <segment>
      <segment>
      <segment_last>
    <line_end>
    <line_join>
    <line_start>
      <segment_first>
      <segment>
      <segment>
      <segment>
      <segment>
      <segment_last>
    <line_end>
 <layer_end>
<document_end>

Default Profile

To prevent having to provide the profile on every invocation of the gcode plugin, you can define a default profile which will be used when no other profile is provided on the command line. You can do so by setting the default_profile configuration variable inside the gcode section of the vpype configuration file:

[gwrite]
default_profile = "gcode"

Coordinate System

Vpype-Gcode uses the standard coordinate system of vpype which uses the SVG spec' system. The origin point is located in the upper-left hand corner. Positive y values are towards the bottom. If you wish to change the coordinate system you should use vertical_flip and horizontal_flip equals true in your profile (or invert_y and invert_x in the uncommon situation where the document page size is not known). For example the native coordinate system of gcode is origin point in the bottom left with positive y values toward the top. This system requires gcode profiles be marked with vertical_flip = true.

[gwrite.gcode]
document_start = "G20\nG17\nG90\n"
segment_first = "G00 X{x:.4f} Y{y:.4f}\n"
segment = "G01 X{x:.4f} Y{y:.4f}\n"
document_end = "M2\n"
unit = "in"
vertical_flip = true
info= "This gcode profile is correctly inverted across the y-axis"

Examples

Convert SVG -> GCode

Loads up a svg and writes it in default gcode.: vpype read butterfly.svg gwrite --profile gcode butterfly.gcode

Create a grid of circles, then we are gwrite using the ninja profile: vpype begin grid -o 25 25 10 10 circle 0 0 100 end gwrite --profile ninja test.gcode

Convert SVG -> CSV

The csv profile is bundled with this package and defined as follows:

[gwrite.csv]
document_start = "#Operation, X-value, Y-value\n"
segment_first = "Move, %f, %f\n"
segment = "Line-to, %f, %f\n"

Using this profile you can generate a CSV for a given input into vpype: vpype begin grid -o 25 25 10 10 circle 0 0 100 end gwrite -p csv test.csv

This produces:

#Operation, X-value, Y-value
Move, 26, 0
Line-to, 26, 0
Line-to, 26, -1
Line-to, 26, -2
Line-to, 26, -3
Line-to, 25, -4
Line-to, 25, -5
Line-to, 25, -6
...

Giving it a simpler example, this produces a plain text CSV file of the rectangle. vpype rect 0 0 100 100 gwrite -p csv test.csv

#Operation, X-value, Y-value
Move, 0, 0
Line-to, 0, 100
Line-to, 100, 100
Line-to, 100, 0
Line-to, 0, 0

This is the secret sauce of gwrite, it writes generic ascii which can be themed as functional gcode.

Convert SVG -> JSON

The json profile is already bundled with this package. It is defined as following:

[gwrite.json]
document_start = "{{"
document_end = "}}\n"
line_join = ","
layer_join = ","
layer_start = "\n\t\"Layer\": {{"
layer_end = "\t}}\n"
line_start = "\n\t\t\"line{index:d}\": [\n"
line_end = "\n\t\t]"
segment = "\t\t{{\n\t\t\t\"X\": {ix:d},\n\t\t\t\"Y\": {iy:d}\n\t\t}},\n"
segment_last = "\t\t{{\n\t\t\t\"X\": {ix:d},\n\t\t\t\"Y\": {iy:d}\n\t\t}}"

Using this profile, you can generate JSON for the rectangle: vpype rect 0 0 100 100 gwrite -p json test.json

{
    "Layer": {
        "Line0": [
        {
            "X": 0,
            "Y": 0
        },
        {
            "X": 0,
            "Y": 26
        },
        {
            "X": 26,
            "Y": 26
        },
        {
            "X": 26,
            "Y": 0
        },
        {
            "X": 0,
            "Y": 0
        }
        ]   }
}

Which is valid JSON.

Release workflow

New release should be created with the following workflow:

1) Make/push as version bump commit 2) Tag this commit with a version number (format: 0.X.Y), push the commit to origin. 3) Check that the GH Action workflow runs without error (the workflow builds wheels, pushes them to PyPI, and creates a draft GH Release). 4) Edit the draft release and publish.

Credits