-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathTurtles.js
181 lines (172 loc) · 6.85 KB
/
Turtles.js
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
import * as util from './utils.js'
// import AgentArray from './AgentArray.js'
import AgentList from './AgentList.js'
import AgentSet from './AgentSet.js'
/**
* A {@link Turtle} is an object living on the {@link Patches} world.
* Their coordinates are floats, unlike Patches with integer coordinates.
* They can morph between types of turtles (breeds) and storee information,
* both within themselves but also on Patches and Links.
*
* The Turtles (plural) array, (AgentSet) is a collection of Turtle objects
* that the Turtles array creates and manages.
*
* You do not create either individual Turtle objects, the Turtles array does.
* You also do not create the Turtles array, class {@link Model} does.
*
* You use both, however, using the methods they both provide.
*
* @param {Model} model The model I belong to
* @param {Turtle|Turtle3d} AgentClass The Turtle class
* @param {string} name The name of this new Turtles instance
* @param {null|Turtles} [baseSet=null] Used to create a breed subclass
*/
class Turtles extends AgentSet {
constructor(model, AgentClass, name, baseSet = null) {
super(model, AgentClass, name, baseSet)
if (!name) console.log('Turtles empty name', this)
}
/**
* Create a single Turtle, adding it to this Turtles array.
* The init function is called to initialize the new Turtle.
* Returns the new Turtle.
*
* @param {Function} [initFcn=turtle => {}]
* @returns {Turtle} The newly created Turtle
*/
createOne(initFcn = turtle => {}) {
const turtle = this.addAgent()
// NetLogo docs: Creates number new turtles at the origin.
// New turtles have random integer headings
turtle.theta = util.randomFloat(Math.PI * 2)
// turtle.heading = this.model.fromRads(util.randomFloat(Math.PI * 2))
const p = turtle.patch
if (p.turtles != null) {
p.turtles.push(turtle)
}
initFcn(turtle)
return turtle
}
/**
* Create num Turtles, adding them to this Turtles array.
* The init function is called to initialize each new Turtle.
* Returns an array of the new Turtles
*
* @param {number} number Number of Turtles to create
* @param {Function} [initFcn=turtle => {}] A function to initialize new turtles.
* @returns {Array} The newly created Turtles
*/
create(num, initFcn = turtle => {}) {
return util.repeat(num, (i, a) => {
a.push(this.createOne(initFcn))
})
}
/**
* Return the closest turtle within radius distance of x,y.
* Return null if no turtles within radius.
* If I am a breed, return the closest fellow breed.
*
* @param {number} x X coordinate
* @param {number} y Y coordinate
* @param {number} radius Radius in patches units
* @returns {Turtle} The closest Turtle
*/
closestTurtle(x, y, radius) {
const ts = this.inPatchRectXY(x, y, radius)
if (ts.length === 0) return null
return ts.minOneOf(t => t.distanceXY(x, y))
//pDisk.minOneOf(t => t.dist)
}
/**
* Return an array of Turtles within the array of patchs.
* If I am a breed, return only the Turtles of my breed.
*
* @param {Patch[]} patches Array of patches
* @returns {AgentList} The turtles withn the Patches array.
*/
inPatches(patches) {
let array = new AgentList(this.model)
for (const p of patches) array.push(...p.turtlesHere)
// REMIND: can't use withBreed .. its not an AgentSet. Move to AgentArray?
if (this.isBreedSet()) array = array.filter(a => a.agentSet === this)
return array
}
/**
* Return an array of Turtles within the dx,dy patchRect centered on turtle.
* If I am a breed, return only the Turtles of my breed.
*
* @param {Turtle} turtle The Turtle at the patchRect center.
* @param {number} dx The integer x radius of the patchRect
* @param {number} [dy=dx] The integer y radius of the patchRect
* @param {boolean} [meToo=false] Whether or not to return me as well
* @returns {AgentList} The turtles within the patchRect
*/
inPatchRect(turtle, dx, dy = dx, meToo = false) {
const agents = this.inPatchRectXY(turtle.x, turtle.y, dx, dy)
if (!meToo) util.removeArrayItem(agents, turtle)
return agents
}
/**
* Return an array of Turtles within the dx,dy patchRect centered on x,y.
* If I am a breed, return only the Turtles of my breed.
*
* @param {number} x the patchRect center's integer x value
* @param {number} y the patchRect center's integer y value
* @param {number} dx The integer x radius of the patchRect
* @param {number} [dy=dx] The integer y radius of the patchRect
* @param {boolean} [meToo=false] Whether or not to return me as well
* @returns {AgentList} The turtles within the patchRect
*/
inPatchRectXY(x, y, dx, dy = dx) {
const patches = this.model.patches.patchRectXY(x, y, dx, dy, true)
return this.inPatches(patches)
}
/**
* Return all the Turtles within radius of me.
* If I am a breed, return only fellow breeds.
*
* @param {Turtle} turtle
* @param {number} radius
* @param {boolean} [meToo=false] Whether or not to return me as well
* @returns {AgentList} The turtles within radius of me
*/
inRadius(turtle, radius, meToo = false) {
const agents = this.inPatchRect(turtle, radius, radius, true)
return agents.inRadius(turtle, radius, meToo)
}
/**
* Return all the Turtles with a cone of me.
* The cone is coneAngle wide, centered on my heading.
* If I am a breed, return only fellow breeds.
*
* @param {Turtle} turtle
* @param {number} radius
* @param {boolean} [meToo=false] Whether or not to return me as well
* @returns {AgentList} The turtles within the cone.
*/
inCone(turtle, radius, coneAngle, meToo = false) {
const agents = this.inPatchRect(turtle, radius, radius, true)
return agents.inCone(turtle, radius, coneAngle, turtle.heading, meToo)
}
/**
* Position the Turtles in this breed in an equally spaced circle
* of the given center and radius.
* The turtle headings will be away from the center.
*
* @param {number} [radius=this.model.world.maxX * 0.9] The circle's radius
* @param {Array} [center=[0, 0]] An x,y array
*/
layoutCircle(radius = this.model.world.maxX * 0.9, center = [0, 0]) {
const startAngle = Math.PI / 2 // up
const direction = -1 // Clockwise
const dTheta = (2 * Math.PI) / this.length
const [x0, y0] = center
this.ask((turtle, i) => {
turtle.setxy(x0, y0)
turtle.theta = startAngle + direction * dTheta * i
turtle.forward(radius)
})
}
}
// export default Turtles
export default Turtles