class AgentClass Agent instances represent the dynamic, behavioral element of modeling. Each agent knows the patch it is on, and interacts with that and other patches, as well as other agents.
class AgentConstructor & Class Variables:
These class variables are “defaults” and many are “promoted” to instance variables. To have these be set to a constant for all instances, use breed.setDefault. This can be a huge savings in memory.
id: null # unique id, promoted by agentset create factory method
breed: null # my agentSet, set by the agentSet owning me
x: 0; y:0; p: null # my location and the patch I'm on
size: 1 # my size in patch coords
color: null # default color, overrides random color if set
strokeColor: null # color of the border of an agent
shape: "default" # my shape
hidden: false # draw me?
label: null # my text
labelColor: [0,0,0] # its color
labelOffset: [0,0] # its offset from my x,y
penDown: false # if my pen is down, I draw my path between changes in x,y
penSize: 1 # the pen thickness in pixels
heading: null # the direction I'm pointed in, in radians
sprite: null # an image of me for optimized drawing
useSprites: false # should I use sprites?
cacheLinks: false # should I keep links to/from me in links array?.
links: null # array of links to/from me as an endpoint; init by ctor
constructor: -> # called by agentSets create factory, not user
@x = @y = 0
@p = @model.patches.patch @x, @y
@color = u.randomColor() unless @color? # or @useSprites or @hidden
@heading = u.randomFloat(Math.PI*2) unless @heading?
@p.agents.push @ if @p.agents? # @model.patches.cacheAgentsHere
@links = [] if @cacheLinksSet agent color to c scaled by s. Usage: see patch.scaleColor
scaleColor: (c, s) ->
u.deprecated "Agent.scaleColor: use ColorMaps ramps or closestColor"
@color = ColorMaps.scaleColor(c, s)@color = u.clone @color unless @hasOwnProperty “color” # promote color to inst var u.scaleColor c, s, @color
scaleOpacity: (c, s) ->
u.deprecated "Agent.scaleOpacity: use ColorMaps ramps"
@color = u.scaleOpacity c, s, @color@color = u.clone @color unless @hasOwnProperty “color” u.scaleOpacity c, s, @color
Return a string representation of the agent.
toString: -> "{id:#{@id} xy:#{u.aToFixed [@x,@y]} c:#{@color.css} h: #{h=@heading.toFixed 2}/#{Math.round(u.radToDeg(h))}}"Place the agent at the given x,y (floats) in patch coords using patch topology (isTorus)
setXY: (x, y) -> # REMIND GC problem, 2 arrays
[x0, y0] = [@x, @y] if @penDown
[@x, @y] = @model.patches.coord x, y
p = @p
@p = @model.patches.patch @x, @y
if p.agents? and p isnt @p # @model.patches.cacheAgentsHere
u.removeItem p.agents, @
@p.agents.push @
if @penDown
drawing = @model.drawing
drawing.strokeStyle = @color.css # u.colorStr @color
drawing.lineWidth = @model.patches.fromBits @penSize
drawing.beginPath()
drawing.moveTo x0, y0; drawing.lineTo x, y # REMIND: euclidean
drawing.stroke()Place the agent at the given patch/agent location
moveTo: (a) -> @setXY a.x, a.yMove forward (along heading) d units (patch coords), using patch topology (isTorus)
forward: (d) ->
@setXY @x + d*Math.cos(@heading), @y + d*Math.sin(@heading)Change current heading by rad radians which can be + (left) or - (right). Returns new heading.
rotate: (rad) -> @heading = u.wrap @heading + rad, 0, Math.PI*2
right: (rad) -> @rotate -rad
left: (rad) -> @rotate radDraw the agent, instanciating a sprite if required
draw: (ctx) ->
shape = Shapes[@shape]
rad = if shape.rotate then @heading else 0 # radians
if @sprite? or @useSprites # @breed.useSprites
@setSprite() unless @sprite? # lazy evaluation of useSprites
Shapes.drawSprite ctx, @sprite, @x, @y, @size, rad
else
Shapes.draw ctx, shape, @x, @y, @size, rad, @color, @strokeColor
if @label?
[x,y] = @model.patches.patchXYtoPixelXY @x, @y
u.ctxDrawText ctx, @label,
x+@labelOffset[0], y+@labelOffset[1], @labelColorSet an individual agent’s sprite, synching its color, shape, size
setSprite: (sprite)->
if (s=sprite)?
@sprite = s; @color = s.color; @strokeColor = s.strokeColor
@shape = s.shape; @size = @model.patches.fromBits(s.size)
else@color = u.randomColor() unless @color? # not needed if lazy evaluation
@sprite = Shapes.shapeToSprite @shape, @color,
@model.patches.toBits(@size), @strokeColorDraw the agent on the drawing layer, leaving permanent image.
stamp: -> @draw @model.drawingReturn distance in patch coords from me to x,y using patch topology (isTorus)
distanceXY: (x,y) ->
if @model.patches.isTorus
then u.torusDistance @x, @y, x, y, @model.patches.numX, @model.patches.numY
else u.distance @x, @y, x, yReturn distance in patch coords from me to given agent/patch using patch topology.
distance: (o) -> # o any object w/ x,y, patch or agent
@distanceXY o.x, o.yReturn the closest torus topology point of given x,y relative to myself. Used internally to determine how to draw links between two agents. See util.torusPt.
torusPtXY: (x, y) ->
u.torusPt @x, @y, x, y, @model.patches.numX, @model.patches.numYReturn the closest torus topology point of given agent/patch relative to myself. See util.torusPt.
torusPt: (o) ->
@torusPtXY o.x, o.ySet my heading towards given agent/patch using patch topology.
face: (o) -> @heading = @towards oReturn heading towards x,y using patch topology.
towardsXY: (x, y) ->
if (ps=@model.patches).isTorus
then u.torusRadsToward @x, @y, x, y, ps.numX, ps.numY
else u.radsToward @x, @y, x, yReturn heading towards given agent/patch using patch topology.
towards: (o) -> @towardsXY o.x, o.yReturn patch ahead of me by given distance and heading. Returns null if non-torus and off patch world. Heading is + for left, - for right.
patchAtHeadingAndDistance: (h,d) ->
[dx,dy] = u.polarToXY d, h + @heading
@patchAt dx,dy
patchLeftAndAhead: (dh, d) -> @patchAtHeadingAndDistance dh, d
patchRightAndAhead: (dh, d) -> @patchAtHeadingAndDistance -dh, d
patchAhead: (d) -> @patchAtHeadingAndDistance 0, d
canMove: (d) -> @patchAhead(d)?
patchAt: (dx,dy) ->
x=@x+dx; y=@y+dy
if (ps=@model.patches).isOnWorld x,y then ps.patch x,y else nullRemove myself from the model. Includes removing myself from the agents agentset and removing any links I may have.
die: ->
@breed.remove @
l.die() for l in @myLinks() by -1
u.removeItem @p.agents, @ if @p.agents?
nullFactory: create num new agents at this agents location. The optional init proc is called on the new agent after inserting in its agentSet.
hatch: (num = 1, breed = @breed, init = ->) ->
breed.create num, (a) => # fat arrow so that @ = this agent
a.setXY @x, @y # for side effects like patches.agentsHere
a.color = @color
a[k] = @[k] for k in breed.ownVariables when a[k] is null # hatched agent inherits parents' breed propertiespossible alternative: a[k] = @[k] for k, v in a when v is null
init(a); a # Important: init called after object inserted in agent setReturn the members of the given agentset that are within radius distance from me, using patch topology
inRadius: (aset, radius) ->
aset.inRadius(@, radius)Return the members of the given agentset that are within distance from me, and within angle radians of my heading using patch topology
inCone: (aset, distance, angle) ->
aset.inCone @, distance, angle, @headingReturn true if world coordinate falls on agent sprite
hitTest: (x, y) ->
@distanceXY(x, y) < @sizeReturn other end of link from me
otherEnd: (l) -> if l.end1 is @ then l.end2 else l.end1Return all links linked to me
myLinks: ->
@links ? (l for l in @model.links when (l.end1 is @) or (l.end2 is @))Return all agents linked to me.
linkNeighbors: -> # return all agents linked to me
@otherEnd l for l in @myLinks()Return links where I am the “to” agent in links.create
myInLinks: ->
l for l in @myLinks() when l.end2 is @Return other end of myInLinks
inLinkNeighbors: ->
l.end1 for l in @myLinks() when l.end2 is @Return links where I am the “from” agent in links.create
myOutLinks: ->
l for l in @myLinks() when l.end1 is @Return other end of myOutinks
outLinkNeighbors: ->
l.end2 for l in @myLinks() when l.end1 is @use colorMixin to setup colors
colorMixin(Agent, "color", null)
colorMixin(Agent, "strokeColor", null)
colorMixin(Agent, "labelColor", "black")