<commit_before>// Copyright (C) 2015 Rémi Bèges - Jérôme Leclercq
// This file is part of the "Nazara Engine - Mathematics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#pragma once

#ifndef NAZARA_VECTOR3_HPP
#define NAZARA_VECTOR3_HPP

#include <Nazara/Core/String.hpp>
#include <functional>

namespace Nz
{
	struct SerializationContext;

	template<typename T> class Vector2;
	template<typename T> class Vector4;

	template<typename T>
	class Vector3
	{
		public:
			Vector3() = default;
			Vector3(T X, T Y, T Z);
			Vector3(T X, const Vector2<T>& vec);
			explicit Vector3(T scale);
			Vector3(const T vec[3]);
			Vector3(const Vector2<T>& vec, T Z = 0.0);
			template<typename U> explicit Vector3(const Vector3<U>& vec);
			Vector3(const Vector3& vec) = default;
			explicit Vector3(const Vector4<T>& vec);
			~Vector3() = default;

			T AbsDotProduct(const Vector3& vec) const;
			T AngleBetween(const Vector3& vec) const;

			Vector3 CrossProduct(const Vector3& vec) const;

			T Distance(const Vector3& vec) const;
			float Distancef(const Vector3& vec) const;
			T DotProduct(const Vector3& vec) const;

			T GetLength() const;
			float GetLengthf() const;
			Vector3 GetNormal(T* length = nullptr) const;
			T GetSquaredLength() const;

			Vector3& MakeBackward();
			Vector3& MakeDown();
			Vector3& MakeForward();
			Vector3& MakeLeft();
			Vector3& MakeRight();
			Vector3& MakeUnit();
			Vector3& MakeUnitX();
			Vector3& MakeUnitY();
			Vector3& MakeUnitZ();
			Vector3& MakeUp();
			Vector3& MakeZero();

			Vector3& Maximize(const Vector3& vec);
			Vector3& Minimize(const Vector3& vec);

			Vector3& Normalize(T* length = nullptr);

			Vector3& Set(T X, T Y, T Z);
			Vector3& Set(T X, const Vector2<T>& vec);
			Vector3& Set(T scale);
			Vector3& Set(const T vec[3]);
			Vector3& Set(const Vector2<T>& vec, T Z = 0.0);
			Vector3& Set(const Vector3<T>& vec);
			template<typename U> Vector3& Set(const Vector3<U>& vec);
			Vector3& Set(const Vector4<T>& vec);

			T SquaredDistance(const Vector3& vec) const;

			String ToString() const;

			operator T* ();
			operator const T* () const;

			const Vector3& operator+() const;
			Vector3 operator-() const;

			Vector3 operator+(const Vector3& vec) const;
			Vector3 operator-(const Vector3& vec) const;
			Vector3 operator*(const Vector3& vec) const;
			Vector3 operator*(T scale) const;
			Vector3 operator/(const Vector3& vec) const;
			Vector3 operator/(T scale) const;
			Vector3& operator=(const Vector3& vec) = default;

			Vector3& operator+=(const Vector3& vec);
			Vector3& operator-=(const Vector3& vec);
			Vector3& operator*=(const Vector3& vec);
			Vector3& operator*=(T scale);
			Vector3& operator/=(const Vector3& vec);
			Vector3& operator/=(T scale);

			bool operator==(const Vector3& vec) const;
			bool operator!=(const Vector3& vec) const;
			bool operator<(const Vector3& vec) const;
			bool operator<=(const Vector3& vec) const;
			bool operator>(const Vector3& vec) const;
			bool operator>=(const Vector3& vec) const;

			static Vector3 Backward();
			static Vector3 CrossProduct(const Vector3& vec1, const Vector3& vec2);
			static T DotProduct(const Vector3& vec1, const Vector3& vec2);
			static T Distance(const Vector3& vec1, const Vector3& vec2);
			static float Distancef(const Vector3& vec1, const Vector3& vec2);
			static Vector3 Down();
			static Vector3 Forward();
			static Vector3 Left();
			static Vector3 Lerp(const Vector3& from, const Vector3& to, T interpolation);
			static Vector3 Normalize(const Vector3& vec);
			static Vector3 Right();
			static T SquaredDistance(const Vector3& vec1, const Vector3& vec2);
			static Vector3 Unit();
			static Vector3 UnitX();
			static Vector3 UnitY();
			static Vector3 UnitZ();
			static Vector3 Up();
			static Vector3 Zero();

			T x, y, z;
	};

	typedef Vector3<double> Vector3d;
	typedef Vector3<float> Vector3f;
	typedef Vector3<int> Vector3i;
	typedef Vector3<unsigned int> Vector3ui;
	typedef Vector3<Int32> Vector3i32;
	typedef Vector3<UInt32> Vector3ui32;

	template<typename T> bool Serialize(SerializationContext& context, const Vector3<T>& vector);
	template<typename T> bool Unserialize(SerializationContext& context, Vector3<T>* vector);
}

template<typename T> std::ostream& operator<<(std::ostream& out, const Nz::Vector3<T>& vec);

template<typename T> Nz::Vector3<T> operator*(T scale, const Nz::Vector3<T>& vec);
template<typename T> Nz::Vector3<T> operator/(T scale, const Nz::Vector3<T>& vec);

namespace std
{

template<class T>
struct hash<Nz::Vector2<T>>;

}

#include <Nazara/Math/Vector3.inl>

#endif // NAZARA_VECTOR3_HPP
<commit_msg>Fixing an oopsie (2)<commit_after>// Copyright (C) 2015 Rémi Bèges - Jérôme Leclercq
// This file is part of the "Nazara Engine - Mathematics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#pragma once

#ifndef NAZARA_VECTOR3_HPP
#define NAZARA_VECTOR3_HPP

#include <Nazara/Core/String.hpp>
#include <functional>

namespace Nz
{
	struct SerializationContext;

	template<typename T> class Vector2;
	template<typename T> class Vector4;

	template<typename T>
	class Vector3
	{
		public:
			Vector3() = default;
			Vector3(T X, T Y, T Z);
			Vector3(T X, const Vector2<T>& vec);
			explicit Vector3(T scale);
			Vector3(const T vec[3]);
			Vector3(const Vector2<T>& vec, T Z = 0.0);
			template<typename U> explicit Vector3(const Vector3<U>& vec);
			Vector3(const Vector3& vec) = default;
			explicit Vector3(const Vector4<T>& vec);
			~Vector3() = default;

			T AbsDotProduct(const Vector3& vec) const;
			T AngleBetween(const Vector3& vec) const;

			Vector3 CrossProduct(const Vector3& vec) const;

			T Distance(const Vector3& vec) const;
			float Distancef(const Vector3& vec) const;
			T DotProduct(const Vector3& vec) const;

			T GetLength() const;
			float GetLengthf() const;
			Vector3 GetNormal(T* length = nullptr) const;
			T GetSquaredLength() const;

			Vector3& MakeBackward();
			Vector3& MakeDown();
			Vector3& MakeForward();
			Vector3& MakeLeft();
			Vector3& MakeRight();
			Vector3& MakeUnit();
			Vector3& MakeUnitX();
			Vector3& MakeUnitY();
			Vector3& MakeUnitZ();
			Vector3& MakeUp();
			Vector3& MakeZero();

			Vector3& Maximize(const Vector3& vec);
			Vector3& Minimize(const Vector3& vec);

			Vector3& Normalize(T* length = nullptr);

			Vector3& Set(T X, T Y, T Z);
			Vector3& Set(T X, const Vector2<T>& vec);
			Vector3& Set(T scale);
			Vector3& Set(const T vec[3]);
			Vector3& Set(const Vector2<T>& vec, T Z = 0.0);
			Vector3& Set(const Vector3<T>& vec);
			template<typename U> Vector3& Set(const Vector3<U>& vec);
			Vector3& Set(const Vector4<T>& vec);

			T SquaredDistance(const Vector3& vec) const;

			String ToString() const;

			operator T* ();
			operator const T* () const;

			const Vector3& operator+() const;
			Vector3 operator-() const;

			Vector3 operator+(const Vector3& vec) const;
			Vector3 operator-(const Vector3& vec) const;
			Vector3 operator*(const Vector3& vec) const;
			Vector3 operator*(T scale) const;
			Vector3 operator/(const Vector3& vec) const;
			Vector3 operator/(T scale) const;
			Vector3& operator=(const Vector3& vec) = default;

			Vector3& operator+=(const Vector3& vec);
			Vector3& operator-=(const Vector3& vec);
			Vector3& operator*=(const Vector3& vec);
			Vector3& operator*=(T scale);
			Vector3& operator/=(const Vector3& vec);
			Vector3& operator/=(T scale);

			bool operator==(const Vector3& vec) const;
			bool operator!=(const Vector3& vec) const;
			bool operator<(const Vector3& vec) const;
			bool operator<=(const Vector3& vec) const;
			bool operator>(const Vector3& vec) const;
			bool operator>=(const Vector3& vec) const;

			static Vector3 Backward();
			static Vector3 CrossProduct(const Vector3& vec1, const Vector3& vec2);
			static T DotProduct(const Vector3& vec1, const Vector3& vec2);
			static T Distance(const Vector3& vec1, const Vector3& vec2);
			static float Distancef(const Vector3& vec1, const Vector3& vec2);
			static Vector3 Down();
			static Vector3 Forward();
			static Vector3 Left();
			static Vector3 Lerp(const Vector3& from, const Vector3& to, T interpolation);
			static Vector3 Normalize(const Vector3& vec);
			static Vector3 Right();
			static T SquaredDistance(const Vector3& vec1, const Vector3& vec2);
			static Vector3 Unit();
			static Vector3 UnitX();
			static Vector3 UnitY();
			static Vector3 UnitZ();
			static Vector3 Up();
			static Vector3 Zero();

			T x, y, z;
	};

	typedef Vector3<double> Vector3d;
	typedef Vector3<float> Vector3f;
	typedef Vector3<int> Vector3i;
	typedef Vector3<unsigned int> Vector3ui;
	typedef Vector3<Int32> Vector3i32;
	typedef Vector3<UInt32> Vector3ui32;

	template<typename T> bool Serialize(SerializationContext& context, const Vector3<T>& vector);
	template<typename T> bool Unserialize(SerializationContext& context, Vector3<T>* vector);
}

template<typename T> std::ostream& operator<<(std::ostream& out, const Nz::Vector3<T>& vec);

template<typename T> Nz::Vector3<T> operator*(T scale, const Nz::Vector3<T>& vec);
template<typename T> Nz::Vector3<T> operator/(T scale, const Nz::Vector3<T>& vec);

namespace std
{

template<class T>
struct hash<Nz::Vector3<T>>;

}

#include <Nazara/Math/Vector3.inl>

#endif // NAZARA_VECTOR3_HPP
<|endoftext|>


<commit_before>#ifndef ALEPH_TOPOLOGY_SPINE_HH__
#define ALEPH_TOPOLOGY_SPINE_HH__

#include <aleph/topology/Intersections.hh>

#include <algorithm>
#include <unordered_map>
#include <set>
#include <vector>

namespace aleph
{

namespace topology
{

// Contains the 'dumbest' implementation for calculating the spine, i.e.
// without any optimizations or skips. This will be used as the baseline
// for comparisons, but also to check the correctness of the approach.
namespace dumb
{

/**
  Checks whether a simplex in a simplicial complex is principal, i.e.
  whether it is not a proper face of any other simplex in K.
*/

template <class SimplicialComplex, class Simplex> bool isPrincipal( const Simplex& s, const SimplicialComplex& K )
{
  // Individual vertices cannot be considered to be principal because
  // they do not have a free face.
  if( s.dimension() == 0 )
    return false;

  bool principal = true;
  auto itPair    = K.range( s.dimension() + 1 );

  for( auto it = itPair.first; it != itPair.second; ++it )
  {
    auto&& t = *it;

    // This check assumes that the simplicial complex is valid, so it
    // suffices to search faces in one dimension _below_ s. Note that
    // the check only has to evaluate the *size* of the intersection,
    // as this is sufficient to determine whether a simplex is a face
    // of another simplex.
    if( sizeOfIntersection(s,t) == s.size() )
      principal = false;
  }

  return principal;
}

/**
  Checks whether a simplex in a simplicial complex is admissible, i.e.
  the simplex is *principal* and has at least one free face.
*/

template <class SimplicialComplex, class Simplex> Simplex isAdmissible( const Simplex& s, const SimplicialComplex& K )
{
  if( !isPrincipal(s,K) )
    return Simplex();

  // Check whether a free face exists ----------------------------------

  std::vector<Simplex> faces( s.begin_boundary(), s.end_boundary() );
  std::vector<bool> admissible( faces.size(), true );

  std::size_t i = 0;
  auto itPair   = K.range( s.dimension() ); // valid range for searches, viz. *all*
                                            // faces in "one dimension up"

  for( auto&& face : faces )
  {
    for( auto it = itPair.first; it != itPair.second; ++it )
    {
      auto&& t = *it;

      // We do not have to check for intersections with the original
      // simplex from which we started---we already know that we are
      // a face.
      if( t != s )
      {
        if( sizeOfIntersection(face,t) == face.size() )
        {
          admissible[i] = false;
          break;
        }
      }
    }

    ++i;
  }

  auto pos = std::find( admissible.begin(), admissible.end(), true );
  if( pos == admissible.end() )
    return Simplex();
  else
    return faces.at( std::distance( admissible.begin(), pos ) );
}

/**
  Calculates all principal faces of a given simplicial complex and
  returns them.
*/

template <class SimplicialComplex> std::unordered_map<typename SimplicialComplex::value_type, typename SimplicialComplex::value_type> principalFaces( const SimplicialComplex& K )
{
  using Simplex = typename SimplicialComplex::value_type;
  auto L        = K;

  std::unordered_map<Simplex, Simplex> admissible;

  // Step 1: determine free faces --------------------------------------
  //
  // This first checks which simplices have at least one free face,
  // meaning that they may be potentially admissible.

  for( auto it = L.begin(); it != L.end(); ++it )
  {
    if( it->dimension() == 0 )
      continue;

    // The range of the complex M is sufficient because we have
    // already encountered all lower-dimensional simplices that
    // precede the current one given by `it`.
    //
    // This complex will be used for testing free faces.
    SimplicialComplex M( L.begin(), it );

    // FIXME:
    //
    // In case of equal data values, the assignment from above does
    // *not* work and will result in incorrect candidates.
    M = L;

    bool hasFreeFace = false;
    Simplex freeFace = Simplex();

    for( auto itFace = it->begin_boundary(); itFace != it->end_boundary(); ++itFace )
    {
      bool isFace = false;
      for( auto&& simplex : M )
      {
        if( itFace->dimension() + 1 == simplex.dimension() && simplex != *it )
        {
          // The current face must *not* be a face of another simplex in
          // the simplicial complex.
          if( intersect( *itFace, simplex ) == *itFace )
          {
            isFace = true;
            break;
          }
        }
      }

      hasFreeFace = !isFace;
      if( hasFreeFace )
      {
        freeFace = *itFace;
        break;
      }
    }

    if( hasFreeFace )
      admissible.insert( std::make_pair( *it, freeFace ) );
  }

  // Step 2: determine principality ------------------------------------
  //
  // All simplices that are faces of higher-dimensional simplices are
  // now removed from the map of admissible simplices.

  for( auto&& s : L )
  {
    for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
      admissible.erase( *itFace );
  }

  return admissible;
}

} // namespace dumb

// ---------------------------------------------------------------------
// From this point on, only 'smart' implementations of the spine
// calculation will be given. Various optimizations will be used
// in order to improve the run-time.
// ---------------------------------------------------------------------

/**
  Stores coface relationships in a simplicial complex. Given a simplex
  \f$\sigma\f$, the map contains all of its cofaces. Note that the map
  will be updated upon every elementary collapse.
*/

template <class Simplex> using CofaceMap = std::unordered_map<Simplex, std::unordered_set<Simplex> >;

template <class SimplicialComplex> CofaceMap<typename SimplicialComplex::ValueType> buildCofaceMap( const SimplicialComplex& K )
{
  using Simplex   = typename SimplicialComplex::ValueType;
  using CofaceMap = CofaceMap<Simplex>;

  CofaceMap cofaces;
  for( auto&& s : K )
  {
    // Adding an *empty* list of cofaces (so far) for this simplex
    // simplifies the rest of the code because there is no need to
    // check for the existence of a simplex.
    if( cofaces.find(s) == cofaces.end() )
      cofaces[s] = {};

    for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
      cofaces[ *itFace ].insert( s );
  }

  return cofaces;
}

template <class Simplex> bool isPrincipal( const CofaceMap<Simplex>& cofaces, const Simplex& s )
{
  return cofaces.at( s ).empty();
}

template <class Simplex> Simplex getFreeFace( const CofaceMap<Simplex>& cofaces, const Simplex& s )
{
  if( !isPrincipal( cofaces, s ) )
    return Simplex();

  // Check whether a free face exists ----------------------------------

  for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
  {
    auto&& allCofaces = cofaces.at( *itFace );
    if( allCofaces.size() == 1 && allCofaces.find( s ) != allCofaces.end() )
      return *itFace;
  }

  return Simplex();
}

template <class SimplicialComplex> std::unordered_map<typename SimplicialComplex::value_type, typename SimplicialComplex::value_type> getPrincipalFaces( const CofaceMap<typename SimplicialComplex::ValueType>& cofaces, const SimplicialComplex& K )
{
  using Simplex = typename SimplicialComplex::value_type;
  auto L        = K;

  std::unordered_map<Simplex, Simplex> admissible;

  for( auto&& s : K )
  {
    auto freeFace = getFreeFace( cofaces, s );
    if( freeFace )
      admissible.insert( std::make_pair( s, freeFace ) );
  }

  return admissible;
}

/**
  Performs an iterated elementary simplicial collapse until *all* of the
  admissible simplices have been collapsed. This leads to the *spine* of
  the simplicial complex.

  @see S. Matveev, "Algorithmic Topology and Classification of 3-Manifolds"
*/

template <class SimplicialComplex> SimplicialComplex spine( const SimplicialComplex& K )
{
  auto L          = K;
  auto cofaces    = buildCofaceMap( L );
  auto admissible = getPrincipalFaces( cofaces, L );

  while( !admissible.empty() )
  {
    auto s = admissible.begin()->first;
    auto t = admissible.begin()->second;

    L.remove_without_validation( s );
    L.remove_without_validation( t );

    admissible.erase( s );

    // Predicate for removing s and t, the principal simplex with its
    // free face, from the coface data structure. This is required in
    // order to keep the coface relation up-to-date.
    using Simplex        = typename SimplicialComplex::ValueType;
    auto removeSimplices = [&cofaces,&s,&t] ( const Simplex& sigma )
    {
      cofaces.at(sigma).erase(s);
      cofaces.at(sigma).erase(t);
    };

    std::for_each( s.begin_boundary(), s.end_boundary(), removeSimplices );
    std::for_each( t.begin_boundary(), t.end_boundary(), removeSimplices );

    // Both s and t do not have to be stored any more because they
    // should not be queried again.
    cofaces.erase(s);
    cofaces.erase(t);

    // New simplices ---------------------------------------------------
    //
    // Add new admissible simplices that may potentially have been
    // spawned by the removal of s.

    // 1. Add all faces of the principal simplex, as they may
    //    potentially become admissible again.
    std::vector<Simplex> faces( s.begin_boundary(), s.end_boundary() );

    std::for_each( faces.begin(), faces.end(),
      [&t, &admissible, &cofaces] ( const Simplex& s )
      {
        if( t != s )
        {
          auto face = getFreeFace( cofaces, s );
          if( face )
            admissible.insert( std::make_pair( s, face ) );
        }
      }
    );

    // 2. Add all faces of the free face, as they may now themselves
    //    become admissible.
    faces.assign( t.begin_boundary(), t.end_boundary() );

    std::for_each( faces.begin(), faces.end(),
      [&admissible, &cofaces] ( const Simplex& s )
      {
        auto face = getFreeFace( cofaces, s );
        if( face )
          admissible.insert( std::make_pair( s, face ) );
      }
    );

#if 0
    // TODO: this check could be simplified by *storing* the free face
    // along with the given simplex
    for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
    {
      auto t = *itFace;

      if( isAdmissible( s, t, L ) )
      {
        L.remove_without_validation( s );
        L.remove_without_validation( t );

        admissible.erase( s );

        // New simplices -----------------------------------------------
        //
        // Add new admissible simplices that may potentially have been
        // spawned by the removal of s.

        // 1. Add all faces of the principal simplex, as they may
        //    potentially become admissible again.
        std::vector<Simplex> faces( s.begin_boundary(), s.end_boundary() );

        std::for_each( faces.begin(), faces.end(),
          [&t, &L, &admissible] ( const Simplex& s )
          {
            if( t != s && isAdmissible( s, L ) )
              admissible.insert( s );
          }
        );

        // 2. Add all faces othe free face, as they may now themselves
        //    become admissible.
        faces.assign( t.begin_boundary(), t.end_boundary() );

        std::for_each( faces.begin(), faces.end(),
          [&L, &admissible] ( const Simplex& s )
          {
            if( isAdmissible( s, L ) )
              admissible.insert( s );
          }
        );

        hasFreeFace = true;
        break;
      }
    }

    // The admissible simplex does not have a free face, so it must not
    // be used.
    if( !hasFreeFace )
      admissible.erase( s );
#endif

    // The heuristic above is incapable of detecting *all* principal
    // faces of the complex because this may involve searching *all*
    // co-faces. Instead, it is easier to fill up the admissible set
    // here.
    if( admissible.empty() )
      admissible = getPrincipalFaces( cofaces, L );
  }

  return L;
}

} // namespace topology

} // namespace aleph

#endif
<commit_msg>Finished 'dumb' implementation of the spine calculation<commit_after>#ifndef ALEPH_TOPOLOGY_SPINE_HH__
#define ALEPH_TOPOLOGY_SPINE_HH__

#include <aleph/topology/Intersections.hh>

#include <algorithm>
#include <unordered_map>
#include <set>
#include <vector>

namespace aleph
{

namespace topology
{

// Contains the 'dumbest' implementation for calculating the spine, i.e.
// without any optimizations or skips. This will be used as the baseline
// for comparisons, but also to check the correctness of the approach.
namespace dumb
{

/**
  Checks whether a simplex in a simplicial complex is principal, i.e.
  whether it is not a proper face of any other simplex in K.
*/

template <class SimplicialComplex, class Simplex> bool isPrincipal( const Simplex& s, const SimplicialComplex& K )
{
  // Individual vertices cannot be considered to be principal because
  // they do not have a free face.
  if( s.dimension() == 0 )
    return false;

  bool principal = true;
  auto itPair    = K.range( s.dimension() + 1 );

  for( auto it = itPair.first; it != itPair.second; ++it )
  {
    auto&& t = *it;

    // This check assumes that the simplicial complex is valid, so it
    // suffices to search faces in one dimension _below_ s. Note that
    // the check only has to evaluate the *size* of the intersection,
    // as this is sufficient to determine whether a simplex is a face
    // of another simplex.
    if( sizeOfIntersection(s,t) == s.size() )
      principal = false;
  }

  return principal;
}

/**
  Checks whether a simplex in a simplicial complex is admissible, i.e.
  the simplex is *principal* and has at least one free face.
*/

template <class SimplicialComplex, class Simplex> Simplex isAdmissible( const Simplex& s, const SimplicialComplex& K )
{
  if( !isPrincipal(s,K) )
    return Simplex();

  // Check whether a free face exists ----------------------------------
  //
  // This involves iterating over all simplices that have the *same*
  // dimension as s, because we are interested in checking whether a
  // simplex shares a face of s.

  std::vector<Simplex> faces( s.begin_boundary(), s.end_boundary() );
  std::vector<bool> admissible( faces.size(), true );

  std::size_t i = 0;
  auto itPair   = K.range( s.dimension() ); // valid range for searches, viz. *all*
                                            // faces in "one dimension up"

  for( auto&& face : faces )
  {
    for( auto it = itPair.first; it != itPair.second; ++it )
    {
      auto&& t = *it;

      // We do not have to check for intersections with the original
      // simplex from which we started---we already know that we are
      // a face.
      if( t != s )
      {
        if( sizeOfIntersection(face,t) == face.size() )
        {
          admissible[i] = false;
          break;
        }
      }
    }

    ++i;
  }

  // Return the free face if possible; as usual, an empty return value
  // indicates that we did not find such a face. Note that the call to
  // the `find()` function prefers the lexicographically smallest one,
  // which ensures consistency.
  auto pos = std::find( admissible.begin(), admissible.end(), true );
  if( pos == admissible.end() )
    return Simplex();
  else
    return faces.at( std::distance( admissible.begin(), pos ) );
}

/**
  Calculates all principal faces of a given simplicial complex and
  returns them.
*/

template <class SimplicialComplex> std::unordered_map<typename SimplicialComplex::value_type, typename SimplicialComplex::value_type> principalFaces( const SimplicialComplex& K )
{
  using Simplex = typename SimplicialComplex::value_type;
  auto L        = K;

  std::unordered_map<Simplex, Simplex> admissible;

  // Step 1: determine free faces --------------------------------------
  //
  // This first checks which simplices have at least one free face,
  // meaning that they may be potentially admissible.

  for( auto it = L.begin(); it != L.end(); ++it )
  {
    if( it->dimension() == 0 )
      continue;

    // The range of the complex M is sufficient because we have
    // already encountered all lower-dimensional simplices that
    // precede the current one given by `it`.
    //
    // This complex will be used for testing free faces.
    SimplicialComplex M( L.begin(), it );

    // FIXME:
    //
    // In case of equal data values, the assignment from above does
    // *not* work and will result in incorrect candidates.
    M = L;

    bool hasFreeFace = false;
    Simplex freeFace = Simplex();

    for( auto itFace = it->begin_boundary(); itFace != it->end_boundary(); ++itFace )
    {
      bool isFace = false;
      for( auto&& simplex : M )
      {
        if( itFace->dimension() + 1 == simplex.dimension() && simplex != *it )
        {
          // The current face must *not* be a face of another simplex in
          // the simplicial complex.
          if( intersect( *itFace, simplex ) == *itFace )
          {
            isFace = true;
            break;
          }
        }
      }

      hasFreeFace = !isFace;
      if( hasFreeFace )
      {
        freeFace = *itFace;
        break;
      }
    }

    if( hasFreeFace )
      admissible.insert( std::make_pair( *it, freeFace ) );
  }

  // Step 2: determine principality ------------------------------------
  //
  // All simplices that are faces of higher-dimensional simplices are
  // now removed from the map of admissible simplices.

  for( auto&& s : L )
  {
    for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
      admissible.erase( *itFace );
  }

  return admissible;
}

/**
  Performs an iterated elementary simplicial collapse until *all* of the
  admissible simplices have been collapsed. This leads to the *spine* of
  the simplicial complex.

  Notice that this is the *dumbest* possible implementation, as no state
  will be stored, and the search for new principal faces starts fresh in
  every iteration.

  This implementation is useful to check improved algorithms.

  @see S. Matveev, "Algorithmic Topology and Classification of 3-Manifolds"
*/

template <class SimplicialComplex> SimplicialComplex spine( const SimplicialComplex& K )
{
  auto L          = K;
  auto admissible = principalFaces( L );

  while( !admissible.empty() )
  {
    auto s = admissible.begin()->first;
    auto t = admissible.begin()->second;

    L.remove_without_validation( s );
    L.remove_without_validation( t );

    admissible = principalFaces( L );
  }

  return L;
}

} // namespace dumb

// ---------------------------------------------------------------------
// From this point on, only 'smart' implementations of the spine
// calculation will be given. Various optimizations will be used
// in order to improve the run-time.
// ---------------------------------------------------------------------

/**
  Stores coface relationships in a simplicial complex. Given a simplex
  \f$\sigma\f$, the map contains all of its cofaces. Note that the map
  will be updated upon every elementary collapse.
*/

template <class Simplex> using CofaceMap = std::unordered_map<Simplex, std::unordered_set<Simplex> >;

template <class SimplicialComplex> CofaceMap<typename SimplicialComplex::ValueType> buildCofaceMap( const SimplicialComplex& K )
{
  using Simplex   = typename SimplicialComplex::ValueType;
  using CofaceMap = CofaceMap<Simplex>;

  CofaceMap cofaces;
  for( auto&& s : K )
  {
    // Adding an *empty* list of cofaces (so far) for this simplex
    // simplifies the rest of the code because there is no need to
    // check for the existence of a simplex.
    if( cofaces.find(s) == cofaces.end() )
      cofaces[s] = {};

    for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
      cofaces[ *itFace ].insert( s );
  }

  return cofaces;
}

template <class Simplex> bool isPrincipal( const CofaceMap<Simplex>& cofaces, const Simplex& s )
{
  return cofaces.at( s ).empty();
}

template <class Simplex> Simplex getFreeFace( const CofaceMap<Simplex>& cofaces, const Simplex& s )
{
  if( !isPrincipal( cofaces, s ) )
    return Simplex();

  // Check whether a free face exists ----------------------------------

  for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
  {
    auto&& allCofaces = cofaces.at( *itFace );
    if( allCofaces.size() == 1 && allCofaces.find( s ) != allCofaces.end() )
      return *itFace;
  }

  return Simplex();
}

template <class SimplicialComplex> std::unordered_map<typename SimplicialComplex::value_type, typename SimplicialComplex::value_type> getPrincipalFaces( const CofaceMap<typename SimplicialComplex::ValueType>& cofaces, const SimplicialComplex& K )
{
  using Simplex = typename SimplicialComplex::value_type;
  auto L        = K;

  std::unordered_map<Simplex, Simplex> admissible;

  for( auto&& s : K )
  {
    auto freeFace = getFreeFace( cofaces, s );
    if( freeFace )
      admissible.insert( std::make_pair( s, freeFace ) );
  }

  return admissible;
}

/**
  Performs an iterated elementary simplicial collapse until *all* of the
  admissible simplices have been collapsed. This leads to the *spine* of
  the simplicial complex.

  @see S. Matveev, "Algorithmic Topology and Classification of 3-Manifolds"
*/

template <class SimplicialComplex> SimplicialComplex spine( const SimplicialComplex& K )
{
  auto L          = K;
  auto cofaces    = buildCofaceMap( L );
  auto admissible = getPrincipalFaces( cofaces, L );

  while( !admissible.empty() )
  {
    auto s = admissible.begin()->first;
    auto t = admissible.begin()->second;

    L.remove_without_validation( s );
    L.remove_without_validation( t );

    admissible.erase( s );

    // Predicate for removing s and t, the principal simplex with its
    // free face, from the coface data structure. This is required in
    // order to keep the coface relation up-to-date.
    using Simplex        = typename SimplicialComplex::ValueType;
    auto removeSimplices = [&cofaces,&s,&t] ( const Simplex& sigma )
    {
      cofaces.at(sigma).erase(s);
      cofaces.at(sigma).erase(t);
    };

    std::for_each( s.begin_boundary(), s.end_boundary(), removeSimplices );
    std::for_each( t.begin_boundary(), t.end_boundary(), removeSimplices );

    // Both s and t do not have to be stored any more because they
    // should not be queried again.
    cofaces.erase(s);
    cofaces.erase(t);

    // New simplices ---------------------------------------------------
    //
    // Add new admissible simplices that may potentially have been
    // spawned by the removal of s.

    // 1. Add all faces of the principal simplex, as they may
    //    potentially become admissible again.
    std::vector<Simplex> faces( s.begin_boundary(), s.end_boundary() );

    std::for_each( faces.begin(), faces.end(),
      [&t, &admissible, &cofaces] ( const Simplex& s )
      {
        if( t != s )
        {
          auto face = getFreeFace( cofaces, s );
          if( face )
            admissible.insert( std::make_pair( s, face ) );
        }
      }
    );

    // 2. Add all faces of the free face, as they may now themselves
    //    become admissible.
    faces.assign( t.begin_boundary(), t.end_boundary() );

    std::for_each( faces.begin(), faces.end(),
      [&admissible, &cofaces] ( const Simplex& s )
      {
        auto face = getFreeFace( cofaces, s );
        if( face )
          admissible.insert( std::make_pair( s, face ) );
      }
    );

#if 0
    // TODO: this check could be simplified by *storing* the free face
    // along with the given simplex
    for( auto itFace = s.begin_boundary(); itFace != s.end_boundary(); ++itFace )
    {
      auto t = *itFace;

      if( isAdmissible( s, t, L ) )
      {
        L.remove_without_validation( s );
        L.remove_without_validation( t );

        admissible.erase( s );

        // New simplices -----------------------------------------------
        //
        // Add new admissible simplices that may potentially have been
        // spawned by the removal of s.

        // 1. Add all faces of the principal simplex, as they may
        //    potentially become admissible again.
        std::vector<Simplex> faces( s.begin_boundary(), s.end_boundary() );

        std::for_each( faces.begin(), faces.end(),
          [&t, &L, &admissible] ( const Simplex& s )
          {
            if( t != s && isAdmissible( s, L ) )
              admissible.insert( s );
          }
        );

        // 2. Add all faces othe free face, as they may now themselves
        //    become admissible.
        faces.assign( t.begin_boundary(), t.end_boundary() );

        std::for_each( faces.begin(), faces.end(),
          [&L, &admissible] ( const Simplex& s )
          {
            if( isAdmissible( s, L ) )
              admissible.insert( s );
          }
        );

        hasFreeFace = true;
        break;
      }
    }

    // The admissible simplex does not have a free face, so it must not
    // be used.
    if( !hasFreeFace )
      admissible.erase( s );
#endif

    // The heuristic above is incapable of detecting *all* principal
    // faces of the complex because this may involve searching *all*
    // co-faces. Instead, it is easier to fill up the admissible set
    // here.
    if( admissible.empty() )
      admissible = getPrincipalFaces( cofaces, L );
  }

  return L;
}

} // namespace topology

} // namespace aleph

#endif
<|endoftext|>


<commit_before>#pragma once

#include <Python.h>
#include <numpy/arrayobject.h>
#include <anyode/anyode.hpp>

BEGIN_NAMESPACE(AnyODE)
struct PyOdeSys : public AnyODE::OdeSysBase<double> {
    int ny;
    PyObject *py_rhs, *py_jac, *py_quads, *py_roots, *py_kwargs, *py_dx0cb, *py_dx_max_cb;
    int mlower, mupper, nquads, nroots;
    PyOdeSys(int ny, PyObject * py_rhs, PyObject * py_jac=nullptr, PyObject * py_quads=nullptr,
             PyObject * py_roots=nullptr, PyObject * py_kwargs=nullptr, int mlower=-1,
             int mupper=-1, int nquads=0, int nroots=0, PyObject * py_dx0cb=nullptr,
             PyObject * py_dx_max_cb=nullptr) :
        ny(ny), py_rhs(py_rhs), py_jac(py_jac), py_quads(py_quads), py_roots(py_roots),
        py_kwargs(py_kwargs), py_dx0cb(py_dx0cb), py_dx_max_cb(py_dx_max_cb),
        mlower(mlower), mupper(mupper), nquads(nquads), nroots(nroots)
    {
        if (py_rhs == nullptr){
            throw std::runtime_error("py_rhs must not be nullptr");
        }
        if ((py_dx_max_cb != nullptr) && (py_dx_max_cb != Py_None)) {
            this->use_get_dx_max = true;
        }
        Py_INCREF(py_rhs);
        Py_XINCREF(py_jac);
        Py_XINCREF(py_quads);
        Py_XINCREF(py_roots);
        if (py_kwargs == Py_None){
            Py_DECREF(Py_None);
            this->py_kwargs = nullptr;
        } else {
            Py_XINCREF(py_kwargs);
        }
    }
    virtual ~PyOdeSys() {
        Py_DECREF(py_rhs);
        Py_XDECREF(py_jac);
        Py_XDECREF(py_quads);
        Py_XDECREF(py_roots);
        Py_XDECREF(py_kwargs);
    }
    int get_ny() const override { return ny; }
    int get_mlower() const override { return mlower; }
    int get_mupper() const override { return mupper; }
    int get_nquads() const override { return nquads; }
    int get_nroots() const override { return nroots; }
    double get_dx0(double t, const double * const y) override {
        if (py_dx0cb == nullptr or py_dx0cb == Py_None) {
            return default_dx0;
        }
        npy_intp dims[1] { static_cast<npy_intp>(this->ny) } ;
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dO)", (double)(t), py_yarr);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_dx0cb, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_yarr);
        if (py_result == nullptr) {
            throw std::runtime_error("get_dx0 failed (dx0cb failed)");
        }
        double res = PyFloat_AsDouble(py_result);
        Py_DECREF(py_result);
        if ((PyErr_Occurred()) && (res == -1.0)) {
            throw std::runtime_error("get_dx0 failed (value returned by dx0cb could not be converted to float)");
        }
        return res;
    }
    double get_dx_max(double t, const double * const y) override {
        if (py_dx_max_cb == nullptr or py_dx_max_cb == Py_None) {
            return INFINITY;
        }
        npy_intp dims[1] { static_cast<npy_intp>(this->ny) } ;
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dO)", (double)(t), py_yarr);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_dx_max_cb, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_yarr);
        if (py_result == nullptr) {
            throw std::runtime_error("get_dx_max failed (dx_max_cb failed)");
        }
        double res = PyFloat_AsDouble(py_result);
        Py_DECREF(py_result);
        if (PyErr_Occurred() && (res == -1.0)) {
            throw std::runtime_error("get_dx_max failed (value returned by dx_max_cb could not be converted to float)");
        }
        return res;
    }
    Status handle_status_(PyObject * py_result, const std::string what_arg){
        if (py_result == nullptr){
            throw std::runtime_error(what_arg + " failed");
        } else if (py_result == Py_None){
            Py_DECREF(py_result);
            return AnyODE::Status::success;
        }
        long result = PyInt_AsLong(py_result);
        Py_DECREF(py_result);

        if ((PyErr_Occurred() && (result == -1)) or
            (result == static_cast<long int>(AnyODE::Status::unrecoverable_error))) {
            return AnyODE::Status::unrecoverable_error;
        } else if (result == static_cast<long int>(AnyODE::Status::recoverable_error)) {
            return AnyODE::Status::recoverable_error;
        } else if (result == static_cast<long int>(AnyODE::Status::success)) {
            return AnyODE::Status::success;
        }
        throw std::runtime_error(what_arg + " did not return None, -1, 0 or 1");
    }
    Status rhs(double t, const double * const y, double * const dydt) override {
        npy_intp dims[1] { static_cast<npy_intp>(this->ny) } ;
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyObject * py_dydt = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(dydt));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dOO)", (double)(t), py_yarr, py_dydt);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_rhs, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_dydt);
        Py_DECREF(py_yarr);
        this->nfev++;
        return handle_status_(py_result, "rhs");
    }
    AnyODE::Status quads(double t, const double * const y, double * const out) override {
        npy_intp ydims[1] { static_cast<npy_intp>(this->ny) };
        npy_intp rdims[1] { static_cast<npy_intp>(this->get_nquads()) };
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, ydims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyObject * py_out = PyArray_SimpleNewFromData(
            1, rdims, type_tag, static_cast<void*>(out));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dOO)", t, py_yarr, py_out);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_quads, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_out);
        Py_DECREF(py_yarr);
        return handle_status_(py_result, "quads");
    }
    AnyODE::Status roots(double t, const double * const y, double * const out) override {
        npy_intp ydims[1] { static_cast<npy_intp>(this->ny) };
        npy_intp rdims[1] { static_cast<npy_intp>(this->get_nroots()) };
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, ydims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyObject * py_out = PyArray_SimpleNewFromData(
            1, rdims, type_tag, static_cast<void*>(out));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dOO)", t, py_yarr, py_out);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_roots, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_out);
        Py_DECREF(py_yarr);
        return handle_status_(py_result, "roots");
    }
    AnyODE::Status call_py_jac(double t, const double * const y, const double * const fy,
                               PyObject * py_jmat, double * const dfdt){
        npy_intp ydims[1] { static_cast<npy_intp>(this->ny) };
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(1, ydims, type_tag, const_cast<double *>(y));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_dfdt = (dfdt == nullptr) ? Py_BuildValue("") : PyArray_SimpleNewFromData(
            1, ydims, type_tag, static_cast<void*>(dfdt));
        PyObject * py_fy;
        if (fy) {
            py_fy = PyArray_SimpleNewFromData(1, ydims, type_tag, const_cast<double *>(fy));
            PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_fy), NPY_ARRAY_WRITEABLE);  // make fy read-only
        } else {
            py_fy = Py_BuildValue(""); // Py_None with incref
        }
        // Call jac with signature: (t, y[:], Jmat[:, :], dfdt[:]=None, fy[:]=None)
        // (NumPy takes cares of row vs. column major ordering. User responsible for dense/banded.)
        PyObject * py_arglist = Py_BuildValue("(dOOOO)", (double)t, py_yarr, py_jmat, py_dfdt, py_fy);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_jac, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_fy);
        Py_DECREF(py_dfdt);
        Py_DECREF(py_yarr);
        this->njev++;
        return handle_status_(py_result, "jac");
    }
    AnyODE::Status dense_jac_cmaj(double t, const double * const y, const double * const fy,
                                  double * const jac, long int ldim, double * const dfdt=nullptr) override {
        npy_intp Jdims[2] { static_cast<npy_intp>(this->ny), static_cast<npy_intp>(this->ny) };
        npy_intp strides[2] { sizeof(double), static_cast<npy_intp>(ldim*sizeof(double)) };
        int flags = NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE;
        if (ldim == Jdims[0]) {
            flags |= NPY_ARRAY_F_CONTIGUOUS;
        }
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_jmat = PyArray_New(
            &PyArray_Type, 2, Jdims, type_tag, strides,
            static_cast<void *>(const_cast<double *>(jac)), sizeof(double),
            flags, nullptr);
        AnyODE::Status status = call_py_jac(t, y, fy, py_jmat, dfdt);
        Py_DECREF(py_jmat);
        return status;
    }
    AnyODE::Status dense_jac_rmaj(double t, const double * const y, const double * const fy,
                                  double * const jac, long int ldim, double * const dfdt=nullptr) override {
        npy_intp Jdims[2] { static_cast<npy_intp>(this->ny), static_cast<npy_intp>(this->ny) };
        npy_intp strides[2] { static_cast<npy_intp>(ldim*sizeof(double)), sizeof(double) };
        const auto type_tag = NPY_DOUBLE;
        int flags = NPY_ARRAY_ALIGNED| NPY_ARRAY_WRITEABLE;
        if (ldim == Jdims[1]) {
            flags |= NPY_ARRAY_C_CONTIGUOUS;
        }
        PyObject * py_jmat = PyArray_New(
            &PyArray_Type, 2, Jdims, type_tag, strides,
            static_cast<void *>(const_cast<double *>(jac)), sizeof(double), flags, nullptr);
        AnyODE::Status status = call_py_jac(t, y, fy, py_jmat, dfdt);
        Py_DECREF(py_jmat);
        return status;
    }
    AnyODE::Status banded_jac_cmaj(double t, const double * const y, const double * const fy,
                                   double * const jac, long int ldim) override {
        npy_intp Jdims[2] { 1 + this->mlower + this->mupper, static_cast<npy_intp>(this->ny) };
        npy_intp strides[2] { sizeof(double), static_cast<npy_intp>(ldim*sizeof(double)) };
        const auto type_tag = NPY_DOUBLE;
        int flags = NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE;
        if (ldim == Jdims[0] ) {
            flags |= NPY_ARRAY_F_CONTIGUOUS;
        }
        PyObject * py_jmat = PyArray_New(
            &PyArray_Type, 2, Jdims, type_tag, strides,
            static_cast<void *>(const_cast<double *>(jac)), sizeof(double), flags, nullptr);
        AnyODE::Status status = call_py_jac(t, y, fy, py_jmat, nullptr);
        Py_DECREF(py_jmat);
        return status;
    }
};
END_NAMESPACE(AnyODE)
<commit_msg>or -> ||<commit_after>#pragma once

#include <Python.h>
#include <numpy/arrayobject.h>
#include <anyode/anyode.hpp>

BEGIN_NAMESPACE(AnyODE)
struct PyOdeSys : public AnyODE::OdeSysBase<double> {
    int ny;
    PyObject *py_rhs, *py_jac, *py_quads, *py_roots, *py_kwargs, *py_dx0cb, *py_dx_max_cb;
    int mlower, mupper, nquads, nroots;
    PyOdeSys(int ny, PyObject * py_rhs, PyObject * py_jac=nullptr, PyObject * py_quads=nullptr,
             PyObject * py_roots=nullptr, PyObject * py_kwargs=nullptr, int mlower=-1,
             int mupper=-1, int nquads=0, int nroots=0, PyObject * py_dx0cb=nullptr,
             PyObject * py_dx_max_cb=nullptr) :
        ny(ny), py_rhs(py_rhs), py_jac(py_jac), py_quads(py_quads), py_roots(py_roots),
        py_kwargs(py_kwargs), py_dx0cb(py_dx0cb), py_dx_max_cb(py_dx_max_cb),
        mlower(mlower), mupper(mupper), nquads(nquads), nroots(nroots)
    {
        if (py_rhs == nullptr){
            throw std::runtime_error("py_rhs must not be nullptr");
        }
        if ((py_dx_max_cb != nullptr) && (py_dx_max_cb != Py_None)) {
            this->use_get_dx_max = true;
        }
        Py_INCREF(py_rhs);
        Py_XINCREF(py_jac);
        Py_XINCREF(py_quads);
        Py_XINCREF(py_roots);
        if (py_kwargs == Py_None){
            Py_DECREF(Py_None);
            this->py_kwargs = nullptr;
        } else {
            Py_XINCREF(py_kwargs);
        }
    }
    virtual ~PyOdeSys() {
        Py_DECREF(py_rhs);
        Py_XDECREF(py_jac);
        Py_XDECREF(py_quads);
        Py_XDECREF(py_roots);
        Py_XDECREF(py_kwargs);
    }
    int get_ny() const override { return ny; }
    int get_mlower() const override { return mlower; }
    int get_mupper() const override { return mupper; }
    int get_nquads() const override { return nquads; }
    int get_nroots() const override { return nroots; }
    double get_dx0(double t, const double * const y) override {
        if (py_dx0cb == nullptr || py_dx0cb == Py_None) {
            return default_dx0;
        }
        npy_intp dims[1] { static_cast<npy_intp>(this->ny) } ;
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dO)", (double)(t), py_yarr);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_dx0cb, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_yarr);
        if (py_result == nullptr) {
            throw std::runtime_error("get_dx0 failed (dx0cb failed)");
        }
        double res = PyFloat_AsDouble(py_result);
        Py_DECREF(py_result);
        if ((PyErr_Occurred()) && (res == -1.0)) {
            throw std::runtime_error("get_dx0 failed (value returned by dx0cb could not be converted to float)");
        }
        return res;
    }
    double get_dx_max(double t, const double * const y) override {
        if (py_dx_max_cb == nullptr || py_dx_max_cb == Py_None) {
            return INFINITY;
        }
        npy_intp dims[1] { static_cast<npy_intp>(this->ny) } ;
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dO)", (double)(t), py_yarr);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_dx_max_cb, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_yarr);
        if (py_result == nullptr) {
            throw std::runtime_error("get_dx_max failed (dx_max_cb failed)");
        }
        double res = PyFloat_AsDouble(py_result);
        Py_DECREF(py_result);
        if (PyErr_Occurred() && (res == -1.0)) {
            throw std::runtime_error("get_dx_max failed (value returned by dx_max_cb could not be converted to float)");
        }
        return res;
    }
    Status handle_status_(PyObject * py_result, const std::string what_arg){
        if (py_result == nullptr){
            throw std::runtime_error(what_arg + " failed");
        } else if (py_result == Py_None){
            Py_DECREF(py_result);
            return AnyODE::Status::success;
        }
        long result = PyInt_AsLong(py_result);
        Py_DECREF(py_result);

        if ((PyErr_Occurred() && (result == -1)) or
            (result == static_cast<long int>(AnyODE::Status::unrecoverable_error))) {
            return AnyODE::Status::unrecoverable_error;
        } else if (result == static_cast<long int>(AnyODE::Status::recoverable_error)) {
            return AnyODE::Status::recoverable_error;
        } else if (result == static_cast<long int>(AnyODE::Status::success)) {
            return AnyODE::Status::success;
        }
        throw std::runtime_error(what_arg + " did not return None, -1, 0 or 1");
    }
    Status rhs(double t, const double * const y, double * const dydt) override {
        npy_intp dims[1] { static_cast<npy_intp>(this->ny) } ;
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyObject * py_dydt = PyArray_SimpleNewFromData(
            1, dims, type_tag, static_cast<void*>(dydt));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dOO)", (double)(t), py_yarr, py_dydt);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_rhs, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_dydt);
        Py_DECREF(py_yarr);
        this->nfev++;
        return handle_status_(py_result, "rhs");
    }
    AnyODE::Status quads(double t, const double * const y, double * const out) override {
        npy_intp ydims[1] { static_cast<npy_intp>(this->ny) };
        npy_intp rdims[1] { static_cast<npy_intp>(this->get_nquads()) };
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, ydims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyObject * py_out = PyArray_SimpleNewFromData(
            1, rdims, type_tag, static_cast<void*>(out));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dOO)", t, py_yarr, py_out);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_quads, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_out);
        Py_DECREF(py_yarr);
        return handle_status_(py_result, "quads");
    }
    AnyODE::Status roots(double t, const double * const y, double * const out) override {
        npy_intp ydims[1] { static_cast<npy_intp>(this->ny) };
        npy_intp rdims[1] { static_cast<npy_intp>(this->get_nroots()) };
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(
            1, ydims, type_tag, static_cast<void*>(const_cast<double*>(y)));
        PyObject * py_out = PyArray_SimpleNewFromData(
            1, rdims, type_tag, static_cast<void*>(out));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_arglist = Py_BuildValue("(dOO)", t, py_yarr, py_out);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_roots, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_out);
        Py_DECREF(py_yarr);
        return handle_status_(py_result, "roots");
    }
    AnyODE::Status call_py_jac(double t, const double * const y, const double * const fy,
                               PyObject * py_jmat, double * const dfdt){
        npy_intp ydims[1] { static_cast<npy_intp>(this->ny) };
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_yarr = PyArray_SimpleNewFromData(1, ydims, type_tag, const_cast<double *>(y));
        PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_yarr), NPY_ARRAY_WRITEABLE);  // make yarr read-only
        PyObject * py_dfdt = (dfdt == nullptr) ? Py_BuildValue("") : PyArray_SimpleNewFromData(
            1, ydims, type_tag, static_cast<void*>(dfdt));
        PyObject * py_fy;
        if (fy) {
            py_fy = PyArray_SimpleNewFromData(1, ydims, type_tag, const_cast<double *>(fy));
            PyArray_CLEARFLAGS(reinterpret_cast<PyArrayObject*>(py_fy), NPY_ARRAY_WRITEABLE);  // make fy read-only
        } else {
            py_fy = Py_BuildValue(""); // Py_None with incref
        }
        // Call jac with signature: (t, y[:], Jmat[:, :], dfdt[:]=None, fy[:]=None)
        // (NumPy takes cares of row vs. column major ordering. User responsible for dense/banded.)
        PyObject * py_arglist = Py_BuildValue("(dOOOO)", (double)t, py_yarr, py_jmat, py_dfdt, py_fy);
        PyObject * py_result = PyEval_CallObjectWithKeywords(this->py_jac, py_arglist, this->py_kwargs);
        Py_DECREF(py_arglist);
        Py_DECREF(py_fy);
        Py_DECREF(py_dfdt);
        Py_DECREF(py_yarr);
        this->njev++;
        return handle_status_(py_result, "jac");
    }
    AnyODE::Status dense_jac_cmaj(double t, const double * const y, const double * const fy,
                                  double * const jac, long int ldim, double * const dfdt=nullptr) override {
        npy_intp Jdims[2] { static_cast<npy_intp>(this->ny), static_cast<npy_intp>(this->ny) };
        npy_intp strides[2] { sizeof(double), static_cast<npy_intp>(ldim*sizeof(double)) };
        int flags = NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE;
        if (ldim == Jdims[0]) {
            flags |= NPY_ARRAY_F_CONTIGUOUS;
        }
        const auto type_tag = NPY_DOUBLE;
        PyObject * py_jmat = PyArray_New(
            &PyArray_Type, 2, Jdims, type_tag, strides,
            static_cast<void *>(const_cast<double *>(jac)), sizeof(double),
            flags, nullptr);
        AnyODE::Status status = call_py_jac(t, y, fy, py_jmat, dfdt);
        Py_DECREF(py_jmat);
        return status;
    }
    AnyODE::Status dense_jac_rmaj(double t, const double * const y, const double * const fy,
                                  double * const jac, long int ldim, double * const dfdt=nullptr) override {
        npy_intp Jdims[2] { static_cast<npy_intp>(this->ny), static_cast<npy_intp>(this->ny) };
        npy_intp strides[2] { static_cast<npy_intp>(ldim*sizeof(double)), sizeof(double) };
        const auto type_tag = NPY_DOUBLE;
        int flags = NPY_ARRAY_ALIGNED| NPY_ARRAY_WRITEABLE;
        if (ldim == Jdims[1]) {
            flags |= NPY_ARRAY_C_CONTIGUOUS;
        }
        PyObject * py_jmat = PyArray_New(
            &PyArray_Type, 2, Jdims, type_tag, strides,
            static_cast<void *>(const_cast<double *>(jac)), sizeof(double), flags, nullptr);
        AnyODE::Status status = call_py_jac(t, y, fy, py_jmat, dfdt);
        Py_DECREF(py_jmat);
        return status;
    }
    AnyODE::Status banded_jac_cmaj(double t, const double * const y, const double * const fy,
                                   double * const jac, long int ldim) override {
        npy_intp Jdims[2] { 1 + this->mlower + this->mupper, static_cast<npy_intp>(this->ny) };
        npy_intp strides[2] { sizeof(double), static_cast<npy_intp>(ldim*sizeof(double)) };
        const auto type_tag = NPY_DOUBLE;
        int flags = NPY_ARRAY_ALIGNED | NPY_ARRAY_WRITEABLE;
        if (ldim == Jdims[0] ) {
            flags |= NPY_ARRAY_F_CONTIGUOUS;
        }
        PyObject * py_jmat = PyArray_New(
            &PyArray_Type, 2, Jdims, type_tag, strides,
            static_cast<void *>(const_cast<double *>(jac)), sizeof(double), flags, nullptr);
        AnyODE::Status status = call_py_jac(t, y, fy, py_jmat, nullptr);
        Py_DECREF(py_jmat);
        return status;
    }
};
END_NAMESPACE(AnyODE)
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkImage.h"
#include "SkSurface.h"

#define FAT_PIXEL_COLOR     SK_ColorBLACK
#define PIXEL_CENTER_SIZE   3
#define WIRE_FRAME_COLOR    0xFFFF0000  /*0xFF00FFFF*/
#define WIRE_FRAME_SIZE     1.5f

static void erase(SkSurface* surface) {
    surface->getCanvas()->clear(0);
}

static SkShader* createChecker() {
    SkBitmap bm;
    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
    bm.allocPixels();
    bm.lockPixels();
    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = SkPreMultiplyColor(0xFFFDFDFD);
    *bm.getAddr32(0, 1) = *bm.getAddr32(1, 0) = SkPreMultiplyColor(0xFFF4F4F4);
    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
                                               SkShader::kRepeat_TileMode);

    SkMatrix m;
    m.setScale(12, 12);
    s->setLocalMatrix(m);
    return s;
}

class FatBits {
public:
    FatBits() : fShader(createChecker()) {
        fAA = false;
        fStyle = kHair_Style;
        fGrid = true;
        fShowSkeleton = true;
        fUseGPU = false;
    }

    int getZoom() const { return fZ; }

    bool getAA() const { return fAA; }
    void setAA(bool aa) { fAA = aa; }

    bool getGrid() const { return fGrid; }
    void setGrid(bool g) { fGrid = g; }

    bool getShowSkeleton() const { return fShowSkeleton; }
    void setShowSkeleton(bool ss) { fShowSkeleton = ss; }

    bool getUseGPU() const { return fUseGPU; }
    void setUseGPU(bool ug) { fUseGPU = ug; }

    enum Style {
        kHair_Style,
        kStroke_Style,
    };
    Style getStyle() const { return fStyle; }
    void setStyle(Style s) { fStyle = s; }

    void setWHZ(int width, int height, int zoom) {
        fW = width;
        fH = height;
        fZ = zoom;
        fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
        fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
        fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
        fShader->setLocalMatrix(fMatrix);

        SkImage::Info info = {
            width, height, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
        };
        fMinSurface.reset(SkSurface::NewRaster(info, NULL));
        info.fWidth *= zoom;
        info.fHeight *= zoom;
        fMaxSurface.reset(SkSurface::NewRaster(info, NULL));
    }

    void drawBG(SkCanvas*);
    void drawFG(SkCanvas*);
    void drawLine(SkCanvas*, SkPoint pts[2]);
    void drawRect(SkCanvas* canvas, SkPoint pts[2]);

private:
    bool fAA, fGrid, fShowSkeleton, fUseGPU;
    Style fStyle;
    int fW, fH, fZ;
    SkMatrix fMatrix, fInverse;
    SkRect   fBounds;
    SkAutoTUnref<SkShader> fShader;
    SkAutoTUnref<SkSurface> fMinSurface;
    SkAutoTUnref<SkSurface> fMaxSurface;

    void setupPaint(SkPaint* paint) {
        bool aa = this->getAA();
        switch (fStyle) {
            case kHair_Style:
                paint->setStrokeWidth(0);
                break;
            case kStroke_Style:
                paint->setStrokeWidth(SK_Scalar1);
//                paint->setStrokeWidth(SK_Scalar1 + SK_Scalar1/500);
                break;
        }
        paint->setAntiAlias(aa);
    }

    void setupSkeletonPaint(SkPaint* paint) {
        paint->setStyle(SkPaint::kStroke_Style);
        paint->setStrokeWidth(WIRE_FRAME_SIZE);
        paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0);
        paint->setAntiAlias(true);
    }

    void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]);
    void drawRectSkeleton(SkCanvas* max, const SkRect& r) {
        SkPaint paint;
        this->setupSkeletonPaint(&paint);
        SkPath path;

        if (fUseGPU && fAA) {
            SkRect rr = r;
            rr.inset(fZ/2, fZ/2);
            path.addRect(rr);
            path.moveTo(rr.fLeft, rr.fTop);
            path.lineTo(rr.fRight, rr.fBottom);
            rr = r;
            rr.inset(-fZ/2, -fZ/2);
            path.addRect(rr);
        } else {
            path.addRect(r);
            if (fUseGPU) {
                path.moveTo(r.fLeft, r.fTop);
                path.lineTo(r.fRight, r.fBottom);
            }
        }
        max->drawPath(path, paint);
    }

    void copyMinToMax() {
        erase(fMaxSurface);
        SkCanvas* canvas = fMaxSurface->getCanvas();
        canvas->save();
        canvas->concat(fMatrix);
        fMinSurface->draw(canvas, 0, 0, NULL);
        canvas->restore();

        SkPaint paint;
        paint.setXfermodeMode(SkXfermode::kClear_Mode);
        for (int iy = 1; iy < fH; ++iy) {
            SkScalar y = SkIntToScalar(iy * fZ);
            canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
        }
        for (int ix = 1; ix < fW; ++ix) {
            SkScalar x = SkIntToScalar(ix * fZ);
            canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
        }
    }
};

void FatBits::drawBG(SkCanvas* canvas) {
    SkPaint paint;

    paint.setShader(fShader);
    canvas->drawRect(fBounds, paint);
    paint.setShader(NULL);
}

void FatBits::drawFG(SkCanvas* canvas) {
    SkPaint inner, outer;

    inner.setAntiAlias(true);
    inner.setColor(SK_ColorBLACK);
    inner.setStrokeWidth(PIXEL_CENTER_SIZE);

    outer.setAntiAlias(true);
    outer.setColor(SK_ColorWHITE);
    outer.setStrokeWidth(PIXEL_CENTER_SIZE + 2);

    SkScalar half = SkIntToScalar(fZ) / 2;
    for (int iy = 0; iy < fH; ++iy) {
        SkScalar y = SkIntToScalar(iy * fZ) + half;
        for (int ix = 0; ix < fW; ++ix) {
            SkScalar x = SkIntToScalar(ix * fZ) + half;

            canvas->drawPoint(x, y, outer);
            canvas->drawPoint(x, y, inner);
        }
    }
}

void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) {
    SkPaint paint;
    this->setupSkeletonPaint(&paint);

    SkPath path;
    path.moveTo(pts[0]);
    path.lineTo(pts[1]);

    switch (fStyle) {
        case kHair_Style:
            if (fUseGPU) {
                SkPaint p;
                p.setStyle(SkPaint::kStroke_Style);
                p.setStrokeWidth(SK_Scalar1 * fZ);
                SkPath dst;
                p.getFillPath(path, &dst);
                path.addPath(dst);
            }
            break;
        case kStroke_Style: {
            SkPaint p;
            p.setStyle(SkPaint::kStroke_Style);
            p.setStrokeWidth(SK_Scalar1 * fZ);
            SkPath dst;
            p.getFillPath(path, &dst);
            path = dst;

            if (fUseGPU) {
                path.moveTo(dst.getPoint(0));
                path.lineTo(dst.getPoint(2));
            }
        } break;
    }
    max->drawPath(path, paint);
}

void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) {
    SkPaint paint;

    fInverse.mapPoints(pts, 2);

    if (fGrid) {
        SkScalar dd = 0;//SK_Scalar1 / 50;
        pts[0].set(SkScalarRoundToScalar(pts[0].fX) + dd,
                   SkScalarRoundToScalar(pts[0].fY) + dd);
        pts[1].set(SkScalarRoundToScalar(pts[1].fX) + dd,
                   SkScalarRoundToScalar(pts[1].fY) + dd);
    }

    erase(fMinSurface);
    this->setupPaint(&paint);
    paint.setColor(FAT_PIXEL_COLOR);
    fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
    this->copyMinToMax();

    SkCanvas* max = fMaxSurface->getCanvas();

    fMatrix.mapPoints(pts, 2);
    this->drawLineSkeleton(max, pts);

    fMaxSurface->draw(canvas, 0, 0, NULL);
}

void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
    SkPaint paint;

    fInverse.mapPoints(pts, 2);

    if (fGrid) {
        pts[0].set(SkScalarRoundToScalar(pts[0].fX), SkScalarRoundToScalar(pts[0].fY));
        pts[1].set(SkScalarRoundToScalar(pts[1].fX), SkScalarRoundToScalar(pts[1].fY));
    }

    SkRect r;
    r.set(pts, 2);

    erase(fMinSurface);
    this->setupPaint(&paint);
    paint.setColor(FAT_PIXEL_COLOR);
    fMinSurface->getCanvas()->drawRect(r, paint);
    this->copyMinToMax();

    SkCanvas* max = fMaxSurface->getCanvas();

    fMatrix.mapPoints(pts, 2);
    r.set(pts, 2);
    this->drawRectSkeleton(max, r);

    fMaxSurface->draw(canvas, 0, 0, NULL);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

class IndexClick : public SkView::Click {
    int fIndex;
public:
    IndexClick(SkView* v, int index) : SkView::Click(v), fIndex(index) {}

    static int GetIndex(SkView::Click* click) {
        return ((IndexClick*)click)->fIndex;
    }
};

class DrawLineView : public SampleView {
    FatBits fFB;
    SkPoint fPts[2];
    bool    fIsRect;
public:
    DrawLineView() {
        fFB.setWHZ(24, 16, 48);
        fPts[0].set(48, 48);
        fPts[1].set(48 * 5, 48 * 4);
        fIsRect = false;
    }

    void setStyle(FatBits::Style s) {
        fFB.setStyle(s);
        this->inval(NULL);
    }

protected:
    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "FatBits");
            return true;
        }
        SkUnichar uni;
        if (SampleCode::CharQ(*evt, &uni)) {
            switch (uni) {
                case 'r':
                    fIsRect = !fIsRect;
                    this->inval(NULL);
                    return true;
                case 'x':
                    fFB.setGrid(!fFB.getGrid());
                    this->inval(NULL);
                    return true;
                case 's':
                    if (FatBits::kStroke_Style == fFB.getStyle()) {
                        this->setStyle(FatBits::kHair_Style);
                    } else {
                        this->setStyle(FatBits::kStroke_Style);
                    }
                    return true;
                case 'a':
                    fFB.setAA(!fFB.getAA());
                    this->inval(NULL);
                    return true;
                case 'w':
                    fFB.setShowSkeleton(!fFB.getShowSkeleton());
                    this->inval(NULL);
                    return true;
                case 'g':
                    fFB.setUseGPU(!fFB.getUseGPU());
                    this->inval(NULL);
                    return true;
            }
        }
        return this->INHERITED::onQuery(evt);
    }

    virtual void onDrawContent(SkCanvas* canvas) {
        fFB.drawBG(canvas);
        if (fIsRect) {
            fFB.drawRect(canvas, fPts);
        } else {
            fFB.drawLine(canvas, fPts);
        }
        fFB.drawFG(canvas);

        {
            SkString str;
            str.printf("%s %s %s",
                       fFB.getAA() ? "AA" : "BW",
                       FatBits::kHair_Style == fFB.getStyle() ? "Hair" : "Stroke",
                       fFB.getUseGPU() ? "GPU" : "CPU");
            SkPaint paint;
            paint.setAntiAlias(true);
            paint.setTextSize(16);
            paint.setColor(SK_ColorBLUE);
            canvas->drawText(str.c_str(), str.size(), 10, 16, paint);
        }
    }

    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
        SkPoint pt = { x, y };
        int index = -1;
        SkScalar tol = 12;
        if (fPts[0].equalsWithinTolerance(pt, tol)) {
            index = 0;
        } else if (fPts[1].equalsWithinTolerance(pt, tol)) {
            index = 1;
        }
        return new IndexClick(this, index);
    }

    virtual bool onClick(Click* click) {
        int index = IndexClick::GetIndex(click);
        if (index >= 0 && index <= 1) {
            fPts[index] = click->fCurr;
        } else {
            SkScalar dx = click->fCurr.fX - click->fPrev.fX;
            SkScalar dy = click->fCurr.fY - click->fPrev.fY;
            fPts[0].offset(dx, dy);
            fPts[1].offset(dx, dy);
        }
        this->inval(NULL);
        return true;
    }

private:

    typedef SampleView INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static SkView* MyFactory() { return new DrawLineView; }
static SkViewRegister reg(MyFactory);

<commit_msg>add 'c' toggle to test clipping<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkImage.h"
#include "SkSurface.h"

#define FAT_PIXEL_COLOR     SK_ColorBLACK
#define PIXEL_CENTER_SIZE   3
#define WIRE_FRAME_COLOR    0xFFFF0000  /*0xFF00FFFF*/
#define WIRE_FRAME_SIZE     1.5f

static void erase(SkSurface* surface) {
    surface->getCanvas()->clear(0);
}

static SkShader* createChecker() {
//    SkColor colors[] = { 0xFFFDFDFD, 0xFFF4F4F4 };
    SkColor colors[] = { 0xFFFFFFFF, 0xFFFFFFFF };
    SkBitmap bm;
    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
    bm.allocPixels();
    bm.lockPixels();
    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = SkPreMultiplyColor(colors[0]);
    *bm.getAddr32(0, 1) = *bm.getAddr32(1, 0) = SkPreMultiplyColor(colors[1]);
    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
                                               SkShader::kRepeat_TileMode);

    SkMatrix m;
    m.setScale(12, 12);
    s->setLocalMatrix(m);
    return s;
}

class FatBits {
public:
    FatBits() : fShader(createChecker()) {
        fAA = false;
        fStyle = kHair_Style;
        fGrid = true;
        fShowSkeleton = true;
        fUseGPU = false;
        fUseClip = false;

        fClipRect.set(2, 2, 11, 8 );
    }

    int getZoom() const { return fZ; }

    bool getAA() const { return fAA; }
    void setAA(bool aa) { fAA = aa; }

    bool getGrid() const { return fGrid; }
    void setGrid(bool g) { fGrid = g; }

    bool getShowSkeleton() const { return fShowSkeleton; }
    void setShowSkeleton(bool ss) { fShowSkeleton = ss; }

    bool getUseGPU() const { return fUseGPU; }
    void setUseGPU(bool ug) { fUseGPU = ug; }

    bool getUseClip() const { return fUseClip; }
    void setUseClip(bool uc) { fUseClip = uc; }

    enum Style {
        kHair_Style,
        kStroke_Style,
    };
    Style getStyle() const { return fStyle; }
    void setStyle(Style s) { fStyle = s; }

    void setWHZ(int width, int height, int zoom) {
        fW = width;
        fH = height;
        fZ = zoom;
        fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
        fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
        fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
        fShader->setLocalMatrix(fMatrix);

        SkImage::Info info = {
            width, height, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
        };
        fMinSurface.reset(SkSurface::NewRaster(info, NULL));
        info.fWidth *= zoom;
        info.fHeight *= zoom;
        fMaxSurface.reset(SkSurface::NewRaster(info, NULL));
    }

    void drawBG(SkCanvas*);
    void drawFG(SkCanvas*);
    void drawLine(SkCanvas*, SkPoint pts[2]);
    void drawRect(SkCanvas* canvas, SkPoint pts[2]);

private:
    bool fAA, fGrid, fShowSkeleton, fUseGPU, fUseClip;
    Style fStyle;
    int fW, fH, fZ;
    SkMatrix fMatrix, fInverse;
    SkRect   fBounds, fClipRect;
    SkAutoTUnref<SkShader> fShader;
    SkAutoTUnref<SkSurface> fMinSurface;
    SkAutoTUnref<SkSurface> fMaxSurface;

    void setupPaint(SkPaint* paint) {
        bool aa = this->getAA();
        switch (fStyle) {
            case kHair_Style:
                paint->setStrokeWidth(0);
                break;
            case kStroke_Style:
                paint->setStrokeWidth(SK_Scalar1);
//                paint->setStrokeWidth(SK_Scalar1 + SK_Scalar1/500);
                break;
        }
        paint->setAntiAlias(aa);
    }

    void setupSkeletonPaint(SkPaint* paint) {
        paint->setStyle(SkPaint::kStroke_Style);
        paint->setStrokeWidth(WIRE_FRAME_SIZE);
        paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0);
        paint->setAntiAlias(true);
    }

    void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]);
    void drawRectSkeleton(SkCanvas* max, const SkRect& r) {
        SkPaint paint;
        this->setupSkeletonPaint(&paint);
        SkPath path;

        if (fUseGPU && fAA) {
            SkRect rr = r;
            rr.inset(fZ/2, fZ/2);
            path.addRect(rr);
            path.moveTo(rr.fLeft, rr.fTop);
            path.lineTo(rr.fRight, rr.fBottom);
            rr = r;
            rr.inset(-fZ/2, -fZ/2);
            path.addRect(rr);
        } else {
            path.addRect(r);
            if (fUseGPU) {
                path.moveTo(r.fLeft, r.fTop);
                path.lineTo(r.fRight, r.fBottom);
            }
        }
        max->drawPath(path, paint);
    }

    void copyMinToMax() {
        erase(fMaxSurface);
        SkCanvas* canvas = fMaxSurface->getCanvas();
        canvas->save();
        canvas->concat(fMatrix);
        fMinSurface->draw(canvas, 0, 0, NULL);
        canvas->restore();

        SkPaint paint;
        paint.setXfermodeMode(SkXfermode::kClear_Mode);
        for (int iy = 1; iy < fH; ++iy) {
            SkScalar y = SkIntToScalar(iy * fZ);
            canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
        }
        for (int ix = 1; ix < fW; ++ix) {
            SkScalar x = SkIntToScalar(ix * fZ);
            canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
        }
    }
};

void FatBits::drawBG(SkCanvas* canvas) {
    SkPaint paint;

    paint.setShader(fShader);
    canvas->drawRect(fBounds, paint);
    paint.setShader(NULL);
}

void FatBits::drawFG(SkCanvas* canvas) {
    SkPaint inner, outer;

    inner.setAntiAlias(true);
    inner.setColor(SK_ColorBLACK);
    inner.setStrokeWidth(PIXEL_CENTER_SIZE);

    outer.setAntiAlias(true);
    outer.setColor(SK_ColorWHITE);
    outer.setStrokeWidth(PIXEL_CENTER_SIZE + 2);

    SkScalar half = SkIntToScalar(fZ) / 2;
    for (int iy = 0; iy < fH; ++iy) {
        SkScalar y = SkIntToScalar(iy * fZ) + half;
        for (int ix = 0; ix < fW; ++ix) {
            SkScalar x = SkIntToScalar(ix * fZ) + half;

            canvas->drawPoint(x, y, outer);
            canvas->drawPoint(x, y, inner);
        }
    }

    if (fUseClip) {
        SkPaint p;
        p.setStyle(SkPaint::kStroke_Style);
        p.setColor(SK_ColorLTGRAY);
        SkRect r = {
            fClipRect.fLeft * fZ,
            fClipRect.fTop * fZ,
            fClipRect.fRight * fZ,
            fClipRect.fBottom * fZ
        };
        canvas->drawRect(r, p);
    }
}

void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) {
    SkPaint paint;
    this->setupSkeletonPaint(&paint);

    SkPath path;
    path.moveTo(pts[0]);
    path.lineTo(pts[1]);

    switch (fStyle) {
        case kHair_Style:
            if (fUseGPU) {
                SkPaint p;
                p.setStyle(SkPaint::kStroke_Style);
                p.setStrokeWidth(SK_Scalar1 * fZ);
                SkPath dst;
                p.getFillPath(path, &dst);
                path.addPath(dst);
            }
            break;
        case kStroke_Style: {
            SkPaint p;
            p.setStyle(SkPaint::kStroke_Style);
            p.setStrokeWidth(SK_Scalar1 * fZ);
            SkPath dst;
            p.getFillPath(path, &dst);
            path = dst;

            if (fUseGPU) {
                path.moveTo(dst.getPoint(0));
                path.lineTo(dst.getPoint(2));
            }
        } break;
    }
    max->drawPath(path, paint);
}

void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) {
    SkPaint paint;

    fInverse.mapPoints(pts, 2);

    if (fGrid) {
        SkScalar dd = 0;//SK_Scalar1 / 50;
        pts[0].set(SkScalarRoundToScalar(pts[0].fX) + dd,
                   SkScalarRoundToScalar(pts[0].fY) + dd);
        pts[1].set(SkScalarRoundToScalar(pts[1].fX) + dd,
                   SkScalarRoundToScalar(pts[1].fY) + dd);
    }

    erase(fMinSurface);
    this->setupPaint(&paint);
    paint.setColor(FAT_PIXEL_COLOR);
    if (fUseClip) {
        fMinSurface->getCanvas()->save();
        SkRect r = fClipRect;
        r.inset(SK_Scalar1/3, SK_Scalar1/3);
        fMinSurface->getCanvas()->clipRect(r, SkRegion::kIntersect_Op, true);
    }
    fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
    if (fUseClip) {
        fMinSurface->getCanvas()->restore();
    }
    this->copyMinToMax();

    SkCanvas* max = fMaxSurface->getCanvas();

    fMatrix.mapPoints(pts, 2);
    this->drawLineSkeleton(max, pts);

    fMaxSurface->draw(canvas, 0, 0, NULL);
}

void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
    SkPaint paint;

    fInverse.mapPoints(pts, 2);

    if (fGrid) {
        pts[0].set(SkScalarRoundToScalar(pts[0].fX), SkScalarRoundToScalar(pts[0].fY));
        pts[1].set(SkScalarRoundToScalar(pts[1].fX), SkScalarRoundToScalar(pts[1].fY));
    }

    SkRect r;
    r.set(pts, 2);

    erase(fMinSurface);
    this->setupPaint(&paint);
    paint.setColor(FAT_PIXEL_COLOR);
    fMinSurface->getCanvas()->drawRect(r, paint);
    this->copyMinToMax();

    SkCanvas* max = fMaxSurface->getCanvas();

    fMatrix.mapPoints(pts, 2);
    r.set(pts, 2);
    this->drawRectSkeleton(max, r);

    fMaxSurface->draw(canvas, 0, 0, NULL);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

class IndexClick : public SkView::Click {
    int fIndex;
public:
    IndexClick(SkView* v, int index) : SkView::Click(v), fIndex(index) {}

    static int GetIndex(SkView::Click* click) {
        return ((IndexClick*)click)->fIndex;
    }
};

class DrawLineView : public SampleView {
    FatBits fFB;
    SkPoint fPts[2];
    bool    fIsRect;
public:
    DrawLineView() {
        fFB.setWHZ(24, 16, 48);
        fPts[0].set(48, 48);
        fPts[1].set(48 * 5, 48 * 4);
        fIsRect = false;
    }

    void setStyle(FatBits::Style s) {
        fFB.setStyle(s);
        this->inval(NULL);
    }

protected:
    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
        if (SampleCode::TitleQ(*evt)) {
            SampleCode::TitleR(evt, "FatBits");
            return true;
        }
        SkUnichar uni;
        if (SampleCode::CharQ(*evt, &uni)) {
            switch (uni) {
                case 'c':
                    fFB.setUseClip(!fFB.getUseClip());
                    this->inval(NULL);
                    return true;
                case 'r':
                    fIsRect = !fIsRect;
                    this->inval(NULL);
                    return true;
                case 'x':
                    fFB.setGrid(!fFB.getGrid());
                    this->inval(NULL);
                    return true;
                case 's':
                    if (FatBits::kStroke_Style == fFB.getStyle()) {
                        this->setStyle(FatBits::kHair_Style);
                    } else {
                        this->setStyle(FatBits::kStroke_Style);
                    }
                    return true;
                case 'a':
                    fFB.setAA(!fFB.getAA());
                    this->inval(NULL);
                    return true;
                case 'w':
                    fFB.setShowSkeleton(!fFB.getShowSkeleton());
                    this->inval(NULL);
                    return true;
                case 'g':
                    fFB.setUseGPU(!fFB.getUseGPU());
                    this->inval(NULL);
                    return true;
            }
        }
        return this->INHERITED::onQuery(evt);
    }

    virtual void onDrawContent(SkCanvas* canvas) {
        fFB.drawBG(canvas);
        if (fIsRect) {
            fFB.drawRect(canvas, fPts);
        } else {
            fFB.drawLine(canvas, fPts);
        }
        fFB.drawFG(canvas);

        {
            SkString str;
            str.printf("%s %s %s %s",
                       fFB.getAA() ? "AA" : "BW",
                       FatBits::kHair_Style == fFB.getStyle() ? "Hair" : "Stroke",
                       fFB.getUseGPU() ? "GPU" : "CPU",
                       fFB.getUseClip() ? "clip" : "noclip");
            SkPaint paint;
            paint.setAntiAlias(true);
            paint.setTextSize(16);
            paint.setColor(SK_ColorBLUE);
            canvas->drawText(str.c_str(), str.size(), 10, 16, paint);
        }
    }

    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
        SkPoint pt = { x, y };
        int index = -1;
        SkScalar tol = 12;
        if (fPts[0].equalsWithinTolerance(pt, tol)) {
            index = 0;
        } else if (fPts[1].equalsWithinTolerance(pt, tol)) {
            index = 1;
        }
        return new IndexClick(this, index);
    }

    virtual bool onClick(Click* click) {
        int index = IndexClick::GetIndex(click);
        if (index >= 0 && index <= 1) {
            fPts[index] = click->fCurr;
        } else {
            SkScalar dx = click->fCurr.fX - click->fPrev.fX;
            SkScalar dy = click->fCurr.fY - click->fPrev.fY;
            fPts[0].offset(dx, dy);
            fPts[1].offset(dx, dy);
        }
        this->inval(NULL);
        return true;
    }

private:

    typedef SampleView INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static SkView* MyFactory() { return new DrawLineView; }
static SkViewRegister reg(MyFactory);

<|endoftext|>


<commit_before>//---------------------------------------------------------------------------//
/*
  Copyright (c) 2012, Stuart R. Slattery
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  *: Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.

  *: Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.

  *: Neither the name of the University of Wisconsin - Madison nor the
  names of its contributors may be used to endorse or promote products
  derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//---------------------------------------------------------------------------//
/*!
  * \file DTK_Box.cpp
  * \author Stuart R. Slattery
  * \brief Bounding box definition.
  */
//---------------------------------------------------------------------------//

#include "DTK_Box.hpp"
#include "DTK_Assertion.hpp"

namespace DataTransferKit
{
//---------------------------------------------------------------------------//
/*!
 * \brief Default constructor.
 */
Box::Box()
    : d_x_min( 0.0 )
    , d_y_min( 0.0 )
    , d_z_min( 0.0 )
    , d_x_max( 0.0 )
    , d_y_max( 0.0 )
    , d_z_max( 0.0 )
{ /* ... */ }

//---------------------------------------------------------------------------//
/*!
 * \brief Constructor.
 *
 * \param x_min Minimum x coordinate value in the box.
 *
 * \param y_min Minimum y coordinate value in the box.
 *
 * \param z_min Minimum z coordinate value in the box.
 *
 * \param x_max Maximum x coordinate value in the box.
 *
 * \param y_max Maximum y coordinate value in the box.
 *
 * \param z_max Maximum z coordinate value in the box.
 */
Box::Box(
    const double x_min, const double y_min, const double z_min,
    const double x_max, const double y_max, const double z_max )
    : d_x_min( x_min )
    , d_y_min( y_min )
    , d_z_min( z_min )
    , d_x_max( x_max )
    , d_y_max( y_max )
    , d_z_max( z_max )
{
    testPrecondition( d_x_min <= d_x_max );
    testPrecondition( d_y_min <= d_y_max );
    testPrecondition( d_z_min <= d_z_max );
}

//---------------------------------------------------------------------------//
/*!
 * \brief Tuple constructor.
 *
 * \param bounds Tuple containing {x_min, y_min, z_min, x_max, y_max, z_max}.
 */
Box::Box( const Teuchos::Tuple<double,6>& bounds )
    : d_x_min( bounds[0] )
    , d_y_min( bounds[1] )
    , d_z_min( bounds[2] )
    , d_x_max( bounds[3] )
    , d_y_max( bounds[4] )
    , d_z_max( bounds[5] )
{
    testPrecondition( d_x_min <= d_x_max );
    testPrecondition( d_y_min <= d_y_max );
    testPrecondition( d_z_min <= d_z_max );
}

//---------------------------------------------------------------------------//
/*!
 * \brief Destructor.
 */
Box::~Box()
{ /* ... */ }

//---------------------------------------------------------------------------//
/*!
 * \brief Determine if a point is in the box within a specified tolerance.
 *
 * \param coords Cartesian coordinates to check for point inclusion. The
 * coordinates must have a dimension between 0 and 3.
 *
 * \param tolerance The geometric tolerance to check point-inclusion with.
 *
 * \return Return true if the point is in the box, false if not. A point on
 * the box boundary will return true.
 */
bool Box::pointInBox( const Teuchos::Array<double>& coords,
		      const double tolerance ) const
{
    testPrecondition( 3 == coords.size() );

    if ( coords[0] >= d_x_min - tolerance &&
	 coords[1] >= d_y_min - tolerance &&
	 coords[2] >= d_z_min - tolerance &&
	 coords[0] <= d_x_max + tolerance &&
	 coords[1] <= d_y_max + tolerance &&
	 coords[2] <= d_z_max + tolerance )
    {
	return true;
    }

    return false;
}

//---------------------------------------------------------------------------//
/*!
 * \brief Compute the volume of the box.
 *
 * \return Return the volume of the box.
 */
double Box::volume() const
{
    return (d_x_max-d_x_min)*(d_y_max-d_y_min)*(d_z_max-d_z_min);
}

//---------------------------------------------------------------------------//
/*!
 * \brief Compute the bounding box around the box.
 *
 * \return The bounding box around the box.
 */
BoundingBox Box::boundingBox() const
{
    return BoundingBox( d_x_min, d_y_min, d_z_min, d_x_max, d_y_max, d_z_max );
}

//---------------------------------------------------------------------------//
/*!
 * \brief Get the centroid of the box.
 *
 * \return The centroid coordinates.
 */
Teuchos::Array<double> Box::centroid() const
{
    Teuchos::Array<double> center(3);
    center[0] = (d_x_max + d_x_min) / 2.0;
    center[1] = (d_y_max + d_y_min) / 2.0;
    center[2] = (d_z_max + d_z_min) / 2.0;
    return center;
}

//---------------------------------------------------------------------------//
/*!
 * \brief Print the box description to an ostream.
 *
 * \return The ostream.
 */

//---------------------------------------------------------------------------//
std::ostream& operator<< (std::ostream& os,const DataTransferKit::Box& b)
{
  Teuchos::Tuple<double,6> bounds = b.getBounds();

  os << "Box: d_x_min=" << bounds[0]
     << ",d_y_min=" << bounds[1]
     << ",d_z_min=" << bounds[2]
     << ",d_x_max=" << bounds[3]
     << ",d_y_max=" << bounds[4]
     << ",d_x_max=" << bounds[5];

  return os;
}

} // end namespace DataTransferKit

//---------------------------------------------------------------------------//
// end DTK_Box.cpp
//---------------------------------------------------------------------------//

<commit_msg>Fixing output string.<commit_after>//---------------------------------------------------------------------------//
/*
  Copyright (c) 2012, Stuart R. Slattery
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  *: Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.

  *: Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.

  *: Neither the name of the University of Wisconsin - Madison nor the
  names of its contributors may be used to endorse or promote products
  derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//---------------------------------------------------------------------------//
/*!
  * \file DTK_Box.cpp
  * \author Stuart R. Slattery
  * \brief Bounding box definition.
  */
//---------------------------------------------------------------------------//

#include "DTK_Box.hpp"
#include "DTK_Assertion.hpp"

namespace DataTransferKit
{
//---------------------------------------------------------------------------//
/*!
 * \brief Default constructor.
 */
Box::Box()
    : d_x_min( 0.0 )
    , d_y_min( 0.0 )
    , d_z_min( 0.0 )
    , d_x_max( 0.0 )
    , d_y_max( 0.0 )
    , d_z_max( 0.0 )
{ /* ... */ }

//---------------------------------------------------------------------------//
/*!
 * \brief Constructor.
 *
 * \param x_min Minimum x coordinate value in the box.
 *
 * \param y_min Minimum y coordinate value in the box.
 *
 * \param z_min Minimum z coordinate value in the box.
 *
 * \param x_max Maximum x coordinate value in the box.
 *
 * \param y_max Maximum y coordinate value in the box.
 *
 * \param z_max Maximum z coordinate value in the box.
 */
Box::Box(
    const double x_min, const double y_min, const double z_min,
    const double x_max, const double y_max, const double z_max )
    : d_x_min( x_min )
    , d_y_min( y_min )
    , d_z_min( z_min )
    , d_x_max( x_max )
    , d_y_max( y_max )
    , d_z_max( z_max )
{
    testPrecondition( d_x_min <= d_x_max );
    testPrecondition( d_y_min <= d_y_max );
    testPrecondition( d_z_min <= d_z_max );
}

//---------------------------------------------------------------------------//
/*!
 * \brief Tuple constructor.
 *
 * \param bounds Tuple containing {x_min, y_min, z_min, x_max, y_max, z_max}.
 */
Box::Box( const Teuchos::Tuple<double,6>& bounds )
    : d_x_min( bounds[0] )
    , d_y_min( bounds[1] )
    , d_z_min( bounds[2] )
    , d_x_max( bounds[3] )
    , d_y_max( bounds[4] )
    , d_z_max( bounds[5] )
{
    testPrecondition( d_x_min <= d_x_max );
    testPrecondition( d_y_min <= d_y_max );
    testPrecondition( d_z_min <= d_z_max );
}

//---------------------------------------------------------------------------//
/*!
 * \brief Destructor.
 */
Box::~Box()
{ /* ... */ }

//---------------------------------------------------------------------------//
/*!
 * \brief Determine if a point is in the box within a specified tolerance.
 *
 * \param coords Cartesian coordinates to check for point inclusion. The
 * coordinates must have a dimension between 0 and 3.
 *
 * \param tolerance The geometric tolerance to check point-inclusion with.
 *
 * \return Return true if the point is in the box, false if not. A point on
 * the box boundary will return true.
 */
bool Box::pointInBox( const Teuchos::Array<double>& coords,
		      const double tolerance ) const
{
    testPrecondition( 3 == coords.size() );

    if ( coords[0] >= d_x_min - tolerance &&
	 coords[1] >= d_y_min - tolerance &&
	 coords[2] >= d_z_min - tolerance &&
	 coords[0] <= d_x_max + tolerance &&
	 coords[1] <= d_y_max + tolerance &&
	 coords[2] <= d_z_max + tolerance )
    {
	return true;
    }

    return false;
}

//---------------------------------------------------------------------------//
/*!
 * \brief Compute the volume of the box.
 *
 * \return Return the volume of the box.
 */
double Box::volume() const
{
    return (d_x_max-d_x_min)*(d_y_max-d_y_min)*(d_z_max-d_z_min);
}

//---------------------------------------------------------------------------//
/*!
 * \brief Compute the bounding box around the box.
 *
 * \return The bounding box around the box.
 */
BoundingBox Box::boundingBox() const
{
    return BoundingBox( d_x_min, d_y_min, d_z_min, d_x_max, d_y_max, d_z_max );
}

//---------------------------------------------------------------------------//
/*!
 * \brief Get the centroid of the box.
 *
 * \return The centroid coordinates.
 */
Teuchos::Array<double> Box::centroid() const
{
    Teuchos::Array<double> center(3);
    center[0] = (d_x_max + d_x_min) / 2.0;
    center[1] = (d_y_max + d_y_min) / 2.0;
    center[2] = (d_z_max + d_z_min) / 2.0;
    return center;
}

//---------------------------------------------------------------------------//
/*!
 * \brief Print the box description to an ostream.
 *
 * \return The ostream.
 */

//---------------------------------------------------------------------------//
std::ostream& operator<< (std::ostream& os,const DataTransferKit::Box& b)
{
  Teuchos::Tuple<double,6> bounds = b.getBounds();

  os << "Box: d_x_min=" << bounds[0]
     << ",d_y_min=" << bounds[1]
     << ",d_z_min=" << bounds[2]
     << ",d_x_max=" << bounds[3]
     << ",d_y_max=" << bounds[4]
     << ",d_z_max=" << bounds[5];

  return os;
}

} // end namespace DataTransferKit

//---------------------------------------------------------------------------//
// end DTK_Box.cpp
//---------------------------------------------------------------------------//

<|endoftext|>