Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 35f624c6 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'openigtlink-support' into 'development'

Openigtlink support

openigtlink processor implementation and matrix processor.
changes to mprrenderer and mprdemo are not relevant (should be in another branch)
parents 1c783aef 43ed4020
...@@ -117,8 +117,9 @@ namespace campvis { ...@@ -117,8 +117,9 @@ namespace campvis {
_widgetMap.insert(std::make_pair(prop, propWidget)); _widgetMap.insert(std::make_pair(prop, propWidget));
_layout->addWidget(propWidget); _layout->addWidget(propWidget);
propWidget->setVisible(prop->isVisible());
prop->s_visibilityChanged.connect(this, &PropertyCollectionWidget::onPropertyVisibilityChanged); prop->s_visibilityChanged.connect(this, &PropertyCollectionWidget::onPropertyVisibilityChanged);
prop->s_visibilityChanged(prop);
} }
void PropertyCollectionWidget::removeProperty(std::map<AbstractProperty*, QWidget*>::iterator it) { void PropertyCollectionWidget::removeProperty(std::map<AbstractProperty*, QWidget*>::iterator it) {
......
...@@ -34,7 +34,7 @@ namespace campvis { ...@@ -34,7 +34,7 @@ namespace campvis {
/** /**
* Data Container to store a position and (optional) rotation as quaternion * Data Container to store a position and (optional) rotation as quaternion
*/ */
class PositionData : public AbstractData { class CAMPVIS_CORE_API PositionData : public AbstractData {
public: public:
/** /**
* Constructor, Creates a new light source. * Constructor, Creates a new light source.
...@@ -46,7 +46,7 @@ namespace campvis { ...@@ -46,7 +46,7 @@ namespace campvis {
/** /**
* Virtual destructor * Virtual destructor
*/ */
virtual ~PositionData();; virtual ~PositionData();
/// \see AbstractData::clone() /// \see AbstractData::clone()
virtual PositionData* clone() const;; virtual PositionData* clone() const;;
......
...@@ -34,7 +34,7 @@ namespace campvis { ...@@ -34,7 +34,7 @@ namespace campvis {
/** /**
* Data Container class for transforms. Stores a \a tgt::mat4 object. * Data Container class for transforms. Stores a \a tgt::mat4 object.
*/ */
class TransformData : public AbstractData { class CAMPVIS_CORE_API TransformData : public AbstractData {
public: public:
/** /**
* Constructor, Creates a new light source. * Constructor, Creates a new light source.
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "matrixprocessor.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/genericimagerepresentationlocal.h"
#include "core/tools/stringutils.h"
#include "core/datastructures/transformdata.h"
namespace campvis {
const std::string MatrixProcessor::loggerCat_ = "CAMPVis.modules.core.MatrixProcessor";
GenericOption<std::string> typeOptions[2] = {
GenericOption<std::string>("fixed", "Fixed Matrix"),
GenericOption<std::string>("data", "Matrix from Data Container")
};
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")
, p_matrixAModifiers("MatrixAModifiers", "Matrix A Modifiers")
, p_matrixBType("MatrixB_Type", "Matrix B Source", typeOptions, 2)
, p_matrixBID("MatrixB_ID", "Matrix B", "matrixB", DataNameProperty::READ)
, p_matrixBString("MatrixB_String", "Matrix B String", "identity")
, p_matrixBModifiers("MatrixBModifiers", "Matrix B Modifiers")
, p_targetMatrixID("TargetMatrixID", "Target Matrix ID", "ProbeToReference", DataNameProperty::WRITE)
, p_cameraProperty("Camera", "Exported Camera")
, _lastdc(nullptr)
{
addProperty(p_parserMode, INVALID_PROPERTIES);
addProperty(p_parserInputString, INVALID_PROPERTIES | INVALID_RESULT);
addProperty(p_matrixAType, INVALID_PROPERTIES | INVALID_RESULT);
addProperty(p_matrixAID, INVALID_RESULT);
addProperty(p_matrixAString, INVALID_RESULT);
addProperty(p_matrixAModifiers, INVALID_RESULT);
addProperty(p_matrixBType, INVALID_PROPERTIES | INVALID_RESULT);
addProperty(p_matrixBID, INVALID_RESULT);
addProperty(p_matrixBString, INVALID_RESULT);
addProperty(p_matrixBModifiers, INVALID_RESULT);
addProperty(p_targetMatrixID, INVALID_RESULT);
addProperty(p_cameraProperty, VALID);
invalidate(INVALID_PROPERTIES);
}
MatrixProcessor::~MatrixProcessor() {
if (_lastdc)
_lastdc->s_dataAdded.disconnect(this);
}
void MatrixProcessor::init() {
}
void MatrixProcessor::deinit() {
}
void MatrixProcessor::updateResult(DataContainer& data) {
#ifdef MATRIX_PROCESSOR_DEBUGGING
LINFO("Updating Result");
#endif
if (&data != _lastdc) {
if (_lastdc) {
_lastdc->s_dataAdded.disconnect(this);
}
data.s_dataAdded.connect(this, &MatrixProcessor::DataContainerDataAdded);
_lastdc = &data;
}
if (p_parserMode.getValue()) {
parseString(p_parserInputString.getValue(), data);
}
else {
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 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);
#endif
TransformData * td = new TransformData(result);
data.addData(p_targetMatrixID.getValue(), td);
}
validate(INVALID_RESULT);
}
void MatrixProcessor::updateProperties(DataContainer& dataContainer)
{
#ifdef MATRIX_PROCESSOR_DEBUGGING
LINFO("Updating Properties");
#endif
bool pmode = p_parserMode.getValue();
p_parserInputString.setVisible(pmode);
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);
}
}
//update the data name dependencies
_dataDependencies.clear();
auto l1 = StringUtils::split(p_parserInputString.getValue(), "[");
for (size_t i = 1, s = l1.size(); i < s; ++i) {
auto l2 = StringUtils::split(l1[i], "]");
//LDEBUG("Data Name: " << l2[0]);
_dataDependencies.insert(l2[0]);
}
validate(INVALID_PROPERTIES);
}
tgt::mat4 MatrixProcessor::processMatrixString(std::string matrixString, DataContainer& data, std::map<std::string, tgt::mat4> *localDefs)
{
std::vector<std::string> tokens = StringUtils::split(matrixString, " ");
if (tokens.size() == 0 || tokens[0] == "identity") {
return tgt::mat4(tgt::mat4::identity);
}
// if we have exactly 16 tokens, we assume we have a matrix in numerical form
else if (tokens.size() == 16) {
tgt::mat4 mat;
float * p = mat.elem;
for (int i = 0; i < 16; i++) {
*p = static_cast<float>(atof(tokens[i].c_str()));
p++;
}
return mat;
}
// if the first token is "rot", we create an angle axis rotation matrix with the specified arguments
else if (tokens[0] == "rot") {
if (tokens.size() != 5) {
LWARNING("Rotation matrix string does not have the correct number of arguments!");
return tgt::mat4::createIdentity();
}
float angle;
tgt::vec3 axis;
angle = static_cast<float>(atof(tokens[1].c_str()));
axis[0] = static_cast<float>(atof(tokens[2].c_str()));
axis[1] = static_cast<float>(atof(tokens[3].c_str()));
axis[2] = static_cast<float>(atof(tokens[4].c_str()));
return tgt::mat4::createRotation(angle, axis);
}
else if (tokens[0] == "trans") {
if (tokens.size() != 4) {
LWARNING("Translation matrix string does not have the correct number of arguments!");
return tgt::mat4::createIdentity();
}
tgt::vec3 translation;
translation[0] = static_cast<float>(atof(tokens[1].c_str()));
translation[1] = static_cast<float>(atof(tokens[2].c_str()));
translation[2] = static_cast<float>(atof(tokens[3].c_str()));
return tgt::mat4::createTranslation(translation);
}
else if (tokens[0] == "scale") {
if (tokens.size() != 2 && tokens.size() != 4) {
LWARNING("Scaling matrix string does not have the correct number of arguments!");
return tgt::mat4::createIdentity();
}
tgt::vec3 scale;
scale[0] = static_cast<float>(atof(tokens[1].c_str()));
if (tokens.size() == 4) {
scale[1] = static_cast<float>(atof(tokens[2].c_str()));
scale[2] = static_cast<float>(atof(tokens[3].c_str()));
}
else {
scale[1] = scale[2] = scale[0];
}
return tgt::mat4::createScale(scale);
}
// 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");
return tgt::mat4::createIdentity();
}
return td->getTransform();
}
}
tgt::mat4 MatrixProcessor::processModifierString(tgt::mat4 matrix, std::string modifiers)
{
size_t pos = 0;
tgt::mat4 result = matrix, tmp;
while (pos < modifiers.size()) {
switch (modifiers[pos]) {
case 'I':
if (!result.invert(tmp)) {
LWARNING("Matrix Inversion failed.");
}
else result = tmp;
break;
case 'T':
result = tgt::transpose(result);
break;
case '-':
result = tgt::mat4::zero - result;
break;
case 'r':
result = result.getRotationalPart();
break;
case 's':
result = tgt::mat4::createScale(result.getScalingPart());
break;
default:
LWARNING("Ignoring unknown modifier: " << modifiers[pos]);
}
++pos;
}
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 = campvis::StringUtils::trim(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 = campvis::StringUtils::trim(eqn.substr(0, equal_pos));
std::string formulaToEvaluate = campvis::StringUtils::trim(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 = campvis::StringUtils::trim(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
// _camera matrix is used to modify m_cameraProperty
for (auto it = results.begin(), end = results.end(); it != end; ++it) {
if (it->first[0] != '_') {
dc.addData(it->first, new TransformData(it->second));
}
if (it->first == "_camera") {
tgt::Camera cam = p_cameraProperty.getValue();
cam.setViewMatrix(it->second);
p_cameraProperty.setValue(cam);
}
}
}
void MatrixProcessor::DataContainerDataAdded(const std::string &name, const DataHandle &data)
{
if (p_parserMode.getValue()) {
if (_dataDependencies.find(name) != _dataDependencies.end())
invalidate(INVALID_RESULT);
}
else {
if (name == p_matrixAID.getValue() || name == p_matrixBID.getValue())
invalidate(INVALID_RESULT);
}
}
}
\ No newline at end of file
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#ifndef MATRIXPROCESSOR_H__
#define MATRIXPROCESSOR_H__
//#define MATRIX_PROCESSOR_DEBUGGING
#include <string>
#include <set>
#include <tgt/matrix.h>
#include "core/pipeline/abstractprocessor.h"
#include "core/properties/allproperties.h"
namespace campvis {
/**
* Matrix processor to perform some basic matrix arithmatic like combining two matrices.
*
* Takes two matrices as an input either from a string or from the data container
* (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.
* An additional feature of the Parser Mode is the option to export a matrix through the \a m_cameraProperty field. Please
* make sure that the m_cameraProperty is initialized and updated correctly regarding viewport changes (for example by setting
* the property as a target for a TrackballEventHandler) as this feature only modifies the position, lookat and up vector of the
* camera.
*
* 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
* adding a new MatrixProcessor that multiplies a hardcoded calibration matrix to the result or the inputs.
*/
class MatrixProcessor : public AbstractProcessor {
public:
enum SourceType {
FIXED = 0,
DATA = 1
};
/// Constructor
MatrixProcessor();
/// Destructor
virtual ~MatrixProcessor();
/// \see AbstractProcessor::init()
virtual void init();
/// \see AbstractProcessor::deinit()
virtual void deinit();
/// \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 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;
StringProperty p_matrixAModifiers; ///< modifier string to be applied to matrix A. \see MatrixProcessor::processModifierString()
GenericOptionProperty<std::string> p_matrixBType;
DataNameProperty p_matrixBID; ///< first Matrix input for the computation. \see MatrixProcessor::processMatrixString()
StringProperty p_matrixBString;
StringProperty p_matrixBModifiers; ///< modifier string to be applied to matrix A. \see MatrixProcessor::processModifierString()
DataNameProperty p_targetMatrixID; ///< name for the output matrix
CameraProperty p_cameraProperty;
void DataContainerDataAdded(const std::string& name, const DataHandle& data);
protected:
/// \see AbstractProcessor::updateResult()
virtual void updateResult(DataContainer& dataContainer);
/// \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
* \param modifiers a string containing modifiers that will be applied to the matrix from left to right.
* Possible Modifiers are:
* - _I_: invert matrix
* - _T_: transpose matrix
* - _r_: extract rotational part \see tgt::mat4::getRotationalPart()
* - _s_: extract scaling part \see tgt::mat4::getScalingPart()
* - _-_: negate componentwise
*
* i.e. a call with a modifier string "IT" will calculate the transpose of the inverse.
*/
tgt::mat4 processModifierString(tgt::mat4 matrix, std::string modifiers);
/**
* Processes a matrix string and returns the resulting matrix.
*
* \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:
* - an empty string or "identity" creates an identity matrix