Copy over all line-us plotting code from fa9dbf2120
There was a problem with origami which should probably be solved by narrowing the focus of the git Line-us git repository
This commit is contained in:
parent
e8b7075974
commit
46fc472a40
4 changed files with 234 additions and 0 deletions
73
src/heyarne/line_us/connection.clj
Normal file
73
src/heyarne/line_us/connection.clj
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
(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])))
|
||||
79
src/heyarne/line_us/edge_detection.clj
Normal file
79
src/heyarne/line_us/edge_detection.clj
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
(ns heyarne.line-us.edge-detection
|
||||
(:require [clojure.set :as set]
|
||||
[opencv4.core :as cv]
|
||||
[opencv4.colors.rgb :as rgb]
|
||||
[opencv4.utils :as u])
|
||||
(:import [org.opencv.core Point Rect]))
|
||||
|
||||
(def img (-> (u/mat-from-url "https://raw.githubusercontent.com/hellonico/origami/master/doc/cat_in_bowl.jpeg")
|
||||
(cv/cvt-color! cv/COLOR_RGB2GRAY)
|
||||
(u/resize-by 0.2)
|
||||
(cv/canny! 300.0 100.0 3 true)
|
||||
(cv/bitwise-not!))) ;; NOTE: The bitwise-not! here is not actually needed
|
||||
|
||||
(defn pixels
|
||||
"Returns a vector of
|
||||
- all pixels in an org.opencv.core.Mat as a byte-array
|
||||
- width
|
||||
- height"
|
||||
[mat]
|
||||
(let [out (byte-array (* (.total mat) (.channels mat)))]
|
||||
(.get mat 0 0 out)
|
||||
[out (.cols mat) (.rows mat)]))
|
||||
|
||||
(defn neighborhood [pixels width height x y]
|
||||
(for [y-off [-1 0 1]
|
||||
x-off [-1 0 1]
|
||||
:let [y' (+ y y-off)
|
||||
x' (+ x x-off)]
|
||||
:when (and (< -1 x' width)
|
||||
(< -1 y' height)
|
||||
(not= y-off x-off 0))]
|
||||
[[x' y'] (nth pixels (+ x' (* y' width)))]))
|
||||
|
||||
(def shape? zero?)
|
||||
|
||||
(defn next-connection
|
||||
"Returns the first neighbor of a pixel that's part of the same shape"
|
||||
[pixels width height contour coord]
|
||||
(let [neighbors (apply neighborhood pixels width height coord)]
|
||||
(reduce (fn [_ [neighbor-coord v]]
|
||||
(when (and (shape? v)
|
||||
(not (contour neighbor-coord)))
|
||||
(reduced neighbor-coord))) neighbors)))
|
||||
|
||||
(defn next-start [contour-pixels found-contours]
|
||||
(first (remove (apply set/union found-contours) contour-pixels)))
|
||||
|
||||
(defn radial-sweep
|
||||
"Returns a seq of sets, where each set represents one stroke / contour"
|
||||
[mat]
|
||||
(let [[pixels width height] (pixels mat)
|
||||
contour-pixels (for [idx (range (.total mat))
|
||||
:when (shape? (nth pixels idx))]
|
||||
[(rem idx width) (quot idx width)])
|
||||
start (first contour-pixels)]
|
||||
(loop [coord start
|
||||
contour #{start}
|
||||
found-contours []]
|
||||
(let [next (next-connection pixels width height contour coord)]
|
||||
(if (nil? next)
|
||||
contour
|
||||
;; we're continuing with our current contour
|
||||
(recur next (conj contour next) found-contours))))))
|
||||
|
||||
(defn draw-coords! [mat [x y]]
|
||||
(cv/circle mat (cv/new-point x y) 1 rgb/green 0 cv/LINE_AA)
|
||||
mat)
|
||||
|
||||
|
||||
(comment
|
||||
(u/show img)
|
||||
|
||||
(radial-sweep img)
|
||||
|
||||
(let [out (-> (cv/clone img)
|
||||
(cv/cvt-color! cv/COLOR_GRAY2RGB))]
|
||||
(doseq [xy (radial-sweep img)]
|
||||
(draw-coords! out xy))
|
||||
(u/show out {:frame {:width (.cols out) :height (.rows out)}})))
|
||||
48
src/heyarne/line_us/gcode.clj
Normal file
48
src/heyarne/line_us/gcode.clj
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
(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])
|
||||
(: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 _))))))
|
||||
|
||||
(comment
|
||||
(require '[thi.ng.geom.circle :as c])
|
||||
|
||||
(->
|
||||
(->gcode (c/circle))
|
||||
(gcode-seq->str)))
|
||||
34
src/heyarne/line_us/helpers.clj
Normal file
34
src/heyarne/line_us/helpers.clj
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
(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