Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
;; esvg-build.el. -*- lexical-binding: nil; -*-
;; This file:
;;    https://anggtwu.net/elisp/esvg-build.el.html
;;    https://anggtwu.net/elisp/esvg-build.el
;;            (find-angg "elisp/esvg-build.el")
;;                  (find-esvg "esvg-build.el")
;;     See: https://anggtwu.net/2026-eepitch-svg.html
;;  Author: Eduardo Ochs <eduardoochs@gmail.com>
;; Version: 2026aug24
;;
;; This is the fourth lowest-level file in esvg - edrx's (REPL for) SVG.
;; It implements my basic functions that build SVG objects.
;;
;; Note that my notion of "basic" is different that the one in svg.el;
;; we say that I "factor" my functions in a different way. This is
;; discussed briefly, but with nice references, here:
;;
;;   https://anggtwu.net/2026-eepitch-svg.html#factoring
;;
;; For example, `svg-rectangle', defined in svg.el, builds an SVG
;; object, calls `svg--append' to append it to a certain top-level SVG
;; object, and then runs `svg-possibly-update-image'.
;;
;; In contrast, `esvg-rectangle', defined here, just builds an SVG
;; object; appending and displaying the image are done in other
;; functions, and in most of my (REPL-based) examples I define
;; functions with vary short names for doing appending, display, and
;; such. See:
;;
;;   https://anggtwu.net/2026-eepitch-svg.html#pict2e
;;   (find-esvg "esvg-xy.el" "tricks-with-g-test-1")
;;   (find-eev-quick-intro "7.4. Commands with very short names")
;;
;; «.esvg-translate»		(to "esvg-translate")
;; «.esvg-flatten»		(to "esvg-flatten")
;; «.esvg-make-simple»		(to "esvg-make-simple")
;; «.tricks-with-g»		(to "tricks-with-g")

(require 'esvg-show)		; (find-esvg "esvg-show.el")
(require 'esvg-eepitch)		; (find-esvg "esvg-eepitch.el")
(require 'esvg-append)		; (find-esvg "esvg-append.el")



;; «esvg-translate»  (to ".esvg-translate")
;; Based on: (find-angg "elisp/2026-esvg.el" "esvg-translate")
;; Tests: (esvg-translate-1 '(:a 1 :b 2         foo  bar))
;;        (esvg-translate     :a 1 :b 2        'foo 'bar)
;;        (esvg-translate-el  :a 1 :b 2 '(:c 3  foo  bar))
;;
(defun esvg-translate-1 (list)
  (let (attrs)
    (while (keywordp (car list))
      (let* ((key (intern (substring (symbol-name (car list)) 1)))
	     (val (cadr list)))
	(push `(,key . ,val) attrs)
	(setq list (cddr list))))
    `(,(reverse attrs) ,@list)))

(defun esvg-expand-last  (list) (append (butlast list) (car (last list))))
(defun esvg-translate    (&rest args) (esvg-translate-1 args))
(defun esvg-translate-el (&rest args) (esvg-translate-1 (esvg-expand-last args)))


;; «esvg-flatten»  (to ".esvg-flatten")
;; svg.el expects the lists of points for polylines and polygons to be
;; lists of dotted pairs; here we also accepts other formats. See:
;;   (find-efile "svg.el" "(svg-polyline svg '((200 . 100) ")
;;   (find-efile "svg.el" "(defun svg-polyline " "mapconcat")
;;   (find-angg "elisp/2026-esvg.el" "esvg-flatten")
;;
(defvar esvg-flattened)

(defun esvg-flattened-add (o)
  "A recursive function used by `esvg-flatten-to-list'."
  (cond ((null o))
	((atom o) (push o esvg-flattened))
	(t (esvg-flattened-add (car o))
	   (esvg-flattened-add (cdr o)))))

;; Test: (esvg-flatten-to-list '(1 2 (3 . "4")))
(defun esvg-flatten-to-list (o)
  (setq esvg-flattened nil)
  (esvg-flattened-add o)
  (reverse esvg-flattened))

;; Test: (esvg-flatten-points  '(1 2 (3 . "4")))
(defun esvg-flatten-points (o)
  (if (stringp o) o
    (let* ((ns  (esvg-flatten-to-list o))
	   (xys (cl-loop for (x y) on ns by 'cddr
			 collect (format "%s %s" x y))))
      (mapconcat 'identity xys ", "))))

;; Test: (esvg-flatten  '(1 2 L (3 . "4")))
(defun esvg-flatten (o)
  (if (stringp o) o
    (mapconcat 'identity
	       (cl-loop for s in (esvg-flatten-to-list o)
			collect (format "%s" s))
	       " ")))

;; «esvg-make-simple»  (to ".esvg-make-simple")
;; Based on: (find-efunction 'svg-rectangle)
;;           (find-efunction 'svg-circle)
;;           (find-efunction 'svg-ellipse)
;;           (find-efunction 'svg-line)
;;           (find-efunction 'svg-polyline)
;;           (find-efunction 'svg-polygon)

;; Tests: (esvg-rectangle 1 2 3 4 :foo "red")
;;        (esvg-circle    1 2 3   :foo "red")
;;        (esvg-elipse    1 2 3 4 :foo "red")
;;        (esvg-line      1 2 3 4 :foo "red")
;;        (esvg-polyline  "1 2  3 4  5 6" :foo "red")
;;        (esvg-polygon   "1 2  3 4  5 6" :foo "red")
;;        (esvg-polyline  '((1 . 2) (3 . 4) (5 . 6)) :foo "red")
;;        (esvg-polygon   '((1 . 2) (3 . 4) (5 . 6)) :foo "red")
;;        (esvg-path      '(M 50 400 Q (97 74)) :fill "none")
;;
(defun esvg-rectangle (x y width height &rest rest)
   `(rect ,@(esvg-translate-el :x x :y y :width width :height height rest)))

(defun esvg-circle (cx cy r &rest rest)
   `(circle ,@(esvg-translate-el :cx cx :cy cy :r r rest)))

(defun esvg-ellipse (cx cy rx ry &rest rest)
  `(ellipse ,@(esvg-translate-el :cx cx :cy cy :rx rx :ry ry rest)))

(defun esvg-line (x1 y1 x2 y2 &rest rest)
  `(line ,@(esvg-translate-el :x1 x1 :y1 y1 :x2 x2 :y2 y2 rest)))

(defun esvg-polyline (points &rest rest)
  `(polyline ,@(esvg-translate-el :points (esvg-flatten-points points) rest)))

(defun esvg-polygon (points &rest rest)
  `(polygon ,@(esvg-translate-el :points (esvg-flatten-points points) rest)))

(defun esvg-path (cmds &rest rest)
  `(path ,@(esvg-translate-el :d (esvg-flatten cmds) rest)))


(defun esvg-g (&rest rest)
  `(g ,@(esvg-translate-el rest)))

(defun esvg-defs (&rest rest)
  `(defs ,@(esvg-translate-el rest)))

(defun esvg-clippath (&rest rest)
  `(clipPath ,@(esvg-translate-el rest)))


(defun esvg-use (href &rest rest)
  `(use ,@(esvg-translate-el :href (format "#%s" href) rest)))


;; «tricks-with-g»  (to ".tricks-with-g")
;; Test: (find-esvg "esvg-xy.el" "tricks-with-g-test-1")
;;
(defun esvg-id (name &rest rest)
  (declare (indent 1))
  (apply 'esvg-g :id name rest))

(defun esvg-scale (sx sy &rest rest)
  (declare (indent 2))
  (apply 'esvg-g :transform (format "scale(%s,%s)" sx sy) rest))

(defun esvg-translate (tx ty &rest rest)
  (declare (indent 2))
  (apply 'esvg-g :transform (format "translate(%s,%s)" tx ty) rest))





(provide 'esvg-build)




;; Local Variables:
;; coding:  utf-8-unix
;; End: