# HG changeset patch # User Robert McIntyre # Date 1283122946 14400 # Node ID 8ad629298649073d1abd8e96459b7b7561d0ecbb # Parent db745c95aabd21b8fcfabe633e36f8c47a11dd5c major refactoring diff -r db745c95aabd -r 8ad629298649 src/laser/rasterize.clj --- a/src/laser/rasterize.clj Sun Aug 29 00:09:28 2010 -0400 +++ b/src/laser/rasterize.clj Sun Aug 29 19:02:26 2010 -0400 @@ -1,107 +1,50 @@ -(ns laser.rasterize) +(ns laser.rasterize + (:use [rlm + image-utils + map-utils] + [clojure.contrib + [str-utils :only [str-join re-gsub]] + [seq :only [indexed]] + [math]]) + (:import [ij ImagePlus IJ])) -(import '(java.io File)) -(import '(org.apache.commons.io FileUtils)) -(import '(javax.imageio ImageIO) ) -(import '(javax.swing JFrame)) -(import '(java.awt Color BorderLayout)) -(import '(ij ImagePlus IJ)) -(import '(java.lang Math)) -(import '(java.awt Graphics2D Panel)) -(import '(ij Macro)) +;(import '(java.io File)) +;(import '(org.apache.commons.io FileUtils)) +;(import '(javax.imageio ImageIO) ) -(import '(java.io BufferedReader InputStreamReader)) -(import '(java.awt.image BufferedImage)) -;(use 'clojure.contrib.str-utils) -;(use 'clojure.contrib.seq-utils) -;(use 'clojure.contrib.combinatorics) -;(use 'clojure.contrib.duck-streams) - -;(use 'clojure.contrib.repl-utils) - -;(set! *print-length* 20) - - - - +(set! *print-length* 20) (def feed 120) (def dpi [500, 500]) +;;; this process is divided into two tasks, +;;; creating the raster g-code, which sweeps back and forth +;;; and creating the gmask, which turns the laser on and off. -(defn preserve-meta [f] - (fn [& x] (with-meta - (apply f x) - (meta (last x))))) +;;; we'll be using frame-hashes, which represent picutres as +;;; a 3D vector field over 2D space, with the vectors representing +;;; the rgb values at that particulat point. -(defmulti frame-hash-multi class) +(defn select-row + "returns a frame hash that is just a single line at the chosen y" + [y window] + (filter-keys (comp (partial = y) last) window)) +(defn make-rows [pic] + (map (partial sort #(< (first %1) (first %2))) + (partition-by last + (sort (fn [[x1 y1][x2 y2]] (> y2 y1)) + (map first (filter-vals (partial = black) pic)))))) -(defmethod frame-hash-multi ImagePlus - [image+] - (with-meta - (let [buf (.. image+ getBufferedImage) - color (.getColorModel buf)] - (apply hash-map - (interleave - (doall (for [x (range (.getWidth image+)) y (range (.getHeight image+))] - (vector x y))) - (doall (for [x (range (.getWidth image+)) y (range (.getHeight image+))] - (let [data (.getRGB buf x y)] - (hash-map :r (bit-shift-right (bit-and 0xff0000 data) 16) - :g (bit-shift-right (bit-and 0x00ff00 data) 8) - :b (bit-and 0x0000ff data)))))))) - {:width (.getWidth image+) :height (.getHeight image+)})) - -(defmethod frame-hash-multi String - [image-name] - (let [image+ (ImagePlus. image-name)] - (frame-hash-multi image+))) - - -(defn frame-hash - "yields a convienent representation for the pixles in an image. - Because of the size of the structvre generated, this must only be used - in a transient way so that java can do it's garbage collection." - [something] - (frame-hash-multi something)) - -;(def frame-hash (preserve-meta frame-hash)) - - - - -(def white {:r 255, :g 255, :b 255}) -(def black {:r 0, :g 0, :b 0}) - - - -(defn rgb-euclidian - [{r1 :r g1 :g b1 :b} {r2 :r g2 :g b2 :b} ] - (expt (+ (expt (- r1 r2) 2) - (expt (- g1 g2) 2) - (expt (- b1 b2) 2)) 0.5)) - -(defn b&w - "turn everything strictly black or white" - [window] - (with-meta - (zipmap - (keys window) - (map (fn [rgb] - (if (> (rgb-euclidian rgb white) (rgb-euclidian rgb black)) - black white)) - (vals window))) (meta window))) - - +;;; generate rastering g-code (defn raster-preamble [] (str-join \newline ["M63 P0\nG61" - (str \F feed) + (str "F" feed) "M101" "M3 S1\n"])) @@ -111,68 +54,9 @@ "M5" "M2\n"])) - (defn raster-comment [string] (str "(" (re-gsub #"[()]" "" string) ")")) -(defn filter-keys [fun m] - (select-keys m (filter fun (keys m)))) - -(def filter-keys (preserve-meta filter-keys)) - -(defn filter-vals [fun m] - (into {} (filter (comp fun val) m))) - -(def filter-vals (preserve-meta filter-vals)) - -(defn frame-hash->bufferedImage - [frame-hash] - (let [data (meta frame-hash) - image (BufferedImage. (:width data) (:height data) BufferedImage/TYPE_INT_BGR)] - - (doall (for [element frame-hash] - (let [coord (key element) - rgb (val element) - packed-RGB - (+ (bit-shift-left (:r rgb) 16) - (bit-shift-left (:g rgb) 8) - (:b rgb))] - (.setRGB image (first coord) (last coord) packed-RGB)))) - image)) - -(defmulti display "Creates a JFrame and displays a buffered image" class) - -(defn- makePanel [image] (proxy [Panel] [] (paint [g] (.drawImage g image 0 0 nil)))) - -(defn select-row [x window] - (filter-keys (comp (partial = x) first) window)) - - - -(defmethod display - BufferedImage [image] - (let [panel (makePanel image) - frame (JFrame. "Oh Yeah!")] - (.add frame panel) - (.pack frame) - (.setVisible frame true ) - (.setSize frame(.getWidth image) (.getHeight image)))) - -(defmethod display - ImagePlus [image] - (display (.getBufferedImage image))) - -(defmethod display - clojure.lang.PersistentHashMap [frame-hash] - (display (frame-hash->bufferedImage frame-hash))) - -(defmethod display - clojure.lang.PersistentArrayMap [frame-hash] - (display (frame-hash->bufferedImage frame-hash))) - - - - ;this is a sequence of rows @@ -202,6 +86,34 @@ (float (* x2 (/ x-dpi))) (float (* y2 (/ y-dpi))))))) + +(defn generate-gcode [pic] + (str (raster-preamble) + (str-join "" + (map + (fn [[index row]] + (row->gcode dpi (even? index) row)) + (indexed (make-rows pic)))) + (raster-epilogue))) + + + + + + + + + + + + + + + + + + + (defn gather-row [row] (let [base [[(first (first row)) (first (first row))]]] ; (println base) @@ -228,11 +140,9 @@ (defn row->gmask [[x-dpi y-dpi] forward? row] -; (println forward?) (let [start (float (* (/ x-dpi) (first (first (if forward? (reverse row) row)))))] - (let [preamble (if-not forward? (format "0 0 0 %.3f\n" start) (format "0 0 1 %.3f\n" start)) @@ -242,7 +152,7 @@ (reverse (gather-row row)) (gather-row row))] (let [x (float (* x (/ x-dpi))) - y (float (* y (/ x-dpi))) + y (float (* y (/ x-dpi)))] ;; x (+ x 0.159)];; shift by a small margin. (if-not forward? (str (format "0 0 1 %.3f\n" x) @@ -254,144 +164,45 @@ (str preamble (str-join "" body))))) - -(defn make-rows [pic] - (map (partial sort #(< (first %1) (first %2))) - (partition-by last - (sort (fn [[x1 y1][x2 y2]] (> y2 y1)) - (map first (filter-vals (partial = black) pic)))))) - - - (defn generate-gmask [pic] - (str "1 0 0 0\n" (str-join "" (map (fn [[index row]] (row->gmask dpi (even? index) row)) (indexed (make-rows pic)))))) -;; 1 0 0 0 -;; 0 0 1 2.881 -;; 0 0 0 2.881 -;; 0 1 0 2.863 -;; 0 0 0 2.769 -;; 0 1 0 2.751 -;; 0 0 0 2.729 -;; 0 1 0 2.617 -;; 0 0 0 2.593 -;; 0 1 0 2.561 -;; 0 0 0 2.463 -;; 0 1 0 2.445 -;; 0 0 0 2.385 -;; 0 1 0 2.317 -;; 0 0 0 2.253 -;; 0 1 0 2.233 -;; 0 0 0 2.177 - - - -(defn generate-gcode [pic] - (str (raster-preamble) - - (str-join "" - (map - (fn [index row] - (partial row->gcode dpi (even? index)) (indexed (make-rows pic)))) - (raster-epilogue)))) - - - -; (str-join "" (map (partial row->gcode dpi) (make-rows pic))) - ; (raster-epilogue))) - - - -(defn rotate [degrees #^ImagePlus image] - (.rotate (.getChannelProcessor image) degrees) - image) - -(defn map-keys [f m] - (into {} (map (fn [[key val]] [(f key) val]) m))) - - - -(defn invert-frame-hash [pic] - (map-keys (fn [[x y]] [x (- (:height (meta pic)) y 1)]) pic )) - - -(defn generate-files [pic] - (let [image (invert-frame-hash (b&w (frame-hash (rotate 180 (ImagePlus. pic)))))] - (spit "/home/r/kevin/out.ngc" (generate-gcode image)) - (spit "/home/r/kevin/out.gmask" (generate-gmask image)) - image)) - - - -(defn update-state [] -(def sing "/home/r/lasercutter/graster/signer4laser2x1.png") - -(def pic (frame-hash (let [image (ImagePlus. sing)] - (.rotate (.getChannelProcessor image) 180) - image))) - -(def pic (b&w pic))) - - - - - - - - - ;;;; testing -(defn init [] - (let [stuff - - (bound-fn [] +(defn generate-files [pic] + (println "made-image") + (spit "/home/r/kevin/out.ngc" (generate-gcode pic)) + (println "/home/r/kevin/out.ngc") + (spit "/home/r/kevin/out.gmask" (generate-gmask pic)) + (println "/home/r/kevin/out.gmask") + pic) - (do - (println "hi everyone") - (def img "/home/r/kevin/sing.png") - (def pic (frame-hash (let [image (ImagePlus. img)] - (.rotate (.getChannelProcessor image) 180) - image))) +(defn update-state [] + (def sing "/home/r/kevin/sing.png") + (def pic (frame-hash (ImagePlus. sing))) + (def pic (b&w pic))) +(defn compare-gen-fn [n f cmp] + (let [theirs (re-split #"\n" (slurp cmp)) + ours (re-split #"\n" (f pic))] + (println (format "%1$-25s%2$s" "OURS" "THEIRS")) + (println "_______________________________________") + (dorun (map (fn [[us them]] (println + (format "%1$-25s%2$s" us them))) + (take n (partition 2 (interleave ours theirs))))))) - (def test-image - (invert-frame-hash (b&w (frame-hash (rotate 180 (ImagePlus. img)))))) - - (defn test-gmask [] - (println (str-join "" (take 170 (generate-gmask test-image))))) +(defn compare-gcode [n] + (compare-gen-fn n generate-gcode "/home/r/kevin/reference.ngc")) +(defn compare-gmask [n] + (compare-gen-fn n generate-gmask "/home/r/kevin/reference.gmask")) + - (println "ALL variables initialized!") - ))] - (.start - (Thread. stuff)))) - -(defn thread-test [] - - (let [temp *out*] - (.start - (Thread. - (fn [] - (with-bindings {#'*out* temp} - (Thread/sleep 5000) - (println "hi"))))))) - - -(comment - (do - (require 'rlm.quick) - (ns laser.rasterize) - (rlm.quick/dirty) - (use :reload-all 'laser.rasterize) - (undef map-keys) - (use :reload-all 'laser.rasterize)))