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
TaskSheet.cpp
#include "TaskSheet.h"
#include "AbsoluteOrientation.h"

#include "StructureCurve.h"

using namespace Structure;

void TaskSheet::prepareSheet()
{
    switch(type)
    {
    case GROW:
    case SHRINK:
        prepareGrowShrinkSheet();
        break;
    case SPLIT:
    case MERGE:
    case MORPH:
        prepareMorphSheet();
        break;
    }
}

void TaskSheet::executeSheet(double t)
{
    switch(type)
    {
    case GROW:
    case SHRINK:
        executeGrowShrinkSheet(t);
        break;
    case MORPH:
		{
			if( property["isCrossing"].toBool() )
				executeCrossingSheet(t);
			else
				executeMorphSheet(t);
		}
        break;
    }
}

void TaskSheet::prepareSheetOneEdge( Structure::Link * l )
{
    Structure::Node * n = node(), *tn = targetNode();
    Structure::Node * base = l->otherNode(n->id);
    Structure::Sheet* structure_sheet = ((Structure::Sheet*)n);
	Vector4d coord = l->getCoord(n->id).front();
	Vector4d otherCoord = l->getCoordOther(n->id).front();

    // Placement:
    structure_sheet->moveBy( base->position(otherCoord) - n->position(coord) );

    // Sheet folding:
	Structure::Sheet targetCopy (*((Structure::Sheet*)tn));
    Array2D_Vector3 deltas = targetCopy.foldTo( Array1D_Vector4d(1, coord), (this->type == GROW) );
    if (this->type != GROW) deltas = inverseVectors3(deltas);

    // Growing / shrinking instructions
    property["deltas"].setValue( deltas );
    property["orgCtrlPoints"].setValue( structure_sheet->surface.mCtrlPoint );
}

void TaskSheet::prepareSheetTwoEdges( Structure::Link * linkA, Structure::Link * linkB )
{
    Structure::Node * n = node();
	Structure::Node * tn = targetNode();

	Structure::Link * tlinkA = target->getEdge( linkA->property["correspond"].toInt() );
	Structure::Link * tlinkB = target->getEdge( linkB->property["correspond"].toInt() );

    Vector3d pointA = linkA->positionOther(n->id);
    Vector3d pointB = linkB->positionOther(n->id);

    // Geodesic distance between two link positions on the active graph excluding the running tasks
    QVector<QString> exclude = active->property["activeTasks"].value< QVector<QString> >();
    GraphDistance gd( active, exclude );
    gd.computeDistances( pointA, DIST_RESOLUTION );
    QVector< GraphDistance::PathPointPair > path;
    gd.smoothPathCoordTo(pointB, path);

    // Otherwise, self expand / contract
    if(path.size() < 3)
    {
        QVector<Link*> twoEdges(2);
        twoEdges[0] = linkA; twoEdges[1] = linkB;
		Structure::Link * pickedLink = preferredEnd(n, twoEdges, active);
        prepareSheetOneEdge( pickedLink );

		QVector<Link*> edges; edges.push_back(pickedLink);
		property["edges"].setValue( active->getEdgeIDs(edges) );
        return;
    }

    // Use the center of the path as the start point
    GraphDistance::PathPointPair startPointCoord = path[path.size() / 2];
    Vector3d startPoint = startPointCoord.position( active );

    // Separate the path into two for linkA and linkB
    int N = path.size(), hN = N / 2;
    if (N %2 == 0) path.insert(hN, path[hN]);

    QVector<GraphDistance::PathPointPair> pathA, pathB;
    for (int i = 0; i < hN; i++)
    {
        pathA.push_back(path[hN+1+i]);
        pathB.push_back(path[hN-1-i]);
    }

    // Record path
    property["pathA"].setValue( pathA );
    property["pathB"].setValue( pathB );

    // Encode sheet on a line segment
	RMF rmf( GraphDistance::positionalPath(active, pathA, 1) );
	property["rmf"].setValue( rmf );
	if(!rmf.count()) return;

	Vector3 X = rmf.U.back().r, Y = rmf.U.back().s, Z = rmf.U.back().t;

    SheetEncoding cpCoords = encodeSheetAsCurve((Structure::Sheet*)tn, tlinkA->position(tn->id), tlinkB->position(tn->id));
	property["cpCoords"].setValue( cpCoords );

    // DEBUG
    node()->property["rmf"].setValue( rmf );
    node()->property["rmf2"].setValue( RMF ( GraphDistance::positionalPath(active, pathB, 3) ) );

    if(this->type == GROW)
    {
        // Initial position and geometry
        n->setControlPoints( Array1D_Vector3(cpCoords.size(), startPoint) );
    }
}

void TaskSheet::prepareGrowShrinkSheet()
{
    Structure::Node * n = node();
    QVector<Structure::Link*> edges = filterEdges(n, active->getEdges(n->id));

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

    if (edges.size() == 1)
    {
        prepareSheetOneEdge( edges.front() );
    }

    if (edges.size() == 2)
    {
        prepareSheetTwoEdges( edges.front(), edges.back() );
    }
}

void TaskSheet::executeGrowShrinkSheet(double t)
{
	Structure::Sheet* structure_sheet = ((Structure::Sheet*)node());
	QVector<Link*> edges = active->getEdges( property["edges"].value< QVector<int> >() );

	/// Single edge case
	if ( property.contains("deltas") )
	{
		Array2D_Vector3 cpts = property["orgCtrlPoints"].value<Array2D_Vector3>();
		Array2D_Vector3 deltas = property["deltas"].value<Array2D_Vector3>();

		// Grow sheet
		for(int u = 0; u < structure_sheet->surface.mNumUCtrlPoints; u++)
			for(int v = 0; v < structure_sheet->surface.mNumVCtrlPoints; v++)
				structure_sheet->surface.mCtrlPoint[u][v] = cpts[u][v] + (deltas[u][v] * t);
	}

	/// Two edges case
	if( property.contains("pathA") && property.contains("pathB") && property.contains("cpCoords") )
	{
		QVector< GraphDistance::PathPointPair > pathA = property["pathA"].value< QVector< GraphDistance::PathPointPair > >();
		QVector< GraphDistance::PathPointPair > pathB = property["pathB"].value< QVector< GraphDistance::PathPointPair > >();
		if(pathA.size() == 0 || pathB.size() == 0)	return;

		double dt = t;
		if(type == SHRINK) dt = 1 - t;

		double decodeT = qMin(1.0, dt * 2.0);

		int idxA = dt * (pathA.size() - 1);
		int idxB = dt * (pathB.size() - 1);

		// Move to next step
		Vector3 pointA = pathA[idxA].position(active);
		Vector3 pointB = pathB[idxB].position(active);

		Structure::Link *linkA = edges.front(), *linkB = edges.back();
		if (type == GROW){
			linkA = target->getEdge(linkA->property["correspond"].toInt());
			linkB = target->getEdge(linkB->property["correspond"].toInt());
		}

		Vector3d deltaA = linkA->property["delta"].value<Vector3d>() * dt;
		Vector3d deltaB = linkB->property["delta"].value<Vector3d>() * dt;

		Array1D_Vector3 decoded = Curve::decodeCurve(property["cpCoords"].value<CurveEncoding>(), pointA + deltaA, pointB + deltaB, decodeT);
		structure_sheet->setControlPoints( decoded );
	}
}


void TaskSheet::prepareMorphSheet()
{
	Structure::Node * n = node();
	Structure::Node * tn = targetNode();

	// Check if the sheet is crossing 
	{
		QVector<Structure::Link*> edges = filterEdges(n, active->getEdges(n->id));

		// check if neighbors on selected edges will change
		foreach(Link * l, edges){
			if(l->property["modified"].toBool()) continue;
			Structure::Link* tl = target->getEdge(l->property["correspond"].toInt());
			if (l->otherNode(n->id)->property["correspond"].toString() != tl->otherNode(tn->id)->id)
				property["isCrossing"] = true;
		}
	}

	// Morph parameters
	{
		Structure::Sheet * sheet = (Structure::Sheet *)n;
		Structure::Sheet * tsheet = (Structure::Sheet *)tn;

		// Get source and target frames
		RMF::Frame sframe = sheetFrame( sheet );
		RMF::Frame tframe = sheetFrame( tsheet );

		// Compute rotations between source and target sheet frames
		Eigen::Quaterniond rotation, eye = Eigen::Quaterniond::Identity();
		AbsoluteOrientation::compute(sframe.r,sframe.s,sframe.t,
			tframe.r,tframe.s,tframe.t, rotation);

		// Parameters needed for morphing
		QVector<double> rot; 
		rot.push_back(rotation.w());
		rot.push_back(rotation.x());
		rot.push_back(rotation.y());
		rot.push_back(rotation.z());

		property["rotation"].setValue( rot );
		property["sframe"].setValue( sframe );
		property["tframe"].setValue( tframe );

		property["cpCoords"].setValue( Structure::Sheet::encodeSheet(sheet, sframe.center, sframe.r,sframe.s,sframe.t) );
		property["cpCoordsT"].setValue( Structure::Sheet::encodeSheet(tsheet, tframe.center, tframe.r,tframe.s,tframe.t) );

		// Visualization
		std::vector<RMF::Frame> frames = smoothRotateFrame(sframe, rotation, 100);
		property["frames"].setValue( frames );
		n->property["frames"].setValue( frames );
		n->property["frame"].setValue( sframe );
		tn->property["frame"].setValue( tframe );
	}

	if ( property["isCrossing"].toBool() ) 
		prepareCrossingSheet();
	
	prepareMorphEdges();
}

void TaskSheet::executeMorphSheet( double t )
{
    Structure::Node * n = node();
    Structure::Sheet * sheet = (Structure::Sheet *)n;

    // Decode
    SheetEncoding cpCoords = property["cpCoords"].value<SheetEncoding>();
    SheetEncoding cpCoordsT = property["cpCoordsT"].value<SheetEncoding>();

    RMF::Frame sframe = property["sframe"].value<RMF::Frame>();
    RMF::Frame tframe = property["tframe"].value<RMF::Frame>();
	QVector<double> rot = property["rotation"].value< QVector<double> >();
    Eigen::Quaterniond rotation(rot[0],rot[1],rot[2],rot[3]),
        eye = Eigen::Quaterniond::Identity();

    // Source sheet
    Eigen::Vector3d R = (sframe.r), S = (sframe.s), T = (sframe.t);
    R = eye.slerp(t, rotation) * R;
    S = eye.slerp(t, rotation) * S;
    T = eye.slerp(t, rotation) * T;
    RMF::Frame curFrame ((R),(S),(T));

    curFrame.center = tframe.center = AlphaBlend(t, sframe.center, tframe.center);

    Array1D_Vector3 newPnts = Sheet::decodeSheet( cpCoords, curFrame.center, curFrame.r, curFrame.s, curFrame.t );
    Array1D_Vector3 newPntsT = Sheet::decodeSheet( cpCoordsT, tframe.center, tframe.r, tframe.s, tframe.t );

    Array1D_Vector3 blendedPnts;

    for(int i = 0; i < (int)newPnts.size(); i++)
    {
        blendedPnts.push_back( AlphaBlend(t, newPnts[i], newPntsT[i]) );
    }

    sheet->setControlPoints( blendedPnts );
}

void TaskSheet::encodeSheet( const Vector4d& coordinateA, const Vector4d& coordinateB )
{
	Node * n = node(), *tn = targetNode();

	//// Parameters needed for morphing
	Vector3 sourceA = n->position(coordinateA), sourceB = n->position(coordinateB);
	Vector3 targetA = tn->position(coordinateA), targetB = tn->position(coordinateB);

	// Two ends
	property["sourceA"].setValue( sourceA ); property["sourceB"].setValue( sourceB );
	property["targetA"].setValue( targetA ); property["targetB"].setValue( targetB );

	// Encoding
	property["cpCoords"].setValue( encodeSheetAsCurve((Structure::Sheet*)n, sourceA, sourceB) );
	property["cpCoordsT"].setValue( encodeSheetAsCurve((Structure::Sheet*)tn, targetA, targetB) );
}

void TaskSheet::prepareCrossingSheet()
{
	Structure::Node * n = node();

	//QVector<Structure::Link*> edges = filterEdges(n, active->getEdges(n->id));

	// For now, let us only use one edge:
	QVector<Structure::Link*> edges;
	edges.push_back( filterEdges(n, active->getEdges(n->id)).front() );

	if (edges.size() == 1)
	{
		// Start and end
		Link * link = edges.front();
		Vector3d start = link->positionOther(n->id);
		NodeCoord futureNodeCord = futureLinkCoord(link);
		Vector3d end = active->position(futureNodeCord.first,futureNodeCord.second);

		// Geodesic distances on the active graph excluding the running tasks
		QVector< GraphDistance::PathPointPair > path;
		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.size() < 2) { path.clear(); path.push_back(GraphDistance::PathPointPair( PathPoint(futureNodeCord.first, futureNodeCord.second))); }

		property["path"].setValue( GraphDistance::positionalPath(active, path) );

		// Save links paths
		path.back() = GraphDistance::PathPointPair( PathPoint(futureNodeCord.first, futureNodeCord.second)  );
		link->setProperty("path", path);
	}

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

void TaskSheet::executeCrossingSheet( double t )
{
	Node *n = node(), *tn = targetNode();

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

	if (property.contains("path"))
	{
		Vector3 p0 = n->position(Vec4d(0,0,0,0));
		Vector3 delta(0,0,0);

		if(!edges.isEmpty())
		{
			p0 = edges.front()->position(n->id);

			// Blend Deltas
			Structure::Link *slink = edges.front();
			Structure::Link* tlink = target->getEdge(slink->property["correspond"].toInt());
			Vector3 d1 = slink->position(n->id) - slink->positionOther(n->id);
			Vector3 d2 = tlink->position(tn->id) - tlink->positionOther(tn->id);
			delta = AlphaBlend(t, d1, d2);
		}

		executeMorphSheet(t);
		
		// Cancel any absolute movement
		if(!edges.isEmpty())
			n->moveBy(p0 - edges.front()->position(n->id));

		// Move it to the correct position
		Array1D_Vector3 path = property["path"].value< Array1D_Vector3 >();
		int idx = t * (path.size() - 1);

		Vector3 oldPosOnMe = p0;
		Vector3 newPosOnMe = path[idx] + delta;

		n->moveBy(newPosOnMe - p0);
	}
}

SheetEncoding TaskSheet::encodeSheetAsCurve( Structure::Sheet * sheet, Vector3 start, Vector3 end)
{
    Array1D_Vector3 controlPoints = sheet->controlPoints();
    return Structure::Curve::encodeCurve(controlPoints,start,end);
}

Structure::Sheet * TaskSheet::targetSheet()
{
    Structure::Node* n = targetNode();
    if(!n || n->type() != Structure::SHEET) return NULL;
    return (Structure::Sheet *)n;
}
back to top