doc.lisp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. ;; Copyright (c) 2008 Accelerated Data Works, Ryan Davis
  2. ;; Permission is hereby granted, free of charge, to any person
  3. ;; obtaining a copy of this software and associated documentation files
  4. ;; (the "Software"), to deal in the Software without restriction,
  5. ;; including without limitation the rights to use, copy, modify, merge,
  6. ;; publish, distribute, sublicense, and/or sell copies of the Software,
  7. ;; and to permit persons to whom the Software is furnished to do so,
  8. ;; subject to the following conditions:
  9. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  10. ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  11. ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  12. ;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  13. ;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  14. ;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  15. ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16. (require 'cl-who)
  17. (require 'cl-ppcre)
  18. (require 'adw-charting)
  19. (defpackage :net.acceleration.documenter
  20. (:nicknames #:adw-doc)
  21. (:use #:cl #:cl-who #:cl-ppcre))
  22. (in-package :adw-doc)
  23. (defvar *root* (merge-pathnames #P"doc/"
  24. (asdf:component-pathname
  25. (asdf:find-system :adw-charting))))
  26. (defvar *tree* )
  27. (defvar *stream* nil)
  28. (eval-when (:compile-toplevel :load-toplevel :execute)
  29. (defmacro defhtmlfun (name lambda-list &rest body)
  30. `(defun ,name ,lambda-list
  31. (with-html-output (*stream*)
  32. ,@body )))
  33. (defmacro defhtmlmethod (name lambda-list &rest body)
  34. `(defmethod ,name ,lambda-list
  35. (with-html-output (*stream*)
  36. ,@body ))))
  37. (defhtmlfun stub ()
  38. (:blink "STUB"))
  39. (defhtmlfun overview ()
  40. (htm
  41. (:p "ADW-Charting is a library that provides a simple interface to the "
  42. (vecto-link)
  43. " vector drawing library. It supports drawing on a canvas and saving the
  44. results to a PNG file. The API was designed to eliminate as many decisions as possible, and simply
  45. produce a reasonable result with minimal fuss. It tries to scale various elements of the chart to
  46. fit nicely, but sometimes this goes awry." )
  47. (:p "ADW-Charting depends on the following libraries:"
  48. (:ul
  49. (:li (vecto-link))))
  50. (:p "ADW-Charting's function interface is similar to "
  51. (vecto-link)
  52. "'s interface: you
  53. create charts by setting up a chart context and adding or setting information on that chart.")
  54. (:p "There are many known limitations at this point. We've got some plans on how to solve
  55. some of these, and other aren't priorities for me, and might stay around for ahwile.")
  56. (:ul
  57. (:li "All colors are RGB, represented as a list of 3 numbers between 0 and 1, eg:"
  58. (:code "'(1 .5 .3)"))
  59. (:li "The bounds on a pie chart are a bit goofy, as the radius of the pie is currently
  60. only determined by the height of the chart. This means a square image will cut off the
  61. legend.")
  62. (:li "Another issue is with printing axis labels. There's some code to try to keep
  63. those reasonably spaces, but sometimes the labels start overlapping. Making the graph
  64. in two passes should let us calculate everything before starting to draw on the canvas,
  65. preventing this issue.")
  66. (:li "The font used for all the text is included in the distribution, some random .ttf file
  67. pulled from the debian freefont library. You can specify the font file using the
  68. *default-font-file* unexported variable. I'm using a with-font macro internally that
  69. could solve this one."))
  70. (:p "Related libraries"
  71. (:ul
  72. (:li (:a :href "http://common-lisp.net/project/cl-plplot/" "cl-plplot"))))))
  73. (defhtmlfun examples ()
  74. (htm
  75. (:p "All examples are available in "
  76. (:tt "test/examples.lisp")
  77. " in the distribution.")))
  78. (defhtmlfun feedback ()
  79. (htm
  80. (:p "If you have any questions, comments, bug reports, or other feedback
  81. regarding ADW-Charting, please email "
  82. (:a :href "mailto:ryan@acceleration.net" "Ryan Davis"))))
  83. (defhtmlfun vecto-link ()
  84. (:a :href "http://www.xach.com/lisp/vecto/" "Vecto"))
  85. (defhtmlfun acknowledgements ()
  86. (htm
  87. (:p "Thanks to:")
  88. (:ul (:li "Zach Beane for creating "
  89. (vecto-link))
  90. (:li "Peter Seibel for his excellent book, "
  91. (:a :href "http://gigamonkeys.com/book/" "Practical Common Lisp"))
  92. (:li "Edi Weitz and Zach Beane for providing good examples on how to write and document lisp libraries")
  93. (:li "Co-workers Nathan, Russ, and Rebecca for advice and code reviews"))))
  94. (defhtmlfun dictionary ()
  95. (:p "The following symbols are exported from the ADW-CHARTING package."))
  96. (defclass section ()
  97. ((title :accessor title :initarg :title)
  98. (anchor :accessor anchor :initform (princ-to-string (gensym)))
  99. (children :accessor children :initarg :children :initform nil)
  100. (content-fn :accessor content-fn :initarg :content-fn :initform #'stub)))
  101. (defclass code (section)
  102. ((code-type :accessor code-type :initarg :type)
  103. (args :accessor args :initarg :args)
  104. (return-val :accessor return-val :initarg :return-val)))
  105. (defun make-section (title content-fn &rest children)
  106. (make-instance 'section :title title :children children :content-fn content-fn))
  107. (defun make-code (title content-fn code-type args &optional (return-val nil))
  108. (make-instance 'code
  109. :title title
  110. :content-fn content-fn
  111. :type code-type
  112. :return-val return-val
  113. :args args))
  114. (defgeneric toc-entry (s))
  115. (defgeneric heading (s depth))
  116. (defhtmlmethod toc-entry ((s section))
  117. (str (title s)))
  118. (defhtmlmethod toc-entry ((s code))
  119. (:tt (str (title s))))
  120. (defhtmlfun toc (sections &optional (depth 0))
  121. (flet ((fn ()
  122. (dolist (section sections)
  123. (let ((sub-sect (children section)))
  124. (htm (:li (:a :href (format nil "#~a" (anchor section))
  125. (toc-entry section))
  126. (when sub-sect (toc sub-sect (1+ depth)))))))))
  127. (if (eq 0 depth)
  128. (htm (:ol (fn)))
  129. (htm (:ul (fn))))))
  130. (defhtmlmethod heading ((s section) depth)
  131. (htm (:a :name (anchor s))
  132. (cond
  133. ((eq 0 depth) (htm (:h2 (str (title s)))))
  134. ((eq 1 depth) (htm (:h3 (str (title s)))))
  135. ((eq 2 depth) (htm (:h4 (str (title s)))))
  136. (t (htm (:strong (str (title s))))))))
  137. (defhtmlmethod heading ((s code) depth)
  138. (htm (:a :name (anchor s))
  139. (:div "[" (str (code-type s)) "]")
  140. (:strong (str (title s)))
  141. (when (args s)
  142. (str " ")
  143. (show-args (args s)))
  144. (when (return-val s)
  145. (htm (str " => ")
  146. (show-args (return-val s))))))
  147. (defhtmlfun show-args (args)
  148. (loop for arg in args
  149. counting T into i
  150. do (show-arg arg)
  151. (when (< i (length args))
  152. (htm (str " ")))))
  153. (defhtmlfun show-arg (arg)
  154. (typecase arg
  155. (null (htm (:em "nil")))
  156. (list (if (symbolp (first arg))
  157. (let ((name (symbol-name (first arg))))
  158. (cond
  159. ((equal "QUOTE" name) (htm "'("
  160. (show-args (second arg))
  161. ")"))
  162. ((equal "FUNCTION" name) (htm "#'"
  163. (show-arg (second arg))))
  164. (t (htm "("
  165. (show-args arg)
  166. ")"))))
  167. (htm "("
  168. (show-args arg)
  169. ")")))
  170. (number (str (princ-to-string arg)))
  171. (symbol (let ((name (string-downcase (symbol-name arg))))
  172. (cond
  173. ((equal #\& (aref name 0)) (htm (:tt (str name))))
  174. ((equal "function" name) (str "#'"))
  175. (t (htm (:em (str name)))))))))
  176. (defhtmlfun content (sections &optional (depth 0))
  177. (dolist (sec sections)
  178. (heading sec depth)
  179. (htm (:div (funcall (content-fn sec))))
  180. (when (children sec)
  181. (content (children sec) (1+ depth)))))
  182. (defun get-sections ()
  183. (list (make-section "Overview and Limitations" #'overview)
  184. (make-section "Examples" #'examples
  185. (make-section "Minimal Pie Chart" #'minimal-pie)
  186. (make-section "Minimal Line Chart" #'minimal-line)
  187. (make-section "Customized Line Chart" #'customized-line)
  188. (make-section "Boinkmark" #'boinkmark)
  189. (make-section "Stuart Mackey 1" #'stuart-mackey-1))
  190. (make-section "Dictionary" #'dictionary
  191. (make-code "with-pie-chart" #'with-chart "Macro"
  192. '((width height &key (background '(1 1 1))) &body body))
  193. (make-code "add-slice" #'add-slice "Function"
  194. '(label value &key color))
  195. (make-code "with-line-chart" #'with-chart "Macro"
  196. '((width height &key (background '(1 1 1))) &body body))
  197. (make-code "add-series" #'add-series "Function"
  198. '(label data &key (color nil)))
  199. (make-code "set-axis" #'set-axis "Function"
  200. '(axis title &key (draw-gridlines-p T)
  201. (label-formatter #'princ-to-string)
  202. (data-interval nil)))
  203. (make-code "save-file" #'save-file "Function"
  204. '(filename)
  205. '(truename)))
  206. (make-section "Acknowledgements" #'acknowledgements)
  207. (make-section "Feedback" #'feedback)))
  208. (defhtmlfun save-file ()
  209. (:blockquote "Draws the chart as a png file to the given path."))
  210. (defhtmlfun set-axis ()
  211. (:blockquote "Sets the axis on the current line chart. "
  212. (:em "axis")
  213. " must be either "
  214. (:tt ":x") " or " (:tt ":y") ". The " (:tt "label-formatter")
  215. " must be either a format control string or a function of 1 argument that
  216. returns a string with the desired axis label. The axis printer will try to
  217. pick decent intervals for labels, but it's still pretty dumb. You can specify
  218. a data interval using the "
  219. (:tt ":data-interval")
  220. " parameter."))
  221. (defhtmlfun add-series ()
  222. (:blockquote "Add another series to the line chart. "
  223. (:em "data")
  224. " is a list of (x y) pairs. A color will
  225. be automatically assigned if none is specified."))
  226. (defhtmlfun with-chart ()
  227. (:blockquote
  228. "Evaluates body with a chart established with the specified
  229. dimensions as the target for chart commands, with the specified background."))
  230. (defhtmlfun minimal-pie ()
  231. (let ((filename (file-namestring
  232. (adw-charting-tests::minimal-pie-chart))))
  233. (htm (:pre :style "height:210px"
  234. (:img :border 0 :align "right" :src (str filename))
  235. "(with-pie-chart (300 200)
  236. (add-slice \"A\" 5.0d0)
  237. (add-slice \"B\" 2.0d0)
  238. (save-file \"minimal-pie-chart.png\"))"))))
  239. (defhtmlfun minimal-line ()
  240. (let ((filename (file-namestring
  241. (adw-charting-tests::minimal-line-chart))))
  242. (htm (:pre :style "height:310px"
  243. (:img :border 0 :align "right" :src (str filename))
  244. "(with-line-chart (400 300)
  245. (add-series \"A\" '((-1 -2) (0 4) (1 5) (4 6) (5 -3)))
  246. (add-series \"B\" '((-1 4) (0 -2) (1 6) (5 -2) (6 5)))
  247. (save-file \"minimal-line-chart.png\"))"))))
  248. (defhtmlfun customized-line ()
  249. (let ((filename (file-namestring
  250. (adw-charting-tests::customized-line-chart))))
  251. (htm (:pre :style "height:310px"
  252. (:img :border 0 :align "right" :src (str filename))
  253. "(with-line-chart (400 300 :background '(.7 .5 .7))
  254. (add-series \"A\" '((-.1 -.2) (0 .4) (.1 .5) (.4 .6) (.5 -.3)))
  255. (add-series \"B\" '((-.1 .4) (0 -.2) (.1 .6) (.5 -.2) (.6 .5)))
  256. (add-series \"C\"
  257. '((-.1 0) (0 .3) (.1 .1) (.2 .5) (.4 -.6))
  258. :color '(.3 .7 .9))
  259. (set-axis :y \"widgets\" :label-formatter \"~,2F\")
  260. (set-axis :x nil
  261. :draw-gridlines-p nil
  262. :label-formatter #'(lambda (v)
  263. ;;could do something more interesting here
  264. (format nil \"~,1F\" (expt 2 v))))
  265. (save-file \"customized-line-chart.png\"))"
  266. ))))
  267. (defhtmlfun boinkmark ()
  268. (let ((filename (file-namestring
  269. (adw-charting-tests::boinkmark))))
  270. (htm (:pre :style "height:310px"
  271. (:img :border 0 :align "right" :src (str filename))
  272. "(with-line-chart (400 300)
  273. (add-series \"baker: SBCL\"
  274. (loop for row in +boink-data+
  275. for i from 0
  276. collect (list i (nth 4 row))))
  277. (set-axis :y \"seconds\" :label-formatter \"~,2F\")
  278. (set-axis :x nil
  279. :draw-gridlines-p nil
  280. :label-formatter #'(lambda (i)
  281. (nth 3 (nth i +boink-data+))))
  282. (save-file \"boink.png\"))"
  283. ))))
  284. (defhtmlfun stuart-mackey-1()
  285. (let ((filename (file-namestring
  286. (adw-charting-tests::stuart-mackey-1))))
  287. (htm (:pre :style "height:310px"
  288. (:img :border 0 :align "right" :src (str filename))
  289. "(with-line-chart (400 300)
  290. (add-series \"test\" '((1 0.0) (2 2.0) (3 3.0) (4 1.5)) :color '(0 0 1))
  291. (set-axis :y \"amount\" :label-formatter \"~,2f\")
  292. (set-axis :x \"days\" :data-interval 1
  293. :draw-gridlines-p nil
  294. :label-formatter (lambda (v) (format nil \"~d\" (round v))))
  295. (save-file \"stuart-mackey-1.png\"))"
  296. ))))
  297. (defhtmlfun add-slice ()
  298. (:blockquote "Adds a slice to the chart, with an optional color. A color will
  299. be automatically assigned if none is specified."))
  300. (defun adw-charting-doc ()
  301. (let ((title "ADW-Charting - simple chart drawing with Common Lisp")
  302. (canonical-url "http://common-lisp.net/project/adw-charting/")
  303. (download-url "http://common-lisp.net/project/adw-charting/adw-charting.tar.gz")
  304. (sections (get-sections))
  305. (outfile (merge-pathnames *root* #P"./index.html")))
  306. (setf adw-charting-tests::*root* *root*)
  307. (with-open-file (*stream* outfile
  308. :direction :output
  309. :if-exists :supersede
  310. :if-does-not-exist :create)
  311. (with-html-output (*stream* nil :prologue T)
  312. (:html
  313. (:head
  314. (:title (str title))
  315. (:style :type "text/css"
  316. (str "
  317. a, a:visited { text-decoration: none }
  318. a[href]:hover { text-decoration: underline }
  319. pre { background: #DDD; padding: 0.25em }
  320. p.download { color: red }
  321. a.top {font-size:smallest;}"))
  322. )
  323. (:body (:h1 (str title))
  324. (:blockquote (:h2 "Abstract")
  325. (:p "ADW-Charting is a simple chart drawing library for quickly
  326. creating nice-looking pie charts and line charts. It presents a function-oriented
  327. interface similar to "
  328. (vecto-link)
  329. ", and saves results to PNG. Since ADW-Charting and all supporting
  330. libraries are written completely in Common Lisp, without depending on external
  331. non-Lisp libraries, it should work in any Common Lisp environment. ADW-Charting is
  332. available under a BSD-like license. The 'ADW' in the name is referencing my
  333. employer, "
  334. (:a :href "http://www.acceleration.net" "Acceleration.net")
  335. ", who has sponsored much of this work. The current version is 0.7,
  336. released on January 28th, 2008.")
  337. (:p "The canonical location for ADW-Charting is "
  338. (:a :href canonical-url (str canonical-url)))
  339. (:p :class "download" "Download shortcut:")
  340. (:a :href download-url (str download-url)))
  341. (:h2 "Contents")
  342. (toc sections)
  343. (content sections)
  344. ))))))