Exploratory scripts for laser cutter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.3 KiB

  1. #!/usr/bin/env python3
  2. """svgturtle - simple library to generate SVG paths from turtle graphics style commands"""
  3. import math
  4. import sys
  5. class SvgTurtle():
  6. def __init__(self, homex=0, homey=0):
  7. """Create a new turtle at the given home location, facing right"""
  8. self.homex = homex
  9. self.homey = homey
  10. self.cvtangle = math.tau/360
  11. self.reset()
  12. def penup(self):
  13. """Subsequent movements will be invisible and only affect location and heading"""
  14. self.pen = False
  15. def pendown(self):
  16. """Subsequent movements will be visible and drawn"""
  17. self.pen = True
  18. def forward(self, distance):
  19. """Move forward"""
  20. if self.pen and (self.path == ''):
  21. self.path = "M %.2f,%.2f" % (self.x,self.y)
  22. dx = distance*math.cos(self.heading)
  23. dy = distance*math.sin(self.heading)
  24. self.x += dx
  25. self.y += dy
  26. if self.pen:
  27. if abs(dy) < .01:
  28. self.path += " h %.2f" % dx
  29. elif abs(dx) < .01:
  30. self.path += " v %.2f" % dy
  31. else:
  32. self.path += " l %.2f,%.2f" % (dx, dy)
  33. elif self.path != '':
  34. self.path += " m %.2f, %.2f" % (dx, dy)
  35. def back(self, distance):
  36. """Move backward"""
  37. self.forward(-distance)
  38. def left(self, angle):
  39. """Turn left by angle specified in degrees"""
  40. self.right(-angle)
  41. def right(self, angle):
  42. """Turn right by angle specified in degrees"""
  43. self.heading = (self.heading + angle*self.cvtangle) % math.tau
  44. def circle(self, radius, extent=360, steps=None):
  45. """Draw a circle or arc spanning extent degrees around a center
  46. radius units to the left (if radius is positive) or right
  47. (if radius is negative). Use a polygon if steps is specified,
  48. otherwise a circle."""
  49. if steps:
  50. w = 1.0*extent/steps
  51. w2 = 0.5*w
  52. l = 2.0*radius*math.sin(w2*math.pi/180.0)
  53. if radius < 0:
  54. l, w, w2 = -l, -w, -w2
  55. self.left(w2)
  56. for i in range(steps):
  57. self.forward(l)
  58. self.left(w)
  59. self.right(w2)
  60. else:
  61. if extent>355:
  62. self.circle(radius, 355)
  63. extent -= 355
  64. ra = self.cvtangle*(extent if radius < 0 else -extent)
  65. cx = self.x+radius*math.cos(self.heading-.5*math.pi)
  66. cy = self.y+radius*math.sin(self.heading-.5*math.pi)
  67. th = self.heading+.5*math.pi+ra
  68. dx = cx+radius*math.cos(th)-self.x
  69. dy = cy+radius*math.sin(th)-self.y
  70. lg = 1 if extent >= 180 else 0
  71. sw = 0 if radius > 0 else 1
  72. if self.pen:
  73. if self.path == '':
  74. self.path = "M %.2f,%.2f" % (self.x,self.y)
  75. self.path += " a %.2f %.2f %.2f %d %d %.2f %.2f" % (radius, radius, extent, lg, sw, dx, dy)
  76. elif self.path != '':
  77. self.path += " m %.2f, %.2f" % (dx, dy)
  78. self.x += dx
  79. self.y += dy
  80. self.heading = (self.heading + ra) % math.tau
  81. def to_s(self):
  82. """Return the generated path, suitable for the d attribute of an SVG <path> element"""
  83. return self.path
  84. def home(self):
  85. """Reset to the initial position and heading"""
  86. self.x = self.homex
  87. self.y = self.homey
  88. self.heading = 0
  89. if self.path != '':
  90. self.path += " M %.2f, %.2f" % (self.x, self.y)
  91. def reset(self):
  92. """Clear the path and return home"""
  93. self.path = ''
  94. self.pen = True
  95. self.home()
  96. if __name__ == "__main__":
  97. turtle = SvgTurtle(50, 100)
  98. turtle.left(90)
  99. turtle.forward(50)
  100. turtle.left(30)
  101. turtle.forward(50)
  102. turtle.penup()
  103. turtle.back(50)
  104. turtle.right(60)
  105. turtle.pendown()
  106. turtle.forward(50)
  107. turtle.penup()
  108. turtle.back(50)
  109. turtle.left(30)
  110. turtle.back(50)
  111. turtle.right(90)
  112. turtle.forward(70)
  113. turtle.pendown()
  114. turtle.circle(25)
  115. print('<svg viewBox="0 0 170 110" xmlns="http://www.w3.org/2000/svg">')
  116. print('<path fill="none" stroke="blue" d="%s"/>' % turtle.to_s())
  117. print('</svg>')