Notice to GitKraken users: A vulnerability has been found in the SSH key generation of GitKraken versions 7.6.0 to 8.0.0 (https://www.gitkraken.com/blog/weak-ssh-key-fix). If you use GitKraken and have generated a SSH key using one of these versions, please remove it both from your local workstation and from your LRZ GitLab profile.

21.10.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

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
* - if the string contains exactly 16 tokens, a direct matrix input is assumed: all