2010年6月13日日曜日

ICFP 2009 orbit visualizer

Visualization helps to better understand each of the problems. This is the visualization of the simplest problem, the Hohmann transfer:




And this screen shot shows the part around the earth in the "Operation Clear Skies" problem:



First some utilities we'll use:


(ns orbit-util)

(defmacro pipi [& body]
"quick hack debug prn macro that can be prepended before any fn/macro call without having to add any parens"
`(let [result# (~@body)]
(prn '~body result#)
result#))

(defn abs [n]
"Math/abs has caused me some trouble before - don't remember exactly, but this is what I always use now"
(if (< n 0) (* -1 n) n))

(defn hypot [a b]
"For some reason faster than Math/hypot"
(Math/sqrt (+ (* a a) (* b b))))


And here's the visualization code:


(ns orbit-plotter
(:use orbit-util)
(:import (java.awt BorderLayout Color Graphics Polygon RenderingHints Toolkit)
(java.awt.geom Ellipse2D$Double)
(javax.swing JComponent JFrame JLabel JPanel)
(java.awt.event ActionListener MouseAdapter MouseListener MouseMotionListener MouseWheelListener)))

(defstruct orbit-object :x :y :radius :color)

(def border-layout-order (list BorderLayout/CENTER BorderLayout/EAST BorderLayout/SOUTH BorderLayout/WEST BorderLayout/NORTH))

(defn create-frame [& components]
"Create a JFrame and add the components to it, the first will be the center, the others fill the border clockwise
starting from EAST"

(let [frame (JFrame. "ORBIT")]
(doseq [[component position] (map list components border-layout-order)]
(.add frame #^java.awt.Component component position))
(.setDefaultCloseOperation frame JFrame/DISPOSE_ON_CLOSE)
(.show frame)
frame))

(defn create-orbit-panel
"Create the main panel, showing the objects in space"
[objects-ref]
(let [scale (atom Long/MAX_VALUE)
panel
(proxy [JPanel] [(BorderLayout.)]
(paintComponent [#^java.awt.Graphics2D g2]
(let [panel #^javax.swing.JPanel this
panel-w (.getWidth panel) panel-h (.getHeight panel)]
(.setColor g2 Color/BLACK)
(.fillRect g2 0 0 panel-w panel-h)
(.setRenderingHint g2 RenderingHints/KEY_ANTIALIASING RenderingHints/VALUE_ANTIALIAS_ON)
(let [[max-rx max-ry]
(reduce (fn [[x y] [name obj]]
(let [r (:radius obj)]
[(max x (+ (abs (:x obj)) r)) (max y (+ (abs (:y obj)) r))])) [0 0] @objects-ref)
x-scale (if (zero? max-rx) 1.0 (/ panel-w (+ max-rx max-rx)))
y-scale (if (zero? max-ry) 1.0 (/ panel-h (+ max-ry max-ry)))
s (swap! scale #(min % x-scale y-scale))]
(.translate g2 #^java.lang.Double (+ (/ panel-w 2)) #^java.lang.Double (+ (/ panel-h 2)))
(.scale g2 s s)
)
(doseq [[name object] @objects-ref]
(let [r (:radius object)
dia (+ r r)]
(.setColor g2 (:color object))
(.fill g2 (Ellipse2D$Double. (- (:x object) r) (- (:y object) r) dia dia)))))))]
(add-watch objects-ref "orbit-panel"
(fn [key ref old-state new-state]
(if (not= old-state new-state)
(.repaint panel))))
panel))


(defn- generate-html-table
"Generate an HTML table for the statistics"
[stats]
(str (apply str
(reduce (fn [strs [k v]]
(let [v (if (= Double (class v)) (format "%+08.2e" v) v)]
(conj strs (format " <tr><td>%s</td><td align='right'>%s</td></tr>%n" k v)))) [ "<html><table>\n" ] stats))
"</table></html>"))

(defn create-stats-panel
"Create the stats panel, showing a table of the stats"
[stats-ref]
(let [label (doto
(javax.swing.JLabel.)
(.setOpaque true)
(.setBackground java.awt.Color/BLACK)
(.setForeground java.awt.Color/WHITE))]
(add-watch stats-ref "stats-panel"
(fn [key ref old-state new-state]
(if (not= old-state new-state)
(.setText label (generate-html-table new-state)))))
label))

0 件のコメント:

コメントを投稿

フォロワー