All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.biopax.paxtools.io.sbgn.VNode Maven / Gradle / Ivy

package org.biopax.paxtools.io.sbgn;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.sbgn.bindings.Port;
import org.ivis.layout.LGraphObject;
import org.ivis.layout.LNode;
import org.ivis.layout.Updatable;
import org.ivis.layout.sbgn.SbgnProcessNode;
import org.sbgn.bindings.Glyph;
import org.sbgn.bindings.Port;
import org.sbgn.bindings.Bbox;
import org.ivis.layout.cose.CoSEGraph;



/**
 * VNode Class
 * @author: Istemi Bahceci
 * */

public class VNode implements Updatable
{
    //Glyph attribute of this VNode
    public Glyph glyph;
    public int clusterID;

    ArrayList  stateGlyphs;
    ArrayList  infoGlyphs;

    /*Glyph class types*/
    private static final String  MACROMOLECULE = "macromolecule";
    private static final  String UNIT_OF_INFORMATION = "unit of information";
    private static final  String STATE_VARIABLE = "state variable";
    private static final  String SOURCE_AND_SINK = "source and sink";
    private static final  String ASSOCIATION = "association";
    private static final  String DISSOCIATION = "dissociation";
    private static final  String OMITTED_PROCESS = "omitted process";
    private static final  String UNCERTAIN_PROCESS = "uncertain process";
    private static final  String SIMPLE_CHEMICAL = "simple chemical";
    private static final  String PROCESS = "process";
    private static final  String COMPLEX = "complex";
    private static final  String AND = "and";
    private static final  String OR = "or";
    private static final  String NOT = "not";
    private static final  String PHENOTYPE = "phenotype";
    private static final  String PERTURBING_AGENT = "perturbing agent";
    private static final  String TAG = "tag";
    private static final  String NUCLEIC_ACID_FEATURE = "nucleic acid feature";
    private static final  String UNSPECIFIED_ENTITY = "unspecified entity";

    // Constats used for determining "state of information" and "unit of information" glyph widths according to
    // their labels
    private static final  int LOWERCASE_LETTER_PIXEL_WIDTH = 6;
    private static final  int UPPERCASE_LETTER_PIXEL_WIDTH = 9;
    private static final  int MAX_STATE_AND_INFO_WIDTH = 50;
    private static final  int MAX_STATE_AND_INFO_HEIGHT = 15;
    private static final  int OFFSET_BTW_INFO_GLYPHS = 5;
    private static final  int MAX_INFO_BOX_NUMBER = 4;
    private static final  int MAX_MACROMOLECULE_HEIGHT_WITH_INFO_BOXES = 25;

    /*Glyph Size Constants for layout*/
    private static Bound  SOURCE_AND_SINK_BOUND;
    private static Bound  LOGICAL_OPERATOR_BOUND;
    private static Bound  PROCESS_NODES_BOUND;
    private static Bound  MACROMOLECULE_BOUND;
    private static Bound  NUCLEIC_ACID_FEATURE_BOUND;
    private static Bound  SIMPLE_CHEMICAL_BOUND;
    private static Bound  UNSPECIFIED_ENTITY_BOUND;
    private static Bound  PHENOTYPE_BOUND;
    private static Bound  PERTURBING_AGENT_BOUND;
    private static Bound  TAG_BOUND;
    private static Bound  INFO_BOUND;
    private static Bound  STATE_BOUND;
    private static Bound  COMPLEX_BOUND;


    /**
     * Default Constructor, sets the geometry of the bounds which are attributes of this class
     * @param g glyph
     * */
    public VNode(Glyph g)
    {
        SOURCE_AND_SINK_BOUND = new Bound(15,15);
        LOGICAL_OPERATOR_BOUND = new Bound(15,15);
        PROCESS_NODES_BOUND = new Bound(15,15);

        MACROMOLECULE_BOUND = new Bound(48,20);
        NUCLEIC_ACID_FEATURE_BOUND = new Bound(50,20);

        SIMPLE_CHEMICAL_BOUND = new Bound(48,20);
        UNSPECIFIED_ENTITY_BOUND = new Bound(40,40);
        PHENOTYPE_BOUND = new Bound(50,20);
        TAG_BOUND = new Bound(50,20);
        PERTURBING_AGENT_BOUND = new Bound(50,20);
        COMPLEX_BOUND = new Bound(48,20);

        INFO_BOUND = new Bound(MAX_STATE_AND_INFO_WIDTH,MAX_STATE_AND_INFO_HEIGHT);
        STATE_BOUND = new Bound(MAX_STATE_AND_INFO_WIDTH,MAX_STATE_AND_INFO_HEIGHT);

        stateGlyphs = new ArrayList();
        infoGlyphs = new  ArrayList();

        this.glyph = g;

        this.setSizeAccordingToClass();
    }

    /**
     * Function that will take place when VNode objects will update in layout process of ChiLay
     *
     * @param lGraphObj LGraphObject for whom the update will take place.
     */
    public void update(LGraphObject lGraphObj)
    {
        if (lGraphObj instanceof CoSEGraph)
        {
            return;
        }

        LNode lNode = (LNode)lGraphObj;

        this.glyph.getBbox().setX((float) lNode.getLeft());
        this.glyph.getBbox().setY((float) lNode.getTop());
        this.placeStateAndInfoGlyphs();
    }

    /**
     * Sets the bound of this VNode by given width and height
     * @param w new width
     * @param h new height
     */
    public void setBounds(float w, float h)
    {
        this.glyph.getBbox().setW(w);
        this.glyph.getBbox().setH(h);
    }

    /**
     * Chooses a proper bound for this VNode according to its class.
     */
    public void setSizeAccordingToClass()
    {
        String glyphClass = this.glyph.getClazz();
		
		/*
		 * need to add bbox objects
		 */
        Bbox b = new Bbox();
        this.glyph.setBbox(b);

        if (glyphClass == SOURCE_AND_SINK)
        {
            setBounds(SOURCE_AND_SINK_BOUND.getWidth(), SOURCE_AND_SINK_BOUND.getHeight());
        }

        else if (glyphClass == AND || glyphClass == OR || glyphClass == NOT )
        {
            setBounds(LOGICAL_OPERATOR_BOUND.getWidth(), LOGICAL_OPERATOR_BOUND.getHeight());
        }

        else if (glyphClass == ASSOCIATION || glyphClass == DISSOCIATION || glyphClass == OMITTED_PROCESS ||
                glyphClass == UNCERTAIN_PROCESS || glyphClass  == PROCESS)
        {
            setBounds(PROCESS_NODES_BOUND.getWidth(), PROCESS_NODES_BOUND.getHeight());
        }

        else if (glyphClass == SIMPLE_CHEMICAL)
        {
            setBounds(SIMPLE_CHEMICAL_BOUND.getWidth(), SIMPLE_CHEMICAL_BOUND.getHeight());
        }

        else if (glyphClass == UNSPECIFIED_ENTITY)
        {
            setBounds(UNSPECIFIED_ENTITY_BOUND.getWidth(), UNSPECIFIED_ENTITY_BOUND.getHeight());
        }

        else if (glyphClass == MACROMOLECULE)
        {
            setBounds(MACROMOLECULE_BOUND.getWidth(), MACROMOLECULE_BOUND.getHeight());
        }

        else if (glyphClass == NUCLEIC_ACID_FEATURE)
        {
            setBounds(NUCLEIC_ACID_FEATURE_BOUND.getWidth(), NUCLEIC_ACID_FEATURE_BOUND.getHeight());
        }

        else if (glyphClass == STATE_VARIABLE)
        {
            setBounds(STATE_BOUND.getWidth(), STATE_BOUND.getHeight());
        }

        else if (glyphClass == UNIT_OF_INFORMATION)
        {
            setBounds(INFO_BOUND.getWidth(), INFO_BOUND.getHeight());
        }

        else if (glyphClass == PHENOTYPE)
        {
            setBounds(PHENOTYPE_BOUND.getWidth(), PHENOTYPE_BOUND.getHeight());
        }

        else if (glyphClass == PERTURBING_AGENT)
        {
            setBounds(PERTURBING_AGENT_BOUND.getWidth(), PERTURBING_AGENT_BOUND.getHeight());
        }

        else if (glyphClass == TAG)
        {
            setBounds(TAG_BOUND.getWidth(), TAG_BOUND.getHeight());
        }

        else if (glyphClass == COMPLEX)
        {
            setBounds(COMPLEX_BOUND.getWidth(), COMPLEX_BOUND.getHeight());
        }

        if( this.glyph.getClone() != null )
        {
            Bbox glyphBbox = this.glyph.getBbox();
            setBounds(3*glyphBbox.getW()/4, 3*glyphBbox.getH()/4);
        }

        if (glyphClass == MACROMOLECULE || glyphClass == NUCLEIC_ACID_FEATURE || glyphClass == SIMPLE_CHEMICAL || glyphClass == COMPLEX)
        {
            updateSizeForStateAndInfo();
        }

    }

    /**
     * Calculates required width according to the given list state and info glyphs of this VNode.
     * This method also previously computes the width and height of state and info glyphs
     * according to their label.
     *
     * @param  stateORinfoList list that keeps state or info glyphs of this VNode
     * @return new width that is adjusted so that all glyphs in stateORinfoList are included.
     * */
    public int calcReqWidthByStateAndInfos(List stateORinfoList)
    {
        int wholeSize = 0;
        int count = 0;

        for (Glyph tmpGlyph: stateORinfoList)
        {
            String text;

            if (tmpGlyph.getState() != null)
            {
                text = tmpGlyph.getState().getValue();

                if (tmpGlyph.getState().getVariable() != null &&
                        tmpGlyph.getState().getVariable().length() > 0)
                {
                    if(tmpGlyph.getState().getVariable() != null)
                        text += "@" + tmpGlyph.getState().getVariable();
                }
            }
            else if (tmpGlyph.getLabel() != null)
            {
                text = tmpGlyph.getLabel().getText();
            }
            else
            {
                throw new RuntimeException("Encountered an information glyph with no state " +
                        "variable (as modification boxes should have) and no label (as molecule type " +
                        "boxed should have). glyph = " + tmpGlyph);
            }

            int numOfUpper = 0;
            int numOfLower = 0;

            for (int i = 0; i < text.length(); i++)
            {
                if (Character.isLowerCase(text.charAt(i)))
                {
                    numOfLower++;

                } else
                    numOfUpper++;
            }

            Bbox b = new Bbox();
            tmpGlyph.setBbox(b);

            //Set width
            float requiredSize = numOfLower * LOWERCASE_LETTER_PIXEL_WIDTH + numOfUpper * UPPERCASE_LETTER_PIXEL_WIDTH;
            if (requiredSize < MAX_STATE_AND_INFO_WIDTH)
                tmpGlyph.getBbox().setW(requiredSize);
            else
                tmpGlyph.getBbox().setW(STATE_BOUND.width);

            //Set height
            tmpGlyph.getBbox().setH(MAX_STATE_AND_INFO_HEIGHT);

            if (count < MAX_INFO_BOX_NUMBER / 2)
                wholeSize += tmpGlyph.getBbox().getW();

            count++;

        }

        return wholeSize;
    }

    /**
     * 	If glyph attribute of this VNode object includes any "state of information" or "unit of information" glyphs, this method updates the
     *  size of VNode accordingly.
     * */
    public void updateSizeForStateAndInfo()
    {

        // Find all state and info glyphs
        for (Glyph glyph : this.glyph.getGlyph())
        {
            if (glyph.getClazz() == STATE_VARIABLE)
            {
                stateGlyphs.add(glyph);
            }
            else if (glyph.getClazz() == UNIT_OF_INFORMATION)
            {
                infoGlyphs.add(glyph);
            }
        }

        //Calculate "state of information" glyphs' sizes
        int wholeWidthOfStates = calcReqWidthByStateAndInfos(stateGlyphs);
        int wholeWidthOfInfos  = calcReqWidthByStateAndInfos(infoGlyphs);

        // Calculate  positions
        int numOfStates = stateGlyphs.size();
        int numOfInfos = infoGlyphs.size();

        //Adjust heights so that info box offsets are taken into account in layout.
        if(numOfStates > 0 || numOfInfos > 0)
            this.glyph.getBbox().setH(this.glyph.getBbox().getH()+MAX_STATE_AND_INFO_HEIGHT/2);

        //Half of the info boxes on the upper side, half on the bottom side.
        numOfStates = (numOfStates >= MAX_INFO_BOX_NUMBER/2) ? MAX_INFO_BOX_NUMBER/2 : numOfStates;
        numOfInfos  = (numOfInfos  >= MAX_INFO_BOX_NUMBER/2) ? MAX_INFO_BOX_NUMBER/2 : numOfInfos;

        float requiredWidthForStates = (numOfStates+1) * OFFSET_BTW_INFO_GLYPHS + wholeWidthOfStates;
        float requiredWidthForInfos =  (numOfInfos+1)  * OFFSET_BTW_INFO_GLYPHS + wholeWidthOfInfos;


        if (this.glyph.getBbox().getW() < requiredWidthForStates || this.glyph.getBbox().getW() < requiredWidthForInfos )
        {
            this.glyph.getBbox().setW(Math.max(requiredWidthForStates, requiredWidthForInfos));
        }
    }

    /**
     * Places state and info glyphs of this node
     * */
    public void placeStateAndInfoGlyphs()
    {
        int numOfStates = stateGlyphs.size();
        int numOfInfos = infoGlyphs.size();

        //Adjust heights so that inf obox offsets are taken into account in layout.
        if(numOfStates > 0 || numOfInfos > 0)
            this.glyph.getBbox().setH(this.glyph.getBbox().getH()-MAX_STATE_AND_INFO_HEIGHT/2);

        float parent_y_up = this.glyph.getBbox().getY()-INFO_BOUND.height/2;
        float parent_y_bot = this.glyph.getBbox().getY()+this.glyph.getBbox().getH()-INFO_BOUND.height/2;;
        float parent_x_up = this.glyph.getBbox().getX();
        float parentWidth = this.glyph.getBbox().getW();
        String parentID = this.glyph.getId();

        int usedWidth = 0;
        for (int i = 0; i < numOfStates; i++)
        {
            Glyph tmpglyph = stateGlyphs.get(i);
            if(numOfStates == 1)
            {
                tmpglyph.getBbox().setX(parent_x_up+parentWidth/2-tmpglyph.getBbox().getW()/2);
                tmpglyph.getBbox().setY(parent_y_bot);
                //set dummy id
                tmpglyph.setId(parentID + ".state." + (i+1) );
                break;
            }

            //set dummy id
            tmpglyph.setId(parentID + ".state." + (i+1) );

            tmpglyph.getBbox().setX(parent_x_up+(i+1)*OFFSET_BTW_INFO_GLYPHS + usedWidth);
            tmpglyph.getBbox().setY(parent_y_bot);

            usedWidth += tmpglyph.getBbox().getW();

        }

        usedWidth = 0;
        for (int i = 0; i < numOfInfos; i++)
        {
            Glyph tmpglyph = infoGlyphs.get(i);
            if(numOfInfos == 1)
            {
                tmpglyph.getBbox().setX(parent_x_up+parentWidth/2-tmpglyph.getBbox().getW()/2);
                tmpglyph.getBbox().setY(parent_y_up);
                //set dummy id
                tmpglyph.setId(parentID + ".info." + (i+1) );
                break;
            }

            //set dummy id
            tmpglyph.setId(parentID + ".info." + (i+1) );

            tmpglyph.getBbox().setX(parent_x_up+(i+1)*OFFSET_BTW_INFO_GLYPHS + usedWidth);
            tmpglyph.getBbox().setY(parent_y_up);

            usedWidth += tmpglyph.getBbox().getW();
        }
    }

    /**
     * Inner Class for glyph bounds
     * */
    public class Bound
    {
        public float width;
        public float height;

        public Bound(float width, float height)
        {
            this.width = width;
            this.height = height;
        }

        public float getWidth()
        {
            return width;
        }

        public void setWidth(float width)
        {
            this.width = width;
        }

        public float getHeight()
        {
            return height;
        }

        public void setHeight(float height)
        {
            this.height = height;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy