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
Blender.cpp
#include "Blender.h"
#include "Controls.h"
#include "ProgressItem.h"
#include "BlendRenderItem.h"
#include "BlendPathRenderer.h"
#include "BlendPathWidget.h"
#include "BlendPathSubButton.h"

#include "TopoBlender.h"
#include "Scheduler.h"
#include "Task.h"
#include "SynthesisManager.h"
#include "ShapeRenderer.h"
#include "SchedulerWidget.h"
#include "PathEvaluator.h"

typedef QVector< QSet<size_t> > ForcedGroups;
Q_DECLARE_METATYPE( ForcedGroups )
Q_DECLARE_METATYPE( ScheduleType )

Blender::Blender(Scene * scene, QString title) : DemoPage(scene,title), m_gcorr(NULL), s_manager(NULL), renderer(NULL), resultViewer(NULL)
{
	this->numSuggestions = 4;
	this->numInBetweens = 6;
	this->numSchedules = 300;
	this->isFiltering = false;
	this->isSample = true;
	this->isUniformPath = false;

#ifdef QT_DEBUG
	this->isSample = false;
#endif

	this->graphItemWidth = s->width() * 0.15;

	setupBlendPathItems();

	// Progress bar
	progress = new ProgressItem("Working..", false, s);

	// Connections
	this->connect(this, SIGNAL(keyUpEvent(QKeyEvent*)), SLOT(keyReleased(QKeyEvent*)));

    this->connect(this, SIGNAL(blendPathsReady()), SLOT(computeBlendPaths()));
    this->connect(this, SIGNAL(blendPathsDone()), SLOT(blenderDone()));
	this->connect(this, SIGNAL(blendDone()), SLOT(blenderAllResultsDone()));

	// Paths evaluation
	pathsEval = new PathEvaluator(this);
	progress->connect(pathsEval, SIGNAL(progressChanged(double)), SLOT(setProgress(double)));
}

void Blender::setupBlendPathItems()
{
	clearResults();

	// Create background items for each blend path
	blendPathWidth = s->width() - (2 * graphItemWidth);
	blendPathHeight = ((s->height()-200) / numSuggestions);

	int padding = 3;
	int totalHeight = numSuggestions * (blendPathHeight + padding);

	int startY = (s->height() * 0.5 - totalHeight * 0.5) - 25;

	QColor color( 255, 180, 68, 100 );

	// Clear previously set items
	foreach(QGraphicsProxyWidget * grp, blendPathsWidgets) s->removeItem(grp);
	blendPathsWidgets.clear();

	double w = blendPathWidth;
	double h = blendPathHeight;

	for(int i = 0; i < numSuggestions; i++)
	{
		double x = 0.5*s->width() - 0.5*w;
		double y = startY + (i * (blendPathHeight + padding));
		BlendPathWidget * widget = new BlendPathWidget(w,h);
		widget->setBackgroundColor( QColor (255, 255, 255, 5) );
		QGraphicsProxyWidget * witem = s->addWidget(widget);
		witem->setPos(x, y);
		witem->setVisible(false);
		witem->setAcceptHoverEvents(true);
		blendPathsWidgets.push_back(witem);
		widget->proxy = witem;
		items.push_back(witem);
	}

	// Show path indicators
	QRectF r0(0,0,graphItemWidth,graphItemWidth);	r0.moveTop((s->height() * 0.5) - (r0.height() * 0.5));
	QRectF r1 = r0; r1.moveRight(s->width());
	QPointF lstart = r0.center() + QPoint(r0.width() * 0.3,0);
	QPointF rstart = r1.center() - QPoint(r1.width() * 0.3,0);
	double tension = 0.5;

	foreach(QGraphicsItem * item, auxItems) s->removeItem(item);
	auxItems.clear();

	for(int i = 0; i < numSuggestions; i++)
	{
		QRectF r = blendPathsWidgets[i]->sceneBoundingRect();
		QPointF lend = QPointF(r.x(), r.center().y());
		QPointF rend = QPointF(r.x() + r.width(), r.center().y());
		double lmidX = (lend.x() - lstart.x()) * tension;
		double rmidX = (rstart.x() - rend.x()) * tension;

		QVector<QPointF> l_points, r_points;

		l_points.push_back(lstart);
		l_points.push_back(lstart + QPointF(lmidX,0));
		l_points.push_back(lend - QPointF(lmidX,0));
		l_points.push_back(lend);

		r_points.push_back(rstart);
		r_points.push_back(rstart - QPointF(rmidX,0));
		r_points.push_back(rend + QPointF(rmidX,0));
		r_points.push_back(rend);

		QPainterPath lpath;
		lpath.moveTo(l_points.at(0));
		{
			int i=1;
			while (i + 2 < l_points.size()) {
				lpath.cubicTo(l_points.at(i), l_points.at(i+1), l_points.at(i+2));
				i += 3;
			}
		}

		QPainterPath rpath;
		rpath.moveTo(r_points.at(0));
		{
			int i=1;
			while (i + 2 < r_points.size()) {
				rpath.cubicTo(r_points.at(i), r_points.at(i+1), r_points.at(i+2));
				i += 3;
			}
		}

		QGraphicsItem * litem = s->addPath(lpath, QPen(color, 1));
		QGraphicsItem * ritem = s->addPath(rpath, QPen(color, 1));
		litem->setVisible(false);
		ritem->setVisible(false);

		auxItems.push_back(litem);
		auxItems.push_back(ritem);
	}

	itemHeight = blendPathHeight;

	// Add paths navigation buttons
	QRectF firstGroup = blendPathsWidgets.front()->sceneBoundingRect();
	QRectF lastGroup = blendPathsWidgets.back()->sceneBoundingRect();

    prevButton = s->addButton(0,0, "prev", colorize( QImage(":/images/arrowUp.png"), QColor(255,153,0), 2 ) );
    nextButton = s->addButton(0,0, "next", colorize( QImage(":/images/arrowDown.png"), QColor(255,153,0), 2 ) );

	prevButton->setPos(firstGroup.right() - prevButton->boundingRect().width(), firstGroup.top() - padding - prevButton->boundingRect().height());
	nextButton->setPos(lastGroup.right() - nextButton->boundingRect().width(), lastGroup.bottom() + padding);
	prevButton->setVisible(false);
	nextButton->setVisible(false);

	auxItems.push_back(prevButton);
	auxItems.push_back(nextButton);

	// Connections
	this->connect(prevButton->widget(), SIGNAL(clicked()), SLOT(showPrevResults()));
	this->connect(nextButton->widget(), SIGNAL(clicked()), SLOT(showNextResults()));

	// Results renderer
	if( renderer ) delete renderer;
	renderer = new BlendPathRenderer(this, itemHeight);
	this->connect( renderer, SIGNAL(itemReady(QGraphicsItem*)), SLOT(blendResultDone(QGraphicsItem*)), Qt::DirectConnection );

	// Results viewer
	if( resultViewer ) delete resultViewer;
	resultViewer = new BlendPathRenderer(this, itemHeight * 2, true );
}

void Blender::show()
{
    if(!s->isInputReady()) return;

    QRectF r0 = s->inputGraphs[0]->boundingRect();
    QRectF r1 = s->inputGraphs[1]->boundingRect();

    property["r0"] = r0;
    property["r1"] = r1;

    // Scale in place
    QPointF oldCenter0 = r0.center();
    QPointF oldCenter1 = r1.center();
    double s_factor = graphItemWidth / r0.width();
    QMatrix m;
    m.scale(s_factor,s_factor);
    r0 = m.mapRect(r0);
    r1 = m.mapRect(r1);
    r0.translate(oldCenter0 - r0.center());
    r1.translate(oldCenter1 - r1.center());
    r0.moveLeft(0);
    r1.moveRight(s->width());

	// Move input shapes to edge of screen
    QParallelAnimationGroup * animGroup = new QParallelAnimationGroup;
    animGroup->addAnimation( s->inputGraphs[0]->animateTo(r0) );
    animGroup->addAnimation( s->inputGraphs[1]->animateTo(r1) );
    animGroup->start( QAbstractAnimation::DeleteWhenStopped );

	DemoPage::show();

	// Give time for animations to finish
	QTimer::singleShot(300, this, SLOT(preparePaths()));
}

void Blender::hide()
{
    if(!s->isInputReady() || !property.contains("r0")) return;

	foreach(QGraphicsItem * item, auxItems) item->setVisible(false);

	// Return the two input shapes to original state
    QParallelAnimationGroup * animGroup = new QParallelAnimationGroup;
    animGroup->addAnimation( s->inputGraphs[0]->animateTo( property["r0"].toRectF() ) );
    animGroup->addAnimation( s->inputGraphs[1]->animateTo( property["r1"].toRectF() ) );
    animGroup->start( QAbstractAnimation::DeleteWhenStopped );

    DemoPage::hide();

	cleanUp();
}

void Blender::setGraphCorresponder( GraphCorresponder * graphCorresponder )
{
	this->m_gcorr = graphCorresponder;
}

void Blender::schedulePaths( const QSharedPointer<Scheduler> & scheduler, const QSharedPointer<TopoBlender> & blender )
{
	blendPaths.clear();
	blendPaths.resize(numSuggestions);

	for(int i = 0; i < numSuggestions; i++)
	{
		// Shared properties
		BlendPath bp;
		bp.source = s->inputGraphs[0]->g;
		bp.target = s->inputGraphs[1]->g;
		bp.gcorr = this->m_gcorr;
		bp.blender = blender;

		// Per-path properties
		bp.scheduler = QSharedPointer<Scheduler>( scheduler->clone() );
		int idx = qMax(0, qMin(allSchedules.size() - 1, (resultsPage * numSuggestions) + i) );
		bp.scheduler->setSchedule( allSchedules[ idx ] );

		// Add blend path
		blendPaths[i] = bp;

		// Connections
		this->connect(bp.scheduler.data(), SIGNAL(progressChanged(int)), SLOT(pathProgressChanged()), Qt::DirectConnection);
	}
}

void Blender::preparePaths()
{    
	if(!s->isInputReady() || m_gcorr == NULL) return;
	emit( blendStarted() );

	isFinished = false;

	// UI and logging
	{	
		if( isFiltering ) 
			progress->setExtra("Filtering / ");
		else
			progress->setExtra("Preparing paths  ");

		progress->show();

		qApp->setOverrideCursor(Qt::WaitCursor);
		qApp->processEvents();
		pathsTimer.start();
	}

	// Blending setup
	m_scheduler = QSharedPointer<Scheduler>( new Scheduler );
	m_blender = QSharedPointer<TopoBlender>( new TopoBlender( m_gcorr, m_scheduler.data() ) );
	
	// Synthesis data preparation
	if( s_manager.isNull() )
	{
		int numSamples = 8000;

#ifdef QT_DEBUG
		numSamples = 100;
#endif

		s_manager = QSharedPointer<SynthesisManager>(new SynthesisManager(m_gcorr, m_scheduler.data(), m_blender.data(), numSamples));

		QVariant p_camera;
		p_camera.setValue( s->camera );
		s_manager->property["camera"] = p_camera;

		// Progress connections
		progress->connect(s_manager.data(), SIGNAL(progressChanged(double)), SLOT(setProgress(double)));
		this->connect(s_manager.data(), SIGNAL(synthDataReady()), SLOT(synthDataReady()));
	}

	// Apply any user-specified groups
	ForcedGroups forcedGroups = s->inputGraphs[0]->g->property["forceGroup"].value< ForcedGroups >();
	foreach(QSet<size_t> uids, forcedGroups)
	{
		// Retrieve all tasks of the set
		QVector<Task*> tasks;
		foreach(Structure::Node * n, m_scheduler->activeGraph->nodes){
			size_t uid = size_t(n->property["mesh"].value< QSharedPointer<SurfaceMeshModel> >().data());
			if(uids.contains(uid)) tasks.push_back( n->property["task"].value<Task*>() );
		}

		// Schedule them together
		int startTime = m_scheduler->totalExecutionTime();
		foreach(Task * t, tasks) startTime = qMin(startTime, t->start);
		foreach(Task * t, tasks) t->setStart( startTime );

		// Clean up
		m_scheduler->trimTasks();
	}

	/// Generate and sort blend paths:
	resultsPage = 0;
	srand(time(NULL));

	// Get 'k' schedules sorted by filter measure
	if( isFiltering )
	{
		allSchedules = pathsEval->filteredSchedules( m_scheduler->manyRandomSchedules( numSchedules ) );
	}
	else
	{
		allSchedules = m_scheduler->manyRandomSchedules( numSchedules );
	}

	schedulePaths( m_scheduler, m_blender );

	// UI and logging
	{
		qApp->restoreOverrideCursor();
		progress->startProgress();
		progress->setExtra("Generating samples  ");
		emit( message(QString("Paths time [%1 ms]").arg(pathsTimer.elapsed())) );
	}

	synthTimer.start();

	// [HEAVY] Generate synthesis data
	if( isSample )
		s_manager->generateSynthesisData();
	else
		emit( s_manager->emitSynthDataReady() );
}

void Blender::synthDataReady()
{
	if( property["isOverrideSynth"].toBool() ) return;

	for(int i = 0; i < numSuggestions; i++)
		this->disconnect( blendPaths[i].scheduler.data() );

	emit( message(QString("Synthesis time [%1 ms]").arg(synthTimer.elapsed())) );
	emit( blendPathsReady() );
}

void executeJob( const QSharedPointer<Scheduler> & scheduler )
{
#ifdef QT_DEBUG
	scheduler->timeStep = 0.3;
#endif

	scheduler->executeAll();
}

void Blender::computeBlendPaths()
{
	progress->startProgress();
	progress->setExtra("Blend paths ");
	blendTimer.start();

	for(int i = 0; i < blendPaths.size(); i++) 
		jobs.push_back( blendPaths[i].scheduler );

	QtConcurrent::run(this, &Blender::computeBlendPathsThread);
	//computeBlendPathsThread();
}

void Blender::computeBlendPathsThread()
{
	#pragma omp parallel for
	for(int i = 0; i < blendPaths.size(); i++) 
	{
		executeJob( blendPaths[i].scheduler );
	}

	//QFuture<void> future = QtConcurrent::map(jobs, executeJob);
	//future.waitForFinished();

	emit( blendPathsDone() );
}

void Blender::computePath( const int & index )
{
    blendPaths[index].scheduler->executeAll();
}

void Blender::pathProgressChanged()
{
	int curProgress = 0;
	for(int i = 0; i < blendPaths.size(); i++){
		curProgress += blendPaths[i].scheduler->property["progress"].toInt();
	}
	int totalProgress = 100 * blendPaths.size();

	progress->setProgress( double(curProgress) / totalProgress );
}

void Blender::blenderDone()
{
	// UI and logging
	{
        emit( message(QString("Blending time [%1 ms]").arg(blendTimer.elapsed())) );

		progress->setExtra("Rendering ");
		progress->setProgress(0.0);
		renderTimer.start();
	}

	// For each blend path, render 'k' in-betweens
	for(int i = 0; i < numSuggestions; i++)
	{
		Scheduler * curSchedule = blendPaths[i].scheduler.data();
		ScheduleType schedule = curSchedule->getSchedule();

		QVector<Structure::Graph*> inBetweens;

		if( isUniformPath )
		{
			inBetweens = curSchedule->topoVaryingInBetweens( numInBetweens );
		}
		else
		{
			inBetweens = curSchedule->interestingInBetweens( numInBetweens );
		}

		for(int j = 0; j < numInBetweens; j++)
		{
			inBetweens[j]->moveCenterTo( AlphaBlend(inBetweens[j]->property["t"].toDouble(), 
													inBetweens[j]->property["sourceGraphCenter"].value<Vector3>(), 
													inBetweens[j]->property["targetGraphCenter"].value<Vector3>()), true);

			inBetweens[j]->property["schedule"].setValue( schedule );

			renderer->generateItem( inBetweens[j], i, j );
		}
	}
} 

void Blender::mousePress( QGraphicsSceneMouseEvent* mouseEvent )
{
	if(!visible) return;

	if( false )
	{
		// Block when controller is clicked
		foreach(QGraphicsItem * i, s->items(mouseEvent->scenePos())){
			QGraphicsProxyWidget * proxy = dynamic_cast<QGraphicsProxyWidget *>(i);
			if(!proxy) continue; else if(qobject_cast<Controls*>(proxy->widget())) return;
		}

		foreach(QGraphicsProxyWidget * proxy, blendPathsWidgets) 
		{
			if(proxy->sceneBoundingRect().contains(mouseEvent->scenePos())) continue;
			QGraphicsScene * s = ((BlendPathWidget*)proxy->widget())->scene;
			s->clearSelection();
		}
	}
}

void Blender::keyReleased( QKeyEvent* keyEvent )
{
	// Special keys across all pages:
	if( keyEvent->key() == Qt::Key_F )
	{
		this->isSample = !this->isSample;
		emit( message( QString("Sampling toggled to: %1").arg(isSample) ) );
		return;
	}

	// Regular keys
	if(!visible) return;

	// Re-draw results
	if( keyEvent->key() == Qt::Key_R ){
		showResultsPage();
		return;
	}

	// Sampling type
	if( keyEvent->key() == Qt::Key_U ){
		this->isUniformPath = !this->isUniformPath;
		emit( message( QString("Uniform sampling on path: %1").arg( this->isUniformPath ) ) );
		return;
	}

	// EXPERIMENTS:
	if( keyEvent->key() == Qt::Key_Q ){

		// Experiment: path evaluation
		//pathsEval->evaluatePaths();
		//pathsEval->clusterPaths();
		pathsEval->test_filtering();

		return;
	}
	if(keyEvent->key() == Qt::Key_W)
	{
		pathsEval->test_topoDistinct();
		return;
	}

	// Debug render graph function
	if(keyEvent->key() == Qt::Key_Backspace)
	{
		QList<QGraphicsItem*> allSelected;
		foreach(QGraphicsProxyWidget * proxy, blendPathsWidgets) allSelected << ((BlendPathWidget*)proxy->widget())->scene->selectedItems();

		foreach(QGraphicsItem * item, allSelected)
		{
			BlendRenderItem * renderItem = qobject_cast<BlendRenderItem *>(item->toGraphicsObject());
			if(!renderItem) continue;

			QString filename = QString("testRender_%1.obj").arg(renderItem->property["pathID"].toInt());
			s_manager->renderGraph(*renderItem->graph(), filename, false, 6, true);
		}
	}

	// Export all in-betweens on screen
	if(keyEvent->key() == Qt::Key_A)
	{
		for(int i = 0; i < numSuggestions; i++)
		{
			for(int j = 0; j < numInBetweens; j++)
			{
				QString filename = QString("inbetween_%1_%2.obj").arg( i ).arg( j );
				s_manager->renderGraph(*resultItems[i][j]->graph(), filename, false, 6, true, false);
			}
		}
	}

	// Show full schedule for selected item
	if(keyEvent->key() == Qt::Key_Space)
	{
		QList<QGraphicsItem*> allSelected;
		foreach(QGraphicsProxyWidget * proxy, blendPathsWidgets) allSelected << ((BlendPathWidget*)proxy->widget())->scene->selectedItems();

		foreach(QGraphicsItem * item, allSelected)
		{
			BlendRenderItem * renderItem = qobject_cast<BlendRenderItem *>(item->toGraphicsObject());
			if(!renderItem) continue;

			int pathID = renderItem->property["pathID"].toInt();

			SchedulerWidget * widget = new SchedulerWidget( blendPaths[pathID].scheduler.data() );
			widget->setAttribute(Qt::WA_DeleteOnClose);
			widget->show();
		}

		return;
	}

	// Show initial schedule
	if(keyEvent->key() == Qt::Key_B)
	{
		SchedulerWidget * widget = new SchedulerWidget( m_scheduler.data() );
		widget->setAttribute(Qt::WA_DeleteOnClose);
		widget->show();
		return;
	}

	if( keyEvent->key() == Qt::Key_E )
	{
		emit( showLogWindow() );
		emit( message( QString("Filtering [%1] Num schedules [%2]").arg(isFiltering).arg(numSchedules) ) );
		return;
	}

	// Changing number of results
	{
		bool paramChanged = false;

		if(keyEvent->key() == Qt::Key_Equal) {numSuggestions++; paramChanged = true;}
		if(keyEvent->key() == Qt::Key_Minus) {numSuggestions--; paramChanged = true;}
		if(keyEvent->key() == Qt::Key_0) {numInBetweens++; paramChanged = true;}
		if(keyEvent->key() == Qt::Key_9) {numInBetweens--; paramChanged = true;}

		if(paramChanged){
			setupBlendPathItems();
			emit( message( QString("Paths [%1] / In-betweens [%2]").arg(numSuggestions).arg(numInBetweens) ) );
		}
	}
}

void Blender::blendResultDone(QGraphicsItem* done_item)
{
	BlendRenderItem * item = (BlendRenderItem*) done_item;

	int pathID = item->property["pathID"].toInt();
	int blendIDX = item->property["blendIDX"].toInt();

	resultItems[pathID][blendIDX] = QSharedPointer<BlendRenderItem>(item);

	//s->addItem( resultItems[pathID][blendIDX].data() );
	QGraphicsScene * scene = ((BlendPathWidget*)blendPathsWidgets[pathID]->widget())->scene;
	scene->addItem( resultItems[pathID][blendIDX].data() );

	this->connect(item, SIGNAL(doubleClicked(BlendRenderItem*)), SLOT(previewItem(BlendRenderItem*)));

	// Placement
	QRectF pathRect = blendPathsWidgets[pathID]->boundingRect();
	double outterWidth = pathRect.width() / numInBetweens;
	int delta = 0.5 * (outterWidth  - item->boundingRect().width() );
	int x = pathRect.x() + (blendIDX * outterWidth) + delta;
	int y = pathRect.y();
	item->setPos(x, y);

	// UI and logging
	{
		int numDone = 0;
		for(int i = 0; i < numSuggestions; i++)
			for(int j = 0; j < numInBetweens; j++)
				if(resultItems[i][j]) numDone++;

		int N = (numSuggestions * numInBetweens);
		progress->setProgress( double(numDone) / N );

		qApp->processEvents();

		if(numDone == N)
		{
            isFinished = true;

			progress->stopProgress();
			progress->hide();
			emit( message(QString("Render time [%1 ms]").arg(renderTimer.elapsed())) );

            emit( blendDone() );
		}
	}
}

void Blender::blenderAllResultsDone()
{
	// Show UI elements
	foreach(QGraphicsItem * item, auxItems) item->setVisible(true);

	// Expand blend sequence near two items
	for(int i = 0; i < numSuggestions; i++)
	{
		QRectF pathRect = blendPathsWidgets[i]->boundingRect();
		double outterWidth = (pathRect.width() / numInBetweens);

		double w = itemHeight * 0.5;
		double h = itemHeight;

		for(int j = 0; j < numInBetweens + 1; j++)
		{
			double x = pathRect.x() + ((j) * outterWidth) - (w * 0.5);

			if(j == 0) x = 0;
			if(j == numInBetweens) x = pathRect.width() - w;

			addBlendSubItem(x, pathRect.y(), w, h, i, j);
		}
	}

    prevButton->setEnabled(true);
    nextButton->setEnabled(true);

    emit( blendFinished() );
}

void Blender::addBlendSubItem(double x, double y, double w, double h, int i, int j)
{
	BlendPathSubButton * subItemButton = new BlendPathSubButton(w, h, this, 20);
	subItemButton->setPos( QPointF(x, y) );

	blendSubItems[i][j] = QSharedPointer<BlendPathSubButton>(subItemButton);
	blendSubItems[i][j]->property["pathIDX"] = i;

	double start = 0;
	double end = 1.0; 

	if(j > 0) start = resultItems[i][j-1]->graph()->property["t"].toDouble();
	if(j + 1 < numInBetweens) end = resultItems[i][j]->graph()->property["t"].toDouble();

	blendSubItems[i][j]->property["start"].setValue( start );
	blendSubItems[i][j]->property["end"].setValue( end );

	//s->addItem( blendSubItems[i][j].data() );
	QGraphicsScene * scene = ((BlendPathWidget*)blendPathsWidgets[i]->widget())->scene;
	scene->addItem( blendSubItems[i][j].data() );
}

QVector<BlendRenderItem *> Blender::selectedInBetween()
{
	QVector<BlendRenderItem *> result;

	QList<QGraphicsItem*> allSelected;
	foreach(QGraphicsProxyWidget * proxy, blendPathsWidgets) allSelected << ((BlendPathWidget*)proxy->widget())->scene->selectedItems();
	
	foreach(QGraphicsItem * item, allSelected)
	{
		BlendRenderItem * renderItem = qobject_cast<BlendRenderItem *>(item->toGraphicsObject());
		if(!renderItem) continue;
		result.push_back( renderItem );
	}

	return result;
}

void Blender::clearSelectedInBetween()
{
	foreach(QGraphicsProxyWidget * proxy, blendPathsWidgets) 
	{
		QGraphicsScene * s = ((BlendPathWidget*)proxy->widget())->scene;
		s->clearSelection();
	}
}

void Blender::exportSelected()
{
	qApp->setOverrideCursor(Qt::WaitCursor);

	QString msg = "nothing selected.";
	
	foreach(BlendRenderItem * renderItem, selectedInBetween())
	{
		Structure::Graph * g = renderItem->graph();
		int idx = int(g->property["t"].toDouble() * 100);
			
		QString sname = g->property["sourceName"].toString();
		QString tname = g->property["targetName"].toString();
		QString filename = sname + tname + "." + QString::number(idx);

		// Create folder
		QDir d("dataset");	
		d.mkpath( filename );

		// Set it as current
		QDir::setCurrent( d.absolutePath() + "/" + filename );

		// Generate the geometry and export the structure graph
		s_manager->renderGraph(*g, filename, false, 6, true);

		// Generate thumbnail
		QString objFile = d.absolutePath() + "/" + filename + "/" + filename + ".obj";
		QString thumbnailFile = d.absolutePath() + "/" + filename + "/" + filename + ".png";
		ShapeRenderer::render( objFile ).save( thumbnailFile );

		// Send to gallery
		PropertyMap info;
		info["Name"] = filename;
		info["graphFile"] = d.absolutePath() + "/" + filename + "/" + filename + ".xml";
		info["thumbFile"] = d.absolutePath() + "/" + filename + "/" + filename + ".png";
		info["objFile"] = d.absolutePath() + "/" + filename + "/" + filename + ".obj";

		emit( exportShape(filename, info) );

		// Restore
		QDir::setCurrent( d.absolutePath() + "/.." );
	}

	emit( message("Exporting: " + msg) );

	qApp->restoreOverrideCursor();
	QCursor::setPos(QCursor::pos());
}

void Blender::saveJob()
{
	QList<QGraphicsItem*> allSelected;
	foreach(QGraphicsProxyWidget * proxy, blendPathsWidgets) allSelected << ((BlendPathWidget*)proxy->widget())->scene->selectedItems();

	foreach(QGraphicsItem * item, allSelected)
	{
		BlendRenderItem * renderItem = qobject_cast<BlendRenderItem *>(item->toGraphicsObject());
		if(!renderItem) continue;
		
		int pathID = renderItem->property["pathID"].toInt();

		if(pathID < 0 || pathID > blendPaths.size() - 1) continue; 

		Structure::Graph * g = renderItem->graph();
		QString sname = g->property["sourceName"].toString();
		QString tname = g->property["targetName"].toString();
		QString filename = sname + "." + tname;
		blendPaths[pathID].scheduler->saveSchedule(filename);

		QString sGraphName = m_gcorr->sgName();
		QString tGraphName = m_gcorr->tgName();
		QString graph_names = ( sGraphName + "_" + tGraphName ) + ".job";
		QString job_filename = QFileDialog::getSaveFileName(0, tr("Save Job"), graph_names, tr("Job Files (*.job)"));

		QFile job_file( job_filename );
		if (!job_file.open(QIODevice::WriteOnly | QIODevice::Text)) return;
		QFileInfo jobFileInfo(job_file.fileName());
		QTextStream out(&job_file);

		// Create folders
		QDir jobDir( jobFileInfo.absolutePath() );

		QString sDir = jobDir.path() + "/Source/";
		QString tDir = jobDir.path() + "/Target/";
		jobDir.mkdir( sDir ); 
		jobDir.mkdir( tDir );

		// Save source and target graphs
		QString sRelative = "Source/" + sGraphName + ".xml";
		QString sgFileName = jobDir.path() + "/" + sRelative;
		m_blender->sg->saveToFile( sgFileName );

		QString tRelative = "Target/" + tGraphName + ".xml";
		QString tgFileName = jobDir.path() + "/"  + tRelative;
		m_blender->tg->saveToFile( tgFileName );

		// Save correspondence file
		QString correspondRelative = "correspondence.txt";
		QString correspondenceFileName = jobDir.path() + "/" + correspondRelative;
		m_gcorr->saveCorrespondences( correspondenceFileName, true );

		// Save the scheduler
		QString scheduleRelative = "schedule.txt";
		QString scheduleFileName = jobDir.path() + "/" + scheduleRelative;
		blendPaths[pathID].scheduler->saveSchedule( scheduleFileName );

		// Save paths & parameters
		out << sRelative << "\n";
		out << tRelative << "\n";
		out << correspondRelative << "\n";
		out << scheduleRelative << "\n";
		out << s_manager->samplesCount << "\t";
		out << DIST_RESOLUTION << "\t" << m_scheduler->timeStep << "\n";
		out << 7 << "\t" << numInBetweens << "\n";
		job_file.close();

		// Save samples
		//s_manager->saveSynthesisData( jobDir.path() + "/" );
	}
}

void Blender::clearResults()
{
	// Clear generated items
	jobs.clear();
	resultItems = QVector< QVector< QSharedPointer<BlendRenderItem> > >(numSuggestions, QVector< QSharedPointer<BlendRenderItem> >(numInBetweens) );
	blendSubItems = QVector< QVector< QSharedPointer<BlendPathSubButton> > >(numSuggestions, QVector< QSharedPointer<BlendPathSubButton> >(numInBetweens + 2) );

	for(int i = 0; i < blendPathsWidgets.size(); i++)
	{
		BlendPathWidget * oldWidget = (BlendPathWidget*)blendPathsWidgets[i]->widget();
		oldWidget->buildScene(blendPathWidth, blendPathHeight);
	}
}

void Blender::cleanUp()
{
	// Clear results
	{
		clearResults();
	}

	// Clean up synthesis data
	{
		s_manager.clear();
	}

	// Clean up blending path data
	{
		blendPaths.clear();
	}
}

QWidget * Blender::viewer()
{
	return scene()->views().front();
}

void Blender::showPrevResults()
{
    prevButton->setEnabled(false);
    emit( blendStarted() );

	if(!blendPaths.size()) return;

	resultsPage--;
	if(resultsPage < 0){
		resultsPage = 0;
		return;
	}
	showResultsPage();
}

void Blender::showNextResults()
{
    nextButton->setEnabled(false);
    emit( blendStarted() );

	if(!blendPaths.size()) return;
	resultsPage++;

	if( isFiltering )
	{
		int cutOff = allSchedules.size() * 0.5;

		// Generate more filtered results
		if((resultsPage * numSuggestions) > cutOff){

			// UI and logging
			{	
				progress->setExtra("Filtering / ");
				progress->show();
				progress->update();
				pathsTimer.restart();

				s->update();
				qApp->processEvents();
			}

			allSchedules = pathsEval->filteredSchedules( m_scheduler->manyRandomSchedules( numSchedules ) );
			resultsPage = 0;

			// UI and logging
			{
				emit( message(QString("Paths filtering time [%1 ms]").arg(pathsTimer.elapsed())) );
			}
		}
	}

	showResultsPage();
}

void Blender::showResultsPage()
{
	clearResults();

	schedulePaths( m_scheduler, m_blender );

	// Generate more items
	emit( blendPathsReady() );
}

void Blender::previewItem( BlendRenderItem* item )
{
	resultViewer->activeGraph = item->graph();

	// Placement
	QRectF viewerRect = resultViewer->geometry();
	viewerRect.moveCenter( QCursor::pos() );
	resultViewer->setGeometry( viewerRect.toRect() );

	resultViewer->show();
}

void Blender::emitMessage( QString msg )
{
	emit( message(msg) );
}

void Blender::filterStateChanged( int state )
{
	this->isFiltering = (state == Qt::Checked);

	emit( message( QString("Filter state changed: ").arg(isFiltering) ) );

	if( this->isFiltering ) numSchedules = 20;
}
back to top