Re-add line-us & gcode utils
This commit is contained in:
parent
d88a17f54e
commit
37e152bc60
3 changed files with 185 additions and 0 deletions
74
src/heyarne/line_us/connection.clj
Normal file
74
src/heyarne/line_us/connection.clj
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
(ns heyarne.line-us.connection
|
||||||
|
(:require [clojure.java.io :as io])
|
||||||
|
(:import [java.net Socket]))
|
||||||
|
|
||||||
|
;; https://github.com/Line-us/Line-us-Programming/blob/master/Documentation/LineUsDrawingArea.pdf
|
||||||
|
(def drawing-area
|
||||||
|
{:x [800 1700]
|
||||||
|
:y [-900 900]
|
||||||
|
:z [0 1000]})
|
||||||
|
|
||||||
|
(defn connect
|
||||||
|
[url port]
|
||||||
|
(Socket. url port))
|
||||||
|
|
||||||
|
(defn disconnect [^Socket line-us]
|
||||||
|
(.close line-us))
|
||||||
|
|
||||||
|
(defn read-response [^Socket line-us]
|
||||||
|
(let [stream (io/input-stream line-us)]
|
||||||
|
(loop [line []]
|
||||||
|
(let [c (.read stream)]
|
||||||
|
(case c
|
||||||
|
0 (when-not (empty? line)
|
||||||
|
(apply str (map char line)))
|
||||||
|
-1 (recur line)
|
||||||
|
(recur (conj line c)))))))
|
||||||
|
|
||||||
|
(defmacro validate-coord
|
||||||
|
"Throws an exception when a coordinate is outside of the drawable area"
|
||||||
|
[coord]
|
||||||
|
(let [[min-v# max-v#] (get drawing-area (keyword (name coord)))]
|
||||||
|
`(when-not (< ~(dec min-v#) ~coord ~(inc max-v#))
|
||||||
|
(throw (IllegalArgumentException.
|
||||||
|
(str ~(name coord) " should be in range [" ~min-v# ", " ~max-v# "] but is " ~coord))))))
|
||||||
|
|
||||||
|
(defn- send-command! [^Socket line-us ^String raw-cmd]
|
||||||
|
;; this is basically taken from the Processing example code and the processing
|
||||||
|
;; "Client" class
|
||||||
|
(doto (io/output-stream line-us)
|
||||||
|
(.write (.getBytes (str raw-cmd "\0")))
|
||||||
|
(.flush))
|
||||||
|
;; wait for the response
|
||||||
|
(let [res (read-response line-us)]
|
||||||
|
(if-not (re-find #"^(ok|hello)" res)
|
||||||
|
(throw (Exception. res))
|
||||||
|
res)))
|
||||||
|
|
||||||
|
(defn send-movement!
|
||||||
|
"Moves the arm to the [x y z] vector that is coord. Coordinates are validated
|
||||||
|
to be inside the valid drawing area."
|
||||||
|
[^Socket line-us [x y z :as coords]]
|
||||||
|
(validate-coord x)
|
||||||
|
(validate-coord y)
|
||||||
|
(validate-coord z)
|
||||||
|
(send-command! line-us (str "G01 X" x " Y" y " Z" z))
|
||||||
|
coords)
|
||||||
|
|
||||||
|
(defn move-home! [^Socket line-us]
|
||||||
|
(send-movement! line-us [1000 1000 1000]))
|
||||||
|
|
||||||
|
(defn- parse-coords [^String raw-coords]
|
||||||
|
(->>
|
||||||
|
(re-find #"X:(-?\d+) Y:(\d+) Z:(\d+)" raw-coords)
|
||||||
|
(rest)
|
||||||
|
(mapv #(Integer/parseInt % 10))))
|
||||||
|
|
||||||
|
(defn move-up! [^Socket line-us]
|
||||||
|
(let [[x y _] (parse-coords (send-command! line-us "M114"))]
|
||||||
|
(send-movement! line-us [x y 1000])))
|
||||||
|
|
||||||
|
(defn move-down! [^Socket line-us]
|
||||||
|
(let [[x y _] (parse-coords (send-command! line-us "M114"))]
|
||||||
|
(send-movement! line-us [x y 200])))
|
||||||
|
|
||||||
76
src/heyarne/line_us/gcode.clj
Normal file
76
src/heyarne/line_us/gcode.clj
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
(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]
|
||||||
|
[thi.ng.geom.types]
|
||||||
|
[thi.ng.geom.vector :as v]
|
||||||
|
[thi.ng.geom.circle :as c])
|
||||||
|
(:import [thi.ng.geom.types Line2 LineStrip2 Circle2]))
|
||||||
|
|
||||||
|
(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."))
|
||||||
|
|
||||||
|
(defn- point-seq->gcode [pts]
|
||||||
|
(let [vertices pts
|
||||||
|
[l-x l-y] (last vertices)]
|
||||||
|
(conj (mapv (fn [[x y]]
|
||||||
|
[x y 0]) vertices)
|
||||||
|
[l-x l-y 1000])))
|
||||||
|
|
||||||
|
(defn gcode-seq->str [gcode-seq]
|
||||||
|
(map (fn [[x y z]]
|
||||||
|
(str "G01 X" x " Y" y " Z" z)) gcode-seq))
|
||||||
|
|
||||||
|
(extend-protocol GCode
|
||||||
|
Line2
|
||||||
|
(->gcode
|
||||||
|
([_] (point-seq->gcode (:points _)))
|
||||||
|
([_ r] (->gcode _)))
|
||||||
|
|
||||||
|
LineStrip2
|
||||||
|
(->gcode
|
||||||
|
([_] (point-seq->gcode (:points _)))
|
||||||
|
([_ r] (->gcode _)))
|
||||||
|
|
||||||
|
Circle2
|
||||||
|
(->gcode
|
||||||
|
([_] (point-seq->gcode (:points (g/as-polygon _))))
|
||||||
|
([_ r] (point-seq->gcode (:points (g/as-polygon _ r))))))
|
||||||
|
|
||||||
|
(defn scene->gcode
|
||||||
|
"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]
|
||||||
|
(loop [[el & rs] scene
|
||||||
|
res []]
|
||||||
|
(cond
|
||||||
|
(seq? 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)))))
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(require '[thi.ng.geom.circle :as c])
|
||||||
|
|
||||||
|
;; convert a single element
|
||||||
|
(->
|
||||||
|
(->gcode (c/circle) 10)
|
||||||
|
(gcode-seq->str))
|
||||||
|
|
||||||
|
;; convert a tree of elements
|
||||||
|
(scene->gcode
|
||||||
|
[(c/circle 10)
|
||||||
|
[(c/circle [0 20] 10)
|
||||||
|
(c/circle [0 40] 10)]])
|
||||||
|
)
|
||||||
35
src/heyarne/line_us/helpers.clj
Normal file
35
src/heyarne/line_us/helpers.clj
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
(ns heyarne.line-us.helpers
|
||||||
|
(:require [thi.ng.geom.rect :as rect]
|
||||||
|
[thi.ng.math.core :as m]))
|
||||||
|
|
||||||
|
(defn g01-bounds
|
||||||
|
"Returns a rect representing the drawing bounds of a sequence of g01 coords"
|
||||||
|
[g01]
|
||||||
|
(let [bounds [Double/MAX_VALUE Double/MIN_VALUE Double/MIN_VALUE Double/MAX_VALUE]
|
||||||
|
[top right bottom left] (reduce (fn [[top right bottom left] [x y _]]
|
||||||
|
[(min top y) (max right x)
|
||||||
|
(max bottom y) (min left x)])
|
||||||
|
bounds
|
||||||
|
g01)]
|
||||||
|
(rect/rect [left bottom] [right top])))
|
||||||
|
|
||||||
|
(defn rescale
|
||||||
|
"Returns a new g01 sequence that is proportionally scaled to fit into the
|
||||||
|
bounding box passed in as the second argument. Assumes that the top left
|
||||||
|
in g01-seq is at [0 0]."
|
||||||
|
[g01-seq [top right bottom left]]
|
||||||
|
(let [s-bounds (g01-bounds g01-seq)
|
||||||
|
t-bounds (rect/rect [left bottom] [right top])
|
||||||
|
;; we need to translate the bounding box
|
||||||
|
[translate-x translate-y] (m/+ (:p s-bounds) (:p t-bounds))
|
||||||
|
;; and scale it
|
||||||
|
[s-x s-y] (:p s-bounds)
|
||||||
|
[t-x t-y] (:p t-bounds)
|
||||||
|
[s-width s-height] (:size s-bounds)
|
||||||
|
[t-width t-height] (:size t-bounds)
|
||||||
|
factor (min (/ (+ t-x t-width) (+ s-x s-width)) (/ (+ t-y t-height) (+ s-y s-height)))]
|
||||||
|
(map (fn [coord]
|
||||||
|
(-> (update coord 0 #(+ translate-x (* % factor)))
|
||||||
|
(update 1 #(+ translate-y (* % factor)))))
|
||||||
|
g01-seq)))
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue