There is absolutely no reason to use a class in this case. Class are only really useful if you have very large objects with lots of behaviour, or you want to build something re-useable.
This does everything you have described.
(
var data_bounds = Rect.newSides(left: -10, top: -10, right: 10, bottom: 10);
var mk_agent = { |pos, vel| (\pos: pos ? Point(), \vel: vel ? Point()) };
var update_agent = {|a, dt=0.01, coords|
mk_agent.(
pos: ( a.pos + (a.vel * dt) ).wrap(coords.leftTop, coords.rightBottom),
vel: a.vel
)
};
var agents = 200.collect({
mk_agent.(
pos: Point(
data_bounds.left + data_bounds.extent.x.rand2,
data_bounds.top + data_bounds.extent.x.rand2
),
vel: Point( 5.0.rand2, 5.0.rand2)
)
});
var window = Window.new("agent display");
var agent_routine = Routine({
var delta_t = 0.01;
loop {
agents = agents.collect( update_agent.(_, dt: delta_t, coords: data_bounds) );
delta_t.wait;
}
});
var window_routine = Routine({
window.view.background_(Color.white);
window.front;
window.drawFunc = {
var to_window = {|pos|
pos.linlin(data_bounds.leftTop, data_bounds.rightBottom, Point(0,0), window.bounds.extent)
};
Pen.strokeColor = Color.black;
agents.do{|a|
Pen.addOval( Rect.aboutPoint( to_window.(a.pos), 10, 10 ));
};
Pen.fillStroke;
};
loop{
window.refresh;
0.016.wait
}
});
agent_routine.play;
window_routine.play(AppClock);
)
Break down…
Define the bounds of the data, this is what portion of the space that will be drawn, also, I am wrapping the particles inside it.
Define a function to make an agent. Here an agent is just an event with the key pos
and vel
, position and velocity.
Define an update agent function. This is immutable, meaning the old agent isn’t modified, but used to create a new one.
Define the agents, I’m making 200 and randomly spacing them in the data bounds.
Make a window
Create a routine to update the agents by applied to aforementioned update_agents
function to each agent.
Create a routine to update and initialise the window first, define color and stuff, then define the draw function. Then loop and wait, calling refresh each cycle which will cause the draw function to be re-evaluated.
The draw function breaks down as follows.
Define a function to map from the data bounds to the screen bounds. Set the pen colour, make a circle at each agent’s position, fill them.
Then run both the routines.
I’d recommend you have a routine for the processing and the window as you will probably want to update them at different speeds.
Using a UserView
is probably better, rather than the window, but if you just want to see them, this will suffice.