diff --git a/src/heyarne/line_us/gcode.clj b/src/heyarne/line_us/gcode.clj index 7412cba..f083ffe 100644 --- a/src/heyarne/line_us/gcode.clj +++ b/src/heyarne/line_us/gcode.clj @@ -2,48 +2,44 @@ "Provides functions to move from geometry types provided by thi.ng to gcode so you can plot them." (:require [thi.ng.geom.core :as g] - [thi.ng.geom.types] - [thi.ng.geom.vector :as v] - [thi.ng.geom.circle :as c]) - (:import [thi.ng.geom.types Line2 LineStrip2 Circle2])) + [clojure.string :as str])) -(defprotocol GCode - "Convert thi.ng.geom types into sequences of GCode to plot them. The GCode - instructions are given as a sequence of vectors, where each vector is given - as [x y z]." - (->gcode [_] [_ r] - "Returns G01 movements on the x, y, and z axis. Z is constantly 1000 in the - current implementation, but this might change in the future. The optional - parameter `r` can be used to adjust the resolution.")) +;; scroll down for a rich comment with usage examples -(defn- point-seq->gcode [pts] - (let [vertices pts +(defn- vertices->gcode-verts [vertices] + (let [[f-x f-y] (first vertices) [l-x l-y] (last vertices)] - (conj (mapv (fn [[x y]] - [x y 0]) vertices) - [l-x l-y 1000]))) + (concat + [[f-x f-y 1000]] + (mapv (fn [[x y]] [x y 0]) vertices) + [[l-x l-y 1000]]))) -(defn gcode-seq->str [gcode-seq] +(defn- gcode-verts->gcode-seq [gcode-verts] (map (fn [[x y z]] - (str "G01 X" x " Y" y " Z" z)) gcode-seq)) + (str "G01 X" x " Y" y " Z" z)) gcode-verts)) -(extend-protocol GCode - Line2 - (->gcode - ([_] (point-seq->gcode (:points _))) - ([_ r] (->gcode _))) +(defn- ->gcode-verts + ([geom] + (vertices->gcode-verts (g/vertices geom))) + ([geom r] + (vertices->gcode-verts (g/vertices geom r)))) - LineStrip2 - (->gcode - ([_] (point-seq->gcode (:points _))) - ([_ r] (->gcode _))) +(def gcode-seq + "Convert thi.ng.geom types into a sequence of G-Code to plot them. The G-Code + instructions are given as a sequence of vectors, where each vector is given + as [x y z]. - Circle2 - (->gcode - ([_] (point-seq->gcode (:points (g/as-polygon _)))) - ([_ r] (point-seq->gcode (:points (g/as-polygon _ r)))))) + Returns G01 movements on the x, y, and z axis. Z is constantly 1000 in the + current implementation, but this might change in the future. The optional + parameter `r` can be used to adjust the resolution." + (comp gcode-verts->gcode-seq ->gcode-verts)) -(defn scene->gcode +(defn geom? [x] + (str/starts-with? (pr-str x) "#thi.ng.geom.")) + +;; (pr-str (c/circle 20)) ;; => "#thi.ng.geom.types.Circle2{:p [0.0 0.0], :r 20.0}" + +(defn scene->gcode-seq "Converts a scene into a sequence of gcode instructions. A scene is an arbitrarily nested vector of geom instances. @@ -52,25 +48,86 @@ object." [scene] (loop [[el & rs] scene - res []] + result []] (cond - (vector? el) (recur (scene->gcode el) res) - (map? el) (recur rs res) - :else (if (seq rs) - (recur rs (conj res (gcode-seq->str (->gcode el)))) - (flatten res))))) + (nil? el) result + (sequential? el) (recur rs (concat result (scene->gcode-seq el))) ;; recursively convert sequences + (geom? el) (recur rs (conj result (gcode-seq el))) ;; convert geoms + (map? el) (recur rs result)))) ;; ignore attribute maps + +;; TODO: Resolution for scenes? +;; `g/vertices` fails when called with a second arg for `r/rect` (comment + ;; example usage: + (require '[thi.ng.geom.line :as l]) (require '[thi.ng.geom.circle :as c]) + (require '[thi.ng.geom.rect :as r]) - ;; convert a single element - (-> - (->gcode (c/circle) 10) - (gcode-seq->str)) + ;; `gcode-seq`` returns a sequence of gcode strings, where z=0 means "down" + (gcode-seq (l/line2 [100 100] [200 200])) + ;; => ("G01 X100.0 Y100.0 Z1000" + ;; "G01 X100.0 Y100.0 Z0" + ;; "G01 X200.0 Y200.0 Z0" + ;; "G01 X200.0 Y200.0 Z1000") - ;; convert a tree of elements - (scene->gcode - [(c/circle 10) - [(c/circle [0 20] 10) - (c/circle [0 40] 10)]]) - ) + ;; you can pass a second argument to increase or decrease resolution + (gcode-seq (c/circle) 10) + ;; => ("G01 X1.0 Y0.0 Z1000" + ;; "G01 X1.0 Y0.0 Z0" + ;; "G01 X0.8090169943749475 Y0.5877852522924731 Z0" + ;; "G01 X0.30901699437494745 Y0.9510565162951535 Z0" + ;; "G01 X-0.30901699437494734 Y0.9510565162951536 Z0" + ;; "G01 X-0.8090169943749473 Y0.5877852522924732 Z0" + ;; "G01 X-1.0 Y1.2246467991473532E-16 Z0" + ;; "G01 X-0.8090169943749476 Y-0.587785252292473 Z0" + ;; "G01 X-0.30901699437494756 Y-0.9510565162951535 Z0" + ;; "G01 X0.30901699437494723 Y-0.9510565162951536 Z0" + ;; "G01 X0.8090169943749473 Y-0.5877852522924734 Z0" + ;; "G01 X0.8090169943749473 Y-0.5877852522924734 Z1000") + + ;; `scene->gcode-seq` converts a tree of elements into a sequence of gcode drawing instructions + (scene->gcode-seq + [[(r/rect [0 0] [10 10]) {:attributes/ignored? true}] + (r/rect [0 0] [10 10]) + [(r/rect [5 5] [15 15]) + (r/rect [20 20] [40 40]) + [(r/rect [5 5] [15 15])] + (r/rect [20 20] [40 40])]]) + ;; => (("G01 X0.0 Y0.0 Z1000" + ;; "G01 X0.0 Y0.0 Z0" + ;; "G01 X10.0 Y0.0 Z0" + ;; "G01 X10.0 Y10.0 Z0" + ;; "G01 X0.0 Y10.0 Z0" + ;; "G01 X0.0 Y10.0 Z1000") + ;; ("G01 X0.0 Y0.0 Z1000" + ;; "G01 X0.0 Y0.0 Z0" + ;; "G01 X10.0 Y0.0 Z0" + ;; "G01 X10.0 Y10.0 Z0" + ;; "G01 X0.0 Y10.0 Z0" + ;; "G01 X0.0 Y10.0 Z1000") + ;; ("G01 X20.0 Y20.0 Z1000" + ;; "G01 X20.0 Y20.0 Z0" + ;; "G01 X40.0 Y20.0 Z0" + ;; "G01 X40.0 Y40.0 Z0" + ;; "G01 X20.0 Y40.0 Z0" + ;; "G01 X20.0 Y40.0 Z1000") + ;; ("G01 X5.0 Y5.0 Z1000" + ;; "G01 X5.0 Y5.0 Z0" + ;; "G01 X15.0 Y5.0 Z0" + ;; "G01 X15.0 Y15.0 Z0" + ;; "G01 X5.0 Y15.0 Z0" + ;; "G01 X5.0 Y15.0 Z1000") + ;; ("G01 X20.0 Y20.0 Z1000" + ;; "G01 X20.0 Y20.0 Z0" + ;; "G01 X40.0 Y20.0 Z0" + ;; "G01 X40.0 Y40.0 Z0" + ;; "G01 X20.0 Y40.0 Z0" + ;; "G01 X20.0 Y40.0 Z1000") + ;; ("G01 X5.0 Y5.0 Z1000" + ;; "G01 X5.0 Y5.0 Z0" + ;; "G01 X15.0 Y5.0 Z0" + ;; "G01 X15.0 Y15.0 Z0" + ;; "G01 X5.0 Y15.0 Z0" + ;; "G01 X5.0 Y15.0 Z1000")) +)