Rebol3 Code Examplex
Boids
Simulate flocking behavior using simple local interaction rules.
Rebol [
title: "Rosetta code: Boids"
file: %Boids.r3
url: https://rosettacode.org/wiki/Boids
note: "Translated from Red"
]
;=============================
; PARAMETERS
;=============================
W: 860
H: 160
N: 60 ; boids
random/seed now/time/precise ; randomize
; == FLOCKING BEHAVIOR ==
max-speed: 5.0
neighbor-radius: 80.0
separation-radius: 10.0
sep-weight: 1.8
ali-weight: 0.9
coh-weight: 0.15
;=============================
; VECTOR HELPERS (float)
;=============================
vmag: func [v][sqrt ((v/x * v/x) + (v/y * v/y))]
vnorm: func [v /local m][ m: vmag v either m > 0 [v * 1.0 / m][0x0] ]
vlimit: func [v m][ either m < vmag v [m * vnorm v][v] ]
vdist: func [a b][vmag a - b]
vdot: func [a b][(a/x * b/x) + (a/y * b/y)]
;=============================
; BOIDS
;=============================
boids: collect [
repeat i N [
keep make map! compose [
pos: (as-pair random 50 random H) ; started at left side
vel: (as-pair
random 1.0 ; heading to the right side
(1.0 - random 2.0) ; with some vertical variations
)
]
]
]
;== OBSTACLES DATA ===
obstacles: [
#[c: 160x20 r: 80]
#[c: 445x89 r: 30]
#[c: 700x140 r: 90]
]
;== SIMPLE OBSTACLE AVOIDANCE LOGIC ==
obstacle-avoidance: function [b][
force: 0x0
dir: vnorm b/vel
ahead: b/pos + (dir * (max-speed * 10))
foreach o obstacles [
d: vdist ahead o/c
if d < (o/r + 6.0) [
side: as-pair (negate dir/y) dir/x
to-c: o/c - b/pos
if ((to-c/x * side/x) + (to-c/y * side/y)) > 0 [
side: side * -1.0
]
force: force + side
]
]
if zero? vmag force [return 0x0]
1.8 * vnorm force ; instead of 2.5
]
resolve-obstacle-collision: function [b o][
dvec: b/pos - o/c
d: vmag dvec
if d < o/r [
; 1) push boid to surface
n: vnorm dvec
b/pos: o/c + (n * o/r)
; 2) remove inward velocity
vn: vdot b/vel n
if vn < 0.0 [
b/vel: b/vel - (n * vn)
]
]
]
;=============================
; UPDATE LOGIC
;=============================
flow: 0.05x0 ; right bias
update-boids: function [][
foreach b boids [
sep: ali: coh: 0x0
count: 0
foreach o boids [
if o <> b [
d: vdist b/pos o/pos
if d < neighbor-radius [
ali: ali + o/vel
coh: coh + o/pos
++ count
]
if d < separation-radius [
sep: sep + b/pos - o/pos
]
]
]
if count > 0 [
nc: 1.0 / count
ali: ali-weight * (vnorm ali * nc)
coh: coh-weight * (vnorm (coh * nc) - b/pos)
sep: sep-weight * (vnorm sep)
]
avoid: obstacle-avoidance b
acc: (((sep + ali) + coh) + avoid) + flow
b/vel: vlimit b/vel + acc max-speed
]
;== EDGE MECHANISM ==
foreach b boids [
b/pos: b/pos + b/vel
;== AVOID OBSTACLE COLLISION ==
foreach o obstacles [
resolve-obstacle-collision b o
]
;== BOUNDARY CHECK
if b/pos/x < 0 [b/pos/x: W]
if b/pos/x > W [b/pos/x: 0]
if b/pos/y < 20 [b/vel/y: b/vel/y + 1.9]
if b/pos/y > (H - 20) [b/vel/y: b/vel/y - 1.9]
]
]
;=============================
; DRAWING
;=============================
;; Prepare constant draw commands for the background.
background: [fill-all black fill-pen brown]
foreach o obstacles [
append background compose [circle (o/c) (o/r)]
]
;; Set the color for drawing boids.
append background [pen red]
draw-frame: function [][
blk: clear []
;== Prepare Background with obstacles
append blk background
;== Prepare Boids
update-boids
foreach b boids [
;; Using 3x append to avoid temporary block construction.
append append append blk
'line
b/pos
b/pos + b/vel
]
;== Draw the image.
draw img blk
]
;=============================
; START
;=============================
import blend2d
img: make image! as-pair W H
either function? :view [
;; Show animation when VIEW is available.
gob: make gob! [image: img]
win: view/no-wait gob
visible?: true
;; Register an event handler to detect closing the window.
handle-events [
name: 'view-boids
priority: 100
handler: func [event] [
if switch event/type [
close [true]
key [event/key = escape]
][
unhandle-events self
unview event/window
visible?: false
]
none
]
]
while [visible?][
draw-frame
show win
wait 0.01
]
][
;; Or simulate some number of frames...
loop 100 [draw-frame]
;; ...and save the image.
save %out.png img
]