;; Copyright (c) 2008 Accelerated Data Works, Ryan Davis ;; Permission is hereby granted, free of charge, to any person ;; obtaining a copy of this software and associated documentation files ;; (the "Software"), to deal in the Software without restriction, ;; including without limitation the rights to use, copy, modify, merge, ;; publish, distribute, sublicense, and/or sell copies of the Software, ;; and to permit persons to whom the Software is furnished to do so, ;; subject to the following conditions: ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (require 'cl-who) (require 'cl-ppcre) (require 'adw-charting) (defpackage :net.acceleration.documenter (:nicknames #:adw-doc) (:use #:cl #:cl-who #:cl-ppcre)) (in-package :adw-doc) (defvar *root* (merge-pathnames #P"doc/" (asdf:component-pathname (asdf:find-system :adw-charting)))) (defvar *tree* ) (defvar *stream* nil) (eval-when (:compile-toplevel :load-toplevel :execute) (defmacro defhtmlfun (name lambda-list &rest body) `(defun ,name ,lambda-list (with-html-output (*stream*) ,@body ))) (defmacro defhtmlmethod (name lambda-list &rest body) `(defmethod ,name ,lambda-list (with-html-output (*stream*) ,@body )))) (defhtmlfun stub () (:blink "STUB")) (defhtmlfun overview () (htm (:p "ADW-Charting is a library that provides a simple interface to the " (vecto-link) " vector drawing library. It supports drawing on a canvas and saving the results to a PNG file. The API was designed to eliminate as many decisions as possible, and simply produce a reasonable result with minimal fuss. It tries to scale various elements of the chart to fit nicely, but sometimes this goes awry." ) (:p "ADW-Charting depends on the following libraries:" (:ul (:li (vecto-link)))) (:p "ADW-Charting's function interface is similar to " (vecto-link) "'s interface: you create charts by setting up a chart context and adding or setting information on that chart.") (:p "There are many known limitations at this point. We've got some plans on how to solve some of these, and other aren't priorities for me, and might stay around for ahwile.") (:ul (:li "All colors are RGB, represented as a list of 3 numbers between 0 and 1, eg:" (:code "'(1 .5 .3)")) (:li "The bounds on a pie chart are a bit goofy, as the radius of the pie is currently only determined by the height of the chart. This means a square image will cut off the legend.") (:li "Another issue is with printing axis labels. There's some code to try to keep those reasonably spaces, but sometimes the labels start overlapping. Making the graph in two passes should let us calculate everything before starting to draw on the canvas, preventing this issue.") (:li "The font used for all the text is included in the distribution, some random .ttf file pulled from the debian freefont library. You can specify the font file using the *default-font-file* unexported variable. I'm using a with-font macro internally that could solve this one.")) (:p "Related libraries" (:ul (:li (:a :href "http://common-lisp.net/project/cl-plplot/" "cl-plplot")))))) (defhtmlfun examples () (htm (:p "All examples are available in " (:tt "test/examples.lisp") " in the distribution."))) (defhtmlfun feedback () (htm (:p "If you have any questions, comments, bug reports, or other feedback regarding ADW-Charting, please email " (:a :href "mailto:ryan@acceleration.net" "Ryan Davis")))) (defhtmlfun vecto-link () (:a :href "http://www.xach.com/lisp/vecto/" "Vecto")) (defhtmlfun acknowledgements () (htm (:p "Thanks to:") (:ul (:li "Zach Beane for creating " (vecto-link)) (:li "Peter Seibel for his excellent book, " (:a :href "http://gigamonkeys.com/book/" "Practical Common Lisp")) (:li "Edi Weitz and Zach Beane for providing good examples on how to write and document lisp libraries") (:li "Co-workers Nathan, Russ, and Rebecca for advice and code reviews")))) (defhtmlfun dictionary () (:p "The following symbols are exported from the ADW-CHARTING package.")) (defclass section () ((title :accessor title :initarg :title) (anchor :accessor anchor :initform (princ-to-string (gensym))) (children :accessor children :initarg :children :initform nil) (content-fn :accessor content-fn :initarg :content-fn :initform #'stub))) (defclass code (section) ((code-type :accessor code-type :initarg :type) (args :accessor args :initarg :args) (return-val :accessor return-val :initarg :return-val))) (defun make-section (title content-fn &rest children) (make-instance 'section :title title :children children :content-fn content-fn)) (defun make-code (title content-fn code-type args &optional (return-val nil)) (make-instance 'code :title title :content-fn content-fn :type code-type :return-val return-val :args args)) (defgeneric toc-entry (s)) (defgeneric heading (s depth)) (defhtmlmethod toc-entry ((s section)) (str (title s))) (defhtmlmethod toc-entry ((s code)) (:tt (str (title s)))) (defhtmlfun toc (sections &optional (depth 0)) (flet ((fn () (dolist (section sections) (let ((sub-sect (children section))) (htm (:li (:a :href (format nil "#~a" (anchor section)) (toc-entry section)) (when sub-sect (toc sub-sect (1+ depth))))))))) (if (eq 0 depth) (htm (:ol (fn))) (htm (:ul (fn)))))) (defhtmlmethod heading ((s section) depth) (htm (:a :name (anchor s)) (cond ((eq 0 depth) (htm (:h2 (str (title s))))) ((eq 1 depth) (htm (:h3 (str (title s))))) ((eq 2 depth) (htm (:h4 (str (title s))))) (t (htm (:strong (str (title s)))))))) (defhtmlmethod heading ((s code) depth) (htm (:a :name (anchor s)) (:div "[" (str (code-type s)) "]") (:strong (str (title s))) (when (args s) (str " ") (show-args (args s))) (when (return-val s) (htm (str " => ") (show-args (return-val s)))))) (defhtmlfun show-args (args) (loop for arg in args counting T into i do (show-arg arg) (when (< i (length args)) (htm (str " "))))) (defhtmlfun show-arg (arg) (typecase arg (null (htm (:em "nil"))) (list (if (symbolp (first arg)) (let ((name (symbol-name (first arg)))) (cond ((equal "QUOTE" name) (htm "'(" (show-args (second arg)) ")")) ((equal "FUNCTION" name) (htm "#'" (show-arg (second arg)))) (t (htm "(" (show-args arg) ")")))) (htm "(" (show-args arg) ")"))) (number (str (princ-to-string arg))) (symbol (let ((name (string-downcase (symbol-name arg)))) (cond ((equal #\& (aref name 0)) (htm (:tt (str name)))) ((equal "function" name) (str "#'")) (t (htm (:em (str name))))))))) (defhtmlfun content (sections &optional (depth 0)) (dolist (sec sections) (heading sec depth) (htm (:div (funcall (content-fn sec)))) (when (children sec) (content (children sec) (1+ depth))))) (defun get-sections () (list (make-section "Overview and Limitations" #'overview) (make-section "Examples" #'examples (make-section "Minimal Pie Chart" #'minimal-pie) (make-section "Minimal Line Chart" #'minimal-line) (make-section "Customized Line Chart" #'customized-line) (make-section "Boinkmark" #'boinkmark) (make-section "Stuart Mackey 1" #'stuart-mackey-1)) (make-section "Dictionary" #'dictionary (make-code "with-pie-chart" #'with-chart "Macro" '((width height &key (background '(1 1 1))) &body body)) (make-code "add-slice" #'add-slice "Function" '(label value &key color)) (make-code "with-line-chart" #'with-chart "Macro" '((width height &key (background '(1 1 1))) &body body)) (make-code "add-series" #'add-series "Function" '(label data &key (color nil))) (make-code "set-axis" #'set-axis "Function" '(axis title &key (draw-gridlines-p T) (label-formatter #'princ-to-string) (data-interval nil))) (make-code "save-file" #'save-file "Function" '(filename) '(truename))) (make-section "Acknowledgements" #'acknowledgements) (make-section "Feedback" #'feedback))) (defhtmlfun save-file () (:blockquote "Draws the chart as a png file to the given path.")) (defhtmlfun set-axis () (:blockquote "Sets the axis on the current line chart. " (:em "axis") " must be either " (:tt ":x") " or " (:tt ":y") ". The " (:tt "label-formatter") " must be either a format control string or a function of 1 argument that returns a string with the desired axis label. The axis printer will try to pick decent intervals for labels, but it's still pretty dumb. You can specify a data interval using the " (:tt ":data-interval") " parameter.")) (defhtmlfun add-series () (:blockquote "Add another series to the line chart. " (:em "data") " is a list of (x y) pairs. A color will be automatically assigned if none is specified.")) (defhtmlfun with-chart () (:blockquote "Evaluates body with a chart established with the specified dimensions as the target for chart commands, with the specified background.")) (defhtmlfun minimal-pie () (let ((filename (file-namestring (adw-charting-tests::minimal-pie-chart)))) (htm (:pre :style "height:210px" (:img :border 0 :align "right" :src (str filename)) "(with-pie-chart (300 200) (add-slice \"A\" 5.0d0) (add-slice \"B\" 2.0d0) (save-file \"minimal-pie-chart.png\"))")))) (defhtmlfun minimal-line () (let ((filename (file-namestring (adw-charting-tests::minimal-line-chart)))) (htm (:pre :style "height:310px" (:img :border 0 :align "right" :src (str filename)) "(with-line-chart (400 300) (add-series \"A\" '((-1 -2) (0 4) (1 5) (4 6) (5 -3))) (add-series \"B\" '((-1 4) (0 -2) (1 6) (5 -2) (6 5))) (save-file \"minimal-line-chart.png\"))")))) (defhtmlfun customized-line () (let ((filename (file-namestring (adw-charting-tests::customized-line-chart)))) (htm (:pre :style "height:310px" (:img :border 0 :align "right" :src (str filename)) "(with-line-chart (400 300 :background '(.7 .5 .7)) (add-series \"A\" '((-.1 -.2) (0 .4) (.1 .5) (.4 .6) (.5 -.3))) (add-series \"B\" '((-.1 .4) (0 -.2) (.1 .6) (.5 -.2) (.6 .5))) (add-series \"C\" '((-.1 0) (0 .3) (.1 .1) (.2 .5) (.4 -.6)) :color '(.3 .7 .9)) (set-axis :y \"widgets\" :label-formatter \"~,2F\") (set-axis :x nil :draw-gridlines-p nil :label-formatter #'(lambda (v) ;;could do something more interesting here (format nil \"~,1F\" (expt 2 v)))) (save-file \"customized-line-chart.png\"))" )))) (defhtmlfun boinkmark () (let ((filename (file-namestring (adw-charting-tests::boinkmark)))) (htm (:pre :style "height:310px" (:img :border 0 :align "right" :src (str filename)) "(with-line-chart (400 300) (add-series \"baker: SBCL\" (loop for row in +boink-data+ for i from 0 collect (list i (nth 4 row)))) (set-axis :y \"seconds\" :label-formatter \"~,2F\") (set-axis :x nil :draw-gridlines-p nil :label-formatter #'(lambda (i) (nth 3 (nth i +boink-data+)))) (save-file \"boink.png\"))" )))) (defhtmlfun stuart-mackey-1() (let ((filename (file-namestring (adw-charting-tests::stuart-mackey-1)))) (htm (:pre :style "height:310px" (:img :border 0 :align "right" :src (str filename)) "(with-line-chart (400 300) (add-series \"test\" '((1 0.0) (2 2.0) (3 3.0) (4 1.5)) :color '(0 0 1)) (set-axis :y \"amount\" :label-formatter \"~,2f\") (set-axis :x \"days\" :data-interval 1 :draw-gridlines-p nil :label-formatter (lambda (v) (format nil \"~d\" (round v)))) (save-file \"stuart-mackey-1.png\"))" )))) (defhtmlfun add-slice () (:blockquote "Adds a slice to the chart, with an optional color. A color will be automatically assigned if none is specified.")) (defun adw-charting-doc () (let ((title "ADW-Charting - simple chart drawing with Common Lisp") (canonical-url "http://common-lisp.net/project/adw-charting/") (download-url "http://common-lisp.net/project/adw-charting/adw-charting.tar.gz") (sections (get-sections)) (outfile (merge-pathnames *root* #P"./index.html"))) (setf adw-charting-tests::*root* *root*) (with-open-file (*stream* outfile :direction :output :if-exists :supersede :if-does-not-exist :create) (with-html-output (*stream* nil :prologue T) (:html (:head (:title (str title)) (:style :type "text/css" (str " a, a:visited { text-decoration: none } a[href]:hover { text-decoration: underline } pre { background: #DDD; padding: 0.25em } p.download { color: red } a.top {font-size:smallest;}")) ) (:body (:h1 (str title)) (:blockquote (:h2 "Abstract") (:p "ADW-Charting is a simple chart drawing library for quickly creating nice-looking pie charts and line charts. It presents a function-oriented interface similar to " (vecto-link) ", and saves results to PNG. Since ADW-Charting and all supporting libraries are written completely in Common Lisp, without depending on external non-Lisp libraries, it should work in any Common Lisp environment. ADW-Charting is available under a BSD-like license. The 'ADW' in the name is referencing my employer, " (:a :href "http://www.acceleration.net" "Acceleration.net") ", who has sponsored much of this work. The current version is 0.7, released on January 28th, 2008.") (:p "The canonical location for ADW-Charting is " (:a :href canonical-url (str canonical-url))) (:p :class "download" "Download shortcut:") (:a :href download-url (str download-url))) (:h2 "Contents") (toc sections) (content sections) ))))))