-
Notifications
You must be signed in to change notification settings - Fork 10
/
sketch.lisp
115 lines (99 loc) · 3.21 KB
/
sketch.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
(defpackage :nature-of-code.forces.exercise-7
(:export :start-sketch)
(:use :cl :trivial-gamekit)
(:import-from :cl-bodge :vector-length :normalize))
(in-package :nature-of-code.forces.exercise-7)
(defvar *width* 600)
(defvar *height* 400)
(defvar *black* (vec4 0 0 0 1))
(defvar *gray* (vec4 0.5 0.5 0.5 0.4))
(defun constrain (value min max)
(if (> value max)
max
(if (< value min)
min
value)))
(defclass mover ()
((location
:accessor location
:initarg :location)
(velocity
:initform (vec2 0 0)
:accessor velocity)
(acceleration
:initform (vec2 1 0)
:accessor acceleration)
(radius
:accessor radius)
(mass
:initform 2
:accessor mass
:initarg :mass)))
(defmethod initialize-instance :after ((mover mover) &key)
;; Make the radius dependent on the mass of the mover.
(setf (radius mover) (* 4 (mass mover)))
(setf (location mover) (vec2 (random *width*) (random *height*))))
(defmethod apply-force ((mover mover) force)
(let ((f (div force (mass mover))))
(setf (acceleration mover) (add (acceleration mover) f))))
(defmethod update ((mover mover))
(let* ((a (acceleration mover))
(v (add (velocity mover) a)))
(setf (velocity mover) v)
(setf (location mover) (add v (location mover)))
(setf (acceleration mover) (vec2 0 0))))
(defmethod display ((mover mover))
(draw-circle (location mover) (radius mover)
:fill-paint *gray*
:stroke-paint *black*
:thickness 2))
(defclass attractor ()
((location
:accessor location
:initform (vec2 (/ *width* 2) (/ *height* 2))
:initarg :location)
(mass
:accessor mass
:initform 30)
(g
:accessor g
:initform 0.4)))
(defmethod display ((attractor attractor))) ; Invisible
(defmethod attract ((attractor attractor) (mover mover))
(let* ((force (subt (location attractor) (location mover)))
;; Constrain the distance to prevent large values when the mover gets
;; really close.
(distance (constrain (vector-length force) 5 25))
;; F = G (m1 * m2) / r^2
(strength (/
(* (g attractor) (mass attractor) (mass mover))
(expt distance 2))))
(mult (normalize force) strength)))
(defgame sketch ()
((mover
:accessor movers
:initform (loop repeat 50 collect (make-instance 'mover)))
(attractors
:accessor attractors))
(:viewport-width *width*)
(:viewport-height *height*)
(:viewport-title "Multiple attractors"))
(defmethod post-initialize ((this sketch))
(setf (attractors this)
(list
(make-instance 'attractor
:location (vec2 (* *width* (/ 1 3)) (/ *height* 2)))
(make-instance 'attractor
:location (vec2 (* *width* (/ 2 3)) (/ *height* 2))))))
(defmethod draw ((this sketch))
(mapc #'display (movers this))
(mapc #'display (attractors this)))
(defmethod act ((this sketch))
(with-accessors ((movers movers) (attractors attractors)) this
(dolist (mover movers)
(progn
(dolist (attractor attractors)
(apply-force mover (attract attractor mover)))
(update mover)))))
(defun start-sketch ()
(start 'sketch))