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);
}