unofficial mirror of emacs-orgmode@gnu.org
 help / color / mirror / Atom feed
* [PATCH] org-plot abstractions and extension
@ 2020-09-06  5:50 TEC
       [not found] ` <87blijmnv9.fsf@gnu.org>
  0 siblings, 1 reply; 2+ messages in thread
From: TEC @ 2020-09-06  5:50 UTC (permalink / raw)
  To: org-mode-email


[-- Attachment #1.1: Type: text/plain, Size: 2607 bytes --]

Hi All,

I think while my org-plot work isn't yet complete, it has reached a
review-able
state.

I see org-plot as a great tool for making it easy to produce
high-quality
graphics from tabular data in Org files. With this in mind, this set of
patches
aims to achieve to overarching goals:
1. improve the flexibility of org-plot by abstracting out some
   hard-coded elements
2. introduce some (hopefully, generally useful) utility functions, and a
   new
   default plot type that makes use of them --- radar

If you are interested in org-plot, or have some time to spare and are
feeling
helpful, please give me your thoughts on the attached patches :)

Timothy.

<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0001-org-plot.el-make-indentation-method-consistent.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0002-org-plot.el-add-new-option-transpose.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0003-org-plot.el-add-new-custom-gnuplot-preamble.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0004-org-plot.el-abstract-plot-types-into-custom-var.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0005-org-plot.el-add-utility-functions-for-range-ticks.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0006-org-plot.el-add-custom-var-for-affecting-the-term.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0007-org-plot.el-tweak-term-preamble-custom-vars.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0008-org-plot.el-add-radar-plot-type.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0009-org-plot.el-fix-logic-error-in-transposition.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0010-org-plot.el-complete-transition-to-softcoded-type.patch"
disposition=attachment>
<#/part>
<#part type="text/x-patch"
filename="/home/tec/.emacs.d/.local/straight/repos/org-mode/0011-org-plot.el-avoid-arithmetic-overflow-error.patch"
disposition=attachment>
<#/part>

[-- Attachment #1.2: Type: text/html, Size: 5682 bytes --]

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] org-plot abstractions and extension
       [not found]   ` <CAHNg_jM8sE4a6XvL5D8Gks4dQXfWhZvRBR33BDLkRgEgZ++ZGg@mail.gmail.com>
@ 2020-09-15  3:43     ` TEC
  0 siblings, 0 replies; 2+ messages in thread
From: TEC @ 2020-09-15  3:43 UTC (permalink / raw)
  To: org-mode-email

[-- Attachment #1: Type: text/plain, Size: 850 bytes --]


Oooops, I've just noticed my patch attachment re-send was only addressed
to Bastien (maybe this is why I haven't heard anything?).
This is what I get for mixing mail clients and not paying attention
I guess 😅.

If someone would be willing to have a look through my work, and comment
- that would be fantastic.

I'd love to get my code into shape to be merged :)

All the best,

Timothy.

> Bastien <bzg@gnu.org> wrote:
>
>> Can you repost as plain text?  The email is not very readable in HTML
>> and the patches are not readable at all.
>
> Ooops, that shouldn't have happened. Unfortunately, I have yet to find
> a good way of attaching files in mu4e.
> Those should have been converted into attachments in a plaintext
> email, but that didn't work.
>
> Let me know if this attempt works as intended,
>
> Timothy.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0002-org-plot.el-add-new-option-transpose.patch --]
[-- Type: text/x-patch, Size: 3084 bytes --]

From c62e817b04dfbe624ee8b2090ebcde257bbd3f23 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Wed, 8 Jul 2020 19:26:07 +0800
Subject: [PATCH 02/11] org-plot.el: add new option :transpose

* lisp/org-plot.el (org-plot/add-options-to-plist,
org-plot/add-options-to-plist): Add a new option :transpose, and a
shorter alias :trans. Transposition is performed if the argument is yes,
y, or t.  This treats the table as a matrix and performs matrix
transposition on it.  If an hline is present, it is assumed that it is a
marks a separation from a first header row.  The first row is then
treated as the new header by inserting a hline in the transposed data.
This is quite useful for some plots, where across multiple categories,
there are a large number of data points.  Without this, the data points
would be columns and the table can spread irritatingly wide.
---
 lisp/org-plot.el | 44 +++++++++++++++++++++++++++++---------------
 1 file changed, 29 insertions(+), 15 deletions(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index c08bc144e..6ff633130 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -50,19 +50,21 @@
   "Parse an OPTIONS line and set values in the property list P.
 Returns the resulting property list."
   (when options
-    (let ((op '(("type"    . :plot-type)
-		("script"  . :script)
-		("line"    . :line)
-		("set"     . :set)
-		("title"   . :title)
-		("ind"     . :ind)
-		("deps"    . :deps)
-		("with"    . :with)
-		("file"    . :file)
-		("labels"  . :labels)
-		("map"     . :map)
-		("timeind" . :timeind)
-		("timefmt" . :timefmt)))
+    (let ((op '(("type"      . :plot-type)
+		("script"    . :script)
+		("line"      . :line)
+		("set"       . :set)
+		("title"     . :title)
+		("ind"       . :ind)
+		("deps"      . :deps)
+		("with"      . :with)
+		("file"      . :file)
+		("labels"    . :labels)
+		("map"       . :map)
+		("timeind"   . :timeind)
+		("timefmt"   . :timefmt)
+		("trans"     . :transpose)
+		("transpose" . :transpose)))
 	  (multiples '("set" "line"))
 	  (regexp ":\\([\"][^\"]+?[\"]\\|[(][^)]+?[)]\\|[^ \t\n\r;,.]*\\)")
 	  (start 0))
@@ -289,8 +291,20 @@ line directly before or after the table."
 	(setf params (plist-put params (car pair) (cdr pair)))))
     ;; collect table and table information
     (let* ((data-file (make-temp-file "org-plot"))
-	   (table (org-table-collapse-header (org-table-to-lisp)))
-	   (num-cols (length (car table))))
+	   (table (let ((tbl (org-table-to-lisp)))
+		    (when (pcase (plist-get params :transpose)
+			    ('y   t)
+			    ('yes t)
+			    ('t   t))
+		      (if (memq 'hline tbl)
+			  (setq tbl (apply #'cl-mapcar #'list tbl))
+			;; When present, remove hlines as they can't (currentily) be easily transposed.
+			(setq tbl (apply #'cl-mapcar #'list
+					 (remove 'hline tbl)))
+			(push 'hline (cdr tbl))))
+		    tbl))
+	   (num-cols (length (if (eq (nth 0 table) 'hline) (nth 1 table)
+			       (nth 0 table)))))
       (run-with-idle-timer 0.1 nil #'delete-file data-file)
       (when (eq (cadr table) 'hline)
 	(setf params
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0003-org-plot.el-add-new-custom-gnuplot-preamble.patch --]
[-- Type: text/x-patch, Size: 2013 bytes --]

From fc7f4015c726e4a685002e8d69fad1eb1d605790 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Wed, 8 Jul 2020 22:26:21 +0800
Subject: [PATCH 03/11] org-plot.el: add new custom gnuplot preamble

* lisp/org-plot.el: Define new custom variable
`org-plot/gnuplot-script-preamble' which can be either a string or a
function.  The value of this (when executed, in the case of the
function) is inserted near the top of the generated gnuplot script.
(org-plot/gnuplot-script): Use the new variable
`org-plot/gnuplot-script-preamble' in the manner described.

This allows for the user to set the font/colour-scheme, default
precision, and much more.
---
 lisp/org-plot.el | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index 6ff633130..f8db45273 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -181,6 +181,13 @@ and dependent variables."
 	  (setf back-edge "") (setf front-edge ""))))
     row-vals))
 
+(defcustom org-plot/gnuplot-script-preamble ""
+  "String or function which provides content to be inserted into the GNUPlot
+script before the plot command. Not that this is in addition to, not instead of
+other content generated in `org-plot/gnuplot-script'."
+  :group 'org-plot
+  :type '(choice string function))
+
 (defun org-plot/gnuplot-script (data-file num-cols params &optional preface)
   "Write a gnuplot script to DATA-FILE respecting the options set in PARAMS.
 NUM-COLS controls the number of columns plotted in a 2-d plot.
@@ -213,6 +220,12 @@ manner suitable for prepending to a user-specified script."
     (when file				; output file
       (funcall ats (format "set term %s" (file-name-extension file)))
       (funcall ats (format "set output '%s'" file)))
+
+    (funcall ats
+	     (if (stringp org-plot/gnuplot-script-preamble)
+		 org-plot/gnuplot-script-preamble
+	       (org-plot/gnuplot-script-preamble)))
+
     (pcase type				; type
       (`2d ())
       (`3d (when map (funcall ats "set map")))
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0005-org-plot.el-add-utility-functions-for-range-ticks.patch --]
[-- Type: text/x-patch, Size: 6285 bytes --]

From d99a61170bb0ff10b9fc7b99cdc957ec574c1e51 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Thu, 9 Jul 2020 04:47:40 +0800
Subject: [PATCH 05/11] org-plot.el: add utility functions for range,ticks

* lisp/org-plot.el (org-plot/add-options-to-plist): Add the options :ymin
:ymax :xmin :xmax, as well as :min and :max as aliases to the y{min,max}
options.  The :ticks option is also added, for specifying how many ticks
should be used.
(org--plot/values-stats, org--plot/sensible-tick-num,
org--plot/nice-frequency-pick, org--plot/merge-alists,
org--plot/item-frequencies, org--plot/prime-factors): New utility
functions added to allow for somewhat sensible determination of a :ticks
value when none is provided.  This turns out to be harder than expected,
and so a number of functions are used to attempt to do so.  The essence
of the method used, is to round values and find their prime
decompositions.  From this we try to select the most common components
to give a reasonable step size.  We also add a 'ticks' parameter for
manually setting the number of ticks, and (y)min/max parameters
similarly.
---
 lisp/org-plot.el | 100 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index 207f5d4af..2a9c0f5bd 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -63,6 +63,11 @@ Returns the resulting property list."
 		("map"       . :map)
 		("timeind"   . :timeind)
 		("timefmt"   . :timefmt)
+		("min"       . :ymin)
+		("max"       . :ymax)
+		("ymin"      . :ymin)
+		("xmax"      . :xmax)
+		("ticks"     . :ticks)
 		("trans"     . :transpose)
 		("transpose" . :transpose)))
 	  (multiples '("set" "line"))
@@ -181,6 +186,101 @@ and dependent variables."
 	  (setf back-edge "") (setf front-edge ""))))
     row-vals))
 
+(defun org--plot/values-stats (nums &optional hard-min hard-max)
+  "From a list of NUMS return a plist containing some rudamentry statistics on the
+values, namely regarding the range."
+  (let* ((minimum (or hard-min (apply #'min nums)))
+	 (maximum (or hard-max (apply #'max nums)))
+	 (range (- maximum minimum))
+	 (rangeOrder (ceiling (- 1 (log10 range))))
+	 (range-factor (expt 10 rangeOrder))
+	 (nice-min (/ (float (floor (* minimum range-factor))) range-factor))
+	 (nice-max (/ (float (ceiling (* maximum range-factor))) range-factor)))
+    `(:min ,minimum :max ,maximum :range ,range
+      :range-factor ,range-factor
+      :nice-min ,nice-min :nice-max ,nice-max :nice-range ,(- nice-max nice-min))))
+
+(defun org--plot/sensible-tick-num (table &optional hard-min hard-max)
+  "From a the values in a TABLE of data, attempt to guess an appropriate number of ticks."
+  (let* ((row-data
+	  (mapcar (lambda (row) (org--plot/values-stats
+			    (mapcar #'string-to-number (cdr row))
+			    hard-min
+			    hard-max)) table))
+	 (row-normalised-ranges (mapcar (lambda (r-data)
+					  (let ((val (round (*
+							     (plist-get r-data :range-factor)
+							     (plist-get r-data :nice-range)))))
+					    (if (= (% val 10) 0) (/ val 10) val)))
+					row-data))
+	 (range-prime-decomposition (mapcar #'org--plot/prime-factors row-normalised-ranges))
+	 (weighted-factors (sort (apply #'org--plot/merge-alists #'+ 0
+					(mapcar (lambda (factors) (org--plot/item-frequencies factors t))
+						range-prime-decomposition))
+				 (lambda (a b) (> (cdr a) (cdr b))))))
+    (apply #'* (org--plot/nice-frequency-pick weighted-factors))))
+
+(defun org--plot/nice-frequency-pick (frequencies)
+  "From a list of frequences, try to sensibly pick a sample of the most frequent."
+  ;; TODO this mosly works decently, but counld do with some tweaking to work more consistently.
+  (case (length frequencies)
+    (1 (list (car (nth 0 frequencies))))
+    (2 (if (<= 3 (/ (cdr (nth 0 frequencies))
+		    (cdr (nth 1 frequencies))))
+	   (make-list 2
+		      (car (nth 0 frequencies)))
+	 (list (car (nth 0 frequencies))
+	       (car (nth 1 frequencies)))))
+    (t
+     (let* ((total-count (apply #'+ (mapcar #'cdr frequencies)))
+	    (n-freq (mapcar (lambda (freq) `(,(car freq) . ,(/ (float (cdr freq)) total-count))) frequencies))
+	    (f-pick (list (car (car n-freq))))
+	    (1-2-ratio (/ (cdr (nth 0 n-freq))
+			  (cdr (nth 1 n-freq))))
+	    (2-3-ratio (/ (cdr (nth 1 n-freq))
+			  (cdr (nth 2 n-freq))))
+	    (1-3-ratio (* 1-2-ratio 2-3-ratio))
+	    (1-val (car (nth 0 n-freq)))
+	    (2-val (car (nth 1 n-freq)))
+	    (3-val (car (nth 2 n-freq))))
+       (when (> 1-2-ratio 4) (push 1-val f-pick))
+       (when (and (< 1-2-ratio 2-val)
+		  (< (* (apply #'* f-pick) 2-val) 30))
+	 (push 2-val f-pick))
+       (when (and (< 1-3-ratio 3-val)
+		  (< (* (apply #'* f-pick) 3-val) 30))
+	 (push 3-val f-pick))
+       f-pick))))
+
+(defun org--plot/merge-alists (function default alist1 alist2 &rest alists)
+  "Using FUNCTION, combine the elements of all given ALISTS. When an element is
+only present in one alist, DEFAULT is used as the second argument for the FUNCTION."
+  (when (> (length alists) 0)
+    (setq alist2 (apply #'org--plot/merge-alists function default alist2 alists)))
+  (flet ((keys (alist) (mapcar #'car alist))
+	 (lookup (key alist) (or (cdr (assoc key alist)) default)))
+    (loop with keys = (union (keys alist1) (keys alist2) :test 'equal)
+	  for k in keys collect
+	  (cons k (funcall function (lookup k alist1) (lookup k alist2))))))
+
+(defun org--plot/item-frequencies (values &optional normalise)
+  "Return an alist indicating the frequency of values in VALUES list."
+  (let ((normaliser (if normalise (float (length values)) 1)))
+    (cl-loop for (n . m) in (seq-group-by #'identity values)
+	     collect (cons n (/ (length m) normaliser)))))
+
+(defun org--plot/prime-factors (value)
+  "Return the prime decomposition of VALUE, e.g. for 12, '(3 2 2)"
+  (let ((factors '(1)) (i 1))
+    (while (/= 1 value)
+      (setq i (1+ i))
+      (when (eq 0 (% value i))
+	(push i factors)
+	(setq value (/ value i))
+	(setq i (1- i))
+	))
+    (subseq factors 0 -1)))
+
 (defcustom org-plot/gnuplot-script-preamble ""
   "String or function which provides content to be inserted into the GNUPlot
 script before the plot command. Not that this is in addition to, not instead of
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: 0004-org-plot.el-abstract-plot-types-into-custom-var.patch --]
[-- Type: text/x-patch, Size: 4470 bytes --]

From fc1ecf42cd8d0d27cda98ced2c2be365ad305df7 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Thu, 9 Jul 2020 04:27:18 +0800
Subject: [PATCH 04/11] org-plot.el: abstract plot types into custom var

* lisp/org-plot.el (org-plot/gnuplot-script): Abstract the generation of
gnuplot commands from the three hardcoded types: 2d, 3d, and grid.
A new custom variable `org-plot/preset-plot-types' is defined to declare
plot types and provide a lambda which is called with a fixed signature
to generate associated gnuplot code.  The previously hardcoded types are
implemented as the default value.

By extracting these types to a custom variable, users are able to create
their own presets for frequently used setups. Note that while this moves
the most significant hardcoding of the 2d, 3d, and grid types in
`org-plot/gnuplot-script', there are still a few minor fragments that I
am not sure how to best address --- yet.
---
 lisp/org-plot.el | 71 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 48 insertions(+), 23 deletions(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index f8db45273..207f5d4af 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -188,6 +188,49 @@ other content generated in `org-plot/gnuplot-script'."
   :group 'org-plot
   :type '(choice string function))
 
+(defcustom org-plot/preset-plot-types
+  '((2d (lambda (data-file num-cols params plot-str)
+	  (let* ((type (plist-get params :plot-type))
+		 (with (if (eq type 'grid) 'pm3d (plist-get params :with)))
+		 (ind (plist-get params :ind))
+		 (deps (if (plist-member params :deps) (plist-get params :deps)))
+		 (text-ind (plist-get params :textind))
+		 (col-labels (plist-get params :labels))
+		 res)
+	    (dotimes (col num-cols res)
+	      (unless (and (eq type '2d)
+			   (or (and ind (equal (1+ col) ind))
+			       (and deps (not (member (1+ col) deps)))))
+		(setf res
+		      (cons
+		       (format plot-str data-file
+			       (or (and ind (> ind 0)
+					(not text-ind)
+					(format "%d:" ind)) "")
+			       (1+ col)
+			       (if text-ind (format ":xticlabel(%d)" ind) "")
+			       with
+			       (or (nth col col-labels)
+				   (format "%d" (1+ col))))
+		       res)))))))
+    (3d (lambda (data-file num-cols params plot-str)
+	  (let* ((type (plist-get params :plot-type))
+		 (with (if (eq type 'grid) 'pm3d (plist-get params :with))))
+	    (list (format "'%s' matrix with %s title ''"
+			  data-file with)))))
+    (grid (lambda (data-file num-cols params plot-str)
+	    (let* ((type (plist-get params :plot-type))
+		   (with (if (eq type 'grid) 'pm3d (plist-get params :with))))
+	    (list (format "'%s' with %s title ''"
+			  data-file with))))))
+  "List of plot presets with the type name as the car, and a function
+which yeilds plot-lines (a list of strings) as the cdr.
+The parameters of `org-plot/gnuplot-script' and PLOT-STR are passed to
+that function. i.e. it is called with the following arguments:
+  DATA-FILE NUM-COLS PARAMS PLOT-STR"
+  :group 'org-plot
+  :type '(alist :value-type (symbol group)))
+
 (defun org-plot/gnuplot-script (data-file num-cols params &optional preface)
   "Write a gnuplot script to DATA-FILE respecting the options set in PARAMS.
 NUM-COLS controls the number of columns plotted in a 2-d plot.
@@ -254,29 +297,11 @@ manner suitable for prepending to a user-specified script."
 			   (or timefmt	; timefmt passed to gnuplot
 			       "%Y-%m-%d-%H:%M:%S") "\"")))
     (unless preface
-      (pcase type			; plot command
-	(`2d (dotimes (col num-cols)
-	       (unless (and (eq type '2d)
-			    (or (and ind (equal (1+ col) ind))
-				(and deps (not (member (1+ col) deps)))))
-		 (setf plot-lines
-		       (cons
-			(format plot-str data-file
-				(or (and ind (> ind 0)
-					 (not text-ind)
-					 (format "%d:" ind)) "")
-				(1+ col)
-				(if text-ind (format ":xticlabel(%d)" ind) "")
-				with
-				(or (nth col col-labels)
-				    (format "%d" (1+ col))))
-			plot-lines)))))
-	(`3d
-	 (setq plot-lines (list (format "'%s' matrix with %s title ''"
-					data-file with))))
-	(`grid
-	 (setq plot-lines (list (format "'%s' with %s title ''"
-					data-file with)))))
+      (let ((type-func (cadr (assoc type org-plot/preset-plot-types))))
+	(when type-func
+	  (setq plot-lines
+		(funcall type-func data-file num-cols params plot-str))))
+
       (funcall ats
 	       (concat plot-cmd " " (mapconcat #'identity
 					       (reverse plot-lines)
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0006-org-plot.el-add-custom-var-for-affecting-the-term.patch --]
[-- Type: text/x-patch, Size: 2034 bytes --]

From 26c09d431030bacfc5a4ce84103b2eca186b2229 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Thu, 9 Jul 2020 05:00:03 +0800
Subject: [PATCH 06/11] org-plot.el: add custom var for affecting the term

* lisp/org-plot.el (org-plot/gnuplot-script): Allow for customisation of
org-plot's term by adding a custom variable
`org-plot/gnuplot-term-extra' which allows the user to tweak the gnuplot
term settings.  This allows for setting characteristics such as default
size, or background colour.
---
 lisp/org-plot.el | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index 2a9c0f5bd..ed4cea195 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -331,6 +331,13 @@ that function. i.e. it is called with the following arguments:
   :group 'org-plot
   :type '(alist :value-type (symbol group)))
 
+(defcustom org-plot/gnuplot-term-extra ""
+  "String or function which provides the extra term options.
+E.g. a value of \"size 1050,650\" would cause
+\"set term ... size 1050,650\" to be used."
+  :group 'org-plot
+  :type '(choice string function))
+
 (defun org-plot/gnuplot-script (data-file num-cols params &optional preface)
   "Write a gnuplot script to DATA-FILE respecting the options set in PARAMS.
 NUM-COLS controls the number of columns plotted in a 2-d plot.
@@ -360,8 +367,15 @@ manner suitable for prepending to a user-specified script."
 	 ;; ats = add-to-script
 	 (ats (lambda (line) (setf script (concat script "\n" line))))
 	 plot-lines)
-    (when file				; output file
-      (funcall ats (format "set term %s" (file-name-extension file)))
+
+
+    ;; handle output file, background, and size
+    (funcall ats (format "set term %s %s"
+			 (if file (file-name-extension file) "GNUTERM")
+			 (if (stringp org-plot/gnuplot-term-extra)
+			     org-plot/gnuplot-term-extra
+			   (org-plot/gnuplot-term-extra))))
+    (when file ; output file
       (funcall ats (format "set output '%s'" file)))
 
     (funcall ats
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #7: 0007-org-plot.el-tweak-term-preamble-custom-vars.patch --]
[-- Type: text/x-patch, Size: 2287 bytes --]

From 5a1b9ff8f3ba5be565828137460023cd39194b6c Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Thu, 9 Jul 2020 05:05:20 +0800
Subject: [PATCH 07/11] org-plot.el: tweak term, preamble custom vars

* lisp/org-plot.el (org-plot/gnuplot-script): Call the term and preamble
functions (mentioned below) with the plot type as the argument.
(org-plot/gnuplot-script-preamble, org-plot/gnuplot-term-extra): update
docstring.
---
 lisp/org-plot.el | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index ed4cea195..52422ea2f 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -284,7 +284,8 @@ only present in one alist, DEFAULT is used as the second argument for the FUNCTI
 (defcustom org-plot/gnuplot-script-preamble ""
   "String or function which provides content to be inserted into the GNUPlot
 script before the plot command. Not that this is in addition to, not instead of
-other content generated in `org-plot/gnuplot-script'."
+other content generated in `org-plot/gnuplot-script'.
+If a function, it is called with the plot type as the argument."
   :group 'org-plot
   :type '(choice string function))
 
@@ -334,7 +335,8 @@ that function. i.e. it is called with the following arguments:
 (defcustom org-plot/gnuplot-term-extra ""
   "String or function which provides the extra term options.
 E.g. a value of \"size 1050,650\" would cause
-\"set term ... size 1050,650\" to be used."
+\"set term ... size 1050,650\" to be used.
+If a function, it is called with the plot type as the argument."
   :group 'org-plot
   :type '(choice string function))
 
@@ -374,14 +376,14 @@ manner suitable for prepending to a user-specified script."
 			 (if file (file-name-extension file) "GNUTERM")
 			 (if (stringp org-plot/gnuplot-term-extra)
 			     org-plot/gnuplot-term-extra
-			   (org-plot/gnuplot-term-extra))))
+			   (org-plot/gnuplot-term-extra type))))
     (when file ; output file
       (funcall ats (format "set output '%s'" file)))
 
     (funcall ats
 	     (if (stringp org-plot/gnuplot-script-preamble)
 		 org-plot/gnuplot-script-preamble
-	       (org-plot/gnuplot-script-preamble)))
+	       (org-plot/gnuplot-script-preamble type)))
 
     (pcase type				; type
       (`2d ())
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #8: 0008-org-plot.el-add-radar-plot-type.patch --]
[-- Type: text/x-patch, Size: 6388 bytes --]

From 3e9338962a4af033bd56e8ab7a1abe5e636d71c5 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Thu, 9 Jul 2020 05:21:44 +0800
Subject: [PATCH 08/11] org-plot.el: add radar plot type

* lisp/org-plot.el (org--plot/radar): Implement a new plot type "radar".
(org--plot/radar-template): A huge template sting for `org-plot/radar'.
(org--plot/radar-ticks, org--plot/radar-setup-template): Smaller
template strings for use in `org-plot/radar'.
(org-plot/preset-plot-types): Add the new "radar" type to the list of
default types.

The radar type has a long and complex implementation, but that's exactly
what makes it perfect for something like this. A complex plot can be
produced with a simple keyword in the #+PLOT options. There are still a
few kinks that would benefit from being ironed out, but the current
state is fully-functional.
---
 lisp/org-plot.el | 138 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 1 deletion(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index 52422ea2f..fd92a12a1 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -323,7 +323,9 @@ If a function, it is called with the plot type as the argument."
 	    (let* ((type (plist-get params :plot-type))
 		   (with (if (eq type 'grid) 'pm3d (plist-get params :with))))
 	    (list (format "'%s' with %s title ''"
-			  data-file with))))))
+			  data-file with)))))
+    (radar (lambda (data-file num-cols params plot-str)
+	     (list (org--plot/radar table params)))))
   "List of plot presets with the type name as the car, and a function
 which yeilds plot-lines (a list of strings) as the cdr.
 The parameters of `org-plot/gnuplot-script' and PLOT-STR are passed to
@@ -332,6 +334,140 @@ that function. i.e. it is called with the following arguments:
   :group 'org-plot
   :type '(alist :value-type (symbol group)))
 
+(defvar org--plot/radar-template
+  "### spider plot/chart with gnuplot
+# also known as: radar chart, web chart, star chart, cobweb chart,
+#                radar plot,  web plot,  star plot,  cobweb plot,  etc. ...
+set datafile separator ' '
+set size square
+unset tics
+set angles degree
+set key bmargin center horizontal
+unset border
+
+# Load data and settup
+load \"%s\"
+
+# General settings
+DataColCount = words($Data[1])-1
+AxesCount = |$Data|-HeaderLines
+AngleOffset = 90
+Max = 1
+d=0.1*Max
+Direction = -1   # counterclockwise=1, clockwise = -1
+
+# Tic settings
+TicCount = %s
+TicOffset = 0.1
+TicValue(axis,i) = real(i)*(word($Settings[axis],3)-word($Settings[axis],2)) \\
+	  / word($Settings[axis],4)+word($Settings[axis],2)
+TicLabelPosX(axis,i) = PosX(axis,i/TicCount) + PosY(axis, TicOffset)
+TicLabelPosY(axis,i) = PosY(axis,i/TicCount) - PosX(axis, TicOffset)
+TicLen = 0.03
+TicdX(axis,i) = 0.5*TicLen*cos(alpha(axis)-90)
+TicdY(axis,i) = 0.5*TicLen*sin(alpha(axis)-90)
+
+# Label
+LabOffset = 0.10
+LabX(axis) = PosX(axis+1,Max+2*d) + PosY(axis, LabOffset)
+LabY(axis) = PosY($0+1,Max+2*d)
+
+# Functions
+alpha(axis) = (axis-1)*Direction*360.0/AxesCount+AngleOffset
+PosX(axis,R) = R*cos(alpha(axis))
+PosY(axis,R) = R*sin(alpha(axis))
+Scale(axis,value) = real(value-word($Settings[axis],2))/(word($Settings[axis],3)-word($Settings[axis],2))
+
+# Spider settings
+set style arrow 1 dt 1 lw 1.0 @fgal head filled size 0.06,25     # style for axes
+set style arrow 2 dt 2 lw 0.5 @fgal nohead   # style for weblines
+set style arrow 3 dt 1 lw 1 @fgal nohead     # style for axis tics
+set samples AxesCount
+set isosamples TicCount
+set urange[1:AxesCount]
+set vrange[1:TicCount]
+set style fill transparent solid 0.2
+
+set xrange[-Max-4*d:Max+4*d]
+set yrange[-Max-4*d:Max+4*d]
+plot \\
+    '+' u (0):(0):(PosX($0,Max+d)):(PosY($0,Max+d)) w vec as 1 not, \\
+    $Data u (LabX($0)): \\
+	(LabY($0)):1 every ::HeaderLines w labels center enhanced @fgt not, \\
+    for [i=1:DataColCount] $Data u (PosX($0+1,Scale($0+1,column(i+1)))): \\
+	(PosY($0+1,Scale($0+1,column(i+1)))) every ::HeaderLines w filledcurves lt i title word($Data[1],i+1), \\
+%s
+#    '++' u (PosX($1,$2/TicCount)-TicdX($1,$2/TicCount)): \\
+#        (PosY($1,$2/TicCount)-TicdY($1,$2/TicCount)): \\
+#        (2*TicdX($1,$2/TicCount)):(2*TicdY($1,$2/TicCount)) \\
+#        w vec as 3 not, \\
+### end of code
+")
+
+(defvar org--plot/radar-ticks
+  "    '++' u (PosX($1,$2/TicCount)):(PosY($1,$2/TicCount)): \\
+	(PosX($1+1,$2/TicCount)-PosX($1,$2/TicCount)):  \\
+	(PosY($1+1,$2/TicCount)-PosY($1,$2/TicCount)) w vec as 2 not, \\
+    '++' u (TicLabelPosX(%s,$2)):(TicLabelPosY(%s,$2)): \\
+	(sprintf('%%g',TicValue(%s,$2))) w labels font ',8' @fgat not")
+
+(defvar org--plot/radar-setup-template
+  "# Data
+$Data <<HEREHAVESOMEDATA
+%s
+HEREHAVESOMEDATA
+HeaderLines = 1
+
+# Settings for scale and offset adjustments
+# axis min max tics axisLabelXoff axisLabelYoff
+$Settings <<EOD
+%s
+EOD
+")
+
+(defun org--plot/radar (table params)
+  (let* ((data
+	  (concat "\"" (s-join "\" \"" (plist-get params :labels)) "\""
+		  "\n"
+		  (s-join "\n"
+			  (mapcar (lambda (row)
+				    (format
+				     "\"%s\" %s"
+				     (car row)
+				     (s-join " " (cdr row))))
+				  table))))
+	 (ticks (or (plist-get params :ticks)
+		    (org--plot/sensible-tick-num table
+						 (plist-get params :ymin)
+						 (plist-get params :ymax))))
+	 (settings
+	  (s-join "\n"
+		  (mapcar (lambda (row)
+			    (let ((data (org--plot/values-stats
+					 (mapcar #'string-to-number (cdr row)))))
+			      (format
+			       "\"%s\" %s %s %s"
+			       (car row)
+			       (or (plist-get params :ymin)
+				   (plist-get data :nice-min))
+			       (or (plist-get params :ymax)
+				   (plist-get data :nice-max))
+			       (if (eq ticks 0) 2 ticks)
+			       )))
+			  table)))
+	 (setup-file (make-temp-file "org-plot-setup")))
+    (f-write-text (format org--plot/radar-setup-template data settings)
+		  'utf-8 setup-file)
+    (format org--plot/radar-template
+	    setup-file
+	    (if (eq ticks 0) 2 ticks)
+	    (if (eq ticks 0) ""
+	      (apply #'format org--plot/radar-ticks
+		     (make-list 3 (if (and (plist-get params :ymin)
+					   (plist-get params :ymax))
+				      ;; FIXME multi-drawing of tick labels with "1"
+				      "1" "$1")))))))
+
 (defcustom org-plot/gnuplot-term-extra ""
   "String or function which provides the extra term options.
 E.g. a value of \"size 1050,650\" would cause
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #9: 0001-org-plot.el-make-indentation-method-consistent.patch --]
[-- Type: text/x-patch, Size: 1467 bytes --]

From 3743e507775b446f5f8188958c20f65861fac3fb Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Wed, 8 Jul 2020 18:34:46 +0800
Subject: [PATCH 01/11] org-plot.el: make indentation method consistent

* lisp/org-plot.el (org-plot/gnuplot): Make indentation consistent, by
replacing a few spaces with tabs.

Only 6 of 347 lines used spaces instead of tabs.
---
 lisp/org-plot.el | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index 0ff96af67..c08bc144e 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -325,12 +325,12 @@ line directly before or after the table."
       (with-temp-buffer
 	(if (plist-get params :script)	; user script
 	    (progn (insert
-                    (org-plot/gnuplot-script data-file num-cols params t))
-                   (insert "\n")
-                   (insert-file-contents (plist-get params :script))
-                   (goto-char (point-min))
-                   (while (re-search-forward "\\$datafile" nil t)
-                     (replace-match data-file nil nil)))
+		    (org-plot/gnuplot-script data-file num-cols params t))
+		   (insert "\n")
+		   (insert-file-contents (plist-get params :script))
+		   (goto-char (point-min))
+		   (while (re-search-forward "\\$datafile" nil t)
+		     (replace-match data-file nil nil)))
 	  (insert (org-plot/gnuplot-script data-file num-cols params)))
 	;; Graph table.
 	(gnuplot-mode)
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #10: 0009-org-plot.el-fix-logic-error-in-transposition.patch --]
[-- Type: text/x-patch, Size: 1668 bytes --]

From 8b1ed7fb3cc418bb90fe48d3c4c8cb711decfded Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Thu, 30 Jul 2020 18:25:19 +0800
Subject: [PATCH 09/11] org-plot.el: fix logic error in transposition

* lisp/org-plot.el (org-plot/gnuplot): If statement in transposition
treated condition as its negative, to fix this the condition was
inverted.
It was also noticed that the code could not operate as expected as the
user-supplied #+plot options were not fetched. Resolved by re-inserting
relevant code from an older version of org-plot.
---
 lisp/org-plot.el | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index fd92a12a1..1b227d698 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -579,6 +579,10 @@ line directly before or after the table."
     (dolist (pair org-plot/gnuplot-default-options)
       (unless (plist-member params (car pair))
 	(setf params (plist-put params (car pair) (cdr pair)))))
+    ;; Collect options.
+    (save-excursion (while (and (equal 0 (forward-line -1))
+				(looking-at "[[:space:]]*#\\+"))
+		      (setf params (org-plot/collect-options params))))
     ;; collect table and table information
     (let* ((data-file (make-temp-file "org-plot"))
 	   (table (let ((tbl (org-table-to-lisp)))
@@ -586,7 +590,7 @@ line directly before or after the table."
 			    ('y   t)
 			    ('yes t)
 			    ('t   t))
-		      (if (memq 'hline tbl)
+		      (if (not (memq 'hline tbl))
 			  (setq tbl (apply #'cl-mapcar #'list tbl))
 			;; When present, remove hlines as they can't (currentily) be easily transposed.
 			(setq tbl (apply #'cl-mapcar #'list
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #11: 0010-org-plot.el-complete-transition-to-softcoded-type.patch --]
[-- Type: text/x-patch, Size: 13498 bytes --]

From bcd45c562966a893449d2412b363f31368ee5983 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Thu, 30 Jul 2020 18:36:11 +0800
Subject: [PATCH 10/11] org-plot.el: complete transition to softcoded type

* lisp/org-plot.el (org-plot/preset-plot-types): Adapt structure to
cover all type-specific logic within org-plot.
(org-plot/gnuplot-script, org-plot/gnuplot): Replace type-specific logic
with references to properties of the type from
`org-plot/preset-plot-types'.
---
 lisp/org-plot.el | 242 ++++++++++++++++++++++++++++-------------------
 1 file changed, 143 insertions(+), 99 deletions(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index 1b227d698..53186bb75 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -290,7 +290,10 @@ If a function, it is called with the plot type as the argument."
   :type '(choice string function))
 
 (defcustom org-plot/preset-plot-types
-  '((2d (lambda (data-file num-cols params plot-str)
+  '((2d :plot-cmd "plot"
+	:check-ind-type t
+	:plot-func
+	(lambda (_table data-file num-cols params plot-str)
 	  (let* ((type (plist-get params :plot-type))
 		 (with (if (eq type 'grid) 'pm3d (plist-get params :with)))
 		 (ind (plist-get params :ind))
@@ -314,23 +317,60 @@ If a function, it is called with the plot type as the argument."
 			       (or (nth col col-labels)
 				   (format "%d" (1+ col))))
 		       res)))))))
-    (3d (lambda (data-file num-cols params plot-str)
+    (3d :plot-cmd "splot"
+	:plot-pre (lambda (_table _data-file _num-cols params _plot-str)
+		    (if (plist-get params :map) "set map"))
+	:plot-func
+	(lambda (_table data-file _num-cols params _plot-str)
 	  (let* ((type (plist-get params :plot-type))
 		 (with (if (eq type 'grid) 'pm3d (plist-get params :with))))
 	    (list (format "'%s' matrix with %s title ''"
 			  data-file with)))))
-    (grid (lambda (data-file num-cols params plot-str)
+    (grid :plot-cmd "splot"
+	  :plot-pre (lambda (_table _data-file _num-cols params _plot-str)
+		      (if (plist-get params :map) "set pm3d map" "set map"))
+	  :data-dump (lambda (table data-file params _num-cols)
+		       (let ((y-labels (org-plot/gnuplot-to-grid-data
+					table data-file params)))
+			 (when y-labels (plist-put params :ylabels y-labels))))
+	  :plot-func
+	  (lambda (table data-file _num-cols params _plot-str)
 	    (let* ((type (plist-get params :plot-type))
 		   (with (if (eq type 'grid) 'pm3d (plist-get params :with))))
-	    (list (format "'%s' with %s title ''"
-			  data-file with)))))
-    (radar (lambda (data-file num-cols params plot-str)
+	      (list (format "'%s' with %s title ''"
+			    data-file with)))))
+    (radar :plot-func
+	   (lambda (table _data-file _num-cols params plot-str)
 	     (list (org--plot/radar table params)))))
-  "List of plot presets with the type name as the car, and a function
-which yeilds plot-lines (a list of strings) as the cdr.
-The parameters of `org-plot/gnuplot-script' and PLOT-STR are passed to
-that function. i.e. it is called with the following arguments:
-  DATA-FILE NUM-COLS PARAMS PLOT-STR"
+  "List of plists describing the avalible plot types.
+The car is the type name, and the property :plot-func must be set.
+The value of :plot-func is a lambda which yields plot-lines
+(a list of strings) as the cdr.
+
+All lambda functions have the parameters of `org-plot/gnuplot-script' and PLOT-STR passed to them.
+i.e. they are called with the following signature: (TABLE DATA-FILE NUM-COLS PARAMS PLOT-STR)
+
+Potentially useful parameters in PARAMS include:
+ :set :line :map :title :file :ind :timeind :timefmt :textind
+ :deps :labels :xlabels :ylabels :xmin :xmax :ymin :ymax :ticks
+
+In addition to :plot-func, the following optional properties may be set.
+
+- :plot-cmd - A gnuplot command appended to each plot-line.
+  Accepts string or nil. Default value: nil.
+
+- :check-ind-type - Whether the types of ind values should be checked.
+  Accepts boolean.
+
+- :plot-str - the formula string passed to :plot-func as PLOT-STR
+  Accepts string. Default value: \"'%s' using %s%d%s with %s title '%s'\"
+
+- :data-dump - Function to dump the table to a datafile for ease of use.
+  Accepts lambda function. Default lambda body: (org-plot/gnuplot-to-data table data-file params)
+
+- :plot-pre - Gnuplot code to be inserted early into the script, just after term and output have been set.
+   Accepts string, nil, or lambda function which returns string or nil. Defaults to nil.
+"
   :group 'org-plot
   :type '(alist :value-type (symbol group)))
 
@@ -476,89 +516,90 @@ If a function, it is called with the plot type as the argument."
   :group 'org-plot
   :type '(choice string function))
 
-(defun org-plot/gnuplot-script (data-file num-cols params &optional preface)
+(defun org-plot/gnuplot-script (table data-file num-cols params &optional preface)
   "Write a gnuplot script to DATA-FILE respecting the options set in PARAMS.
 NUM-COLS controls the number of columns plotted in a 2-d plot.
 Optional argument PREFACE returns only option parameters in a
 manner suitable for prepending to a user-specified script."
-  (let* ((type (plist-get params :plot-type))
-	 (with (if (eq type 'grid) 'pm3d (plist-get params :with)))
-	 (sets (plist-get params :set))
-	 (lines (plist-get params :line))
-	 (map (plist-get params :map))
-	 (title (plist-get params :title))
-	 (file (plist-get params :file))
-	 (ind (plist-get params :ind))
-	 (time-ind (plist-get params :timeind))
-	 (timefmt (plist-get params :timefmt))
-	 (text-ind (plist-get params :textind))
-	 (deps (if (plist-member params :deps) (plist-get params :deps)))
-	 (col-labels (plist-get params :labels))
-	 (x-labels (plist-get params :xlabels))
-	 (y-labels (plist-get params :ylabels))
-	 (plot-str "'%s' using %s%d%s with %s title '%s'")
-	 (plot-cmd (pcase type
-		     (`2d "plot")
-		     (`3d "splot")
-		     (`grid "splot")))
-	 (script "reset")
-	 ;; ats = add-to-script
-	 (ats (lambda (line) (setf script (concat script "\n" line))))
-	 plot-lines)
-
-
-    ;; handle output file, background, and size
-    (funcall ats (format "set term %s %s"
-			 (if file (file-name-extension file) "GNUTERM")
-			 (if (stringp org-plot/gnuplot-term-extra)
-			     org-plot/gnuplot-term-extra
-			   (org-plot/gnuplot-term-extra type))))
-    (when file ; output file
-      (funcall ats (format "set output '%s'" file)))
-
-    (funcall ats
-	     (if (stringp org-plot/gnuplot-script-preamble)
-		 org-plot/gnuplot-script-preamble
-	       (org-plot/gnuplot-script-preamble type)))
-
-    (pcase type				; type
-      (`2d ())
-      (`3d (when map (funcall ats "set map")))
-      (`grid (funcall ats (if map "set pm3d map" "set pm3d"))))
-    (when title (funcall ats (format "set title '%s'" title))) ; title
-    (mapc ats lines)					       ; line
-    (dolist (el sets) (funcall ats (format "set %s" el)))      ; set
-    ;; Unless specified otherwise, values are TAB separated.
-    (unless (string-match-p "^set datafile separator" script)
-      (funcall ats "set datafile separator \"\\t\""))
-    (when x-labels			; x labels (xtics)
-      (funcall ats
-	       (format "set xtics (%s)"
-		       (mapconcat (lambda (pair)
-				    (format "\"%s\" %d" (cdr pair) (car pair)))
-				  x-labels ", "))))
-    (when y-labels			; y labels (ytics)
-      (funcall ats
-	       (format "set ytics (%s)"
-		       (mapconcat (lambda (pair)
-				    (format "\"%s\" %d" (cdr pair) (car pair)))
-				  y-labels ", "))))
-    (when time-ind			; timestamp index
-      (funcall ats "set xdata time")
-      (funcall ats (concat "set timefmt \""
-			   (or timefmt	; timefmt passed to gnuplot
-			       "%Y-%m-%d-%H:%M:%S") "\"")))
-    (unless preface
-      (let ((type-func (cadr (assoc type org-plot/preset-plot-types))))
-	(when type-func
-	  (setq plot-lines
-		(funcall type-func data-file num-cols params plot-str))))
+  (let* ((type-name (plist-get params :plot-type))
+	 (type (cdr (assoc type-name org-plot/preset-plot-types))))
+    (unless type
+      (user-error "Org-plot type `%s' is undefined." type-name))
+    (let* ((sets (plist-get params :set))
+	   (lines (plist-get params :line))
+	   (map (plist-get params :map))
+	   (title (plist-get params :title))
+	   (file (plist-get params :file))
+	   (ind (plist-get params :ind))
+	   (time-ind (plist-get params :timeind))
+	   (timefmt (plist-get params :timefmt))
+	   (text-ind (plist-get params :textind))
+	   (deps (if (plist-member params :deps) (plist-get params :deps)))
+	   (col-labels (plist-get params :labels))
+	   (x-labels (plist-get params :xlabels))
+	   (y-labels (plist-get params :ylabels))
+	   (plot-str (or (plist-get type :plot-str)
+			 "'%s' using %s%d%s with %s title '%s'"))
+	   (plot-cmd (plist-get type :plot-cmd))
+	   (plot-pre (plist-get type :plot-pre))
+	   (script "reset")
+	   ;; ats = add-to-script
+	   (ats (lambda (line) (when line (setf script (concat script "\n" line)))))
+	   plot-lines)
+
+
+      ;; handle output file, background, and size
+      (funcall ats (format "set term %s %s"
+			   (if file (file-name-extension file) "GNUTERM")
+			   (if (stringp org-plot/gnuplot-term-extra)
+			       org-plot/gnuplot-term-extra
+			     (funcall org-plot/gnuplot-term-extra type))))
+      (when file ; output file
+	(funcall ats (format "set output '%s'" file)))
+
+      (when plot-pre
+	(funcall ats (funcall plot-pre table data-file num-cols params plot-str)))
 
       (funcall ats
-	       (concat plot-cmd " " (mapconcat #'identity
-					       (reverse plot-lines)
-					       ",\\\n    "))))
-    script))
+	       (if (stringp org-plot/gnuplot-script-preamble)
+		   org-plot/gnuplot-script-preamble
+		 (funcall org-plot/gnuplot-script-preamble type)))
+
+      (when title (funcall ats (format "set title '%s'" title))) ; title
+      (mapc ats lines)					       ; line
+      (dolist (el sets) (funcall ats (format "set %s" el)))      ; set
+      ;; Unless specified otherwise, values are TAB separated.
+      (unless (string-match-p "^set datafile separator" script)
+	(funcall ats "set datafile separator \"\\t\""))
+      (when x-labels			; x labels (xtics)
+	(funcall ats
+		 (format "set xtics (%s)"
+			 (mapconcat (lambda (pair)
+				      (format "\"%s\" %d" (cdr pair) (car pair)))
+				    x-labels ", "))))
+      (when y-labels			; y labels (ytics)
+	(funcall ats
+		 (format "set ytics (%s)"
+			 (mapconcat (lambda (pair)
+				      (format "\"%s\" %d" (cdr pair) (car pair)))
+				    y-labels ", "))))
+      (when time-ind			; timestamp index
+	(funcall ats "set xdata time")
+	(funcall ats (concat "set timefmt \""
+			     (or timefmt	; timefmt passed to gnuplot
+				 "%Y-%m-%d-%H:%M:%S") "\"")))
+      (unless preface
+	(let ((type-func (plist-get type :plot-func)))
+	  (when type-func
+	    (setq plot-lines
+		  (funcall type-func table data-file num-cols params plot-str))))
+	(funcall ats
+		 (concat plot-cmd
+			 (when plot-cmd " ")
+			 (mapconcat #'identity
+				    (reverse plot-lines)
+				    ",\\\n    "))))
+      script)))
 
 ;;-----------------------------------------------------------------------------
 ;; facade functions
@@ -598,7 +639,13 @@ line directly before or after the table."
 			(push 'hline (cdr tbl))))
 		    tbl))
 	   (num-cols (length (if (eq (nth 0 table) 'hline) (nth 1 table)
-			       (nth 0 table)))))
+			       (nth 0 table))))
+	   (type (assoc (plist-get params :plot-type)
+			org-plot/preset-plot-types)))
+
+      (unless type
+	(user-error "Org-plot type `%s' is undefined." type-name))
+
       (run-with-idle-timer 0.1 nil #'delete-file data-file)
       (when (eq (cadr table) 'hline)
 	(setf params
@@ -608,15 +655,12 @@ line directly before or after the table."
       (save-excursion (while (and (equal 0 (forward-line -1))
 				  (looking-at "[[:space:]]*#\\+"))
 			(setf params (org-plot/collect-options params))))
-      ;; Dump table to datafile (very different for grid).
-      (pcase (plist-get params :plot-type)
-	(`2d   (org-plot/gnuplot-to-data table data-file params))
-	(`3d   (org-plot/gnuplot-to-data table data-file params))
-	(`grid (let ((y-labels (org-plot/gnuplot-to-grid-data
-				table data-file params)))
-		 (when y-labels (plist-put params :ylabels y-labels)))))
+      ;; Dump table to datafile
+      (if-let ((dump-func (plist-get type :data-dump)))
+	  (funcall dump-func table data-file num-cols params)
+	(org-plot/gnuplot-to-data table data-file params))
       ;; Check type of ind column (timestamp? text?)
-      (when (eq `2d (plist-get params :plot-type))
+      (when (plist-get params :check-ind-type)
 	(let* ((ind (1- (plist-get params :ind)))
 	       (ind-column (mapcar (lambda (row) (nth ind row)) table)))
 	  (cond ((< ind 0) nil) ; ind is implicit
@@ -633,13 +677,13 @@ line directly before or after the table."
       (with-temp-buffer
 	(if (plist-get params :script)	; user script
 	    (progn (insert
-		    (org-plot/gnuplot-script data-file num-cols params t))
+		    (org-plot/gnuplot-script table data-file num-cols params t))
 		   (insert "\n")
 		   (insert-file-contents (plist-get params :script))
 		   (goto-char (point-min))
 		   (while (re-search-forward "\\$datafile" nil t)
 		     (replace-match data-file nil nil)))
-	  (insert (org-plot/gnuplot-script data-file num-cols params)))
+	  (insert (org-plot/gnuplot-script table data-file num-cols params)))
 	;; Graph table.
 	(gnuplot-mode)
 	(gnuplot-send-buffer-to-gnuplot))
-- 
2.28.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #12: 0011-org-plot.el-avoid-arithmetic-overflow-error.patch --]
[-- Type: text/x-patch, Size: 1584 bytes --]

From a299ec63c91260b68237da3a6c19e8ed8523fd6d Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Sat, 5 Sep 2020 21:05:36 +0800
Subject: [PATCH 11/11] org-plot.el: avoid arithmetic overflow error

* lisp/org-plot.el (org--plot/values-stats): A set of numbers with the
same value (i.e. 0 range) should not produce an arithmetic overflow
error. This error was caused by taking the log of 0 (when the range is
0). This is mitigated by explicit checking against this case.
---
 lisp/org-plot.el | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lisp/org-plot.el b/lisp/org-plot.el
index 53186bb75..0a9694263 100644
--- a/lisp/org-plot.el
+++ b/lisp/org-plot.el
@@ -192,10 +192,13 @@ values, namely regarding the range."
   (let* ((minimum (or hard-min (apply #'min nums)))
 	 (maximum (or hard-max (apply #'max nums)))
 	 (range (- maximum minimum))
-	 (rangeOrder (ceiling (- 1 (log10 range))))
+	 (rangeOrder (if (= range 0) 0
+			 (ceiling (- 1 (log10 range)))))
 	 (range-factor (expt 10 rangeOrder))
-	 (nice-min (/ (float (floor (* minimum range-factor))) range-factor))
-	 (nice-max (/ (float (ceiling (* maximum range-factor))) range-factor)))
+	 (nice-min (if (= range 0) (car nums)
+		     (/ (float (floor (* minimum range-factor))) range-factor)))
+	 (nice-max (if (= range 0) (car nums)
+		     (/ (float (ceiling (* maximum range-factor))) range-factor))))
     `(:min ,minimum :max ,maximum :range ,range
       :range-factor ,range-factor
       :nice-min ,nice-min :nice-max ,nice-max :nice-range ,(- nice-max nice-min))))
-- 
2.28.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-09-15  3:44 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-06  5:50 [PATCH] org-plot abstractions and extension TEC
     [not found] ` <87blijmnv9.fsf@gnu.org>
     [not found]   ` <CAHNg_jM8sE4a6XvL5D8Gks4dQXfWhZvRBR33BDLkRgEgZ++ZGg@mail.gmail.com>
2020-09-15  3:43     ` TEC

unofficial mirror of emacs-orgmode@gnu.org

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://yhetil.org/orgmode/0 orgmode/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 orgmode orgmode/ https://yhetil.org/orgmode \
		emacs-orgmode@gnu.org
	public-inbox-index orgmode

Example config snippet for mirrors.
Newsgroups are available over NNTP:
	nntp://news.yhetil.org/yhetil.emacs.orgmode
	nntp://news.gmane.io/gmane.emacs.orgmode


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git