Skip to content
Snippets Groups Projects
GraphViewer.cpp 9.92 KiB
/*
 ------------------------------------------------------------------
 
 This file is part of the Open Ephys GUI
 Copyright (C) 2014 Open Ephys
 
 ------------------------------------------------------------------
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */

#include "GraphViewer.h"

GraphViewer::GraphViewer()
{
    
    labelFont = Font("Paragraph", 50, Font::plain);
    rootNum = 0;
}

GraphViewer::~GraphViewer()
{
    
}

void GraphViewer::addNode(GenericEditor* editor)
{
    
    GraphNode* gn = new GraphNode(editor, this);
    addAndMakeVisible(gn);
    availableNodes.add(gn);
    gn->setBounds(20, 20, 150, 50);
    updateNodeLocations();
    
}


void GraphViewer::removeNode(GenericEditor* editor)
{
    
    availableNodes.remove(indexOfEditor(editor));
    
    updateNodeLocations();

}

void GraphViewer::removeAllNodes()
{
    availableNodes.clear();

    updateNodeLocations();
    
}

// void GenericEditor::updateNodeName(GenericEditor* editor)
// {
//     GraphNode* n = getNodeForEditor(editor);
//     n->repaint();
// }

void GraphViewer::updateNodeLocations()
{
    // set the initial locations
    for (int i = 0; i < availableNodes.size(); i++)
    {

    }

    rootNum = 0;

    // do initial layout
    for (int i = 0; i < availableNodes.size(); i++)
    {
        checkLayout(availableNodes[i]);
    }

    // check for overlap
    for (int i = 0; i < availableNodes.size(); i++)
    {
        for (int j = 0; j < availableNodes.size(); j++)
        {
            if (j != i)
            {
                if (availableNodes[j]->getLevel() == availableNodes[i]->getLevel() &&
                    availableNodes[j]->getHorzShift() == availableNodes[i]->getHorzShift())
                {
                    availableNodes[j]->setHorzShift(availableNodes[j]->getHorzShift()+1);
                }
            }
        }
    }

    repaint();
}

void GraphViewer::checkLayout(GraphNode* gn)
{
 
    if (gn != nullptr)
    {

        GraphNode* sourceNode;

        if (gn->isMerger())
        {
            Array<GenericEditor*> editors = gn->getConnectedEditors();

            int level1 = 0;
            int level2 = 0;

            if (editors[0] != nullptr)
            {
                level1 = getNodeForEditor(editors[0])->getLevel();
            }

            if (editors[1] != nullptr)
            {
                level2 = getNodeForEditor(editors[1])->getLevel();
            }

           // std::cout << "LEVEL1 = " << level1 << " LEVEL2 = " << level2 << std::endl;

            sourceNode = level1 > level2 ? getNodeForEditor(editors[0]) : 
                                           getNodeForEditor(editors[1]); // choose the higher source

        } else {
            sourceNode = getNodeForEditor(gn->getSource());
        }

        if (sourceNode == nullptr)
        {
            gn->setLevel(0);
            gn->setHorzShift(rootNum);
            rootNum++;
        } else if (sourceNode->isSplitter()) 
        {
            Array<GenericEditor*> editors = sourceNode->getConnectedEditors();

            if (gn->hasEditor(editors[1]))
            {
                gn->setLevel(sourceNode->getLevel()+1); // increase level
                gn->setHorzShift(sourceNode->getHorzShift()+1); // increase horz shift
            } else {
                 gn->setLevel(sourceNode->getLevel()+1); // increase level
                gn->setHorzShift(sourceNode->getHorzShift()); // same horz shift
            }

        } else {

            gn->setLevel(sourceNode->getLevel()+1); // increase level
            gn->setHorzShift(sourceNode->getHorzShift()); // same horz shift
        }

        checkLayout(getNodeForEditor(gn->getDest()));

    }

}

int GraphViewer::indexOfEditor(GenericEditor* editor)
{
    int index = -1;
    
    for (int i = 0; i < availableNodes.size(); i++)
    {
        if (availableNodes[i]->hasEditor(editor))
        {
            return i;
        }
    }
    
    return index;
}

GraphNode* GraphViewer::getNodeForEditor(GenericEditor* editor)
{
    int index = indexOfEditor(editor);

    if (index > -1)
        return availableNodes[index];
    else
        return nullptr;
}

int GraphViewer::nodesAtLevel(int level)
{

    int numNodes;

    for (int i = 0; i < availableNodes.size(); i++)
    {
        if (availableNodes[i]->getLevel() == numNodes)
        {
            numNodes++;
        }
    }

    return numNodes;

}

int GraphViewer::getHorizontalShift(GraphNode* gn)
{
    int shift = 0;

    for (int i = 0; i < availableNodes.size(); i++)
    {
        if (availableNodes[i] == gn)
        {
            break;
        } else
        {
            if (availableNodes[i]->getLevel() == gn->getLevel())
            {
                shift++;
            }
        }
    }

    return shift;

}


void GraphViewer::paint(Graphics& g)
{
    g.fillAll(Colours::darkgrey);
    
    g.setFont(labelFont);
    g.setFont(50);
    
    g.setColour(Colours::grey);

    JUCEApplication* app = JUCEApplication::getInstance();
    String text = "GUI version ";
    text += app->getApplicationVersion();
    
    g.drawFittedText("open ephys", 40, 40, getWidth()-50, getHeight()-60, Justification::bottomRight, 100);
    
    g.setFont(Font("Small Text", 14, Font::plain));
    g.drawFittedText(text, 40, 40, getWidth()-50, getHeight()-45, Justification::bottomRight, 100);
    
    // draw connections

    for (int i = 0; i < availableNodes.size(); i++)
    {

        if (!availableNodes[i]->isSplitter())
        {
            if (availableNodes[i]->getDest() != nullptr)
            {
                int indexOfDest = indexOfEditor(availableNodes[i]->getDest());
                
                if (indexOfDest > -1)
                    connectNodes(i, indexOfDest, g);
            }
        } else {

            Array<GenericEditor*> editors = availableNodes[i]->getConnectedEditors();

            for (int path = 0; path < 2; path++)
            {
                int indexOfDest = indexOfEditor(editors[path]);
                    
                if (indexOfDest > -1)    
                    connectNodes(i, indexOfDest, g);
            }
            

        }

    }
}

void GraphViewer::connectNodes(int node1, int node2, Graphics& g)
{

    Point<float> start = availableNodes[node1]->getCenterPoint();
    Point<float> end = availableNodes[node2]->getCenterPoint();

    Path linePath;
    float x1 = start.getX();
    float y1 = start.getY();
    float x2 = end.getX();
    float y2 = end.getY();
    linePath.startNewSubPath (x1, y1);
    linePath.cubicTo (x1, y1 + (y2 - y1) * 0.9f,
                      x2, y1 + (y2 - y1) * 0.1f,
                      x2, y2);
    
    PathStrokeType stroke (2.0f);
    g.strokePath(linePath, stroke);
}

/// ------------------------------------------------------

GraphNode::GraphNode(GenericEditor* ed, GraphViewer* g)
{
    editor = ed;
    mouseOver = false;
    labelFont = Font("Paragraph", 14, Font::plain);

    gv = g;
}

GraphNode::~GraphNode()
{
    
}

int GraphNode::getLevel()
{
    // int level = -1;

    // GenericEditor* ed = editor;

    // while (ed != nullptr)
    // {
    //     level += 1;
    //     ed = ed->getSourceEditor();
    // }

    return vertShift;
}

void GraphNode::setLevel(int level)
{
    setBounds(getX(), 20+level*40, getWidth(), getHeight());
    
    vertShift = level;
}

int GraphNode::getHorzShift()
{
    return horzShift; //gv->getHorizontalShift(this);
}

void GraphNode::setHorzShift(int shift)
{
    setBounds(20+shift*140, getY(), getWidth(), getHeight());
    horzShift = shift;
}
    
void GraphNode::mouseEnter(const MouseEvent& m)
{
    mouseOver = true;
    repaint();
}
void GraphNode::mouseExit(const MouseEvent& m)
{
    mouseOver = false;
    repaint();
}
void GraphNode::mouseDown(const MouseEvent& m)
{
    editor->makeVisible();
}
    
bool GraphNode::hasEditor(GenericEditor* ed)
{
    if (ed == editor)
        return true;
    else
        return false;
}

bool GraphNode::isSplitter()
{
    return editor->isSplitter();
}

bool GraphNode::isMerger()
{
    return editor->isMerger();
}


GenericEditor* GraphNode::getDest()
{
    return editor->getDestEditor();
}

GenericEditor* GraphNode::getSource()
{
    return editor->getSourceEditor();
}

Array<GenericEditor*> GraphNode::getConnectedEditors()
{
    return editor->getConnectedEditors();
}

const String GraphNode::getName()
{
    return editor->getDisplayName();
}

Point<float> GraphNode::getCenterPoint()
{
    Point<float> center = Point<float>(getX()+10, getY()+10);

    return center;
}

void GraphNode::switchIO(int path)
{

}

void GraphNode::updateBoundaries()
{

    int level = getLevel();

    int horzShift = gv->getHorizontalShift(this);
    
    setBounds(20+horzShift*140, 20+getLevel()*40, 150, 50);

}

void GraphNode::paint(Graphics& g)
{

    if (mouseOver)
    {
        g.setColour(Colours::yellow);
        g.fillEllipse(0,0,20,20);
    } else {
        g.setColour(Colours::lightgrey);
        g.fillEllipse(1,1,18,18);
    
    }
    
    g.drawText(getName(), 25, 0, getWidth()-25, 20, Justification::left, true);
    
}