One of the main objectives of the weavecoding project is to provide a simulation of the warp weighted loom to use for demonstrations and exploration of ancient weaving techniques. Beyond the 4 shaft loom dyadic calculator we need to show the actual process of weaving to explain how the structures and patterns emerge. Weaving is very much a 3D process and these visualisations fail to show that well. It also needs to be able to be driven by the flotsam tangible livecoding hardware so running on a Raspberry Pi is another requirement.
I've decided to make use of the Jellyfish procedural renderer to build something fast and flexible enough, while remaining cross platform. Jellyfish is a lisp-like language which compiles to a vector processing virtual machine written in C++, and approaches speeds of native code (with no garbage collection) while remaining very creative to work with, similar to fluxus livecoding. Previously I've only used it for small experiments rather than production like this, so I've needed to tighten up the compiler quite a bit. One of the areas which needed work (along with function arguments which were coming out backwards!) were the conditional statements, which I removed and replaced with a single if. Here is the compiler code at the lowest level which emits all the instructions required:
;; compiler code to output a list of instructions for (if pred true-expr false-expr) (define (emit-if x) (let ((tblock (emit-expr (caddr x))) ;; compile true expression to a block (fblock (emit-expr (cadddr x)))) ;; compile false expression to block (append (emit-expr (cadr x)) ;; predicate - returns true or false (emit (vector jmz (+ (length tblock) 2) 0)) ;; if false skip true block tblock (emit (vector jmr (+ (length fblock) 1) 0)) ;; skip false block fblock)))
Then I can implement cond (which is a list of different options to check rather than one) as a purely syntactic form with a pre-processor function to create a series of nested ifs before compiling them:
;; preprocessor to take a cond list and convert to nested ifs (define (preprocess-cond-to-if x) (define (_ l) (cond ((null? l) 0) ;; a cond without an else returns 0 ((eq? (caar l) 'else) ;; check for else clause to do (cons 'do (pre-process (cdr (car l))))) (else (list 'if (pre-process (caar l)) ;; build an if (cons 'do (pre-process (cdr (car l)))) (_ (cdr l)))))) ;; keep going (_ (cdr x))) ;; ignores the 'cond'
Here's an example of the if in use in the loom simulation at the 'top' level - it gets the current weaving draft value for the weft and warp thread position and uses it to move the weft polygons forward or back (in the z) a tiny amount to show up on the correct side of the warp.
(define calc-weft-z (lambda () (set! weft-count (+ weft-count 1)) (set! weft-z (if (> (read-draft) 0.5) (vector 0 0 0.01) (vector 0 0 -0.01)))))
One of the reasons I'm writing about all these levels of representation is that they feel close to the multiple representations present in weaving from draft to heddle layout, lift plan, fabric structure and resulting pattern.