File: svgcode.py

package info (click to toggle)
bcnc 0.9.14.318%2Bds-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,072 kB
  • sloc: python: 39,148; sh: 45; makefile: 44
file content (67 lines) | stat: -rw-r--r-- 2,375 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# SVGcode 0.2
# Converts SVG paths to g-code
# (c) 2018 - Tomas 'Harvie' Mudrunka
# https://github.com/harvie
# License: GPLv2+

# Usage:
# svgcode = SVGcode('./image.svg')
# for path in svgcode.get_gcode():
#	print(path['id'])
#	print(path['path'])

import numpy
from svgelements import SVG, Path, Move, Shape, Line, Close, Arc


class SVGcode:
	def __init__(self, filepath=None):
		self._filepath = filepath

	def path2gcode(self, path, samples_per_unit=100, d=4):
		gcode = []
		if isinstance(path, str):
			path = Path(path)

		def rv(v):
			return ('%*f' % (d, round(v, d))).rstrip("0").rstrip(".")

		for segment in path:
			subdiv=max(1, round(segment.length(error=1e-5) * samples_per_unit))

			if isinstance(segment, Move):
				gcode.append('G0 X%s Y%s' % (rv(segment.end.x), rv(-segment.end.y)))
			elif isinstance(segment, (Line, Close)):
				gcode.append('G1 X%s Y%s' % (rv(segment.end.x), rv(-segment.end.y)))
			elif isinstance(segment, Arc) and abs(segment.rx - segment.ry) < 1e-9:
				# Strictly speaking, svg arcs can be non circular, whereas gcode only permits circular arcs.
				garc = "G02" if segment.sweep > 0 else "G03"
				gcode.append('%s X%s Y%s R%s' % (garc,
												 rv(segment.end.x),
												 rv(-segment.end.y),
												 rv(segment.rx)))
			else:  # Non-circular arc, Cubic or Quad Bezier Curves.
				subdiv_points = numpy.linspace(0, 1, subdiv, endpoint=True)[1:]
				points = segment.npoint(subdiv_points)  # numpy accelerated point() call
				gcode.extend(['G1 X%s Y%s' % (rv(sp[0]), rv(-sp[1])) for sp in points])

		return '\n'.join(gcode)

	def get_gcode(self, scale=1.0/96.0, samples_per_unit=100, digits=4, ppi=96.0):
		"""
		Parse gcode from an SVG file.

		scale: unit scaling between svg pixels and desired units. 1.0/96.0 is inches.
		subdivratio: How many subdivisions per unit? 1/100th inch steps.
		digits: How many digits of gcode accuracy.
		ppi: pixels per inch of the file being loaded. 96 is standard.
		"""
		gcode = []
		transform = "scale(%g)" % scale if scale != 1.0 else None
		svg = SVG.parse(self._filepath, reify=False, ppi=ppi, transform=transform)
		for element in svg.elements():
			if isinstance(element, Shape):
				if not isinstance(element, Path):
					element = Path(element)
				gcode.append({'id': element.id, 'path': self.path2gcode(element.reify(), samples_per_unit, digits)})
		return gcode