Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visualization changes #1278

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
76 changes: 76 additions & 0 deletions mesa/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __init__(self, unique_id: int, model: "Model") -> None:
self.unique_id = unique_id
self.model = model
self.pos: Optional[Position] = None
self.heading = 90
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should define a new class for the new attribute and methods, AgentWithHeading / AgentWithDirection / if you have a better name for it. Because not all models need the feature.


def step(self) -> None:
"""A single step of the agent."""
Expand All @@ -36,6 +37,81 @@ def step(self) -> None:
def advance(self) -> None:
pass

def move_forward_or_backward(self, amount, sign):
"""Does the calculation to find the agent's next move and is used within the forward and backward functions"""
new_x = float(self.pos[0]) + sign * math.cos(self.heading * math.pi / 180) * amount
new_y = float(self.pos[1]) + sign * math.sin(self.heading * math.pi / -180) * amount
next_pos = (new_x, new_y)
try:
self.model.space.move_agent(self, next_pos)
except:
print("agent.py (forward_backwards): could not move agent within self.model.space")

def move_forward(self, amount):
"""Moves the agent forward by the amount given"""
self.move_forward_or_backward(amount, 1)

def move_backward(self, amount):
"""Moves the agent backwards from where its facing by the given amount"""
self.move_forward_or_backward(amount, -1)

def turn_right(self, degree):
"""Turns the agent right by the given degree"""
self.heading = (self.heading - degree) % 360

def turn_left(self, degree):
"""Turns the agent left by the given degree"""
self.heading = (self.heading + degree) % 360

def setxy(self, x, y):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about set_position(self, position)? This way, the API remains the same in the implementation with higher number of dimensions, instead of e.g. set_xyz for 3D.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setxy needs to be removed now that we have set_pos.

"""Sets the current position to the specified x,y parameters"""
self.pos = (x, y)

def set_pos(self, apos):
"""Sets the current position to the specified pos parameter"""
self.pos = apos

def distancexy(self, x, y):
"""Gives you the distance of the agent and the given coordinate"""
return math.dist(self.pos, (x, y))

def distance(self, another_agent):
"""Gives you the distance between the agent and another agent"""
return self.distancexy(another_agent.pos[0], another_agent.pos[1])

def die(self):
"""Removes the agent from the schedule and the grid """
try:
self.model.schedule.remove(self)
except:
print("agent.py (die): could not remove agent from self.model.schedule")
try:
self.model.space.remove_agent(self)
except:
print("agent.py (die): could not remove agent from self.model.space")

def towardsxy(self, x, y):
"""Calculates angle between a given coordinate and horizon as if the current position is the origin"""
dx = x - float(self.pos[0])
dy = float(self.pos[1]) - y
if dx == 0:
return 90 if dy > 0 else 270
else:
return math.degrees(math.atan2(dy, dx))

def towards(self, another_agent):
"""Calculates angle between an agent and horizon as if the current position is the origin"""
return self.towardsxy(*another_agent.pos)

def facexy(self, x, y):
"""Makes agent face a given coordinate"""
self.heading = self.towardsxy(x, y)

def face(self, another_agent):
"""Makes agent face another agent"""
x, y = another_agent.pos
self.facexy(x, y)

@property
def random(self) -> Random:
return self.model.random
34 changes: 34 additions & 0 deletions mesa/visualization/modules/SimpleContinuousModule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from mesa.visualization.ModularVisualization import VisualizationElement


class SimpleCanvas(VisualizationElement):
local_includes = ["simple_continuous_canvas.js"]
portrayal_method = None
canvas_height = 500
canvas_width = 500

def __init__(self, portrayal_method, canvas_height=500, canvas_width=500, background_src=None):
"""
Instantiate a new SimpleCanvas
"""
self.portrayal_method = portrayal_method
self.canvas_height = canvas_height
self.canvas_width = canvas_width
self.background_src = background_src
new_element = "new Simple_Continuous_Module({}, {}, {})".format(
self.canvas_width, self.canvas_height, "'" + self.background_src + "'" if self.background_src != None else "null"
)
self.js_code = "elements.push(" + new_element + ");"

def render(self, model):
space_state = []
for obj in model.schedule.agents:
portrayal = self.portrayal_method(obj)
x, y = obj.pos
x = (x - model.space.x_min) / (model.space.x_max - model.space.x_min)
y = (y - model.space.y_min) / (model.space.y_max - model.space.y_min)
portrayal["x"] = x
portrayal["y"] = y
portrayal["heading"] = obj.heading
space_state.append(portrayal)
return space_state
Loading