0

I need to change attributes of nodes and edges over time. The time is split into timeperiods, every timeperiod looks the same: Check every node and edge for possible changes and edit attribute if necessary. Specifically there are numeric attributes and the size of a node and the width of an edge are based on the attributes value. Initially the graph displays correctly. The nodes and edges have the supposed size. But changing the attribute values dynamically over time does not change the elements sizes. How can I make sure, attribute changes also change the graphs visualisation?

As far as I understand the Graphstream Docs and tutorials there are sources, sinks and sipes (a pipe is both a source and a sink). Sources create events, sinks consumes them. I use the GridGenerator which is a source. I can add the graph as a sink and let the generator create the graph. I think, I have to add a sink to the graph then, because changing attributes of elements of the graph makes it a source. But what do I use as sink? graph.display() returns a Viewer but I can't add it as sink, it says it's not compatible with the arguments for graph.addSink(sink). Even though the Graphstream Docs says that a Viewer is a sink and that the Viewer gets added automatically as a sink. Why do I don't see changes in the UI then? I don't get it.

After generating the graph the nodes and edges get there attributes

public static void configureElements(Graph world) {
  for (Node node : world.getEachNode()) {
    double random = Math.random() * 100;
    if (random < 20) {
      // remove obstacles
      world.removeNode(node)
    } else if (random < 30) {
      // node have rohstoffe
      node.addAttribute("ui.class", "rohstoff");
      node.addAttribute("ui.label", node.getId()); 
      node.addAttribute("isRohstoff");
      int capacity = (int) (Math.random() * maxCapacity);
      node.addAttribute("capacity", capacity);ity);
      // nodes size is based on capacity of rohstoffe
      node.setAttribute("ui.size", node.getNumber("capacity") + 10);
    } else if (random < 32) {
      // node is a lager
      node.addAttribute("ui.class", "lager");
      node.addAttribute("ui.label", node.getId());
      node.addAttribute("isLager");
      node.addAttribute("lagerstand", 0);
      // nodes size is based on capacity of the lager
      node.setAttribute("ui.size", node.getNumber("lagerstand") + 10);
    } else {
      // normal node
      node.addAttribute("isNode");
    }
  }

  for (Edge edge : world.getEachEdge()) {
    // add pheromones to edge
    edge.addAttribute("pheromones", 0);
    // edges size is based on number of pheromones
    edge.setAttribute("ui.size", edge.getNumber("pheromones"));
  }
}

Here I change the node attribute dynamically over time

public void dropRohstoff(Node node) {
  int oldRohstoff = (int) node.getNumber("rohstoff");
  int newRohstoff = oldRohstoff++;
  node.setAttribute("rohstoff", newRohstoff);
  world.nodeAttributeChanged(world.getId(), (long) world.getStep(), node.getId(),"rohstoff", oldRohstoff, newRohstoff);
}

public void pickRohstoff(Node node) {
  int oldCapacity = (int) node.getNumber("capacity");
  int newCapicity = oldCapacity++;
  node.setAttribute("capacity", newCapicity);
  world.nodeAttributeChanged(world.getId(), (long) world.getStep(), node.getId(), "capacity", oldCapacity, newCapicity);
}

Here the edge attributes

public void evaporateAll() {
  for (Edge edge : world.getEachEdge()) {
    Double oldEvaporateRate = edge.getNumber("pheromones");
    Double newEvaporateRate = oldEvaporateRate * (1.0 - evaporateRate);
    edge.setAttribute("pheromones", newEvaporateRate);
    world.edgeAttributeChanged(world.getId(), (long) world.getStep(), edge.getId(), "pheromones", oldEvaporateRate, newEvaporateRate);
  }
}

Does anybody know how do I have to add the sink? Or am I missing something else?

0x4b50
  • 629
  • 6
  • 18
  • I did a mistake. Further informations [here](https://github.com/graphstream/gs-core/issues/320). – 0x4b50 Apr 26 '19 at 14:05

1 Answers1

0

First, you can have a ViewerPipe with your viewer like that :

ViewerPipe pipeIn = viewer.newViewerPipe();

With that, you can add a sink to your graph :

pipeIn.addAttributeSink( graph );
pipeIn.pump();

Also, if you want to make a dynamic size, don't forget to add the css property to your nodes :

size-mode: dyn-size;

Here a minimal example for graphstream 1.3

public class Issue {
    public static void main(String args[]) {
        System.setProperty( "org.graphstream.ui.renderer", "org.graphstream.ui.j2dviewer.J2DGraphRenderer" );
        new Issue();
    }

    protected boolean loop = true;

    public Issue() {
        Graph graph = new MultiGraph("main graph");
        Viewer viewer = new Viewer(graph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
        ViewerPipe pipeIn = viewer.newViewerPipe();
        viewer.addView("view1", new J2DGraphRenderer());

        graph.addAttribute("ui.antialias");

        pipeIn.addAttributeSink( graph );
        pipeIn.pump();

        Node A = graph.addNode("A");
        Node B = graph.addNode("B");
        Node C = graph.addNode("C");

        graph.addEdge("AB", "A", "B", true);
        graph.addEdge("BC", "B", "C", true);
        graph.addEdge("CA", "C", "A", true);

        A.addAttribute("xyz", 0, 1, 0);
        B.addAttribute("xyz", 1, 0, 0);
        C.addAttribute("xyz", -1, 0, 0);

        A.setAttribute("ui.label", "A");
        B.setAttribute("ui.label", "B");
        C.setAttribute("ui.label", "C");

        graph.addAttribute("ui.stylesheet", styleSheet);

        float color = 0;
        float dir = 0.01f;

        float size = 20f;
        float sizeInc = 1f;

        while( loop ) {
            pipeIn.pump();
            sleep( 40 );
            A.setAttribute( "ui.size", size );

            size += sizeInc;

            if( size > 50 ) {
                sizeInc = -1f; size = 50f;
            } else if( size < 20 ) {
                sizeInc = 1f; size = 20f;
            }
            System.out.println(size);
        }

        System.exit(0);
    }

    protected void sleep( long ms ) {
        try { Thread.sleep( ms ) ; } 
        catch (InterruptedException e) { e.printStackTrace(); }
    }

    private String styleSheet = 
            "graph {"+
            "   canvas-color: white;"+
            "       fill-mode: gradient-radial;"+
            "       fill-color: white, #EEEEEE;"+
            "       padding: 60px;"+
            "   }"+
            "node {"+
            "   size-mode: dyn-size;"+
            "   shape: circle;"+
            "   size: 20px;"+
            "   fill-mode: plain;"+
            "   fill-color: #CCC;"+
            "   stroke-mode: plain;"+
            "   stroke-color: black;"+
            "   stroke-width: 1px;"+
            "}";
}

You can find an example here in graphstream 2.0 swing, here in graphstream 2.0 javafx or here in graphstream 1.3 (in scala).

H.Brahimi
  • 254
  • 1
  • 7
  • While this is a nice answer, it doesn't add a listener to the attribute events of the node. I would like to know how to add a listener for when attributes, specifically, the "ui.selected" attribute is set. – Brian Michalk Sep 27 '22 at 20:16