svg2gcode 3.2.8__tar.gz → 3.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/PKG-INFO +97 -44
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/README.md +96 -43
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/__init__.py +1 -1
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/__main__.py +49 -2
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/__init__.py +4 -2
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/compiler/_compiler.py +255 -98
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/css_color.py +8 -2
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/LICENSE +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/pyproject.toml +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/LICENSE +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/compiler/__init__.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/compiler/interfaces/__init__.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/compiler/interfaces/_abstract_interface.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/compiler/interfaces/_fan_controlled_gcode.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/compiler/interfaces/_gcode.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/formulas.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/__init__.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_abstract_chain.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_abstract_curve.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_circular_arc.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_cubic_bazier.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_elliptical_arc.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_line.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_line_segment_chain.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_matrix.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_quadratic_bazier.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_raster_image.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_smooth_arc_chain.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/geometry/_vector.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/svg_parser/__init__.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/svg_parser/_parser_methods.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/svg_parser/_path.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/svg_parser/_transformation.py +0 -0
- {svg2gcode-3.2.8 → svg2gcode-3.3.1}/svg2gcode/svg_to_gcode/svg_parser/debug_methods.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: svg2gcode
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.1
|
|
4
4
|
Summary: svg2gcode: convert an SVG (Scalable Vector Graphic) image to gcode.
|
|
5
5
|
Keywords: scalable vector graphics,svg,image,laser cutter,laser engraving
|
|
6
6
|
Author-email: Johannes Noordanus <mailjohannes.mailnoordanus@gmail.com>
|
|
@@ -14,46 +14,97 @@ Project-URL: Home, https://github.com/johannesnoordanus/svg2gcode
|
|
|
14
14
|
|
|
15
15
|
# svg2gcode
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
It is based on library *SvgToGcode* (*fork*: *https://github.com/johannesnoordanus/SvgToGcode*)<sup>(*)</sup>.
|
|
17
|
+
*svg2gcode* is a *WYSIWYG* converter of Scalable Vector Graphic *(SVG)* drawings. It is based on library *SvgToGcode* (*fork*: *https://github.com/johannesnoordanus/SvgToGcode*)<sup>(*)</sup>.
|
|
19
18
|
|
|
20
|
-
Drawings and images can be composed using Inkscape (or other SVG image software)
|
|
21
|
-
|
|
22
|
-
This makes combined cutting and engraving as easy as orientating the (wood) slab once.
|
|
19
|
+
Drawings and images can be composed using Inkscape (or other *SVG* image software), saved to a *.svg* file and - *WYSIWYG* - converted to gcode.
|
|
20
|
+
Color coding can be used to mark what part(s) of the drawing to cut, engrave or even ignore.
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
This
|
|
27
|
-
|
|
22
|
+
Raster elements within a *SVG* drawing are converted pixel perfect, vector elements are drawn at the specified resolution.
|
|
23
|
+
Vector elements are converted including line width, fill and color, but note that colors (including alpha-channel) are converted to black and white.
|
|
24
|
+
(This is because laser engravings resemble black and white images.)
|
|
25
|
+
This means that it is possible to make laser engravings of text (fonts) and other drawing elements or even the entire vector drawing.
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
(Image attributes can be added within the XMLeditor (Inkscape) by using the **+**).
|
|
32
|
-
Note that image attributes override explicit or default commandline settings.
|
|
27
|
+
You have full control of placing (locating) of the gcode end result. You can for example translate, scale, rotate and have the origin at the image center.
|
|
28
|
+
*svg2gcode* produces highly optimised and accurate *gcode*. *gcode* file length is relatively short (because codes and coordinates are emitted sparsely) and laser drawing is sped up by speed moves and skipping of all empty parts.
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
Option *--selfcenter* can be used to set the origin at the center of the image.
|
|
36
|
-
Note that gcode file headers contain compile information like the boundingbox and boundingbox center coordinates.
|
|
30
|
+
More info can be obtained by looking at the documentation and examples below and the documentation of *image2gcode* (for example about calibration of laser engravers).
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
See options ```--speedmoves``` and ```--noise``` for example.
|
|
32
|
+
*svg2gcode* has three related programs: *image2gcode* mentioned above has similar capabilities but handles raster image files (like *png* and *jpg*) directly, *gcode2image* performs the inverse function and is capable of showing multiple writes (burns) to the same location and last *grblhud* which gives full control over gcode execution.
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
When the *stroke* attribute of a *path* is nonexistent or set to none, the path will be laser burned with the value set by option *--cuttingpower*.
|
|
34
|
+
-----
|
|
43
35
|
|
|
44
|
-
|
|
36
|
+
Please consider supporting me, so I can make this application better and add new functionality to it: <http://paypal.me/johannesnoordanus/5,00>
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
Option ```--nofill``` (not set by default) is added to disable path fills.
|
|
38
|
+
My next update will add *fill-rule* 'nonzero' (see information on *fill-rule* below).
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Please consider supporting me, so I can make this application better and add new functionality to it: <http://paypal.me/johannesnoordanus/5,00>
|
|
40
|
+
<sup>(*)</sup> Note that an upgraded and corrected version of this library is included.
|
|
41
|
+
### Supported SVG elements and attributes
|
|
52
42
|
|
|
53
|
-
|
|
43
|
+
*SVG* *path* and *image* elements (specific: ```<svg:path ..>```tags and images```<svg:image ..>```tags) are supported. Other drawing objects must be converted to a *path* first to be able to translate them to a gcode sequence.
|
|
54
44
|
|
|
45
|
+
Attributes *stroke* (color), *stroke-width*, *stroke_alpha*, *fill* (color), *fill-rule*, *fill_alpha* are supported.
|
|
46
|
+
Currently only value *evenodd* of *fill-rule* is supported.
|
|
55
47
|
|
|
56
|
-
|
|
48
|
+
### Important Commandline options
|
|
49
|
+
|
|
50
|
+
Option ```--color_coded``` can be used to specify what part(s) of the drawing to cut, engrave or even ignore.
|
|
51
|
+
For example:
|
|
52
|
+
```
|
|
53
|
+
> svg2gcode --color_coded "black = ignore red = cut blue = engrave" paws.svg paws.gc
|
|
54
|
+
```
|
|
55
|
+
As a consequence all 'black' path elements will be ignored and will not appear after conversion, all red paths will be cut and all blue paths will be engraved (having color blue).
|
|
56
|
+
The corresponding gcode file contains lines like below to indicated which 'black path' is skipped:
|
|
57
|
+
```
|
|
58
|
+
; svg2gcode 3.2.8 (2024-07-28 11:39:27)
|
|
59
|
+
; arguments:
|
|
60
|
+
; laser_power: 850,
|
|
61
|
+
; movement_speed: 1000,
|
|
62
|
+
...
|
|
63
|
+
; image_showoverscan: False,
|
|
64
|
+
; color_coded: black = ignore red = cut blue = engrave
|
|
65
|
+
; Boundingbox: (X0.8,Y0.0:X5.1,Y4.3)
|
|
66
|
+
; boundingbox center: (X2.951648,Y2.148172)
|
|
67
|
+
; GRBL 1.1, unit=mm, absolute coordinates
|
|
68
|
+
|
|
69
|
+
; Machine settings:
|
|
70
|
+
; rapid movement,
|
|
71
|
+
...
|
|
72
|
+
; XY plane,
|
|
73
|
+
; cutter compensation off,
|
|
74
|
+
; coordinate system 1,
|
|
75
|
+
; move 'unit'/min
|
|
76
|
+
G0 G17 G40 G54 G94
|
|
77
|
+
|
|
78
|
+
G21
|
|
79
|
+
G90
|
|
80
|
+
; pass #1
|
|
81
|
+
|
|
82
|
+
; --color_coded: ignore path 'path304-9-2-9-41-7', color '#000000'
|
|
83
|
+
```
|
|
84
|
+
Note that it doesn't matter if a specific color action is set but no corresponding path exits.
|
|
85
|
+
Note that multiple collors can be set per 'action':
|
|
86
|
+
```
|
|
87
|
+
> svg2gcode --color_coded "black = ignore red = cut green = ignore blue = engrave orange = engrave yellow = cut" paws.svg paws.gc
|
|
88
|
+
```
|
|
89
|
+
Note that engraving is the default action, so if option *color_coded* isn't set or *color_coded* has no engrave color set, all paths having no other action set will be engraved (but see below).
|
|
90
|
+
|
|
91
|
+
This makes it possible to make a drawing where all path elements are drawn in their respective colors to make an engraving of the entire vector drawing.
|
|
92
|
+
When you set the engrave action for a set of colors, only those colors will be engraved (having their respective colors), all other paths having no action set are ignored.
|
|
93
|
+
Also, when a path has no stroke attribute (or it is set to none) the element cannot be engraved (because it has no color) so it is interpreted as a cut action.
|
|
94
|
+
|
|
95
|
+
Option ```--pathcut``` can be used to override all stroke attributes and force cutting of all *paths* of the *SVG*.
|
|
96
|
+
Note that this option cannot be set at the same time as *--color_coded* above.
|
|
97
|
+
|
|
98
|
+
Option ```--nofill``` makes it possible to disable all path fills.
|
|
99
|
+
Options ```--origin --rotate --scale --selfcenter``` can be used to locate and transform the gcode image.
|
|
100
|
+
Option ```--speedmoves``` makes it possible to run engravings significantly faster and skip from one image zone to the other at maximum speed.
|
|
101
|
+
Option ```--noise``` suppresses stray pixels
|
|
102
|
+
|
|
103
|
+
Controlling laser power, pixel size and other settings can be done via commandline parameters but also via the following attributes (which can be set within the *.svg* file directly or within Inkscape using the XMLeditor):
|
|
104
|
+
```gcode_pixelsize```, ```gcode_maxpower```, ```gcode_speed```, ```gcode_noise```, ```gcode_speedmoves```, ```gcode_overscan``` and ```gcode_showoverscan``` can be set per vector element. (Image attributes can be added within the XMLeditor (Inkscape) by using the **+**).
|
|
105
|
+
Note that these attributes override explicit or default commandline settings.
|
|
106
|
+
|
|
107
|
+
### gcode optimizations
|
|
57
108
|
|
|
58
109
|
Optimized gcode
|
|
59
110
|
- draw pixels in one go until change of power
|
|
@@ -62,18 +113,14 @@ Optimized gcode
|
|
|
62
113
|
- does not emit zero power (or below cutoff) pixels
|
|
63
114
|
|
|
64
115
|
General optimizations
|
|
65
|
-
- laser head has
|
|
116
|
+
- laser head has deferred and sparse moves.
|
|
66
117
|
(XY locations are virtual, head does not always follow)
|
|
67
118
|
- moves at high speed (G0) over 10mm (default) or more zero pixels
|
|
68
119
|
- low burn levels (stray pixels) can be suppressed (default off)
|
|
69
|
-
-
|
|
120
|
+
- default --constantburn mode *gcode M3*
|
|
70
121
|
- borders are drawn in parallel and in one go, following the *path* coordinates.
|
|
71
122
|
- fill has support for alpha channel and 'fill-opacity'
|
|
72
123
|
|
|
73
|
-
**Tip**: use commandline program *grblhud* *(https://github.com/johannesnoordanus/grblhud)* to have full control over gcode execution,
|
|
74
|
-
also, program *image2gcode* has similar capabilities but handles raster images files (like *png* and *jpg*) directly.
|
|
75
|
-
|
|
76
|
-
**Tip2**: another program *LaserWeb* (not made by me) is quite capable and has an excelent 3D gcode visualizer, it is able to calculate 3D paths for CNC machines, including the bit diameter.
|
|
77
124
|
|
|
78
125
|
### Install:
|
|
79
126
|
```
|
|
@@ -86,14 +133,14 @@ Some linux distributions use a managed environment in which you cannot install p
|
|
|
86
133
|
> pipx install svg2gcode
|
|
87
134
|
```
|
|
88
135
|
|
|
89
|
-
<sup>(*)</sup> Note that an upgraded and corrected version of this library is included.
|
|
90
136
|
### Usage:
|
|
91
137
|
```
|
|
92
138
|
> svg2gcode --help
|
|
93
|
-
usage:
|
|
94
|
-
[--poweroffset <default:0>] [--cuttingpower <default:850>] [--passes <default:1>] [--pass_depth <default:0>] [--rapidmove <default:10>]
|
|
95
|
-
[--overscan <default:0>] [--showoverscan] [--constantburn] [--origin delta-x delta-y] [--scale factor-x factor-y]
|
|
96
|
-
[--xmaxtravel <default:300>] [--ymaxtravel <default:400>] [--
|
|
139
|
+
usage: runsvg2gcode [-h] [--showimage] [--selfcenter] [--pixelsize <default:0.1>] [--imagespeed <default:800>] [--cuttingspeed <default:1000>] [--imagepower <default:300>]
|
|
140
|
+
[--poweroffset <default:0>] [--cuttingpower <default:850>] [--passes <default:1>] [--pass_depth <default:0>] [--rapidmove <default:10>]
|
|
141
|
+
[--noise <default:0>] [--overscan <default:0>] [--showoverscan] [--constantburn | --no-constantburn] [--origin delta-x delta-y] [--scale factor-x factor-y]
|
|
142
|
+
[--rotate <default:0>] [--splitfile] [--pathcut] [--nofill] [--xmaxtravel <default:300>] [--ymaxtravel <default:400>] [--color_coded <default:"">] [--fan]
|
|
143
|
+
[-V]
|
|
97
144
|
svg gcode
|
|
98
145
|
|
|
99
146
|
Convert svg to gcode for GRBL v1.1 compatible diode laser engravers.
|
|
@@ -127,7 +174,8 @@ options:
|
|
|
127
174
|
--overscan <default:0>
|
|
128
175
|
overscan image lines to avoid incorrect power levels for pixels at left and right borders, number in pixels, default off
|
|
129
176
|
--showoverscan show overscan pixels (note that this is visible and part of the gcode emitted!)
|
|
130
|
-
--constantburn
|
|
177
|
+
--constantburn, --no-constantburn
|
|
178
|
+
default constant burn mode (M3)
|
|
131
179
|
--origin delta-x delta-y
|
|
132
180
|
translate origin by vector (delta-x,delta-y) in mm (default not set, option --selfcenter cannot be used at the same time)
|
|
133
181
|
--scale factor-x factor-y
|
|
@@ -140,6 +188,8 @@ options:
|
|
|
140
188
|
machine x-axis lengh in mm
|
|
141
189
|
--ymaxtravel <default:400>
|
|
142
190
|
machine y-axis lengh in mm
|
|
191
|
+
--color_coded <default:"">
|
|
192
|
+
set action for path with specific stroke color "[color = [cut|engrave|ignore] *]*", example: --color_coded "black = ignore purple = cut blue = engrave"
|
|
143
193
|
--fan set machine fan on
|
|
144
194
|
-V, --version show version number and exit
|
|
145
195
|
```
|
|
@@ -151,8 +201,9 @@ You can also store svg2gcode settings in configuration file `~/.config/svg2gcode
|
|
|
151
201
|
xmaxtravel= 400
|
|
152
202
|
ymaxtravel= 400
|
|
153
203
|
imagespeed = 6000
|
|
204
|
+
color_coded = "black = ignore purple = cut blue = engrave"
|
|
154
205
|
```
|
|
155
|
-
It can be used with any parameter which takes a value, and
|
|
206
|
+
It can be used with any parameter which takes a value, and allows to persist your laser settings.
|
|
156
207
|
You can create this configuration file using an editor like vi or nano.
|
|
157
208
|
An alternative (quick) way to do that is to enter:
|
|
158
209
|
```
|
|
@@ -160,6 +211,7 @@ $ mkdir ~/.config
|
|
|
160
211
|
$ echo "xmaxtravel= 400
|
|
161
212
|
ymaxtravel= 400
|
|
162
213
|
imagespeed = 6000
|
|
214
|
+
color_coded = \"black = ignore purple = cut blue = engrave\"
|
|
163
215
|
" > ~/.config/svg2gcode.toml
|
|
164
216
|
```
|
|
165
217
|
(but use your own settings!)
|
|
@@ -251,7 +303,7 @@ or add 'gcode-pathcut' to the style string:
|
|
|
251
303
|
```
|
|
252
304
|
(Note that this makes it possible to selectively cut path objects within the SVG.)
|
|
253
305
|
|
|
254
|
-
or add *svg2gcode* option *--pathcut* to override all path stroke attributes within the SVG document
|
|
306
|
+
or add *svg2gcode* option *--pathcut* to override all path stroke attributes within the SVG document or use option *--color_coded* and set a cut action for the stroke color.
|
|
255
307
|
|
|
256
308
|
Run *svg2gcode* (with same options) again.
|
|
257
309
|
Now the gcode after conversion will look like this:
|
|
@@ -280,6 +332,7 @@ Don't worry if your lasercutter has no Z-axis, because this parameter will be ig
|
|
|
280
332
|
|
|
281
333
|
If you have a CNC machine (which does have a Z-axis) you can mill in depth (so to speak), but you can also add - or switch to - a laser head and use the gcode to be able to really laser cut deeper!
|
|
282
334
|
|
|
335
|
+
|
|
283
336
|
#### Create two types of gcode file
|
|
284
337
|
One containing the drawings of the .svg, the other containing the images:
|
|
285
338
|
```
|
|
@@ -292,7 +345,7 @@ One containing the drawings of the .svg, the other containing the images:
|
|
|
292
345
|
|
|
293
346
|
### Burn mode M3/M4:
|
|
294
347
|
|
|
295
|
-
Default *svg2gcode* uses
|
|
348
|
+
Default *svg2gcode* uses constant burn mode *M3*. This can be changed by setting option *--no-constantburn* which selects burn mode *M4*. Mode *M4* is not suitable for engravings because it automatically compensates (laser)power for speed. This conflicts with the specific gcode settings given by *image2gcode* (called by *svg2gcode*) for each pixel. In fact some experiments show that *M4* causes loss of quality and image deterioration when speed is increased. On white oak images had too much black and grey which did not go away for substantially higher speed. When switched back to constant burn (*M3*) mode, the same high speed gave excellent images having a sepia (licht yellow brown) color tone.
|
|
296
349
|
|
|
297
350
|
### Notes:
|
|
298
351
|
- drawing objects - within the composer - must be converted to a```path```to be translated to a gcode sequence
|
|
@@ -1,45 +1,96 @@
|
|
|
1
1
|
# svg2gcode
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
It is based on library *SvgToGcode* (*fork*: *https://github.com/johannesnoordanus/SvgToGcode*)<sup>(*)</sup>.
|
|
3
|
+
*svg2gcode* is a *WYSIWYG* converter of Scalable Vector Graphic *(SVG)* drawings. It is based on library *SvgToGcode* (*fork*: *https://github.com/johannesnoordanus/SvgToGcode*)<sup>(*)</sup>.
|
|
5
4
|
|
|
6
|
-
Drawings and images can be composed using Inkscape (or other SVG image software)
|
|
7
|
-
|
|
8
|
-
This makes combined cutting and engraving as easy as orientating the (wood) slab once.
|
|
5
|
+
Drawings and images can be composed using Inkscape (or other *SVG* image software), saved to a *.svg* file and - *WYSIWYG* - converted to gcode.
|
|
6
|
+
Color coding can be used to mark what part(s) of the drawing to cut, engrave or even ignore.
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
This
|
|
13
|
-
|
|
8
|
+
Raster elements within a *SVG* drawing are converted pixel perfect, vector elements are drawn at the specified resolution.
|
|
9
|
+
Vector elements are converted including line width, fill and color, but note that colors (including alpha-channel) are converted to black and white.
|
|
10
|
+
(This is because laser engravings resemble black and white images.)
|
|
11
|
+
This means that it is possible to make laser engravings of text (fonts) and other drawing elements or even the entire vector drawing.
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
(Image attributes can be added within the XMLeditor (Inkscape) by using the **+**).
|
|
18
|
-
Note that image attributes override explicit or default commandline settings.
|
|
13
|
+
You have full control of placing (locating) of the gcode end result. You can for example translate, scale, rotate and have the origin at the image center.
|
|
14
|
+
*svg2gcode* produces highly optimised and accurate *gcode*. *gcode* file length is relatively short (because codes and coordinates are emitted sparsely) and laser drawing is sped up by speed moves and skipping of all empty parts.
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
Option *--selfcenter* can be used to set the origin at the center of the image.
|
|
22
|
-
Note that gcode file headers contain compile information like the boundingbox and boundingbox center coordinates.
|
|
16
|
+
More info can be obtained by looking at the documentation and examples below and the documentation of *image2gcode* (for example about calibration of laser engravers).
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
See options ```--speedmoves``` and ```--noise``` for example.
|
|
18
|
+
*svg2gcode* has three related programs: *image2gcode* mentioned above has similar capabilities but handles raster image files (like *png* and *jpg*) directly, *gcode2image* performs the inverse function and is capable of showing multiple writes (burns) to the same location and last *grblhud* which gives full control over gcode execution.
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
When the *stroke* attribute of a *path* is nonexistent or set to none, the path will be laser burned with the value set by option *--cuttingpower*.
|
|
20
|
+
-----
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
Please consider supporting me, so I can make this application better and add new functionality to it: <http://paypal.me/johannesnoordanus/5,00>
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
Option ```--nofill``` (not set by default) is added to disable path fills.
|
|
24
|
+
My next update will add *fill-rule* 'nonzero' (see information on *fill-rule* below).
|
|
34
25
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Please consider supporting me, so I can make this application better and add new functionality to it: <http://paypal.me/johannesnoordanus/5,00>
|
|
26
|
+
<sup>(*)</sup> Note that an upgraded and corrected version of this library is included.
|
|
27
|
+
### Supported SVG elements and attributes
|
|
38
28
|
|
|
39
|
-
|
|
29
|
+
*SVG* *path* and *image* elements (specific: ```<svg:path ..>```tags and images```<svg:image ..>```tags) are supported. Other drawing objects must be converted to a *path* first to be able to translate them to a gcode sequence.
|
|
40
30
|
|
|
31
|
+
Attributes *stroke* (color), *stroke-width*, *stroke_alpha*, *fill* (color), *fill-rule*, *fill_alpha* are supported.
|
|
32
|
+
Currently only value *evenodd* of *fill-rule* is supported.
|
|
41
33
|
|
|
42
|
-
|
|
34
|
+
### Important Commandline options
|
|
35
|
+
|
|
36
|
+
Option ```--color_coded``` can be used to specify what part(s) of the drawing to cut, engrave or even ignore.
|
|
37
|
+
For example:
|
|
38
|
+
```
|
|
39
|
+
> svg2gcode --color_coded "black = ignore red = cut blue = engrave" paws.svg paws.gc
|
|
40
|
+
```
|
|
41
|
+
As a consequence all 'black' path elements will be ignored and will not appear after conversion, all red paths will be cut and all blue paths will be engraved (having color blue).
|
|
42
|
+
The corresponding gcode file contains lines like below to indicated which 'black path' is skipped:
|
|
43
|
+
```
|
|
44
|
+
; svg2gcode 3.2.8 (2024-07-28 11:39:27)
|
|
45
|
+
; arguments:
|
|
46
|
+
; laser_power: 850,
|
|
47
|
+
; movement_speed: 1000,
|
|
48
|
+
...
|
|
49
|
+
; image_showoverscan: False,
|
|
50
|
+
; color_coded: black = ignore red = cut blue = engrave
|
|
51
|
+
; Boundingbox: (X0.8,Y0.0:X5.1,Y4.3)
|
|
52
|
+
; boundingbox center: (X2.951648,Y2.148172)
|
|
53
|
+
; GRBL 1.1, unit=mm, absolute coordinates
|
|
54
|
+
|
|
55
|
+
; Machine settings:
|
|
56
|
+
; rapid movement,
|
|
57
|
+
...
|
|
58
|
+
; XY plane,
|
|
59
|
+
; cutter compensation off,
|
|
60
|
+
; coordinate system 1,
|
|
61
|
+
; move 'unit'/min
|
|
62
|
+
G0 G17 G40 G54 G94
|
|
63
|
+
|
|
64
|
+
G21
|
|
65
|
+
G90
|
|
66
|
+
; pass #1
|
|
67
|
+
|
|
68
|
+
; --color_coded: ignore path 'path304-9-2-9-41-7', color '#000000'
|
|
69
|
+
```
|
|
70
|
+
Note that it doesn't matter if a specific color action is set but no corresponding path exits.
|
|
71
|
+
Note that multiple collors can be set per 'action':
|
|
72
|
+
```
|
|
73
|
+
> svg2gcode --color_coded "black = ignore red = cut green = ignore blue = engrave orange = engrave yellow = cut" paws.svg paws.gc
|
|
74
|
+
```
|
|
75
|
+
Note that engraving is the default action, so if option *color_coded* isn't set or *color_coded* has no engrave color set, all paths having no other action set will be engraved (but see below).
|
|
76
|
+
|
|
77
|
+
This makes it possible to make a drawing where all path elements are drawn in their respective colors to make an engraving of the entire vector drawing.
|
|
78
|
+
When you set the engrave action for a set of colors, only those colors will be engraved (having their respective colors), all other paths having no action set are ignored.
|
|
79
|
+
Also, when a path has no stroke attribute (or it is set to none) the element cannot be engraved (because it has no color) so it is interpreted as a cut action.
|
|
80
|
+
|
|
81
|
+
Option ```--pathcut``` can be used to override all stroke attributes and force cutting of all *paths* of the *SVG*.
|
|
82
|
+
Note that this option cannot be set at the same time as *--color_coded* above.
|
|
83
|
+
|
|
84
|
+
Option ```--nofill``` makes it possible to disable all path fills.
|
|
85
|
+
Options ```--origin --rotate --scale --selfcenter``` can be used to locate and transform the gcode image.
|
|
86
|
+
Option ```--speedmoves``` makes it possible to run engravings significantly faster and skip from one image zone to the other at maximum speed.
|
|
87
|
+
Option ```--noise``` suppresses stray pixels
|
|
88
|
+
|
|
89
|
+
Controlling laser power, pixel size and other settings can be done via commandline parameters but also via the following attributes (which can be set within the *.svg* file directly or within Inkscape using the XMLeditor):
|
|
90
|
+
```gcode_pixelsize```, ```gcode_maxpower```, ```gcode_speed```, ```gcode_noise```, ```gcode_speedmoves```, ```gcode_overscan``` and ```gcode_showoverscan``` can be set per vector element. (Image attributes can be added within the XMLeditor (Inkscape) by using the **+**).
|
|
91
|
+
Note that these attributes override explicit or default commandline settings.
|
|
92
|
+
|
|
93
|
+
### gcode optimizations
|
|
43
94
|
|
|
44
95
|
Optimized gcode
|
|
45
96
|
- draw pixels in one go until change of power
|
|
@@ -48,18 +99,14 @@ Optimized gcode
|
|
|
48
99
|
- does not emit zero power (or below cutoff) pixels
|
|
49
100
|
|
|
50
101
|
General optimizations
|
|
51
|
-
- laser head has
|
|
102
|
+
- laser head has deferred and sparse moves.
|
|
52
103
|
(XY locations are virtual, head does not always follow)
|
|
53
104
|
- moves at high speed (G0) over 10mm (default) or more zero pixels
|
|
54
105
|
- low burn levels (stray pixels) can be suppressed (default off)
|
|
55
|
-
-
|
|
106
|
+
- default --constantburn mode *gcode M3*
|
|
56
107
|
- borders are drawn in parallel and in one go, following the *path* coordinates.
|
|
57
108
|
- fill has support for alpha channel and 'fill-opacity'
|
|
58
109
|
|
|
59
|
-
**Tip**: use commandline program *grblhud* *(https://github.com/johannesnoordanus/grblhud)* to have full control over gcode execution,
|
|
60
|
-
also, program *image2gcode* has similar capabilities but handles raster images files (like *png* and *jpg*) directly.
|
|
61
|
-
|
|
62
|
-
**Tip2**: another program *LaserWeb* (not made by me) is quite capable and has an excelent 3D gcode visualizer, it is able to calculate 3D paths for CNC machines, including the bit diameter.
|
|
63
110
|
|
|
64
111
|
### Install:
|
|
65
112
|
```
|
|
@@ -72,14 +119,14 @@ Some linux distributions use a managed environment in which you cannot install p
|
|
|
72
119
|
> pipx install svg2gcode
|
|
73
120
|
```
|
|
74
121
|
|
|
75
|
-
<sup>(*)</sup> Note that an upgraded and corrected version of this library is included.
|
|
76
122
|
### Usage:
|
|
77
123
|
```
|
|
78
124
|
> svg2gcode --help
|
|
79
|
-
usage:
|
|
80
|
-
[--poweroffset <default:0>] [--cuttingpower <default:850>] [--passes <default:1>] [--pass_depth <default:0>] [--rapidmove <default:10>]
|
|
81
|
-
[--overscan <default:0>] [--showoverscan] [--constantburn] [--origin delta-x delta-y] [--scale factor-x factor-y]
|
|
82
|
-
[--xmaxtravel <default:300>] [--ymaxtravel <default:400>] [--
|
|
125
|
+
usage: runsvg2gcode [-h] [--showimage] [--selfcenter] [--pixelsize <default:0.1>] [--imagespeed <default:800>] [--cuttingspeed <default:1000>] [--imagepower <default:300>]
|
|
126
|
+
[--poweroffset <default:0>] [--cuttingpower <default:850>] [--passes <default:1>] [--pass_depth <default:0>] [--rapidmove <default:10>]
|
|
127
|
+
[--noise <default:0>] [--overscan <default:0>] [--showoverscan] [--constantburn | --no-constantburn] [--origin delta-x delta-y] [--scale factor-x factor-y]
|
|
128
|
+
[--rotate <default:0>] [--splitfile] [--pathcut] [--nofill] [--xmaxtravel <default:300>] [--ymaxtravel <default:400>] [--color_coded <default:"">] [--fan]
|
|
129
|
+
[-V]
|
|
83
130
|
svg gcode
|
|
84
131
|
|
|
85
132
|
Convert svg to gcode for GRBL v1.1 compatible diode laser engravers.
|
|
@@ -113,7 +160,8 @@ options:
|
|
|
113
160
|
--overscan <default:0>
|
|
114
161
|
overscan image lines to avoid incorrect power levels for pixels at left and right borders, number in pixels, default off
|
|
115
162
|
--showoverscan show overscan pixels (note that this is visible and part of the gcode emitted!)
|
|
116
|
-
--constantburn
|
|
163
|
+
--constantburn, --no-constantburn
|
|
164
|
+
default constant burn mode (M3)
|
|
117
165
|
--origin delta-x delta-y
|
|
118
166
|
translate origin by vector (delta-x,delta-y) in mm (default not set, option --selfcenter cannot be used at the same time)
|
|
119
167
|
--scale factor-x factor-y
|
|
@@ -126,6 +174,8 @@ options:
|
|
|
126
174
|
machine x-axis lengh in mm
|
|
127
175
|
--ymaxtravel <default:400>
|
|
128
176
|
machine y-axis lengh in mm
|
|
177
|
+
--color_coded <default:"">
|
|
178
|
+
set action for path with specific stroke color "[color = [cut|engrave|ignore] *]*", example: --color_coded "black = ignore purple = cut blue = engrave"
|
|
129
179
|
--fan set machine fan on
|
|
130
180
|
-V, --version show version number and exit
|
|
131
181
|
```
|
|
@@ -137,8 +187,9 @@ You can also store svg2gcode settings in configuration file `~/.config/svg2gcode
|
|
|
137
187
|
xmaxtravel= 400
|
|
138
188
|
ymaxtravel= 400
|
|
139
189
|
imagespeed = 6000
|
|
190
|
+
color_coded = "black = ignore purple = cut blue = engrave"
|
|
140
191
|
```
|
|
141
|
-
It can be used with any parameter which takes a value, and
|
|
192
|
+
It can be used with any parameter which takes a value, and allows to persist your laser settings.
|
|
142
193
|
You can create this configuration file using an editor like vi or nano.
|
|
143
194
|
An alternative (quick) way to do that is to enter:
|
|
144
195
|
```
|
|
@@ -146,6 +197,7 @@ $ mkdir ~/.config
|
|
|
146
197
|
$ echo "xmaxtravel= 400
|
|
147
198
|
ymaxtravel= 400
|
|
148
199
|
imagespeed = 6000
|
|
200
|
+
color_coded = \"black = ignore purple = cut blue = engrave\"
|
|
149
201
|
" > ~/.config/svg2gcode.toml
|
|
150
202
|
```
|
|
151
203
|
(but use your own settings!)
|
|
@@ -237,7 +289,7 @@ or add 'gcode-pathcut' to the style string:
|
|
|
237
289
|
```
|
|
238
290
|
(Note that this makes it possible to selectively cut path objects within the SVG.)
|
|
239
291
|
|
|
240
|
-
or add *svg2gcode* option *--pathcut* to override all path stroke attributes within the SVG document
|
|
292
|
+
or add *svg2gcode* option *--pathcut* to override all path stroke attributes within the SVG document or use option *--color_coded* and set a cut action for the stroke color.
|
|
241
293
|
|
|
242
294
|
Run *svg2gcode* (with same options) again.
|
|
243
295
|
Now the gcode after conversion will look like this:
|
|
@@ -266,6 +318,7 @@ Don't worry if your lasercutter has no Z-axis, because this parameter will be ig
|
|
|
266
318
|
|
|
267
319
|
If you have a CNC machine (which does have a Z-axis) you can mill in depth (so to speak), but you can also add - or switch to - a laser head and use the gcode to be able to really laser cut deeper!
|
|
268
320
|
|
|
321
|
+
|
|
269
322
|
#### Create two types of gcode file
|
|
270
323
|
One containing the drawings of the .svg, the other containing the images:
|
|
271
324
|
```
|
|
@@ -278,7 +331,7 @@ One containing the drawings of the .svg, the other containing the images:
|
|
|
278
331
|
|
|
279
332
|
### Burn mode M3/M4:
|
|
280
333
|
|
|
281
|
-
Default *svg2gcode* uses
|
|
334
|
+
Default *svg2gcode* uses constant burn mode *M3*. This can be changed by setting option *--no-constantburn* which selects burn mode *M4*. Mode *M4* is not suitable for engravings because it automatically compensates (laser)power for speed. This conflicts with the specific gcode settings given by *image2gcode* (called by *svg2gcode*) for each pixel. In fact some experiments show that *M4* causes loss of quality and image deterioration when speed is increased. On white oak images had too much black and grey which did not go away for substantially higher speed. When switched back to constant burn (*M3*) mode, the same high speed gave excellent images having a sepia (licht yellow brown) color tone.
|
|
282
335
|
|
|
283
336
|
### Notes:
|
|
284
337
|
- drawing objects - within the composer - must be converted to a```path```to be translated to a gcode sequence
|
|
@@ -4,6 +4,7 @@ svg2gcode: convert an image to gcode.
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import sys
|
|
7
|
+
import re
|
|
7
8
|
try:
|
|
8
9
|
import tomllib
|
|
9
10
|
except ImportError:
|
|
@@ -16,6 +17,7 @@ from svg2gcode.svg_to_gcode.svg_parser import parse_file
|
|
|
16
17
|
from svg2gcode.svg_to_gcode.compiler import Compiler, interfaces
|
|
17
18
|
|
|
18
19
|
from svg2gcode import __version__
|
|
20
|
+
from svg2gcode.svg_to_gcode import css_color
|
|
19
21
|
|
|
20
22
|
config_file = os.path.expanduser('~/.config/svg2gcode.toml')
|
|
21
23
|
|
|
@@ -35,10 +37,25 @@ def svg2gcode(args) -> int:
|
|
|
35
37
|
'maximum_image_laser_power':args.imagepower, 'image_movement_speed':args.imagespeed, 'fan':args.fan,'rapid_move':args.rapidmove,
|
|
36
38
|
'showimage':args.showimage, 'x_axis_maximum_travel':args.xmaxtravel,'y_axis_maximum_travel':args.ymaxtravel, 'image_noise':args.noise,
|
|
37
39
|
'pass_depth':args.pass_depth, 'laser_mode':"constant" if args.constantburn else "dynamic", 'splitfile':args.splitfile, 'pathcut':args.pathcut,
|
|
38
|
-
'nofill':args.nofill, 'image_poweroffset':args.poweroffset, 'image_overscan':args.overscan, 'image_showoverscan':args.showoverscan
|
|
40
|
+
'nofill':args.nofill, 'image_poweroffset':args.poweroffset, 'image_overscan':args.overscan, 'image_showoverscan':args.showoverscan,
|
|
41
|
+
'color_coded': args.color_coded,})
|
|
39
42
|
|
|
40
43
|
compiler = init_compiler(args)
|
|
41
44
|
|
|
45
|
+
if args.color_coded != "":
|
|
46
|
+
# check color_coded argument validity (2)
|
|
47
|
+
pathignore, pathcut, pathengrave = compiler.color_coded_paths(True)
|
|
48
|
+
|
|
49
|
+
# a path color should be in one set only
|
|
50
|
+
for i in pathignore:
|
|
51
|
+
if i in pathcut or i in pathengrave:
|
|
52
|
+
print(f"argument error: '--color_coded \"{args.color_coded}\"' has a path color '{i}' set in multiple categories (cut|engrave|ignore) .")
|
|
53
|
+
return 1
|
|
54
|
+
for i in pathcut:
|
|
55
|
+
if i in pathengrave:
|
|
56
|
+
print(f"argument error: '--color_coded \"{args.color_coded}\"' has a path color '{i}' set in multiple categories (cut|engrave|ignore) .")
|
|
57
|
+
return 1
|
|
58
|
+
|
|
42
59
|
# emit gcode for svg
|
|
43
60
|
if args.selfcenter:
|
|
44
61
|
print("pass 1")
|
|
@@ -92,6 +109,8 @@ def main() -> int:
|
|
|
92
109
|
"pass_depth_default": 0,
|
|
93
110
|
"passes_default": 1,
|
|
94
111
|
"rotate_default": 0,
|
|
112
|
+
"colorcoded_default": "",
|
|
113
|
+
"constantburn_default": True,
|
|
95
114
|
}
|
|
96
115
|
|
|
97
116
|
if os.path.exists(config_file):
|
|
@@ -127,7 +146,7 @@ def main() -> int:
|
|
|
127
146
|
parser.add_argument('--overscan', default=cfg["overscan_default"], metavar="<default:" +str(cfg["overscan_default"])+ ">",
|
|
128
147
|
type=int, help="overscan image lines to avoid incorrect power levels for pixels at left and right borders, number in pixels, default off")
|
|
129
148
|
parser.add_argument('--showoverscan', action='store_true', default=False, help='show overscan pixels (note that this is visible and part of the gcode emitted!)' )
|
|
130
|
-
parser.add_argument('--constantburn', action=
|
|
149
|
+
parser.add_argument('--constantburn', action=argparse.BooleanOptionalAction, default=cfg["constantburn_default"], help='default constant burn mode (M3)')
|
|
131
150
|
parser.add_argument('--origin', default=None, nargs=2, metavar=('delta-x', 'delta-y'),
|
|
132
151
|
type=float, help="translate origin by vector (delta-x,delta-y) in mm (default not set, option --selfcenter cannot be used at the same time)")
|
|
133
152
|
parser.add_argument('--scale', default=None, nargs=2, metavar=('factor-x', 'factor-y'),
|
|
@@ -141,12 +160,40 @@ def main() -> int:
|
|
|
141
160
|
type=int, help="machine x-axis lengh in mm")
|
|
142
161
|
parser.add_argument('--ymaxtravel', default=cfg["ymaxtravel_default"], metavar="<default:" +str(cfg["ymaxtravel_default"])+ ">",
|
|
143
162
|
type=int, help="machine y-axis lengh in mm")
|
|
163
|
+
parser.add_argument( '--color_coded', action = 'store', default=cfg["colorcoded_default"], metavar="<default:\"" + str(cfg["colorcoded_default"])+ "\">",
|
|
164
|
+
type = str, help = 'set action for path with specific stroke color "[color = [cut|engrave|ignore] *]*"'
|
|
165
|
+
', example: --color_coded "black = ignore purple = cut blue = engrave"' )
|
|
144
166
|
parser.add_argument('--fan', action='store_true', default=False, help='set machine fan on' )
|
|
145
167
|
parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__, help="show version number and exit")
|
|
146
168
|
|
|
147
169
|
args = parser.parse_args()
|
|
148
170
|
|
|
149
171
|
try:
|
|
172
|
+
if args.color_coded != "":
|
|
173
|
+
if args.pathcut:
|
|
174
|
+
print("options --color_coded and --pathcut cannot be used at the same time, program abort")
|
|
175
|
+
return 1
|
|
176
|
+
# check argument validity (1)
|
|
177
|
+
|
|
178
|
+
# category names
|
|
179
|
+
category = ["cut", "engrave", "ignore"]
|
|
180
|
+
# get css color names
|
|
181
|
+
colors = str([*css_color.css_color_keywords])
|
|
182
|
+
colors = re.sub("(,|\[|\]|\'| )", '', colors.replace(",", "|"))
|
|
183
|
+
|
|
184
|
+
# make a color list
|
|
185
|
+
colors = colors.split("|")
|
|
186
|
+
|
|
187
|
+
# get all names from color_coded
|
|
188
|
+
names_regex = "[a-zA-Z]+"
|
|
189
|
+
match = re.findall(names_regex, args.color_coded)
|
|
190
|
+
names = [i for i in match]
|
|
191
|
+
|
|
192
|
+
for name in names:
|
|
193
|
+
if not (name in colors or name in category):
|
|
194
|
+
print(f"argument error: '--color_coded {args.color_coded}' has a name '{name}' that does not correspond to a css color or category (cut|engrave|ignore).")
|
|
195
|
+
return 1
|
|
196
|
+
|
|
150
197
|
if args.origin is not None and args.selfcenter:
|
|
151
198
|
print("options --selfcenter and --origin cannot be used at the same time, program abort")
|
|
152
199
|
return 1
|
|
@@ -40,7 +40,8 @@ SETTING = {
|
|
|
40
40
|
"showimage", # boolean show image used for conversion to gcode
|
|
41
41
|
"splitfile", # boolean return SVG path objects to <filename>.<gcext> and SVG image objects to <filename>_images.<gcext>
|
|
42
42
|
"pathcut", # boolean always cut SVG path objects! use laser_power setting
|
|
43
|
-
"nofill"
|
|
43
|
+
"nofill", # boolean ignore SVG fill attribute
|
|
44
|
+
"color_coded" # string color of path determines whether it is a cut or engrave
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
# Set defaults 'minimum_laser_power', 'pass_depth', 'dwell_time', 'laser_power', 'laser_mode', 'unit', 'distance_mode'
|
|
@@ -74,7 +75,8 @@ DEFAULT_SETTING = {
|
|
|
74
75
|
"showimage": False, # default image is not shown
|
|
75
76
|
"splitfile": False, # SVG path and image objects are emitted to one file: <filename>.<gcext>
|
|
76
77
|
"pathcut": False, # always cut SVG path objects! use laser_power setting
|
|
77
|
-
"nofill": False
|
|
78
|
+
"nofill": False, # ignore SVG fill attribute
|
|
79
|
+
"color_coded": str # color of path determines whether it is a cut or engrave
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
def check_setting(setting: dict[str,Any] =None) -> bool:
|
|
@@ -58,8 +58,9 @@ class Compiler:
|
|
|
58
58
|
|
|
59
59
|
# save params
|
|
60
60
|
self.params = params
|
|
61
|
-
|
|
61
|
+
# get default settings
|
|
62
62
|
self.settings = copy.deepcopy(DEFAULT_SETTING)
|
|
63
|
+
# and update
|
|
63
64
|
for key in params.keys():
|
|
64
65
|
self.settings[key] = params[key]
|
|
65
66
|
|
|
@@ -108,12 +109,13 @@ class Compiler:
|
|
|
108
109
|
else:
|
|
109
110
|
params += f"; {k}: {v}"
|
|
110
111
|
|
|
111
|
-
center = self.boundingbox.center()
|
|
112
112
|
gcode += [ f"; svg2gcode {__version__} ({str(datetime.now()).split('.')[0]})",
|
|
113
|
-
f"; arguments: \n{params}",
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
f"; arguments: \n{params}",]
|
|
114
|
+
if self.boundingbox.get():
|
|
115
|
+
center = self.boundingbox.center()
|
|
116
|
+
gcode += [ f"; {self.boundingbox}",
|
|
117
|
+
f"; boundingbox center: (X{center[0]:.{0 if center[0].is_integer() else self.precision}f},"
|
|
118
|
+
f"Y{center[1]:.{0 if center[1].is_integer() else self.precision}f})" ]
|
|
117
119
|
|
|
118
120
|
gcode += [ f"; GRBL 1.1, unit={self.settings['unit']}, {self.settings['distance_mode']} coordinates" ]
|
|
119
121
|
|
|
@@ -209,10 +211,15 @@ class Compiler:
|
|
|
209
211
|
file.write((self.gcode_file_header() if len(self.body) == 0 else "") + '\n' + self.compile_images() + '\n' + self.interface.program_end() + '\n')
|
|
210
212
|
logger.info(f"Added image(s) to {file_name}")
|
|
211
213
|
|
|
212
|
-
def append_line_chain(self, line_chain: LineSegmentChain, step: float, color: int = None):
|
|
214
|
+
def append_line_chain(self, line_chain: LineSegmentChain, step: float, color: int = None, speed: int = None):
|
|
213
215
|
"""
|
|
214
|
-
Draws a LineSegmentChain by calling interface.linear_move() for each segment.
|
|
215
|
-
self.body
|
|
216
|
+
Draws a LineSegmentChain (path) by calling interface.linear_move() for each segment.
|
|
217
|
+
The resulting code is appended to self.body
|
|
218
|
+
|
|
219
|
+
:param line_chain: path to draw.
|
|
220
|
+
:param step: path offset (indicates that this path is offset to another path).
|
|
221
|
+
:param color: laser intensity, 'laser_power' when set to 'None'.
|
|
222
|
+
:param speed: laser head movement speed, 'movement_speed' when set to 'None'.
|
|
216
223
|
"""
|
|
217
224
|
|
|
218
225
|
if line_chain.chain_size() == 0:
|
|
@@ -224,6 +231,7 @@ class Compiler:
|
|
|
224
231
|
|
|
225
232
|
# set laser power to color value when svg attribute 'stroke' (color) is set
|
|
226
233
|
laser_power = color if color is not None else self.settings["laser_power"]
|
|
234
|
+
movement_speed = speed if speed is not None else self.settings["movement_speed"]
|
|
227
235
|
|
|
228
236
|
# Move to the next line_chain when the next line segment doesn't connect to the end of the previous one.
|
|
229
237
|
if self.interface.position is None or abs(self.interface.position - start) > TOLERANCES["operation"]:
|
|
@@ -231,13 +239,13 @@ class Compiler:
|
|
|
231
239
|
# move to the next line_chain: set laser off, rapid move to start of chain,
|
|
232
240
|
# set movement (cutting) speed, set laser mode and power on
|
|
233
241
|
code += [self.interface.laser_off(), self.interface.rapid_move(start.x, start.y),
|
|
234
|
-
self.interface.set_movement_speed(
|
|
242
|
+
self.interface.set_movement_speed(movement_speed),
|
|
235
243
|
self.interface.set_laser_mode(self.settings["laser_mode"]), self.interface.set_laser_power_value(laser_power)]
|
|
236
244
|
else:
|
|
237
245
|
# move to the next line_chain: set laser mode, set laser power to 0 (cutting is off),
|
|
238
246
|
# set movement speed, (no rapid) move to start of chain, set laser to power
|
|
239
247
|
code += [self.interface.set_laser_mode(self.settings["laser_mode"]), self.interface.set_laser_power_value(0),
|
|
240
|
-
self.interface.set_movement_speed(
|
|
248
|
+
self.interface.set_movement_speed(movement_speed), self.interface.linear_move(start.x, start.y),
|
|
241
249
|
self.interface.set_laser_power_value(laser_power)]
|
|
242
250
|
|
|
243
251
|
self.boundingbox.update(start)
|
|
@@ -398,7 +406,9 @@ class Compiler:
|
|
|
398
406
|
if 'fill' in style_str:
|
|
399
407
|
fill = re.search('fill:[^;]+;',style_str)
|
|
400
408
|
if fill:
|
|
401
|
-
|
|
409
|
+
fill_str = fill.group(0)[5:-1]
|
|
410
|
+
if fill_str != 'none':
|
|
411
|
+
style['fill'] = fill_str
|
|
402
412
|
# parse fill-rule
|
|
403
413
|
if 'fill-rule' in style_str:
|
|
404
414
|
fill_rule = re.search('fill-rule:#(evenodd|nonzero)',style_str)
|
|
@@ -413,7 +423,9 @@ class Compiler:
|
|
|
413
423
|
if 'stroke' in style_str:
|
|
414
424
|
stroke = re.search('stroke:[^;]+;',style_str)
|
|
415
425
|
if stroke:
|
|
416
|
-
|
|
426
|
+
stroke_str = stroke.group(0)[7:-1]
|
|
427
|
+
if stroke_str != 'none':
|
|
428
|
+
style['stroke'] = stroke_str
|
|
417
429
|
# parse stroke-width
|
|
418
430
|
if 'stroke-width' in style_str:
|
|
419
431
|
stroke_width = re.search('stroke-width:(\d*\.)?\d+',style_str)
|
|
@@ -435,7 +447,8 @@ class Compiler:
|
|
|
435
447
|
|
|
436
448
|
# parse fill attribute
|
|
437
449
|
if 'fill' in curve.path_attrib:
|
|
438
|
-
|
|
450
|
+
if curve.path_attrib['fill'] != 'none':
|
|
451
|
+
style['fill'] = curve.path_attrib['fill']
|
|
439
452
|
if 'fill-rule' in curve.path_attrib:
|
|
440
453
|
style['fill-rule'] = curve.path_attrib['fill-rule']
|
|
441
454
|
# parse fill-opacity
|
|
@@ -445,7 +458,9 @@ class Compiler:
|
|
|
445
458
|
style['fill-opacity'] = re.search('(\d*\.)?\d+', fill_opacity.group(0)).group(0)
|
|
446
459
|
# parse stroke attribute
|
|
447
460
|
if 'stroke' in curve.path_attrib:
|
|
448
|
-
|
|
461
|
+
stroke_str = curve.path_attrib['stroke']
|
|
462
|
+
if stroke_str != 'none':
|
|
463
|
+
style['stroke'] = stroke_str
|
|
449
464
|
# parse stroke-width attribute
|
|
450
465
|
if 'stroke-width' in curve.path_attrib:
|
|
451
466
|
style['stroke-width'] = curve.path_attrib['stroke-width']
|
|
@@ -459,6 +474,87 @@ class Compiler:
|
|
|
459
474
|
|
|
460
475
|
return style
|
|
461
476
|
|
|
477
|
+
def color_coded_paths(self, set_color_coded = False) -> ():
|
|
478
|
+
"""
|
|
479
|
+
Get color_coded info
|
|
480
|
+
Return string tuple (pathignore, pathcut, pathengrave)
|
|
481
|
+
"""
|
|
482
|
+
pathignore = None
|
|
483
|
+
pathcut = None
|
|
484
|
+
pathengrave = None
|
|
485
|
+
|
|
486
|
+
if self.settings["color_coded"]:
|
|
487
|
+
color_code = self.settings["color_coded"]
|
|
488
|
+
|
|
489
|
+
# get css color names
|
|
490
|
+
colors = str([*css_color.css_color_keywords])
|
|
491
|
+
colors = re.sub("(,|\[|\]|\'| )", '', colors.replace(",", "|"))
|
|
492
|
+
|
|
493
|
+
# get ignore (do not draw) colors
|
|
494
|
+
colors_ignore_regex = "((" + colors + ") *= *ignore)+"
|
|
495
|
+
match = re.findall(colors_ignore_regex, color_code)
|
|
496
|
+
pathignore = [i[1] for i in match]
|
|
497
|
+
|
|
498
|
+
# get cut colors
|
|
499
|
+
colors_cut_regex = "((" + colors + ") *= *cut)+"
|
|
500
|
+
match = re.findall(colors_cut_regex, color_code)
|
|
501
|
+
pathcut = [i[1] for i in match]
|
|
502
|
+
|
|
503
|
+
# get engrave colors
|
|
504
|
+
colors_engrave_regex = "((" + colors + ") *= *engrave)+"
|
|
505
|
+
match = re.findall(colors_engrave_regex, color_code)
|
|
506
|
+
pathengrave = [i[1] for i in match]
|
|
507
|
+
|
|
508
|
+
if set_color_coded:
|
|
509
|
+
cced = ""
|
|
510
|
+
for i in pathignore:
|
|
511
|
+
cced += f"{i} = ignore "
|
|
512
|
+
for i in pathcut:
|
|
513
|
+
cced += f"{i} = cut "
|
|
514
|
+
for i in pathengrave:
|
|
515
|
+
cced += f"{i} = engrave "
|
|
516
|
+
|
|
517
|
+
# set color_coded option string to the actual version
|
|
518
|
+
self.settings["color_coded"] = cced
|
|
519
|
+
self.params["color_coded"] = cced
|
|
520
|
+
|
|
521
|
+
return (pathignore, pathcut, pathengrave)
|
|
522
|
+
|
|
523
|
+
def parse_color_coded(self, stroke_color: str = None) -> ():
|
|
524
|
+
"""
|
|
525
|
+
Parse option --color_coded
|
|
526
|
+
Return tuple (ignorepath, cutpath, engravepath)
|
|
527
|
+
"""
|
|
528
|
+
if self.settings["color_coded"]:
|
|
529
|
+
|
|
530
|
+
ignorepath = False
|
|
531
|
+
cutpath = False
|
|
532
|
+
engravepath = False
|
|
533
|
+
|
|
534
|
+
# Get colo_coded info
|
|
535
|
+
pathignore, pathcut, pathengrave = self.color_coded_paths();
|
|
536
|
+
|
|
537
|
+
for color in pathignore:
|
|
538
|
+
if css_color.rgb24equal(css_color.parse_css_color(stroke_color), css_color.css_color_keywords[color]["decimal"]):
|
|
539
|
+
# stroke color is ignored (no path is drawn)
|
|
540
|
+
ignorepath = True
|
|
541
|
+
break
|
|
542
|
+
|
|
543
|
+
if not ignorepath:
|
|
544
|
+
# cut path if option --color_coded is selected and stroke_color == "red"
|
|
545
|
+
for color in pathcut:
|
|
546
|
+
if css_color.rgb24equal(css_color.parse_css_color(stroke_color), css_color.css_color_keywords[color]["decimal"]):
|
|
547
|
+
cutpath = True
|
|
548
|
+
break
|
|
549
|
+
|
|
550
|
+
if not cutpath:
|
|
551
|
+
for color in pathengrave:
|
|
552
|
+
if css_color.rgb24equal(css_color.parse_css_color(stroke_color), css_color.css_color_keywords[color]["decimal"]):
|
|
553
|
+
engravepath = True
|
|
554
|
+
break
|
|
555
|
+
|
|
556
|
+
return (ignorepath, cutpath, engravepath)
|
|
557
|
+
|
|
462
558
|
def append_curves(self, curves: list[Curve]):
|
|
463
559
|
"""
|
|
464
560
|
Draws curves.
|
|
@@ -518,6 +614,84 @@ class Compiler:
|
|
|
518
614
|
pixel = 1/self.settings["pixel_size"]
|
|
519
615
|
draw(img, (int(p1[0]*pixel),int(p1[1]*pixel)),(int(p2[0]*pixel),int(p2[1]*pixel)), gray)
|
|
520
616
|
|
|
617
|
+
def render_pathwidth(line_chain: LineSegmentChain, steps: list[float], color: int = None, speed: int = None, boundingbox = None):
|
|
618
|
+
"""
|
|
619
|
+
Render - generate gcode for - a path of certain 'width'.
|
|
620
|
+
"""
|
|
621
|
+
# A path can be an 'engraving' or a 'cut'.
|
|
622
|
+
for step in steps: # step 'width'
|
|
623
|
+
if step:
|
|
624
|
+
# calculate delta chain
|
|
625
|
+
delta_chain = LineSegmentChain.delta_chain(line_chain, step)
|
|
626
|
+
|
|
627
|
+
# Note that append_line_chain generates a path cut when inverse_bw and speed are set to None.
|
|
628
|
+
self.append_line_chain(delta_chain, step, color, speed)
|
|
629
|
+
|
|
630
|
+
# update boundingbox of this 'name_id'
|
|
631
|
+
for line in delta_chain:
|
|
632
|
+
boundingbox.update(line.start)
|
|
633
|
+
boundingbox.update(line.end)
|
|
634
|
+
else:
|
|
635
|
+
# Note that append_line_chain generates a path cut when inverse_bw and speed are set to None.
|
|
636
|
+
self.append_line_chain(line_chain, 0, color, speed)
|
|
637
|
+
# update boundingbox of this 'name_id'
|
|
638
|
+
for line in line_chain:
|
|
639
|
+
boundingbox.update(line.start)
|
|
640
|
+
boundingbox.update(line.end)
|
|
641
|
+
|
|
642
|
+
def get_style_info_of_line_chain(line_chain: LineSegmentChain) -> ():
|
|
643
|
+
"""
|
|
644
|
+
Get style info of line_chain.
|
|
645
|
+
Returns tuple (stroke_width, stroke_color, stroke_alpha, fill_color, fill_alpha, fill_rule)
|
|
646
|
+
"""
|
|
647
|
+
# defaults
|
|
648
|
+
stroke_width = 0
|
|
649
|
+
stroke_color = ""
|
|
650
|
+
stroke_alpha = None
|
|
651
|
+
fill_color = None
|
|
652
|
+
fill_alpha = None
|
|
653
|
+
fill_rule = None
|
|
654
|
+
|
|
655
|
+
# get style info for this line chain
|
|
656
|
+
first_line_of_chain = line_chain.get(0)
|
|
657
|
+
style = self.parse_style_attribute(first_line_of_chain)
|
|
658
|
+
if style:
|
|
659
|
+
if not (style['pathcut'] == 'true' or self.settings["pathcut"]) \
|
|
660
|
+
and style['stroke'] is not None and style['stroke'] != "none":
|
|
661
|
+
stroke_color = style['stroke']
|
|
662
|
+
if style['stroke-opacity'] is not None:
|
|
663
|
+
# fill opacity attribute overrides rgba property
|
|
664
|
+
stroke_alpha = float(style['stroke-opacity'])
|
|
665
|
+
if not (stroke_alpha >=0 and stroke_alpha <= 1):
|
|
666
|
+
logger.warn(f"Opacity value '{stroke_alpha}' should be in range [0.0..1.0]!")
|
|
667
|
+
if style['stroke-width'] is not None and style['stroke-width'] != "none":
|
|
668
|
+
width = float(style['stroke-width'])
|
|
669
|
+
stroke_width = math.ceil(round(width/pixel_size, self.precision)/2)
|
|
670
|
+
if style['fill'] is not None and style['fill'] != 'none':
|
|
671
|
+
# Invert b&w value and apply alpha channel - when available - to the inverted b&w value
|
|
672
|
+
# Library function image2gcode - as it is now - cannot invert a color value and then apply the alpha channel.
|
|
673
|
+
# Note that step 6 of '# Render svg fill attribute' below, sets option 'invert' of image2gcode to false to use fill_color directly.)
|
|
674
|
+
fill_color = Image2gcode.linear_power(css_color.parse_css_color2bw8(style['fill']), self.settings["maximum_image_laser_power"])
|
|
675
|
+
if style['fill-rule'] is not None:
|
|
676
|
+
fill_rule = style['fill-rule']
|
|
677
|
+
if fill_rule == "nonzero":
|
|
678
|
+
logger.warn(f"fill-rule 'nonzero' of object '{name_id}' is currently unsupported!")
|
|
679
|
+
if style['fill-opacity'] is not None:
|
|
680
|
+
# fill opacity attribute overrides rgba property
|
|
681
|
+
fill_alpha = float(style['fill-opacity'])
|
|
682
|
+
if not (fill_alpha >=0 and fill_alpha <= 1):
|
|
683
|
+
logger.warn(f"Opacity value '{fill_alpha}' should be in range [0.0..1.0]!")
|
|
684
|
+
fill_alpha = 1
|
|
685
|
+
else:
|
|
686
|
+
rgba = css_color.parse_css_color(style['fill'])
|
|
687
|
+
fill_alpha = 1
|
|
688
|
+
if len(rgba) == 4:
|
|
689
|
+
fill_alpha = rgba[3]
|
|
690
|
+
fill_color = fill_color * fill_alpha
|
|
691
|
+
|
|
692
|
+
return (stroke_width, stroke_color, stroke_alpha, fill_color, fill_alpha, fill_rule)
|
|
693
|
+
|
|
694
|
+
|
|
521
695
|
path_curves = {}
|
|
522
696
|
pixel_size = float(self.settings["pixel_size"])
|
|
523
697
|
|
|
@@ -556,71 +730,33 @@ class Compiler:
|
|
|
556
730
|
# add line segments to curves having this id
|
|
557
731
|
path_curves[curve_name_id].append(line_chain)
|
|
558
732
|
|
|
559
|
-
# default stroke width is one line (thickness)
|
|
560
|
-
stroke_width = pixel_size
|
|
561
|
-
|
|
562
733
|
# emit all paths (organized by name id)
|
|
563
734
|
for name_id in path_curves:
|
|
735
|
+
|
|
564
736
|
steps = []
|
|
565
737
|
fill_color = None
|
|
738
|
+
fill_alpha = None
|
|
566
739
|
fill_rule = None
|
|
740
|
+
|
|
567
741
|
# set a boundingbox per 'name_id'
|
|
568
742
|
boundingbox = Boundingbox()
|
|
743
|
+
|
|
744
|
+
# Render svg 'stroke' attribute
|
|
569
745
|
for line_chain in path_curves[name_id]:
|
|
746
|
+
# get style info
|
|
747
|
+
stroke_width, stroke_color, stroke_alpha, fill_color, fill_alpha, fill_rule = get_style_info_of_line_chain(line_chain)
|
|
570
748
|
|
|
571
|
-
# default stroke width is one line (thickness)
|
|
572
|
-
width = 0
|
|
573
|
-
stroke_color = ""
|
|
574
|
-
stroke_alpha = None
|
|
575
|
-
|
|
576
|
-
# get style info for this line chain (of object 'name_id')
|
|
577
|
-
first_line_of_chain = line_chain.get(0)
|
|
578
|
-
style = self.parse_style_attribute(first_line_of_chain)
|
|
579
|
-
if style:
|
|
580
|
-
if not (style['pathcut'] == 'true' or self.settings["pathcut"]) \
|
|
581
|
-
and style['stroke'] is not None and style['stroke'] != "none":
|
|
582
|
-
stroke_color = style['stroke']
|
|
583
|
-
if style['stroke-opacity'] is not None:
|
|
584
|
-
# fill opacity attribute overrides rgba property
|
|
585
|
-
stroke_alpha = float(style['stroke-opacity'])
|
|
586
|
-
if not (stroke_alpha >=0 and stroke_alpha <= 1):
|
|
587
|
-
logger.warn(f"Opacity value '{stroke_alpha}' should be in range [0.0..1.0]!")
|
|
588
|
-
if style['stroke-width'] is not None and style['stroke-width'] != "none":
|
|
589
|
-
stroke_width = float(style['stroke-width'])
|
|
590
|
-
width = math.ceil(round(stroke_width/pixel_size, self.precision)/2)
|
|
591
|
-
if style['fill'] is not None:
|
|
592
|
-
# Invert b&w value and apply alpha channel - when available - to the inverted b&w value
|
|
593
|
-
# (library function image2gcode - as it is now - cannot invert a color value and then apply
|
|
594
|
-
# the alpha channel) Note that step 6 sets option 'invert' to false to be able to use the value below directly.
|
|
595
|
-
fill_color = Image2gcode.linear_power(css_color.parse_css_color2bw8(style['fill']), self.settings["maximum_image_laser_power"])
|
|
596
|
-
if style['fill-rule'] is not None:
|
|
597
|
-
fill_rule = style['fill-rule']
|
|
598
|
-
if fill_rule == "nonzero":
|
|
599
|
-
logger.warn(f"fill-rule 'nonzero' of object '{name_id}' is currently unsupported!")
|
|
600
|
-
if style['fill-opacity'] is not None:
|
|
601
|
-
# fill opacity attribute overrides rgba property
|
|
602
|
-
fill_alpha = float(style['fill-opacity'])
|
|
603
|
-
if not (fill_alpha >=0 and fill_alpha <= 1):
|
|
604
|
-
logger.warn(f"Opacity value '{fill_alpha}' should be in range [0.0..1.0]!")
|
|
605
|
-
fill_alpha = 1
|
|
606
|
-
else:
|
|
607
|
-
rgba = css_color.parse_css_color(style['fill'])
|
|
608
|
-
fill_alpha = 1
|
|
609
|
-
if len(rgba) == 4:
|
|
610
|
-
fill_alpha = rgba[3]
|
|
611
|
-
fill_color = fill_color * fill_alpha
|
|
612
|
-
|
|
613
|
-
# Get steps (offsets) for the lines that make the border
|
|
614
|
-
steps = [0]
|
|
615
|
-
if len(stroke_color) and width:
|
|
616
|
-
for delta in range(width):
|
|
617
|
-
if delta:
|
|
618
|
-
steps.append(round(delta * pixel_size, self.precision))
|
|
619
|
-
if not (delta == width and width % 2 != 0):
|
|
620
|
-
steps.append(round(-delta * pixel_size, self.precision))
|
|
621
|
-
|
|
622
|
-
# Get stroke alpha channel (opacity)
|
|
623
749
|
if len(stroke_color):
|
|
750
|
+
# Get steps (offsets) for the lines that make the border
|
|
751
|
+
steps = [0]
|
|
752
|
+
if stroke_width:
|
|
753
|
+
for delta in range(stroke_width):
|
|
754
|
+
if delta:
|
|
755
|
+
steps.append(round(delta * pixel_size, self.precision))
|
|
756
|
+
if not (delta == stroke_width and stroke_width % 2 != 0):
|
|
757
|
+
steps.append(round(-delta * pixel_size, self.precision))
|
|
758
|
+
|
|
759
|
+
# Get stroke alpha channel (opacity)
|
|
624
760
|
# fill opacity attribute overrides rgba property
|
|
625
761
|
if stroke_alpha is None:
|
|
626
762
|
stroke_alpha = 1
|
|
@@ -628,30 +764,49 @@ class Compiler:
|
|
|
628
764
|
if len(rgba) == 4:
|
|
629
765
|
stroke_alpha = rgba[3]
|
|
630
766
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
767
|
+
# engrave values
|
|
768
|
+
# set inversed b&w value (and apply alpha channel, when available)
|
|
769
|
+
inverse_bw = Image2gcode.linear_power(css_color.parse_css_color2bw8(stroke_color), \
|
|
770
|
+
self.settings["maximum_image_laser_power"]) * stroke_alpha
|
|
771
|
+
# set laser head movement speed
|
|
772
|
+
speed = self.settings["image_movement_speed"]
|
|
773
|
+
|
|
774
|
+
# handle option color_coded
|
|
775
|
+
if self.settings["color_coded"]:
|
|
776
|
+
# get color_coded info
|
|
777
|
+
ignorepath, cutpath, engravepath = self.parse_color_coded(stroke_color)
|
|
778
|
+
|
|
779
|
+
if not ignorepath:
|
|
780
|
+
|
|
781
|
+
if cutpath:
|
|
782
|
+
# color_coded set cut path for this stroke_color
|
|
783
|
+
self.body.extend([f"\n; --color_coded cut path '{name_id}', color '{stroke_color}'"])
|
|
784
|
+
render_pathwidth(line_chain, [0], None, None, boundingbox)
|
|
785
|
+
else:
|
|
786
|
+
# engrave path ...
|
|
787
|
+
pathignore, pathcut, pathengrave = self.color_coded_paths()
|
|
788
|
+
|
|
789
|
+
# ... if color_coded did not set engrave (at all) or set engrave for this stroke_color
|
|
790
|
+
if len(pathengrave) == 0 or engravepath:
|
|
791
|
+
if len(pathengrave) == 0:
|
|
792
|
+
self.body.extend([f"\n; --color_coded: engrave not set, path '{name_id}', color '{stroke_color}'"])
|
|
793
|
+
else:
|
|
794
|
+
self.body.extend([f"\n; --color_coded: engrave path '{name_id}', color '{stroke_color}'"])
|
|
795
|
+
render_pathwidth(line_chain, steps, inverse_bw, speed, boundingbox)
|
|
796
|
+
else:
|
|
797
|
+
self.body.extend([f"\n; --color_coded: ignore path '{name_id}', color '{stroke_color}'"])
|
|
798
|
+
|
|
645
799
|
else:
|
|
646
|
-
#
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
800
|
+
# color_coded isn't set: engrave path
|
|
801
|
+
self.body.extend([f"\n; engrave path '{name_id}', color '{stroke_color}'"])
|
|
802
|
+
render_pathwidth(line_chain, steps, inverse_bw, speed, boundingbox)
|
|
803
|
+
else:
|
|
804
|
+
# line stroke isn't set: cut path
|
|
805
|
+
self.body.extend([f"\n; no stroke color: cut path '{name_id}'"])
|
|
806
|
+
render_pathwidth(line_chain, [0], None, None, boundingbox)
|
|
807
|
+
|
|
808
|
+
# Render svg 'fill' attribute
|
|
809
|
+
if not self.settings["nofill"] and fill_color is not None and boundingbox.get() is not None:
|
|
655
810
|
# fill a path
|
|
656
811
|
# this is done in 6 steps:
|
|
657
812
|
# step 1: create two raster images matching the bbox
|
|
@@ -660,7 +815,9 @@ class Compiler:
|
|
|
660
815
|
# (fill rule 'nonzero' to be implemented later on see note below)
|
|
661
816
|
# step 4: draw white borders to erase fill overlap
|
|
662
817
|
# step 5: filter stray pixels (to remove noise from the action above)
|
|
663
|
-
# step 6: generate gcode from image_fill
|
|
818
|
+
# step 6: generate gcode from image_fill (by execution function image2gcode)
|
|
819
|
+
|
|
820
|
+
self.body.extend([f"\n; fill path '{name_id}'"])
|
|
664
821
|
|
|
665
822
|
# get bounding box info from the path border
|
|
666
823
|
lowerleft = boundingbox.get()[0]
|
|
@@ -754,11 +911,11 @@ class Compiler:
|
|
|
754
911
|
# for markers (border,inside border) and apply rule
|
|
755
912
|
# 'evenodd' to fill when an even number of borders
|
|
756
913
|
# is crossed, untill odd
|
|
757
|
-
# Note that
|
|
914
|
+
# Note that image 'image_fill' is filled (not 'image_mark') to make sure marks stay in place.
|
|
758
915
|
|
|
759
916
|
# Note that the fill algorithm can be adapted to support direct rendering of gcode (instead of rendering via 'image_fill' and
|
|
760
917
|
# function 'image2gcode', converting a raster image to gcode). This reduces the number of steps needed: steps 4, 5 and 6 can
|
|
761
|
-
# be left out. It does need a few
|
|
918
|
+
# be left out. It does need a few adaptations in the previous steps, namely drawing the line chain border (color '10') in full width
|
|
762
919
|
# (instead of 1 pixel) and draw the 'inside' line (color 128) one pixel further. In addition to this uncomment the '## direct gcode'
|
|
763
920
|
# lines of step 3 to activate gcode rendering.
|
|
764
921
|
|
|
@@ -914,7 +1071,6 @@ class Compiler:
|
|
|
914
1071
|
|
|
915
1072
|
## direct gcode: ignore this step
|
|
916
1073
|
# start gcode fill
|
|
917
|
-
self.body.extend([f"\n;fill '{name_id}'"])
|
|
918
1074
|
self.image2gcode(img_attrib, image_fill)
|
|
919
1075
|
|
|
920
1076
|
def check_axis_maximum_travel(self):
|
|
@@ -929,9 +1085,10 @@ class Compiler:
|
|
|
929
1085
|
:return true when box is in machine area bounds, false otherwise
|
|
930
1086
|
"""
|
|
931
1087
|
|
|
932
|
-
if self.settings["distance_mode"] == "absolute" and self.check_axis_maximum_travel():
|
|
1088
|
+
if self.settings["distance_mode"] == "absolute" and self.check_axis_maximum_travel() and self.boundingbox.get():
|
|
933
1089
|
machine_max = Vector(self.settings["x_axis_maximum_travel"],self.settings["y_axis_maximum_travel"])
|
|
934
1090
|
bbox = self.boundingbox.get()
|
|
1091
|
+
|
|
935
1092
|
# bbox[0] == lowerleft, bbox[1] == uperright, bbox[0/1][0] == x, bbox[0/1][1] == y
|
|
936
1093
|
# lower left x and y >= 0 and upperright x and y <= resp. machine max x and y
|
|
937
1094
|
return (bbox[0][0] >= 0 and bbox[0][1] >=0
|
|
@@ -253,8 +253,8 @@ def parse_css_color(color: str) -> [int,int,int,float]:
|
|
|
253
253
|
rgbcolor = [int(rgbcolor[0] * 255), int(rgbcolor[1] * 255), int(rgbcolor[2] * 255), a]
|
|
254
254
|
|
|
255
255
|
if not rgbcolor:
|
|
256
|
-
logger.warn(f"Not a valid css color: '{color}', color set to '
|
|
257
|
-
rgbcolor = css_color_keywords['
|
|
256
|
+
logger.warn(f"Not a valid css color: '{color}', color set to 'white'!")
|
|
257
|
+
rgbcolor = css_color_keywords['white']["decimal"]
|
|
258
258
|
|
|
259
259
|
return rgbcolor
|
|
260
260
|
|
|
@@ -274,6 +274,12 @@ def rgb24tobw8(rgb24: [int,int,int,float]) -> int:
|
|
|
274
274
|
"""
|
|
275
275
|
return int(rgb24[0] * 0.299 + rgb24[1] * 0.587 + rgb24[2] * 0.114) & 0xff
|
|
276
276
|
|
|
277
|
+
def rgb24equal(rgb24, rgb24_2) -> bool:
|
|
278
|
+
"""
|
|
279
|
+
compare two 24 bit rgb (color) values
|
|
280
|
+
"""
|
|
281
|
+
return (rgb24[0] == rgb24_2[0]) and (rgb24[1] == rgb24_2[1]) and (rgb24[2] == rgb24_2[2])
|
|
282
|
+
|
|
277
283
|
def parse_css_color2bw8(color: str) -> int:
|
|
278
284
|
"""
|
|
279
285
|
parse css color string to 8 bit b&w value
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|