# HG changeset patch # User Steve Losh # Date 1482351726 18000 # Node ID 04de8001e1e00cabff32a6a27aceecbc3b4c3422 # Parent 363d33f1b089526942a596c081925cb34b1f01b4 Clean up gnuplot stuff, add `digit` diff -r 363d33f1b089 -r 04de8001e1e0 DOCUMENTATION.markdown --- a/DOCUMENTATION.markdown Wed Dec 21 15:21:56 2016 -0500 +++ b/DOCUMENTATION.markdown Wed Dec 21 15:22:06 2016 -0500 @@ -550,6 +550,65 @@ +## Package `LOSH.GNUPLOT` + +Utilities for plotting data with gnuplot. + +### `GNUPLOT` (function) + + (GNUPLOT DATA &REST ARGS &KEY (X #'CAR) (Y #'CDR) &ALLOW-OTHER-KEYS) + +Plot `data` to `filename` with gnuplot. + + This will (silently) quickload the `external-program` system to handle the + communication with gnuplot. + + `data` should be a sequence of data points to plot. + + `x` should be a function to pull the x-values from each item in data. + + `y` should be a function to pull the y-values from each item in data. + + See the docstring of `gnuplot-args` for other keyword arguments. + + + +### `GNUPLOT-ARGS` (function) + + (GNUPLOT-ARGS &KEY (FILENAME plot.png) (SIZE-X 1200) (SIZE-Y 800) (LABEL-X) + (LABEL-Y) (LINE-TITLE 'DATA) (LINE-WIDTH 4) (AXIS-X NIL) + (AXIS-Y NIL) (GRAPH-TITLE) &ALLOW-OTHER-KEYS) + +Return the formatted command line arguments for the given gnuplot arguments. + + You shouldn't call this function directly — it's exposed just so you can see + the list of possible gnuplot arguments all in one place. + + + +### `GNUPLOT-EXPR` (macro) + + (GNUPLOT-EXPR EXPR &REST ARGS) + +Plot `expr` (an expression involving `x`) with gnuplot. + + See the docstring of `gnuplot-args` for other keyword arguments. + + + +### `GNUPLOT-FUNCTION` (function) + + (GNUPLOT-FUNCTION FUNCTION &REST ARGS &KEY (START 0.0) (END 1.0) (STEP 0.1) + (INCLUDE-END NIL) &ALLOW-OTHER-KEYS) + +Plot `function` over [`start`, `end`) by `step` with gnuplot. + + If `include-end` is `t` the `end` value will also be plotted. + + See the docstring of `gnuplot-args` for other keyword arguments. + + + ## Package `LOSH.HASH-SETS` Simple hash set implementation. @@ -833,33 +892,29 @@ +### `DIGIT` (function) + + (DIGIT POSITION INTEGER &OPTIONAL (BASE 10)) + +Return the value of the digit at `position` in `integer`. + + Examples: + + (digit 0 135) ; => 5 + (digit 1 135) ; => 3 + (digit 2 135) ; => 1 + + (digit 0 #xD4 16) ; => 4 + (digit 1 #xD4 16) ; => 13 + + + ### `DIVIDESP` (function) (DIVIDESP N DIVISOR) Return whether `n` is evenly divisible by `divisor`. -### `GNUPLOT` (function) - - (GNUPLOT DATA &KEY (FILENAME plot.png) (X #'CAR) (Y #'CDR)) - -Plot `data` to `filename` with gnuplot. - - This will (silently) quickload the `external-program` system to handle the - communication with gnuplot. - - - -### `GNUPLOT-FUNCTION` (function) - - (GNUPLOT-FUNCTION FUNCTION &KEY (START 0.0) (END 1.0) (STEP 0.1)) - -Plot `function` with gnuplot. - - See `plot` for more information. - - - ### `IN-RANGE-P` (function) (IN-RANGE-P LOW VALUE HIGH) diff -r 363d33f1b089 -r 04de8001e1e0 losh.lisp --- a/losh.lisp Wed Dec 21 15:21:56 2016 -0500 +++ b/losh.lisp Wed Dec 21 15:22:06 2016 -0500 @@ -164,46 +164,22 @@ (< value high))) -(defun gnuplot (data &key - (filename "plot.png") - (x #'car) - (y #'cdr)) - "Plot `data` to `filename` with gnuplot. - - This will (silently) quickload the `external-program` system to handle the - communication with gnuplot. +(defun-inline digit (position integer &optional (base 10)) + "Return the value of the digit at `position` in `integer`. + + Examples: + + (digit 0 135) ; => 5 + (digit 1 135) ; => 3 + (digit 2 135) ; => 1 + + (digit 0 #xD4 16) ; => 4 + (digit 1 #xD4 16) ; => 13 " - (uiop/package:symbol-call :ql :quickload 'external-program :silent t) - (let* ((process (uiop/package:symbol-call - :external-program :start - "gnuplot" - `("-e" "set terminal png" - "-e" ,(format nil "set output '~A'" filename) - "-e" "plot '-' using 1:2 title 'DATA' with lines linewidth 2") - :input :stream - :output nil)) - (in (uiop/package:symbol-call - :external-program :process-input-stream - process))) - (unwind-protect - (progn - (iterate (for item :in data) - (format in "~A ~A~%" (funcall x item) (funcall y item))) - (finish-output in)) - (close in)) - process)) - -(defun gnuplot-function (function &key (start 0.0) (end 1.0) (step 0.1)) - "Plot `function` with gnuplot. - - See `plot` for more information. - - " - (let* ((x (range start end :step step)) - (y (mapcar function x)) - (data (mapcar #'cons x y))) - (gnuplot data))) + (-<> integer + (floor <> (expt base position)) + (mod <> base))) ;;;; Random ------------------------------------------------------------------- @@ -791,7 +767,7 @@ (defmacro-driver (FOR var MODULO divisor &sequence) - "Iterate numerically modulo `divisor`. + "Iterate numerically with `var` bound modulo `divisor`. This driver iterates just like the vanilla `for`, but each resulting value will be modulo'ed by `divisor` before being bound to `var`. @@ -845,10 +821,9 @@ (cons (first ,current) (car ,l)) (setf ,current nil))) - (t - (prog1 - (cons (first ,current) (second ,current)) - (setf ,current (cdr ,current)))))))))) + (t (prog1 + (cons (first ,current) (second ,current)) + (setf ,current (cdr ,current)))))))))) (defmacro-clause (AVERAGING expr &optional INTO var) @@ -2040,6 +2015,119 @@ (apply #'make-bset list)) +;;;; Gnuplot ------------------------------------------------------------------ +(defun gnuplot-args% (&rest args) + (mapcan (lambda (arg) (list "-e" arg)) + (remove nil args))) + +(defun gnuplot-args (&key + (filename "plot.png") + (size-x 1200) + (size-y 800) + (label-x) + (label-y) + (line-title 'data) + (line-width 4) + (axis-x nil) + (axis-y nil) + (graph-title) + &allow-other-keys) + "Return the formatted command line arguments for the given gnuplot arguments. + + You shouldn't call this function directly — it's exposed just so you can see + the list of possible gnuplot arguments all in one place. + + " + (flet ((esc (string) (remove #\' (aesthetic-string string))) + (f (&rest args) (apply #'format nil args))) + (gnuplot-args% + (f "set terminal pngcairo dashed size ~D,~D font \"Lucida Grande,20\"" + size-x size-y) + (f "set output '~A'" (esc filename)) + (f "set border linewidth 1") + (f "set style line 10 dashtype 2 linewidth 3 linecolor \"#666666\"") + (when axis-x (f "set xzeroaxis linestyle 10")) + (when axis-y (f "set yzeroaxis linestyle 10")) + (when graph-title (f "set title '~A'" (esc graph-title))) + (when label-x (f "set xlabel '~A'" (esc label-x))) + (when label-y (f "set ylabel '~A'" (esc label-y))) + (f "plot '-' using 1:2 title '~A' with lines linewidth ~D" + (esc line-title) line-width)))) + + +(defun gnuplot (data + &rest args + &key + (x #'car) + (y #'cdr) + &allow-other-keys) + "Plot `data` to `filename` with gnuplot. + + This will (silently) quickload the `external-program` system to handle the + communication with gnuplot. + + `data` should be a sequence of data points to plot. + + `x` should be a function to pull the x-values from each item in data. + + `y` should be a function to pull the y-values from each item in data. + + See the docstring of `gnuplot-args` for other keyword arguments. + + " + (uiop/package:symbol-call :ql :quickload 'external-program :silent t) + (let* ((process (uiop/package:symbol-call + :external-program :start + "gnuplot" + (apply #'gnuplot-args args) + :input :stream + :output nil)) + (in (uiop/package:symbol-call + :external-program :process-input-stream + process))) + (unwind-protect + (progn + (iterate (for item :in-whatever data) + (format in "~F ~F~%" (funcall x item) (funcall y item))) + (finish-output in)) + (close in)) + process)) + +(defun gnuplot-function (function + &rest args + &key + (start 0.0) + (end 1.0) + (step 0.1) + (include-end nil) + &allow-other-keys) + "Plot `function` over [`start`, `end`) by `step` with gnuplot. + + If `include-end` is `t` the `end` value will also be plotted. + + See the docstring of `gnuplot-args` for other keyword arguments. + + " + (let* ((x (range start end :step step)) + (x (append x + (when (and include-end + (not= (car (last x)) end)) + (list end)))) + (y (mapcar function x)) + (data (mapcar #'cons x y))) + (apply #'gnuplot data args))) + +(defmacro gnuplot-expr (expr &rest args) + "Plot `expr` (an expression involving `x`) with gnuplot. + + See the docstring of `gnuplot-args` for other keyword arguments. + + " + `(gnuplot-function (lambda (x) ,expr) + :line-title ',expr + ,@args)) + + ;;;; Licensing ---------------------------------------------------------------- ;;; Original code from @dk_jackdaniel: ;;; http://paste.lisp.org/display/327154 diff -r 363d33f1b089 -r 04de8001e1e0 make-docs.lisp --- a/make-docs.lisp Wed Dec 21 15:21:56 2016 -0500 +++ b/make-docs.lisp Wed Dec 21 15:22:06 2016 -0500 @@ -9,6 +9,7 @@ "LOSH.DEBUGGING" "LOSH.ELDRITCH-HORRORS" "LOSH.FUNCTIONS" + "LOSH.GNUPLOT" "LOSH.HASH-SETS" "LOSH.HASH-TABLES" "LOSH.ITERATE" diff -r 363d33f1b089 -r 04de8001e1e0 package.lisp --- a/package.lisp Wed Dec 21 15:21:56 2016 -0500 +++ b/package.lisp Wed Dec 21 15:22:06 2016 -0500 @@ -48,9 +48,7 @@ :precise-lerp :radians :square - - :gnuplot - :gnuplot-function)) + :digit)) (defpackage :losh.random (:documentation "Utilities related to randomness.") @@ -221,6 +219,15 @@ (:export :print-licenses)) +(defpackage :losh.gnuplot + (:documentation "Utilities for plotting data with gnuplot.") + (:export + :gnuplot + :gnuplot-args + :gnuplot-expr + :gnuplot-function + :x)) + (defpackage :losh.eldritch-horrors (:documentation "Abandon all hope, ye who enter here.") (:export @@ -242,6 +249,7 @@ :losh.debugging :losh.eldritch-horrors :losh.functions + :losh.gnuplot :losh.hash-sets :losh.hash-tables :losh.iterate