<commit_before>/** @file gsFitting.hpp

    @brief Provides implementation of data fitting algorithms by least
    squares approximation.

    This file is part of the G+Smo library.

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at http://mozilla.org/MPL/2.0/.

    Author(s): M. Kapl, G. Kiss, A. Mantzaflaris

*/

#include <gsCore/gsBasis.h>
#include <gsCore/gsGeometry.h>
#include <gsCore/gsLinearAlgebra.h>
#include <gsTensor/gsTensorDomainIterator.h>

namespace gismo
{

template<class T>
gsFitting<T>::~gsFitting()
{
    if ( m_result )
        delete m_result;
}

template<class T>
gsFitting<T>::  gsFitting(gsMatrix<T> const & param_values,
                          gsMatrix<T> const & points,
                          gsBasis<T>  & basis)
{
    m_param_values = param_values;
    m_points = points;
    m_result = NULL;
    m_basis = &basis;
    m_points.transposeInPlace();
}

template<class T>
void gsFitting<T>::compute(T lambda)
{
    // Wipe out previous result
    if ( m_result )
        delete m_result;

    const int num_basis=m_basis->size();
    const int dimension=m_points.cols();

    //left side matrix
    //gsMatrix<T> A_mat(num_basis,num_basis);
    gsSparseMatrix<T> A_mat(num_basis, num_basis);
    //gsMatrix<T>A_mat(num_basis,num_basis);
    //To optimize sparse matrix an estimation of nonzero elements per
    //column can be given here
    int nonZerosPerCol = 1;
    for (int i = 0; i < m_basis->dim(); ++i) // to do: improve
        nonZerosPerCol *= m_basis->degree(i) + 1;
        // nonZerosPerCol *= ( 2 * m_basis->degree(i) + 1 ) * 4;
    A_mat.reservePerColumn( nonZerosPerCol );

    //right side vector (more dimensional!)
    gsMatrix<T> m_B(num_basis, dimension);
    m_B.setZero(); // enusure that all entries are zero in the beginning

    // building the matrix A and the vector b of the system of linear
    // equations A*x==b

    assembleSystem(A_mat, m_B);

    // --- Smoothing matrix computation
    //test degree >=3
    if(lambda > 0)
      applySmoothing(lambda, A_mat);

    //Solving the system of linear equations A*x=b (works directly for a right side which has a dimension with higher than 1)

    //gsDebugVar( A_mat.nonZerosPerCol().maxCoeff() );
    //gsDebugVar( A_mat.nonZerosPerCol().minCoeff() );
    A_mat.makeCompressed();

    typename gsSparseSolver<T>::BiCGSTABILUT solver( A_mat );

    if ( solver.preconditioner().info() != Eigen::Success )
    {
        gsWarn<<  "The preconditioner failed. Aborting.\n";
        m_result = NULL;
        return;
    }
    // Solves for many right hand side  columns
    gsMatrix<T> x;
    x = solver.solve(m_B); //toDense()
    //gsMatrix<T> x (m_B.rows(), m_B.cols());
    //x=A_mat.fullPivHouseholderQr().solve( m_B);
    // Solves for many right hand side  columns
    // finally generate the B-spline curve
    m_result = m_basis->makeGeometry( give(x) ).release();
}

template <class T>
void gsFitting<T>::assembleSystem(gsSparseMatrix<T>& A_mat,
                                  gsMatrix<T>& m_B)
{
    const int num_points = m_points.rows();

    //for computing the value of the basis function
    gsMatrix<T> value, curr_point;
    gsMatrix<unsigned> actives;

    for(index_t k = 0; k != num_points; ++k)
    {
        curr_point = m_param_values.col(k);

        //computing the values of the basis functions at the current point
        m_basis->eval_into(curr_point, value);

        // which functions have been computed i.e. which are active
        m_basis->active_into(curr_point, actives);

        const index_t numActive = actives.rows();

        for (index_t i = 0; i != numActive; ++i)
        {
            const int ii = actives.at(i);
            m_B.row(ii) += value.at(i) * m_points.row(k);
            for (index_t j = 0; j != numActive; ++j)
                A_mat(ii, actives.at(j)) += value.at(i) * value.at(j);
        }
    }
}

template<class T>
void gsFitting<T>::applySmoothing(T lambda, gsSparseMatrix<T> & A_mat)
{
    const int dim = m_basis->dim();
    const int stride = dim*(dim+1)/2;

    gsVector<int> numNodes(dim);
    for ( int i = 0; i!= dim; ++i )
        numNodes[i] = this->m_basis->degree(i);//+1;
    gsGaussRule<T> QuRule( numNodes ); // Reference Quadrature rule
    gsMatrix<T> quNodes, der2, localA;
    gsVector<T> quWeights;
    gsMatrix<unsigned> actives;

    typename gsBasis<T>::domainIter domIt = m_basis->makeDomainIterator();

    for (; domIt->good(); domIt->next() )
    {
        // Map the Quadrature rule to the element and compute basis derivatives
        QuRule.mapTo( domIt->lowerCorner(), domIt->upperCorner(), quNodes, quWeights );
        m_basis->deriv2_into(quNodes, der2);
        m_basis->active_into(domIt->center, actives);
        const index_t numActive = actives.rows();
        localA.setZero(numActive, numActive );

        // perform the quadrature
        for (index_t k = 0; k < quWeights.rows(); ++k)
        {
            const T weight = quWeights[k] * lambda;

            for (index_t i=0; i!=numActive; ++i)
                for (index_t j=0; j!=numActive; ++j)
                {
                    T localAij = 0; // temporary variable

                    for (int s = 0; s < stride; s++)
                    {
                        // The pure second derivatives
                        // d^2u N_i * d^2u N_j + ...
                        if (s < dim)
                        {
                            localAij += der2(i * stride + s, k) *
                                        der2(j * stride + s, k);
                        }
                        // Mixed derivatives 2 * dudv N_i * dudv N_j + ...
                        else
                        {
                            localAij += 2 * der2(i * stride + s, k) *
                                            der2(j * stride + s, k);
                        }
                    }

                    localA(i, j) += weight * localAij;

                    // old code, just for the case if I break something

//                    localA(i,j) += weight * (
//                            // der2.template block<3,1>(i*stride,k)
//                            der2(i*stride  , k) * der2(j*stride  , k) +  // d^2u N_i * d^2u N_j
//                            der2(i*stride+1, k) * der2(j*stride+1, k) +  // d^2v N_i * d^2v N_j
//                            2 * der2(i*stride+2, k) * der2(j*stride+2, k)// dudv N_i * dudv N_j
//                            );
                }
        }

        for (index_t i=0; i!=numActive; ++i)
        {
            const int ii = actives(i,0);
            for (index_t j=0; j!=numActive; ++j)
                A_mat( ii, actives(j,0) ) += localA(i,j);
        }
    }
}

template<class T>
void gsFitting<T>::computeErrors()
{
    m_pointErrors.clear();

    gsMatrix<T> val_i;
    //m_result->eval_into(m_param_values.col(0), val_i);
    m_result->eval_into(m_param_values, val_i);
    m_pointErrors.push_back( (m_points.row(0) - val_i.col(0).transpose()).norm() );
    m_max_error = m_min_error = m_pointErrors.back();

    for (index_t i = 1; i < m_points.rows(); i++)
    {
        //m_result->eval_into(m_param_values.col(i), val_i);

        const T err = (m_points.row(i) - val_i.col(i).transpose()).norm() ;

        m_pointErrors.push_back(err);

        if ( err > m_max_error ) m_max_error = err;
        if ( err < m_min_error ) m_min_error = err;
    }
}

template<class T>
void gsFitting<T>::computeMaxNormErrors()
{
    m_pointErrors.clear();

    gsMatrix<T> values;
    m_result->eval_into(m_param_values, values);

    for (index_t i = 0; i != m_points.rows(); i++)
    {
        const T err = (m_points.row(i) - values.col(i).transpose()).cwiseAbs().maxCoeff();

        m_pointErrors.push_back(err);

        if ( i == 0 || m_max_error < err ) m_max_error = err;
        if ( i == 0 || err < m_min_error ) m_min_error = err;
    }
}

template<class T>
void gsFitting<T>::computeApproxError(T& error, int type) const
{
    gsMatrix<T> results;
    m_result->eval_into(m_param_values, results);
    error = 0;

    //computing the approximation error = sum_i ||x(u_i)-p_i||^2

    for (index_t i = 0; i != m_points.rows(); ++i)
    {
        const T err = (m_points.row(i) - results.col(i).transpose()).squaredNorm();

        switch (type) {
        case 0:
            error += err;
            break;
        case 1:
            error += sqrt(err);
            break;
        default:
            gsWarn << "Unknown type in computeApproxError(error, type)...\n";
            break;
        }
    }
}

template<class T>
void gsFitting<T>::get_Error(std::vector<T>& errors, int type) const
{
    errors.clear();
    gsMatrix<T> results;
    m_result->eval_into(m_param_values,results);
    results.transposeInPlace();

    for (index_t row = 0; row != m_points.rows(); row++)
    {
        T err = 0;
        for (index_t col = 0; col != m_points.cols(); col++)
        {
            err += math::pow(m_points(row, col) - results(row, col), 2);
        }

        switch (type)
        {
        case 0:
            errors.push_back(err);
            break;
        case 1:
            errors.push_back(sqrt(err));
            break;
        default:
            gsWarn << "Unknown type in get_Error(errors, type)...\n";
            break;
        }
    }
}

} // namespace gismo
<commit_msg>fitting is faster with higher number of non zero entries...<commit_after>/** @file gsFitting.hpp

    @brief Provides implementation of data fitting algorithms by least
    squares approximation.

    This file is part of the G+Smo library.

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at http://mozilla.org/MPL/2.0/.

    Author(s): M. Kapl, G. Kiss, A. Mantzaflaris

*/

#include <gsCore/gsBasis.h>
#include <gsCore/gsGeometry.h>
#include <gsCore/gsLinearAlgebra.h>
#include <gsTensor/gsTensorDomainIterator.h>

namespace gismo
{

template<class T>
gsFitting<T>::~gsFitting()
{
    if ( m_result )
        delete m_result;
}

template<class T>
gsFitting<T>::  gsFitting(gsMatrix<T> const & param_values,
                          gsMatrix<T> const & points,
                          gsBasis<T>  & basis)
{
    m_param_values = param_values;
    m_points = points;
    m_result = NULL;
    m_basis = &basis;
    m_points.transposeInPlace();
}

template<class T>
void gsFitting<T>::compute(T lambda)
{
    // Wipe out previous result
    if ( m_result )
        delete m_result;

    const int num_basis=m_basis->size();
    const int dimension=m_points.cols();

    //left side matrix
    //gsMatrix<T> A_mat(num_basis,num_basis);
    gsSparseMatrix<T> A_mat(num_basis, num_basis);
    //gsMatrix<T>A_mat(num_basis,num_basis);
    //To optimize sparse matrix an estimation of nonzero elements per
    //column can be given here
    int nonZerosPerCol = 1;
    for (int i = 0; i < m_basis->dim(); ++i) // to do: improve
        // nonZerosPerCol *= m_basis->degree(i) + 1;
        nonZerosPerCol *= ( 2 * m_basis->degree(i) + 1 ) * 4;
    A_mat.reservePerColumn( nonZerosPerCol );

    //right side vector (more dimensional!)
    gsMatrix<T> m_B(num_basis, dimension);
    m_B.setZero(); // enusure that all entries are zero in the beginning

    // building the matrix A and the vector b of the system of linear
    // equations A*x==b

    assembleSystem(A_mat, m_B);

    // --- Smoothing matrix computation
    //test degree >=3
    if(lambda > 0)
      applySmoothing(lambda, A_mat);

    //Solving the system of linear equations A*x=b (works directly for a right side which has a dimension with higher than 1)

    //gsDebugVar( A_mat.nonZerosPerCol().maxCoeff() );
    //gsDebugVar( A_mat.nonZerosPerCol().minCoeff() );
    A_mat.makeCompressed();

    typename gsSparseSolver<T>::BiCGSTABILUT solver( A_mat );

    if ( solver.preconditioner().info() != Eigen::Success )
    {
        gsWarn<<  "The preconditioner failed. Aborting.\n";
        m_result = NULL;
        return;
    }
    // Solves for many right hand side  columns
    gsMatrix<T> x;
    x = solver.solve(m_B); //toDense()
    //gsMatrix<T> x (m_B.rows(), m_B.cols());
    //x=A_mat.fullPivHouseholderQr().solve( m_B);
    // Solves for many right hand side  columns
    // finally generate the B-spline curve
    m_result = m_basis->makeGeometry( give(x) ).release();
}

template <class T>
void gsFitting<T>::assembleSystem(gsSparseMatrix<T>& A_mat,
                                  gsMatrix<T>& m_B)
{
    const int num_points = m_points.rows();

    //for computing the value of the basis function
    gsMatrix<T> value, curr_point;
    gsMatrix<unsigned> actives;

    for(index_t k = 0; k != num_points; ++k)
    {
        curr_point = m_param_values.col(k);

        //computing the values of the basis functions at the current point
        m_basis->eval_into(curr_point, value);

        // which functions have been computed i.e. which are active
        m_basis->active_into(curr_point, actives);

        const index_t numActive = actives.rows();

        for (index_t i = 0; i != numActive; ++i)
        {
            const int ii = actives.at(i);
            m_B.row(ii) += value.at(i) * m_points.row(k);
            for (index_t j = 0; j != numActive; ++j)
                A_mat(ii, actives.at(j)) += value.at(i) * value.at(j);
        }
    }
}

template<class T>
void gsFitting<T>::applySmoothing(T lambda, gsSparseMatrix<T> & A_mat)
{
    const int dim = m_basis->dim();
    const int stride = dim*(dim+1)/2;

    gsVector<int> numNodes(dim);
    for ( int i = 0; i!= dim; ++i )
        numNodes[i] = this->m_basis->degree(i);//+1;
    gsGaussRule<T> QuRule( numNodes ); // Reference Quadrature rule
    gsMatrix<T> quNodes, der2, localA;
    gsVector<T> quWeights;
    gsMatrix<unsigned> actives;

    typename gsBasis<T>::domainIter domIt = m_basis->makeDomainIterator();

    for (; domIt->good(); domIt->next() )
    {
        // Map the Quadrature rule to the element and compute basis derivatives
        QuRule.mapTo( domIt->lowerCorner(), domIt->upperCorner(), quNodes, quWeights );
        m_basis->deriv2_into(quNodes, der2);
        m_basis->active_into(domIt->center, actives);
        const index_t numActive = actives.rows();
        localA.setZero(numActive, numActive );

        // perform the quadrature
        for (index_t k = 0; k < quWeights.rows(); ++k)
        {
            const T weight = quWeights[k] * lambda;

            for (index_t i=0; i!=numActive; ++i)
                for (index_t j=0; j!=numActive; ++j)
                {
                    T localAij = 0; // temporary variable

                    for (int s = 0; s < stride; s++)
                    {
                        // The pure second derivatives
                        // d^2u N_i * d^2u N_j + ...
                        if (s < dim)
                        {
                            localAij += der2(i * stride + s, k) *
                                        der2(j * stride + s, k);
                        }
                        // Mixed derivatives 2 * dudv N_i * dudv N_j + ...
                        else
                        {
                            localAij += 2 * der2(i * stride + s, k) *
                                            der2(j * stride + s, k);
                        }
                    }

                    localA(i, j) += weight * localAij;

                    // old code, just for the case if I break something

//                    localA(i,j) += weight * (
//                            // der2.template block<3,1>(i*stride,k)
//                            der2(i*stride  , k) * der2(j*stride  , k) +  // d^2u N_i * d^2u N_j
//                            der2(i*stride+1, k) * der2(j*stride+1, k) +  // d^2v N_i * d^2v N_j
//                            2 * der2(i*stride+2, k) * der2(j*stride+2, k)// dudv N_i * dudv N_j
//                            );
                }
        }

        for (index_t i=0; i!=numActive; ++i)
        {
            const int ii = actives(i,0);
            for (index_t j=0; j!=numActive; ++j)
                A_mat( ii, actives(j,0) ) += localA(i,j);
        }
    }
}

template<class T>
void gsFitting<T>::computeErrors()
{
    m_pointErrors.clear();

    gsMatrix<T> val_i;
    //m_result->eval_into(m_param_values.col(0), val_i);
    m_result->eval_into(m_param_values, val_i);
    m_pointErrors.push_back( (m_points.row(0) - val_i.col(0).transpose()).norm() );
    m_max_error = m_min_error = m_pointErrors.back();

    for (index_t i = 1; i < m_points.rows(); i++)
    {
        //m_result->eval_into(m_param_values.col(i), val_i);

        const T err = (m_points.row(i) - val_i.col(i).transpose()).norm() ;

        m_pointErrors.push_back(err);

        if ( err > m_max_error ) m_max_error = err;
        if ( err < m_min_error ) m_min_error = err;
    }
}

template<class T>
void gsFitting<T>::computeMaxNormErrors()
{
    m_pointErrors.clear();

    gsMatrix<T> values;
    m_result->eval_into(m_param_values, values);

    for (index_t i = 0; i != m_points.rows(); i++)
    {
        const T err = (m_points.row(i) - values.col(i).transpose()).cwiseAbs().maxCoeff();

        m_pointErrors.push_back(err);

        if ( i == 0 || m_max_error < err ) m_max_error = err;
        if ( i == 0 || err < m_min_error ) m_min_error = err;
    }
}

template<class T>
void gsFitting<T>::computeApproxError(T& error, int type) const
{
    gsMatrix<T> results;
    m_result->eval_into(m_param_values, results);
    error = 0;

    //computing the approximation error = sum_i ||x(u_i)-p_i||^2

    for (index_t i = 0; i != m_points.rows(); ++i)
    {
        const T err = (m_points.row(i) - results.col(i).transpose()).squaredNorm();

        switch (type) {
        case 0:
            error += err;
            break;
        case 1:
            error += sqrt(err);
            break;
        default:
            gsWarn << "Unknown type in computeApproxError(error, type)...\n";
            break;
        }
    }
}

template<class T>
void gsFitting<T>::get_Error(std::vector<T>& errors, int type) const
{
    errors.clear();
    gsMatrix<T> results;
    m_result->eval_into(m_param_values,results);
    results.transposeInPlace();

    for (index_t row = 0; row != m_points.rows(); row++)
    {
        T err = 0;
        for (index_t col = 0; col != m_points.cols(); col++)
        {
            err += math::pow(m_points(row, col) - results(row, col), 2);
        }

        switch (type)
        {
        case 0:
            errors.push_back(err);
            break;
        case 1:
            errors.push_back(sqrt(err));
            break;
        default:
            gsWarn << "Unknown type in get_Error(errors, type)...\n";
            break;
        }
    }
}

} // namespace gismo
<|endoftext|>


<commit_before>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2016 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  glm/test/gtx_utilities.cpp                                                      */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// includes, system

#include <array>                 // std::array<>
#include <glm/gtc/constants.hpp> // glm::pi
#include <glm/gtc/random.hpp>    // glm::*Rand
#include <glm/gtc/vec1.hpp>      // glm::vec1
#include <glm/gtx/io.hpp>        // glm::operator<<
#include <glm/gtx/transform.hpp> // glm::rotate, glm::scale, glm::translate>

// includes, project

#include <glm/gtx/utilities.hpp>

#define HUGH_USE_TRACE
#undef HUGH_USE_TRACE
#include <hugh/support/trace.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  // variables, internal

  // functions, internal

} // namespace {

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_op_literal_deg)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "glm::pi<double>():" << glm::pi<double>()
                     << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(glm::pi<double>() == 180.0_deg);

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "glm::pi<double>():" << glm::pi<double>()
                     << " =?= 180.0_deg:"    << 180.0_deg);
#endif
}

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_op_literal_rad)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "glm::pi<double>():" << glm::pi<double>()
                     << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(180.0_deg == glm::pi<double>());

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "180.0_deg:"              << 180.0_deg
                     << " =?= glm::pi<double>():" << glm::pi<double>());
#endif
}

using rev_types = boost::mpl::list<glm::vec1,  glm::vec2,  glm::vec3,  glm::vec4,
                                   glm::dvec1, glm::dvec2, glm::dvec3, glm::dvec4>;

BOOST_AUTO_TEST_CASE_TEMPLATE(test_hugh_glm_gtx_utilities_rev, T, rev_types)
{
  BOOST_CHECK(glm::rev(T(glm::two_pi<typename T::value_type>())) == T(0.0));
}

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_sgn)
{
  BOOST_CHECK(glm::sgn(-1) < 0);
  BOOST_CHECK(glm::sgn(+1) > 0);
}

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_convert_transform)
{
  using matrix_pair = std::pair<glm::mat4 const, glm::mat4 const>;

#if defined(_MSC_VER) && (_MSC_VER <= 1900)
  glm::vec3::value_type const angle(float(45 _deg));
#else
  glm::vec3::value_type const angle(float(45_deg));
#endif

  glm::mat4 const ir(glm::rotate   (angle, glm::vec3(glm::gaussRand(  0.0, 20.0),
                                                     glm::gaussRand(  0.0, 20.0),
                                                     glm::gaussRand(  0.0, 20.0))));
  glm::mat4 const is(glm::scale    (       glm::vec3(glm::linearRand( 0.1, 10.0),
                                                     glm::linearRand( 0.1, 10.0),
                                                     glm::linearRand( 0.1, 10.0))));
  glm::mat4 const it(glm::translate(       glm::vec3(glm::linearRand(-1.0,  1.0),
                                                     glm::linearRand(-1.0,  1.0),
                                                     glm::linearRand(-1.0,  1.0))));

  std::array<std::pair<glm::mat4, std::string>, 7> const input_xform_list = {
    {
      std::make_pair(glm::mat4(),  "Identity"),
      std::make_pair(ir * is * it, "RST"),
      std::make_pair(ir * it * is, "RTS"),
      std::make_pair(is * ir * it, "SRT"),
      std::make_pair(is * it * ir, "STR"),
      std::make_pair(it * ir * is, "TRS"),
      std::make_pair(it * is * ir, "TSR"),
    }
  };

  std::array<std::pair<glm::decompose::order, std::string>, 6> const decompose_order_list = {
    {
      std::make_pair(glm::decompose::rst, "RST"),
      std::make_pair(glm::decompose::rts, "RTS"),
      std::make_pair(glm::decompose::srt, "SRT"),
      std::make_pair(glm::decompose::str, "STR"),
      std::make_pair(glm::decompose::trs, "TRS"),
      std::make_pair(glm::decompose::tsr, "TSR"),
    }
  };

  for (auto i : input_xform_list) {
    for (auto e : decompose_order_list) {
      glm::mat4 r, s, t;

      glm::mat4 const   x(glm::convert::transform(i.first, e.first, r, s, t));
      matrix_pair const p(std::make_pair(i.first, x));

      BOOST_TEST_MESSAGE(glm::io::precision(7) << glm::io::width(1 + 1 + 1 + 7)
                         << i.second << ':' << std::string(47 - i.second.length(), ' ')
                         << e.second << ':' << p << '\n');

      static float const epsilon(177 * std::numeric_limits<float>::epsilon());

      for (unsigned i(0); i < 4; ++i) {
        for (unsigned j(0); j < 4; ++j) {
          BOOST_CHECK(std::abs(p.first[i][j] - p.second[i][j]) < epsilon);
        }
      }
    }
  }
}
<commit_msg>fixed: MSVC 19.0 warning<commit_after>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2016 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  glm/test/gtx_utilities.cpp                                                      */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// includes, system

#include <array>                 // std::array<>
#include <glm/gtc/constants.hpp> // glm::pi
#include <glm/gtc/random.hpp>    // glm::*Rand
#include <glm/gtc/vec1.hpp>      // glm::vec1
#include <glm/gtx/io.hpp>        // glm::operator<<
#include <glm/gtx/transform.hpp> // glm::rotate, glm::scale, glm::translate>

// includes, project

#include <glm/gtx/utilities.hpp>

#define HUGH_USE_TRACE
#undef HUGH_USE_TRACE
#include <hugh/support/trace.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  // variables, internal

  // functions, internal

} // namespace {

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_op_literal_deg)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "glm::pi<double>():" << glm::pi<double>()
                     << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(glm::pi<double>() == 180.0_deg);

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "glm::pi<double>():" << glm::pi<double>()
                     << " =?= 180.0_deg:"    << 180.0_deg);
#endif
}

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_op_literal_rad)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "glm::pi<double>():" << glm::pi<double>()
                     << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(180.0_deg == glm::pi<double>());

  BOOST_TEST_MESSAGE(std::setprecision(12)
                     << "180.0_deg:"              << 180.0_deg
                     << " =?= glm::pi<double>():" << glm::pi<double>());
#endif
}

using rev_types = boost::mpl::list<glm::vec1,  glm::vec2,  glm::vec3,  glm::vec4,
                                   glm::dvec1, glm::dvec2, glm::dvec3, glm::dvec4>;

BOOST_AUTO_TEST_CASE_TEMPLATE(test_hugh_glm_gtx_utilities_rev, T, rev_types)
{
  BOOST_CHECK(glm::rev(T(glm::two_pi<typename T::value_type>())) == T(0.0));
}

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_sgn)
{
  BOOST_CHECK(glm::sgn(-1) < 0);
  BOOST_CHECK(glm::sgn(+1) > 0);
}

BOOST_AUTO_TEST_CASE(test_hugh_glm_gtx_utilities_convert_transform)
{
  using matrix_pair = std::pair<glm::mat4 const, glm::mat4 const>;

#if defined(_MSC_VER) && (_MSC_VER <= 1900)
  glm::vec3::value_type const angle(float(45 _deg));
#else
  glm::vec3::value_type const angle(float(45_deg));
#endif

  glm::mat4 const ir(glm::rotate   (angle, glm::vec3(glm::gaussRand(  0.0, 20.0),
                                                     glm::gaussRand(  0.0, 20.0),
                                                     glm::gaussRand(  0.0, 20.0))));
  glm::mat4 const is(glm::scale    (       glm::vec3(glm::linearRand( 0.1, 10.0),
                                                     glm::linearRand( 0.1, 10.0),
                                                     glm::linearRand( 0.1, 10.0))));
  glm::mat4 const it(glm::translate(       glm::vec3(glm::linearRand(-1.0,  1.0),
                                                     glm::linearRand(-1.0,  1.0),
                                                     glm::linearRand(-1.0,  1.0))));

  std::array<std::pair<glm::mat4, std::string>, 7> const input_xform_list = {
    {
      std::make_pair(glm::mat4(),  "Identity"),
      std::make_pair(ir * is * it, "RST"),
      std::make_pair(ir * it * is, "RTS"),
      std::make_pair(is * ir * it, "SRT"),
      std::make_pair(is * it * ir, "STR"),
      std::make_pair(it * ir * is, "TRS"),
      std::make_pair(it * is * ir, "TSR"),
    }
  };

  std::array<std::pair<glm::decompose::order, std::string>, 6> const decompose_order_list = {
    {
      std::make_pair(glm::decompose::rst, "RST"),
      std::make_pair(glm::decompose::rts, "RTS"),
      std::make_pair(glm::decompose::srt, "SRT"),
      std::make_pair(glm::decompose::str, "STR"),
      std::make_pair(glm::decompose::trs, "TRS"),
      std::make_pair(glm::decompose::tsr, "TSR"),
    }
  };

  for (auto ix : input_xform_list) {
    for (auto e : decompose_order_list) {
      glm::mat4 r, s, t;

      glm::mat4 const   x(glm::convert::transform(ix.first, e.first, r, s, t));
      matrix_pair const p(std::make_pair(ix.first, x));

      BOOST_TEST_MESSAGE(glm::io::precision(7) << glm::io::width(1 + 1 + 1 + 7)
                         << ix.second << ':' << std::string(47 - ix.second.length(), ' ')
                         << e.second << ':' << p << '\n');

      static float const epsilon(177 * std::numeric_limits<float>::epsilon());

      for (unsigned i(0); i < 4; ++i) {
        for (unsigned j(0); j < 4; ++j) {
          BOOST_CHECK(std::abs(p.first[i][j] - p.second[i][j]) < epsilon);
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>#include "GUI.hpp"

#include <string>
#include <vector>

#include <QMessageBox>
#include <QAction>
#include <QCloseEvent>
#include <QFontDatabase>
#include "Game.hpp"

#include "NewGameDialog.hpp"
#include "VirtualView.hpp"
#include "AugmentedView.hpp"

namespace Go_GUI {

GUI::GUI(QWidget *parent) : QMainWindow(parent), go_game(nullptr)
{
    ui_main.setupUi(this);

    texture_path = "res/textures/";
    QApplication::setWindowIcon(QIcon(QPixmap(texture_path + "augmented_logo_transparent_icon.png")));
    augmented_logo = QImage(texture_path + "augmented_logo.png");

    virtual_view = new VirtualView(this);
    augmented_view = new AugmentedView(this);

    switchbutton_icon = QIcon(texture_path + "Arrow_SwitchButton.png");
    switchbuttonpressed_icon = QIcon(texture_path + "Arrow_SwitchButton_pressed.png");

    whitebasket_pixmap = QPixmap(texture_path + "white_basket.png");
    blackbasket_pixmap = QPixmap(texture_path + "black_basket.png");
    closedbasket_pixmap = QPixmap(texture_path + "Closed_basket.png");
    gotable_pixmap = QPixmap(texture_path + "go_table.png");
    if (blackbasket_pixmap.isNull() || whitebasket_pixmap.isNull() || closedbasket_pixmap.isNull() || gotable_pixmap.isNull())
        QMessageBox::critical(this, "GUI element not found", QString("White and/or black basket textures not found!\n searched relative to exe in: " + texture_path));

    // loading font
    QFontDatabase fontDatabase;
    QString font_path = "res/fonts/SHOJUMARU-REGULAR.TTF";
    if (fontDatabase.addApplicationFont(font_path) == -1)
        QMessageBox::critical(this, "Font not found", QString("Shojumaru font was not found!\n searched relative to exe in: " + font_path));

    // connections
    connect(ui_main.open_action,		&QAction::triggered,	this, &GUI::slot_MenuOpen);
    connect(ui_main.save_action,		&QAction::triggered,	this, &GUI::slot_MenuSave);
    connect(ui_main.exit_action,		&QAction::triggered,	this, &QWidget::close);
    connect(ui_main.info_action,		&QAction::triggered,	this, &GUI::slot_MenuInfo);
    connect(ui_main.automatic_action,   &QAction::triggered,	this, &GUI::slot_BoardDetectionAutomatically);
    connect(ui_main.manually_action,	&QAction::triggered,	this, &GUI::slot_BoardDetectionManually);
    connect(ui_main.virtual_game_mode_action,	&QAction::triggered, this, &GUI::slot_ToggleVirtualGameMode);
    connect(this,	&GUI::signal_setVirtualGameMode, this->virtual_view, &VirtualView::slot_setVirtualGameMode);
    connect(ui_main.viewswitch_button,	&QPushButton::pressed,	this, &GUI::slot_ViewSwitch);
    connect(ui_main.viewswitch_button,	&QPushButton::released,	this, &GUI::slot_ViewSwitch_released);
    connect(ui_main.newgame_button,	    &QPushButton::clicked,	this, &GUI::slot_ButtonNewGame);
    connect(ui_main.pass_button,	    &QPushButton::clicked,	this, &GUI::slot_ButtonPass);
    connect(ui_main.resign_button,	    &QPushButton::clicked,	this, &GUI::slot_ButtonResign);
    connect(this->virtual_view,	        &VirtualView::signal_virtualViewplayMove,	this, &GUI::slot_passOnVirtualViewPlayMove);

    // setting initial values
    this->init();
}

void GUI::init(){
    this->setWindowTitle("Augmented Go");
    this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

    // Attaching augmented view to big container
    augmented_view->setParent(ui_main.big_container);
    augmented_view->rescaleImage(ui_main.big_container->size());
    ui_main.big_container->setToolTip("augmented view");
    augmented_view->show();

    // Attaching virtual view to small container
    virtual_view->setParent(ui_main.small_container);
    ui_main.small_container->setToolTip("virtual view");
    virtual_view->show();

    ui_main.white_basket->setPixmap(closedbasket_pixmap);
    ui_main.black_basket->setPixmap(closedbasket_pixmap);
    ui_main.go_table_label->setPixmap(gotable_pixmap);

    ui_main.viewswitch_button->setIcon(switchbutton_icon);

    ui_main.capturedwhite_label->setText(QString());
    ui_main.capturedblack_label->setText(QString());

    emit signal_setVirtualGameMode(ui_main.virtual_game_mode_action->isChecked());
}

void GUI::setPlayerLabels(QString blackplayer_name, QString whiteplayer_name){
    ui_main.blackplayer_label->setText(blackplayer_name);
    ui_main.whiteplayer_label->setText(whiteplayer_name);
}

//////////
//Private Slots
//////////

void GUI::slot_ButtonNewGame(){
    NewGameDialog* newgame = new NewGameDialog(this);
    newgame->exec();
}

void GUI::slot_ButtonResign(){
    if (QMessageBox::question(this, "Resign", "Do you really want to admit defeat?") == QMessageBox::Yes)
        emit signal_resign();
}

void GUI::slot_ButtonPass(){
    if (QMessageBox::question(this, "Pass", "Do you really want to pass?") == QMessageBox::Yes)
        emit signal_pass();
}

void GUI::slot_ViewSwitch(){
    ui_main.viewswitch_button->setIcon(this->switchbuttonpressed_icon);
    auto differences = go_game->getDifferences();

    if (ui_main.big_container->toolTip() == "virtual view"){

        // switching augmented view to big container
        augmented_view->setParent(ui_main.big_container);
        augmented_view->rescaleImage(ui_main.big_container->size());
        ui_main.big_container->setToolTip("augmented view");
        augmented_view->show();		// when changing parent, it gets invisible -> show again! -.- !!

        // new style
        virtual_view->setParent(ui_main.small_container);
        virtual_view->createAndSetScene(ui_main.small_container->size(), differences, &(go_game->getBoard()));
        ui_main.small_container->setToolTip("virtual view");
        virtual_view->show();

    }
    else if (ui_main.big_container->toolTip() == "augmented view"){
        // switching augmented view to small container
        augmented_view->setParent(ui_main.small_container);
        augmented_view->rescaleImage(ui_main.small_container->size());
        ui_main.small_container->setToolTip("augmented view");
        augmented_view->show();		// when changing parent, it gets invisible -> show again! -.- !!

        virtual_view->setParent(ui_main.big_container);
        virtual_view->createAndSetScene(ui_main.big_container->size(), differences, &(go_game->getBoard()));
        ui_main.big_container->setToolTip("virtual view");
        virtual_view->show();
    }
}

void GUI::slot_ViewSwitch_released(){
    ui_main.viewswitch_button->setIcon(this->switchbutton_icon);
}

void GUI::slot_MenuOpen(){
    QString selfilter = tr("SGF (*.sgf)");
    QString fileName = QFileDialog::getOpenFileName(
        this,
        "open sgf-file",
        NULL,
        tr("SGF (*.sgf)" ),
        &selfilter
    );

    if (!fileName.isNull()){
        // TODO ask if user wants to save the current game!
        emit signal_openGame(fileName);
    }
}

void GUI::slot_MenuSave(){
    QString selfilter = tr("SGF (*.sgf)");
    QString fileName = QFileDialog::getSaveFileName(
        this,
        "save sgf-file",
        NULL,
        tr("SGF (*.sgf)" ),
        &selfilter
    );

    if (!fileName.isNull())
        emit signal_saveGame(fileName, ui_main.blackplayer_label->text(), ui_main.whiteplayer_label->text(), this->game_name);
}

void GUI::slot_MenuInfo(){
    // Appliction Info
    std::string output = "Augmented Go - Interactive Game of Go as Augmented Reality Application\n\n\n";

    // Build date and time
    output += "This build of Augmented Go was compiled at " __DATE__ ", " __TIME__ ".\n";

    // Copyright
    std::string year = __DATE__;
    year = year.substr(year.find_last_of(" "));	// deleting day and month
    output += "Copyright " + year + "\n";

    // Licence
    output += "\nThis software is released under the \"MIT License\".\n"
        "See the file LICENSE for full license and copying terms.\n";

    // Final InfoBox
    QMessageBox::about(this, "Info", output.c_str());
}

void GUI::slot_BoardDetectionManually() {
    emit signal_boardDetectionManually();
}

void GUI::slot_BoardDetectionAutomatically() {
    emit signal_boardDetectionAutomatically();
}

void GUI::slot_ToggleVirtualGameMode() {
    // if in augmented mode -> switch to virtual
    if (ui_main.virtual_game_mode_action->isChecked()){
        this->setWindowTitle("Augmented Go - Virtual Mode");

        // change virtual view to big container
        if (ui_main.big_container->toolTip() != "virtual view")
            this->slot_ViewSwitch();

        emit signal_setVirtualGameMode(true);
        slot_newImage(augmented_logo);
    }

    // if in virtual mode -> switch to augmented
    else{
        this->setWindowTitle("Augmented Go");

        // change augmented view to big container
        if (ui_main.big_container->toolTip() != "augmented view")
            this->slot_ViewSwitch();
        augmented_view->setStyleSheet("background-color: black");

        emit signal_setVirtualGameMode(false);
    }
}

void GUI::slot_passOnVirtualViewPlayMove(const int x, const int y){
    emit this->signal_playMove(x, y);
}

//////////
//Public Slots
//////////

void GUI::slot_newImage(QImage image) {
        printf(">>> New Image arrived! '%d x %d' -- Format: %d <<<\n", image.width(), image.height(), image.format());
        augmented_view->setImage(image);
        augmented_view->rescaleImage(augmented_view->parentWidget()->size());
    }

void GUI::slot_newGameData(const GoBackend::Game* game) {
    // update internal pointer if the board has been changed
    if (go_game != game)
        go_game = game;

    auto& board = go_game->getBoard();
    auto differences = go_game->getDifferences();

    auto current_player = board.ToPlay();

    // Updating basket pictures
    switch (current_player) {
    case SG_WHITE:
        ui_main.white_basket->setPixmap(whitebasket_pixmap);
        ui_main.black_basket->setPixmap(closedbasket_pixmap);
        break;
    case SG_BLACK:
        ui_main.white_basket->setPixmap(closedbasket_pixmap);
        ui_main.black_basket->setPixmap(blackbasket_pixmap);
        break;
    default:
        assert(false);
        break;
    }

    // Updating Game-Settings
    ui_main.movenumber_label->setText(QString::number(board.MoveNumber()));
    ui_main.kominumber_label->setText(QString::number(board.Rules().Komi().ToFloat()));
    ui_main.handicapnumber_label->setText(QString::number(board.Rules().Handicap()));
    ui_main.capturedwhite_label->setText(QString::number(board.NumPrisoners(SG_WHITE)));
    ui_main.capturedblack_label->setText(QString::number(board.NumPrisoners(SG_BLACK)));

    // refresh virtual view
    if (ui_main.big_container->toolTip() == "virtual view")
        virtual_view->createAndSetScene(ui_main.big_container->size(), differences, &board);

    else if (ui_main.big_container->toolTip() == "augmented view")
        virtual_view->createAndSetScene(ui_main.small_container->size(), differences, &board);

    printf(">>> New Game data! <<<\n");
}

void GUI::slot_showFinishedGameResults(QString result){
    QMessageBox::information(this, "Game results", result);
    auto answer = QMessageBox::question(this, "New Game?", "Do you want to start a new game?");
    if (answer == QMessageBox::Yes)
        this->slot_ButtonNewGame();
}

void GUI::slot_setupNewGame(QString game_name, QString blackplayer_name, QString whiteplayer_name, float komi){

    // emit to backend that gui wants to set up a new game!
    auto rules = GoRules(0, GoKomi(komi), true, true);
    emit signal_newGame(rules);

    // Setting up new names for players
    ui_main.gamename_label->setText(game_name);
    ui_main.blackplayer_label->setText(blackplayer_name);
    ui_main.whiteplayer_label->setText(whiteplayer_name);
    ui_main.kominumber_label->setText(QString::number(komi));
    ui_main.handicapnumber_label->setText(QString::number(0));
}

///////////
//Events
///////////

void GUI::closeEvent(QCloseEvent *event){

    // If at least one move was was done
    // TODO If game loaded -> move number greater than start number!
    int answer = 0;

    // if at least one move was made -> ask if user wants to save
    bool saveable = ui_main.movenumber_label->text().toInt() > 0;

    if (saveable)
        answer = QMessageBox::question(this, "Save?", "Do you want to save before quitting?", "Save", "Don't Save", "Cancel");
    else
        answer = QMessageBox::question(this, "Quit?", "Do you really want to quit?", "Quit", "Cancel");

    if(answer == 0 && saveable){
        this->slot_MenuSave();
        emit stop_backend_thread();
        event->accept();
    }
    else if((answer == 1 && saveable)
        || (answer == 0 && !saveable)){
        emit stop_backend_thread();
        event->accept();
    }
    else
        event->ignore();
}

} // namespace Go_GUI<commit_msg>refs #115 - Quick fix for the crash if go_game is null<commit_after>#include "GUI.hpp"

#include <string>
#include <vector>

#include <QMessageBox>
#include <QAction>
#include <QCloseEvent>
#include <QFontDatabase>
#include "Game.hpp"

#include "NewGameDialog.hpp"
#include "VirtualView.hpp"
#include "AugmentedView.hpp"

namespace Go_GUI {

GUI::GUI(QWidget *parent) : QMainWindow(parent), go_game(nullptr)
{
    ui_main.setupUi(this);

    texture_path = "res/textures/";
    QApplication::setWindowIcon(QIcon(QPixmap(texture_path + "augmented_logo_transparent_icon.png")));
    augmented_logo = QImage(texture_path + "augmented_logo.png");

    virtual_view = new VirtualView(this);
    augmented_view = new AugmentedView(this);

    switchbutton_icon = QIcon(texture_path + "Arrow_SwitchButton.png");
    switchbuttonpressed_icon = QIcon(texture_path + "Arrow_SwitchButton_pressed.png");

    whitebasket_pixmap = QPixmap(texture_path + "white_basket.png");
    blackbasket_pixmap = QPixmap(texture_path + "black_basket.png");
    closedbasket_pixmap = QPixmap(texture_path + "Closed_basket.png");
    gotable_pixmap = QPixmap(texture_path + "go_table.png");
    if (blackbasket_pixmap.isNull() || whitebasket_pixmap.isNull() || closedbasket_pixmap.isNull() || gotable_pixmap.isNull())
        QMessageBox::critical(this, "GUI element not found", QString("White and/or black basket textures not found!\n searched relative to exe in: " + texture_path));

    // loading font
    QFontDatabase fontDatabase;
    QString font_path = "res/fonts/SHOJUMARU-REGULAR.TTF";
    if (fontDatabase.addApplicationFont(font_path) == -1)
        QMessageBox::critical(this, "Font not found", QString("Shojumaru font was not found!\n searched relative to exe in: " + font_path));

    // connections
    connect(ui_main.open_action,		&QAction::triggered,	this, &GUI::slot_MenuOpen);
    connect(ui_main.save_action,		&QAction::triggered,	this, &GUI::slot_MenuSave);
    connect(ui_main.exit_action,		&QAction::triggered,	this, &QWidget::close);
    connect(ui_main.info_action,		&QAction::triggered,	this, &GUI::slot_MenuInfo);
    connect(ui_main.automatic_action,   &QAction::triggered,	this, &GUI::slot_BoardDetectionAutomatically);
    connect(ui_main.manually_action,	&QAction::triggered,	this, &GUI::slot_BoardDetectionManually);
    connect(ui_main.virtual_game_mode_action,	&QAction::triggered, this, &GUI::slot_ToggleVirtualGameMode);
    connect(this,	&GUI::signal_setVirtualGameMode, this->virtual_view, &VirtualView::slot_setVirtualGameMode);
    connect(ui_main.viewswitch_button,	&QPushButton::pressed,	this, &GUI::slot_ViewSwitch);
    connect(ui_main.viewswitch_button,	&QPushButton::released,	this, &GUI::slot_ViewSwitch_released);
    connect(ui_main.newgame_button,	    &QPushButton::clicked,	this, &GUI::slot_ButtonNewGame);
    connect(ui_main.pass_button,	    &QPushButton::clicked,	this, &GUI::slot_ButtonPass);
    connect(ui_main.resign_button,	    &QPushButton::clicked,	this, &GUI::slot_ButtonResign);
    connect(this->virtual_view,	        &VirtualView::signal_virtualViewplayMove,	this, &GUI::slot_passOnVirtualViewPlayMove);

    // setting initial values
    this->init();
}

void GUI::init(){
    this->setWindowTitle("Augmented Go");
    this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

    // Attaching augmented view to big container
    augmented_view->setParent(ui_main.big_container);
    augmented_view->rescaleImage(ui_main.big_container->size());
    ui_main.big_container->setToolTip("augmented view");
    augmented_view->show();

    // Attaching virtual view to small container
    virtual_view->setParent(ui_main.small_container);
    ui_main.small_container->setToolTip("virtual view");
    virtual_view->show();

    ui_main.white_basket->setPixmap(closedbasket_pixmap);
    ui_main.black_basket->setPixmap(closedbasket_pixmap);
    ui_main.go_table_label->setPixmap(gotable_pixmap);

    ui_main.viewswitch_button->setIcon(switchbutton_icon);

    ui_main.capturedwhite_label->setText(QString());
    ui_main.capturedblack_label->setText(QString());

    emit signal_setVirtualGameMode(ui_main.virtual_game_mode_action->isChecked());
}

void GUI::setPlayerLabels(QString blackplayer_name, QString whiteplayer_name){
    ui_main.blackplayer_label->setText(blackplayer_name);
    ui_main.whiteplayer_label->setText(whiteplayer_name);
}

//////////
//Private Slots
//////////

void GUI::slot_ButtonNewGame(){
    NewGameDialog* newgame = new NewGameDialog(this);
    newgame->exec();
}

void GUI::slot_ButtonResign(){
    if (QMessageBox::question(this, "Resign", "Do you really want to admit defeat?") == QMessageBox::Yes)
        emit signal_resign();
}

void GUI::slot_ButtonPass(){
    if (QMessageBox::question(this, "Pass", "Do you really want to pass?") == QMessageBox::Yes)
        emit signal_pass();
}

void GUI::slot_ViewSwitch(){

    if (go_game == nullptr)
        return;

    ui_main.viewswitch_button->setIcon(this->switchbuttonpressed_icon);
    auto differences = go_game->getDifferences();

    if (ui_main.big_container->toolTip() == "virtual view"){

        // switching augmented view to big container
        augmented_view->setParent(ui_main.big_container);
        augmented_view->rescaleImage(ui_main.big_container->size());
        ui_main.big_container->setToolTip("augmented view");
        augmented_view->show();		// when changing parent, it gets invisible -> show again! -.- !!

        // new style
        virtual_view->setParent(ui_main.small_container);
            virtual_view->createAndSetScene(ui_main.small_container->size(), differences, &(go_game->getBoard()));
        ui_main.small_container->setToolTip("virtual view");
        virtual_view->show();

    }
    else if (ui_main.big_container->toolTip() == "augmented view"){
        // switching augmented view to small container
        augmented_view->setParent(ui_main.small_container);
        augmented_view->rescaleImage(ui_main.small_container->size());
        ui_main.small_container->setToolTip("augmented view");
        augmented_view->show();		// when changing parent, it gets invisible -> show again! -.- !!

        virtual_view->setParent(ui_main.big_container);
        virtual_view->createAndSetScene(ui_main.big_container->size(), differences, &(go_game->getBoard()));
        ui_main.big_container->setToolTip("virtual view");
        virtual_view->show();
    }
}

void GUI::slot_ViewSwitch_released(){
    ui_main.viewswitch_button->setIcon(this->switchbutton_icon);
}

void GUI::slot_MenuOpen(){
    QString selfilter = tr("SGF (*.sgf)");
    QString fileName = QFileDialog::getOpenFileName(
        this,
        "open sgf-file",
        NULL,
        tr("SGF (*.sgf)" ),
        &selfilter
    );

    if (!fileName.isNull()){
        // TODO ask if user wants to save the current game!
        emit signal_openGame(fileName);
    }
}

void GUI::slot_MenuSave(){
    QString selfilter = tr("SGF (*.sgf)");
    QString fileName = QFileDialog::getSaveFileName(
        this,
        "save sgf-file",
        NULL,
        tr("SGF (*.sgf)" ),
        &selfilter
    );

    if (!fileName.isNull())
        emit signal_saveGame(fileName, ui_main.blackplayer_label->text(), ui_main.whiteplayer_label->text(), this->game_name);
}

void GUI::slot_MenuInfo(){
    // Appliction Info
    std::string output = "Augmented Go - Interactive Game of Go as Augmented Reality Application\n\n\n";

    // Build date and time
    output += "This build of Augmented Go was compiled at " __DATE__ ", " __TIME__ ".\n";

    // Copyright
    std::string year = __DATE__;
    year = year.substr(year.find_last_of(" "));	// deleting day and month
    output += "Copyright " + year + "\n";

    // Licence
    output += "\nThis software is released under the \"MIT License\".\n"
        "See the file LICENSE for full license and copying terms.\n";

    // Final InfoBox
    QMessageBox::about(this, "Info", output.c_str());
}

void GUI::slot_BoardDetectionManually() {
    emit signal_boardDetectionManually();
}

void GUI::slot_BoardDetectionAutomatically() {
    emit signal_boardDetectionAutomatically();
}

void GUI::slot_ToggleVirtualGameMode() {
    // if in augmented mode -> switch to virtual
    if (ui_main.virtual_game_mode_action->isChecked()){
        this->setWindowTitle("Augmented Go - Virtual Mode");

        // change virtual view to big container
        if (ui_main.big_container->toolTip() != "virtual view")
            this->slot_ViewSwitch();

        emit signal_setVirtualGameMode(true);
        slot_newImage(augmented_logo);
    }

    // if in virtual mode -> switch to augmented
    else{
        this->setWindowTitle("Augmented Go");

        // change augmented view to big container
        if (ui_main.big_container->toolTip() != "augmented view")
            this->slot_ViewSwitch();
        augmented_view->setStyleSheet("background-color: black");

        emit signal_setVirtualGameMode(false);
    }
}

void GUI::slot_passOnVirtualViewPlayMove(const int x, const int y){
    emit this->signal_playMove(x, y);
}

//////////
//Public Slots
//////////

void GUI::slot_newImage(QImage image) {
        printf(">>> New Image arrived! '%d x %d' -- Format: %d <<<\n", image.width(), image.height(), image.format());
        augmented_view->setImage(image);
        augmented_view->rescaleImage(augmented_view->parentWidget()->size());
    }

void GUI::slot_newGameData(const GoBackend::Game* game) {

    // update internal pointer if the board has been changed
    if (go_game != game)
        go_game = game;

    if (go_game == nullptr)
        return;

    auto& board = go_game->getBoard();
    auto differences = go_game->getDifferences();

    auto current_player = board.ToPlay();

    // Updating basket pictures
    switch (current_player) {
    case SG_WHITE:
        ui_main.white_basket->setPixmap(whitebasket_pixmap);
        ui_main.black_basket->setPixmap(closedbasket_pixmap);
        break;
    case SG_BLACK:
        ui_main.white_basket->setPixmap(closedbasket_pixmap);
        ui_main.black_basket->setPixmap(blackbasket_pixmap);
        break;
    default:
        assert(false);
        break;
    }

    // Updating Game-Settings
    ui_main.movenumber_label->setText(QString::number(board.MoveNumber()));
    ui_main.kominumber_label->setText(QString::number(board.Rules().Komi().ToFloat()));
    ui_main.handicapnumber_label->setText(QString::number(board.Rules().Handicap()));
    ui_main.capturedwhite_label->setText(QString::number(board.NumPrisoners(SG_WHITE)));
    ui_main.capturedblack_label->setText(QString::number(board.NumPrisoners(SG_BLACK)));

    // refresh virtual view
    if (ui_main.big_container->toolTip() == "virtual view")
        virtual_view->createAndSetScene(ui_main.big_container->size(), differences, &board);

    else if (ui_main.big_container->toolTip() == "augmented view")
        virtual_view->createAndSetScene(ui_main.small_container->size(), differences, &board);

    printf(">>> New Game data! <<<\n");
}

void GUI::slot_showFinishedGameResults(QString result){
    QMessageBox::information(this, "Game results", result);
    auto answer = QMessageBox::question(this, "New Game?", "Do you want to start a new game?");
    if (answer == QMessageBox::Yes)
        this->slot_ButtonNewGame();
}

void GUI::slot_setupNewGame(QString game_name, QString blackplayer_name, QString whiteplayer_name, float komi){

    // emit to backend that gui wants to set up a new game!
    auto rules = GoRules(0, GoKomi(komi), true, true);
    emit signal_newGame(rules);

    // Setting up new names for players
    ui_main.gamename_label->setText(game_name);
    ui_main.blackplayer_label->setText(blackplayer_name);
    ui_main.whiteplayer_label->setText(whiteplayer_name);
    ui_main.kominumber_label->setText(QString::number(komi));
    ui_main.handicapnumber_label->setText(QString::number(0));
}

///////////
//Events
///////////

void GUI::closeEvent(QCloseEvent *event){

    // If at least one move was was done
    // TODO If game loaded -> move number greater than start number!
    int answer = 0;

    // if at least one move was made -> ask if user wants to save
    bool saveable = ui_main.movenumber_label->text().toInt() > 0;

    if (saveable)
        answer = QMessageBox::question(this, "Save?", "Do you want to save before quitting?", "Save", "Don't Save", "Cancel");
    else
        answer = QMessageBox::question(this, "Quit?", "Do you really want to quit?", "Quit", "Cancel");

    if(answer == 0 && saveable){
        this->slot_MenuSave();
        emit stop_backend_thread();
        event->accept();
    }
    else if((answer == 1 && saveable)
        || (answer == 0 && !saveable)){
        emit stop_backend_thread();
        event->accept();
    }
    else
        event->ignore();
}

} // namespace Go_GUI<|endoftext|>


<commit_before>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2014 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  glm/test/gtx_utilities.cpp                                                      */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// includes, system

#include <array>                 // std::array<>
#include <glm/gtc/constants.hpp> // glm::pi
#include <glm/gtc/random.hpp>    // glm::*Rand
#include <glm/gtx/io.hpp>        // glm::operator<<
#include <glm/gtx/transform.hpp> // glm::rotate, gm::scale, glm::translate>

// includes, project

#include <glm/gtx/utilities.hpp>

#define UKACHULLDCS_USE_TRACE
#undef UKACHULLDCS_USE_TRACE
#include <support/trace.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  // variables, internal

  // functions, internal

} // namespace {

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_utilities_deg2rad)
{
  BOOST_CHECK(glm::pi<double>() == glm::deg2rad(180.0));

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():"   << glm::pi<double>()
                << " =?= deg2rad(180.0):" << glm::deg2rad(180.0));
}

BOOST_AUTO_TEST_CASE(test_utilities_rad2deg)
{
  BOOST_CHECK(180.0 == glm::rad2deg(glm::pi<double>()));

  BOOST_MESSAGE(std::setprecision(12)
                << 180.0 << " =?= rad2deg(glm::pi<double>():"
                << glm::rad2deg(glm::pi<double>()));
}

BOOST_AUTO_TEST_CASE(test_utilities_op_literal_deg)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1700)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():" << glm::pi<double>()
                << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(glm::pi<double>() == 180.0_deg);

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():" << glm::pi<double>()
                << " =?= 180.0_deg:"    << 180.0_deg);
#endif
}

BOOST_AUTO_TEST_CASE(test_utilities_op_literal_rad)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1700)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():" << glm::pi<double>()
                << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(180.0_deg == glm::pi<double>());

  BOOST_MESSAGE(std::setprecision(12)
                << "180.0_deg:"              << 180.0_deg
                << " =?= glm::pi<double>():" << glm::pi<double>());
#endif
}

BOOST_AUTO_TEST_CASE(test_utilities_convert_transform)
{
  typedef std::pair<glm::mat4 const, glm::mat4 const> matrix_pair;

#if defined(_MSC_VER) && (_MSC_VER <= 1700)
  glm::vec3::value_type const angle(45 _deg);
#else
  glm::vec3::value_type const angle(45_deg);
#endif

  glm::mat4 const ir(glm::rotate   (angle, glm::vec3(glm::gaussRand(0.0, 20.0),
                                                     glm::gaussRand(0.0, 20.0),
                                                     glm::gaussRand(0.0, 20.0))));
  glm::mat4 const is(glm::scale    (       glm::vec3(glm::linearRand(0.1, 10.0),
                                                     glm::linearRand(0.1, 10.0),
                                                     glm::linearRand(0.1, 10.0))));
  glm::mat4 const it(glm::translate(       glm::vec3(glm::linearRand(-1.0, 1.0),
                                                     glm::linearRand(-1.0, 1.0),
                                                     glm::linearRand(-1.0, 1.0))));

  std::array<std::pair<glm::mat4, std::string>, 7> const input_xform_list = {
    {
      std::make_pair(glm::mat4(),  "Identity"),
      std::make_pair(ir * is * it, "RST"),
      std::make_pair(ir * it * is, "RTS"),
      std::make_pair(is * ir * it, "SRT"),
      std::make_pair(is * it * ir, "STR"),
      std::make_pair(it * ir * is, "TRS"),
      std::make_pair(it * is * ir, "TSR"),
    }
  };

  std::array<std::pair<glm::decompose::order, std::string>, 6> const decompose_order_list = {
    {
      std::make_pair(glm::decompose::rst, "RST"),
      std::make_pair(glm::decompose::rts, "RTS"),
      std::make_pair(glm::decompose::srt, "SRT"),
      std::make_pair(glm::decompose::str, "STR"),
      std::make_pair(glm::decompose::trs, "TRS"),
      std::make_pair(glm::decompose::tsr, "TSR"),
    }
  };

  for (auto i : input_xform_list) {
    for (auto e : decompose_order_list) {
      glm::mat4 r, s, t;

      glm::mat4 const   x(glm::convert::transform(i.first, e.first, r, s, t));
      matrix_pair const p(std::make_pair(i.first, x));

      BOOST_MESSAGE(i.second << ':' << std::string(43 - i.second.length(), ' ') << e.second << ':'
                    << p << '\n');

      for (unsigned i(0); i < 4; ++i) {
        for (unsigned j(0); j < 4; ++j) {
          BOOST_CHECK(std::abs(p.first[i][j] - p.second[i][j]) < 5E-6);
        }
      }
    }
  }
}
<commit_msg>fixed: epsilon comparison value for decomposition tests<commit_after>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2014 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  glm/test/gtx_utilities.cpp                                                      */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// includes, system

#include <array>                 // std::array<>
#include <glm/gtc/constants.hpp> // glm::pi
#include <glm/gtc/random.hpp>    // glm::*Rand
#include <glm/gtx/io.hpp>        // glm::operator<<
#include <glm/gtx/transform.hpp> // glm::rotate, gm::scale, glm::translate>

// includes, project

#include <glm/gtx/utilities.hpp>

#define UKACHULLDCS_USE_TRACE
#undef UKACHULLDCS_USE_TRACE
#include <support/trace.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  // variables, internal

  // functions, internal

} // namespace {

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_utilities_deg2rad)
{
  BOOST_CHECK(glm::pi<double>() == glm::deg2rad(180.0));

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():"   << glm::pi<double>()
                << " =?= deg2rad(180.0):" << glm::deg2rad(180.0));
}

BOOST_AUTO_TEST_CASE(test_utilities_rad2deg)
{
  BOOST_CHECK(180.0 == glm::rad2deg(glm::pi<double>()));

  BOOST_MESSAGE(std::setprecision(12)
                << 180.0 << " =?= rad2deg(glm::pi<double>():"
                << glm::rad2deg(glm::pi<double>()));
}

BOOST_AUTO_TEST_CASE(test_utilities_op_literal_deg)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1700)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():" << glm::pi<double>()
                << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(glm::pi<double>() == 180.0_deg);

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():" << glm::pi<double>()
                << " =?= 180.0_deg:"    << 180.0_deg);
#endif
}

BOOST_AUTO_TEST_CASE(test_utilities_op_literal_rad)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1700)
  BOOST_CHECK(glm::pi<double>() == 180.0 _deg);

  BOOST_MESSAGE(std::setprecision(12)
                << "glm::pi<double>():" << glm::pi<double>()
                << " =?= 180.0_deg:"    << 180.0 _deg);
#else
  BOOST_CHECK(180.0_deg == glm::pi<double>());

  BOOST_MESSAGE(std::setprecision(12)
                << "180.0_deg:"              << 180.0_deg
                << " =?= glm::pi<double>():" << glm::pi<double>());
#endif
}

BOOST_AUTO_TEST_CASE(test_utilities_convert_transform)
{
  typedef std::pair<glm::mat4 const, glm::mat4 const> matrix_pair;

#if defined(_MSC_VER) && (_MSC_VER <= 1700)
  glm::vec3::value_type const angle(45 _deg);
#else
  glm::vec3::value_type const angle(45_deg);
#endif

  glm::mat4 const ir(glm::rotate   (angle, glm::vec3(glm::gaussRand(  0.0, 20.0),
                                                     glm::gaussRand(  0.0, 20.0),
                                                     glm::gaussRand(  0.0, 20.0))));
  glm::mat4 const is(glm::scale    (       glm::vec3(glm::linearRand( 0.1, 10.0),
                                                     glm::linearRand( 0.1, 10.0),
                                                     glm::linearRand( 0.1, 10.0))));
  glm::mat4 const it(glm::translate(       glm::vec3(glm::linearRand(-1.0,  1.0),
                                                     glm::linearRand(-1.0,  1.0),
                                                     glm::linearRand(-1.0,  1.0))));

  std::array<std::pair<glm::mat4, std::string>, 7> const input_xform_list = {
    {
      std::make_pair(glm::mat4(),  "Identity"),
      std::make_pair(ir * is * it, "RST"),
      std::make_pair(ir * it * is, "RTS"),
      std::make_pair(is * ir * it, "SRT"),
      std::make_pair(is * it * ir, "STR"),
      std::make_pair(it * ir * is, "TRS"),
      std::make_pair(it * is * ir, "TSR"),
    }
  };

  std::array<std::pair<glm::decompose::order, std::string>, 6> const decompose_order_list = {
    {
      std::make_pair(glm::decompose::rst, "RST"),
      std::make_pair(glm::decompose::rts, "RTS"),
      std::make_pair(glm::decompose::srt, "SRT"),
      std::make_pair(glm::decompose::str, "STR"),
      std::make_pair(glm::decompose::trs, "TRS"),
      std::make_pair(glm::decompose::tsr, "TSR"),
    }
  };

  for (auto i : input_xform_list) {
    for (auto e : decompose_order_list) {
      glm::mat4 r, s, t;

      glm::mat4 const   x(glm::convert::transform(i.first, e.first, r, s, t));
      matrix_pair const p(std::make_pair(i.first, x));

      BOOST_MESSAGE(glm::io::precision(7) << glm::io::width(1 + 1 + 1 + 7)
                    << i.second << ':' << std::string(47 - i.second.length(), ' ')
                    << e.second << ':' << p << '\n');

      static float const epsilon(9 * std::numeric_limits<float>::epsilon());

      for (unsigned i(0); i < 4; ++i) {
        for (unsigned j(0); j < 4; ++j) {
          BOOST_CHECK(std::abs(p.first[i][j] - p.second[i][j]) < epsilon);
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>/* Copyright (c) 2014, Falko Schumann <falko.schumann@muspellheim.de>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *  2. 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.
 *
 * 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.
 */

#include "recordnavigation.h"
#include "recordnavigation_p.h"
#include "ui_recordnavigation.h"

#include <QModelIndex>
#include <QStandardItemModel>

using namespace Core;

RecordNavigationPrivate::RecordNavigationPrivate(RecordNavigation *q) :
    q_ptr(q),
    currentIndex(0),
    ui(new Ui::RecordNavigation),
    model(new QStandardItemModel())
{
    ui->setupUi(q);
    ui->currentRow->setFocus();
}

RecordNavigationPrivate::~RecordNavigationPrivate()
{
    delete ui;
}

void RecordNavigationPrivate::update()
{
    ui->currentRow->setText(QString::number(currentIndex + 1));
    ui->rowCountLabel->setText(QString("of %1").arg(model->rowCount()));

    bool currentIsFirst = currentIndex == 0;
    ui->toFirstButton->setEnabled(!currentIsFirst);
    ui->toPreviousButton->setEnabled(!currentIsFirst);
}

void RecordNavigationPrivate::currentRowEdited()
{
    bool ok;
    int newIndex = ui->currentRow->text().toInt(&ok);
    if (ok) q_ptr->setCurrentIndex(newIndex - 1);
}

/*!
 * \brief Ein Widget zum Navigieren innerhalb eines Modells.
 *
 * Man kann einen Datensatz vor- oder zurückspringen oder zum ersten oder letzten Datensatz
 * springen. Der aktuelle Datensatz und die Anzahl der Datensätze werden angezeigt. Man kann direkt
 * zu einem bestimmten Datensatz springen, in dem man den Datensatzindex angibt. Und natürlich kann
 * ein neuer Datensatz angelegt werden.
 *
 * \remarks Die Klasse übernimmt nicht den Besitz des Modells und löscht es auch nicht, wenn sie
 *     zerstört wird.
 * \invariant 0 <= currentIndex() && currentIndex() <= model()->rowCount()
 */
RecordNavigation::RecordNavigation(QWidget *parent) :
    QWidget(parent),
    d_ptr(new RecordNavigationPrivate(this))
{
    connect(d_ptr->ui->toFirstButton, SIGNAL(clicked()), this, SLOT(toFirst()));
    connect(d_ptr->ui->toPreviousButton, SIGNAL(clicked()), this, SLOT(toPrevious()));
    connect(d_ptr->ui->currentRow, SIGNAL(editingFinished()), d_ptr, SLOT(currentRowEdited()));
    connect(d_ptr->ui->toNextButton, SIGNAL(clicked()), this, SLOT(toNext()));
    connect(d_ptr->ui->toLastButton, SIGNAL(clicked()), this, SLOT(toLast()));
    connect(d_ptr->ui->toNewButton, SIGNAL(clicked()), this, SLOT(toNew()));
}

RecordNavigation::~RecordNavigation()
{
    delete d_ptr;
}

QAbstractItemModel *RecordNavigation::model() const
{
    return d_ptr->model;
}

/*!
 * \post model() == model;
 */
void RecordNavigation::setModel(QAbstractItemModel *model)
{
    d_ptr->model = model;
    setCurrentIndex(0);
}

int RecordNavigation::currentIndex() const
{
    return d_ptr->currentIndex;
}

/*!
 * \pre 0 <= index && index <= model()->rowCount()
 * \post currentIndex() == index
 */
void RecordNavigation::setCurrentIndex(int index)
{
    d_ptr->currentIndex = index;
    d_ptr->update();
}

/*!
 * \pre 0 <= index.row() && index.row() <= model()->rowCount()
 * \post currentIndex() == index.row()
 */
void RecordNavigation::setCurrentModelIndex(const QModelIndex &index)
{
    setCurrentIndex(index.row());
}

/*!
 * \post currentIndex() == 0
 */
void RecordNavigation::toFirst()
{
    setCurrentIndex(0);
}

/*!
 * \post currentIndex() == model()->rowCount() - 1
 */
void RecordNavigation::toLast()
{
    setCurrentIndex(model()->rowCount() - 1);
}

/*!
 * \pre $index = currentIndex()
 * \pre currentIndex() <= model()->rowCount()
 * \post currentIndex() == $index + 1
 */
void RecordNavigation::toNext()
{
    setCurrentIndex(currentIndex() + 1);
}

/*!
 * \pre $index = currentIndex()
 * \pre currentIndex() > 0
 * \post currentIndex() == $index - 1
 */
void RecordNavigation::toPrevious()
{
    setCurrentIndex(currentIndex() - 1);
}

/**
 * \post currentIndex() == model()->rowCount()
 */
void RecordNavigation::toNew()
{
    setCurrentIndex(model()->rowCount());
}
<commit_msg>Verwendung von Namespaces verbessert.<commit_after>/* Copyright (c) 2014, Falko Schumann <falko.schumann@muspellheim.de>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *  2. 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.
 *
 * 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.
 */

#include "recordnavigation.h"
#include "recordnavigation_p.h"
#include "ui_recordnavigation.h"

#include <QModelIndex>
#include <QStandardItemModel>

namespace Core {

RecordNavigationPrivate::RecordNavigationPrivate(RecordNavigation *q) :
    q_ptr(q),
    currentIndex(0),
    ui(new Ui::RecordNavigation),
    model(new QStandardItemModel())
{
    ui->setupUi(q);
    ui->currentRow->setFocus();
}

RecordNavigationPrivate::~RecordNavigationPrivate()
{
    delete ui;
}

void RecordNavigationPrivate::update()
{
    ui->currentRow->setText(QString::number(currentIndex + 1));
    ui->rowCountLabel->setText(QString("of %1").arg(model->rowCount()));

    bool currentIsFirst = currentIndex == 0;
    ui->toFirstButton->setEnabled(!currentIsFirst);
    ui->toPreviousButton->setEnabled(!currentIsFirst);
}

void RecordNavigationPrivate::currentRowEdited()
{
    bool ok;
    int newIndex = ui->currentRow->text().toInt(&ok);
    if (ok) q_ptr->setCurrentIndex(newIndex - 1);
}

/*!
 * \brief Ein Widget zum Navigieren innerhalb eines Modells.
 *
 * Man kann einen Datensatz vor- oder zurückspringen oder zum ersten oder letzten Datensatz
 * springen. Der aktuelle Datensatz und die Anzahl der Datensätze werden angezeigt. Man kann direkt
 * zu einem bestimmten Datensatz springen, in dem man den Datensatzindex angibt. Und natürlich kann
 * ein neuer Datensatz angelegt werden.
 *
 * \remarks Die Klasse übernimmt nicht den Besitz des Modells und löscht es auch nicht, wenn sie
 *     zerstört wird.
 * \invariant 0 <= currentIndex() && currentIndex() <= model()->rowCount()
 */
RecordNavigation::RecordNavigation(QWidget *parent) :
    QWidget(parent),
    d_ptr(new RecordNavigationPrivate(this))
{
    connect(d_ptr->ui->toFirstButton, SIGNAL(clicked()), this, SLOT(toFirst()));
    connect(d_ptr->ui->toPreviousButton, SIGNAL(clicked()), this, SLOT(toPrevious()));
    connect(d_ptr->ui->currentRow, SIGNAL(editingFinished()), d_ptr, SLOT(currentRowEdited()));
    connect(d_ptr->ui->toNextButton, SIGNAL(clicked()), this, SLOT(toNext()));
    connect(d_ptr->ui->toLastButton, SIGNAL(clicked()), this, SLOT(toLast()));
    connect(d_ptr->ui->toNewButton, SIGNAL(clicked()), this, SLOT(toNew()));
}

RecordNavigation::~RecordNavigation()
{
    delete d_ptr;
}

QAbstractItemModel *RecordNavigation::model() const
{
    return d_ptr->model;
}

/*!
 * \post model() == model;
 */
void RecordNavigation::setModel(QAbstractItemModel *model)
{
    d_ptr->model = model;
    setCurrentIndex(0);
}

int RecordNavigation::currentIndex() const
{
    return d_ptr->currentIndex;
}

/*!
 * \pre 0 <= index && index <= model()->rowCount()
 * \post currentIndex() == index
 */
void RecordNavigation::setCurrentIndex(int index)
{
    d_ptr->currentIndex = index;
    d_ptr->update();
}

/*!
 * \pre 0 <= index.row() && index.row() <= model()->rowCount()
 * \post currentIndex() == index.row()
 */
void RecordNavigation::setCurrentModelIndex(const QModelIndex &index)
{
    setCurrentIndex(index.row());
}

/*!
 * \post currentIndex() == 0
 */
void RecordNavigation::toFirst()
{
    setCurrentIndex(0);
}

/*!
 * \post currentIndex() == model()->rowCount() - 1
 */
void RecordNavigation::toLast()
{
    setCurrentIndex(model()->rowCount() - 1);
}

/*!
 * \pre $index = currentIndex()
 * \pre currentIndex() <= model()->rowCount()
 * \post currentIndex() == $index + 1
 */
void RecordNavigation::toNext()
{
    setCurrentIndex(currentIndex() + 1);
}

/*!
 * \pre $index = currentIndex()
 * \pre currentIndex() > 0
 * \post currentIndex() == $index - 1
 */
void RecordNavigation::toPrevious()
{
    setCurrentIndex(currentIndex() - 1);
}

/**
 * \post currentIndex() == model()->rowCount()
 */
void RecordNavigation::toNew()
{
    setCurrentIndex(model()->rowCount());
}

} // namespace Core
<|endoftext|>