whd whd - 6 months ago 17
Java Question

Visualization of graph

I have simple program that simulates distributed environment. I want to visualize processor behaviour by using GraphStream library. Each processor is a thread, they do some computations, but not all the time, only when I set the

switch
variable

while(running){
if(switch)
computate();
}


I have written a class which take processorList and prepare graph visualization.
There is a function for this task

public void adjustBFSGraph2(){
for(Edge e: this.g.getEdgeSet())
{
e.clearAttributes();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(Processor p : processorList)
{
p.getNode().setAttribute("ui.label", "Pid" +" " + p.getPID() +" "+"Dist: " + p.getFieldValue("bfs") + " " + "Parent" + " " + p.getFieldValue("parent"));
if(!p.getFieldValue("parent").equals("-1"))
{
Edge e = p.getNode().getEdgeBetween(p.getFieldValue("parent"));
if(e != null)
{
e.setAttribute("ui.color", p.getColor());
e.setAttribute("ui.label", "added" + p.getPID());

}
}
}
}


My problem is that i see colors on edges only for blink of eye and then color disapear. It looks like it adds attribute
"ui.color"
and removes it in same loop round, but how is it possible?
@update
I have edited my code, now i can see edges for time specified in
thread.sleep()
after first loop, well i dont get why after clearing all attributes i can actually see them.
here is how i'm calling my function

while(true)
{ i++;
//if any processor is not runing
boolean aux = false;
while(!aux) {
for (Processor proc : s.processorList) {
aux = aux || proc.isEnabled();
}
aux = !aux;
}
s.gp.adjustBFSGraph();
Thread.sleep(5000);
for(Processor proc: s.processorList)
{
proc.enable();
}
}


When
Thread.sleep()
inside adjust function is set to less then 100 ms it starts to blinks again.

Because it may be a little unclear what i'm doing i have created smaller example

This is equivalent of my processor class

public class SomeObjectWithNode {
public Node n;
public Color c;
public SomeObjectWithNode(Node n)
{
Random rand = new Random();
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
Color randomColor = new Color(r, g, b);
c = randomColor;
this.n = n;
}

}


Here is a class witch change graph styling/ drawing it

public class TestDraw {
Vector<SomeObjectWithNode> n;
Graph g;
public TestDraw(Vector<SomeObjectWithNode> k, Graph g)
{
this.g= g;
this.n = k;
}
public void adjust()
{
Random rand = new Random();
for(Edge e: g.getEdgeSet())
{
e.clearAttributes();
}
for(SomeObjectWithNode k: n)
{
k.n.addAttribute("ui.color", k.c);
for(Edge e: k.n.getEdgeSet())
{
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
Color randomColor = new Color(r, g, b);
e.addAttribute("ui.color", randomColor);
}
}
}
}


and here is main class

public class TestGs {

public static void main(String[] args) {
Node lastNode;
TestDraw t;
Vector<SomeObjectWithNode> a = new Vector<SomeObjectWithNode>();
System.setProperty("gs.ui.renderer", "org.graphstream.ui.j2dviewer.J2DGraphRenderer");
Graph g = new SingleGraph("test1");
g.display();
g.addAttribute("ui.stylesheet", "node { fill-mode: dyn-plain; size: 10px;} edge { fill-mode: dyn-plain; size: 2px;}");
a.add(new SomeObjectWithNode(g.addNode(Integer.toString(0))));
lastNode = g.getNode("0");
for(int i = 1; i < 10; i++)
{
a.add(new SomeObjectWithNode(g.addNode(Integer.toString(i))));
g.addEdge(Integer.toString(i-1).concat(Integer.toString(i)), a.get(i).n, lastNode);
lastNode = a.get(i).n;
}
t = new TestDraw(a,g);
while(true)
{
t.adjust();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

}


When i run example we can see graph has colours only for the blink of eye instead of displaying it for time in
Thread.sleep()

Answer

The main problem is e.clearAttributes(); in method adjust(). You remove ALL attributes not only "ui.color". You can use e.setAttribute("ui.color", randomColor); instead e.addAttribute("ui.color", randomColor); and remove block with e.clearAttributes() at all (see original example number 2).

I've modified your code accordingly my comments and shared it on gist.github. Now it changes color every 2 secs.

PS: now about cause of blining. I've had short investigation of GraphStream source code (a lot of java code, sorry). AbstractElement.java (only interesting methods):

    public void clearAttributes() {
            if (attributes != null) {
                for (Map.Entry<String, Object> entry : attributes.entrySet())
                    attributeChanged(AttributeChangeEvent.REMOVE, entry.getKey(),
                            entry.getValue(), null);

                attributes.clear();
            }
        }
...
    public void setAttribute(String attribute, Object... values) {
        addAttribute(attribute, values);
    }
...
    public void addAttribute(String attribute, Object... values) {
        if (attributes == null)
            attributes = new HashMap<String, Object>(1);

        Object oldValue;
        Object value;
        if (values.length == 0)
            value = true;
        else if (values.length == 1)
            value = values[0];
        else
            value = values;

        AttributeChangeEvent event = AttributeChangeEvent.ADD;
        if (attributes.containsKey(attribute)) // In case the value is null,
            event = AttributeChangeEvent.CHANGE; // but the attribute exists.

        oldValue = attributes.put(attribute, value);
        attributeChanged(event, attribute, oldValue, value);
    }
...

As you can see setAttribute and addAttribute is really similar. Now attributeChanged() implementation in GraphicElement.java:

@Override
    protected void attributeChanged(AttributeChangeEvent event,
            String attribute, Object oldValue, Object newValue) {
        if (event == AttributeChangeEvent.ADD
                || event == AttributeChangeEvent.CHANGE) {
            if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') {
                if (attribute.equals("ui.class")) {
                    mygraph.styleGroups.checkElementStyleGroup(this);
                    // mygraph.styleGroups.removeElement( tis );
                    // mygraph.styleGroups.addElement( this );
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.label")) {
                    label = StyleConstants.convertLabel(newValue);
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.style")) {
                    // Cascade the new style in the style sheet.

                    if (newValue instanceof String) {
                        try {
                            mygraph.styleSheet.parseStyleFromString(
                                    new Selector(getSelectorType(), getId(),
                                            null), (String) newValue);
                        } catch (Exception e) {
                            logger.log(Level.WARNING, String.format("Error while parsing style for %S '%s' :", getSelectorType(), getId()), e);
                        }
                        mygraph.graphChanged = true;
                    } else {
                        logger.warning("Unknown value for style [" + newValue + "].");
                    }
                } else if (attribute.equals("ui.hide")) {
                    hidden = true;
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.clicked")) {
                    style.pushEventFor(this, "clicked");
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.selected")) {
                    style.pushEventFor(this, "selected");
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.color")) {
                    style.pushElementAsDynamic(this);
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.size")) {
                    style.pushElementAsDynamic(this);
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.icon")) {
                    mygraph.graphChanged = true;
                }
                // else if( attribute.equals( "ui.state" ) )
                // {
                // if( newValue == null )
                // state = null;
                // else if( newValue instanceof String )
                // state = (String) newValue;
                // }
            } else if (attribute.equals("label")) {
                label = StyleConstants.convertLabel(newValue);
                mygraph.graphChanged = true;
            }
        } else // REMOVE
        {
            if (attribute.charAt(0) == 'u' && attribute.charAt(1) == 'i') {
                if (attribute.equals("ui.class")) {
                    Object o = attributes.remove("ui.class"); // Not yet removed
                                                                // at
                                                                // this point !
                    mygraph.styleGroups.checkElementStyleGroup(this);
                    attributes.put("ui.class", o);
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.label")) {
                    label = "";
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.hide")) {
                    hidden = false;
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.clicked")) {
                    style.popEventFor(this, "clicked");
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.selected")) {
                    style.popEventFor(this, "selected");
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.color")) {
                    style.popElementAsDynamic(this);
                    mygraph.graphChanged = true;
                } else if (attribute.equals("ui.size")) {
                    style.popElementAsDynamic(this);
                    mygraph.graphChanged = true;
                }
            } else if (attribute.equals("label")) {
                label = "";
                mygraph.graphChanged = true;
            }
        }
    }

Note: As you can see when you clear attributes, dynamic style will be added/removed by methods popElementAsDynamic/pushElementAsDynamic. And implementations these methods in StyleGroup.java:

     /**
     * Indicate the element has dynamic values and thus cannot be drawn in bulk
     * operations. Called by the GraphicElement.
     * 
     * @param element
     *            The element.
     */
    protected void pushElementAsDynamic(Element element) {
        if (dynamicOnes == null)
            dynamicOnes = new HashSet<Element>();

        dynamicOnes.add(element);
    }

    /**
     * Indicate the element has no more dynamic values and can be drawn in bulk
     * operations. Called by the GraphicElement.
     * 
     * @param element
     *            The element.
     */
    protected void popElementAsDynamic(Element element) {
        dynamicOnes.remove(element);

        if (dynamicOnes.isEmpty())
            dynamicOnes = null;
    }

Well when we add/setAttribute we only add new elements in existed hashmap. When we use clearAttributes we create new instances HashMap for dynamicOnes. Probably in that case we have synchronization issue and that is cause of blinking.

Comments