Skip to content

Commit

Permalink
progress fast exploration
Browse files Browse the repository at this point in the history
  • Loading branch information
YouGuessedMyName committed Feb 1, 2025
1 parent b0b5be9 commit 41b7136
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 308 deletions.
423 changes: 165 additions & 258 deletions docs/getting_started/01_study.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/getting_started/layouts/pinkgreen.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"enable_physics": true,
"width": 800,
"height": 600,
"explore": false
"explore": true
},
"saving": {
"relative_path": true,
Expand Down
29 changes: 1 addition & 28 deletions docs/getting_started/study.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

<iframe
id="studysFvnWfhilS"
id="studymrkLMSGkNb"
width="820"
height="620"
frameborder="0"
Expand Down Expand Up @@ -217,33 +217,6 @@
edges: edges,
};
var network = new vis.Network(container, data, options);
function makeAllNodesInvisible() {
ids = nodes.getIds();
for (let i = 0; i &lt; ids.length; i++) {
var nodeId = ids[i];
var node = nodes.get(nodeId);
node[&quot;hidden&quot;] = true;
nodes.update(node);
}
};
function makeNeighborsVisible(myNode) {
var ids = network.getConnectedNodes(myNode, &#x27;to&#x27;);
for (let i = 0; i &lt; ids.length; i++) {
var nodeId = ids[i];
var node = nodes.get(nodeId);
node[&quot;hidden&quot;] = false;
nodes.update(node);
}
};
function makeNodeVisible(nodeId) {
var node = nodes.get(nodeId);
node[&quot;hidden&quot;] = false;
nodes.update(node);
};
network.on( &#x27;click&#x27;, function(properties) {
var nodeId = network.getNodeAt({x:properties.event.srcEvent.offsetX, y:properties.event.srcEvent.offsetY});
makeNeighborsVisible(nodeId);
});
&lt;/script&gt;
&lt;/body&gt;
Expand Down
17 changes: 13 additions & 4 deletions stormvogel/html_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@
edges: edges,
};
var network = new vis.Network(container, data, options);
// network.on( 'click', function(properties) {
// var nodeId = network.getNodeAt({x:properties.event.srcEvent.offsetX, y:properties.event.srcEvent.offsetY});
// alert(nodeId);
// });
"""

NETWORK_JS_OLD = """//js
var container = document.getElementById("mynetwork");
var data = {
nodes: nodes,
edges: edges,
};
var network = new vis.Network(container, data, options);
function makeAllNodesInvisible() {
ids = nodes.getIds();
for (let i = 0; i < ids.length; i++) {
Expand All @@ -56,8 +69,4 @@
node["hidden"] = false;
nodes.update(node);
};
network.on( 'click', function(properties) {
var nodeId = network.getNodeAt({x:properties.event.srcEvent.offsetX, y:properties.event.srcEvent.offsetY});
makeNeighborsVisible(nodeId);
});
"""
1 change: 1 addition & 0 deletions stormvogel/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def show(
e.show()
box = widgets.HBox(children=[vis.output, e.output])
ipd.display(box)
vis.after_show()
else: # Unfortunately, the sphinx docs only work if we save the html as a file and embed.
iframe = vis.nt.generate_iframe()
with open(name + ".html", "w") as f:
Expand Down
58 changes: 44 additions & 14 deletions stormvogel/visjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
spam: widgets.Output = widgets.Output()


@dataclass(frozen=True)
@dataclass
class Node:
id: int
label: str | None
Expand Down Expand Up @@ -76,15 +76,40 @@ def __init__(
self.positions = positions
self.nodes: dict[int, Node] = {}
self.edges: dict[int, set[Edge]] = {}
self.reverse_edges: dict[int, set[Edge]] = {}

def enable_exploration_mode(self):
"""Make the initial node visible, and inject the JS that enables exploration by clicking."""
"""Inject the JS that enables exploration by clicking."""
js = f"""//js
{self.content_window}.network.on( 'click', function(properties) {{
var nodeId = {self.content_window}.network.getNodeAt({{x:properties.event.srcEvent.offsetX, y:properties.event.srcEvent.offsetY}});
if (! (nodeId === undefined)) {{
FUNCTION(nodeId);
}}
}});"""
self.server.add_event(js, self.make_neighbors_visible)

def make_neighbors_visible(self, id: str) -> None:
"""Make the neighbors of this node visible."""
print("explore", id)
for edge in self.edges[int(id)]:
print(edge.to_)
self.make_node_visible(edge.to_, True)

def make_node_visible(self, id):
"""Make the node with this id visible."""
def make_node_visible(self, id: int, two: bool = False) -> None:
"""Make the node with this id visible, i.e. adding it to the JS network.
Add incoming edges if the source node is visible and outgoing edges if the target node is visible."""
print("make visible", id, type(id))
node = self.nodes[id]
node.visible = True
js = f"""alert('{id}');"""
if two:
js = f"""{self.content_window}.nodes.update({self.__create_node_code(node)});"""
print(js)
ipd.display(ipd.Javascript(js))

def get_positions(self) -> dict:
"""Get the current positions of the nodes on the canvas. Returns empty dict if unsucessful.
"""Get the current positions of the nodes on the canvas. Returns empty dict if unsuccessful.
Example result: {"0": {"x": 5, "y": 10}}"""
if self.server is None:
with self.debug_output:
Expand All @@ -104,17 +129,17 @@ def get_positions(self) -> dict:
logging.warning("Timed out. Could not retrieve position data.")
raise TimeoutError("Timed out. Could not retrieve position data.")

def __add_node_pre(self, node: Node) -> None:
"""Add the node to the pre-creation Javascript."""
def __create_node_code(self, node: Node) -> str:
"""Generate the code to add the node to the pre-creation Javascript."""
current = "{ id: " + str(node.id)
if node.label is not None:
current += f", label: `{node.label}`"
if node.group is not None:
current += f', group: "{node.group}"'
if self.positions is not None and str(node.id) in self.positions:
current += f', x: {self.positions[str(node.id)]["x"]}, y: {self.positions[str(node.id)]["y"]}'
current += " },\n"
self.nodes_js += current
current += " }"
return current

def add_node(
self,
Expand All @@ -127,15 +152,15 @@ def add_node(
node = Node(id, label, group, visible)
self.nodes[id] = node
if visible:
self.__add_node_pre(node)
self.nodes_js += self.__create_node_code(node) + ",\n"

def add_edge_pre(self, edge: Edge):
def __create_edge_code(self, edge: Edge) -> str:
"""Add the edge to the pre-creation Javascript."""
current = "{ from: " + str(edge.from_) + ", to: " + str(edge.to_)
if edge.label is not None:
current += f', label: "{edge.label}"'
current += " },\n"
self.edges_js += current
return current

def add_edge(
self,
Expand All @@ -148,9 +173,14 @@ def add_edge(
if from_ in self.edges:
self.edges[from_].add(edge)
else:
self.edges[from_] = set()
self.edges[from_] = {edge}
if to_ in self.reverse_edges:
self.reverse_edges[to_].add(edge)
else:
self.reverse_edges[to_] = {edge}

if self.nodes[from_].visible and self.nodes[from_].visible:
self.add_edge_pre(edge)
self.edges_js += self.__create_edge_code(edge)

def set_options(self, options: str) -> None:
"""Set the options. Only use before calling show."""
Expand Down
13 changes: 10 additions & 3 deletions stormvogel/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Note to future maintainers: The way that IPython display behaves is very flakey sometimes.
# If you remove a with output: statement, everything might just break, be prepared.

from time import sleep
import stormvogel.model
import stormvogel.layout
import stormvogel.result
Expand Down Expand Up @@ -104,9 +105,6 @@ def show(self) -> None:
with self.output:
ipd.clear_output()
self.__create_nt()
if self.layout.layout["misc"]["explore"]:
self.nt.enable_exploration_mode()
self.nt.make_node_visible(self.model.get_initial_state().id)
self.layout.set_groups(self.separate_labels)
self.__add_states()
self.__add_transitions()
Expand All @@ -116,6 +114,14 @@ def show(self) -> None:
self.nt.show()
self.maybe_display_output()

def after_show(self) -> None:
"""Initialization that needs to be called after the network is shown."""
sleep(1) # Delay because JS needs some time to load.
if self.layout.layout["misc"]["explore"]:
self.nt.enable_exploration_mode()
# sleep(3)
# self.nt.make_node_visible(self.model.get_initial_state().id, two=True)

def update(self) -> None:
"""Tries to update an existing visualization to apply layout changes WITHOUT reloading. If show was not called before, nothing happens."""
if self.nt is not None:
Expand Down Expand Up @@ -184,6 +190,7 @@ def __add_transitions(self) -> None:
id=action_id,
label=",".join(action.labels) + reward,
group=group,
visible=not self.layout.layout["misc"]["explore"],
)
# Add transition from this state TO the action.
self.nt.add_edge(state_id, action_id) # type: ignore
Expand Down

0 comments on commit 41b7136

Please sign in to comment.