<commit_before>/*
 * File:   FieldDetector.cpp
 * Author: Heinrich Mellmann
 *
 */

#include "FieldDetector.h"

#include <Tools/Math/ConvexHull.h>

using namespace std;

FieldDetector::FieldDetector()
:
  cameraID(CameraInfo::Bottom)
{
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_field_polygon", "mark polygonal boundary of the detected field on the image", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_corrected_field_polygon", "mark polygonal boundary of the detected field cutted by horizon on the image", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:setHoleImageAsField", "mark hole image as if field were detected", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_field_polygon_old", "mark polygonal boundary of the detected field before outlier detection", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_field_polygon_new", "mark new polygonal boundary of the detected field after outlier detection", false);

  getDebugParameterList().add(&theParameters);
}

FieldDetector::~FieldDetector()
{}

void FieldDetector::execute(CameraInfo::CameraID id)
{
  cameraID = id;

  bool simulateField = false;

  DEBUG_REQUEST("Vision:FieldDetector:setHoleImageAsField",
    simulateField = true;
  );

  if(simulateField)
  {
    vector<Vector2i> result;
    result.push_back(Vector2i(0,0));
    result.push_back(Vector2i(0,getImage().height()-1));
    result.push_back(Vector2i(getImage().width()-1, getImage().height()-0));
    result.push_back(Vector2i(getImage().width()-1,0));
    result.push_back(Vector2i(0,0));

    // create the polygon
    FieldPercept::FieldPoly fieldPoly;
    for(size_t i = 0; i < result.size(); i++)
    {
      fieldPoly.add(result[i]);
    }
    Math::LineSegment horizon(Vector2i(0, 0), Vector2i(getImage().width() - 1, 0));

    getFieldPercept().setField(fieldPoly, horizon);
    if(fieldPoly.getArea() >= 5600)
    {
      getFieldPercept().valid = true;
    }

    DEBUG_REQUEST( "Vision:FieldDetector:mark_field_polygon",
      size_t idx = 0;
      ColorClasses::Color color = getFieldPercept().valid ? ColorClasses::green : ColorClasses::red;
      for(size_t i = 1; i < result.size(); i++)
      {
        LINE_PX(color, result[idx].x, result[idx].y, result[i].x, result[i].y);
        idx = i;
      }
    );
  }
  else if(!getScanLineEdgelPercept().endPoints.empty())
  {
    static vector<Vector2i> points;
    points.clear();

    for(size_t i = 0; i < getScanLineEdgelPercept().endPoints.size(); i++)
    {
      const ScanLineEdgelPercept::EndPoint& p = getScanLineEdgelPercept().endPoints[i];
      if(p.greenFound && p.posInImage.y < static_cast<int>(getImage().height()) - 6)
      {
        points.push_back(p.posInImage);
      }
    }

    // move the outer points
    //points.front().x = 0;
    //points.back().x = getImage().width()-1;

    // lower image points
    //points.push_back(Vector2i(0,getImage().height()-1));
    //points.push_back(Vector2i(getImage().width()-1, getImage().height()-1));

    if(points.size() > 1)
    {
      Vector2i p1 = points.front();
      p1.y = getImage().height() - 1;
      Vector2i p2 = points.back();
      p2.y = getImage().height() - 1;

      points.push_back(p1);
      points.push_back(p2);

      if(getScanLineEdgelPercept().endPoints.front().posInImage.y < static_cast<int>(getImage().height()) - 6)
      {
        points.push_back(Vector2i(0, getScanLineEdgelPercept().endPoints.front().posInImage.y));
        points.push_back(Vector2i(0, getImage().height() - 1));
      }

      if(getScanLineEdgelPercept().endPoints.back().posInImage.y < static_cast<int>(getImage().height()) - 6)
      {
        points.push_back(Vector2i(getImage().width() - 1, getScanLineEdgelPercept().endPoints.back().posInImage.y));
        points.push_back(Vector2i(getImage().width() - 1, getImage().height() - 1));
      }

    }
    // calculate the convex hull
    vector<Vector2i> result = ConvexHull::convexHull(points);

    // create the polygon
    FieldPercept::FieldPoly fieldPoly;

    for(size_t i = 0; i < result.size(); i++)
    {
      fieldPoly.add(result[i]);
    }
    DEBUG_REQUEST("Vision:FieldDetector:mark_field_polygon_old",
      int idx = 0;
      for(int i = 1; i < fieldPoly.length; i++)
      {
        LINE_PX(ColorClasses::red, fieldPoly[idx].x, fieldPoly[idx].y, fieldPoly[i].x, fieldPoly[i].y);
        idx = i;
      }
    );

    // sort points by x value
    std::sort(points.begin(), points.end(), cmpVectorInstance);

    /*
    // remove points on the edge of the BodyContour
    std::vector<size_t> badPoints;
    for(size_t i = 2; i+2 < points.size(); i++)
    {
      if(points[i].y > 0)
      {
        //Vector2i dummyPoint = points[i];
        //dummyPoint.y += 6;
        //if(getBodyContour().isOccupied(dummyPoint))
        {
          badPoints.push_back(i);
        }
      }
    }
    if(badPoints.size() > 0)
    {
      for(size_t i = 0; i < badPoints.size(); i++)
      {
        // badPoints are ordered so the small indices are removed first
        points.erase(points.begin() + (badPoints[i] - i));
      }
    }*/

    // check outliers but keep first and last point in any case
    static std::vector<size_t> badPoints;
    badPoints.clear();
    for(size_t nLoop = 0; nLoop < 5; nLoop++)
    {
      badPoints.clear();
      for(size_t i = 2; i+2 < points.size(); i++)
      {
        std::vector<Vector2i> pointsCheck = points;
        pointsCheck.erase(pointsCheck.begin()+i);

        vector<Vector2i> resultCheck = ConvexHull::convexHull(pointsCheck);

        FieldPercept::FieldPoly fieldPolyCheck;
        for(size_t j = 0; j < resultCheck.size(); j++)
        {
          fieldPolyCheck.add(resultCheck[j]);
        }
        if(fieldPolyCheck.getArea() / fieldPoly.getArea() < theParameters.pruneThresholdArea)
        {
          badPoints.push_back(i);
        }
      }
      // remove outliers
      if(!badPoints.empty())
      {
        for(size_t i = 0; i < badPoints.size(); i++)
        {
          // badPoints are ordered so the small indices are removed first
          points.erase(points.begin() + (badPoints[i] - i));
        }
        result = ConvexHull::convexHull(points);
        // clear old polygon
        fieldPoly = FieldPercept::FieldPoly();
        for(size_t i = 0; i < result.size(); i++)
        {
          fieldPoly.add(result[i]);
        }
      } else
      {
        break;
      }
    }
    DEBUG_REQUEST("Vision:FieldDetector:mark_field_polygon_new",
      int idx = 0;
      for(int i = 1; i < fieldPoly.length; i++)
      {
        LINE_PX(ColorClasses::green, fieldPoly[idx].x, fieldPoly[idx].y, fieldPoly[i].x, fieldPoly[i].y);
        idx = i;
      }
    );

    // add field to percept
    getFieldPercept().setField(fieldPoly, getArtificialHorizon());
    // check result
    if(fieldPoly.getArea() >= 5600)
    {
      getFieldPercept().valid = true;
    }
    else
    {
      getFieldPercept().valid = false;
    }

    DEBUG_REQUEST( "Vision:FieldDetector:mark_field_polygon",
      size_t idx = 0;
      ColorClasses::Color color = getFieldPercept().valid ? ColorClasses::black : ColorClasses::red;
      for(size_t i = 1; i < result.size(); i++)
      {
        LINE_PX(color, result[idx].x, result[idx].y, result[i].x, result[i].y);
        idx = i;
      }
    );

    DEBUG_REQUEST( "Vision:FieldDetector:mark_corrected_field_polygon",
      int idx = 0;
      ColorClasses::Color color = getFieldPercept().valid ? ColorClasses::green : ColorClasses::red;
      const FieldPercept::FieldPoly& fieldpoly = getFieldPercept().getValidField();
      for(int i = 1; i < fieldpoly.length; i++)
      {
        LINE_PX(color, fieldpoly[idx].x, fieldpoly[idx].y, fieldpoly[i].x, fieldpoly[i].y);
        idx = i;
      }
    );
  }
}//end execute
<commit_msg>don't re-initialize FieldPoly all the time<commit_after>/*
 * File:   FieldDetector.cpp
 * Author: Heinrich Mellmann
 *
 */

#include "FieldDetector.h"

#include <Tools/Math/ConvexHull.h>

using namespace std;

FieldDetector::FieldDetector()
:
  cameraID(CameraInfo::Bottom)
{
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_field_polygon", "mark polygonal boundary of the detected field on the image", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_corrected_field_polygon", "mark polygonal boundary of the detected field cutted by horizon on the image", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:setHoleImageAsField", "mark hole image as if field were detected", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_field_polygon_old", "mark polygonal boundary of the detected field before outlier detection", false);
  DEBUG_REQUEST_REGISTER("Vision:FieldDetector:mark_field_polygon_new", "mark new polygonal boundary of the detected field after outlier detection", false);

  getDebugParameterList().add(&theParameters);
}

FieldDetector::~FieldDetector()
{}

void FieldDetector::execute(CameraInfo::CameraID id)
{
  cameraID = id;

  bool simulateField = false;

  DEBUG_REQUEST("Vision:FieldDetector:setHoleImageAsField",
    simulateField = true;
  );

  if(simulateField)
  {
    vector<Vector2i> result;
    result.push_back(Vector2i(0,0));
    result.push_back(Vector2i(0,getImage().height()-1));
    result.push_back(Vector2i(getImage().width()-1, getImage().height()-0));
    result.push_back(Vector2i(getImage().width()-1,0));
    result.push_back(Vector2i(0,0));

    // create the polygon
    FieldPercept::FieldPoly fieldPoly;
    for(size_t i = 0; i < result.size(); i++)
    {
      fieldPoly.add(result[i]);
    }
    Math::LineSegment horizon(Vector2i(0, 0), Vector2i(getImage().width() - 1, 0));

    getFieldPercept().setField(fieldPoly, horizon);
    if(fieldPoly.getArea() >= 5600)
    {
      getFieldPercept().valid = true;
    }

    DEBUG_REQUEST( "Vision:FieldDetector:mark_field_polygon",
      size_t idx = 0;
      ColorClasses::Color color = getFieldPercept().valid ? ColorClasses::green : ColorClasses::red;
      for(size_t i = 1; i < result.size(); i++)
      {
        LINE_PX(color, result[idx].x, result[idx].y, result[i].x, result[i].y);
        idx = i;
      }
    );
  }
  else if(!getScanLineEdgelPercept().endPoints.empty())
  {
    static vector<Vector2i> points;
    points.clear();

    for(size_t i = 0; i < getScanLineEdgelPercept().endPoints.size(); i++)
    {
      const ScanLineEdgelPercept::EndPoint& p = getScanLineEdgelPercept().endPoints[i];
      if(p.greenFound && p.posInImage.y < static_cast<int>(getImage().height()) - 6)
      {
        points.push_back(p.posInImage);
      }
    }

    // move the outer points
    //points.front().x = 0;
    //points.back().x = getImage().width()-1;

    // lower image points
    //points.push_back(Vector2i(0,getImage().height()-1));
    //points.push_back(Vector2i(getImage().width()-1, getImage().height()-1));

    if(points.size() > 1)
    {
      Vector2i p1 = points.front();
      p1.y = getImage().height() - 1;
      Vector2i p2 = points.back();
      p2.y = getImage().height() - 1;

      points.push_back(p1);
      points.push_back(p2);

      if(getScanLineEdgelPercept().endPoints.front().posInImage.y < static_cast<int>(getImage().height()) - 6)
      {
        points.push_back(Vector2i(0, getScanLineEdgelPercept().endPoints.front().posInImage.y));
        points.push_back(Vector2i(0, getImage().height() - 1));
      }

      if(getScanLineEdgelPercept().endPoints.back().posInImage.y < static_cast<int>(getImage().height()) - 6)
      {
        points.push_back(Vector2i(getImage().width() - 1, getScanLineEdgelPercept().endPoints.back().posInImage.y));
        points.push_back(Vector2i(getImage().width() - 1, getImage().height() - 1));
      }

    }
    // calculate the convex hull
    vector<Vector2i> result = ConvexHull::convexHull(points);

    // create the polygon
    static FieldPercept::FieldPoly fieldPoly;
    fieldPoly.clear();

    for(size_t i = 0; i < result.size(); i++)
    {
      fieldPoly.add(result[i]);
    }
    DEBUG_REQUEST("Vision:FieldDetector:mark_field_polygon_old",
      int idx = 0;
      for(int i = 1; i < fieldPoly.length; i++)
      {
        LINE_PX(ColorClasses::red, fieldPoly[idx].x, fieldPoly[idx].y, fieldPoly[i].x, fieldPoly[i].y);
        idx = i;
      }
    );

    // sort points by x value
    std::sort(points.begin(), points.end(), cmpVectorInstance);

    /*
    // remove points on the edge of the BodyContour
    std::vector<size_t> badPoints;
    for(size_t i = 2; i+2 < points.size(); i++)
    {
      if(points[i].y > 0)
      {
        //Vector2i dummyPoint = points[i];
        //dummyPoint.y += 6;
        //if(getBodyContour().isOccupied(dummyPoint))
        {
          badPoints.push_back(i);
        }
      }
    }
    if(badPoints.size() > 0)
    {
      for(size_t i = 0; i < badPoints.size(); i++)
      {
        // badPoints are ordered so the small indices are removed first
        points.erase(points.begin() + (badPoints[i] - i));
      }
    }*/

    // check outliers but keep first and last point in any case
    static std::vector<size_t> badPoints;
    badPoints.clear();
    for(size_t nLoop = 0; nLoop < 5; nLoop++)
    {
      badPoints.clear();
      for(size_t i = 2; i+2 < points.size(); i++)
      {
        std::vector<Vector2i> pointsCheck = points;
        pointsCheck.erase(pointsCheck.begin()+i);

        vector<Vector2i> resultCheck = ConvexHull::convexHull(pointsCheck);

        static FieldPercept::FieldPoly fieldPolyCheck;
        fieldPolyCheck.clear();
        for(size_t j = 0; j < resultCheck.size(); j++)
        {
          fieldPolyCheck.add(resultCheck[j]);
        }
        if(fieldPolyCheck.getArea() / fieldPoly.getArea() < theParameters.pruneThresholdArea)
        {
          badPoints.push_back(i);
        }
      }
      // remove outliers
      if(!badPoints.empty())
      {
        for(size_t i = 0; i < badPoints.size(); i++)
        {
          // badPoints are ordered so the small indices are removed first
          points.erase(points.begin() + (badPoints[i] - i));
        }
        result = ConvexHull::convexHull(points);
        // clear old polygon
        fieldPoly.clear();
        for(size_t i = 0; i < result.size(); i++)
        {
          fieldPoly.add(result[i]);
        }
      } else
      {
        break;
      }
    }
    DEBUG_REQUEST("Vision:FieldDetector:mark_field_polygon_new",
      int idx = 0;
      for(int i = 1; i < fieldPoly.length; i++)
      {
        LINE_PX(ColorClasses::green, fieldPoly[idx].x, fieldPoly[idx].y, fieldPoly[i].x, fieldPoly[i].y);
        idx = i;
      }
    );

    // add field to percept
    getFieldPercept().setField(fieldPoly, getArtificialHorizon());
    // check result
    if(fieldPoly.getArea() >= 5600)
    {
      getFieldPercept().valid = true;
    }
    else
    {
      getFieldPercept().valid = false;
    }

    DEBUG_REQUEST( "Vision:FieldDetector:mark_field_polygon",
      size_t idx = 0;
      ColorClasses::Color color = getFieldPercept().valid ? ColorClasses::black : ColorClasses::red;
      for(size_t i = 1; i < result.size(); i++)
      {
        LINE_PX(color, result[idx].x, result[idx].y, result[i].x, result[i].y);
        idx = i;
      }
    );

    DEBUG_REQUEST( "Vision:FieldDetector:mark_corrected_field_polygon",
      int idx = 0;
      ColorClasses::Color color = getFieldPercept().valid ? ColorClasses::green : ColorClasses::red;
      const FieldPercept::FieldPoly& fieldpoly = getFieldPercept().getValidField();
      for(int i = 1; i < fieldpoly.length; i++)
      {
        LINE_PX(color, fieldpoly[idx].x, fieldpoly[idx].y, fieldpoly[i].x, fieldpoly[i].y);
        idx = i;
      }
    );
  }
}//end execute
<|endoftext|>


<commit_before>/**
 * @file
 *
 * @brief Some common functions in use for testing framework
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#ifndef KDB_TESTS_HPP
#define KDB_TESTS_HPP

#include <kdb.hpp>
#include <kdbmacros.h>
#include <key.hpp>
#include <keyset.hpp>

#include <cstdlib>
#include <cstring>
#include <exception>
#include <iostream>
#include <string>

#include <gtest/gtest.h>

using namespace std;
using namespace kdb;

// -- Macros -------------------------------------------------------------------------------------------------------------------------------

#define succeed_if(x, y) ASSERT_TRUE (x) << y

#define exit_if_fail(expression, message)                                                                                \
	if (!(expression))                                                                                \
	{                                                                                \
		cerr << __FILE__ << ":" << __LINE__ << ": Failure" << endl;                                                                \
		cerr << "Value of: " << ELEKTRA_STRINGIFY (expression) << endl;                                                            \
		cerr << "  Actual: false" << endl;                                                                                \
		cerr << "Expected: true" << endl;                                                                                \
		cerr << message << endl;                                                                                \
		exit (1);                                                                                \
	}                                                                                \
	SUCCEED () << message

#define succeed_if_same(x, y, message) ASSERT_EQ (x, y) << message

#define compare_keyset(keySet1, keySet2) ASSERT_TRUE (compareKeySet (keySet1, keySet2))

// -- Functions ----------------------------------------------------------------------------------------------------------------------------

/**
 * @brief This function adds the string representation of a key to an assertion result.
 *
 * @param stream This parameter specifies the assertion result to which this function adds a string representation of `key`.
 * @param key This parameter stores the key this function converts to an assertion result.
 *
 * @returns An assertion result containing the string representation of `key`
 */
testing::AssertionResult & operator<< (testing::AssertionResult & stream, kdb::Key & key)
{
	stream << key.getName () << ": " << (key.isString () ? key.getString () : (key.getBinarySize () == 0 ? "NULL" : "BINARY"));
	key.rewindMeta ();

	key.rewindMeta ();
	while (key.nextMeta ())
	{
		stream << ", " << key.currentMeta ().getName () << ": " << key.currentMeta ().getString ();
	}
	stream << endl;
	return stream;
}

/**
 * @brief This function adds the string representation of a key set to an assertion result.
 *
 * @param stream This parameter specifies the assertion result to which this function adds a string representation of `keys`.
 * @param key This parameter stores the key set this function converts to an assertion result.
 *
 * @returns An assertion result containing the string representation of `keys`
 */
testing::AssertionResult & operator<< (testing::AssertionResult & stream, kdb::KeySet & keys)
{
	for (auto key : keys)
	{
		stream << key;
	}
	return stream;
}

/**
 * @brief This function checks if the meta key set of two keys are equal.
 *
 * @param key1 This parameter stores the first meta key set this function uses for comparison.
 * @param key2 This parameter stores the second meta key set this function uses for comparison.
 *
 * @retval true if all meta keys of `key1` and `key2` are equal
 * @retval false otherwise
 */
bool isMetaDataEqual (kdb::Key & key1, kdb::Key & key2)
{
	key1.rewindMeta ();
	key2.rewindMeta ();

	while (key1.nextMeta ())
	{
		key2.nextMeta ();
		if (!key2.currentMeta ()) return false;
		if (key1.currentMeta ().getName () != key2.currentMeta ().getName ()) return false;
		if (key1.currentMeta ().getString () != key2.currentMeta ().getString ()) return false;
	}

	return key1.nextMeta () == key2.nextMeta ();
}

/**
 * @brief This function checks if two keys are equal.
 *
 * @param key1 This parameter stores the first key this function compares.
 * @param key2 This parameter stores the second key this function compares.
 *
 * @retval true if all attributes of `key1` and `key2` are equal
 * @retval false otherwise
 */
bool isKeyEqual (kdb::Key & key1, kdb::Key & key2)
{
	if (key1.getName () != key2.getName ()) return false;
	if ((key1.isString () && key2.isBinary ()) || (key1.isBinary () && key2.isString ())) return false;
	if (key1.getBinarySize () != key2.getBinarySize ()) return false;
	if (memcmp (key1.getValue (), key2.getValue (), key1.getBinarySize ())) return false;
	return isMetaDataEqual (key1, key2);
}

/**
 * @brief This function checks if two key sets are equal.
 *
 * @param keys1 This parameter stores the first key set this function compares.
 * @param keys2 This parameter stores the second key set this function compares.
 *
 * @retval true if all keys of `keys1` and `keys2` are equal
 * @retval false otherwise
 */
bool isKeySetEqual (kdb::KeySet & keys1, kdb::KeySet & keys2)
{
	if (keys1.size () != keys2.size ()) return false;

	keys1.rewind ();
	keys2.rewind ();
	while (keys1.next ())
	{
		keys2.next ();
		if (!keys2.current ()) return false;
		kdb::Key key1 = keys1.current ();
		kdb::Key key2 = keys2.current ();
		if (!isKeyEqual (key1, key2)) return false;
	}

	return true;
}

/**
 * @brief This function compares the given key sets.
 *
 * @param keys1 This parameter stores the first key set this function compares.
 * @param keys2 This parameter stores the second key set this function compares.
 *
 * @retval AssertionSuccess if all keys of `keys1` and `keys2` are equal
 * @retval AssertionFailure otherwise
 */
testing::AssertionResult compareKeySet (kdb::KeySet & keys1, kdb::KeySet & keys2)
{
	if (isKeySetEqual (keys1, keys2))
		return testing::AssertionSuccess ();
	else
		return testing::AssertionFailure () << endl
						    << endl
						    << "First key set:" << endl
						    << endl
						    << keys1 << endl
						    << "——————————" << endl
						    << endl
						    << "Second key set:" << endl
						    << endl
						    << keys2 << endl;
}

#endif
<commit_msg>Test: Fix comparison of key sets<commit_after>/**
 * @file
 *
 * @brief Some common functions in use for testing framework
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 */

#ifndef KDB_TESTS_HPP
#define KDB_TESTS_HPP

#include <kdb.hpp>
#include <kdbmacros.h>
#include <key.hpp>
#include <keyset.hpp>

#include <cstdlib>
#include <cstring>
#include <exception>
#include <iostream>
#include <string>

#include <gtest/gtest.h>

using namespace std;
using namespace kdb;

// -- Macros -------------------------------------------------------------------------------------------------------------------------------

#define succeed_if(x, y) ASSERT_TRUE (x) << y

#define exit_if_fail(expression, message)                                                                                \
	if (!(expression))                                                                                \
	{                                                                                \
		cerr << __FILE__ << ":" << __LINE__ << ": Failure" << endl;                                                                \
		cerr << "Value of: " << ELEKTRA_STRINGIFY (expression) << endl;                                                            \
		cerr << "  Actual: false" << endl;                                                                                \
		cerr << "Expected: true" << endl;                                                                                \
		cerr << message << endl;                                                                                \
		exit (1);                                                                                \
	}                                                                                \
	SUCCEED () << message

#define succeed_if_same(x, y, message) ASSERT_EQ (x, y) << message

#define compare_keyset(keySet1, keySet2) ASSERT_TRUE (compareKeySet (keySet1, keySet2))

// -- Functions ----------------------------------------------------------------------------------------------------------------------------

/**
 * @brief This function adds the string representation of a key to an assertion result.
 *
 * @param stream This parameter specifies the assertion result to which this function adds a string representation of `key`.
 * @param key This parameter stores the key this function converts to an assertion result.
 *
 * @returns An assertion result containing the string representation of `key`
 */
testing::AssertionResult & operator<< (testing::AssertionResult & stream, kdb::Key & key)
{
	stream << key.getName () << ": " << (key.isString () ? key.getString () : (key.getBinarySize () == 0 ? "NULL" : "BINARY"));
	key.rewindMeta ();

	key.rewindMeta ();
	while (key.nextMeta ())
	{
		stream << ", " << key.currentMeta ().getName () << ": " << key.currentMeta ().getString ();
	}
	stream << endl;
	return stream;
}

/**
 * @brief This function adds the string representation of a key set to an assertion result.
 *
 * @param stream This parameter specifies the assertion result to which this function adds a string representation of `keys`.
 * @param key This parameter stores the key set this function converts to an assertion result.
 *
 * @returns An assertion result containing the string representation of `keys`
 */
testing::AssertionResult & operator<< (testing::AssertionResult & stream, kdb::KeySet & keys)
{
	for (auto key : keys)
	{
		stream << key;
	}
	return stream;
}

/**
 * @brief This function checks if the meta key set of two keys are equal.
 *
 * @param key1 This parameter stores the first meta key set this function uses for comparison.
 * @param key2 This parameter stores the second meta key set this function uses for comparison.
 *
 * @retval true if all meta keys of `key1` and `key2` are equal
 * @retval false otherwise
 */
bool isMetaDataEqual (kdb::Key & key1, kdb::Key & key2)
{
	key1.rewindMeta ();
	key2.rewindMeta ();

	while (key1.nextMeta ())
	{
		key2.nextMeta ();
		if (!key2.currentMeta ()) return false;
		if (key1.currentMeta ().getName () != key2.currentMeta ().getName ()) return false;
		if (key1.currentMeta ().getString () != key2.currentMeta ().getString ()) return false;
	}

	return key1.nextMeta () == key2.nextMeta ();
}

/**
 * @brief This function checks if two keys are equal.
 *
 * @param key1 This parameter stores the first key this function compares.
 * @param key2 This parameter stores the second key this function compares.
 *
 * @retval true if all attributes of `key1` and `key2` are equal
 * @retval false otherwise
 */
bool isKeyEqual (kdb::Key & key1, kdb::Key & key2)
{
	if (key1.getName () != key2.getName ()) return false;
	if ((key1.isString () && key2.isBinary ()) || (key1.isBinary () && key2.isString ())) return false;
	if (key1.getBinarySize () != key2.getBinarySize ()) return false;
	if (memcmp (key1.getValue (), key2.getValue (), key1.getBinarySize ())) return false;
	return isMetaDataEqual (key1, key2);
}

/**
 * @brief This function checks if two key sets are equal.
 *
 * @param keys1 This parameter stores the first key set this function compares.
 * @param keys2 This parameter stores the second key set this function compares.
 *
 * @retval true if all keys of `keys1` and `keys2` are equal
 * @retval false otherwise
 */
bool isKeySetEqual (kdb::KeySet & keys1, kdb::KeySet & keys2)
{
	if (keys1.size () != keys2.size ()) return false;

	keys1.rewind ();
	keys2.rewind ();
	while (keys1.next ())
	{
		keys2.next ();
		if (!keys2.current ()) return false;
		kdb::Key key1 = keys1.current ();
		kdb::Key key2 = keys2.current ();
		if (!isKeyEqual (key1, key2)) return false;
	}

	return keys1.next () == keys2.next ();
}

/**
 * @brief This function compares the given key sets.
 *
 * @param keys1 This parameter stores the first key set this function compares.
 * @param keys2 This parameter stores the second key set this function compares.
 *
 * @retval AssertionSuccess if all keys of `keys1` and `keys2` are equal
 * @retval AssertionFailure otherwise
 */
testing::AssertionResult compareKeySet (kdb::KeySet & keys1, kdb::KeySet & keys2)
{
	if (isKeySetEqual (keys1, keys2))
		return testing::AssertionSuccess ();
	else
		return testing::AssertionFailure () << endl
						    << endl
						    << "First key set:" << endl
						    << endl
						    << keys1 << endl
						    << "——————————" << endl
						    << endl
						    << "Second key set:" << endl
						    << endl
						    << keys2 << endl;
}

#endif
<|endoftext|>


<commit_before>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkPolygonToRingFilter.h"
#include "mitkMesh.h"
#include "mitkSurface.h"
#include "mitkPlaneGeometry.h"

#include <vnl/vnl_cross.h>
#include <vnl/vnl_quaternion.h>
#include <vnl/vnl_quaternion.txx>

#include <vtkPolyData.h>
#include <vtkPoints.h>
#include <vtkCellArray.h>
#include <vtkCardinalSpline.h>

#include <vector>

mitk::PolygonToRingFilter::PolygonToRingFilter()
: m_RingRadius(3.5f), m_RingResolution(30), m_SplineResolution(20)
{
  m_SplineX = vtkCardinalSpline::New();
  m_SplineY = vtkCardinalSpline::New();
  m_SplineZ = vtkCardinalSpline::New();
}

mitk::PolygonToRingFilter::~PolygonToRingFilter()
{
  m_SplineX->Delete();
  m_SplineY->Delete();
  m_SplineZ->Delete();
}

void mitk::PolygonToRingFilter::GenerateOutputInformation()
{
  mitk::Mesh::ConstPointer input  = this->GetInput();
  mitk::Surface::Pointer output = this->GetOutput();

  itkDebugMacro(<<"GenerateOutputInformation()");

  if(input.IsNull()) return;

  output->SetGeometry(static_cast<Geometry3D*>(input->GetGeometry()->Clone().GetPointer()));

  output->Resize( input->GetPointSetSeriesSize() );
}

void mitk::PolygonToRingFilter::GenerateData()
{
  mitk::Mesh::ConstPointer input  = this->GetInput();
  mitk::Surface::Pointer output = this->GetOutput();

  int t;
  for ( t = 0; t < input->GetPointSetSeriesSize(); ++t )
  {

    vtkPolyData *polyData = vtkPolyData::New();
    vtkPoints *vPoints = vtkPoints::New();
    vtkCellArray *polys = vtkCellArray::New();

    mitk::Mesh::PointType thisPoint;

    // iterate through all cells and build tubes
    Mesh::CellIterator cellIt, cellEnd;
    cellEnd = input->GetMesh( t )->GetCells()->End();
    for ( cellIt = input->GetMesh( t )->GetCells()->Begin();
          cellIt != cellEnd;
          ++cellIt )
    {
      m_PointList.clear();
      m_VectorList.clear();

      this->BuildPointAndVectorList(
        *cellIt->Value(), m_PointList, m_VectorList, t );
      this->BuildVtkTube( vPoints, polys, m_PointList, m_VectorList );
    }

    polyData->SetPoints( vPoints );
    vPoints->Delete();
    polyData->SetPolys( polys );
    polys->Delete();

    output->SetVtkPolyData( polyData, t );
    polyData->Delete();

  }

}

//sl: Stern Letzter
//sc: Stern Current=aktueller Stern
//idmax: Id des Strahls des aktuellen Sterns (sc), der am besten zum ersten Strahl vom letzten Stern (sl) passt.
//last_p: Mittelpunkt des letzten Sterns
//cur_p: Mittelpunkt des aktuellen Sterns
void mitk::PolygonToRingFilter::DrawCyl(vtkPoints *vPoints, vtkCellArray *polys,
                                        VectorListType &sl, VectorListType &sc, int idmax, Point3D & last_p, Point3D & cur_p)
{
  unsigned int i;
  //jetzt haben wir alles: sl0 wird mit sc->at(idmax) verbunden usw.
  VectorListType::iterator slit=sl.begin(), scit=sc.begin(), scend=sc.end();
  scit+=idmax;
  Point3D a,b;
  Point3D a_first,b_first;
  int a_firstID = 0, b_firstID = 0;

  int front[4];
  for(i=0;i<m_RingResolution;++i)
  {
    VnlVector v0,v1,v2,v3,normal;
    v0=a.Get_vnl_vector(); v1=b.Get_vnl_vector();
    a=last_p+*slit*m_RingRadius; b=cur_p+*scit*m_RingRadius;
    v2=b.Get_vnl_vector(); v3=a.Get_vnl_vector();
    normal=vnl_cross_3d(v1-v0,v3-v0);

    if(i!=0)
    {
      front[3]=vPoints->InsertNextPoint(v0[0],v0[1],v0[2]);
      front[2]=vPoints->InsertNextPoint(v1[0],v1[1],v1[2]);
      front[1]=vPoints->InsertNextPoint(v2[0],v2[1],v2[2]);
      front[0]=vPoints->InsertNextPoint(v3[0],v3[1],v3[2]);
      polys->InsertNextCell( 4, front );
      if(i==1)
      {
        a_firstID=front[3]; b_firstID=front[2]; //continue;
      }
    }

    ++slit; ++scit; if(scit==scend) scit=sc.begin();
  }
  front[3]=front[0];
  front[2]=front[1];
  front[1]=b_firstID;
  front[0]=a_firstID;
  polys->InsertNextCell( 4, front );
}

void mitk::PolygonToRingFilter::BuildVtkTube(vtkPoints *vPoints, vtkCellArray *polys, PointListType& ptList, VectorListType& vecList)
{
  PointListType::iterator pit  = ptList.begin(), pend = ptList.end();
  VectorListType::iterator vit = vecList.begin();

  Vector3D axis, last_v, next_v, s;
  Point3D cur_p,last_p;

  //Listen fr den Stern
  VectorListType *sl, *sc, *swp, sfirst, buf1, buf2;
  sl=&buf1; sc=&buf2;

  Vector3D a,b;
  Matrix3D m;
  //Initialisierung fr ersten Punkt
  //alternative1:
  //	last_v=*(vl.getLast()); next_v=*vit.current(); axis=last_v+next_v; s.cross(last_v,next_v); s.normalize();
  //alternative2:
  //	last_v=*(vl.getLast()); next_v=*vit.current(); s.cross(last_v,next_v); s.normalize();
  //	axis=next_v-last_v; axis.normalize(); aa.set(s, M_PI/2.0); m.set(aa); m.transform(&axis);
  //alternative3:
  last_v=vecList.back(); next_v=*vit; s.Set_vnl_vector( vnl_cross_3d(last_v.Get_vnl_vector(),next_v.Get_vnl_vector()) ); s.Normalize();
  a=last_v; b=next_v; a.Normalize(); b.Normalize(); axis=a+b; axis.Normalize();

  //Stern am ersten Punkt aufbauen
  m = vnl_quaternion<mitk::ScalarType>(axis.Get_vnl_vector(),2*vnl_math::pi/(double)m_RingResolution).rotation_matrix_transpose();
  unsigned int i;
  for(i=0;i<m_RingResolution;++i)
  {
    sfirst.push_back(s);
    s=m*s;
  }
  *sl=sfirst;
  last_p=*pit;
  ++pit; ++vit;

  //nun die Hauptschleife ber alle Punkte
  for ( ; pit != pend; ++pit, ++vit )
  {
    //		cur_p=*pit.current(); last_v=next_v; next_v=*vit.current(); axis=last_v+next_v; s.cross(last_v,next_v); s.normalize();
    cur_p=*pit; last_v=next_v; next_v=*vit; s.Set_vnl_vector( vnl_cross_3d(last_v.Get_vnl_vector(),next_v.Get_vnl_vector()) ); s.Normalize();
    //		axis=next_v-last_v; axis.normalize(); aa.set(s, M_PI/2.0); m.set(aa); m.transform(&axis);
    a=last_v; b=next_v; a.Normalize(); b.Normalize(); axis=a+b; axis.Normalize();

    //neuen Stern sc (SternCurrent) bauen und dabei Start fr neuen Stern suchen
    double max=0; int idmax=0; Vector3D sl0=*(sl->begin());
    m = vnl_quaternion<mitk::ScalarType>(axis.Get_vnl_vector(),2*vnl_math::pi/(double)m_RingResolution).rotation_matrix_transpose();
    for(i=0;i<m_RingResolution;++i)
    {
      sc->push_back(s);
      double tmp=s*sl0;
      if(tmp>max)
      {
        max=tmp;
        idmax=i;
      }
      s=m*s;
    }

    //sl: Stern Letzter
    //sc: Stern Current=aktueller Stern
    //idmax: Id des Strahls des aktuellen Sterns (sc), der am besten zum ersten Strahl vom letzten Stern (sl) passt.
    //last_p: Mittelpunkt des letzten Sterns
    //cur_p: Mittelpunkt des aktuellen Sterns
    DrawCyl(vPoints, polys, *sl, *sc, idmax, last_p, cur_p);

    //bergang zum nchsten
    last_p=cur_p;
    swp=sl; sl=sc; sc=swp; sc->clear();
  }

  //idmax fr Verbindung ersten mit letztem ausrechnen:
  double max=0; int idmax=0; Vector3D sl0=*(sl->begin());
  for(i=0;i<m_RingResolution;++i)
  {
    s=sfirst[i];
    double tmp=s*sl0;
    if(tmp>max)
    {
      max=tmp;
      idmax=i;
    }
  }
  cur_p=*ptList.begin();
  DrawCyl(vPoints, polys, *sl, sfirst, idmax, last_p, cur_p);
}

void
mitk::PolygonToRingFilter
::BuildPointAndVectorList( mitk::Mesh::CellType& cell,
  PointListType& ptList, VectorListType& vecList, int timeStep )
{
  // This method constructs a spline from the given point list and retrieves
  // a number of interpolated points from it to form a ring-like structure.
  //
  // To make the spline "closed", the end point is connected to the start
  // point. For ensuring smoothness at the start-end-point transition, the
  // (intrinsically non-circular) spline array is extended on both sides
  // by wrapping a number of points from the respective other side.
  //
  // The used VTK filters do principally support this kind of "closed" spline,
  // but it does not produce results as consistent as with the method used
  // here. Also, the spline class of VTK 4.4 has only poor support for
  // arbitrary parametric coordinates (t values in vtkSpline). VTK 5.0 has
  // better support, and also provides a new class vtkParametricSpline for
  // directly calculating 3D splines.

  // Remove points from previous call of this method
  m_SplineX->RemoveAllPoints();
  m_SplineY->RemoveAllPoints();
  m_SplineZ->RemoveAllPoints();

  int numberOfPoints = cell.GetNumberOfPoints();

  Mesh::PointType inputPoint;
  vtkFloatingPointType t, tStart(0), tEnd(0);

  // Add input points to the spline and assign each the parametric value t
  // derived from the point euclidean distances.
  int i;
  Mesh::PointIdIterator pit = cell.PointIdsEnd() - 3;
  for ( i = -3, t = 0.0; i < numberOfPoints + 3; ++i )
  {
    if ( i == 0 ) { tStart = t; }
    if ( i == numberOfPoints ) { tEnd = t; }

    inputPoint = this->GetInput()->GetPoint( *pit, timeStep );
    m_SplineX->AddPoint( t, inputPoint[0] );
    m_SplineY->AddPoint( t, inputPoint[1] );
    m_SplineZ->AddPoint( t, inputPoint[2] );

    ++pit;
    if ( pit == cell.PointIdsEnd() )
    {
      pit = cell.PointIdsBegin();
    }

    t += inputPoint.EuclideanDistanceTo(
      this->GetInput()->GetPoint( *pit, timeStep ) );
  }

  // Evaluate the spline for the desired number of points
  // (number of input points) * (spline resolution)
  Point3D point, firstPoint, lastPoint;
  int numberOfSegments = numberOfPoints * m_SplineResolution;
  vtkFloatingPointType step = (tEnd - tStart) / numberOfSegments;
  for ( i = 0, t = tStart; i < numberOfSegments; ++i, t += step )
  {
    FillVector3D( point,
      m_SplineX->Evaluate(t), m_SplineY->Evaluate(t), m_SplineZ->Evaluate(t)
    );

    ptList.push_back( point );

    if ( i == 0 )
    {
      firstPoint = point;
    }
    else
    {
      vecList.push_back( point - lastPoint );
    }
    lastPoint = point;
  }
  vecList.push_back( firstPoint - lastPoint );
}

const mitk::Mesh *mitk::PolygonToRingFilter::GetInput(void)
{
  if (this->GetNumberOfInputs() < 1)
  {
    return 0;
  }

  return static_cast<const mitk::Mesh * >
    (this->ProcessObject::GetInput(0) );
}

void mitk::PolygonToRingFilter::SetInput(const mitk::Mesh *input)
{
  // Process object is not const-correct so the const_cast is required here
  this->ProcessObject::SetNthInput(0,
    const_cast< mitk::Mesh * >( input ) );
}
<commit_msg>BUGFIX: replaced int with vtkIdType due to a mail from Christian Teich on the sourceforge list. He said, his compiler (?) argued about this.<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkPolygonToRingFilter.h"
#include "mitkMesh.h"
#include "mitkSurface.h"
#include "mitkPlaneGeometry.h"

#include <vnl/vnl_cross.h>
#include <vnl/vnl_quaternion.h>
#include <vnl/vnl_quaternion.txx>

#include <vtkPolyData.h>
#include <vtkPoints.h>
#include <vtkCellArray.h>
#include <vtkCardinalSpline.h>

#include <vector>

mitk::PolygonToRingFilter::PolygonToRingFilter()
: m_RingRadius(3.5f), m_RingResolution(30), m_SplineResolution(20)
{
  m_SplineX = vtkCardinalSpline::New();
  m_SplineY = vtkCardinalSpline::New();
  m_SplineZ = vtkCardinalSpline::New();
}

mitk::PolygonToRingFilter::~PolygonToRingFilter()
{
  m_SplineX->Delete();
  m_SplineY->Delete();
  m_SplineZ->Delete();
}

void mitk::PolygonToRingFilter::GenerateOutputInformation()
{
  mitk::Mesh::ConstPointer input  = this->GetInput();
  mitk::Surface::Pointer output = this->GetOutput();

  itkDebugMacro(<<"GenerateOutputInformation()");

  if(input.IsNull()) return;

  output->SetGeometry(static_cast<Geometry3D*>(input->GetGeometry()->Clone().GetPointer()));

  output->Resize( input->GetPointSetSeriesSize() );
}

void mitk::PolygonToRingFilter::GenerateData()
{
  mitk::Mesh::ConstPointer input  = this->GetInput();
  mitk::Surface::Pointer output = this->GetOutput();

  int t;
  for ( t = 0; t < input->GetPointSetSeriesSize(); ++t )
  {

    vtkPolyData *polyData = vtkPolyData::New();
    vtkPoints *vPoints = vtkPoints::New();
    vtkCellArray *polys = vtkCellArray::New();

    mitk::Mesh::PointType thisPoint;

    // iterate through all cells and build tubes
    Mesh::CellIterator cellIt, cellEnd;
    cellEnd = input->GetMesh( t )->GetCells()->End();
    for ( cellIt = input->GetMesh( t )->GetCells()->Begin();
          cellIt != cellEnd;
          ++cellIt )
    {
      m_PointList.clear();
      m_VectorList.clear();

      this->BuildPointAndVectorList(
        *cellIt->Value(), m_PointList, m_VectorList, t );
      this->BuildVtkTube( vPoints, polys, m_PointList, m_VectorList );
    }

    polyData->SetPoints( vPoints );
    vPoints->Delete();
    polyData->SetPolys( polys );
    polys->Delete();

    output->SetVtkPolyData( polyData, t );
    polyData->Delete();

  }

}

//sl: Stern Letzter
//sc: Stern Current=aktueller Stern
//idmax: Id des Strahls des aktuellen Sterns (sc), der am besten zum ersten Strahl vom letzten Stern (sl) passt.
//last_p: Mittelpunkt des letzten Sterns
//cur_p: Mittelpunkt des aktuellen Sterns
void mitk::PolygonToRingFilter::DrawCyl(vtkPoints *vPoints, vtkCellArray *polys,
                                        VectorListType &sl, VectorListType &sc, int idmax, Point3D & last_p, Point3D & cur_p)
{
  unsigned int i;
  //jetzt haben wir alles: sl0 wird mit sc->at(idmax) verbunden usw.
  VectorListType::iterator slit=sl.begin(), scit=sc.begin(), scend=sc.end();
  scit+=idmax;
  Point3D a,b;
  Point3D a_first,b_first;
  int a_firstID = 0, b_firstID = 0;

  vtkIdType front[4];
  for(i=0;i<m_RingResolution;++i)
  {
    VnlVector v0,v1,v2,v3,normal;
    v0=a.Get_vnl_vector(); v1=b.Get_vnl_vector();
    a=last_p+*slit*m_RingRadius; b=cur_p+*scit*m_RingRadius;
    v2=b.Get_vnl_vector(); v3=a.Get_vnl_vector();
    normal=vnl_cross_3d(v1-v0,v3-v0);

    if(i!=0)
    {
      front[3]=vPoints->InsertNextPoint(v0[0],v0[1],v0[2]);
      front[2]=vPoints->InsertNextPoint(v1[0],v1[1],v1[2]);
      front[1]=vPoints->InsertNextPoint(v2[0],v2[1],v2[2]);
      front[0]=vPoints->InsertNextPoint(v3[0],v3[1],v3[2]);
      polys->InsertNextCell( (vtkIdType) 4, front );
      if(i==1)
      {
        a_firstID=front[3]; b_firstID=front[2]; //continue;
      }
    }

    ++slit; ++scit; if(scit==scend) scit=sc.begin();
  }
  front[3]=front[0];
  front[2]=front[1];
  front[1]=b_firstID;
  front[0]=a_firstID;
  polys->InsertNextCell( 4, front );
}

void mitk::PolygonToRingFilter::BuildVtkTube(vtkPoints *vPoints, vtkCellArray *polys, PointListType& ptList, VectorListType& vecList)
{
  PointListType::iterator pit  = ptList.begin(), pend = ptList.end();
  VectorListType::iterator vit = vecList.begin();

  Vector3D axis, last_v, next_v, s;
  Point3D cur_p,last_p;

  //Listen fr den Stern
  VectorListType *sl, *sc, *swp, sfirst, buf1, buf2;
  sl=&buf1; sc=&buf2;

  Vector3D a,b;
  Matrix3D m;
  //Initialisierung fr ersten Punkt
  //alternative1:
  //	last_v=*(vl.getLast()); next_v=*vit.current(); axis=last_v+next_v; s.cross(last_v,next_v); s.normalize();
  //alternative2:
  //	last_v=*(vl.getLast()); next_v=*vit.current(); s.cross(last_v,next_v); s.normalize();
  //	axis=next_v-last_v; axis.normalize(); aa.set(s, M_PI/2.0); m.set(aa); m.transform(&axis);
  //alternative3:
  last_v=vecList.back(); next_v=*vit; s.Set_vnl_vector( vnl_cross_3d(last_v.Get_vnl_vector(),next_v.Get_vnl_vector()) ); s.Normalize();
  a=last_v; b=next_v; a.Normalize(); b.Normalize(); axis=a+b; axis.Normalize();

  //Stern am ersten Punkt aufbauen
  m = vnl_quaternion<mitk::ScalarType>(axis.Get_vnl_vector(),2*vnl_math::pi/(double)m_RingResolution).rotation_matrix_transpose();
  unsigned int i;
  for(i=0;i<m_RingResolution;++i)
  {
    sfirst.push_back(s);
    s=m*s;
  }
  *sl=sfirst;
  last_p=*pit;
  ++pit; ++vit;

  //nun die Hauptschleife ber alle Punkte
  for ( ; pit != pend; ++pit, ++vit )
  {
    //		cur_p=*pit.current(); last_v=next_v; next_v=*vit.current(); axis=last_v+next_v; s.cross(last_v,next_v); s.normalize();
    cur_p=*pit; last_v=next_v; next_v=*vit; s.Set_vnl_vector( vnl_cross_3d(last_v.Get_vnl_vector(),next_v.Get_vnl_vector()) ); s.Normalize();
    //		axis=next_v-last_v; axis.normalize(); aa.set(s, M_PI/2.0); m.set(aa); m.transform(&axis);
    a=last_v; b=next_v; a.Normalize(); b.Normalize(); axis=a+b; axis.Normalize();

    //neuen Stern sc (SternCurrent) bauen und dabei Start fr neuen Stern suchen
    double max=0; int idmax=0; Vector3D sl0=*(sl->begin());
    m = vnl_quaternion<mitk::ScalarType>(axis.Get_vnl_vector(),2*vnl_math::pi/(double)m_RingResolution).rotation_matrix_transpose();
    for(i=0;i<m_RingResolution;++i)
    {
      sc->push_back(s);
      double tmp=s*sl0;
      if(tmp>max)
      {
        max=tmp;
        idmax=i;
      }
      s=m*s;
    }

    //sl: Stern Letzter
    //sc: Stern Current=aktueller Stern
    //idmax: Id des Strahls des aktuellen Sterns (sc), der am besten zum ersten Strahl vom letzten Stern (sl) passt.
    //last_p: Mittelpunkt des letzten Sterns
    //cur_p: Mittelpunkt des aktuellen Sterns
    DrawCyl(vPoints, polys, *sl, *sc, idmax, last_p, cur_p);

    //bergang zum nchsten
    last_p=cur_p;
    swp=sl; sl=sc; sc=swp; sc->clear();
  }

  //idmax fr Verbindung ersten mit letztem ausrechnen:
  double max=0; int idmax=0; Vector3D sl0=*(sl->begin());
  for(i=0;i<m_RingResolution;++i)
  {
    s=sfirst[i];
    double tmp=s*sl0;
    if(tmp>max)
    {
      max=tmp;
      idmax=i;
    }
  }
  cur_p=*ptList.begin();
  DrawCyl(vPoints, polys, *sl, sfirst, idmax, last_p, cur_p);
}

void
mitk::PolygonToRingFilter
::BuildPointAndVectorList( mitk::Mesh::CellType& cell,
  PointListType& ptList, VectorListType& vecList, int timeStep )
{
  // This method constructs a spline from the given point list and retrieves
  // a number of interpolated points from it to form a ring-like structure.
  //
  // To make the spline "closed", the end point is connected to the start
  // point. For ensuring smoothness at the start-end-point transition, the
  // (intrinsically non-circular) spline array is extended on both sides
  // by wrapping a number of points from the respective other side.
  //
  // The used VTK filters do principally support this kind of "closed" spline,
  // but it does not produce results as consistent as with the method used
  // here. Also, the spline class of VTK 4.4 has only poor support for
  // arbitrary parametric coordinates (t values in vtkSpline). VTK 5.0 has
  // better support, and also provides a new class vtkParametricSpline for
  // directly calculating 3D splines.

  // Remove points from previous call of this method
  m_SplineX->RemoveAllPoints();
  m_SplineY->RemoveAllPoints();
  m_SplineZ->RemoveAllPoints();

  int numberOfPoints = cell.GetNumberOfPoints();

  Mesh::PointType inputPoint;
  vtkFloatingPointType t, tStart(0), tEnd(0);

  // Add input points to the spline and assign each the parametric value t
  // derived from the point euclidean distances.
  int i;
  Mesh::PointIdIterator pit = cell.PointIdsEnd() - 3;
  for ( i = -3, t = 0.0; i < numberOfPoints + 3; ++i )
  {
    if ( i == 0 ) { tStart = t; }
    if ( i == numberOfPoints ) { tEnd = t; }

    inputPoint = this->GetInput()->GetPoint( *pit, timeStep );
    m_SplineX->AddPoint( t, inputPoint[0] );
    m_SplineY->AddPoint( t, inputPoint[1] );
    m_SplineZ->AddPoint( t, inputPoint[2] );

    ++pit;
    if ( pit == cell.PointIdsEnd() )
    {
      pit = cell.PointIdsBegin();
    }

    t += inputPoint.EuclideanDistanceTo(
      this->GetInput()->GetPoint( *pit, timeStep ) );
  }

  // Evaluate the spline for the desired number of points
  // (number of input points) * (spline resolution)
  Point3D point, firstPoint, lastPoint;
  int numberOfSegments = numberOfPoints * m_SplineResolution;
  vtkFloatingPointType step = (tEnd - tStart) / numberOfSegments;
  for ( i = 0, t = tStart; i < numberOfSegments; ++i, t += step )
  {
    FillVector3D( point,
      m_SplineX->Evaluate(t), m_SplineY->Evaluate(t), m_SplineZ->Evaluate(t)
    );

    ptList.push_back( point );

    if ( i == 0 )
    {
      firstPoint = point;
    }
    else
    {
      vecList.push_back( point - lastPoint );
    }
    lastPoint = point;
  }
  vecList.push_back( firstPoint - lastPoint );
}

const mitk::Mesh *mitk::PolygonToRingFilter::GetInput(void)
{
  if (this->GetNumberOfInputs() < 1)
  {
    return 0;
  }

  return static_cast<const mitk::Mesh * >
    (this->ProcessObject::GetInput(0) );
}

void mitk::PolygonToRingFilter::SetInput(const mitk::Mesh *input)
{
  // Process object is not const-correct so the const_cast is required here
  this->ProcessObject::SetNthInput(0,
    const_cast< mitk::Mesh * >( input ) );
}
<|endoftext|>


<commit_before>// ================================================================================
// == This file is a part of TinkerBell UI Toolkit. (C) 2011-2013, Emil Segers  ==
// ==                   See tinkerbell.h for more information.                   ==
// ================================================================================

#include "tinkerbell.h"
#include "tb_skin.h"
#include "tb_widgets_reader.h"
#include "tb_language.h"
#include "tb_font_renderer.h"
#include "tb_addon.h"
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>

namespace tinkerbell {

static const char *empty = "";
inline void safe_delete(char *&str)
{
	if(str != empty && str)
		free(str);
	str = const_cast<char*>(empty);
}

// ================================================================

TBRenderer *g_renderer = nullptr;
TBSkin *g_tb_skin = nullptr;
TBWidgetsReader *g_widgets_reader = nullptr;
TBLanguage *g_tb_lng = nullptr;
TBFontManager *g_font_manager = nullptr;

bool init_tinkerbell(TBRenderer *renderer, const char *lng_file)
{
	g_renderer = renderer;
	g_tb_lng = new TBLanguage;
	g_tb_lng->Load(lng_file);
	g_font_manager = new TBFontManager();
	g_tb_skin = new TBSkin();
	g_widgets_reader = TBWidgetsReader::Create();
	return TBInitAddons();
}

void shutdown_tinkerbell()
{
	TBShutdownAddons();
	delete g_widgets_reader;
	delete g_tb_skin;
	delete g_font_manager;
	delete g_tb_lng;
}

// == TBRect ======================================================

bool TBRect::Intersects(const TBRect &rect) const
{
	if (IsEmpty() || rect.IsEmpty())
		return false;
	if (x + w > rect.x && x < rect.x + rect.w &&
		y + h > rect.y && y < rect.y + rect.h)
		return true;
	return false;
}

TBRect TBRect::Union(const TBRect &rect) const
{
	assert(!IsInsideOut());
	assert(!rect.IsInsideOut());

	if (IsEmpty())
		return rect;
	if (rect.IsEmpty())
		return *this;

	int minx = MIN(x, rect.x);
	int miny = MIN(y, rect.y);
	int maxx = x + w > rect.x + rect.w ?
				x + w : rect.x + rect.w;
	int maxy = y + h > rect.y + rect.h ?
				y + h : rect.y + rect.h;
	return TBRect(minx, miny, maxx - minx, maxy - miny);
}

TBRect TBRect::Clip(const TBRect &clip_rect) const
{
	assert(!clip_rect.IsInsideOut());
	TBRect tmp;
	if (!Intersects(clip_rect))
		return tmp;
	tmp.x = MAX(x, clip_rect.x);
	tmp.y = MAX(y, clip_rect.y);
	tmp.w = MIN(x + w, clip_rect.x + clip_rect.w) - tmp.x;
	tmp.h = MIN(y + h, clip_rect.y + clip_rect.h) - tmp.y;
	return tmp;
}

// ===================================================================

const char *stristr(const char *arg1, const char *arg2)
{
	const char *a, *b;

	for(;*arg1;*arg1++)
	{
		a = arg1;
		b = arg2;
		while(toupper(*a++) == toupper(*b++))
			if(!*b)
				return arg1;
	}
	return nullptr;
}

// == TBStr ==========================================================

TBStr::TBStr()
	: TBStrC(empty)
{
}

TBStr::TBStr(const char* str)
	: TBStrC(str == empty ? empty : strdup(str))
{
	if (!s)
		s = const_cast<char*>(empty);
}

TBStr::TBStr(const TBStr &str)
	: TBStrC(str.s == empty ? empty : strdup(str.s))
{
	if (!s)
		s = const_cast<char*>(empty);
}

TBStr::TBStr(const char* str, int len)
	: TBStrC(empty)
{
	Set(str, len);
}

TBStr::~TBStr()
{
	safe_delete(s);
}

bool TBStr::Set(const char* str, int len)
{
	safe_delete(s);
	if (len == TB_ALL_TO_TERMINATION)
		len = strlen(str);
	if (char *new_s = (char *) malloc(len + 1))
	{
		s = new_s;
		memcpy(s, str, len);
		s[len] = 0;
		return true;
	}
	return false;
}

bool TBStr::SetFormatted(const char* format, ...)
{
	safe_delete(s);
	if (!format)
		return true;
	va_list ap;
	int max_len = 64;
	char *new_s = nullptr;
	while (true)
	{
		if (char *tris_try_new_s = (char *) realloc(new_s, max_len))
		{
			new_s = tris_try_new_s;

			va_start(ap, format);
			int ret = vsnprintf(new_s, max_len, format, ap);
			va_end(ap);

			if (ret > max_len) // Needed size is known (+2 for termination and avoid ambiguity)
				max_len = ret + 2;
			else if (ret == -1 || ret >= max_len - 1) // Handle some buggy vsnprintf implementations.
				max_len *= 2;
			else // Everything fit for sure
			{
				s = new_s;
				return true;
			}
		}
		else
		{
			// Out of memory
			free(new_s);
			break;
		}
	}
	return false;
}

void TBStr::Clear()
{
	safe_delete(s);
}

void TBStr::Remove(int ofs, int len)
{
	assert(ofs >= 0 && (ofs + len <= (int)strlen(s)));
	if (!len)
		return;
	char *dst = s + ofs;
	char *src = s + ofs + len;
	while (*src != 0)
		*(dst++) = *(src++);
	*dst = *src;
}

bool TBStr::Insert(int ofs, const char *ins, int ins_len)
{
	int len1 = strlen(s);
	if (ins_len == TB_ALL_TO_TERMINATION)
		ins_len = strlen(ins);
	int newlen = len1 + ins_len;
	if (char *news = (char *) malloc(newlen + 1))
	{
		memcpy(&news[0], s, ofs);
		memcpy(&news[ofs], ins, ins_len);
		memcpy(&news[ofs + ins_len], &s[ofs], len1 - ofs);
		news[newlen] = 0;
		safe_delete(s);
		s = news;
		return true;
	}
	return false;
}

// == TBID ==================================================================================================

#ifdef _DEBUG

// Hash table for checking if we get any collisions (same hash value for TBID's created
// from different strings)
static TBHashTableAutoDeleteOf<TBID> all_id_hash;
static bool is_adding = false;

void TBID::Set(uint32 newid)
{
	id = newid;
	debug_string.Clear();
	if (!is_adding)
	{
		if (TBID *other_id = all_id_hash.Get(id))
		{
			assert(other_id->debug_string.IsEmpty());
		}
		else
		{
			is_adding = true;
			all_id_hash.Add(id, new TBID(*this));
			is_adding = false;
		}
	}
}

void TBID::Set(const TBID &newid)
{
	id = newid;
	TB_IF_DEBUG(debug_string.Set(newid.debug_string));
	if (!is_adding)
	{
		if (TBID *other_id = all_id_hash.Get(id))
		{
			// If this happens, 2 different strings result in the same hash.
			// It might be a good idea to change one of them, but it might not matter.
			assert(other_id->debug_string.Equals(debug_string));
		}
		else
		{
			is_adding = true;
			all_id_hash.Add(id, new TBID(*this));
			is_adding = false;
		}
	}
}

void TBID::Set(const char *string)
{
	id = TBGetHash(string);
	TB_IF_DEBUG(debug_string.Set(string));
	if (!is_adding)
	{
		if (TBID *other_id = all_id_hash.Get(id))
		{
			assert(other_id->debug_string.Equals(debug_string));
		}
		else
		{
			is_adding = true;
			all_id_hash.Add(id, new TBID(*this));
			is_adding = false;
		}
	}
}

#endif // _DEBUG

// == TBColor ===============================================================================================

void TBColor::SetFromString(const char *str, int len)
{
	int r, g, b, a;
	if (len == 9 && sscanf(str, "#%2x%2x%2x%2x", &r, &g, &b, &a) == 4)			// rrggbbaa
		Set(TBColor(r, g, b, a));
	else if (len == 7 && sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3)			// rrggbb
		Set(TBColor(r, g, b));
	else if (len == 5 && sscanf(str, "#%1x%1x%1x%1x", &r, &g, &b, &a) == 4)		// rgba
		Set(TBColor(r + (r << 4), g + (g << 4), b + (b << 4), a + (a << 4)));
	else if (len == 4 && sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3)			// rgb
		Set(TBColor(r + (r << 4), g + (g << 4), b + (b << 4)));
	else
		Set(TBColor());
}

// == TBDimensionConverter ==================================================================================

void TBDimensionConverter::SetDPI(int src_dpi, int dst_dpi)
{
	m_src_dpi = src_dpi;
	m_dst_dpi = dst_dpi;
	m_dst_dpi_str.Clear();
	if (NeedConversion())
		m_dst_dpi_str.SetFormatted("@%d", m_dst_dpi);
}

void TBDimensionConverter::GetDstDPIFilename(const char *filename, TBTempBuffer *tempbuf) const
{
	int dot_pos = 0;
	for (dot_pos = strlen(filename) - 1; dot_pos > 0; dot_pos--)
		if (filename[dot_pos] == '.')
			break;
	tempbuf->ResetAppendPos();
	tempbuf->Append(filename, dot_pos);
	tempbuf->AppendString(GetDstDPIStr());
	tempbuf->AppendString(filename + dot_pos);
}

int TBDimensionConverter::DpToPx(int dp) const
{
	if (dp <= TB_INVALID_DIMENSION || dp == 0)
		return dp;
	if (dp > 0)
	{
		dp = dp * m_dst_dpi / m_src_dpi;
		return MAX(dp, 1);
	}
	else
	{
		dp = dp * m_dst_dpi / m_src_dpi;
		return MIN(dp, -1);
	}
}

// == TBPx ==================================================================================================

void TBPx::SetFromString(const TBDimensionConverter &converter, const char *str)
{
	if (!str || !isdigit(*str))
		return;
	int len = strlen(str);
	int val = atoi(str);
	if (len > 2 && strcmp(str + len - 2, "dp") == 0)
		px = converter.DpToPx(val);
	else
		px = val;
}

void TBPx::SetDP(const TBDimensionConverter &converter, int dp)
{
	px = converter.DpToPx(dp);
}

}; // namespace tinkerbell
<commit_msg>Minor optimization<commit_after>// ================================================================================
// == This file is a part of TinkerBell UI Toolkit. (C) 2011-2013, Emil Segers  ==
// ==                   See tinkerbell.h for more information.                   ==
// ================================================================================

#include "tinkerbell.h"
#include "tb_skin.h"
#include "tb_widgets_reader.h"
#include "tb_language.h"
#include "tb_font_renderer.h"
#include "tb_addon.h"
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>

namespace tinkerbell {

static const char *empty = "";
inline void safe_delete(char *&str)
{
	if(str != empty && str)
		free(str);
	str = const_cast<char*>(empty);
}

// ================================================================

TBRenderer *g_renderer = nullptr;
TBSkin *g_tb_skin = nullptr;
TBWidgetsReader *g_widgets_reader = nullptr;
TBLanguage *g_tb_lng = nullptr;
TBFontManager *g_font_manager = nullptr;

bool init_tinkerbell(TBRenderer *renderer, const char *lng_file)
{
	g_renderer = renderer;
	g_tb_lng = new TBLanguage;
	g_tb_lng->Load(lng_file);
	g_font_manager = new TBFontManager();
	g_tb_skin = new TBSkin();
	g_widgets_reader = TBWidgetsReader::Create();
	return TBInitAddons();
}

void shutdown_tinkerbell()
{
	TBShutdownAddons();
	delete g_widgets_reader;
	delete g_tb_skin;
	delete g_font_manager;
	delete g_tb_lng;
}

// == TBRect ======================================================

bool TBRect::Intersects(const TBRect &rect) const
{
	if (IsEmpty() || rect.IsEmpty())
		return false;
	if (x + w > rect.x && x < rect.x + rect.w &&
		y + h > rect.y && y < rect.y + rect.h)
		return true;
	return false;
}

TBRect TBRect::Union(const TBRect &rect) const
{
	assert(!IsInsideOut());
	assert(!rect.IsInsideOut());

	if (IsEmpty())
		return rect;
	if (rect.IsEmpty())
		return *this;

	int minx = MIN(x, rect.x);
	int miny = MIN(y, rect.y);
	int maxx = x + w > rect.x + rect.w ?
				x + w : rect.x + rect.w;
	int maxy = y + h > rect.y + rect.h ?
				y + h : rect.y + rect.h;
	return TBRect(minx, miny, maxx - minx, maxy - miny);
}

TBRect TBRect::Clip(const TBRect &clip_rect) const
{
	assert(!clip_rect.IsInsideOut());
	TBRect tmp;
	if (!Intersects(clip_rect))
		return tmp;
	tmp.x = MAX(x, clip_rect.x);
	tmp.y = MAX(y, clip_rect.y);
	tmp.w = MIN(x + w, clip_rect.x + clip_rect.w) - tmp.x;
	tmp.h = MIN(y + h, clip_rect.y + clip_rect.h) - tmp.y;
	return tmp;
}

// ===================================================================

const char *stristr(const char *arg1, const char *arg2)
{
	const char *a, *b;

	for(;*arg1;*arg1++)
	{
		a = arg1;
		b = arg2;
		while(toupper(*a++) == toupper(*b++))
			if(!*b)
				return arg1;
	}
	return nullptr;
}

// == TBStr ==========================================================

TBStr::TBStr()
	: TBStrC(empty)
{
}

TBStr::TBStr(const char* str)
	: TBStrC(str == empty ? empty : strdup(str))
{
	if (!s)
		s = const_cast<char*>(empty);
}

TBStr::TBStr(const TBStr &str)
	: TBStrC(str.s == empty ? empty : strdup(str.s))
{
	if (!s)
		s = const_cast<char*>(empty);
}

TBStr::TBStr(const char* str, int len)
	: TBStrC(empty)
{
	Set(str, len);
}

TBStr::~TBStr()
{
	safe_delete(s);
}

bool TBStr::Set(const char* str, int len)
{
	safe_delete(s);
	if (len == TB_ALL_TO_TERMINATION)
		len = strlen(str);
	if (char *new_s = (char *) malloc(len + 1))
	{
		s = new_s;
		memcpy(s, str, len);
		s[len] = 0;
		return true;
	}
	return false;
}

bool TBStr::SetFormatted(const char* format, ...)
{
	safe_delete(s);
	if (!format)
		return true;
	va_list ap;
	int max_len = 64;
	char *new_s = nullptr;
	while (true)
	{
		if (char *tris_try_new_s = (char *) realloc(new_s, max_len))
		{
			new_s = tris_try_new_s;

			va_start(ap, format);
			int ret = vsnprintf(new_s, max_len, format, ap);
			va_end(ap);

			if (ret > max_len) // Needed size is known (+2 for termination and avoid ambiguity)
				max_len = ret + 2;
			else if (ret == -1 || ret >= max_len - 1) // Handle some buggy vsnprintf implementations.
				max_len *= 2;
			else // Everything fit for sure
			{
				s = new_s;
				return true;
			}
		}
		else
		{
			// Out of memory
			free(new_s);
			break;
		}
	}
	return false;
}

void TBStr::Clear()
{
	safe_delete(s);
}

void TBStr::Remove(int ofs, int len)
{
	assert(ofs >= 0 && (ofs + len <= (int)strlen(s)));
	if (!len)
		return;
	char *dst = s + ofs;
	char *src = s + ofs + len;
	while (*src != 0)
		*(dst++) = *(src++);
	*dst = *src;
}

bool TBStr::Insert(int ofs, const char *ins, int ins_len)
{
	int len1 = strlen(s);
	if (ins_len == TB_ALL_TO_TERMINATION)
		ins_len = strlen(ins);
	int newlen = len1 + ins_len;
	if (char *news = (char *) malloc(newlen + 1))
	{
		memcpy(&news[0], s, ofs);
		memcpy(&news[ofs], ins, ins_len);
		memcpy(&news[ofs + ins_len], &s[ofs], len1 - ofs);
		news[newlen] = 0;
		safe_delete(s);
		s = news;
		return true;
	}
	return false;
}

// == TBID ==================================================================================================

#ifdef _DEBUG

// Hash table for checking if we get any collisions (same hash value for TBID's created
// from different strings)
static TBHashTableAutoDeleteOf<TBID> all_id_hash;
static bool is_adding = false;

void TBID::Set(uint32 newid)
{
	id = newid;
	debug_string.Clear();
	if (!is_adding)
	{
		if (TBID *other_id = all_id_hash.Get(id))
		{
			assert(other_id->debug_string.IsEmpty());
		}
		else
		{
			is_adding = true;
			all_id_hash.Add(id, new TBID(*this));
			is_adding = false;
		}
	}
}

void TBID::Set(const TBID &newid)
{
	id = newid;
	TB_IF_DEBUG(debug_string.Set(newid.debug_string));
	if (!is_adding)
	{
		if (TBID *other_id = all_id_hash.Get(id))
		{
			// If this happens, 2 different strings result in the same hash.
			// It might be a good idea to change one of them, but it might not matter.
			assert(other_id->debug_string.Equals(debug_string));
		}
		else
		{
			is_adding = true;
			all_id_hash.Add(id, new TBID(*this));
			is_adding = false;
		}
	}
}

void TBID::Set(const char *string)
{
	id = TBGetHash(string);
	TB_IF_DEBUG(debug_string.Set(string));
	if (!is_adding)
	{
		if (TBID *other_id = all_id_hash.Get(id))
		{
			assert(other_id->debug_string.Equals(debug_string));
		}
		else
		{
			is_adding = true;
			all_id_hash.Add(id, new TBID(*this));
			is_adding = false;
		}
	}
}

#endif // _DEBUG

// == TBColor ===============================================================================================

void TBColor::SetFromString(const char *str, int len)
{
	int r, g, b, a;
	if (len == 9 && sscanf(str, "#%2x%2x%2x%2x", &r, &g, &b, &a) == 4)			// rrggbbaa
		Set(TBColor(r, g, b, a));
	else if (len == 7 && sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3)			// rrggbb
		Set(TBColor(r, g, b));
	else if (len == 5 && sscanf(str, "#%1x%1x%1x%1x", &r, &g, &b, &a) == 4)		// rgba
		Set(TBColor(r + (r << 4), g + (g << 4), b + (b << 4), a + (a << 4)));
	else if (len == 4 && sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3)			// rgb
		Set(TBColor(r + (r << 4), g + (g << 4), b + (b << 4)));
	else
		Set(TBColor());
}

// == TBDimensionConverter ==================================================================================

void TBDimensionConverter::SetDPI(int src_dpi, int dst_dpi)
{
	m_src_dpi = src_dpi;
	m_dst_dpi = dst_dpi;
	m_dst_dpi_str.Clear();
	if (NeedConversion())
		m_dst_dpi_str.SetFormatted("@%d", m_dst_dpi);
}

void TBDimensionConverter::GetDstDPIFilename(const char *filename, TBTempBuffer *tempbuf) const
{
	int dot_pos = 0;
	for (dot_pos = strlen(filename) - 1; dot_pos > 0; dot_pos--)
		if (filename[dot_pos] == '.')
			break;
	tempbuf->ResetAppendPos();
	tempbuf->Append(filename, dot_pos);
	tempbuf->AppendString(GetDstDPIStr());
	tempbuf->AppendString(filename + dot_pos);
}

int TBDimensionConverter::DpToPx(int dp) const
{
	if (dp <= TB_INVALID_DIMENSION || dp == 0 || !NeedConversion())
		return dp;
	if (dp > 0)
	{
		dp = dp * m_dst_dpi / m_src_dpi;
		return MAX(dp, 1);
	}
	else
	{
		dp = dp * m_dst_dpi / m_src_dpi;
		return MIN(dp, -1);
	}
}

// == TBPx ==================================================================================================

void TBPx::SetFromString(const TBDimensionConverter &converter, const char *str)
{
	if (!str || !isdigit(*str))
		return;
	int len = strlen(str);
	int val = atoi(str);
	if (len > 2 && strcmp(str + len - 2, "dp") == 0)
		px = converter.DpToPx(val);
	else
		px = val;
}

void TBPx::SetDP(const TBDimensionConverter &converter, int dp)
{
	px = converter.DpToPx(dp);
}

}; // namespace tinkerbell
<|endoftext|>


<commit_before>#include "main.h"

// --- Params --- //

NoiseParams noise_params;
WaterParams water_params;
LightParams light_params;
TextureParams texture_params;
WindowParams window_params{ WIDTH, HEIGHT };
GridParams grid_params;

// --- Scene Objects --- //

Grid grid;
Water water;
Box box;
Sky sky;

// --- Buffers --- //

Framebuffer fbw(WIDTH, HEIGHT);
Depthbuffer fb_water_depth(WIDTH, HEIGHT);

// --- Textures --- //

GLuint tex_height;
GLuint tex_mirror;
GLuint tex_water_depth;
GLuint tex_normal_map;

// --- Other --- //

NoiseGenerator noise_generator(&tex_height, &noise_params);
Camera camera(&window_params);

// --- TODO: refactor and move bezier related code to a better location */

NAVIGATION_MODE navmode;

Bezier bezier;

// Gets called when the windows is resized.
void resize_callback(int width, int height) {
    window_params.width = width;
	window_params.height = height;

    glViewport(0, 0, width, height);
	fbw.resize(width, height);
	fb_water_depth.resize(width, height);

	camera.compute_projection_matrix();
}

void compute_height_map() {
	noise_generator.renderFractal();
	box.set_height_texture(tex_height);
	grid.set_height_texture(tex_height);
	water.set_height_texture(tex_height);
	water.set_mirror_texture(tex_mirror);
	water.set_normal_map(tex_normal_map);
}

void init(){

    // Sets background color.
    glClearColor(/*gray*/ .937,.937,.937, /*solid*/1.0);

    // Enable depth test.
    glEnable(GL_DEPTH_TEST);

	bezier.init();

	initParams();
	initTextures();
	initSceneObjects();

	noise_generator.init();

	navmode = FREE;

	compute_height_map();

#ifdef WITH_ANTTWEAKBAR

	initAntTwBar(&grid_params, &window_params, &noise_params, &water_params, &texture_params);
#endif
	check_error_gl();
}

// Gets called for every frame.
void display(){
	opengp::update_title_fps("Terrain");

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	mat4 view_matrix = camera.get_view_matrix();
	mat4 view_matrix_mirrored = camera.get_view_matrix_mirrored(); // for water reflections

	if (navmode == BEZIER) {
		vec3 cam_pos(2.0f, 2.0f, 2.0f);
		vec3 cam_look(0.0f, 0.0f, 0.0f);
		vec3 cam_up(0.0f, 0.0f, 1.0f);

		float t = (glfwGetTime() - bezier.get_start_time()) / bezier.get_travel_time();

		if (t >= 1) {
			bezier.set_start_time(glfwGetTime());
			t = t - 1;
		}

		bezier.pos_curve_sample_point(t, cam_pos);
		bezier.cam_look_sample_point(t, cam_look);

		view_matrix = Eigen::lookAt(cam_pos, cam_look, cam_up);
	} else {
		// FREE/default mode
		camera.move();
	}

	// Render the water reflect
	fbw.bind();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	grid.draw(view_matrix_mirrored, camera.get_projection_matrix(), true);
	fbw.unbind();
	glViewport(0, 0, window_params.width, window_params.height);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	fb_water_depth.bind();
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	grid.draw(view_matrix, camera.get_projection_matrix(), false);
	fb_water_depth.unbind();

	grid.draw(view_matrix, camera.get_projection_matrix(), false);
	water.draw(view_matrix, camera.get_projection_matrix());
	box.draw(view_matrix, camera.get_projection_matrix());
	sky.draw(view_matrix, camera.get_projection_matrix());

#ifdef WITH_ANTTWEAKBAR
	TwDraw();
#endif
}

void cleanup(){
#ifdef WITH_ANTTWEAKBAR
    TwTerminate();
#endif
	bezier.cleanup();
}

int main(int, char**){
    glfwInitWindowSize(window_params.width, window_params.height);
    glfwCreateWindow("Terrain");
    glfwDisplayFunc(display);

	glfwSetWindowSizeCallback(OnWindowSize);
	glfwSetMouseButtonCallback(OnMouseButton);
	glfwSetMousePosCallback(OnMousePos);
	glfwSetMouseWheelCallback(OnMouseWheel);
	glfwSetKeyCallback(OnKey);
	glfwSetCharCallback(OnChar);

    init();
    glfwMainLoop();
    cleanup();
    return EXIT_SUCCESS;
}

// --- Helpers --- //

void initParams() {

	// --- Window ---
	window_params.width = WIDTH;
	window_params.height = HEIGHT;

	// --- Grid ---
	grid_params.resolution = 100;
	grid_params.length = 2;
	grid_params.width = 2;

	// --- Noise ---
	noise_params.noise_type = PERLIN_NOISE;
	noise_params.fractal_type = FBM;
	noise_params.noise_effect = NO_EFFECT;
	noise_params.fractal_effect = NO_EFFECT;
	noise_params.height = 1;
	noise_params.width = 1;
	noise_params.offset = 0.0f;
	noise_params.amplitude = 1.0f;
	noise_params.H = 1.2f;
	noise_params.lacunarity = 2;
	noise_params.octaves = 12;
	noise_params.seed = glfwGetTime();
	noise_params.seed -= floor(noise_params.seed);

	// --- Water ---
	water_params.height = 0;
	water_params.color = vec3(0.4f, 0.55f, 0.6f);
	water_params.depth_alpha_factor = 1.4f;
	water_params.depth_color_factor = 0.05f;
	water_params.transparency = 0.4f;

	water_params.reflection_factor = 0.25f;
	/*water_params.waves_speed = 0.04f;
	water_params.waves_tile_factor = 2.0;
	water_params.waves_amplitude = 1.0f;*/

	// --- Light ---
	light_params.Ia = vec3(0.7f, 0.7f, 0.7f);
	light_params.Id = vec3(0.3f, 0.3f, 0.3f);
	light_params.position = vec3(2.0f, 2.0f, 2.0f);

	// --- Texture ---
	texture_params.sand_min_height = -1;
	texture_params.sand_max_height = 0.15f;
	texture_params.grass_max_height = 1.0f;
	texture_params.sand_max_slope = 0.92f;
	texture_params.grass_max_slope = 0.9f;

	texture_params.grass_h_transition = 3;
	texture_params.grass_s_transition = 50;
	texture_params.sand_h_transition = 5;
	texture_params.sand_s_transition = 50;
}

void initSceneObjects() {
	box.init(&grid_params, &water_params, &noise_params);
	grid.init(&grid_params, &light_params, &texture_params);
	tex_mirror = fbw.init_texture();
	fbw.init(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE);
	tex_water_depth = fb_water_depth.init_texture();
	fb_water_depth.init();

	water.init(&grid_params, &water_params, &light_params);
	water.set_depth_texture(tex_water_depth);
	sky.init();

	check_error_gl();
}

void initTextures() {
	glGenTextures(1, &tex_normal_map);
	glBindTexture(GL_TEXTURE_2D, tex_normal_map);
	glfwLoadTexture2D("textures/water/tex_normal_map.tga", 0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

// --- Callbacks --- //

// Callback function called by GLFW when a mouse button is clicked
void GLFWCALL OnMouseButton(int glfwButton, int glfwAction)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventMouseButtonGLFW(glfwButton, glfwAction))   // Send event to AntTweakBar
	{
#endif
		if (navmode == BEZIER) {
			std::cout << "[Warning] No mouse interaction in Bezier mode.\n" << std::flush;
			return;
		}

		// Event not handled by AntTweakBar, we handle it ourselves
		camera.mouse_button(glfwButton, glfwAction);
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW when mouse has moved
void GLFWCALL OnMousePos(int mouseX, int mouseY)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventMousePosGLFW(mouseX, mouseY))  // Send event to AntTweakBar
	{
#endif
		if (navmode == BEZIER) {
			return;
		}

		camera.mouse_pos(mouseX, mouseY);
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW on mouse wheel event
void GLFWCALL OnMouseWheel(int pos)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventMouseWheelGLFW(pos))   // Send event to AntTweakBar
	{
#endif
		// Nothing for the moment
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW on key event
void GLFWCALL OnKey(int glfwKey, int glfwAction)
{

#ifdef WITH_ANTTWEAKBAR
	if (!TwEventKeyGLFW(glfwKey, glfwAction))  // Send event to AntTweakBar
	{
#endif

		if (glfwKey == GLFW_KEY_ESC && glfwAction == GLFW_PRESS) // Want to quit?
			glfwCloseWindow();
		else
		{
			camera.keyboard(glfwKey, glfwAction);

			if (glfwAction != GLFW_RELEASE) return; ///< only act on release

			switch (glfwKey){
			case '1':
				navmode = FREE;
				std::cout << "[Info] Running in free mode\n" << std::flush;
				isVisible(true);
				break;
			case '2':
				navmode = BEZIER;
				std::cout << "[Info] Running in bezier mode\n" << std::flush;
				bezier.set_start_time(glfwGetTime());
				isVisible(false);
				break;
			default:
				std::cout << "[Warning] No keyboard interaction in bezier mode.\n" << std::flush;
				break;
			}
		}
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW on char event
void GLFWCALL OnChar(int glfwChar, int glfwAction)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventCharGLFW(glfwChar, glfwAction))    // Send event to AntTweakBar
	{
#endif
		// Nothing for the moment
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW when window size changes
void GLFWCALL OnWindowSize(int width, int height)
{
#ifdef WITH_ANTTWEAKBAR
	// Send the new window size to AntTweakBar
	TwWindowSize(width, height);
#endif

	resize_callback(width, height);
}<commit_msg>Ready to merge<commit_after>#include "main.h"

// --- Params --- //

NoiseParams noise_params;
WaterParams water_params;
LightParams light_params;
TextureParams texture_params;
WindowParams window_params{ WIDTH, HEIGHT };
GridParams grid_params;

// --- Scene Objects --- //

Grid grid;
Water water;
Box box;
Sky sky;

// --- Buffers --- //

Framebuffer fbw(WIDTH, HEIGHT);
Depthbuffer fb_water_depth(WIDTH, HEIGHT);

// --- Textures --- //

GLuint tex_height;
GLuint tex_mirror;
GLuint tex_water_depth;
GLuint tex_normal_map;

// --- Camera --- //
Camera camera(&window_params);
NAVIGATION_MODE navmode;
Bezier bezier;

// --- Other --- //

NoiseGenerator noise_generator(&tex_height, &noise_params);

// Gets called when the windows is resized.
void resize_callback(int width, int height) {
    window_params.width = width;
	window_params.height = height;

    glViewport(0, 0, width, height);
	fbw.resize(width, height);
	fb_water_depth.resize(width, height);

	camera.compute_projection_matrix();
}

void compute_height_map() {
	noise_generator.renderFractal();
	box.set_height_texture(tex_height);
	grid.set_height_texture(tex_height);
	water.set_height_texture(tex_height);
	water.set_mirror_texture(tex_mirror);
	water.set_normal_map(tex_normal_map);
}

void init(){

    // Sets background color.
    glClearColor(/*gray*/ .937,.937,.937, /*solid*/1.0);

    // Enable depth test.
    glEnable(GL_DEPTH_TEST);

	bezier.init();

	initParams();
	initTextures();
	initSceneObjects();

	noise_generator.init();

	navmode = FREE;

	compute_height_map();

#ifdef WITH_ANTTWEAKBAR
	initAntTwBar(&grid_params, &window_params, &noise_params, &water_params, &texture_params);
#endif
	check_error_gl();
}

// Gets called for every frame.
void display(){
	opengp::update_title_fps("Terrain");

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	mat4 view_matrix = camera.get_view_matrix();
	mat4 view_matrix_mirrored = camera.get_view_matrix_mirrored(); // for water reflections

	if (navmode == BEZIER) {
		vec3 cam_pos(2.0f, 2.0f, 2.0f);
		vec3 cam_look(0.0f, 0.0f, 0.0f);
		vec3 cam_up(0.0f, 0.0f, 1.0f);

		float t = (glfwGetTime() - bezier.get_start_time()) / bezier.get_travel_time();

		if (t >= 1) {
			bezier.set_start_time(glfwGetTime());
			t = t - 1;
		}

		bezier.pos_curve_sample_point(t, cam_pos);
		bezier.cam_look_sample_point(t, cam_look);

		view_matrix = Eigen::lookAt(cam_pos, cam_look, cam_up);
	} else {
		// FREE/default mode
		camera.move();
	}

	// Render the water reflect
	fbw.bind();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	grid.draw(view_matrix_mirrored, camera.get_projection_matrix(), true);
	fbw.unbind();
	glViewport(0, 0, window_params.width, window_params.height);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	fb_water_depth.bind();
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	grid.draw(view_matrix, camera.get_projection_matrix(), false);
	fb_water_depth.unbind();

	grid.draw(view_matrix, camera.get_projection_matrix(), false);
	water.draw(view_matrix, camera.get_projection_matrix());
	box.draw(view_matrix, camera.get_projection_matrix());
	sky.draw(view_matrix, camera.get_projection_matrix());

#ifdef WITH_ANTTWEAKBAR
	TwDraw();
#endif
}

void cleanup(){
#ifdef WITH_ANTTWEAKBAR
    TwTerminate();
#endif
	bezier.cleanup();
}

int main(int, char**){
    glfwInitWindowSize(window_params.width, window_params.height);
    glfwCreateWindow("Terrain");
    glfwDisplayFunc(display);

	glfwSetWindowSizeCallback(OnWindowSize);
	glfwSetMouseButtonCallback(OnMouseButton);
	glfwSetMousePosCallback(OnMousePos);
	glfwSetMouseWheelCallback(OnMouseWheel);
	glfwSetKeyCallback(OnKey);
	glfwSetCharCallback(OnChar);

    init();
    glfwMainLoop();
    cleanup();
    return EXIT_SUCCESS;
}

// --- Helpers --- //

void initParams() {

	// --- Window ---
	window_params.width = WIDTH;
	window_params.height = HEIGHT;

	// --- Grid ---
	grid_params.resolution = 100;
	grid_params.length = 2;
	grid_params.width = 2;

	// --- Noise ---
	noise_params.noise_type = PERLIN_NOISE;
	noise_params.fractal_type = FBM;
	noise_params.noise_effect = NO_EFFECT;
	noise_params.fractal_effect = NO_EFFECT;
	noise_params.height = 1;
	noise_params.width = 1;
	noise_params.offset = 0.0f;
	noise_params.amplitude = 1.0f;
	noise_params.H = 1.2f;
	noise_params.lacunarity = 2;
	noise_params.octaves = 12;
	noise_params.seed = glfwGetTime();
	noise_params.seed -= floor(noise_params.seed);

	// --- Water ---
	water_params.height = 0;
	water_params.color = vec3(0.4f, 0.55f, 0.6f);
	water_params.depth_alpha_factor = 1.4f;
	water_params.depth_color_factor = 0.05f;
	water_params.transparency = 0.4f;

	water_params.reflection_factor = 0.25f;
	/*water_params.waves_speed = 0.04f;
	water_params.waves_tile_factor = 2.0;
	water_params.waves_amplitude = 1.0f;*/

	// --- Light ---
	light_params.Ia = vec3(0.7f, 0.7f, 0.7f);
	light_params.Id = vec3(0.3f, 0.3f, 0.3f);
	light_params.position = vec3(2.0f, 2.0f, 2.0f);

	// --- Texture ---
	texture_params.sand_min_height = -1;
	texture_params.sand_max_height = 0.15f;
	texture_params.grass_max_height = 1.0f;
	texture_params.sand_max_slope = 0.92f;
	texture_params.grass_max_slope = 0.9f;

	texture_params.grass_h_transition = 3;
	texture_params.grass_s_transition = 50;
	texture_params.sand_h_transition = 5;
	texture_params.sand_s_transition = 50;
}

void initSceneObjects() {
	box.init(&grid_params, &water_params, &noise_params);
	grid.init(&grid_params, &light_params, &texture_params);
	tex_mirror = fbw.init_texture();
	fbw.init(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE);
	tex_water_depth = fb_water_depth.init_texture();
	fb_water_depth.init();

	water.init(&grid_params, &water_params, &light_params);
	water.set_depth_texture(tex_water_depth);
	sky.init();

	check_error_gl();
}

void initTextures() {
	glGenTextures(1, &tex_normal_map);
	glBindTexture(GL_TEXTURE_2D, tex_normal_map);
	glfwLoadTexture2D("textures/water/tex_normal_map.tga", 0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

// --- Callbacks --- //

// Callback function called by GLFW when a mouse button is clicked
void GLFWCALL OnMouseButton(int glfwButton, int glfwAction)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventMouseButtonGLFW(glfwButton, glfwAction))   // Send event to AntTweakBar
	{
#endif
		if (navmode == BEZIER) {
			std::cout << "[Warning] No mouse interaction in Bezier mode.\n" << std::flush;
			return;
		}

		// Event not handled by AntTweakBar, we handle it ourselves
		camera.mouse_button(glfwButton, glfwAction);
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW when mouse has moved
void GLFWCALL OnMousePos(int mouseX, int mouseY)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventMousePosGLFW(mouseX, mouseY))  // Send event to AntTweakBar
	{
#endif
		if (navmode == BEZIER) {
			return;
		}

		camera.mouse_pos(mouseX, mouseY);
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW on mouse wheel event
void GLFWCALL OnMouseWheel(int pos)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventMouseWheelGLFW(pos))   // Send event to AntTweakBar
	{
#endif
		// Nothing for the moment
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW on key event
void GLFWCALL OnKey(int glfwKey, int glfwAction)
{

#ifdef WITH_ANTTWEAKBAR
	if (!TwEventKeyGLFW(glfwKey, glfwAction))  // Send event to AntTweakBar
	{
#endif

		if (glfwKey == GLFW_KEY_ESC && glfwAction == GLFW_PRESS) // Want to quit?
			glfwCloseWindow();
		else
		{
			camera.keyboard(glfwKey, glfwAction);

			if (glfwAction != GLFW_RELEASE) return; ///< only act on release

			switch (glfwKey){
			case '1':
				navmode = FREE;
				std::cout << "[Info] Running in free mode\n" << std::flush;
				isVisible(true);
				break;
			case '2':
				navmode = BEZIER;
				std::cout << "[Info] Running in bezier mode\n" << std::flush;
				bezier.set_start_time(glfwGetTime());
				isVisible(false);
				break;
			default:
				std::cout << "[Warning] No keyboard interaction in bezier mode.\n" << std::flush;
				break;
			}
		}
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW on char event
void GLFWCALL OnChar(int glfwChar, int glfwAction)
{
#ifdef WITH_ANTTWEAKBAR
	if (!TwEventCharGLFW(glfwChar, glfwAction))    // Send event to AntTweakBar
	{
#endif
		// Nothing for the moment
#ifdef WITH_ANTTWEAKBAR
	}
#endif
}

// Callback function called by GLFW when window size changes
void GLFWCALL OnWindowSize(int width, int height)
{
#ifdef WITH_ANTTWEAKBAR
	// Send the new window size to AntTweakBar
	TwWindowSize(width, height);
#endif

	resize_callback(width, height);
}<|endoftext|>