https://github.com/ialhashim/topo-blend
Revision 39b13612ebd645a65eda854771b517371f2f858a authored by ennetws on 13 March 2015, 18:17:18 UTC, committed by ennetws on 13 March 2015, 18:17:18 UTC
1 parent c702819
Raw File
Tip revision: 39b13612ebd645a65eda854771b517371f2f858a authored by ennetws on 13 March 2015, 18:17:18 UTC
Create README.md
Tip revision: 39b1361
Task.cpp
#include "DynamicGraph.h"
#include "Scheduler.h"
#include "Task.h"
#include <QGraphicsSceneMouseEvent>

#include "Synthesizer.h"
#include "weld.h"
#include "LineSegment.h"

#include "AbsoluteOrientation.h"

#include "ExportDynamicGraph.h"

using namespace NURBS;
using namespace Structure;

Task::Task( Structure::Graph * activeGraph, Structure::Graph * targetGraph, TaskType taskType, int ID )
{
	// Task properties
	this->active = activeGraph;
	this->target = targetGraph;

	this->start = 0;
	this->length = Task::DEFAULT_LENGTH;

	this->type = taskType;
	this->taskID = ID;
	this->mycolor = TaskColors[taskType];
	this->currentTime = start;
	this->isReady = false;
	this->isDone = false;

	// Visual properties
	isResizing = false;
	width = length;
	height = 17;
	setFlags( ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges );
}

QRectF Task::boundingRect() const
{
    return QRectF(0, 0, width, height);
}

void Task::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

	painter->fillRect(0,0, width, height, mycolor);

	// Highlight & shade
	painter->fillRect(0,0, width, 1, QColor(255,255,255,90));
	painter->fillRect(0,1, 1, height-1, QColor(255,255,255,90));

	painter->fillRect(0,height-1, width, 1, QColor(0,0,0,90));
	painter->fillRect(width - 1,0, 1, height, QColor(0,0,0,90));

	// Resize handles
	QColor shadeColor(0,0,0,100);
	painter->fillRect(0,0,5,height,shadeColor);
	painter->fillRect(width - 5,0,5,height,shadeColor);

	// Caption
	QString caption = node()->id.left(12);
	painter->drawText(boundingRect(), QString("%1").arg(caption), QTextOption(Qt::AlignVCenter | Qt::AlignHCenter));
}

QVariant Task::itemChange( GraphicsItemChange change, const QVariant & value )
{  
	// Glow when selected
	if( this->isSelected() )
	{
		QGraphicsDropShadowEffect * taskGlow = new QGraphicsDropShadowEffect;
		taskGlow->setColor(QColor(255,255,255,255));
		taskGlow->setBlurRadius(20);
		taskGlow->setOffset(0);
		this->setGraphicsEffect(taskGlow);

		if(node() && targetNode())
		{
			node()->vis_property["glow"] = true;
			targetNode()->vis_property["glow"] = true;
		}
	}
	else
	{
		this->setGraphicsEffect(0);

		if(node() && targetNode())
		{
			node()->vis_property["glow"] = false;
			targetNode()->vis_property["glow"] = false;
		}
	}
	
	if (change == ItemPositionChange && scene() && !isResizing) 
	{
		QPointF newPos = value.toPointF();

		newPos.setX(qMax(0.0,newPos.x()));
		newPos.setY(this->y());

		start = newPos.x();
		currentTime = 0;

		return newPos;
	}

	return QGraphicsItem::itemChange(change, value);
}

void Task::setLength( int newLength )
{
	newLength = qMax(1,newLength);

	this->length = newLength;

	// Visual
	prepareGeometryChange();
	this->width = length;
}

void Task::mouseMoveEvent( QGraphicsSceneMouseEvent * event )
{
	if(isResizing)
	{
		if(resizeDir == 0)
			setLength(event->pos().x());
		else
		{
			int deltaX = event->pos().x() - clickPos.x();
			setPos(qMax(0.0,deltaX + myOldPos.x()), myOldPos.y());
			setLength(myOldWidth - deltaX);
		}

		event->accept();
		return;
	}

	if(!(event->modifiers() & Qt::ShiftModifier))
	{
		// Select siblings in the group, if non-else selected
		bool nothingElseSelected = true;
		foreach(Task * otherTask, allOtherTasks()){
			if(otherTask->isSelected()){
				nothingElseSelected = false;
				break;
			}
		}
		if( nothingElseSelected ){
			bool needRefresh = false;
			foreach(QString member, active->groupsOf(nodeID).front()){
				Task * t = active->getNode(member)->property["task"].value<Task*>();

				if(abs(t->start - this->start) < 10){
					t->setSelected(true);
					needRefresh = true;
				}
			}
			if(needRefresh) ((Scheduler*)this->scene())->emitUpdateExternalViewer();
		}

	}

	QGraphicsItem::mouseMoveEvent(event);
}

void Task::mousePressEvent( QGraphicsSceneMouseEvent * event )
{
	if (event->button() == Qt::LeftButton)
	{
		clickPos = event->pos();
		myOldPos = pos();
		myOldWidth = width;

		int eventX = event->pos().x();

		int resizeRegion = 5;
		if(eventX < resizeRegion || eventX > width - resizeRegion)
		{
			resizeDir = (eventX > width - resizeRegion) ? 0 : 1;

			isResizing = true;
			event->accept();
			return;
		}
		//event->accept();
	}

	QGraphicsItem::mousePressEvent(event);
}

void Task::mouseReleaseEvent( QGraphicsSceneMouseEvent * event )
{
	event->accept();
	isResizing = false;

	QGraphicsItem::mouseReleaseEvent(event);
}

void Task::drawDebug()
{
	glColorQt(QColor(60,220,100));
	glPointSize(15);
	glDisable(GL_LIGHTING);

	glBegin(GL_POINTS);
	foreach(Vector3 p, debugPoints)	glVector3(p);
	glColorQt(QColor(220,100,60));
	foreach(Vector3 p, debugPoints2) glVector3(p);
	glEnd();

	vs1.draw(3);
	vs2.draw(6);

	ls1.draw();
	ls2.draw();

	glEnable(GL_LIGHTING);
}

Structure::Node * Task::node()
{
	return active->getNode(this->property["nodeID"].toString());
}

bool Task::stillWorking()
{
	return currentTime < start + length;
}

void Task::reset()
{
	isReady = false;
	currentTime = start;
}

int Task::endTime()
{
	return start + length;
}

double Task::localT( int globalTime )
{
	double t = 0;

	if(globalTime >= start)
	{
		t = qMin( 1.0, double(globalTime - start) / double(length) );
	}
	else
	{
		t = -1;
	}

	return t;
}

void Task::setStart( int newStart )
{
	start = newStart;
	currentTime = 0;
	setX(newStart);
}

Structure::Node * Task::targetNode()
{
	if(!node()->property.contains("correspond")) return NULL;
	return target->getNode( node()->property["correspond"].toString() );
}

bool Task::isActive( double t )
{
	return (t >= 0.0 && !isDone);
}

// Helper functions
NodeCoord Task::futureOtherNodeCoord( Structure::Link *link )
{
	Structure::Node *tn = targetNode();
	Structure::Link * tlink = target->getEdge( link->property["correspond"].toInt() );

	Structure::Node * tfutureOther = tlink->otherNode(tn->id);
	QString futureOtherID = tfutureOther->property["correspond"].toString();

	Structure::Node * otherNode = active->getNode(futureOtherID);
	if(otherNode->property.contains("mergedTo"))
		futureOtherID = otherNode->property["mergedTo"].toString();

	Vector4d futureOtherCoord = tlink->getCoordOther(tn->id).front();

	return qMakePair(futureOtherID, futureOtherCoord);
}

NodeCoord Task::futureLinkCoord( Structure::Link *link )
{
	NodeCoord fnc = futureOtherNodeCoord(link);
	return fnc;
}

void Task::geometryMorph( double t )
{
	if(t < 0.0 || t > 1.0) return;
	
	node()->property["localT"] = t;
	active->property["targetGraph"].setValue( target );
}

// PREPARE
void Task::prepare()
{
	if ( isReady ) return;

	// Initialize
	{
		this->start = this->x();
		this->currentTime = start;
		this->isDone = false;
		this->property["orgCtrlPoints"].setValue( node()->controlPoints() );
	}

	// Reset any modified edges
	{
		Node * n = node();
		if(n->property["edgesModified"].toBool())
		{
			QVector<Link*> edges = active->getEdges(n->id);
			foreach(Link * l, edges)
			{
				l->popState();
			}
		}
	}

	if (node()->type() == Structure::CURVE)
	{
        prepareCurve();
	}

    if (node()->type() == Structure::SHEET)
	{
        prepareSheet();
	}

	this->isReady = true;
	node()->property["isReady"] = true;
	node()->property["taskIsReady"] = true;
}

QVector<Structure::Link*> Task::filterEdges( Structure::Node * n, QVector<Structure::Link*> allEdges )
{
	// Check edge's real ownership
	Node * tn = target->getNode(n->property["correspond"].toString());
	if( tn )
	{
		QVector<Link*> keepEdges;
		foreach(Link * sl, allEdges)
		{
			Link * tl = target->getEdge( sl->property["correspond"].toInt() );

			// Skip target edges that are not related to current node
			if(!tl->hasNode(tn->id))
				continue;
			else
				keepEdges.push_back(sl);
		}
		allEdges = keepEdges;
	}

	QVector<Structure::Link*> edges;

	for(int i = 0; i < (int)allEdges.size(); i++){
		Structure::Node * otherI = allEdges[i]->otherNode(n->id);

		/*for(int j = 0; j < (int)edges.size(); j++){
			Structure::Node * otherJ = edges[j]->otherNode(n->id);
			double sumV = sumvec( otherI->geometricDiff(otherJ) ).norm();
			if(sumV < 1e-10){
				otherI = NULL;
				break;
			}
		}*/

		// Skip edges not yet made
		if( (!otherI) || ungrownNode(otherI->id) ) continue;

		if(otherI) edges.push_back(allEdges[i]);
	}

	// Bin edges by their coordinates into 4 locations (2 for curves)
	QMap< int, QVector<Structure::Link*> > bin;
	foreach(Structure::Link * l, edges){
		Vector4d coord = l->getCoord(n->id).front();
		int idx = coord[0] > 0.5 ? (coord[1] > 0.5 ? 3 : 1) : (coord[1] > 0.5 ? 2 : 0);
		bin[idx].push_back(l);
	}

	edges.clear();

	foreach( int i, bin.keys() ){
		QVector<Structure::Link*> similarEdges = bin[i];

		// Arbitrary choice for now..
		Structure::Link * furthest = similarEdges.front();

		if(edges.size() < 2) edges.push_back( furthest );
	}

	return edges;
}

QVector<Structure::Link*> Task::filteredFromTargetEdges()
{
	Node *tn = targetNode();
	QVector<Link*> all_tedges = target->getEdges(tn->id);

	// Do not consider edges with non-ready nodes
	QVector<Link*> tedges, edges;
	foreach(Link* edge, all_tedges)
	{
		Node * tother = edge->otherNode( tn->id );
		Node * other = active->getNode( tother->property["correspond"].toString() );

		// Skip not grown others
		if( all_tedges.size() > 1 && ungrownNode(other->id) )	
			continue;

		tedges.push_back(edge);		
	}
	
	// Skip all but one edge of splitting nodes
	foreach(Link * edge, tedges)
	{
		Node * other = active->getNode( edge->otherNode( tn->id )->property["correspond"].toString() );
		if(other->property["taskTypeReal"].toInt() == Task::SPLIT && !other->property["taskIsReady"].toBool()){
			if(tedges.size() > 1) 
				tedges.remove(tedges.indexOf(edge));
		}
	}

	// Find corresponding edges
	foreach(Link * edge, tedges)
	{
		Link * slink = active->getEdge( edge->property["correspond"].toInt() );

		// The edge has been removed, possibly by Task::postDone
		if(!slink)
		{
			Node * n1 = active->getNode(edge->n1->property["correspond"].toString());
			Node * n2 = active->getNode(edge->n2->property["correspond"].toString());
			Array1D_Vector4d c1 = edge->coord.front();
			Array1D_Vector4d c2 = edge->coord.back();

			// Bring it back
			slink = active->addEdge(n1, n2, c1, c2, active->linkName(n1,n2));

			// Correspond
			slink->property["correspond"] = edge->property["uid"].toInt();
			edge->property["correspond"] = slink->property["uid"].toInt();
		}

		if(slink) edges.push_back( slink );
	}

	// Sort by valence of other
	{
		QMap<Link*,int> linkValence;
		foreach(Link * l, edges){
			Node * tn = target->getNode( l->otherNode(node()->id)->property["correspond"].toString() );
			linkValence[l] = qMax(active->valence(l->otherNode(node()->id)), target->valence( tn ));
		}

		typedef QPair<int, Link*> ValenceLink;
		QList< ValenceLink > sorted = sortQMapByValue(linkValence);

		QVector<Link*> sortedEdges;
		foreach(ValenceLink vl, sorted) sortedEdges.push_back( vl.second );
		edges = sortedEdges;
	}

	// Surrounded by un-grown nodes
	if( edges.isEmpty() )
	{
		QVector<Structure::Link*> edgs = active->getEdges( nodeID );
		if(!edgs.isEmpty()) edges.push_back( edgs.front() );
	}

	return edges;
}

Structure::Link * Task::preferredEnd(Structure::Node * n, QVector<Structure::Link*> edges, Structure::Graph * g)
{
	// Pick end with more valence
	int maxValence = 0;
	Structure::Link * preferredLink = edges.front();
	foreach(Structure::Link* edge, edges){
		Structure::Node * otherNode = edge->otherNode( n->id );
		int curValence = g->valence(otherNode) * (otherNode->type() == Structure::CURVE ? 1 : 100 );
		if(curValence > maxValence){
			maxValence = curValence;
			preferredLink = edge;
		}
	}
	return preferredLink;
}


RMF::Frame Task::sheetFrame( Structure::Sheet * sheet )
{
	Vector3 corner = sheet->position(Vector4d(0,0,0,0));
	Vector3 A = sheet->position(Vector4d(0,1,0,0));
	Vector3 B = sheet->position(Vector4d(1,0,0,0));
	Vector3 C = sheet->position(Vector4d(1,1,1,1));

	Vector3 X = (A - corner).normalized();
	Vector3 Y = (B - corner).normalized();
	Vector3 Z = cross(X,Y);

	RMF::Frame frame = RMF::Frame::fromTR(Z,X);
	frame.center = (A + B + C + corner) / 4.0;
	return frame;
}

Array1D_Vector3 Task::sheetDeltas( Structure::Sheet * sheet )
{
	Array1D_Vector3 deltas;
	RMF::Frame frame = sheetFrame(sheet);
	Array1D_Vector3 controlPoints = sheet->controlPoints();
	for(int i = 0; i < (int)controlPoints.size(); i++)
		deltas.push_back( controlPoints[i] - frame.center );
	return deltas;
}

RMF::Frame Task::curveFrame( Structure::Curve * curve, bool isFlip )
{
	Vector4d zero(0,0,0,0);
	Vector4d one(1,1,1,1);
	if(isFlip) std::swap(zero,one);
	Vector3d origin = curve->position(zero);
	Vector3d X = (curve->position(one) - origin).normalized();
	Vector3d Y = orthogonalVector(X);
	RMF::Frame frame = RMF::Frame::fromRS(X,Y);
	frame.center = origin; 
	return frame;
}

// EXECUTE
void Task::execute( double t )
{	
	if( !isActive(t) ) return;

	// Range check
	t = qRanged(0.0, t, 1.0);

	currentTime = start + (t * length);

	// Make available for reconstruction
	if( t > 0.0 ) node()->property["zeroGeometry"] = false;

	// Execute task by type
	if (node()->type() == Structure::CURVE)	executeCurve( t );
	if (node()->type() == Structure::SHEET)	executeSheet( t );
	
	// Fix the other end of all edges
	executeMorphEdges( t );

	// Post execution steps
	if(t >= 1.0) postDone();

	// Record current time
	property["t"] = t;
	node()->property["t"] = t;
}

void Task::postDone()
{
	Structure::Node * n = node();
	Structure::Node * tn = targetNode();

	this->isDone = true;
	n->property["taskIsDone"] = true;

	// Remove paths for edges
	QVector<Structure::Link*> edges = active->getEdges( property["edges"].value< QVector<int> >() );

	foreach (Structure::Link* link, edges){
		QVector< GraphDistance::PathPointPair > path = link->property["path"].value< QVector< GraphDistance::PathPointPair > >();
		if(path.size()) 
			link->property.remove("path");
	}

	// Post shrinking
	if(type == SHRINK)
	{
		n->property["shrunk"] = true;
		n->property["zeroGeometry"] = true;

		// remove all edges for non cutting node after shrunk
		if (!isCutting())
		{
			foreach(Link* link, active->getEdges(nodeID))
			{
				active->removeEdge(link->n1, link->n2);
			}
		}
	}

	// Post merging
	if(n->property["taskTypeReal"].toInt() == Task::MERGE)
	{
		if( true )
		{
			// Disconnect other merged nodes
			QVector< QVector<QString> > targetGroups = target->groupsOf(tn->id);

			foreach(QString tsiblingID, targetGroups.back())
			{
				QString siblingID = target->getNode(tsiblingID)->property["correspond"].toString();
				Structure::Node * sibling = active->getNode(siblingID);

				// Ignore myself, non-merging, and already dealt with
				if(siblingID == nodeID || !(sibling->property["taskTypeReal"].toInt() == Task::MERGE)) continue;
				if(sibling->property["merged"].toBool()) continue;

				// Transfer edges
				foreach(Link* link, active->getEdges(siblingID))
				{
					QString neighbour = link->otherNode(siblingID)->id;
					if(active->getEdge(nodeID, neighbour)) 
					{
						// Replace in target
						target->getEdge(link->property["correspond"].toInt())->property["correspond"] = active->getEdge(n->id, neighbour)->property["uid"].toInt();

						// Remove shared ones
						active->removeEdge(siblingID, neighbour);
					}
					else
					{
						// Replace to node of this task
						link->replace(siblingID, n, link->getCoord(siblingID));

						// Avoid modifying this link in the future
						link->clearState();
					}
				}

				// Remove from un-grown edges
				foreach(Link * link, active->edges){
					if(link->isInState(siblingID)){
						link->clearState();
						link->property["mergedEdge"] = true;
					}
				}

				// "remove" the node from execution
				sibling->property["shrunk"] = true;
				sibling->property["zeroGeometry"] = true;
				sibling->property["taskIsDone"] = true;
				sibling->property["mergedTo"] = n->id;

				Task * siblingTask = sibling->property["task"].value<Task*>();
				siblingTask->isDone = true;
				siblingTask->property.remove("edges");
			}
		}
		
		/*
		// Fix coordinates of edges by looking at the target
		foreach(Link* link, active->getEdges(nodeID))
		{
			Link * tlink = target->getEdge(link->property["correspond"].toInt());
			Array1D_Vector4d coordMe = tlink->getCoord(n->property["correspond"].toString());
			Array1D_Vector4d coordOther = tlink->getCoordOther(n->property["correspond"].toString());

			link->setCoord(nodeID, coordMe);
			link->setCoord(link->otherNode(nodeID)->id, coordOther);
		}
		*/

		n->property["merged"] = true;
	}

	// Clean up:
	n->property.remove("path");
	n->property.remove("path2");
}

Structure::Link * Task::getCoorespondingEdge( Structure::Link * link, Structure::Graph * otherGraph )
{
	return otherGraph->getEdge(link->property["correspond"].toInt());
}

void Task::setNode( QString node_ID )
{
	property["nodeID"] = node_ID;
	this->nodeID = node_ID;

	node()->property["task"].setValue( this );
	node()->property["taskType"] = type;
	node()->property["taskIsDone"] = false;
	node()->property["taskIsReady"] = false;

	// Override default colors for split and merge
	QString snID = node()->id, tnID = targetNode()->id;

	node()->property["taskTypeReal"] = type;

	if( snID.contains("_") && !snID.contains("_null") ) 
	{
		mycolor = TaskColors[Task::SPLIT];
		node()->property["taskTypeReal"] = Task::SPLIT;
	}
	if( tnID.contains("_") && !tnID.contains("_null") ) 
	{
		mycolor = TaskColors[Task::MERGE];
		node()->property["taskTypeReal"] = Task::MERGE;
	}

	targetNode()->property["taskTypeReal"] = node()->property["taskTypeReal"];
}

std::vector<RMF::Frame> Task::smoothRotateFrame( RMF::Frame sframe, Eigen::Quaterniond & rotation, int steps )
{
	std::vector<RMF::Frame> frames;

	double stepSize = 1.0 / double(steps);

	Eigen::Quaterniond eye = Eigen::Quaterniond::Identity();

	for(double alpha = 0; alpha <= 1.0; alpha += stepSize)
	{
		Eigen::Vector3d r = sframe.r, s = sframe.s, t = sframe.t;
		r = eye.slerp(alpha, rotation) * r;
		s = eye.slerp(alpha, rotation) * s;
		t = eye.slerp(alpha, rotation) * t;

		RMF::Frame curFrame = RMF::Frame((r),(s),(t));
		curFrame.center = sframe.center;
		frames.push_back(curFrame);
	}
	return frames;
}

void Task::prepareMorphEdges()
{
	Node * n = node(), *tn = targetNode();
	QVector<Link*> allEdges = active->getEdges(n->id), edges;

	// Filter edges
	foreach(Link * l, allEdges)
	{
		Structure::Node* other = l->otherNode(n->id);

		// Skip shrunk and non-grown nodes
		if (other->property["shrunk"].toBool()) continue;
		if (other->property["taskType"].toInt() == Task::GROW && !other->property["taskIsDone"].toBool()) continue;
		
		// Skip edges that will leave me in future
		Structure::Link* tl = target->getEdge(l->property["correspond"].toInt());
		if (!tl->hasNode(tn->id)) continue;

		edges.push_back(l);
	}

	property["edges"].setValue( active->getEdgeIDs( edges ) );

	// Compute paths for all edges
	foreach(Link * link, edges)
	{
		Vector3d start = link->positionOther(n->id);
		NodeCoord futureNodeCord = futureOtherNodeCoord(link);
		Vector3d end = active->position(futureNodeCord.first, futureNodeCord.second);

		// Geodesic distances on the active graph excluding the running tasks
		QVector< GraphDistance::PathPointPair > path;
		
		QString otherOld = link->otherNode(n->id)->id;
		QString otherNew = futureNodeCord.first;

		if( otherOld == otherNew )
		{
			GraphDistance gd( active->getNode(otherOld) );
			gd.computeDistances( end, DIST_RESOLUTION );	
			gd.smoothPathCoordTo( start, path );
		}
		else
		{
			QVector<QString> exclude = active->property["activeTasks"].value< QVector<QString> >();
			GraphDistance gd( active, exclude );
			gd.computeDistances( end, DIST_RESOLUTION );	
			gd.smoothPathCoordTo( start, path );
		}

		// Check
		if(path.isEmpty()) path.push_back(GraphDistance::PathPointPair( PathPoint(futureNodeCord.first, futureNodeCord.second)));

		// End of path should be exactly as in target
		path.back() = GraphDistance::PathPointPair( PathPoint(futureNodeCord.first, futureNodeCord.second)  );

		link->setProperty("path", path);
	}
}

void Task::executeMorphEdges( double t )
{
	Node * n = node();

	QVector<Structure::Link*> edges = active->getEdges( property["edges"].value< QVector<int> >() );

	foreach (Structure::Link* link, edges)
	{
		QVector< GraphDistance::PathPointPair > path = link->property["path"].value< QVector< GraphDistance::PathPointPair > >();
		if(!path.size()) continue;

		// Local link coordinates
		int idx = t * (path.size() - 1);
		GraphDistance::PathPointPair cur = path[idx];
		Structure::Node *otherOld = link->otherNode(n->id);
		Structure::Node *otherNew = active->getNode(cur.a.first);

		// Do not perform on active nodes
		Task * otherTask = otherOld->property["task"].value<Task*>();
		if(otherTask->isReady && !otherTask->isDone) continue;

		Array1D_Vector4d newCoords = Array1D_Vector4d(1, cur.a.second);

		if(otherOld->id != otherNew->id)
			link->replace( otherOld->id, otherNew, newCoords );
		else
			link->setCoord( otherOld->id, newCoords );
	}
}

bool Task::isCrossing()
{
	Structure::Node *n = node(), *tn = targetNode();

	bool isCross = false;

	if( isReady && !isDone )
	{
		QVector<Link*> edges = active->getEdges(n->id);

		foreach(Link * l, edges)
		{
			if(l->property["modified"].toBool())
				continue;

			// check if my neighbor will change
			Structure::Link* tl = target->getEdge(l->property["correspond"].toInt());
			Structure::Node* sNb = l->otherNode(n->id);
			QString tNbID = tl->otherNode(tn->id)->id;
			if (sNb->property["correspond"].toString() != tNbID){
				isCross = true;
				break;
			}
		}
	}

	property["isCrossing"] = isCross;
	return isCross;
}

bool Task::isCutting( bool isSkipUngrown )
{
	bool result = false;

	Structure::Graph copyActive (*active);

	// Clean up
	copyActive.removeIsolatedNodes();

	// Exclude nodes that is active and non-existing
	QSet<QString> excludeNodes;

	foreach(QString nid, active->property["activeTasks"].value< QVector<QString> >())
		excludeNodes.insert(nid);

	// Skip un-grown nodes
	if( isSkipUngrown )
	{
		foreach(Node* n, active->nodes)
		{
			if ( ungrownNode(n->id) ) 
			{
				// If it cuts the graph, check if both halves have grown nodes 
				if( copyActive.isCutNode(n->id) )
				{
					int numGrownComponents = 0;

					Structure::Graph cutCheckGraph ( copyActive );
					cutCheckGraph.removeNode(n->id);

					foreach(QSet<QString> component, cutCheckGraph.connectedComponents())
					{
						bool hasGrown = false;

						foreach(QString element, component){
							if( !ungrownNode(element) ){
								hasGrown = true;
								break;
							}
						}

						if( hasGrown )
							numGrownComponents++;
					}

					if( numGrownComponents > 1 )
						continue;
				}

				excludeNodes.insert(n->id);
			}
		}
	}

	// Keep myself in graph for checking
	excludeNodes.remove(nodeID);
	foreach (QString nid, excludeNodes)	copyActive.removeNode(nid);

	result = copyActive.isCutNode(nodeID);

	return result;
}

bool Task::ungrownNode( QString nid )
{
	Task* t = active->getNode(nid)->property["task"].value<Task*>();
	return t->type == GROW && !t->isReady;
}

QVector<Task*> Task::allOtherTasks()
{
	QVector<Task*> result;
	foreach(Node * n, active->nodes){
		if(n->id != nodeID){
			result.push_back( n->property["task"].value<Task*>() );
		}
	}
	return result;
}
back to top