Commit 3424e596 authored by CAMP C++ Builder's avatar CAMP C++ Builder
Browse files

MatrixProcessor feature extension: parser mode

parent d8161329
......@@ -40,6 +40,8 @@ namespace campvis {
MatrixProcessor::MatrixProcessor()
: AbstractProcessor()
, p_parserMode("parsermode", "Enable Parser Mode (Experimental!)", false)
, p_parserInputString("parserInput", "Parser Input")
, p_matrixAType("MatrixA_Type", "Matrix A Source", typeOptions, 2)
, p_matrixAID("MatrixA_ID", "Matrix A", "matrixA", DataNameProperty::READ)
, p_matrixAString("MatrixA_String", "Matrix A String", "identity")
......@@ -51,6 +53,9 @@ namespace campvis {
, p_targetMatrixID("TargetMatrixID", "Target Matrix ID", "ProbeToReference", DataNameProperty::WRITE)
, _lastdc(nullptr)
{
addProperty(p_parserMode, INVALID_PROPERTIES);
addProperty(p_parserInputString, INVALID_RESULT);
addProperty(p_matrixAType, INVALID_PROPERTIES | INVALID_RESULT);
addProperty(p_matrixAID, INVALID_RESULT);
addProperty(p_matrixAString, INVALID_RESULT);
......@@ -93,40 +98,45 @@ namespace campvis {
_lastdc = &data;
}
tgt::mat4 matA = tgt::mat4::createIdentity();
if(p_matrixAType.getOptionValue() == "fixed")
matA = processMatrixString(p_matrixAString.getValue(), data);
else {
ScopedTypedData<TransformData> td(data, p_matrixAID.getValue());
if (td != 0) matA = td->getTransform();
if (p_parserMode.getValue()) {
parseString(p_parserInputString.getValue(), data);
}
tgt::mat4 matB = tgt::mat4::createIdentity();
if (p_matrixBType.getOptionValue() == "fixed")
matB = processMatrixString(p_matrixBString.getValue(), data);
else {
ScopedTypedData<TransformData> td(data, p_matrixBID.getValue());
if (td != 0) matB = td->getTransform();
}
tgt::mat4 matA = tgt::mat4::createIdentity();
if (p_matrixAType.getOptionValue() == "fixed")
matA = processMatrixString(p_matrixAString.getValue(), data);
else {
ScopedTypedData<TransformData> td(data, p_matrixAID.getValue());
if (td != 0) matA = td->getTransform();
}
tgt::mat4 matB = tgt::mat4::createIdentity();
if (p_matrixBType.getOptionValue() == "fixed")
matB = processMatrixString(p_matrixBString.getValue(), data);
else {
ScopedTypedData<TransformData> td(data, p_matrixBID.getValue());
if (td != 0) matB = td->getTransform();
}
tgt::mat4 matAProcessed = processModifierString(matA, p_matrixAModifiers.getValue());
tgt::mat4 matBProcessed = processModifierString(matB, p_matrixBModifiers.getValue());
tgt::mat4 matAProcessed = processModifierString(matA, p_matrixAModifiers.getValue());
tgt::mat4 matBProcessed = processModifierString(matB, p_matrixBModifiers.getValue());
tgt::mat4 result = matAProcessed * matBProcessed;
tgt::mat4 result = matAProcessed * matBProcessed;
#ifdef MATRIX_PROCESSOR_DEBUGGING
LDEBUG("Matrix A: " << std::endl << matA);
LDEBUG("Matrix A':" << std::endl << matAProcessed);
LDEBUG("Matrix B: " << std::endl << matB);
LDEBUG("Matrix B':" << std::endl << matBProcessed);
LDEBUG("Result Matrix: " << std::endl << result);
LDEBUG(std::endl);
LDEBUG("Matrix A: " << std::endl << matA);
LDEBUG("Matrix A':" << std::endl << matAProcessed);
LDEBUG("Matrix B: " << std::endl << matB);
LDEBUG("Matrix B':" << std::endl << matBProcessed);
LDEBUG("Result Matrix: " << std::endl << result);
LDEBUG(std::endl);
#endif
TransformData * td = new TransformData(result);
TransformData * td = new TransformData(result);
data.addData(p_targetMatrixID.getValue(), td);
data.addData(p_targetMatrixID.getValue(), td);
}
validate(INVALID_RESULT);
}
......@@ -136,28 +146,45 @@ namespace campvis {
#ifdef MATRIX_PROCESSOR_DEBUGGING
LINFO("Updating Properties");
#endif
if (p_matrixAType.getOptionValue() == "fixed") {
p_matrixAID.setVisible(false);
p_matrixAString.setVisible(true);
}
else {
p_matrixAID.setVisible(true);
p_matrixAString.setVisible(false);
}
bool pmode = p_parserMode.getValue();
p_parserInputString.setVisible(pmode);
if (p_matrixBType.getOptionValue() == "fixed") {
p_matrixBID.setVisible(false);
p_matrixBString.setVisible(true);
}
else {
p_matrixBID.setVisible(true);
p_matrixBString.setVisible(false);
p_matrixAType.setVisible(!pmode);
p_matrixAID.setVisible(!pmode);
p_matrixAString.setVisible(!pmode);
p_matrixAModifiers.setVisible(!pmode);
p_matrixBType.setVisible(!pmode);
p_matrixBID.setVisible(!pmode);
p_matrixBString.setVisible(!pmode);
p_matrixBModifiers.setVisible(!pmode);
p_targetMatrixID.setVisible(!pmode);
if(!pmode) {
if (p_matrixAType.getOptionValue() == "fixed") {
p_matrixAID.setVisible(false);
p_matrixAString.setVisible(true);
}
else {
p_matrixAID.setVisible(true);
p_matrixAString.setVisible(false);
}
if (p_matrixBType.getOptionValue() == "fixed") {
p_matrixBID.setVisible(false);
p_matrixBString.setVisible(true);
}
else {
p_matrixBID.setVisible(true);
p_matrixBString.setVisible(false);
}
}
validate(INVALID_PROPERTIES);
}
tgt::mat4 MatrixProcessor::processMatrixString(std::string matrixString, DataContainer& data)
tgt::mat4 MatrixProcessor::processMatrixString(std::string matrixString, DataContainer& data, std::map<std::string, tgt::mat4> *localDefs)
{
std::vector<std::string> tokens = StringUtils::split(matrixString, " ");
......@@ -220,6 +247,14 @@ namespace campvis {
}
// if we cannot find another pattern, we assume we have a data container ID
else {
// see if we can find the matrix in the local definitions
if (localDefs) {
auto pos = localDefs->find(matrixString);
if (pos != localDefs->end()) {
return pos->second;
}
}
ScopedTypedData<TransformData> td(data, matrixString);
if (td == 0) {
LWARNING("Data Container ID \"" << matrixString << "\" was not suitable as input Matrix");
......@@ -265,6 +300,74 @@ namespace campvis {
return result;
}
void MatrixProcessor::parseString(const std::string & parserInput, DataContainer & dc)
{
std::map<std::string, tgt::mat4> results;
std::vector<std::string> equations = StringUtils::split(parserInput, ";");
//evaluate every assignment
for (size_t i = 0, nEq = equations.size(); i != nEq; ++i)
{
std::string & eqn = equations[i];
try {
//skip empty equations
if (!eqn.size()) continue;
//LDEBUG("Equation: " << eqn);
size_t equal_pos = eqn.find('=', 0);
if (equal_pos == std::string::npos) {
LWARNING("No equal sign in equation \"" << eqn << "\". Skipping this assignment.");
continue;
}
std::string assignedMatName = eqn.substr(0, equal_pos);
std::string formulaToEvaluate = eqn.substr(equal_pos + 1, std::string::npos);
//LDEBUG("Matrix Name: " << assignedMatName << ". Formula: " << formulaToEvaluate);
//split formulaToEvaluate by the multiplications
tgt::mat4 assignedResult = tgt::mat4::identity;
std::vector<std::string> multiplicands = StringUtils::split(formulaToEvaluate, "*");
for (size_t m = 0, nMul = multiplicands.size(); m != nMul; ++m)
{
std::string & matStrCombined = multiplicands[m];
//parse multiplicands of form "[<MatrixString>]_<Modifiers>"
size_t delimPos = matStrCombined.find("]");
if (!(matStrCombined[0] == '[') || delimPos == std::string::npos) {
LWARNING("Error parsing matrix part \"" << matStrCombined << "\": Delimiters not found! Ignoring multiplicand..");
continue;
}
std::string matStr = matStrCombined.substr(1, delimPos - 1);
std::string modifiers = delimPos + 2 > matStrCombined.size() ? ""
: matStrCombined.substr(delimPos + 2, std::string::npos);
//LDEBUG("Matrix String: " << matStr << " Modifiers: " << modifiers);
//evaluate matrix and and multiply to the result
tgt::mat4 multiplicand = processMatrixString(matStr, dc, &results);
assignedResult *= processModifierString(multiplicand, modifiers);
}
//save result into result map
results[assignedMatName] = assignedResult;
}
catch (std::exception &e) {
LWARNING("Exception while parsing equation \"" << eqn << "\": " << e.what());
}
}
// put all results into the data container
// matrix names beginning with an underscore are skipped
for (auto it = results.begin(), end = results.end(); it != end; ++it) {
if (it->first[0] != '_') {
dc.addData(it->first, new TransformData(it->second));
}
}
}
void MatrixProcessor::DataContainerDataAdded(const std::string &name, const DataHandle &data)
{
if (name == p_matrixAID.getValue() || name == p_matrixBID.getValue())
......
......@@ -42,6 +42,12 @@ namespace campvis {
* (see \a processMatrixString()), preprocesses them according to the specified modifiers (see \a processModiferString())
* and puts the result of multiplying A*B into the data container as a \a TransformData entry.
*
* As an alternative, the "Parser Mode" is offered. This offers a possibility to evaluate more complicated formulas
* without the need for multiple MatrixProcessor instances. For details on the syntax, see \a parseString(). The parsing is
* slower than the "basic" mode, but for most applications this performance hit should not be a problem.
* Performance could be reduced in a future iteration by pre-processing the input string instead of parsing it on the fly in each update call.
* Please note that this features is EXPERIMENTAL and has not been extensively tested.
*
* Example use case: OpenIGTLink client outputs matrices TrackerToReference and TrackerToProbe. Configure
* matrixA as "TrackerToProbe" with modifier "I" and matrixB as "TrackerToReference" with empty modifier
* to compute the "ProbeToReference" matrix. If an additional calibration matrix is needed, this can be achieved by
......@@ -68,12 +74,15 @@ namespace campvis {
/// \see AbstractProcessor::getName()
virtual const std::string getName() const { return "MatrixProcessor"; };
/// \see AbstractProcessor::getDescription()
virtual const std::string getDescription() const { return "Matrix Processor to process/combine one or two matrices and write the result into the data container"; };
virtual const std::string getDescription() const { return "Matrix Processor to process/combine matrices and write the result into the data container"; };
/// \see AbstractProcessor::getAuthor()
virtual const std::string getAuthor() const { return "Jakob Weiss <weissj@in.tum.de>"; };
/// \see AbstractProcessor::getProcessorState()
virtual ProcessorState getProcessorState() const { return AbstractProcessor::EXPERIMENTAL; };
BoolProperty p_parserMode; ///< toggle parsing mode. if enabled, a formula must be specified that should be parsed
StringProperty p_parserInputString; ///< formula to be parsed when parsing mode is enabled. \see MatrixProcessor::parseString()
GenericOptionProperty<std::string> p_matrixAType;
DataNameProperty p_matrixAID; ///< first Matrix input for the computation. \see MatrixProcessor::processMatrixString()
StringProperty p_matrixAString;
......@@ -97,7 +106,7 @@ namespace campvis {
/// \see AbstractProcessor::updateProperties()
virtual void updateProperties(DataContainer& dataContainer);
/**
/**
* Processes a modifier string and returns the modified matrix.
*
* \param matrix the input matrix to be modified
......@@ -118,6 +127,8 @@ namespace campvis {
*
* \param matrixString the matrix string to be parsed
* \param data the data container that is used to read data from
* \param localDefs map of local definitions. if supplied, matrix names will be looked for in this
* map prior to a datacontainer lookup
*
* The matrix string can either be a name to a data handle or a string specifying a matrix directly.
* The string is split into tokens with space as a delimiter and is parsed according to the following rules:
......@@ -132,10 +143,36 @@ namespace campvis {
* - "scale <sx> [<sy> <sz>]" creates a scaling matrix. if only one coefficient is specified, a uniform scaling
* is created, otherwise all three scaling factors are used.
* - if any of the above fails, a warning is emitted and identity is returned
* - if none of the above cases apply, the name is assumed to be a name of a data handle in the supplied data container,
* containing an entry of type \a TransformData
* - if none of the above cases apply, the name is assumed to be a name of a data handle in the supplied data container
* or the localDefs map, containing an entry of type \a TransformData
*/
tgt::mat4 processMatrixString(std::string matrixString, DataContainer& data);
tgt::mat4 processMatrixString(std::string matrixString, DataContainer& data, std::map<std::string, tgt::mat4> *localDefs = nullptr);
/**
* Parses and interprets a formula input string.
* The string has to follow a given syntax to be parsed successfully:
* - whitespaces are not removed or skipped. if they appear in the wrong places the parsing will fail!
* - multiple computations are separated by a colon ';'
* - each computation is of the form <ResultName>=<Formula>
* - <Formula> is a combination of matrix multiplications: <MatrixId>*<MatrixId>*...
* - <MatrixId>s are always of the form '[<MatrixString>]_<Modifiers>
* - <MatrixString is a string parseable by \a MatrixProcessor::processMatrixString()
* - <Modifiers> is a string of modifiers as evaluated by \a MatrixProcessor::processModifierString()
*
* Notes:
* - Results of a previous computation can be reused in all following computations
* - If a result name starts with an underscore '_', the result will not be written to the data
* container, but it is still possible to use it in subsequent calulations
*
* Example:
* _ProbeToReference=[ReferenceToTracker]_I*[ProbeToTracker];_TipToProbe=[0.13758 0.0266467 0.00606382 -310.999 0.00447841 0.00887565 -0.137823 -18.5525 -0.0272137 0.133125 0.00797508 -105.741 0 0 0 1];TipToReference=[_ProbeToReference]*[_TipToProbe]
*
* This will assume two matrices "ProbeToTracker" and "ReferenceToTracker" in the data container and use two
* intermediate results _ProbeToReference and _TipToProbe that are not pushed to the data container to compute the
* final result "TipToReference" that is then pushed to the data container
*/
void parseString(const std::string & parserInput, DataContainer & dc);
DataContainer * _lastdc;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment