Blob Blame Raw


#include "tmacrofx.h"

// TnzBase includes
#include "tparamcontainer.h"
#include "tfxattributes.h"

// TnzCore includes
#include "tstream.h"

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

namespace
{

class MatchesFx
{
public:
	MatchesFx(const TFxP &fx) : m_fx(fx) {}

	bool operator()(const TFxP &fx)
	{
		return m_fx.getPointer() == fx.getPointer();
	}

	TFxP m_fx;
};

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

void pushParents(const TFxP &root, std::vector<TFxP> &fxs, const std::vector<TFxP> &selectedFxs)
{
	int i, count = root->getInputPortCount();
	if (count == 0) {
		std::vector<TFxP>::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(root));
		if (found == fxs.end())
			fxs.push_back(root);
		return;
	}
	for (i = 0; i < count; i++) {
		TFxP inutFx = root->getInputPort(i)->getFx();
		std::vector<TFxP>::const_iterator found = std::find_if(selectedFxs.begin(), selectedFxs.end(), MatchesFx(inutFx));
		if (found != selectedFxs.end())
			pushParents(inutFx, fxs, selectedFxs);
	}
	std::vector<TFxP>::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(root));
	if (found == fxs.end())
		fxs.push_back(root);
}

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

std::vector<TFxP> sortFxs(const std::vector<TFxP> &fxs)
{
	std::vector<TFxP> app;
	std::vector<TFxP> roots;
	//find fxs that could be in back of the vector.
	int i;
	for (i = 0; i < (int)fxs.size(); i++) {
		TFxP fx = fxs[i];
		int j, count = (int)fx->getOutputConnectionCount();
		if (count == 0) {
			roots.push_back(fx);
			continue;
		}
		for (j = 0; j < count; j++) {
			TFxP connectedFx = fx->getOutputConnection(j)->getOwnerFx();
			std::vector<TFxP>::const_iterator found = std::find_if(fxs.begin(), fxs.end(), MatchesFx(connectedFx));
			if (found == fxs.end()) {
				roots.push_back(fx);
				break;
			}
		}
	}
	for (i = 0; i < (int)roots.size(); i++)
		pushParents(roots[i], app, fxs);
	assert(fxs.size() == app.size());
	return app;
}

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

// raccoglie tutti i parametri dai vari TFx e li assegna anche alla macro
void collectParams(TMacroFx *macroFx)
{
	int k;
	for (k = 0; k < (int)macroFx->m_fxs.size(); k++) {
		TFxP fx = macroFx->m_fxs[k];
		int j;
		for (j = 0; j < fx->getParams()->getParamCount(); j++)
			macroFx->getParams()->add(fx->getParams()->getParamVar(j)->clone());
	}
}

} // anonymous namespace

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

bool TMacroFx::analyze(const std::vector<TFxP> &fxs,
					   TFxP &root,
					   std::vector<TFxP> &roots,
					   std::vector<TFxP> &leafs)
{
	if (fxs.size() == 1)
		return false;
	else {
		leafs.clear();
		roots.clear();
		std::vector<TFxP>::const_iterator it = fxs.begin();
		for (; it != fxs.end(); ++it) {
			TFxP fx = *it;
			int inputInternalConnection = 0;
			int inputExternalConnection = 0;
			int outputInternalConnection = 0;
			int outputExternalConnection = 0;

			int i;

			// calcola se ci sono connessioni in input dall'esterno
			// verso l'interno e/o internamente a orderedFxs
			int inputPortCount = fx->getInputPortCount();
			for (i = 0; i < inputPortCount; ++i) {
				TFxPort *inputPort = fx->getInputPort(i);
				TFx *inputPortFx = inputPort->getFx();
				if (inputPortFx) {
					if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(inputPortFx)) != fxs.end())
						++inputInternalConnection;
					else
						++inputExternalConnection;
				}
			}

			// calcola se ci sono connessioni in output dall'interno
			// verso l'esterno e/o internamente a orderedFxs
			int outputPortCount = fx->getOutputConnectionCount();
			for (i = 0; i < outputPortCount; ++i) {
				TFxPort *outputPort = fx->getOutputConnection(i);
				TFx *outputFx = outputPort->getOwnerFx();
				if (outputFx) {
					if (std::find_if(fxs.begin(), fxs.end(), MatchesFx(outputFx)) != fxs.end())
						++outputInternalConnection;
					else
						++outputExternalConnection;
				}
			}

			// se fx e' una radice
			if ((outputExternalConnection > 0) ||
				(outputExternalConnection == 0 && outputInternalConnection == 0)) {
				root = fx;
				roots.push_back(fx);
			}

			// se fx e' una foglia
			if (inputExternalConnection > 0 || fx->getInputPortCount() == 0 ||
				(inputExternalConnection == 0 && inputInternalConnection < fx->getInputPortCount())) {
				leafs.push_back(fx);
			}
		}

		if (roots.size() != 1)
			return false;
		else {
			if (leafs.size() == 0)
				return false;
		}

		return true;
	}
}

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

bool TMacroFx::analyze(const std::vector<TFxP> &fxs)
{
	TFxP root = 0;
	std::vector<TFxP> leafs;
	std::vector<TFxP> roots;
	return analyze(fxs, root, roots, leafs);
}

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

bool TMacroFx::isaLeaf(TFx *fx) const
{
	int count = fx->getInputPortCount();
	if (count == 0)
		return true;

	for (int i = 0; i < count; ++i) {
		TFxPort *port = fx->getInputPort(i);
		TFx *inputFx = port->getFx();
		if (inputFx) {
			if (std::find_if(m_fxs.begin(), m_fxs.end(), MatchesFx(inputFx)) == m_fxs.end()) {
				// il nodo di input non appartiene al macroFx
				return true;
			}
		} else {
			// la porta di input non e' connessa
			return true;
		}
	}

	// tutte le porte di input sono connesse verso nodi appartenenti al macroFx
	return false;
}

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

TMacroFx::TMacroFx() : m_isEditing(false) {}

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

TMacroFx::~TMacroFx() {}

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

TFx *TMacroFx::clone(bool recursive) const
{
	int n = m_fxs.size();
	std::vector<TFxP> clones(n);
	std::map<TFx *, int> table;
	std::map<TFx *, int>::iterator it;
	int i, rootIndex = -1;
	// nodi
	for (i = 0; i < n; ++i) {
		TFx *fx = m_fxs[i].getPointer();
		assert(fx);
		clones[i] = fx->clone(false);
		assert(table.count(fx) == 0);
		table[fx] = i;
		if (fx == m_root.getPointer())
			rootIndex = i;
		TFx *linkedFx = fx->getLinkedFx();
		if (linkedFx && table.find(linkedFx) != table.end())
			clones[i]->linkParams(clones[table[linkedFx]].getPointer());
	}
	assert(rootIndex >= 0);
	// connessioni
	for (i = 0; i < n; i++) {
		TFx *fx = m_fxs[i].getPointer();
		for (int j = 0; j < fx->getInputPortCount(); j++) {
			TFxPort *port = fx->getInputPort(j);
			TFx *inputFx = port->getFx();
			if (!inputFx)
				continue;
			it = table.find(inputFx);
			if (it == table.end()) {
				// il j-esimo input di fx e' esterno alla macro
				if (recursive)
					clones[i]->connect(fx->getInputPortName(j), inputFx->clone(true));
			} else {
				// il j-esimo input di fx e' interno alla macro
				clones[i]->connect(fx->getInputPortName(j), clones[it->second].getPointer());
			}
		}
	}

	//TFx *rootClone =
	//  const_cast<TMacroFx*>(this)->
	//  clone(m_root.getPointer(), recursive, visited, clones);

	TMacroFx *clone = TMacroFx::create(clones);
	clone->setName(getName());
	clone->setFxId(getFxId());

	//Copy the index of the passive cache manager.
	clone->getAttributes()->passiveCacheDataIdx() = getAttributes()->passiveCacheDataIdx();

	assert(clone->getRoot() == clones[rootIndex].getPointer());

	return clone;
}

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

bool TMacroFx::doGetBBox(double frame, TRectD &bBox, const TRenderSettings &info)
{
	return m_root->doGetBBox(frame, bBox, info);
}

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

void TMacroFx::doDryCompute(TRectD &rect,
							double frame,
							const TRenderSettings &info)
{
	assert(m_root);
	m_root->dryCompute(rect, frame, info);
}

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

void TMacroFx::doCompute(TTile &tile, double frame, const TRenderSettings &ri)
{
	assert(m_root);
	m_root->compute(tile, frame, ri);
}

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

TFxTimeRegion TMacroFx::getTimeRegion() const
{
	return m_root->getTimeRegion();
}

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

std::string TMacroFx::getPluginId() const
{
	return "Base";
}

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

void TMacroFx::setRoot(TFx *root)
{
	m_root = root;
	// TFx::m_imp->m_outputPort = root->m_imp->m_outputPort;
}

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

TFx *TMacroFx::getRoot() const
{
	return m_root.getPointer();
}

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

TFx *TMacroFx::getFxById(const std::wstring &id) const
{
	int i;
	for (i = 0; i < (int)m_fxs.size(); i++) {
		TFx *fx = m_fxs[i].getPointer();
		if (fx->getFxId() == id)
			return fx;
	}
	return 0;
}

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

const std::vector<TFxP> &TMacroFx::getFxs() const
{
	return m_fxs;
}

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

std::string TMacroFx::getMacroFxType() const
{
	std::string name = getFxType() + "(";
	for (int i = 0; i < (int)m_fxs.size(); i++) {
		if (i > 0)
			name += ",";
		if (TMacroFx *childMacro = dynamic_cast<TMacroFx *>(m_fxs[i].getPointer()))
			name += childMacro->getMacroFxType();
		else
			name += m_fxs[i]->getFxType();
	}
	return name + ")";
}

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

TMacroFx *TMacroFx::create(const std::vector<TFxP> &fxs)
{
	std::vector<TFxP> leafs;
	std::vector<TFxP> roots;
	TFxP root = 0;

	std::vector<TFxP> orederedFxs = sortFxs(fxs);

	// verifica che gli effetti selezionati siano idonei ad essere raccolti
	// in una macro. Ci deve essere un solo nodo terminale
	// (roots.size()==1, roots[0] == root) e uno o piu' nodi di ingresso
	// (assert leafs.size()>0)
	if (!analyze(orederedFxs, root, roots, leafs))
		return 0;

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

	TMacroFx *macroFx = new TMacroFx;

	// tutti i nodi vengono spostati (e non copiati) nella macro stessa
	std::vector<TFxP>::const_iterator it = orederedFxs.begin();
	for (; it != orederedFxs.end(); ++it)
		macroFx->m_fxs.push_back(*it);

	// i nodi di ingresso vengono messi in collegamento con le
	// porte di ingresso della macro
	for (int i = 0; i < (int)leafs.size(); i++) {
		TFxP fx = leafs[i];
		int k = 0;
		int count = fx->getInputPortCount();
		for (; k < count; k++) {
			TFxPort *port = fx->getInputPort(k);
			std::string portName = fx->getInputPortName(k);
			std::string fxId = ::to_string(fx->getFxId());
			portName += "_" + std::to_string(macroFx->getInputPortCount()) + "_" + fxId;
			TFx *portFx = port->getFx();
			if (portFx) {
				// se la porta k-esima del nodo di ingresso i-esimo e' collegata
				// ad un effetto, la porta viene inserita solo se l'effetto non fa
				// gia' parte della macro
				if (std::find_if(orederedFxs.begin(), orederedFxs.end(), MatchesFx(portFx)) == orederedFxs.end())
					macroFx->addInputPort(portName, *port);
			} else
				macroFx->addInputPort(portName, *port);
		}
	}

	// le porte di uscita di root diventano le porte di uscita della macro
	int count = root->getOutputConnectionCount();
	int k = count - 1;
	for (; k >= 0; --k) {
		TFxPort *port = root->getOutputConnection(k);
		port->setFx(macroFx);
	}

	macroFx->setRoot(root.getPointer());

	// tutti i parametri delle funzioni figlie diventano parametri della macro
	collectParams(macroFx);
	return macroFx;
}

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

bool TMacroFx::canHandle(const TRenderSettings &info, double frame)
{
	return m_root->canHandle(info, frame);
}

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

std::string TMacroFx::getAlias(double frame, const TRenderSettings &info) const
{
	std::string alias = getFxType();
	alias += "[";

	// alias degli effetti connessi alle porte di input separati da virgole
	// una porta non connessa da luogo a un alias vuoto (stringa vuota)
	int i;
	for (i = 0; i < getInputPortCount(); i++) {
		TFxPort *port = getInputPort(i);
		if (port->isConnected()) {
			TRasterFxP ifx = port->getFx();
			assert(ifx);
			alias += ifx->getAlias(frame, info);
		}
		alias += ",";
	}

	// alias dei valori dei parametri dell'effetto al frame dato
	for (int j = 0; j < (int)m_fxs.size(); j++) {
		alias += (j == 0) ? "(" : ",(";
		for (i = 0; i < m_fxs[j]->getParams()->getParamCount(); i++) {
			if (i > 0)
				alias += ",";
			TParam *param = m_fxs[j]->getParams()->getParam(i);
			alias += param->getName() + "=" + param->getValueAlias(frame, 2);
		}
		alias += ")";
	}

	alias += "]";
	return alias;
}

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

void TMacroFx::compatibilityTranslatePort(int major, int minor, std::string &portName)
{
	// Reroute translation to the actual fx associated to the port
	const std::string &fxId = portName.substr(portName.find_last_of('_') + 1,
											  std::string::npos);

	if (TFx *fx = getFxById(::to_wstring(fxId))) {
		size_t opnEnd = portName.find_first_of('_');

		std::string originalPortName = portName.substr(0, opnEnd);
		fx->compatibilityTranslatePort(major, minor, originalPortName);

		portName.replace(0, opnEnd, originalPortName);
	}

	// Seems that at a certain point, the port name got extended...
	if (VersionNumber(major, minor) == VersionNumber(1, 16)) {
		for (int i = 0; i < getInputPortCount(); ++i) {
			const std::string &name = getInputPortName(i);
			if (name.find(portName) != std::string::npos) {
				portName = name;
				break;
			}
		}
	}
}

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

void TMacroFx::loadData(TIStream &is)
{
	VersionNumber tnzVersion = is.getVersion();
	std::string tagName;
	while (is.openChild(tagName)) {
		if (tagName == "root") {
			TPersist *p = 0;
			is >> p;
			m_root = dynamic_cast<TFx *>(p);
		} else if (tagName == "nodes") {
			while (!is.eos()) {
				TPersist *p = 0;
				is >> p;

				// NOTE: In current implementation p is sharedly owned by is - it's automatically
				//       released upon stream destruction if the below assignment fails

				if (TFx *fx = dynamic_cast<TFx *>(p)) {
					m_fxs.push_back(fx);
				}
			}
		} else if (tagName == "ports") {
			int i = 0;
			while (is.matchTag(tagName)) {
				if (tagName == "port") {
					std::string name = is.getTagAttribute("name");
					if (tnzVersion < VersionNumber(1, 16) && name != "") {
						TRasterFxPort *port = new TRasterFxPort();
						addInputPort(name, *port);
					} else {
						name = is.getTagAttribute("name_inFx");
						if (tnzVersion < VersionNumber(1, 17) && tnzVersion != VersionNumber(0, 0))
							name.insert(name.find("_"), "_" + std::to_string(i));

						compatibilityTranslatePort(tnzVersion.first, tnzVersion.second, name);

						std::string inPortName = name;
						inPortName.erase(inPortName.find("_"), inPortName.size() - 1);

						std::string inFxId = name;
						inFxId.erase(0, inFxId.find_last_of("_") + 1);

						for (int i = 0; i < (int)m_fxs.size(); i++) {
							std::wstring fxId = m_fxs[i]->getFxId();
							if (fxId == ::to_wstring(inFxId)) {
								if (TFxPort *port = m_fxs[i]->getInputPort(inPortName))
									addInputPort(name, *port);
							}
						}
					}
					i++;
				} else
					throw TException("unexpected tag " + tagName);
			}
		} else if (tagName == "super") {
			TRasterFx::loadData(is);
		} else
			throw TException("unexpected tag " + tagName);
		is.closeChild();
	}
	collectParams(this);
}

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

void TMacroFx::saveData(TOStream &os)
{
	int i;
	os.openChild("root");
	TPersist *p = m_root.getPointer();
	os << p;
	os.closeChild();
	os.openChild("nodes");
	for (i = 0; i < (int)m_fxs.size(); i++) {
		TFxP fx = m_fxs[i];
		TPersist *p = fx.getPointer();
		os << p;
	}
	os.closeChild();
	os.openChild("ports");
	for (i = 0; i < getInputPortCount(); i++) {
		std::string portName = getInputPortName(i);
		std::map<std::string, std::string> attr;
		attr["name_inFx"] = portName;
		os.openCloseChild("port", attr);
	}
	os.closeChild();
	os.openChild("super");
	TRasterFx::saveData(os);
	os.closeChild();
}

//--------------------------------------------------
FX_IDENTIFIER(TMacroFx, "macroFx")
//FX_IDENTIFIER_IS_HIDDEN(TMacroFx, "macroFx")