-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathadvanced_template.html
195 lines (171 loc) · 7.29 KB
/
advanced_template.html
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<html>
<head>
<title>Template Model</title>
<script src="../lib/agentbase.js"></script>
<script src="../tools/coffee-script.js"></script>
<script type="text/coffeescript">
# AgentBase is Free Software, available under GPL v3 or any later version.
# Original AgentScript code @ 2013, 2014 Owen Densmore and RedfishGroup LLC.
# AgentBase (c) 2014, Wybo Wiersma.
# This is a slightly more complicated general template for
# creating models.
#
# To build your own model, extend class ABM.Model supplying the
# two built in methods `setup` and `step`.
#
# `@foo` signifies an instance variable or method.
# See [CoffeeScript](http://jashkenas.github.com/coffee-script/#classes)
# for explanation of context of a class and its constructor.
#
# We do not provide a constructor of our own.
#
# AgentBase automatically calls `Model.constructor <args>`
# and `setup` will be called by `Model.constructor`. See:
#
# model = new AdvancedTemplateModel
#
# below, which passes all its arguments to `Model`
u = ABM.util # shortcut for ABM.util, some tools
log = (arg) -> console.log arg # log to the browser console
class ABM.AdvancedTemplateModel extends ABM.Model
# `startup` initializes resources used by `setup` and `step`.
# This is called by the constructor which waits for all files
# processed by `starup`. Not needed by simple models.
#
startup: -> # called by constructor
u.shapes.add "bowtie", true, (context) ->
u.shapes.polygon context, [[-.5, -.5], [.5, .5],
[-.5, .5], [.5, -.5]]
# The following lines don't work when opened locally in
# Chrome (they work fine from a webserver). Uncomment if you'd
# like to add custom images.
# u.shapes.add "cc", true, u.importImage("images/coffee.png")
# Initialize our model via the `setup` method. This model simply
# creates a population of agents with arbitrary shapes with `size`
# size and `speed` velocity. We also periodically change the patch
# colors to random gray values.
#
setup: -> # called by Model.constructor
# First, we initialize our model instance variables.
# Most instance variables are parameters we would like
# an external UI to setup for us.
@population = 100
@size = 1.5 # size in patch coords
@speed = .5 # move forward this amount in patch coords
@wiggle = u.degreesToRadians(30) # degrees/radians to wiggle
@startCircle = true # initialize agents randomly or in circle
# Set the default agent size (conserves storage)
@agents.setDefault "size", @size
# Set the agent to convert shape to bitmap for better performance.
@agents.setUseSprites()
# Set animation to 30fps, without multiple steps per draw:
@animator.setRate 30, false
# The patch grid will have been setup for us. Here we initialize
# patch variables, either built-in ones or any new patch variables
# our model needs. In this case, we set the built-in color to a
# random gray value.
for patch in @patches.create()
patch.color = u.color.random "gray"
# Set x & y axes to a different color
if patch.position.x is 0
patch.color = u.color.yellow
if patch.position.y is 0
patch.color = u.color.red
# Our empty @agents Agents will have been created. Here we
# add `population` Agents we use in our model.
#
# We set the build-in Agent variables `size` and `shape`
# and layout the agents randomly or in a circle depending
# on our modeel's `startCircle` variable.
for agent in @agents.create @population
agent.shape = u.shapes.names().sample() # random shapes
if @startCircle
agent.forward @patches.max.x / 2 # start in circle
else
agent.moveTo @patches.randomPoint() # set random location
# Print the number of agents and patches to the console.
# Note CoffeeScript
# [string interpolation](http://jashkenas.github.com/coffee-script/#strings)
log "total agents: #{@agents.length}, patches: #{@patches.length}"
# Print number of agents with each shape
for shape in u.shapes.names()
num = @agents.with((agent) -> agent.shape == shape).length
log "#{num} #{shape}"
# Update, or run our model for one step, via the second built in
# method, `step`.
#
step: -> # called by Model.animate
# First, update our agents via `updateAgents` below
for agent in @agents
@updateAgents(agent)
# Every 100 steps, update our patches, print stats to
# the console, and use the Model refresh flag to redraw
# the patches. Otherwise don't refresh.
if @animator.ticks % 100 is 0
for patch in @patches
@updatePatches(patch)
@reportInfo()
@refreshPatches = true
# Add use of our first pull request:
@setSpotlight @agents.sample() if @animator.ticks is 300
@setSpotlight null if @animator.ticks is 600
else
@refreshPatches = false
log @animator.toString() if @animator.ticks % 100 is 0
# Stop the animation at 1000.
# Restart by `ABM.model.start()` in console.
if @animator.ticks is 1000
log "..stopping, restart by ABM.model.start()"
@stop()
# Three of our own methods to manage agents & patches
# and report model state.
#
updateAgents: (agent) -> # a is agent
# Have our agent "wiggle" by changing
# our heading by +/- `wiggle/2` radians
agent.rotate u.randomCentered @wiggle
# Then move forward by our speed.
agent.forward @speed
# Update patch colors to be a random gray.
#
updatePatches: (patch) -> # p is patch
if patch.position.x isnt 0 and patch.position.y isnt 0
patch.color = u.color.random "gray"
# Report the average heading, in radians and degrees
#
reportInfo: ->
headings = @agents.getProperty "heading"
avgHeading = (headings.reduce (a, b) -> a + b) / @agents.length
# Note: multiline strings. block strings also available.
log """
average heading of agents:
#{avgHeading.toFixed(2)} radians,
#{u.radiansToDegrees(avgHeading).toFixed(2)} degrees
"""
# Now that we've build our class, we call it with Model's
# constructor arguments:
#
# div: name of the div
# patchSize = 13: size (width & height) of each patch in pixels
# mapSize = 32: size (width & height) of the map in patches
# isTorus = false: map has torus topology
#
# Alternatively to mapSize you can set the map-size manually:
# min: {x: x-coordinaye, y: y-coordinate}
# max: {x: x-coordinaye, y: y-coordinate}
#
# Defaults 13 for patchSize and 32 for mapSize
#
model = new ABM.AdvancedTemplateModel {
div: "world",
patchSize: 13,
mapSize: 32
isTorus: true
}
model.start()
</script>
</head>
<body>
<div id="world"></div>
</body>
</html>