;#lang scheme
;(require fluxus-016/drflux)

; extrusion code

(define (draw-profile index profile offset)
    (cond ((not (null? profile))
            (pdata-set! "p" index (vadd (car profile) offset))
            (draw-profile (+ index 1) (cdr profile) offset))))


(define (transform-profile profile m)
    (cond
        ((null? profile) '())
        (else
            (cons (vtransform (car profile) m) 
                (transform-profile (cdr profile) m)))))

; figures out the vector for rotation of the profile
(define (path-vector first-segment path lv)
    (let* ((v (if (null? (cdr path))        ; last segment?
                    lv                      ; use the last vector used
                    (vsub (cadr path) (car path)))) ; use the next point
            (vd (if first-segment v          ; first segment?
                    (vadd (vmul lv 0.5) ; blend with the last vector
                        (vmul v 0.5)))))
        vd))

(define (extrude-segment index profile path width lv up size)
    (cond ((not (null? path))
            (let ((v (path-vector (zero? index) path lv)))                
                (draw-profile index (transform-profile profile 
                        (mmul
                            (maim v up)
                            (mrotate (vector 0 90 0))
                            (mscale (vmul (vector (car width) (car width) (car width)) size))))
                    (car path))
                v))))

(define (extrude index profile path width lv up)
    (cond ((not (null? path))
            (let ((v (extrude-segment index profile path width lv up 1)))
                (extrude (+ index (length profile)) profile (cdr path) (cdr width) v up)))))

(define (stitch-face index count profile-size in)
    (cond 
        ((eq? 1 count) 
            (append in (list (+ (- index profile-size) 1) index (+ index profile-size) 
                    (+ (- index profile-size) 1 profile-size))))
        (else
            (append 
                (list (+ index 1) index
                    (+ index profile-size) (+ index profile-size 1))
                (stitch-face (+ index 1) (- count 1) profile-size in)))))

(define (stitch-indices index profile-size path-size in)
    (cond 
        ((eq? 1 path-size) in)
        (else
            (append
                (stitch-face index profile-size profile-size '())
                (stitch-indices (+ index profile-size) 
                    profile-size
                    (- path-size 1)
                    in)))))

(define (build-tex-coords profile-size path-size vscale)
    (pdata-index-map!
        (lambda (i t)
            (vector (* vscale (/ (quotient i profile-size) path-size))
                (/ (modulo i profile-size) profile-size) 0))
        "t"))

(define (build-extrusion profile path width tex-vscale up)
    (let ((p (build-polygons (* (length profile) (length path)) 'quad-list)))
        (with-primitive p
            (poly-set-index (stitch-indices 0 (length profile) (length path) '()))
            (build-tex-coords (length profile) (length path) tex-vscale)
            (extrude 0 profile path width (vector 0 0 0) up)
            (recalc-normals 0))
        p))

; partial extrusions are for animating

(define (build-partial-extrusion profile path tex-vscale)
    (let ((p (build-polygons (* (length profile) (length path)) 'quad-list)))
        (with-primitive p
            (poly-set-index (stitch-indices 0 (length profile) (length path) '()))
            (build-tex-coords (length profile) (length path) tex-vscale))
        p))

(define (partial-extrude t profile path width up grow)
    (define (chop-front l n)
        (cond ((null? l) l)
            (else
                (if (zero? n) (cons (car l) (chop-front (cdr l) n))
                    (chop-front (cdr l) (- n 1))))))
    
    (define (collapse-front)
        (let ((start (* (floor t) (length profile))))
            (for ((i (in-range (+ start (* (length profile) 1)) (pdata-size))))
                (pdata-set! "p" i (pdata-ref "p" start)))))
    
    (define (scale-front)
        (when (> t 1)
            (let* ((start (* (floor t) (length profile)))
                    (from (list-ref path (- (inexact->exact (floor t)) 1)))
                    (to (list-ref path (+ (inexact->exact (floor t)) 0))))
                
                (for ((i (in-range start (+ start (length profile)))))
                    (pdata-set! "p" i (vmix (pdata-ref "p" i)
                        (vmix to from (- t (floor t))) (- t (floor t))))))))
    

    (define (_ t v g)
        (cond 
            ((< t 1) (with-primitive p (recalc-normals 0)) v)
            (else
                (let ((start (* (floor t) (length profile))))
                    (_ (- t 1)
                        (extrude-segment start profile
                            (chop-front path (floor t))
                            (chop-front width (floor t)) v up 
                            (if (< g 1)
                                (+ g (* (- t (floor t)) grow))
                                g))
                        (if (< g 1)
                            (+ g grow)
                            1))))))
    (_ t (vector 0 0 0) 0)
    (scale-front)
    (collapse-front)
)

(define (build-circle-profile n r)
    (define (_ n c l)
        (cond ((zero? c) l)
            (else
                (let ((a (* (/ c n) (* 2 3.141))))                 
                    (_ n (- c 1) 
                        (cons (vmul (vector (sin a) (cos a) 0) r) l))))))
    (_ n n '()))

(clear)
(clear-colour 0.5)
(define profile (build-circle-profile 12 0.5))

(define width (build-list 100
        (lambda (n) (* n 0.01 (+ 1.5 (cos (* 0.5 n)))))))

(define path (build-list 100
        (lambda (n) (vmul (vector (sin (* 0.2 n)) 0 (cos (* 0.2 n))) (* 0.05 n)))))

(define p (with-state
        (wire-colour 0)
;        (colour (vector 0.5 0.3 0.2))   
        (colour (vector 1 1 1))   
        (specular (vector 1 1 1))
        (shinyness 20)
        (hint-wire)
    (texture (load-texture "textures/root.png"))
        (build-partial-extrusion profile path 10)))

#;(with-state
    (wire-opacity 0.4)
    (translate (vector 0 0 0))
    (wire-colour (vector 0 0 1))
    (hint-none)
    (hint-wire)
    ;    (hint-normal)
    (backfacecull 1)
    (point-width 5)
    (build-extrusion profile path width 1 (vector 0 1 0))) 

(define t 0)

(define (animate)
    (with-primitive p
        (partial-extrude 
            (* (* 0.5 (+ 1 (sin (* 0.2 t)))) (length path)) 
            profile path width (vector 0 1 0) 0.05)
        (set! t (+ t 0.01))))

(every-frame (animate))


(end-framedump)
;(start-framedump "ext-" "jpg")