groworld/sketcher/sketch-lsys.scm
2009-05-01 21:34:29 +01:00

202 lines
7.5 KiB
Scheme

;------------------------------------------------------
; lsystem sketching prototype
; ---------------------------
;
; an idea for putting lsystems under human control,
; and making them sketchable.
; not sure how this would be best extended to 3D.
; [I need to work on the mouse stuff in fluxus]
;
; instructions:
; * draw from the centre by dragging with left mouse
; * repeat to make a new plant/drawing
;
; don't make the drawing too long, it'll make your computer cry
(require "ls.ss")
; gets a line representing a segment of the projection of the mouse into 3D space
; should move this into the fluxus scheme library
(define (get-line-from-mouse)
(let* ((ndcpos (vector (* (- (/ (mouse-x) (vx (get-screen-size))) 0.5) 2)
(* (- (- (/ (mouse-y) (vy (get-screen-size))) 0.5)) 1.5) -1))
(scrpos2 (vtransform (vmul ndcpos 50) (minverse (get-camera-transform))))
(scrpos (vtransform ndcpos (minverse (get-camera-transform)))))
(list scrpos scrpos2)))
; we'll just use the end of the projection line here
(define (mouse-pos)
(cadr (get-line-from-mouse)))
; converts a 2D vector into an angle, with some dodgy dave maths
(define (2dvec->angle x y)
(let ((q (/ 3.141 2)))
(when (zero? y) (set! y 0.0001))
(cond
((>= y 0)
(fmod (* (+ q q q (- q (atan (/ x y)))) 57.2957795) 360))
(else
(fmod (* (+ q (- q (atan (/ x y)))) 57.2957795) 360)))))
;-----------------------------------------------------
; builds objects from a string
; would be good to abstract this asap
(define (ls-build string angle branch-scale branch-col leaf-col)
(hint-depth-sort)
(for-each
(lambda (char)
(cond
((char=? #\F char)
(with-state
(translate (vector 1 0 0))
(translate (vmul (crndvec) 0.01))
(scale (vector 1.2 2 2))
(rotate (vector 0 90 0))
(colour (vector 0.5 1 0.2))
(with-primitive (build-ribbon 2)
; (texture (load-texture "textures/fade4.png"))
; (hint-unlit)
(pdata-set! "w" 0 0.1)
(pdata-set! "w" 1 0.1)
(pdata-set! "p" 0 (vector 0 0 1))
(pdata-set! "p" 1 (vector 0 0 0))))
(translate (vector 1 0 0)))
((char=? #\L char)
#; (with-state
(translate (vector 1 0 0))
(scale (vector 2 1 1))
; (rotate (vector 0 90 0))
(colour leaf-col)
(texture (load-texture "../textures/leaf.png"))
(build-plane))
(translate (vector 1 0 0)))
((char=? #\f char)
(translate (vector 1 0 0)))
((char=? #\/ char)
(rotate (vector angle 0 0)))
((char=? #\\ char)
(rotate (vector (- angle) 0 0)))
((char=? #\+ char)
(rotate (vector 0 angle 0)))
((char=? #\- char)
(rotate (vector 0 (- angle) 0)))
((char=? #\^ char)
(rotate (vector 0 0 angle)))
((char=? #\& char)
(rotate (vector 0 0 (- angle))))
((char=? #\| char)
(rotate (vector 0 0 180)))
((char=? #\[ char)
(push)
(scale (vector branch-scale branch-scale branch-scale)))
((char=? #\] char)
(pop))))
(string->list string)))
;------------------------------------------------------
; strokes are collections of points representing mouse movement
(define-struct stroke ((points #:mutable)))
(define (build-stroke)
(make-stroke (list (vector 0 0 -40)))) ; start with a point in the middle of the screen
(define (stroke-clear stroke)
(set-stroke-points! stroke (list (vector 0 0 -40))))
(define (stroke-add stroke pos)
(set-stroke-points! stroke (cons pos (stroke-points stroke))))
(define (stroke-last-point stroke)
(car (stroke-points stroke)))
(define (stroke-update stroke)
; make a new point when the mouse is suitibly far from the last point
(when (> (vdist (stroke-last-point stroke) (mouse-pos)) 2)
(stroke-add stroke (mouse-pos))))
; draw some blobs to indicate the path drawn
(define (stroke-render stroke)
(for-each
(lambda (pos)
(with-state
(opacity 0.7)
(translate pos)
(hint-unlit)
(colour (vector 1 1 0))
(draw-sphere)))
(stroke-points stroke)))
; converts a stroke into the corresponding lsystem string,
; with some branchpoints to recurse the drawing - would be
; better to get the branchpoints from the drawing somehow...
(define (stroke->string stroke angle)
(define (collect pos next-pos last-angle str c)
(cond ((null? next-pos) str)
(else
(let* ((v (vsub (car pos) (car next-pos)))
(a (2dvec->angle (vx v) (vy v))) ; get the absolute angle
(ra (- a last-angle)) ; get angle relative to the last angle
; make a string which represents this turn
(new-str (if (> ra 0) ; which way are we turning?
(make-string (inexact->exact (round (/ ra angle))) #\-)
(make-string (inexact->exact (round (abs (/ ra angle)))) #\+)))
(out (if (zero? (modulo c 10))
(string-append str new-str "F") ; add branch
(string-append str new-str "F")))) ; normal
(collect (cdr pos) (cdr next-pos) a out (+ c 1))))))
(collect
(reverse (stroke-points stroke))
(cdr (reverse (stroke-points stroke))) 0 "" 0))
;------------------------------------------------------
; a fluxus mouse pointer!
(define (draw-mouse)
(with-state
(translate (mouse-pos))
(hint-unlit)
(colour (vector 1 0.4 0.3))
(draw-sphere)))
(define stroke (build-stroke))
(define root (build-locator))
(define debounce #t)
(define (animate)
(draw-mouse)
(when (mouse-button 1) (stroke-update stroke))
(stroke-render stroke)
(when (and (not debounce) (not (mouse-button 1)))
(display (stroke->string stroke 45))(newline)
(with-state
(parent root)
; (translate (vector 0 -30 -40))
(scale 0.75)
(rotate (vector 90 90 0))
(ls-build
(lsystem-generate 4 "A" (list (list "A" "B[----AB][++++AB]")
(list "B" (stroke->string stroke 10))))
10 0.75 (vector 1 1 1) (vector 1 1 1)))
(set! debounce #t))
(when (and debounce (mouse-button 1))
(set! debounce #f)
(stroke-clear stroke)
(destroy root)
(set! root (build-locator))))
(clear)
(clear-colour (vector 0.2 0.4 0.3))
(set-camera-transform (mtranslate (vector 0 0 -10)))
(light-diffuse 0 (vector 0 0 0))
(define l (make-light 'point 'free))
(light-diffuse l (vector 1 1 1))
(light-position l (vector -50 50 0))
(every-frame (animate))