|
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: