Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
parent
d6e5e6e927
commit
7bff7e9f68
878
js/simu/aiosXnet.js
Normal file
878
js/simu/aiosXnet.js
Normal file
|
@ -0,0 +1,878 @@
|
|||
// Simulation extension for phyiscal (behaviorual) agents
|
||||
var RTree = Require('rtree/rtree');
|
||||
var RBush = Require('rtree/rbush');
|
||||
var RBushKnn = Require('rtree/rbush-knn');
|
||||
var Comp = Require('com/compat');
|
||||
|
||||
var current=none;
|
||||
var Aios = none;
|
||||
|
||||
// patchgrid agent instance counter
|
||||
var instances = 0;
|
||||
|
||||
// Geometric Utiliy Functions
|
||||
function sind(x) { return Math.sin(x/360*(2*Math.PI)) }
|
||||
function cosd(x) { return Math.cos(x/360*(2*Math.PI)) }
|
||||
function rotate(d,a) {
|
||||
return [
|
||||
int(d[0]*cosd(a)-d[1]*sind(a)),
|
||||
int(d[1]*sind(a)+d[0]*sind(a))
|
||||
]
|
||||
}
|
||||
function distance2Rect (pos,bbox,scale) {
|
||||
if (!scale) scale={x:1,y:1};
|
||||
var px = pos.x,
|
||||
py = pos.y,
|
||||
x0 = bbox.x+bbox.w/2,
|
||||
y0 = bbox.y+bbox.h/2,
|
||||
dx = (Math.max(Math.abs(px - x0) - bbox.w / 2, 0))/scale.x,
|
||||
dy = (Math.max(Math.abs(py - y0) - bbox.h / 2, 0))/scale.y;
|
||||
|
||||
return Math.sqrt(Math.pow(dx,2)+Math.pow(dy,2))
|
||||
}
|
||||
function distance (pos1,pos2,scale) {
|
||||
if (!scale) scale={x:1,y:1};
|
||||
var
|
||||
dx = Math.abs(pos1.x - pos2.x) / scale.x,
|
||||
dy = Math.abs(pos1.y - pos2.y) / scale.y;
|
||||
|
||||
return Math.sqrt(Math.pow(dx,2)+Math.pow(dy,2))
|
||||
}
|
||||
|
||||
/* construct bbox {x,y,w,h} from geometric data
|
||||
{x,y,x0,y0,x1,y1,dx,dy,w,h,dir} relative
|
||||
to current position {x,y}
|
||||
optional bounds {x0,y0,x1,y1}
|
||||
|
||||
+-----> x
|
||||
| N x,y--+
|
||||
| W X E | |
|
||||
v S +--w,h
|
||||
|
||||
y
|
||||
|
||||
*/
|
||||
|
||||
function makeBbox (pos,geo,bounds) {
|
||||
bbox={x:pos.x,y:pos.y,w:0,h:0} // {x,y,w,h}
|
||||
if (typeof geo == 'number') // radius around center pos
|
||||
return {x:pos.x-geo,y:pos.y-geo,w:2*geo+1,h:2*geo+1};
|
||||
|
||||
if (geo.x) bbox.x=geo.x;
|
||||
if (geo.y) bbox.y=geo.y;
|
||||
if (geo.x0) bbox.x=geo.x0;
|
||||
if (geo.y0) bbox.x=geo.y0;
|
||||
if (geo.dx) bbox.x=pos.x+geo.dx;
|
||||
if (geo.dy) bbox.y=pos.y+geo.dy;
|
||||
if (geo.w) bbox.w=geo.w;
|
||||
if (geo.h) bbox.w=geo.h;
|
||||
if (geo.x1) bbox.w=geo.x1-bbox.x+1;
|
||||
if (geo.y1) bbox.h=geo.y1-bbox.y+1;
|
||||
if (geo.r) return {x:bbox.x-geo.r,y:bbox.y-geo.r,w:2*geo.r+1,h:2*geo.r+1};
|
||||
if (geo.dir) switch (geo.dir) {
|
||||
// including current position X
|
||||
// Ex. WEST:
|
||||
// ****
|
||||
// ***X
|
||||
// ****
|
||||
case Aios.DIR.NORTH:
|
||||
if (geo.distance) bbox.w=geo.spread||1,bbox.h=geo.distance+1;
|
||||
bbox.x -= int(bbox.w/2); bbox.y -= (bbox.h-1);
|
||||
break;
|
||||
case Aios.DIR.SOUTH:
|
||||
if (geo.distance) bbox.w=geo.spread||1,bbox.h=geo.distance+1;
|
||||
bbox.x -= int(bbox.w/2);
|
||||
break;
|
||||
case Aios.DIR.WEST:
|
||||
if (geo.distance) bbox.h=geo.spread||1,bbox.w=geo.distance+1;
|
||||
bbox.y -= int(bbox.h/2); bbox.x -= (bbox.w-1);
|
||||
break;
|
||||
case Aios.DIR.EAST:
|
||||
if (geo.distance) bbox.h=geo.spread||1,bbox.w=geo.distance+1;
|
||||
bbox.y -= int(bbox.h/2);
|
||||
break;
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
function bbox2pp(bbox) {
|
||||
return {x0:bbox.x,y0:bbox.y,x1:bbox.x+bbox.w-1,y1:bbox.y+bbox.h-1,
|
||||
dir:bbox.dir,distance:bbox.distance}
|
||||
}
|
||||
function pp2bbox(pp) {
|
||||
return {x:pp.x0,y:pp.y0,w:pp.x1-pp.x0+1,h:pp.y1-pp.y0+1}
|
||||
}
|
||||
|
||||
function whatType(what) {
|
||||
// agent-twin => agent
|
||||
var tokens = what.match(/([a-z]+)(-.+)/)
|
||||
return tokens?tokens[1]:what;
|
||||
}
|
||||
function whatName(what) {
|
||||
// agent-twin => twin
|
||||
var tokens = what.match(/[a-z]+-(.+)/)
|
||||
return tokens?tokens[1]:null;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generic simulation object iterator (NetLogo comp.)
|
||||
** 1. Agents
|
||||
** ask('agent','*',cb) // all
|
||||
** ask('agent',null,cb) // here
|
||||
** ask('agent',dir,cb)
|
||||
** ask('agent',bbox,cb)
|
||||
** ask('agents-class','*',cb)
|
||||
** ask('agent',id:string,cb)
|
||||
** ask('agent',[id1:string,id2,..],cb)
|
||||
** ask('agent',5,cb) == in-radius 5
|
||||
** ask('agent',[x,y],cb) == @(x,y)
|
||||
** ask('agent',{x,y,w,h},cb?) == within[x,y,x+w,y+h]
|
||||
** ask('agent',{x0,y0,x1,y1},cb?) == within[x,y,x+w,y+h]
|
||||
** ask('agent',{dx,dy,w,h},cb?) == within[dx+x0,dy+y0,x0+dx+w,y0+dy+h]
|
||||
** ask('agent',{x,y,w,h},cb?) == within[x,y-x+w,y+h]
|
||||
** => returns only physical agents!
|
||||
**
|
||||
** 2. Resources
|
||||
** ask('resource',..)
|
||||
** ask('resources-class',..)
|
||||
**
|
||||
** 3. Patches
|
||||
** ask('patch',..)
|
||||
** ask('patches',..)
|
||||
**
|
||||
** Groups:
|
||||
** ask('children')
|
||||
** ask('parent')
|
||||
**
|
||||
** ask('distance',dir|number|bbox)
|
||||
**
|
||||
** Note: callback is executed in CURRENT agent context unless remote flag is set True:
|
||||
** typeof callback = function (agent object,node object)
|
||||
**
|
||||
** TODO: check for consitency when using arrow callback functions (no this rebind possible)
|
||||
*/
|
||||
function aiosXnet(aiosXsimu,module) {
|
||||
var self=this;
|
||||
|
||||
Aios=module;current=Aios.current;
|
||||
|
||||
function ask(what,who,callback,remote) {
|
||||
var type = self.gui.classObject(what)||what,
|
||||
node = current.node,nodeId,
|
||||
pos = node.position, bbox, jump,
|
||||
desc,id,pro,set=[],set2=[],set3=[],multiple=true,
|
||||
i,j,r,row,col,p,q,agent,agents,nodes,pro,
|
||||
name=what.match(/[a-z]+-(.+)/,'');
|
||||
if (!self.options.patch) return;
|
||||
if (name) name=name[1];
|
||||
if (what.indexOf(type+'s')==0) type += 's';
|
||||
switch (type) {
|
||||
|
||||
case 'agent':
|
||||
multiple=false;
|
||||
case 'agents':
|
||||
if (typeof who == 'string') {
|
||||
// entire world has to be searched for agent
|
||||
if (who=='*') who=/.+/;
|
||||
else if (self.cache.agent2node[who]) {
|
||||
agent=self.cache.agent2node[who].getAgentProcess(0);
|
||||
set=[{
|
||||
agent:agent.agent.id,
|
||||
class:agent.agent.ac,
|
||||
pos:agent.node.position,
|
||||
distance:distance(pos,agent.node.position),
|
||||
obj:agent.agent
|
||||
}]
|
||||
set2=[agent.node]
|
||||
}
|
||||
if (set.length==0) {
|
||||
agents=self.world.getAgentProcess(who,name);
|
||||
if (agents) {
|
||||
if (Comp.obj.isArray(agents)) {
|
||||
set=agents.map(function (ap) {
|
||||
return {
|
||||
agent:ap.agent.id,
|
||||
class:ap.agent.ac,
|
||||
pos:ap.node.position,
|
||||
distance:distance(pos,ap.node.position),
|
||||
obj:ap.agent
|
||||
}
|
||||
});
|
||||
set2=agents.map(function (ap) { return ap.node });
|
||||
} else {
|
||||
agent=agents;
|
||||
self.cache.agent2node[agent.agent.id]=agent.node;
|
||||
node = agent.node;
|
||||
set.push({
|
||||
agent:agent.agent.id,
|
||||
class:agent.agent.ac,
|
||||
pos:agent.node.position,
|
||||
distance:distance(pos,agent.node.position),
|
||||
obj:agent.agent
|
||||
});
|
||||
set2.push(node);
|
||||
if (remote) set3.push(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((Comp.obj.isArray(who) && who.length==2) || who==null) {
|
||||
if (!who) who=[pos.x,pos.y];
|
||||
if (self.agentMap) {
|
||||
if (!self.checkBounds(who[0],who[1])) return [];
|
||||
row=self.agentMap[who[1]];
|
||||
col=row[who[0]];
|
||||
for(p in col) {
|
||||
agent=self.getAgentProcess(col[p],p);
|
||||
if (agent && (!name || agent.agent.ac==name)) {
|
||||
set.push({
|
||||
agent:agent.agent.id,
|
||||
class:agent.agent.ac,
|
||||
pos:agent.node.position,
|
||||
distance:distance(pos,agent.node.position),
|
||||
obj:agent.agent
|
||||
});
|
||||
set2.push(agent.node);
|
||||
if (remote) set3.push(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof who == 'number' || Comp.obj.isObj(who)) {
|
||||
// Bounding box {}?
|
||||
bbox=bbox2pp(makeBbox(pos,who));
|
||||
// find agents on nodes in the neighbourhood
|
||||
if (self.agentMap) {
|
||||
for(i=bbox.x0;i<=bbox.x1;i++)
|
||||
for(j=bbox.y0;j<=bbox.y1;j++) {
|
||||
if (!self.checkBounds(i,j)) continue;
|
||||
row=self.agentMap[j];
|
||||
col=row[i];
|
||||
for(p in col) {
|
||||
agent=self.getAgentProcess(col[p],p);
|
||||
if (agent && (!name || agent.agent.ac==name)) {
|
||||
set.push({
|
||||
agent:agent.agent.id,
|
||||
class:agent.agent.ac,
|
||||
pos:agent.node.position,
|
||||
distance:distance(pos,agent.node.position),
|
||||
obj:agent.agent
|
||||
});
|
||||
set2.push(agent.node);
|
||||
if (remote) set3.push(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!name) return;
|
||||
self.world.nodes.forEach(function (_node) {
|
||||
if (wihtin(_node.position,node.position,who)) {
|
||||
agents=_node.getAgentProcess(/[a-zA-Z]+/);
|
||||
for(p in agents) {
|
||||
if (agents[p].agent.ac==name) {
|
||||
set.push({
|
||||
agent:agents[p].agent.id,
|
||||
class:agents[p].agent.ac,
|
||||
pos:_node.position,
|
||||
distance:distance(pos,_node.position),
|
||||
obj:agents[p].agent
|
||||
});
|
||||
set2.push(_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'resource':
|
||||
multiple=false;
|
||||
case 'resources':
|
||||
var px = pos.x * self.options.patch.width,
|
||||
py = pos.y * self.options.patch.height;
|
||||
|
||||
if (typeof who == 'string' && who.indexOf('DIR.')<0 && who != '*') {
|
||||
var obj = self.gui.objects['resource['+who+']'];
|
||||
if (obj) set = {resource:who,
|
||||
class:obj.class,
|
||||
data:obj.data,
|
||||
distance:distance2Rect({x:px,y:py},
|
||||
obj.visual,
|
||||
{x:self.options.patch.width,
|
||||
y:self.options.patch.height}),
|
||||
x:int(obj.visual.x/self.options.patch.width),
|
||||
y:int(obj.visual.y/self.options.patch.height),
|
||||
w:int(obj.visual.w/self.options.patch.width),
|
||||
h:int(obj.visual.h/self.options.patch.height)
|
||||
}
|
||||
} else if (typeof who == 'number' || Comp.obj.isObj(who) || who=='*' ||
|
||||
(Comp.obj.isArray(who) && who.length==2) || who==null) {
|
||||
if (!who) who=[pos.x,pos.y];
|
||||
if (Comp.obj.isArray(who)) bbox={x:who[0],y:who[1],w:1,h:1}; // or w/h=0
|
||||
else if (who=='*') bbox={x:0,y:0,w:self.options.x,h:self.options.y};
|
||||
else bbox=makeBbox(pos,who);
|
||||
set=self.rtree.search(bbox).map(function (rid) {
|
||||
var obj = self.gui.objects[rid];
|
||||
if (name && obj.class != name) return;
|
||||
return {resource:rid.replace(/resource\[([^\]]+)\]/,'$1'),
|
||||
class:obj.class,
|
||||
data:obj.data,
|
||||
distance:distance2Rect({x:px,y:py},
|
||||
obj.visual,
|
||||
{x:self.options.patch.width,
|
||||
y:self.options.patch.height}),
|
||||
x:int(obj.visual.x/self.options.patch.width),
|
||||
y:int(obj.visual.y/self.options.patch.height),
|
||||
w:int(obj.visual.w/self.options.patch.width),
|
||||
h:int(obj.visual.h/self.options.patch.height)
|
||||
}
|
||||
}).filter (function (o) {return o});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'patch':
|
||||
multiple=false;
|
||||
case 'patches':
|
||||
if ((Comp.obj.isArray(who) && who.length==2) || who==null) {
|
||||
if (!who) who=[pos.x,pos.y];
|
||||
if (!self.checkBounds(who[0],who[1])) return [];
|
||||
set=self.patches[who[1]][who[0]];
|
||||
} else if (who=='*') {
|
||||
set=self.patches;
|
||||
} else if (typeof who == 'number' || Comp.obj.isObj(who)) {
|
||||
// Bounding box?
|
||||
bbox=bbox2pp(makeBbox(pos,who));
|
||||
// find patches in the neighbourhood
|
||||
if (name == 'array') {
|
||||
for(j=bbox.y0;j<=bbox.y1;j++) {
|
||||
for(i=bbox.x0;i<=bbox.x1;i++) {
|
||||
if (!self.checkBounds(i,j)) continue;
|
||||
set.push(self.patches[j][i])
|
||||
}
|
||||
}
|
||||
} else for(j=bbox.y0;j<=bbox.y1;j++) {
|
||||
row=[]
|
||||
for(i=bbox.x0;i<=bbox.x1;i++) {
|
||||
if (!self.checkBounds(i,j)) continue;
|
||||
row.push(self.patches[j][i])
|
||||
}
|
||||
if (row.length==1) set.push(row[0])
|
||||
else set.push(row)
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// groups
|
||||
case 'parent':
|
||||
if (node.parent) {
|
||||
agents = node.parent.getAgent(/.+/);
|
||||
set=agents && agents[0]?agents[0].id:undefined;
|
||||
}
|
||||
break;
|
||||
case 'children':
|
||||
multiple=true;
|
||||
if (node.children) {
|
||||
agents=[]
|
||||
node.children.forEach(function (child) {
|
||||
agents = agents.concat(child.getAgent(/.+/));
|
||||
})
|
||||
set=agents;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'distance':
|
||||
function lookup(x,y) {
|
||||
var row,col,p,a=[];
|
||||
if (jump && jump.x == x && jump.y == y) return;
|
||||
row=self.agentMap[y];
|
||||
if (!row) return;
|
||||
col=row[x];
|
||||
if (!col) return;
|
||||
for(p in col) {
|
||||
a.push(self.getNode(col[p]));
|
||||
}
|
||||
return a.length?a:null;
|
||||
}
|
||||
name=name||'';
|
||||
if ((typeof who == 'string' && who.indexOf('DIR.')==0) ||
|
||||
typeof who == 'number' || Comp.obj.isObj(who)) {
|
||||
bbox={x:pos.x,y:pos.y}
|
||||
if (typeof who == 'number') bbox.distance=who;
|
||||
if (typeof who == 'string') bbox.dir=who;
|
||||
if (typeof who == 'object') bbox.dir=who.dir,bbox.distance=who.distance;
|
||||
agents=null;
|
||||
switch (bbox.dir) {
|
||||
case Aios.DIR.NORTH:
|
||||
bbox={y1:bbox.y-1,y0:bbox.distance?(bbox.y-bbox.distance):0,
|
||||
x0:pos.x,x1:pos.x,dir:bbox.dir}; break;
|
||||
case Aios.DIR.SOUTH:
|
||||
bbox={y0:bbox.y+1,y1:bbox.distance?(bbox.y+bbox.distance):self.options.y-1,
|
||||
x0:pos.x,x1:pos.x,dir:bbox.dir}; break;
|
||||
case Aios.DIR.WEST:
|
||||
bbox={x1:bbox.x-1,x0:bbox.distance?(bbox.x+bbox.distance):0,
|
||||
y0:pos.y,y1:pos.y,dir:bbox.dir}; break;
|
||||
case Aios.DIR.EAST:
|
||||
bbox={x0:bbox.x+1,x1:bbox.distance?(bbox.x+bbox.distance):self.options.x-1,
|
||||
y0:pos.y,y1:pos.y,dir:bbox.dir}; break;
|
||||
}
|
||||
if (name.indexOf('resource')<0) {
|
||||
// 1. Agents
|
||||
// Jump over attached group children
|
||||
if (node.children) {
|
||||
jump=node.children[0].position;
|
||||
}
|
||||
switch (bbox.dir) {
|
||||
case Aios.DIR.NORTH: for(i=bbox.y1;i>=bbox.y0 && !nodes;i--) nodes=lookup(pos.x,i); break;
|
||||
case Aios.DIR.SOUTH: for(i=bbox.y0;i<=bbox.y1 && !nodes;i++) nodes=lookup(pos.x,i); break;
|
||||
case Aios.DIR.WEST: for(i=bbox.x1;i>=bbox.x0 && !nodes;i--) nodes=lookup(i,pos.y); break;
|
||||
case Aios.DIR.EAST: for(i=bbox.x0;i<=bbox.x1 && !nodes;i++) nodes=lookup(i,pos.y); break;
|
||||
default:
|
||||
// radius or full bbox search?
|
||||
for(r=1;r<=bbox.distance;r++) {
|
||||
// TODO
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (nodes) nodes = {
|
||||
distance:distance(pos,nodes[0].position),
|
||||
objects:nodes.map(function (node) {
|
||||
var a = node.getAgent(0);
|
||||
return a?{agent:a.id,class:a.ac,pos:node.position}:undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
if (name.indexOf('agent')<0) {
|
||||
if (name.indexOf('resource')==0) name=null;
|
||||
// 2. Resources
|
||||
bbox=pp2bbox(bbox)
|
||||
self.rtree.search(bbox).forEach(function (rid) {
|
||||
var obj = self.gui.objects[rid];
|
||||
var px = pos.x * self.options.patch.width,
|
||||
py = pos.y * self.options.patch.height;
|
||||
if (name && obj.class != name) return;
|
||||
// only resources with distance < nodes.distance are considered
|
||||
var d = distance2Rect({x:px,y:py},
|
||||
obj.visual,
|
||||
{x:self.options.patch.width,
|
||||
y:self.options.patch.height})
|
||||
rid=rid.replace(/resource\[([^\]]+)\]/,'$1');
|
||||
if (nodes && nodes.distance==d) nodes.objects.push({
|
||||
resource:rid,
|
||||
class:obj.class,
|
||||
data:obj.data,
|
||||
distance:d,
|
||||
x:int(obj.visual.x/self.options.patch.width),
|
||||
y:int(obj.visual.x/self.options.patch.height),
|
||||
w:int(obj.visual.w/self.options.patch.width),
|
||||
h:int(obj.visual.h/self.options.patch.height)
|
||||
}); else if (!nodes || d < nodes.distance) nodes = {
|
||||
distance:d,
|
||||
objects:[{
|
||||
resource:rid,
|
||||
class:obj.class,
|
||||
data:obj.data,
|
||||
distance:d,
|
||||
x:int(obj.visual.x/self.options.patch.width),
|
||||
y:int(obj.visual.x/self.options.patch.height),
|
||||
w:int(obj.visual.w/self.options.patch.width),
|
||||
h:int(obj.visual.h/self.options.patch.height)
|
||||
}]
|
||||
}
|
||||
})
|
||||
}
|
||||
return nodes;
|
||||
} else if (typeof who == 'string') {
|
||||
// specific object id
|
||||
// 1. Agent?
|
||||
if (self.cache.agent2node[who]) {
|
||||
return distance(pos,self.cache.agent2node[who].position)
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
remote=false;
|
||||
break;
|
||||
}
|
||||
if (set && callback) {
|
||||
if (Comp.obj.isMatrix(set))
|
||||
for(p in set) {
|
||||
for (q in set[p]) {
|
||||
if (set2.length)
|
||||
callback.call(current.process.agent,set[p][q],set2[p][q],current.process.agent);
|
||||
else
|
||||
callback.call(current.process.agent,set[p][q],q,p,current.process.agent);
|
||||
}
|
||||
}
|
||||
else if (Comp.obj.isArray(set))
|
||||
for(p in set) {
|
||||
if (remote && set3.length) {
|
||||
pro=set3[p];
|
||||
Aios.CB(pro,callback,[pro.agent])
|
||||
} else
|
||||
callback.call(current.process.agent,set[p],set2[p],current.process.agent);
|
||||
}
|
||||
else {
|
||||
if (remote && set3) {
|
||||
pro=set3;
|
||||
Aios.CB(pro,callback,[pro.agent])
|
||||
} else
|
||||
callback.call(current.process.agent,set,set2,current.process.agent);
|
||||
}
|
||||
}
|
||||
if (!multiple && set) return set.length==0?null:(set.length==1?set[0]:set);
|
||||
else return set;
|
||||
}
|
||||
/*
|
||||
** Generic create operation (NetLogo comp., **physical** agents)
|
||||
** Creates node+agent pair.
|
||||
**
|
||||
** Note: callback is executed in that agent context!
|
||||
*/
|
||||
function create(what,num,callback) {
|
||||
var type = whatType(what),
|
||||
node = current.node,nodeId,
|
||||
desc,id,pro,
|
||||
set=[],
|
||||
name=whatName(what);
|
||||
if (!self.options.patch) return;
|
||||
for(var i=0;i<num;i++) switch (type) {
|
||||
case 'agent':
|
||||
case 'agents':
|
||||
// 1. Get the agent descriptor
|
||||
if (!name) self.err('create: agent class is missing')
|
||||
desc=self.model.agents[name];
|
||||
if (!desc) self.err('create: agent class '+name+' is unknown')
|
||||
if (desc.type == 'physical') {
|
||||
// Physical agent: Create a <node,agent> tuple; create agent on this node
|
||||
nodeId = aiosXsimu.createNode(
|
||||
name,
|
||||
node.position.x, // use current position
|
||||
node.position.y,
|
||||
name+'-'+instances);
|
||||
if (!nodeId) self.err('create: node class '+name+' cannot be created')
|
||||
id=aiosXsimu.createOn(nodeId,name,{},3);
|
||||
if (self.options.patch)
|
||||
self.agentMap[node.position.y][node.position.x][id]=nodeId;
|
||||
pro=self.getProcess(nodeId,id);
|
||||
// the callback must be executed before
|
||||
// agent starts execution! Prevent transition after CB
|
||||
pro.notransition=true;
|
||||
self.cache.agent2node[id]=pro.node;
|
||||
pro.type=pro.node.type='physical';
|
||||
// cover arrow functions too, no this rebind possible! first argument is self, too!
|
||||
if (callback) Aios.CB(pro,callback,[pro.agent,i]);
|
||||
set.push(id);
|
||||
instances++;
|
||||
} else {
|
||||
// Create a new computational agent on this node
|
||||
nodeId=node.id;
|
||||
id=aiosXsimu.createOn(nodeId,name,{},3);
|
||||
pro=self.getProcess(nodeId,id);
|
||||
pro.notransition=true;
|
||||
if (callback) Aios.CB(pro,callback,[pro.agent,i])
|
||||
}
|
||||
break;
|
||||
}
|
||||
return set.length==1? set[0]:set
|
||||
}
|
||||
|
||||
function die (who) {
|
||||
var node=current.node,
|
||||
agent=current.process.agent;
|
||||
|
||||
if (!who) {
|
||||
delete self.cache.agent2node[agent.id];
|
||||
Aios.kill();
|
||||
if (self.world.getNode(node.id)) aiosXsimu.deleteNode(node.id);
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
function forward(delta) {
|
||||
var i,x,y,
|
||||
node=current.node,
|
||||
_node,_agent,
|
||||
agent=current.process.agent,
|
||||
desc=self.model.agents[agent.ac],id,
|
||||
agents=[current.process],
|
||||
Delta=[0,0];
|
||||
if (!self.options.patch || desc.type != 'physical') return;
|
||||
id='node['+node.id+']';
|
||||
var visual=aiosXsimu.getVisual(id);
|
||||
if (!visual.heading) visual.heading=0;
|
||||
if (visual.heading<90) Delta[1] -= delta;
|
||||
else if (visual.heading<180) Delta[0] += delta;
|
||||
else if (visual.heading<270) Delta[1] += delta;
|
||||
else if (visual.heading<360) Delta[0] -= delta;
|
||||
|
||||
if (node.children) {
|
||||
node.children.forEach(function (child) {
|
||||
agents.push(child.getAgentProcess(0));
|
||||
})
|
||||
}
|
||||
// check spatial bounds of all agents to be moved
|
||||
for(i in agents) {
|
||||
_agent=agents[i];
|
||||
_node=_agent.node;
|
||||
x=_node.position.x+Delta[0];
|
||||
y=_node.position.y+Delta[1];
|
||||
if (!self.checkBounds(x,y)) return;
|
||||
}
|
||||
// passed - now move all agents
|
||||
for(i in agents) {
|
||||
_agent=agents[i];
|
||||
_node=_agent.node;
|
||||
x=_node.position.x+Delta[0];
|
||||
y=_node.position.y+Delta[1];
|
||||
aiosXnet.setxy(x,y,'agent',_agent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function globals() {
|
||||
return self.model.parameter
|
||||
}
|
||||
|
||||
function get(p) {
|
||||
var agent=current.process.agent,
|
||||
node=current.node,id,
|
||||
desc=self.model.agents[agent.ac],
|
||||
visual;
|
||||
if (!self.options.patch) return;
|
||||
switch (p) {
|
||||
case 'color':
|
||||
id='node['+node.id+']';
|
||||
visual=aiosXsimu.getVisual(id);
|
||||
return visual.fill.color;
|
||||
break;
|
||||
case 'heading':
|
||||
id='node['+node.id+']';
|
||||
visual=aiosXsimu.getVisual(id);
|
||||
return visual.heading;
|
||||
break;
|
||||
case 'shape':
|
||||
id='node['+node.id+']';
|
||||
visual=aiosXsimu.getVisual(id);
|
||||
return visual.shape;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
group = {
|
||||
add: function (parent,children,align) {
|
||||
var agent,desc,node,pos
|
||||
agent=self.world.getAgentProcess(parent);
|
||||
// if (!self.options.patch) return;
|
||||
if (agent) {
|
||||
node = agent.node;
|
||||
pos = Comp.obj.copy(node.position);
|
||||
switch (align) {
|
||||
case Aios.DIR.NORTH: pos.y--; break;
|
||||
case Aios.DIR.SOUTH: pos.y++; break;
|
||||
case Aios.DIR.WEST : pos.x--; break;
|
||||
case Aios.DIR.EAST : pos.x++; break;
|
||||
}
|
||||
if (!self.checkBounds(pos.x,pos.y)) return;
|
||||
desc=self.model.agents[agent.agent.ac];
|
||||
if (desc.type != 'physical') return;
|
||||
if (!node.children) node.children=[];
|
||||
children.forEach(function (child) {
|
||||
var agent2 = self.world.getAgentProcess(child);
|
||||
if (agent2) {
|
||||
var desc2=self.model.agents[agent2.agent.ac];
|
||||
var node2 = agent2.node;
|
||||
if (desc2.type != 'physical') return;
|
||||
var pos2 = Comp.obj.copy(pos);
|
||||
node.children.push(node2);
|
||||
node2.parent = node;
|
||||
// move node container to group position
|
||||
delete self.agentMap[node2.position.y][node2.position.x][agent2.agent.id];
|
||||
node2.position = pos2;
|
||||
self.agentMap[pos2.y][pos2.x][agent2.agent.id]=node2.id;
|
||||
self.moveObjectTo('node['+node2.id+']',
|
||||
self.world2draw(pos2).x,self.world2draw(pos2).y);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
rem: function (parent, children) {
|
||||
var agent,desc,node,pos
|
||||
agent=self.world.getAgentProcess(parent);
|
||||
if (agent) {
|
||||
node = agent.node;
|
||||
if (!node.children) return;
|
||||
children.forEach(function (child) {
|
||||
var agent2 = self.world.getAgentProcess(child);
|
||||
if (agent2) {
|
||||
var desc2=self.model.agents[agent2.agent.ac];
|
||||
var node2 = agent2.node;
|
||||
if (desc2.type != 'physical') return;
|
||||
var pos2 = Comp.obj.copy(pos);
|
||||
node.children=node.children.filter(function (_node) { return _node.id!=node2.id });
|
||||
node2.parent = null;
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// TODO send net agents a signal
|
||||
function signal (destination,signal,argument) {
|
||||
// destination is agent id;
|
||||
// we need the node id, too
|
||||
var agent = self.world.getAgentProcess(destination);
|
||||
return agent?agent.node:'??'
|
||||
}
|
||||
|
||||
function set(p,v) {
|
||||
var node=current.node,
|
||||
agent=current.process.agent,
|
||||
desc=self.model.agents[agent.ac],
|
||||
obj;
|
||||
if (!self.options.patch) return;
|
||||
switch (p) {
|
||||
case 'color':
|
||||
if (desc.type == 'physical') {
|
||||
// Change color of node and agent
|
||||
id='node['+node.id+']';
|
||||
aiosXsimu.changeVisual(id,{fill:{color:v}});
|
||||
id='agent['+agent.ac+':'+agent.id+':'+node.id+']';
|
||||
aiosXsimu.changeVisual(id,{fill:{color:v}});
|
||||
} else {
|
||||
// Change color of agent
|
||||
}
|
||||
break;
|
||||
case 'shape':
|
||||
if (desc.type == 'physical') {
|
||||
// Change shape of node
|
||||
id='node['+node.id+']';
|
||||
aiosXsimu.changeVisual(id,{shape:v,align:v=='circle'?'center':undefined});
|
||||
} else {
|
||||
// Change color of agent
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO resources, ..
|
||||
function setxy(x,y,what,who) {
|
||||
var type=what||'agent',
|
||||
desc,
|
||||
pos,
|
||||
node=current.node,
|
||||
agent=current.process.agent;
|
||||
// only discrete coordinates allowed (due to pos-map tables)
|
||||
x=x|0; y=y|0;
|
||||
if (!self.options.patch) return;
|
||||
// self.log([x,y,type,agent.ac])
|
||||
switch (type) {
|
||||
case 'agent':
|
||||
case 'agents':
|
||||
if (typeof who == 'string') {
|
||||
agent=aiosXnet.ask('agent',who);
|
||||
if (!agent) return;
|
||||
} else if (typeof who == 'object') {
|
||||
agent=who.agent; // agent process!
|
||||
node=who.node;
|
||||
}
|
||||
desc=self.model.agents[agent.ac];
|
||||
if (!desc) return;
|
||||
if (desc.type == 'physical') {
|
||||
// move agent and its node!
|
||||
if (!self.checkBounds(x,y)) return;
|
||||
pos={x:x,y:y};
|
||||
// checkBounds(x,y)
|
||||
if (node.position) {
|
||||
// Invalidate old worldmap entry
|
||||
delete self.agentMap[node.position.y][node.position.x][agent.id];
|
||||
}
|
||||
self.agentMap[y][x][agent.id]=node.id;
|
||||
self.moveObjectTo('node['+node.id+']',
|
||||
self.world2draw(pos).x,self.world2draw(pos).y);
|
||||
node.position=pos;
|
||||
|
||||
// Move child nodes, too (but not parent vice versa)!
|
||||
if (node.children) {
|
||||
node.children.forEach(function (node2) {
|
||||
node2.processes.table.forEach(function (agent2) {
|
||||
if (!agent2) return;
|
||||
if (self.model.agents[agent2.agent.ac] &&
|
||||
self.model.agents[agent2.agent.ac].type != 'physical') return;
|
||||
if (node2.position) {
|
||||
// Invalidate old worldmap entry
|
||||
delete self.agentMap[node2.position.y][node2.position.x][agent2.agent.id];
|
||||
}
|
||||
self.agentMap[y][x][agent2.agent.id]=node2.id;
|
||||
})
|
||||
self.moveObjectTo('node['+node2.id+']',
|
||||
self.world2draw(pos).x,self.world2draw(pos).y);
|
||||
node2.position=pos;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// not supported!
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Turn agent or group of agents ...
|
||||
function turn(angle) {
|
||||
var node=current.node,_node,_pos,visual,agents,
|
||||
agent=current.process.agent, relative,
|
||||
desc=self.model.agents[agent.ac],id,
|
||||
pos=node.position,delta=0,Delta=[0,0];
|
||||
switch (angle) {
|
||||
case Aios.DIR.NORTH: angle=0; break;
|
||||
case Aios.DIR.SOUTH: angle=180; break;
|
||||
case Aios.DIR.WEST: angle=270; break;
|
||||
case Aios.DIR.EAST: angle=90; break;
|
||||
default:
|
||||
if (typeof angle == 'number') relative=true;
|
||||
}
|
||||
if (!self.options.patch || desc.type != 'physical') return;
|
||||
id='node['+node.id+']';
|
||||
visual = aiosXsimu.getVisual(id)
|
||||
if (!visual.heading) visual.heading=0;
|
||||
if (relative) angle=(visual.heading+angle) % 360;
|
||||
delta=angle-visual.heading;
|
||||
// translate group children?
|
||||
if (node.children && node.children.length) {
|
||||
_pos=node.children[0].position;
|
||||
// All children are overlayed !?
|
||||
Delta=[_pos.x-pos.x,_pos.y-pos.y]
|
||||
self.log({from:Delta, to:rotate(Delta,delta)})
|
||||
Delta=rotate(Delta,delta);
|
||||
node.children.forEach(function (child) {
|
||||
var x=pos.x+Delta[0],y=pos.y+Delta[1];
|
||||
if (!self.checkBounds(x,y)) return;
|
||||
var _agent = child.getAgentProcess(0);
|
||||
aiosXnet.setxy(x,y,'agent',_agent)
|
||||
});
|
||||
}
|
||||
// update visual heading parameter
|
||||
visual.heading = angle;
|
||||
}
|
||||
function within(x,y,bbox) {
|
||||
return x >= bbox.x &&
|
||||
y >= bbox.y &&
|
||||
x < (bbox.x+bbox.w) &&
|
||||
y < (bbox.y+bbox.h)
|
||||
}
|
||||
return {
|
||||
ask:ask,
|
||||
create:create,
|
||||
die:die,
|
||||
forward:forward,
|
||||
globals:globals,
|
||||
get:get,
|
||||
group:group,
|
||||
set:set,
|
||||
setxy:setxy,
|
||||
signal:signal,
|
||||
turn:turn,
|
||||
within:within
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = aiosXnet
|
Loading…
Reference in New Issue
Block a user