1
0
mirror of https://github.com/gsi-upm/soil synced 2024-11-14 23:42:29 +00:00

Colors and shapes

This commit is contained in:
Tasio Mendez 2018-04-10 18:26:24 +02:00
parent 9caec2dad3
commit da1ce1ea30
14 changed files with 485 additions and 30 deletions

245
TerroristNetworkModel.py Normal file
View File

@ -0,0 +1,245 @@
import random
import networkx as nx
from soil.agents import BaseAgent
from scipy.spatial import cKDTree as KDTree
global betweenness_centrality_global
global degree_centrality_global
betweenness_centrality_global = None
degree_centrality_global = None
class TerroristSpreadModel(BaseAgent):
"""
Settings:
information_spread_intensity
terrorist_additional_influence
min_vulnerability (optional else zero)
max_vulnerability
prob_interaction
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
global betweenness_centrality_global
global degree_centrality_global
if betweenness_centrality_global == None:
betweenness_centrality_global = nx.betweenness_centrality(self.global_topology)
if degree_centrality_global == None:
degree_centrality_global = nx.degree_centrality(self.global_topology)
self.information_spread_intensity = environment.environment_params['information_spread_intensity']
self.terrorist_additional_influence = environment.environment_params['terrorist_additional_influence']
self.prob_interaction = environment.environment_params['prob_interaction']
if self.state['id'] == 0: # Civilian
self.initial_belief = random.uniform(0.00, 0.5)
elif self.state['id'] == 1: # Terrorist
self.initial_belief = random.uniform(0.8, 1.00)
elif self.state['id'] == 2: # Leader
self.initial_belief = 1.00
if 'min_vulnerability' in environment.environment_params:
self.vulnerability = random.uniform( environment.environment_params['min_vulnerability'], environment.environment_params['max_vulnerability'] )
else :
self.vulnerability = random.uniform( 0, environment.environment_params['max_vulnerability'] )
self.mean_belief = self.initial_belief
self.betweenness_centrality = betweenness_centrality_global[self.id]
self.degree_centrality = degree_centrality_global[self.id]
# self.state['radicalism'] = self.mean_belief
def count_neighboring_agents(self, state_id=None):
if isinstance(state_id, list):
return len(self.get_neighboring_agents(state_id))
else:
return len(super().get_agents(state_id, limit_neighbors=True))
def get_neighboring_agents(self, state_id=None):
if isinstance(state_id, list):
_list = []
for i in state_id:
_list += super().get_agents(i, limit_neighbors=True)
return [ neighbour for neighbour in _list if isinstance(neighbour, TerroristSpreadModel) ]
else:
_list = super().get_agents(state_id, limit_neighbors=True)
return [ neighbour for neighbour in _list if isinstance(neighbour, TerroristSpreadModel) ]
def step(self):
if self.state['id'] == 0: # Civilian
self.civilian_behaviour()
elif self.state['id'] == 1: # Terrorist
self.terrorist_behaviour()
elif self.state['id'] == 2: # Leader
self.leader_behaviour()
def civilian_behaviour(self):
if self.count_neighboring_agents() > 0:
neighbours = []
for neighbour in self.get_neighboring_agents():
if random.random() < self.prob_interaction:
neighbours.append(neighbour)
influence = sum( neighbour.degree_centrality for neighbour in neighbours )
mean_belief = sum( neighbour.mean_belief * neighbour.degree_centrality / influence for neighbour in neighbours )
self.initial_belief = self.mean_belief
mean_belief = mean_belief * self.information_spread_intensity + self.initial_belief * ( 1 - self.information_spread_intensity )
self.mean_belief = mean_belief * self.vulnerability + self.initial_belief * ( 1 - self.vulnerability )
if self.mean_belief >= 0.8:
self.state['id'] = 1
# self.state['radicalism'] = self.mean_belief
def leader_behaviour(self):
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
if self.count_neighboring_agents(state_id=[1,2]) > 0:
for neighbour in self.get_neighboring_agents(state_id=[1,2]):
if neighbour.betweenness_centrality > self.betweenness_centrality:
self.state['id'] = 1
# self.state['radicalism'] = self.mean_belief
def terrorist_behaviour(self):
if self.count_neighboring_agents(state_id=[1,2]) > 0:
neighbours = self.get_neighboring_agents(state_id=[1,2])
influence = sum( neighbour.degree_centrality for neighbour in neighbours )
mean_belief = sum( neighbour.mean_belief * neighbour.degree_centrality / influence for neighbour in neighbours )
self.initial_belief = self.mean_belief
self.mean_belief = mean_belief * self.vulnerability + self.initial_belief * ( 1 - self.vulnerability )
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
if self.count_neighboring_agents(state_id=2) == 0 and self.count_neighboring_agents(state_id=1) > 0:
max_betweenness_centrality = self
for neighbour in self.get_neighboring_agents(state_id=1):
if neighbour.betweenness_centrality > max_betweenness_centrality.betweenness_centrality:
max_betweenness_centrality = neighbour
if max_betweenness_centrality == self:
self.state['id'] = 2
# self.state['radicalism'] = self.mean_belief
def add_edge(self, G, source, target):
G.add_edge(source.id, target.id, start=self.env._now)
def link_search(self, G, node, radius):
pos = nx.get_node_attributes(G, 'pos')
nodes, coords = list(zip(*pos.items()))
kdtree = KDTree(coords) # Cannot provide generator.
edge_indexes = kdtree.query_pairs(radius, 2)
_list = [ edge[int(not edge.index(node))] for edge in edge_indexes if node in edge ]
return [ G.nodes()[index]['agent'] for index in _list ]
def social_search(self, G, node, steps):
nodes = list(nx.ego_graph(G, node, radius=steps).nodes())
nodes.remove(node)
return [ G.nodes()[index]['agent'] for index in nodes ]
class TrainingAreaModel(BaseAgent):
"""
Settings:
training_influence
min_vulnerability
Requires TerroristSpreadModel.
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
self.training_influence = environment.environment_params['training_influence']
if 'min_vulnerability' in environment.environment_params:
self.min_vulnerability = environment.environment_params['min_vulnerability']
else: self.min_vulnerability = 0
def step(self):
for neighbour in self.get_neighboring_agents():
if isinstance(neighbour, TerroristSpreadModel) and neighbour.vulnerability > self.min_vulnerability:
neighbour.vulnerability = neighbour.vulnerability * ( 1 - self.training_influence )
class HavenModel(BaseAgent):
"""
Settings:
haven_influence
max_vulnerability
Requires TerroristSpreadModel.
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
self.haven_influence = environment.environment_params['haven_influence']
self.max_vulnerability = environment.environment_params['max_vulnerability']
def step(self):
if self.count_neighboring_agents(state_id=0) == 0:
self.state['id'] = 1 # Terrorism Haven
else:
self.state['id'] = 0 # Civilian Haven
for neighbour in self.get_neighboring_agents():
if isinstance(neighbour, TerroristSpreadModel) and neighbour.vulnerability < self.max_vulnerability:
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.haven_influence )
class TerroristNetworkModel(TerroristSpreadModel):
"""
Settings:
sphere_influence
vision_range
weight_social_distance
weight_link_distance
"""
def __init__(self, environment=None, agent_id=0, state=()):
super().__init__(environment=environment, agent_id=agent_id, state=state)
self.vision_range = environment.environment_params['vision_range']
self.sphere_influence = environment.environment_params['sphere_influence']
self.weight_social_distance = environment.environment_params['weight_social_distance']
self.weight_link_distance = environment.environment_params['weight_link_distance']
def step(self):
if self.state['id'] == 1 or self.state['id'] == 2:
self.update_relationships()
super().step()
def update_relationships(self):
if self.count_neighboring_agents(state_id=0) == 0:
close_ups = self.link_search(self.global_topology, self.id, self.vision_range)
step_neighbours = self.social_search(self.global_topology, self.id, self.sphere_influence)
search = list(set(close_ups).union(step_neighbours))
neighbours = self.get_neighboring_agents()
search = [item for item in search if not item in neighbours and isinstance(item, TerroristNetworkModel)]
for agent in search:
social_distance = 1 / self.shortest_path_length(self.global_topology, self.id, agent.id)
spatial_proximity = ( 1 - self.get_distance(self.global_topology, self.id, agent.id) )
prob_new_interaction = self.weight_social_distance * social_distance + self.weight_link_distance * spatial_proximity
if random.random() < prob_new_interaction:
self.add_edge(self.global_topology, self, agent)
break
def get_distance(self, G, source, target):
source_x, source_y = nx.get_node_attributes(G, 'pos')[source]
target_x, target_y = nx.get_node_attributes(G, 'pos')[target]
dx = abs( source_x - target_x )
dy = abs( source_y - target_y )
return ( dx ** 2 + dy ** 2 ) ** ( 1 / 2 )
def shortest_path_length(self, G, source, target):
try:
return nx.shortest_path_length(G, source, target)
except nx.NetworkXNoPath:
return float('inf')

58
TerroristNetworkModel.yml Normal file
View File

@ -0,0 +1,58 @@
name: TerroristNetworkModel_sim
load_module: TerroristNetworkModel
max_time: 100
num_trials: 1
network_params:
generator: random_geometric_graph
radius: 0.2
# generator: geographical_threshold_graph
# theta: 20
n: 50
network_agents:
- agent_type: TerroristNetworkModel
weight: 0.8
state:
id: 0 # Civilians
- agent_type: TerroristNetworkModel
weight: 0.1
state:
id: 2 # Leaders
- agent_type: TrainingAreaModel
weight: 0.05
state:
id: 2 # Terrorism
- agent_type: HavenModel
weight: 0.05
state:
id: 0 # Civilian
environment_params:
# TerroristSpreadModel
information_spread_intensity: 0.7
terrorist_additional_influence: 0.035
max_vulnerability: 0.5
prob_interaction: 0.5
# TrainingAreaModel and HavenModel
training_influence: 0.20
haven_influence: 0.20
# TerroristNetworkModel
vision_range: 0.50
sphere_influence: 2
weight_social_distance: 0.035
weight_link_distance: 0.035
visualization_params:
# Icons downloaded from https://www.iconfinder.com/
shape_property: agent
shapes:
TrainingAreaModel: target
HavenModel: home
colors:
- attr_id: 0
color: green
- attr_id: 1
color: red
- attr_id: 2
color: '#c16a6a'

View File

@ -86,6 +86,9 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
self.config = self.config[0] self.config = self.config[0]
self.send_log('INFO.soil', 'Using config: {name}'.format(name=self.config['name'])) self.send_log('INFO.soil', 'Using config: {name}'.format(name=self.config['name']))
if 'visualization_params' in self.config:
self.write_message({'type': 'visualization_params',
'data': self.config['visualization_params']})
self.name = self.config['name'] self.name = self.config['name']
self.run_simulation() self.run_simulation()
@ -93,6 +96,8 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
for key in self.config['environment_params']: for key in self.config['environment_params']:
if type(self.config['environment_params'][key]) == float: if type(self.config['environment_params'][key]) == float:
setting_type = 'number' setting_type = 'number'
elif type(self.config['environment_params'][key]) == int:
setting_type = 'number'
elif type(self.config['environment_params'][key]) == bool: elif type(self.config['environment_params'][key]) == bool:
setting_type = 'boolean' setting_type = 'boolean'
else: else:
@ -149,8 +154,15 @@ class SocketHandler(tornado.websocket.WebSocketHandler):
def run_simulation(self): def run_simulation(self):
# Run simulation and capture logs # Run simulation and capture logs
if 'visualization_params' in self.config:
del self.config['visualization_params']
with self.logging(self.application.model.name): with self.logging(self.application.model.name):
self.simulation = self.application.model.run(self.config) try:
self.simulation = self.application.model.run(self.config)
except:
error = 'Something went wrong. Please, try again.'
self.write_message({'type': 'error',
'error': error})
trials = [] trials = []
for i in range(self.config['num_trials']): for i in range(self.config['num_trials']):
@ -232,5 +244,5 @@ class ModularServer(tornado.web.Application):
url = 'http://127.0.0.1:{PORT}'.format(PORT=self.port) url = 'http://127.0.0.1:{PORT}'.format(PORT=self.port)
print('Interface starting at {url}'.format(url=url)) print('Interface starting at {url}'.format(url=url))
self.listen(self.port) self.listen(self.port)
webbrowser.open(url) # webbrowser.open(url)
tornado.ioloop.IOLoop.instance().start() tornado.ioloop.IOLoop.instance().start()

View File

@ -183,6 +183,7 @@ hr {
z-index: 5; z-index: 5;
height: 35px; height: 35px;
padding: .5rem 1rem; padding: .5rem 1rem;
overflow: hidden;
line-height: 1.5; line-height: 1.5;
color: #464a4c; color: #464a4c;
pointer-events: none; pointer-events: none;

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><svg height="1792" viewBox="0 0 1792 1792" width="1792" xmlns="http://www.w3.org/2000/svg"><path d="M1472 992v480q0 26-19 45t-45 19h-384v-384h-256v384h-384q-26 0-45-19t-19-45v-480q0-1 .5-3t.5-3l575-474 575 474q1 2 1 6zm223-69l-62 74q-8 9-21 11h-3q-13 0-21-7l-692-577-692 577q-12 8-24 7-13-2-21-11l-62-74q-8-10-7-23.5t11-21.5l719-599q32-26 76-26t76 26l244 204v-195q0-14 9-23t23-9h192q14 0 23 9t9 23v408l219 182q10 8 11 21.5t-7 23.5z"/></svg>

After

Width:  |  Height:  |  Size: 462 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M448,448c0,0,0-26.4-2.2-40.2c-1.8-10.9-16.9-25.3-81.1-48.9c-63.2-23.2-59.3-11.9-59.3-54.6c0-27.7,14.1-11.6,23.1-64.2 c3.5-20.7,6.3-6.9,13.9-40.1c4-17.4-2.7-18.7-1.9-27c0.8-8.3,1.6-15.7,3.1-32.7C345.4,119.3,325.9,64,256,64 c-69.9,0-89.4,55.3-87.5,76.4c1.5,16.9,2.3,24.4,3.1,32.7c0.8,8.3-5.9,9.6-1.9,27c7.6,33.1,10.4,19.3,13.9,40.1 c9,52.6,23.1,36.5,23.1,64.2c0,42.8,3.9,31.5-59.3,54.6c-64.2,23.5-79.4,38-81.1,48.9C64,421.6,64,448,64,448h192H448z"/></svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'><svg enable-background="new 0 0 91.8 92.6" id="Layer_1" version="1.0" viewBox="0 0 91.8 92.6" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M46.3,3.6c-23.5,0-42.5,19-42.5,42.5s19,42.5,42.5,42.5c23.5,0,42.5-19,42.5-42.5S69.8,3.6,46.3,3.6z M72.8,52.9H53v19.8c0,2-1.6,3.6-3.6,3.6h-6.2c-2,0-3.6-1.6-3.6-3.6V52.9H19.8c-2,0-3.6-1.6-3.6-3.6v-6.2c0-2,1.6-3.6,3.6-3.6h19.8 V19.7c0-2,1.6-3.6,3.6-3.6h6.2c2,0,3.6,1.6,3.6,3.6v19.8h19.8c2,0,3.6,1.6,3.6,3.6v6.2C76.4,51.2,74.8,52.9,72.8,52.9z" fill="#1E1E1E"/></svg>

After

Width:  |  Height:  |  Size: 697 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><defs/><g fill="none" fill-rule="evenodd" id="Icons with numbers" stroke="none" stroke-width="1"><g fill="#000000" id="Group" transform="translate(-192.000000, -192.000000)"><path d="M201,205.917042 C203.512502,205.49553 205.495527,203.512505 205.917042,201 L203,201 L203,199 L205.917042,199 C205.495527,196.487495 203.512502,194.50447 201,194.082958 L201,197 L199,197 L199,194.082958 C196.487498,194.50447 194.504473,196.487495 194.082958,199 L197,199 L197,201 L194.082958,201 C194.504473,203.512505 196.487498,205.49553 199,205.917042 L199,203 L201,203 Z M200,208 C195.581722,208 192,204.418278 192,200 C192,195.581722 195.581722,192 200,192 C204.418278,192 208,195.581722 208,200 C208,204.418278 204.418278,208 200,208 Z M200,208" id="Oval 163"/></g></g></svg>

After

Width:  |  Height:  |  Size: 992 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 64 64" height="64px" id="Layer_1" version="1.1" viewBox="0 0 64 64" width="64px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><g><path d="M52.419,15.975c0,0,1.013,1.019,1.727,0.002l1.363-1.953c0.476-0.687-0.139-1.162-0.202-1.209 l-8.265-5.775H47.04c-0.509-0.354-0.847-0.139-1.024,0.06l-0.148,0.213l-1.259,1.802c-0.006,0.007-0.71,1.119,0.416,1.707v0.001 c1.61,0.792,4.563,2.462,7.392,5.158L52.419,15.975z" fill="#241F20"/></g><g><path d="M38.512,0.071H25.488c-1.011,0-1.839,0.812-1.839,1.839v1.518c0,1.026,0.828,1.854,1.839,1.854h0.644 v1.072c0.001,1.541,0.974,1.669,1.462,1.636c0.083-0.012,0.169-0.025,0.26-0.037c0.001,0,0.013-0.003,0.013-0.003L27.866,7.95 c1.734-0.237,4.605-0.464,7.898-0.045l0.002-0.003c0,0,2.109,0.391,2.103-1.549V5.281h0.644c1.012,0,1.839-0.827,1.839-1.854V1.91 C40.351,0.884,39.523,0.071,38.512,0.071z" fill="#241F20"/></g><path d="M32,10.301c-14.808,0-26.812,12.005-26.812,26.815c0,14.807,12.004,26.812,26.812,26.812 c14.809,0,26.812-12.006,26.812-26.812C58.812,22.306,46.809,10.301,32,10.301z M33.717,37.108 c-1.575,0.002-1.709-1.094-1.717-1.41V17.155c0.046-0.645,0.381-1.86,2.248-1.546c0.037,0.005,0.072,0.009,0.111,0.014 c0.12,0.02,0.233,0.036,0.32,0.043c5.44,0.764,17.373,4.302,18.864,20.343c-0.042,0.446-0.295,1.096-1.412,1.103 C42.529,37.085,36.454,37.097,33.717,37.108z" fill="#241F20"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -104,7 +104,7 @@
$('#simulation_modal .btn-success').click(function() { $('#simulation_modal .btn-success').click(function() {
if ( !jQuery.isEmptyObject(run_simulation()) ) { if ( !jQuery.isEmptyObject(run_simulation()) ) {
self.GraphVisualization.reset(); self.GraphVisualization.reset();
$('#load').show().addClass('loader');; $('#load').show().addClass('loader');
_socket.send(run_simulation(), 'run_simulation'); _socket.send(run_simulation(), 'run_simulation');
$('.console').append('<br/>'); $('.console').append('<br/>');
} }

View File

@ -15,15 +15,15 @@ ws.onmessage = function(message) {
switch(msg['type']) { switch(msg['type']) {
case 'trials': case 'trials':
$('#load').removeClass('loader');
reset_trials(); reset_trials();
set_trials(msg['data']); set_trials(msg['data']);
// $('#load').removeClass('loader');
break; break;
case 'get_trial': case 'get_trial':
console.log(msg['data']); console.log(msg['data']);
GraphVisualization.import(convertJSON(msg['data']), function() {
$('#load').hide(); self.GraphVisualization.import(convertJSON(msg['data']), function() {
reset_configuration(); reset_configuration();
set_configuration(); set_configuration();
$('#home_menu').click(function() { $('#home_menu').click(function() {
@ -34,6 +34,7 @@ ws.onmessage = function(message) {
}); });
reset_timeline(); reset_timeline();
set_timeline(msg['data']); set_timeline(msg['data']);
$('#load').hide();
}); });
$('#charts .chart').removeClass('no-data'); $('#charts .chart').removeClass('no-data');
set_chart_nodes(msg['data'], chart_nodes) set_chart_nodes(msg['data'], chart_nodes)
@ -61,6 +62,11 @@ ws.onmessage = function(message) {
$('.console').animate({ scrollTop: $('.console')[0].scrollHeight }, 'fast'); $('.console').animate({ scrollTop: $('.console')[0].scrollHeight }, 'fast');
break; break;
case 'visualization_params':
console.log(msg['data']);
self.GraphVisualization.set_params(msg['data']['shape_property'], msg['data']['shapes'], msg['data']['colors']);
break;
default: default:
console.log('Unexpected message!') console.log('Unexpected message!')
} }
@ -103,6 +109,16 @@ var reset_trials = function() {
} }
var convertJSON = function(json) { var convertJSON = function(json) {
// For NetworkX Geometric Graphs get positions
json.nodes.forEach(function(node) {
var scx = d3.scale.linear().domain([0, 1]).range([0, height]);
var scy = d3.scale.linear().domain([0, 1]).range([height, 0]);
if ( node.pos ) {
node.scx = scx(node.pos[0]);
node.scy = scy(node.pos[1]);
}
delete node.pos;
});
json.links.forEach(function(link) { json.links.forEach(function(link) {
link.source = json.nodes[link.source] link.source = json.nodes[link.source]
link.target = json.nodes[link.target] link.target = json.nodes[link.target]
@ -136,7 +152,7 @@ var update_statistics_table = function() {
statisticsSorted[i].split('.').pop().split('\'')[0] : statisticsSorted[i]; statisticsSorted[i].split('.').pop().split('\'')[0] : statisticsSorted[i];
$('<tr>').addClass('col-sm-12').appendTo('#percentTable > tbody'); $('<tr>').addClass('col-sm-12').appendTo('#percentTable > tbody');
$('<td>').css('background-color', self.GraphVisualization.color(statisticsSorted[i])).addClass('col-sm-1').appendTo(appendTo); $('<td>').css('background-color', self.GraphVisualization.color($('.config-item #properties').val(), statisticsSorted[i])).addClass('col-sm-1').appendTo(appendTo);
$('<td>').addClass('text-left col-sm-4').text(self.GraphVisualization.statistics[statisticsSorted[i]] + ' %').appendTo(appendTo); $('<td>').addClass('text-left col-sm-4').text(self.GraphVisualization.statistics[statisticsSorted[i]] + ' %').appendTo(appendTo);
$('<td>').addClass('text-right col-sm-6 property-name').text(propertyName).appendTo(appendTo); $('<td>').addClass('text-right col-sm-6 property-name').text(propertyName).appendTo(appendTo);
} }
@ -211,6 +227,9 @@ var set_timeline = function(graph) {
// Draw graph for the first time // Draw graph for the first time
self.GraphVisualization.update_graph($('.config-item #properties').val(), maxUnix, function() { self.GraphVisualization.update_graph($('.config-item #properties').val(), maxUnix, function() {
update_statistics_table(); update_statistics_table();
setTimeout(function() {
self.GraphVisualization.fit();
}, 100);
}); });
// 'Speed' slider // 'Speed' slider

View File

@ -82,7 +82,7 @@ var initGUI = function(model_params) {
addSliderInput(model_params[option]['label'], model_params[option]['value']); addSliderInput(model_params[option]['label'], model_params[option]['value']);
break; break;
default: default:
console.log(model_params[option]['type'] + ' not defined!'); console.log(model_params[option]['label'] + ' not defined!');
break; break;
} }
} }

View File

@ -14,7 +14,7 @@
// Private constants // Private constants
var focus_opacity = 0.1, var focus_opacity = 0.1,
radius = 8, radius = 8,
required_node = ['id', 'index', 'label', 'px', 'py', 'spells', 'weight', 'x', 'y']; required_node = ['id', 'index', 'label', 'px', 'py', 'spells', 'weight', 'x', 'y', 'pos', 'scx', 'scy'];
// Private variables // Private variables
var width, var width,
@ -35,7 +35,10 @@
data_link, // Actual link data for the graph data_link, // Actual link data for the graph
link, // Line svgs link, // Line svgs
node; // Circles for the nodes node, // Circles for the nodes
shape_property, // Property to whom the shape will be applied
shapes, // Dictionary of shapes for nodes
colors; // Dictionary of colors for nodes
Number.prototype.between = function(min, max) { Number.prototype.between = function(min, max) {
var min = (min || min === 0) ? min : Math.max(), var min = (min || min === 0) ? min : Math.max(),
@ -60,15 +63,29 @@
.attr('r', radius) .attr('r', radius)
.style('fill', function (d) { .style('fill', function (d) {
if ( Array.isArray(d[property]) ) { if ( Array.isArray(d[property]) ) {
var color_node = color(d[property][0][0]); var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) { d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = color(p[0]); if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
}); });
return color_node; return color_node;
} else { } else {
return color(d[property]); return _helpers.set_color(property, d[property]);
} }
}) })
.style('stroke', function(d) {
if (_helpers.set_shape(d[shape_property]) !== (-1))
if ( Array.isArray(d[property]) ) {
var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
});
return color_node;
} else {
return _helpers.set_color(property, d[property]);
}
else
return '#ffffff';
})
// Cancel zoom movement so you can move the node // Cancel zoom movement so you can move the node
.on('mousedown', function(d) { .on('mousedown', function(d) {
d3.event.stopPropagation(); d3.event.stopPropagation();
@ -93,16 +110,33 @@
node.attr('class', 'node') node.attr('class', 'node')
.attr('r', radius) .attr('r', radius)
.style('fill', function (d) { .style('fill', function (d) {
if (_helpers.set_shape(d[shape_property]) !== (-1)) {
return 'url(#' + _helpers.set_shape(d[shape_property]) + ')';
}
if ( Array.isArray(d[property]) ) { if ( Array.isArray(d[property]) ) {
var color_node = color(d[property][0][0]); var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) { d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = color(p[0]); if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
}); });
return color_node; return color_node;
} else { } else {
return color(d[property]); return _helpers.set_color(property, d[property]);
} }
}) })
.style('stroke', function(d) {
if (_helpers.set_shape(d[shape_property]) !== (-1))
if ( Array.isArray(d[property]) ) {
var color_node = _helpers.set_color(property, d[property][0][0]);
d[property].forEach(function(p) {
if ( time.between(p[1], p[2]) ) color_node = _helpers.set_color(property, p[0]);
});
return color_node;
} else {
return _helpers.set_color(property, d[property]);
}
else
return '#ffffff';
})
.on('dblclick', function(d) { .on('dblclick', function(d) {
d3.event.stopPropagation(); d3.event.stopPropagation();
if (d === lastFocusNode) { if (d === lastFocusNode) {
@ -139,12 +173,33 @@
}); });
}, },
push_once: function(array, item, key) { push_once: function(array, item, key) {
for (var i in array) { for (var i in array) {
if ( array[i][key] == item[key] ) return false; if ( array[i][key] == item[key] ) return false;
}
array.push(item);
return true;
},
set_color: function(property, value) {
if ( colors instanceof Array ) {
for ( var c in colors ) {
if ( colors[c][property] == value ) { return colors[c]['color']; }
}
return color(value);
} else {
return color(value);
}
},
set_shape: function(value) {
if ( shapes instanceof Object && shape_property ) {
for ( var s in shapes ) {
var str_value = (value.includes('class')) ? value.split('.').pop().split('\'')[0] : value;
if ( str_value == s ) return shapes[s];
}
return (-1);
} else {
return (-1);
}
} }
array.push(item);
return true;
}
} }
@ -170,6 +225,29 @@
glinks = groot.append('g') .attr('id', 'links'); glinks = groot.append('g') .attr('id', 'links');
gnodes = groot.append('g') .attr('id', 'nodes'); gnodes = groot.append('g') .attr('id', 'nodes');
// Add patterns for shapes
var defs = [];
for ( var i in shapes )
if (!defs.includes(shapes[i])) defs.push(shapes[i])
svg.append('defs')
.selectAll('pattern')
.data(defs)
.enter()
.append('pattern')
.attr('id', function(d, i) {
return d;
})
.attr('patternUnits', 'objectBoundingBox')
.attr('width', 1)
.attr('height', 1)
.append('image')
.attr('href', function(d) {
return window.location.protocol + '//' + window.location.host + '/img/svg/' + d + '.svg';
})
.attr('width', 16)
.attr('height', 16);
// Zoom // Zoom
zoom = d3.behavior zoom = d3.behavior
.zoom() .zoom()
@ -200,17 +278,22 @@
force.on('tick', function () { force.on('tick', function () {
link.attr('x1', function (d) { link.attr('x1', function (d) {
return d.source.x; if ( d.source.scx ) return d.source.scx;
else return d.source.x;
}).attr('y1', function (d) { }).attr('y1', function (d) {
return d.source.y; if ( d.source.scy ) return d.source.scy;
else return d.source.y;
}).attr('x2', function (d) { }).attr('x2', function (d) {
return d.target.x; if ( d.target.scx ) return d.target.scx;
else return d.target.x;
}).attr('y2', function (d) { }).attr('y2', function (d) {
return d.target.y; if ( d.target.scy ) return d.target.scy;
else return d.target.y;
}); });
node.attr('transform', function translate(d) { node.attr('transform', function translate(d) {
return 'translate(' + d.x + ',' + d.y + ')'; if ( d.scx || d.scy ) return 'translate(' + d.scx + ',' + d.scy + ')';
else return 'translate(' + d.x + ',' + d.y + ')';
}); });
}); });
@ -384,6 +467,25 @@
} }
} }
/**
* Set shapes and color of graph.
* A function that set the shapes and colors of the nodes depending on their status.
*
* @param {object} set_shapes Shapes for nodes.
* @param {object} set_colors Colors for nodes.
* @param {object} callback A function called at the end.
*/
function set_params(set_shape_property, set_shapes, set_colors, callback) {
shape_property = set_shape_property;
shapes = set_shapes;
colors = set_colors;
self.GraphVisualization.shapes = shapes;
self.GraphVisualization.colors = colors;
if (callback) { callback(); }
}
/** /**
* Adjust the graph to the whole area. * Adjust the graph to the whole area.
* A function that adjust the graph to the svg item. * A function that adjust the graph to the svg item.
@ -437,9 +539,9 @@
* @param {object} value Value. * @param {object} value Value.
* @return {object} color The color in hexadecimal. * @return {object} color The color in hexadecimal.
*/ */
function color(value) { function color(property, value) {
if (graph) { if (graph) {
return color(value); return _helpers.set_color(property, value);
} }
} }
@ -519,6 +621,7 @@
create: create, create: create,
import: importJSON, import: importJSON,
update_graph: update_graph, update_graph: update_graph,
set_params: set_params,
set_link_distance: set_link_distance, set_link_distance: set_link_distance,
fit: zoom_to_fit, fit: zoom_to_fit,
reset: reset, reset: reset,
@ -530,6 +633,8 @@
// Getters // Getters
color: color, color: color,
shapes: shapes,
colors: colors,
get_attributes: get_attributes, get_attributes: get_attributes,
get_nodes: get_nodes, get_nodes: get_nodes,

View File

@ -1,4 +1,5 @@
import os import os
import networkx as nx
from server import VisualizationElement from server import VisualizationElement
from soil.simulation import SoilSimulation from soil.simulation import SoilSimulation
from xml.etree import ElementTree from xml.etree import ElementTree
@ -22,7 +23,16 @@ class Model():
print('Dumping results to {} : {}'.format(sim.dir_path, sim.dump)) print('Dumping results to {} : {}'.format(sim.dir_path, sim.dump))
return sim.run_simulation() simulation_results = sim.run_simulation()
G = simulation_results[0].history_to_graph()
for node in G.nodes():
if 'pos' in G.node[node]:
G.node[node]['viz'] = {"position": {"x": G.node[node]['pos'][0], "y": G.node[node]['pos'][1], "z": 0.0}}
del (G.node[node]['pos'])
nx.write_gexf(G, 'test.gexf', version='1.2draft')
return simulation_results
def reset(self): def reset(self):
pass pass