|
Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
;; esvg-xy.el. -*- lexical-binding: nil; -*-
;; This file:
;; https://anggtwu.net/elisp/esvg-xy.el.html
;; https://anggtwu.net/elisp/esvg-xy.el
;; (find-angg "elisp/esvg-xy.el")
;; (find-esvg "esvg-xy.el")
;; See: https://anggtwu.net/2026-eepitch-svg.html
;; Author: Eduardo Ochs <eduardoochs@gmail.com>
;; Version: 2026aug24
;;
;; The _basic_ part of esvg consists of these four files,
;; (find-esvg "esvg-append.el")
;; (find-esvg "esvg-show.el")
;; (find-esvg "esvg-eepitch.el")
;; (find-esvg "esvg-build.el")
;; and this fifth file with demos:
;; (find-esvg "esvg-demos.el")
;;
;; This file contains an experimental extension of esvg that I am
;; using to draw figures in the (x,y) plane on top a grid with axes
;; and ticks. See:
;;
;; https://anggtwu.net/2026-eepitch-svg.html#pict2e
;;
;; This file contains some definitions AND its demos (in tests blocks).
;; It is "experimental" in the sense that I am probably still going to
;; rewrite it completely several times.
;;
;; Index:
;; «.S-B-G-A-T» (to "S-B-G-A-T")
;; «.esvg-xy-svg» (to "esvg-xy-svg")
;; «.esvg-xy-rect» (to "esvg-xy-rect")
;; «.esvg-xy-grid» (to "esvg-xy-grid")
;; «.esvg-xy-axes» (to "esvg-xy-axes")
;; «.esvg-xy-ticks» (to "esvg-xy-ticks")
;; «.S-B-G-A-T-test» (to "S-B-G-A-T-test")
;; «.SBGAT-nod» (to "SBGAT-nod")
;; «.esvg-xy-sbgat-nod» (to "esvg-xy-sbgat-nod")
;; «.esvg-xy-sbgat-nod-test» (to "esvg-xy-sbgat-nod-test")
;; «.SBGAT» (to "SBGAT")
;; «.esvg-xy-sbgat» (to "esvg-xy-sbgat")
;; «.SBGAT-test-1» (to "SBGAT-test-1")
;; «.esvg-xy-sbgat-test-1» (to "esvg-xy-sbgat-test-1")
;; «.esvg-xy-sbgat-test» (to "esvg-xy-sbgat-test")
;; «.esvg-xy-circles» (to "esvg-xy-circles")
;; «.esvg-xy-dots» (to "esvg-xy-dots")
;; «.SBGAT-test-2» (to "SBGAT-test-2")
;; «.tricks-with-g-test-1» (to "tricks-with-g-test-1")
;; «.tricks-with-g-test-2» (to "tricks-with-g-test-2")
;; «.comprehension-test-1» (to "comprehension-test-1")
(require 'esvg-show) ; (find-esvg "esvg-show.el")
(require 'esvg-eepitch) ; (find-esvg "esvg-eepitch.el")
(require 'esvg-append) ; (find-esvg "esvg-append.el")
(require 'esvg-build) ; (find-esvg "esvg-build.el")
;;; ____ ____ ____ _ _____
;;; / ___| | __ ) / ___| / \ |_ _|
;;; \___ \ | _ \ | | _ / _ \ | |
;;; ___) | | |_) | | |_| | / ___ \ | |
;;; |____/ |____/ \____| /_/ \_\ |_|
;;;
;; «S-B-G-A-T» (to ".S-B-G-A-T")
;; In this section we define five low-level functions that create:
;; 1. an `S'VG with a viewbox
;; 2. its white `B'ackground, using a white rectangle
;; 3. its `G'rid,
;; 4. its `A'xes,
;; 5. its `T'icks,
;; «esvg-xy-svg» (to ".esvg-xy-svg")
;; Some tests (hint: use `M-2 M-e'):
;; (find-epp (esvg-xy-svg 0 0 4 3 0 400 300))
;; (find-epp (esvg-xy-svg 0 0 4 3 0 nil 300))
;; (find-epp (esvg-xy-svg 0 0 4 3 0 400 nil))
;; (find-epp (esvg-xy-svg 0 0 4 3 0.1 400 nil))
;; (find-epp (esvg-xy-svg 0 0 4 3 0.1 400 nil :fill "none"))
(defun esvg-xy-svg (x0 y0 x1 y1 e width height &rest rest)
"This is a wrapper around `svg-create' that returns an SVG with a viewBox.
It supposes that (x0,y0) are the coordinates of the lower left corner of
a viewBox, that (x1,y1) are the coordinates of its upper right corner,
and that E is an amount of extra space that needs to be added above,
below, and left and right of that box, yielding a new viewbox whose
extremities are (x0-e,y0-e) and (x1+e,y1+e). This function creates an
SVG object whose dimensions in pixels are WIDTH and HEIGHT, and whose
viewBox has extremities (x0-e,y0-e) and (x1+e,y1+e).
This function uses uses transform=\"scale(1,-1) translate(0,-<height>)\"
to make the y coordinate grow upwards. If WIDTH is nil it is calculated
from HEIGHT in the natural way; if HEIGHT is nil it is calculated from
WIDTH in the natural way."
(let* ((x0e (- x0 e))
(y0e (- y0 e))
(x1e (+ x1 e))
(y1e (+ y1 e))
(w (- x1e x0e))
(h (- y1e y0e))
(w/h (/ w 1.0 h))
(h/w (/ h 1.0 w)))
(if (not width) (setq width (* height w/h)))
(if (not height) (setq height (* width h/w)))
(let* ((transform (format "scale(1,-1) translate(0,%S)" (- height)))
(viewbox (format "%S %S %S %S" x0e y0e w h)))
(apply 'svg-create width height
:transform transform :viewBox viewbox rest))))
;; «esvg-xy-rect» (to ".esvg-xy-rect")
;; Test: (find-epp (esvg-xy-axes 0 0 4 3 0.1 :stroke "black"))
(defun esvg-xy-rect (x0 y0 x1 y1 e &rest rest)
(let* ((x0e (- x0 e))
(y0e (- y0 e))
(x1e (+ x1 e))
(y1e (+ y1 e)))
(apply 'esvg-rectangle x0e y0e (- x1e x0e) (- y1e y0e) rest)))
;; «esvg-xy-grid» (to ".esvg-xy-grid")
;; Test: (find-epp (esvg-xy-grid 0 0 4 3 0.1 :stroke "gray"))
(defun esvg-xy-grid (x0 y0 x1 y1 e &rest rest)
(cl-letf* ((svg-a (apply 'esvg-g rest))
((symbol-function 'f) (lambda (o) (esvg-append svg-a o))))
(cl-loop for x from x0 to x1
do (f (esvg-line x (- y0 e) x (+ y1 e)))) ; vertical line on x
(cl-loop for y from y0 to y1
do (f (esvg-line (- x0 e) y (+ x1 e) y))) ; horizontal line on y
svg-a))
;; «esvg-xy-axes» (to ".esvg-xy-axes")
;; Test: (find-epp (esvg-xy-axes 0 0 4 3 0.1 :stroke "black"))
(defun esvg-xy-axes (x0 y0 x1 y1 e &rest rest)
(cl-letf* ((svg-a (apply 'esvg-g rest))
((symbol-function 'f) (lambda (o) (esvg-append svg-a o))))
(f (esvg-line (- x0 e) 0 (+ x1 e) 0)) ; x axis
(f (esvg-line 0 (- y0 e) 0 (+ y1 e))) ; y axis
svg-a))
;; «esvg-xy-ticks» (to ".esvg-xy-ticks")
;; Test: (find-epp (esvg-xy-ticks 0 0 4 3 0.1 :stroke "black"))
(defun esvg-xy-ticks (x0 y0 x1 y1 e &rest rest)
(cl-letf* ((svg-a (apply 'esvg-g rest))
((symbol-function 'f) (lambda (o) (esvg-append svg-a o))))
(cl-loop for x from x0 to x1
do (f (esvg-line x (- e) x e))) ; vertical tick on x
(cl-loop for y from y0 to y1
do (f (esvg-line (- e) y e y))) ; horizontal tick on y
svg-a))
;; «S-B-G-A-T-test» (to ".S-B-G-A-T-test")
;; In this demo we draw, in separate steps:
;; 1. an `S'VG with a viewbox
;; 2. its white `B'ackground, using a white rectangle
;; 3. its `G'rid,
;; 4. its `A'xes,
;; 5. its `T'icks,
;; 6. a polyline.
;; It the next section we will define a function that creates a SBGAT.
;;
'("This is a test block for `eepitch-svg'! Use <f9>s!"
* (load-esvg) ; (find-angg ".emacs" "load-esvg")
(defun f (o) (esvg-append svg-a o) (show-svg-pp))
(defun f (o) (esvg-append svg-a o) (show-svg))
(setq svg (esvg-xy-svg 0 0 5 4 0.5 nil 350 :fill "none" :stroke-width 0.1))
(setq svg-a svg)
(show-svg)
(f (esvg-xy-rect 0 0 5 4 0.5 :fill "white" :stroke "none"))
(f (esvg-xy-grid 0 0 5 4 0.15 :stroke "#cccccc" :stroke-width 0.015))
(f (esvg-xy-axes 0 0 5 4 0.25 :stroke "black" :stroke-width 0.05))
(f (esvg-xy-ticks 0 0 5 4 0.15 :stroke "black" :stroke-width 0.02))
(f (esvg-polyline "1 1 2 1 3 2 4 2" :stroke "red"))
"--")
;;; ____ ____ ____ _ _____ __ ___
;;; / ___|| __ ) / ___| / \|_ _| / / __ ___ __| \ \
;;; \___ \| _ \| | _ / _ \ | | | | '_ \ / _ \ / _` || |
;;; ___) | |_) | |_| |/ ___ \| | | | | | | (_) | | (_| || |
;;; |____/|____/ \____/_/ \_\_| | |_| |_|\___/ \__,_|| |
;;; \_\ /_/
;;
;; «SBGAT-nod» (to ".SBGAT-nod")
;; «esvg-xy-sbgat-nod» (to ".esvg-xy-sbgat-nod")
;; This is a low-level way to create a SBGAT ("with no defaults").
;; The `S', `B', `G', `A', and `T' parts all use the same values for
;; the coordinates (x0,y0) and (x1,y1) of the "inner viewBox"; the `S'
;; and `B' parts use the same `e'xtra space; the `width' and `height'
;; in pixels are only used by the `S' part...
;; A test:
;;
;; (setq o (esvg-xy-sbgat-nod 0 0 4 3 0 40 30 '() '() '(0) '(0) '(0)))
;; (find-2a nil '(find-epp o))
;;
(defun esvg-xy-sbgat-nod (x0 y0 x1 y1 e width height
opts-svg
opts-background
opts-grid
opts-axes
opts-ticks)
"`S'vg with viewBox + `B'ackround + `G'rid + `A'xes + `T'icks.
This is the version with `NO' `D'efaults."
(let* ((svg (apply 'esvg-xy-svg x0 y0 x1 y1 e width height opts-svg))
(bg (apply 'esvg-xy-rect x0 y0 x1 y1 e opts-background))
(grid (apply 'esvg-xy-grid x0 y0 x1 y1 opts-grid))
(axes (apply 'esvg-xy-axes x0 y0 x1 y1 opts-axes))
(ticks (apply 'esvg-xy-ticks x0 y0 x1 y1 opts-ticks)))
(esvg-append svg bg)
(esvg-append svg grid)
(esvg-append svg axes)
(esvg-append svg ticks)
svg))
;; «esvg-xy-sbgat-nod-test» (to ".esvg-xy-sbgat-nod-test")
'("This is a test block for `eepitch-svg'! Use <f9>s!"
* (load-esvg) ; (find-angg ".emacs" "load-esvg")
(defun f (o) (esvg-append svg-a o) (show-svg))
(setq svg (esvg-xy-sbgat-nod
0 0 5 4 0.5 nil 300
'(:fill "none" :stroke-width 0.1) ; for `esvg-xy-svg'
'(:fill "white" :stroke "none") ; for the background
'(0.15 :stroke "#cccccc" :stroke-width 0.015) ; for the grid
'(0.25 :stroke "black" :stroke-width 0.05) ; for the axes
'(0.15 :stroke "black" :stroke-width 0.02))) ; for the ticks
(setq svg-a svg)
(show-svg)
(f (esvg-polyline "1 1 2 1 3 2 4 2" :stroke "red"))
"--")
;;; ____ ____ ____ _ _____
;;; / ___|| __ ) / ___| / \|_ _|
;;; \___ \| _ \| | _ / _ \ | |
;;; ___) | |_) | |_| |/ ___ \| |
;;; |____/|____/ \____/_/ \_\_|
;;;
;; «SBGAT» (to ".SBGAT")
;; «esvg-xy-sbgat» (to ".esvg-xy-sbgat")
;; Like `esvg-xy-sbgat-nod', but with good defaults.
;;
(defun esvg-xy-sbgat (x0 y0 x1 y1 width height &rest rest)
(esvg-xy-sbgat-nod x0 y0 x1 y1 0.5 width height
`(:fill "none" :stroke-width 0.1 ,@rest) ; SVG
'(:fill "white" :stroke "none") ; background
'(0.10 :stroke "#cccccc" :stroke-width 0.015) ; grid
'(0.25 :stroke "black" :stroke-width 0.05) ; axes
'(0.10 :stroke "black" :stroke-width 0.03) ; ticks
))
(defalias 'esvg-sbgat 'esvg-xy-sbgat)
;; «SBGAT-test-1» (to ".SBGAT-test-1")
;; See: https://svgwg.org/svg2-draft/paths.html#PathData
;; https://svgwg.org/svg2-draft/paths.html#PathDataCubicBezierCommands
;;
'("This is a test block for `eepitch-svg'! Use <f9>s!"
* (load-esvg) ; (find-angg ".emacs" "load-esvg")
(defun d (&rest r) (show-svg)) ; display
(defun st (o) (setq svg o)) ; set top-level svg
(defun sa (o) (setq svg-a o)) ; set where appends happen
(defun sta (o) (setq svg o svg-a o)) ; set top-level and append
(defun ad (o) (d (esvg-append svg-a o))) ; append and display
(d (sta (esvg-xy-sbgat 0 0 5 4 nil 500)))
(ad (esvg-polyline "1 1 2 1 3 2 4 2" :stroke "red"))
(ad (esvg-path "M 1 3 C 2 3 2 1 3 1" :stroke "orange"))
"--")
;; «esvg-xy-circles» (to ".esvg-xy-circles")
;; See: https://anggtwu.net/2026-eepitch-svg.html#pict2e
;; https://anggtwu.net/IMAGES/2026-eepitch-svg-xy-2.png
;; Tests: (find-epp (esvg-xy-circles '((0 1) (2 3) (4 5)) 9))
;; (to "SBGAT-test-2")
(defun esvg-xy-circles (points radius &rest rest)
(let ((colors '("red" "orange" "yellow" "green" "blue" "violet")))
(cl-letf* ((svg-a (apply 'esvg-g rest))
((symbol-function 'f) (lambda (o) (esvg-append svg-a o))))
(cl-loop for (cx cy) in points
for i from 0
do (f (esvg-circle cx cy radius
:fill (nth i colors))))
svg-a)))
;; «esvg-xy-dots» (to ".esvg-xy-dots")
;; Based on: https://anggtwu.net/eev-maxima.html#set-comprehensions
;; https://anggtwu.net/IMAGES/2025-maxima-set-comprehensions.png
;; Tests: (find-epp (esvg-xy-dots '((0 1) (2 3) (4 5)) 0.6 :fill "blue"))
(defun esvg-xy-dots (points radius &rest rest)
(cl-letf* ((svg-a (apply 'esvg-g rest))
((symbol-function 'f) (lambda (o) (esvg-append svg-a o))))
(cl-loop for (cx cy) in points
do (f (esvg-circle cx cy radius)))
svg-a))
;; «SBGAT-test-2» (to ".SBGAT-test-2")
'("This is a test block for `eepitch-svg'! Use <f9>s!"
* (load-esvg) ; (find-angg ".emacs" "load-esvg")
(defun d (&rest r) (show-svg)) ; display
(defun st (o) (setq svg o)) ; set top-level svg
(defun sa (o) (setq svg-a o)) ; set where appends happen
(defun sta (o) (setq svg o svg-a o)) ; set top-level and append
(defun a (o) (esvg-append svg-a o)) ; append
(defun ad (o) (a o) (d)) ; append and display
(defun asad (o) (a o) (sa o) (d)) ; append, set append, display
;; First drawing: two polylines, drawn with paths.
;; See: https://svgwg.org/svg2-draft/paths.html#PathData
(d (sta (esvg-xy-sbgat 0 0 5 4 nil 500)))
(asad (esvg-g :stroke "orange"))
(ad (esvg-path "M 1 2 L 2 2 M 3 1 L 4 1")) ; use absolute movement
(ad (esvg-path "M 1 1 l 1 0 l 1 1 l 1 0")) ; use relative movement
;; Second drawing: circles.
(setq pts '((1 1) (1 2) (2 3) (3 3)))
(d (sta (esvg-xy-sbgat 0 0 5 4 nil 500)))
(ad (esvg-xy-circles pts 0.15 :stroke "none" :id "circles"))
;; Third drawing: a cubic bezier curve and its control points.
;; When we modify the control points everything changes.
(defun p (n) (nth n pts))
(defun drawp () (ad (esvg-path `(M ,(p 0) C ,(p 1) ,(p 2) ,(p 3)) :id "p")))
(defun drawcs () (ad (esvg-xy-circles pts 0.15 :stroke "none" :id "circles")))
(defun spdraw (newpts) (setq pts newpts) (drawp) (drawcs))
(d (sta (esvg-xy-sbgat 0 0 5 4 nil 500)))
(asad (esvg-g :stroke "gray"))
(spdraw '((1 1) (1 2) (2 3) (3 3)))
(spdraw '((1 1) (1 2) (2 3.5) (3 3)))
(spdraw '((1 1) (1 2) (2 3) (3 3)))
(spdraw '((1 1) (0.5 2) (2 3) (3 3)))
(spdraw '((1 1) (1 2) (2 3) (3 3)))
"--")
;; «tricks-with-g-test-1» (to ".tricks-with-g-test-1")
;; In this test I draw some objects inside a named group, and then I
;; use `use' to draw copies of that group in other places of the plane.
;; Its "Second test" use several functions defined here:
;; (find-esvg "esvg-build.el" "tricks-with-g")
;;
'("This is a test block for `eepitch-svg'! Use <f9>s!"
* (load-esvg) ; (find-angg ".emacs" "load-esvg")
(defun d (&rest r) (show-svg)) ; display
(defun st (o) (setq svg o)) ; set top-level svg
(defun sa (o) (setq svg-a o)) ; set where appends happen
(defun sta (o) (setq svg o svg-a o)) ; set top-level and append
(defun a (o) (esvg-append svg-a o)) ; append
(defun ad (o) (a o) (d)) ; append and display
(defun asad (o) (a o) (sa o) (d)) ; append, set append, display
;; First test, with low-level functions.
;; Here the object "grp1" is built by appending subobjects.
;;
(d (sta (esvg-xy-sbgat -1 -1 5 4 nil 500)))
(asad (esvg-g :id "grp1"))
(ad (esvg-rectangle 0 0 1 1 :fill "gray"))
(ad (esvg-circle 0 0 0.25 :fill "red"))
(ad (esvg-circle 1 0 0.25 :fill "orange"))
(d (sa svg))
(ad (esvg-circle 1 1 0.25 :fill "green")) ; this is outside grp1
(ad (esvg-use "grp1" :id "use1" :x 2 :y 1)) ; a shifted copy of grp1
(ad (esvg-use "grp1" :id "use1" :x 3 :y 1)) ; move it around
(ad (esvg-use "grp1" :id "use1" :x 3 :y 2))
(ad (esvg-use "grp1" :id "use1" :x 2 :y 2))
(ad (esvg-use "grp1" :id "use1" :x 2 :y 1))
;; Second test.
;; Here the object "grp1" is built at once, and:
;; :id "grp1" --> (esvg-id "grp1" ...)
;; :id "use1" --> (esvg-id "use1" ...)
;; :x 2 :y 1 --> (esvg-translate 2 1 ...)
;;
(d (sta (esvg-xy-sbgat -1 -1 5 4 nil 500)))
(ad (esvg-id "grp1"
(esvg-rectangle 0 0 1 1 :fill "gray")
(esvg-circle 0 0 0.25 :fill "red")
(esvg-circle 1 0 0.25 :fill "orange")))
(ad (esvg-circle 1 1 0.25 :fill "green")) ; this is outside grp1
(setq grp1 (esvg-use "grp1"))
(ad (esvg-id "use1" (esvg-translate 2 1 grp1))) ; a shifted copy of grp1
(ad (esvg-id "use1" (esvg-translate 3 1 grp1))) ; move it around
(ad (esvg-id "use1" (esvg-translate 3 2 grp1)))
(ad (esvg-id "use1" (esvg-translate 2 2 grp1)))
(ad (esvg-id "use1" (esvg-translate 2 1 grp1)))
"--")
;; «tricks-with-g-test-2» (to ".tricks-with-g-test-2")
;; Sometimes I need to define objects and then draw copies of them,
;; maybe applying different translations, scalings, rotations, etc, to
;; each copy. The most obvious way to do that is to use a `transform'
;; with several transformation functions in its argument, like this:
;;
;; transform="scale(1,-1) translate(0,-400)"
;;
;; ...but I saw that my code becomes much simpler if I use nested
;; `g's and the "tricks with `g'" that are defined here:
;;
;; (find-esvg "esvg-build.el" "tricks-with-g")
;;
;; Note that
;;
;; (esvg-scale 1 1 ...)
;; (esvg-translate 0 0 ...)
;;
;; are no-ops.
;;
'("This is a test block for `eepitch-svg'! Use <f9>s!"
* (load-esvg) ; (find-angg ".emacs" "load-esvg")
(defun d (&rest r) (show-svg)) ; display
(defun st (o) (setq svg o)) ; set top-level svg
(defun sa (o) (setq svg-a o)) ; set where appends happen
(defun sta (o) (setq svg o svg-a o)) ; set top-level and append
(defun a (o) (esvg-append svg-a o)) ; append
(defun ad (o) (a o) (d)) ; append and display
(defun asad (o) (a o) (sa o) (d)) ; append, set append, display
(d (sta (esvg-xy-sbgat -1 -1 5 4 nil 500)))
(ad (esvg-defs
(esvg-id "grp1"
(esvg-rectangle 0 0 1 1 :fill "gray")
(esvg-circle 0 0 0.25 :fill "red")
(esvg-circle 1 0 0.25 :fill "orange"))))
(setq grp1 (esvg-use "grp1"))
(ad (esvg-id "obj1" (esvg-scale 1 1 grp1)))
(ad (esvg-id "obj1" (esvg-scale 1 0.5 grp1)))
(ad (esvg-id "obj1" (esvg-scale 1 -0.5 grp1)))
(ad (esvg-id "obj1" (esvg-scale 1 -1 grp1)))
(ad (esvg-id "obj1" grp1))
(ad (esvg-id "obj1" (esvg-translate 1 0 grp1)))
(ad (esvg-id "obj1" (esvg-translate 1 0 (esvg-scale 1 -1 grp1))))
(ad (esvg-id "obj1" (esvg-translate 1 1 (esvg-scale 1 -1 grp1))))
(ad (esvg-id "obj1" (esvg-translate 0 1 (esvg-scale 1 -1 grp1))))
(ad (esvg-id "obj1" (esvg-scale 1 -1 grp1)))
(ad (esvg-id "obj1" grp1))
"--")
;; «comprehension-test-1» (to ".comprehension-test-1")
;; Based on: (find-myqdraw "myqdraw3.mac" "set-comprehensions")
;; https://anggtwu.net/eev-maxima.html#set-comprehensions
;;
'("This is a test block for `eepitch-svg'! Use <f9>s!"
* (load-esvg) ; (find-angg ".emacs" "load-esvg")
(defun d (&rest r) (show-svg)) ; display
(defun st (o) (setq svg o)) ; set top-level svg
(defun sa (o) (setq svg-a o)) ; set where appends happen
(defun sta (o) (setq svg o svg-a o)) ; set top-level and append
(defun a (o) (esvg-append svg-a o)) ; append
(defun ad (o) (a o) (d)) ; append and display
(defun asad (o) (a o) (sa o) (d)) ; append, set append, display
(d (sta (esvg-xy-sbgat -4 -4 4 4 nil 550)))
(defun dots-square (step)
(cl-loop for y downfrom 4 to -4 by step
append (cl-loop for x from -4 to 4 by step
collect (list x y))))
(defun dot-yes (x y) (>= y (* x x))) ; "inside" the parabola y=x^2
(defun dots-yes (points)
(cl-loop for (x y) in points
if (dot-yes x y)
collect (list x y)))
(defun dots (step &optional radius)
(setq radius (or radius (* step 0.375)))
(esvg-xy-dots (dots-yes (dots-square step))
radius :id "dots" :fill "blue"))
;; Tests that return interesting things
;; but that don't draw anything
(dots-square 8)
(dots-square 4)
(dots-yes (dots-square 4))
(dots-yes (dots-square 2))
(dots 2)
;; Tests that draw dots inside a parabola
(ad (dots 2))
(ad (dots 1))
(ad (dots 0.5))
(ad (dots 0.25))
(ad (dots 0.125))
(ad (dots 0.25))
(ad (dots 0.5))
(ad (dots 1))
(ad (dots 2))
(ad (dots 4))
(ad (dots 0.125))
"--")
(provide 'esvg-xy)
;; Local Variables:
;; coding: utf-8-unix
;; End: