Software: pattern player, web page with a design library, GRBL tools
- python3 -m sandtable web: local web page to play designs from a library of 27 built-in patterns, upload .thr or .svg, pause/skip/stop, jog and calibrate. Reconnects by itself when the USB cable comes back. - Plays .thr files and built-in generators with an erase spiral between them, constant ball speed, host-side homing on stock GRBL 1.1 (Uno + CNC shield), FluidNC for the ESP32 board. - Home switch guard: patterns never press the switch, so a press means lost steps and stops the machine at once with an alert. - lineart.py turns SVG line drawings into one continuous path. - Tests against a GRBL simulator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1400cc8afd
commit
95ad4d25de
52 files changed
+388117
No files matched your search
@@ -0,0 +1,50 @@
|
||||
"""Read and write .thr (theta-rho) pattern files.
|
||||
|
||||
Each line holds "<theta> <rho>":
|
||||
theta: angle in radians, continuous (it keeps counting past 2*pi; it is not wrapped)
|
||||
rho: distance from the centre, 0 (centre) to 1 (edge)
|
||||
Lines starting with '#' or '//' are comments. This is the format used by Sandify,
|
||||
Sisyphus and Dune Weaver.
|
||||
"""
|
||||
import math
|
||||
|
||||
Pattern = list # list of (theta_rad, rho) tuples
|
||||
|
||||
|
||||
def loads(text):
|
||||
pts = []
|
||||
for raw in text.splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith('#') or line.startswith('//'):
|
||||
continue
|
||||
parts = line.replace(',', ' ').split()
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
try:
|
||||
th, rho = float(parts[0]), float(parts[1])
|
||||
except ValueError:
|
||||
continue
|
||||
if math.isfinite(th) and math.isfinite(rho):
|
||||
pts.append((th, min(1.0, max(0.0, rho))))
|
||||
return pts
|
||||
|
||||
|
||||
def load(path):
|
||||
with open(path, encoding='utf-8', errors='replace') as f:
|
||||
pts = loads(f.read())
|
||||
if len(pts) < 2:
|
||||
raise ValueError(f'{path}: no theta-rho points found')
|
||||
return pts
|
||||
|
||||
|
||||
def dumps(pts, header=None):
|
||||
out = []
|
||||
if header:
|
||||
out += [f'# {h}' for h in header.splitlines()]
|
||||
out += [f'{th:.5f} {rho:.5f}' for th, rho in pts]
|
||||
return '\n'.join(out) + '\n'
|
||||
|
||||
|
||||
def save(path, pts, header=None):
|
||||
with open(path, 'w') as f:
|
||||
f.write(dumps(pts, header))
|
||||
Reference in new issue
Block a user