#!/home/arne/wisp/wisp-multiline.sh
; !#
; A world projected on a d20 (20-sided die, ikosaeder)
; For this we need a vector with 20 elements, a vector which shows the
; neighboring elements and accessor functions which give us the
; relevant elements for any set of longitude and latitude as well as
; its inverse (element-id to lon+lat). For further subdivisions, just
; elevate the center of each edge and connect these centers.
; Advection: Give each field a wind direction: target fields with an
; advection fraction: The fraction of the value which will be
; transported into the other field. Basic system: Follow the numbers.
define-module : examples d20world
. #:export : world neighbors d20-as-text d20-diffuse
use-modules : ice-9 format
define world : make-vector 20 0
define neighbors : make-vector 20
; count from the top
; Contains the numbers instead of the indexes, to make it easier for
; me to think about them.
;
; 7 8
; 3 4
; 1
; 6 2 9
; 5 10
;
; 14 13
; 18 17
; 20
; 15 19 12
; 16 11
;
define neighbors-helper
' : 1 2 3 4
2 1 5 10
3 1 6 7
4 1 8 9
5 2 6 14
6 3 5 15
7 3 8 16
8 4 7 11
9 4 10 12
10 1 9 13
20 19 18 17
19 20 16 11
18 20 15 14
17 20 13 12
16 19 17 7
15 18 16 6
14 18 13 5
13 17 14 10
12 17 11 9
11 19 12 8
let loop : : relationships neighbors-helper
cond
: null? relationships
. neighbors
else
let*
: cur : car relationships
idx : 1- : car cur
vec : cdr cur
vector-set! world idx : 1+ idx
vector-set! neighbors idx : make-vector 3
let setidx : : idxtoset '(0 1 2)
cond
: null? idxtoset
; the outer loop continues here
loop : cdr relationships
else
vector-set!
vector-ref neighbors idx
car idxtoset
1- : list-ref vec : car idxtoset
setidx : cdr idxtoset
define advection-directions
make-vector 20
let loop : : index 20
cond
: = 0 index
. advection-directions
: = 20 index
vector-set! advection-directions (1- index) (1- index)
loop : 1- index
else
vector-set! advection-directions (1- index) index
loop : 1- index
define : d20-value-ascii-color-string letter value
. "Create an ascii color string for d20."
let
: csi "["
color : inexact->exact : max 17 : min 230 : floor : * 12 value
format #f "~A38;5;~dm~A~Am" csi color letter csi
define : d20-value-ascii-color-string-show-values letter value
. "Create an ascii color string for d20."
let
: csi "["
color : inexact->exact : max 17 : min 230 : floor : * 12 value
int : inexact->exact : floor : * 12 value
format #f "~A38;5;~dm~A~Am" csi color int csi
define : d20-as-text-base world-vector function
. "show the given d20 world as text"
let
: template "
~A ~A
~A ~A
~A
~A ~A ~A
~A ~A
~A ~A
~A ~A
~A
~A ~A ~A
~A ~A
"
indexes ' : 7 8 3 4 1 6 2 9 5 10 14 13 18 17 20 15 19 12 16 11
apply format : append (list #f template) : map function indexes : map (lambda (x) (vector-ref world (1- x))) indexes
define : d20-as-text world-vector
. "show the given d20 world as text"
d20-as-text-base world-vector d20-value-ascii-color-string-show-values
define : d20-cursor-up-text world-vector
. "Kill each line of the text of the world vector in a terminal."
let*
: text : d20-as-text-base world-vector d20-value-ascii-color-string-show-values
lines : string-split text #\newline
format #t "[~AA" : 1- : length lines
define : d20-diffuse world neighbors D
. "Diffuse the values on the d20 using the diffusion constant D. Step 1: Simply iterative."
let leapfrog : : targets '(0 1 2)
if : null? targets
. world
let loop : : neighbors-to-diffuse : iota : vector-length neighbors
cond
: null? neighbors-to-diffuse
leapfrog : cdr targets
else
let*
: originidx : car neighbors-to-diffuse ; index in world and in neighbors
targetleap : car targets
targetidx : vector-ref (vector-ref neighbors originidx) targetleap
originval : vector-ref world originidx
targetval : vector-ref world targetidx
diff : * (/ D 3) : - targetval originval
vector-set! world originidx : + originval diff
vector-set! world targetidx : - targetval diff
loop : cdr neighbors-to-diffuse
define : d20-advect world advection-directions A
. "Advect the values on the d20 using the advection constant A."
let loop : : neighbors-to-advect : iota : vector-length advection-directions
cond
: null? neighbors-to-advect
. world
else
let*
: source : car neighbors-to-advect
target : vector-ref advection-directions source
source-value : vector-ref world source
target-value : vector-ref world target
change : * A source-value
source-new : - source-value change
target-new : + target-value change
; format #t "target: ~A, source: ~A, change: ~A\n" target source change
when : not : = source target
vector-set! world source source-new
vector-set! world target target-new
loop : cdr neighbors-to-advect
define φ : * (/ 1 2) : 1+ : sqrt 5
define : latlon2cellidx lat lon
. "Convert a position given as latitude and longitude into the correct cell index."
; cell 1 (index 0) is on top, cell 20 at the bottom. The right
; border of cell 2 is situated at longitude 0. With that, the
; left corner of cell 19 is at longitude 180. Top and bottom
; are point-symmetric. We can cleanly divide the upper part of
; the icosaeder into 3 regions by longitude. Let's do that.
let*
: upper : > lat 0
; we start by switching to a symmetric longitude
slon : if upper lon : + lon 180
; the sector number is defined by the uppermost triangle
; in it.
sector : if (< slon 120) 4 (if (< slon 270) 3 2)
; we start by calculating the fraction inside the sector
lonsectorfraction : modulo slon 120
; we can further subdivide the sector by longitude into two subsectors
subseclon : if (< lon 60) lon (-120 lon)
; TODO find some more symmetry or start nontrivial geometry.
. #t
display : d20-as-text world
newline
format #t "Diffuse ~A\n" 0.01
d20-diffuse world neighbors 0.01
display : d20-as-text world
newline
format #t "Advect ~A\n" 0.1
d20-advect world advection-directions 0.1
display : d20-as-text world
newline
format #t "Diffuse ~A\n" 0.1
d20-diffuse world neighbors 0.1
display : d20-as-text world
newline
format #t "Diffuse: ~A*(~A)\n" 100 0.1
let loop : : steps 100
cond
: = 0 steps
. world
else
d20-diffuse world neighbors 0.1
loop : 1- steps
display : d20-as-text world
newline
let
: number 20
val 1
format #t "disturb: ~A to ~A\n" number val
vector-set! world (1- number) val
display : d20-as-text world
newline
format #t "Diffuse ~A\n" 0.1
d20-diffuse world neighbors 0.1
display : d20-as-text world
newline
format #t "Advect: ~A*(~A)\n" 1000 0.001
let loop : : steps 1000
cond
: = 0 steps
. world
else
d20-advect world advection-directions 0.001
display : d20-as-text world
d20-cursor-up-text world
loop : 1- steps
display : d20-as-text world
newline
format #t "Diffuse: ~A*(~A)\n" 1000 0.004
let loop : : steps 1000
cond
: = 0 steps
. world
else
d20-diffuse world neighbors 0.004
display : d20-as-text world
d20-cursor-up-text world
loop : 1- steps
display : d20-as-text world
newline
format #t "Diffuse+Advect: ~A*(~A+~A)\n" 1000 0.002 0.001
let loop : : steps 1000
cond
: = 0 steps
. world
else
d20-diffuse world neighbors 0.002
d20-advect world advection-directions 0.001
display : d20-as-text world
d20-cursor-up-text world
loop : 1- steps
display : d20-as-text world
newline