(ns heyarne.line-us.gcode "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] [clojure.string :as str] [thi.ng.geom.circle :as c] [thi.ng.geom.rect :as r])) ;; scroll down for a rich comment with usage examples (defn- vertices->gcode-verts [vertices] (let [[f-x f-y] (first vertices) [l-x l-y] (last vertices)] (concat [[f-x f-y 1000]] (mapv (fn [[x y]] [x y 0]) vertices) [[l-x l-y 1000]]))) (defn- gcode-verts->gcode-seq [gcode-verts] (map (fn [[x y z]] (str "G01 X" x " Y" y " Z" z)) gcode-verts)) (defn- ->gcode-verts ([geom] (->gcode-verts geom {})) ([geom {:keys [r]}] (vertices->gcode-verts (try (if r (g/vertices geom r) (g/vertices geom)) (catch IllegalArgumentException _ (g/vertices geom)))))) (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]. 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- 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. A scene is hiccup like in that each object can be followed by a map of attributes, which can provide addition information about the preceding object." ([scene] (scene->gcode-seq scene {})) ([scene {:keys [r] :as opts}] (loop [[el & rs] scene result []] (cond (nil? el) result (sequential? el) (recur rs (vec (concat result (scene->gcode-seq el opts)))) ;; recursively convert sequences (geom? el) (recur rs (conj result (if r (gcode-seq el r) (gcode-seq el)))) ;; convert geoms (map? el) (recur rs result))))) ;; ignore attribute maps (comment ;; example usage: (require '[thi.ng.geom.line :as l]) (require '[thi.ng.geom.circle :as c]) (require '[thi.ng.geom.rect :as r]) ;; `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") ;; you can pass a second argument to increase or decrease resolution (gcode-seq (c/circle) {:r 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 arbitrarily nested 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]) [(c/circle [5 5] 10)] (r/rect [20 20] [40 40])]] {:r 10}) ;; => [("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 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 X15.0 Y5.0 Z1000" ;; "G01 X15.0 Y5.0 Z0" ;; "G01 X13.090169943749475 Y10.877852522924732 Z0" ;; "G01 X8.090169943749475 Y14.510565162951535 Z0" ;; "G01 X1.9098300562505264 Y14.510565162951536 Z0" ;; "G01 X-3.0901699437494727 Y10.877852522924734 Z0" ;; "G01 X-5.0 Y5.000000000000001 Z0" ;; "G01 X-3.0901699437494763 Y-0.87785252292473 Z0" ;; "G01 X1.9098300562505246 Y-4.510565162951535 Z0" ;; "G01 X8.090169943749473 Y-4.510565162951536 Z0" ;; "G01 X13.090169943749473 Y-0.8778525229247336 Z0" ;; "G01 X13.090169943749473 Y-0.8778525229247336 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")] )