From e5fcf0b10c6d2e1c7ca44f685370671b14f16c10 Mon Sep 17 00:00:00 2001 From: heyarne Date: Sat, 19 Feb 2022 19:36:34 +0100 Subject: [PATCH] Commit unstaged changes --- src/heyarne/all_my_friends/core.cljc | 151 +++++++++++++++++++++++++-- src/heyarne/line_us/gcode.clj | 47 ++++++--- 2 files changed, 176 insertions(+), 22 deletions(-) diff --git a/src/heyarne/all_my_friends/core.cljc b/src/heyarne/all_my_friends/core.cljc index 9771bdd..3ed4451 100644 --- a/src/heyarne/all_my_friends/core.cljc +++ b/src/heyarne/all_my_friends/core.cljc @@ -5,6 +5,7 @@ [cheshire.core :as json] [net.cgrand.xforms :as x] [thi.ng.geom.aabb :as aabb] + [thi.ng.geom.circle :as c] [thi.ng.geom.vector :as v] [thi.ng.geom.core :as g] [thi.ng.geom.line :as l] @@ -16,6 +17,7 @@ [clojure.java.io :as io] [clojure.string :as str])) +;; TODO: Implement camera (def canvas (r/rect 0 0 500 500)) @@ -74,7 +76,7 @@ (q/frame-rate (if debug 1 30)) (q/ellipse-mode :center) (q/color-mode :hsb) - {:current-faces (rand-nth uploads)}) + {:current-faces (last uploads)}) (defn find-index [xs x] (first (keep-indexed (fn [idx item] (when (= x item) idx)) xs))) @@ -94,20 +96,37 @@ state)) (def scale 200) +(def line-length 5) +(def gap-size 10) (defn draw [{:keys [current-faces]}] (q/clear) - (q/background 40 80 255) - (q/stroke 4 120 255) - (q/no-fill) - (q/stroke-weight 1) + (q/background 40 5 255) (q/with-translation [(/ (q/width) 2) (/ (q/height) 2)] + + ;; draw face + (q/no-stroke) + (q/fill 0 0 190) (doseq [face current-faces [x y z] face :let [d (* 5 (- 1 (+ 0.5 z)))]] - (q/ellipse (* scale x) (* scale y) d d)))) + (q/ellipse (* scale x) (* scale y) 3 3)) + + ;; draw dashed line + (q/stroke 140 60 240) + (q/stroke-weight 4) + (q/no-fill) + (let [dashes-y (map #(l/line2 0 % 0 (+ % line-length)) + (range -160 160 (+ line-length gap-size))) + dashes-x (map #(l/line2 % 0 (+ % line-length) 0) + (range -160 160 (+ line-length gap-size)))] + (doseq [line dashes-y :let [[a b] (:points line)]] + (q/line a b)) + #_(doseq [line dashes-x :let [[a b] (:points line)]] + (q/line a b))))) + #_:clj-kondo/ignore (q/defsketch all-my-friends @@ -119,3 +138,123 @@ :key-pressed key-pressed :draw draw :size (:size canvas)) + +(comment + (require '[heyarne.line-us.connection :as line-us]) + (require '[heyarne.line-us.gcode :refer [->gcode]]) + + ;; paulas faces are the last two, there's only one face in the upload + (def smiling-paula + (-> (nth uploads (- (count uploads) 2)) + (first))) + + (def neutral-paula (first (last uploads))) + + ;; TODO: Validate all points before sending them to the line-us + (def plot-scale 900) + (def line-length 3) + + (identity line-us/drawing-area) + ;; => {:x [800 1700], :y [-900 900], :z [0 1000]} + ;; + + (defn invalid-in-gcode-seq [gcode-seq] + (let [{[min-x max-x] :x + [min-y max-y] :y + [min-z max-z] :z} line-us/drawing-area] + (keep-indexed (fn [idx [x y z]] + (when (or (< x min-x) (> x max-x) + (< y min-y) (> y max-y) + (< z min-z) (> z max-z)) + [idx [x y z]])) + gcode-seq))) + + (def face-gcode-seq + (into [] + (comp + (map #(m/* % plot-scale)) + ;; we need to center horizontally + (map #(g/translate % + (v/vec2 (+ (first (:x line-us/drawing-area)) + (* 0.5 (- (second (:x line-us/drawing-area)) + (first (:x line-us/drawing-area))))) + 0))) + (map (fn [[x y _]] (v/vec2 x y))) + (mapcat ->gcode) + #_#_ (mapcat (fn [[x y _z]] + (let [l (* 0.5 line-length)] + [(l/line2 (- x l) (- y l) (+ x l) (+ y l)) + #_(l/line2 (- x l) (+ y l) (+ x l) (- y l))]))) + (mapcat ->gcode)) + neutral-paula)) + + (assert (empty? (invalid-in-gcode-seq face-gcode-seq))) + + #_(def instructions (gcode-seq->str gcode-seq)) + + ;; NOTE: Calibration works quite well with a 20 Euro-Cent and a 1 Euro-Cent coint + + (defn plot! [gcode-seq] + (let [log (partial println "Line-us:")] + (with-open [line-us (line-us/connect "line-us.lan" 1337)] + (log (line-us/read-response line-us)) + (Thread/sleep 1000) + (doseq [coords gcode-seq] + (log (line-us/send-movement! line-us coords))) + (Thread/sleep 1000) + (log "Done!")))) + + (plot! face-gcode-seq) + + (count face-gcode-seq) ;; => 1404 + + (def accents + (into [] (comp (partition-all 3) + (random-sample 0.1) + cat) + face-gcode-seq)) + + (count accents) + + (plot! accents) + + ;; now we draw some extra circles + + + (def bounds + (reduce (fn [[[min-x min-y min-z] + [max-x max-y max-z]] + [x y z]] + [[(min min-x x) (min min-y y) (min min-z z)] + [(max max-x x) (max max-y y) (max max-z z)]]) + [(first face-gcode-seq) (first face-gcode-seq)] (rest face-gcode-seq))) + + (def plot-width (- (get-in bounds [1 0]) (get-in bounds [0 0]))) + (def plot-height (- (get-in bounds [1 1]) (get-in bounds [0 1]))) + + (def circle-one (c/circle (first bounds) (/ plot-width 8))) + (def circle-two (c/circle (second bounds) (* (/ plot-width 8) 1))) + + (def circle-gcode-seq + (mapcat ->gcode [circle-one circle-two])) + + ;; the following section is commented out because it might be skipped + + ;; NOTE: This should be empty! + #_(assert (empty? (invalid-in-gcode-seq circle-gcode-seq))) + #_(plot! circle-gcode-seq) + + (def center-x (+ (ffirst bounds) (* 0.5 plot-width))) + (def dash-start-y (- (get-in bounds [0 1]) (* (/ 1 3) plot-height))) + (def dash-stop-y (+ (get-in bounds [1 1]) (* (/ 1 3) plot-height))) + (def dash-length 20) + (def dash-gap 40) + (def dashes + (->> (range dash-start-y dash-stop-y (+ dash-length dash-gap)) + (map #(l/line2 center-x % center-x (+ % dash-length))) + (mapcat ->gcode))) + + (assert (empty? (invalid-in-gcode-seq dashes))) + (plot! dashes) + +) diff --git a/src/heyarne/line_us/gcode.clj b/src/heyarne/line_us/gcode.clj index 413a35e..0f1bc73 100644 --- a/src/heyarne/line_us/gcode.clj +++ b/src/heyarne/line_us/gcode.clj @@ -2,29 +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]) - (:import [thi.ng.geom.types Line2 LineStrip2 Circle2])) + (:import [thi.ng.geom.types Line2 LineStrip2 Circle2] + [thi.ng.geom.vector Vec2])) (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.")) + (->gcode [_] [_ res] + "Returns G01 movements on the x, y, and z axis. Z=1000 means up, Z=0 means + down. The optional parameter `res` can be used to adjust the resolution if + the shape is not natively supported by the Line-us and has to be converted + into a polygon.")) -(defn- point-seq->gcode [pts] +(defn- point-seq->gcode + "Turns a seq into a drawable seq of points, so that before and after the last + point in the seq the drawing arm is lifted." + [pts] (let [vertices pts + [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]))) + (vec (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] - (map (fn [[x y z]] - (str "G01 X" x " Y" y " Z" z)) gcode-seq)) +(comment + ;; NOTE This is not actually needed. It makes more sense to work with vecs so + ;; the coordinates can be verified to be within a valid range + (defn gcode-seq->str [gcode-seq] + (map (fn [[x y z]] + (str "G01 X" x " Y" y " Z" z)) gcode-seq))) (extend-protocol GCode + Circle2 + (->gcode + ([_] (point-seq->gcode (:points (g/as-polygon _)))) + ([_ r] (point-seq->gcode (:points (g/as-polygon _ r))))) + Line2 (->gcode ([_] (point-seq->gcode (:points _))) @@ -35,14 +50,14 @@ ([_] (point-seq->gcode (:points _))) ([_ r] (->gcode _))) - Circle2 + Vec2 (->gcode - ([_] (point-seq->gcode (:points (g/as-polygon _)))) - ([_ r] (point-seq->gcode (:points (g/as-polygon _)))))) + ([_] (point-seq->gcode [_])) + ([_ r] (->gcode _)))) (comment (require '[thi.ng.geom.circle :as c]) (-> (->gcode (c/circle)) - (gcode-seq->str))) + #_(gcode-seq->str)))