0f2460b0b42e

Hack together `gnuplot` so I can see my math
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Thu, 15 Dec 2016 18:46:31 -0500
parents 6f1c9878ddbe
children c62d7dfcaed7
branches/tags (none)
files DOCUMENTATION.markdown losh.lisp package.lisp vendor/make-quickutils.lisp vendor/quickutils.lisp

Changes

--- a/DOCUMENTATION.markdown	Thu Dec 15 12:36:37 2016 -0500
+++ b/DOCUMENTATION.markdown	Thu Dec 15 18:46:31 2016 -0500
@@ -839,6 +839,27 @@
 
 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.
+
+  
+
 ### `LERP` (function)
 
     (LERP FROM TO N)
--- a/losh.lisp	Thu Dec 15 12:36:37 2016 -0500
+++ b/losh.lisp	Thu Dec 15 18:46:31 2016 -0500
@@ -159,6 +159,48 @@
       (t value))))
 
 
+(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.
+
+  "
+  (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 linespoints")
+                    :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)))
+
+
 ;;;; Random -------------------------------------------------------------------
 (defun-inlineable randomp (&optional (chance 0.5))
   "Return a random boolean with `chance` probability of `t`."
--- a/package.lisp	Thu Dec 15 12:36:37 2016 -0500
+++ b/package.lisp	Thu Dec 15 18:46:31 2016 -0500
@@ -46,7 +46,10 @@
     :lerp
     :precise-lerp
     :map-range
-    :clamp))
+    :clamp
+
+    :gnuplot
+    :gnuplot-function))
 
 (defpackage :losh.random
   (:documentation "Utilities related to randomness.")
--- a/vendor/make-quickutils.lisp	Thu Dec 15 12:36:37 2016 -0500
+++ b/vendor/make-quickutils.lisp	Thu Dec 15 18:46:31 2016 -0500
@@ -16,6 +16,7 @@
                :map-tree
                :mkstr
                :once-only
+               :range
                :rcurry
                :symb
                :weave
--- a/vendor/quickutils.lisp	Thu Dec 15 12:36:37 2016 -0500
+++ b/vendor/quickutils.lisp	Thu Dec 15 18:46:31 2016 -0500
@@ -2,7 +2,7 @@
 ;;;; See http://quickutil.org for details.
 
 ;;;; To regenerate:
-;;;; (qtlc:save-utils-as "quickutils.lisp" :utilities '(:COMPOSE :COPY-HASH-TABLE :CURRY :EMPTYP :ENSURE-KEYWORD :ENSURE-LIST :FLATTEN :HASH-TABLE-KEYS :HASH-TABLE-VALUES :MAP-TREE :MKSTR :ONCE-ONLY :RCURRY :SYMB :WEAVE :WITH-GENSYMS) :ensure-package T :package "LOSH.QUICKUTILS")
+;;;; (qtlc:save-utils-as "quickutils.lisp" :utilities '(:COMPOSE :COPY-HASH-TABLE :CURRY :EMPTYP :ENSURE-KEYWORD :ENSURE-LIST :FLATTEN :HASH-TABLE-KEYS :HASH-TABLE-VALUES :MAP-TREE :MKSTR :ONCE-ONLY :RANGE :RCURRY :SYMB :WEAVE :WITH-GENSYMS) :ensure-package T :package "LOSH.QUICKUTILS")
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (unless (find-package "LOSH.QUICKUTILS")
@@ -19,7 +19,7 @@
                                          :ENSURE-LIST :FLATTEN :MAPHASH-KEYS
                                          :HASH-TABLE-KEYS :MAPHASH-VALUES
                                          :HASH-TABLE-VALUES :MAP-TREE :MKSTR
-                                         :ONCE-ONLY :RCURRY :SYMB :WEAVE
+                                         :ONCE-ONLY :RANGE :RCURRY :SYMB :WEAVE
                                          :STRING-DESIGNATOR :WITH-GENSYMS))))
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defun make-gensym-list (length &optional (x "G"))
@@ -248,6 +248,14 @@
                ,@forms)))))
   
 
+  (defun range (start end &key (step 1) (key 'identity))
+    "Return the list of numbers `n` such that `start <= n < end` and
+`n = start + k*step` for suitable integers `k`. If a function `key` is
+provided, then apply it to each number."
+    (assert (<= start end))
+    (loop :for i :from start :below end :by step :collecting (funcall key i)))
+  
+
   (defun rcurry (function &rest arguments)
     "Returns a function that applies the arguments it is called
 with and `arguments` to `function`."
@@ -319,6 +327,6 @@
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (export '(compose copy-hash-table curry emptyp ensure-keyword ensure-list
             flatten hash-table-keys hash-table-values map-tree mkstr once-only
-            rcurry symb weave with-gensyms with-unique-names)))
+            range rcurry symb weave with-gensyms with-unique-names)))
 
 ;;;; END OF quickutils.lisp ;;;;