From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id UK/ELYU4YF83PQAA0tVLHw (envelope-from ) for ; Tue, 15 Sep 2020 03:44:05 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id UH/CJ4U4YF9gIwAAB5/wlQ (envelope-from ) for ; Tue, 15 Sep 2020 03:44:05 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 9B8A2940215 for ; Tue, 15 Sep 2020 03:44:03 +0000 (UTC) Received: from localhost ([::1]:56246 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kI1sv-0001DH-8M for larch@yhetil.org; Mon, 14 Sep 2020 23:44:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:54482) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kI1sX-0001Cn-4j for emacs-orgmode@gnu.org; Mon, 14 Sep 2020 23:43:37 -0400 Received: from mail-pf1-x442.google.com ([2607:f8b0:4864:20::442]:43496) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kI1sS-0002UY-NX for emacs-orgmode@gnu.org; Mon, 14 Sep 2020 23:43:36 -0400 Received: by mail-pf1-x442.google.com with SMTP id f18so1127463pfa.10 for ; Mon, 14 Sep 2020 20:43:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=references:user-agent:from:to:subject:message-id:date:mime-version; bh=A2wBZXC7km0FJgXR36Vpng+Q/R4IzKDsTlfhHngTXU0=; b=nCMot7I8f3gkosqJ7g6GDM76fy6TEd1bOWqce+Cnc32Lfar0lI25KSuFuhVGp/fuN0 92kPSxmXlbeeHFtBx60SS04j+F6Ly+8M3HX4bkPHtwD7MsDOF25DpqCm0SnZKA66lDaT NOvImN76zu3/pOT/aBhd39kqRyjBKaH9W/31WrlLoS22Tix9Z6lfBvwvtcNnr1u8RbOy YTPfZC2cTFeEivH9bxY+FO5M2cISRuq3TU1h4t6P//59yedij607Mog9pVEAithedH/2 3UsaQBmfZhkik1EOkAIn7K9UcwifJJcgveFOgtYWV6cKvtWY9B+VaRTcjrbC70OJ5Wbg OEtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:references:user-agent:from:to:subject:message-id :date:mime-version; bh=A2wBZXC7km0FJgXR36Vpng+Q/R4IzKDsTlfhHngTXU0=; b=XgRJUe/dMAza4MQ864OkZiuB02OPwuDDU8mVGgX8w5AUFcIeJqUMTjeF6odQMNft/s q+Q5jkhu9rhPYvSRWJtwwutmzLXqTjcnsOyzZGm/NL1OJY91fUTqEHblKGeR2UQcYpkW LlbuyOFH7SFzyUvS5DhzNGIi1ddKwrN7d1jheMwu7i/idHYBshmjvIHGzCdfbqERChNp 5zFngQwbUMjp67GIWLFPE5sf2PYNpAyx73qMnLnUaGp3JZt6fHFZpTvhs7LLqZ+cV5CA XxACZiRyyCK4u/tLT9AzvDK0g16QYROyaWdYXVxUaSZegrYNGeQGzGT3CQpU3Lv6W3Xu 7ltw== X-Gm-Message-State: AOAM532lmuyHtDyrPuii4TarmS2Z59Rh0ZaNrHXsAa/ADKy5mizQzx73 SwJ9fze3hzfH7BLKmL1SSZiFl8Ga+0kXZA== X-Google-Smtp-Source: ABdhPJyM9AFhARVU8PpEoLvj8YUo2VIzcU2RnHEQY/zYkXgpnlbNY2UBdPmtaR/P3FI4/0ut/76QRg== X-Received: by 2002:a65:691a:: with SMTP id s26mr13478315pgq.103.1600141410593; Mon, 14 Sep 2020 20:43:30 -0700 (PDT) Received: from localhost (180-150-91-8.b4965b.per.nbn.aussiebb.net. [180.150.91.8]) by smtp.gmail.com with ESMTPSA id n7sm11332110pfq.114.2020.09.14.20.43.28 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 14 Sep 2020 20:43:29 -0700 (PDT) References: <87imcrfntf.fsf@gmail.com> <87blijmnv9.fsf@gnu.org> User-agent: mu4e 1.4.13; emacs 27.1 From: TEC To: org-mode-email Subject: Re: [PATCH] org-plot abstractions and extension Message-ID: <87lfhbhfhe.fsf@gmail.com> Date: Tue, 15 Sep 2020 11:43:25 +0800 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2607:f8b0:4864:20::442; envelope-from=tecosaur@gmail.com; helo=mail-pf1-x442.google.com X-detected-operating-system: by eggs.gnu.org: No matching host in p0f cache. That's all we know. X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: "Emacs-orgmode" X-Scanner: scn0 Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20161025 header.b=nCMot7I8; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (aspmx1.migadu.com: domain of emacs-orgmode-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=emacs-orgmode-bounces@gnu.org X-Spam-Score: -1.71 X-TUID: Y8ja2VfFEBoS --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable 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 =F0=9F=98=85. 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 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. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-org-plot.el-add-new-option-transpose.patch >From c62e817b04dfbe624ee8b2090ebcde257bbd3f23 Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0003-org-plot.el-add-new-custom-gnuplot-preamble.patch >From fc7f4015c726e4a685002e8d69fad1eb1d605790 Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0005-org-plot.el-add-utility-functions-for-range-ticks.patch >From d99a61170bb0ff10b9fc7b99cdc957ec574c1e51 Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0004-org-plot.el-abstract-plot-types-into-custom-var.patch >From fc1ecf42cd8d0d27cda98ced2c2be365ad305df7 Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0006-org-plot.el-add-custom-var-for-affecting-the-term.patch >From 26c09d431030bacfc5a4ce84103b2eca186b2229 Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0007-org-plot.el-tweak-term-preamble-custom-vars.patch >From 5a1b9ff8f3ba5be565828137460023cd39194b6c Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0008-org-plot.el-add-radar-plot-type.patch >From 3e9338962a4af033bd56e8ab7a1abe5e636d71c5 Mon Sep 17 00:00:00 2001 From: TEC 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 <From 3743e507775b446f5f8188958c20f65861fac3fb Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0009-org-plot.el-fix-logic-error-in-transposition.patch >From 8b1ed7fb3cc418bb90fe48d3c4c8cb711decfded Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0010-org-plot.el-complete-transition-to-softcoded-type.patch >From bcd45c562966a893449d2412b363f31368ee5983 Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0011-org-plot.el-avoid-arithmetic-overflow-error.patch >From a299ec63c91260b68237da3a6c19e8ed8523fd6d Mon Sep 17 00:00:00 2001 From: TEC 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 --=-=-=--