/************************************************************************/
/*                                                                      */
/*    vspline - a set of generic tools for creation and evaluation      */
/*              of uniform b-splines                                    */
/*                                                                      */
/*            Copyright 2016 - 2023 by Kay F. Jahnke                    */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

/// \file channels.cc
///
/// \brief demonstrates the use of 'channel views'
///
/// This example is derived from 'slice.cc', we use the same volume
/// as source data. But instead of producing an image output, we create
/// three separate colour channels of the bspline object and assert that
/// the evaluation of the channel views is identical with the evaluation
/// of the 'mother' spline.
/// For a more involved example using channel views, see ca_correct.cc
///
/// compile with:
/// clang++ -std=c++11 -march=native -o channels -O3 -pthread -DUSE_VC channels.cc -lvigraimpex -lVc
///
/// If you don't have Vc on your system, use
///
/// clang++ -std=c++11 -march=native -o channels -O3 -pthread channels.cc -lvigraimpex
/// note that the assertion at the end fails if the program is compiled
/// with -Ofast - then the result is only near equal.

#include <iostream>

#include <vspline/vspline.h>

#include <vigra/stdimage.hxx>
#include <vigra/imageinfo.hxx>
#include <vigra/impex.hxx>

int main ( int argc , char * argv[] )
{
  // pixel_type is the result type, an RGB float pixel
  typedef vigra::TinyVector < float , 3 > pixel_type ;
  
  // voxel_type is the source data type
  typedef vigra::TinyVector < float , 3 > voxel_type ;
  
  // coordinate_type has a 3D coordinate
  typedef vigra::TinyVector < float , 3 > coordinate_type ;
  
  // warp_type is a 2D array of coordinates
  typedef vigra::MultiArray < 2 , coordinate_type > warp_type ;
  
  // target_type is a 2D array of pixels  
  typedef vigra::MultiArray < 2 , pixel_type > target_type ;
  
  // we want a b-spline with natural boundary conditions
  vigra::TinyVector < vspline::bc_code , 3 > bcv ( vspline::NATURAL ) ;
  
  // create quintic 3D b-spline object containing voxels
  vspline::bspline < voxel_type , 3 >
    space ( vigra::Shape3 ( 10 , 10 , 10 ) , 5 , bcv ) ;

  // here we create the channel views. Since these are merely views
  // to the same data, no data will be copied, and it doesn't matter
  // whether we create these views before or after prefiltering.

  auto red_channel = space.get_channel_view ( 0 ) ;
  auto green_channel = space.get_channel_view ( 1 ) ;
  auto blue_channel = space.get_channel_view ( 2 ) ;

  // fill the b-spline's core with a three-way gradient

  for ( int z = 0 ; z < 10 ; z++ )
  {
    for ( int y = 0 ; y < 10 ; y++ )
    {
      for ( int x = 0 ; x < 10 ; x++ )
      {
        voxel_type & c ( space.core [ vigra::Shape3 ( x , y , z ) ] ) ;
        c[0] = 25.5 * x ;
        c[1] = 25.5 * y ;
        c[2] = 25.5 * z ;
      }
    }
  }
  
  // prefilter the b-spline
  space.prefilter() ;
  
  // now make a warp array with 1920X1080 3D coordinates
  warp_type warp ( vigra::Shape2 ( 1920 , 1080 ) ) ;
  
  // we want the coordinates to follow this scheme:
  // warp(x,y) = (x,1-x,y)
  // scaled appropriately
  
  for ( int y = 0 ; y < 1080 ; y++ )
  {
    for ( int x = 0 ; x < 1920 ; x++ )
    {
      coordinate_type & c ( warp [ vigra::Shape2 ( x , y ) ] ) ;
      c[0] = float ( x ) / 192.0 ;
      c[1] = 10.0 - c[0] ;
      c[2] = float ( y ) / 108.0 ;
    }
  }
  
  // get an evaluator for the b-spline

  typedef vspline::evaluator < coordinate_type , voxel_type > ev_type ;
  ev_type ev ( space ) ;
  
  // the evaluators of the channel views have their own type:
  
  typedef vspline::evaluator < coordinate_type , float > ch_ev_type ;
  
  // we create the three evaluators for the three channel views

  ch_ev_type red_ev ( red_channel ) ;
  ch_ev_type green_ev ( green_channel ) ;
  ch_ev_type blue_ev ( blue_channel ) ;

  // and make sure the evaluation results match

  // TODO: compiling with clang++, the two results are actually equal,
  // but not with g++ - there, the results differ slightly.

  for ( int y = 0 ; y < 1080 ; y++ )
  {
    for ( int x = 0 ; x < 1920 ; x++ )
    {
      coordinate_type & c ( warp [ vigra::Shape2 ( x , y ) ] ) ;
      
      auto diff = fabs ( ev ( c ) [ 0 ] - red_ev ( c ) ) ;
      if ( diff > 1e-4 )
      {
        std::cerr << "red result differs at c " << c << " "
                  << ev ( c ) [ 0 ] << " != " << red_ev ( c )
                  << std::endl ;
      }
      diff = fabs ( ev ( c ) [ 1 ] - green_ev ( c ) ) ;
      if ( diff > 1e-4 )
      {
        std::cerr << "green result differs at c " << c << " "
                  << ev ( c ) [ 1 ] << " != " << green_ev ( c )
                  << std::endl ;
      }
      diff = fabs ( ev ( c ) [ 2 ] - blue_ev ( c ) ) ;
      if ( diff > 1e-4 )
      {
        std::cerr << "blue result differs at c " << c << " "
                  << ev ( c ) [ 2 ] << " != " << blue_ev ( c )
                  << std::endl ;
      }
    }
  }

  std::cout << "success" << std::endl ;
  exit ( 0 ) ;
}
