text: /*
   * This program demonstrates some of the low-level functionality of DIPviewer:
   *  - Creating and using a dip::viewer::Manager window manager.
   *  - Creating multiple dip::viewer::SliceViewer and dip::viewer::ImageViewer windows.
   *  - Updating the image shown in a dip::viewer::ImageViewer window in a background thread.
   */
  
  #include <diplib.h>
  #include <diplib/file_io.h>
  #include <diplib/generation.h>
  #include <diplib/analysis.h>
  #include <diplib/linear.h>
  #include <diplib/math.h>
  
  #ifdef DIP_CONFIG_HAS_GLFW
  #  include <diplib/viewer/glfw.h>
     dip::viewer::GLFWManager manager;
  #else
  #  include <diplib/viewer/glut.h>
     dip::viewer::GLUTManager manager;
  #endif
  #include <diplib/viewer/image.h>
  #include <diplib/viewer/slice.h>
  
  void run( dip::viewer::ImageViewer::Ptr const& viewer ) {
     double sigma = 1., fact = 1.1;
     dip::Image original;
  
     // Get original image from viewer.
     {
        dip::viewer::Viewer::Guard guard( *viewer );
        original = viewer->image();
     }
  
     while( !viewer->destroyed() ) {
        // Do some filtering.
        dip::Image image = dip::Gauss( original, { sigma, sigma } );
        sigma *= fact;
        if( sigma > 20. || sigma < 1. ) {
           fact = 1. / fact;
        }
        image.Convert( dip::DT_UINT8 );
  
        // Update displayed image.
        {
           dip::viewer::Viewer::Guard guard( *viewer );
           viewer->setImage( image );
        }
  
        std::this_thread::sleep_for( std::chrono::microseconds( 10000 ));
     }
  }
  
  int main() {
  
     // Read and display primary image
     dip::Image image3 = dip::ImageReadICS( DIP_EXAMPLES_DIR "/chromo3d.ics" );
     image3.PixelSize().Set( 2, 5 );
     manager.createWindow( dip::viewer::SliceViewer::Create( image3, "chromo3d", 500, 400 ));
  
     // Calculate and display structure tensor
     dip::Image st = dip::StructureTensor( image3 );
     manager.createWindow( dip::viewer::SliceViewer::Create( st, "chromo3d structure tensor", 500, 400 ));
  
     // Generate 2D RGB image
     dip::Image image2 = dip::Image{ dip::UnsignedArray{ 50, 40 }, 3, dip::DT_UINT8 };
     dip::Image tmp = image2[ 0 ];
     dip::FillXCoordinate( tmp, { "corner" } );
     tmp = image2[ 1 ];
     dip::FillYCoordinate( tmp, { "corner" } );
     tmp = image2[ 2 ];
     dip::FillRadiusCoordinate( tmp );
     image2 *= 5;
  
     // Display
     auto iv = dip::viewer::ImageViewer::Create( image2 );
     manager.createWindow( iv );
  
     // Create thread to programmatically alter image
     std::thread thread = std::thread( run, iv );
  
     // Generate 0D RGB image
     dip::Image image0 = dip::Image{ dip::UnsignedArray{}, 3, dip::DT_UINT8 };
     image0[ 0 ] = 64;
     image0[ 1 ] = 128;
     image0[ 2 ] = 192;
     manager.createWindow( dip::viewer::SliceViewer::Create( image0, "0d", 500, 400 ));
  
     // Generate 1D RGB image
     dip::Image image1 = dip::Image{ dip::UnsignedArray{ 160 }, 1, dip::DT_UINT8 };
     dip::FillXCoordinate( image1, { "corner" } );
     image1 = dip::Sin( image1 / 10 );
     manager.createWindow( dip::viewer::SliceViewer::Create( image1, "1d", 500, 400 ));
  
     while( manager.activeWindows() > 0 ) {
        // Only necessary for GLFW
        manager.processEvents();
        std::this_thread::sleep_for( std::chrono::microseconds( 1000 ));
     }
  
     // Clean up
     thread.join();
  }
  