Example #1
0
void wxSFThumbnail::_OnPaint(wxPaintEvent& event)
{
	wxUnusedVar( event );
	
	#if wxVERSION_NUMBER < 2900 && wxUSE_GRAPHICS_CONTEXT
	bool fGCEnabled = wxSFShapeCanvas::IsGCEnabled();
    wxSFScaledDC::EnableGC( false );
    #endif
	
	wxBufferedPaintDC dc(this);
	
	// clear background
	dc.SetBackground( wxBrush(wxColour(150, 150, 150)) );
	dc.Clear();
	
	if( m_pCanvas )
	{
		wxSize szCanvas = m_pCanvas->GetClientSize();
		wxSize szVirtCanvas = m_pCanvas->GetVirtualSize();
		wxSize szCanvasOffset = GetCanvasOffset();
		wxSize szThumb = GetClientSize();
		
		// scale and copy bitmap to DC
		double cx = szVirtCanvas.x, cy = szVirtCanvas.y, tx = szThumb.x, ty = szThumb.y;
		
		if( (tx/ty) > (cx/cy) )	m_nScale = ty/cy;
		else
			m_nScale = tx/cx;

		// draw virtual canvas area
		dc.SetPen(*wxWHITE_PEN);
		dc.SetBrush( wxBrush(wxColour(240, 240, 240)) );
		dc.DrawRectangle(0, 0, double(szVirtCanvas.x)*m_nScale, double(szVirtCanvas.y)*m_nScale);
		
		// draw top level shapes
		wxSFScaledDC sdc( (wxWindowDC*)&dc, m_nScale * m_pCanvas->GetScale() );
		this->DrawContent( sdc );
		
		// draw canvas client area
		dc.SetPen(*wxRED_PEN);
		dc.SetBrush(*wxTRANSPARENT_BRUSH);
		dc.DrawRectangle(double(szCanvasOffset.x)*m_nScale, double(szCanvasOffset.y)*m_nScale, double(szCanvas.x)*m_nScale, double(szCanvas.y)*m_nScale);
		
		dc.SetBrush(wxNullBrush);
		dc.SetPen(wxNullPen);
	} 
	
	dc.SetBackground( wxNullBrush );
	
    #if wxVERSION_NUMBER < 2900 && wxUSE_GRAPHICS_CONTEXT
    wxSFScaledDC::EnableGC( fGCEnabled );
    #endif
}
Example #2
0
bool wxSFPrintout::OnPrintPage(int page)
{
	wxUnusedVar( page );

    wxASSERT_MSG(m_pCanvas, wxT("Shape canvas must be set in the wxSFPrintout class instance."));

    wxDC *dc = GetDC();
    if (dc && m_pCanvas)
    {
        // get grawing size
        wxRect fitRect, totalBB = m_pCanvas->GetTotalBoundingBox();
        wxCoord maxX = totalBB.GetRight();
        wxCoord maxY = totalBB.GetBottom();

        // set printing mode
        switch( m_pCanvas->GetPrintMode() )
        {
            case wxSFShapeCanvas::prnFIT_TO_PAGE:
                FitThisSizeToPage(wxSize(maxX, maxY));
                fitRect = GetLogicalPageRect();
                break;

            case wxSFShapeCanvas::prnFIT_TO_PAPER:
                FitThisSizeToPaper(wxSize(maxX, maxY));
                fitRect = GetLogicalPaperRect();
                break;

            case wxSFShapeCanvas::prnFIT_TO_MARGINS:
                FitThisSizeToPageMargins(wxSize(maxX, maxY), *g_pageSetupData);
                fitRect = GetLogicalPageMarginsRect(*g_pageSetupData);
                break;

            case wxSFShapeCanvas::prnMAP_TO_PAGE:
                MapScreenSizeToPage();
                fitRect = GetLogicalPageRect();
                break;

            case wxSFShapeCanvas::prnMAP_TO_PAPER:
                MapScreenSizeToPaper();
                fitRect = GetLogicalPaperRect();
                break;

            case wxSFShapeCanvas::prnMAP_TO_MARGINS:
                MapScreenSizeToPage();
                fitRect = GetLogicalPageMarginsRect(*g_pageSetupData);
                break;

            case wxSFShapeCanvas::prnMAP_TO_DEVICE:
                MapScreenSizeToDevice();
                fitRect = GetLogicalPageRect();
                break;
        }

        // This offsets the image so that it is centered within the reference
        // rectangle defined above.

        wxCoord xoff = ((fitRect.width - maxX - totalBB.GetLeft()) / 2) - fitRect.x;
        wxCoord yoff = ((fitRect.height - maxY - totalBB.GetTop()) / 2) - fitRect.y;

        switch( m_pCanvas->GetPrintHAlign() )
        {
            case wxSFShapeCanvas::halignLEFT:
                xoff = 0;
                break;

            case wxSFShapeCanvas::halignRIGHT:
                xoff = fitRect.width - totalBB.GetWidth();
                break;

            default:
                break;
        }

        switch( m_pCanvas->GetPrintVAlign() )
        {
            case wxSFShapeCanvas::valignTOP:
                yoff = 0;
                break;

            case wxSFShapeCanvas::valignBOTTOM:
                yoff = fitRect.height - totalBB.GetHeight();
                break;

            default:
                break;
        }

        OffsetLogicalOrigin(xoff, yoff);

        // store current canvas properties
        double prevScale = m_pCanvas->GetScale();
        long prevStyle = m_pCanvas->GetStyle();
        wxColour prevColour = m_pCanvas->GetCanvasColour();

        // disable canvas background drawing if required
        if( !m_pCanvas->ContainsStyle( wxSFShapeCanvas::sfsPRINT_BACKGROUND ) )
        {
            m_pCanvas->RemoveStyle( wxSFShapeCanvas::sfsGRADIENT_BACKGROUND );
            m_pCanvas->RemoveStyle( wxSFShapeCanvas::sfsGRID_SHOW );
            m_pCanvas->SetCanvasColour( *wxWHITE);
        }

        // draw the canvas content without any scale (dc is scaled by the printing framework)
#if wxVERSION_NUMBER < 2900
		double nScale = 1;
		if( wxSFShapeCanvas::IsGCEnabled() ) dc->GetUserScale( &nScale, &nScale );
        m_pCanvas->SetScale(1);

		#ifdef __WXMSW__
		wxSFScaledDC sdc( (wxWindowDC*)dc, nScale );
		sdc.PrepareGC();
        m_pCanvas->DrawContent(sdc, sfNOT_FROM_PAINT);
        #else
		m_pCanvas->DrawContent(*dc, sfNOT_FROM_PAINT);
		#endif

        m_pCanvas->SetScale(prevScale);
#else
		m_pCanvas->SetScale(1);
		m_pCanvas->DrawContent(*dc, sfNOT_FROM_PAINT);
		m_pCanvas->SetScale(prevScale);
#endif

        // restore previous canvas properties if needed
        if( !m_pCanvas->ContainsStyle( wxSFShapeCanvas::sfsPRINT_BACKGROUND ) )
        {
            m_pCanvas->SetStyle( prevStyle );
            m_pCanvas->SetCanvasColour( prevColour );
        }

        return true;
    }
    else
        return false;
}
Example #3
0
int main( int argc, char* argv[]) {
  if(argc != 2) {
    std::cerr << "Usage : ./main.out <edge_file>" << std::endl;
    exit(1);
  }

  Network network;
  std::ifstream fin(argv[1]);
  std::cerr << "Loading input file" << std::endl;
  network.LoadFile( fin );

  bool is_weighted = network.IsWeighted();
  if( ! is_weighted ) {
    std::cerr << "All the link weights are 1. Analyze the network as a non-weighted network." << std::endl;
  }

  std::pair<double,double> fc;
  if( is_weighted ) {
  std::cerr << "Conducting percolation analysis" << std::endl;
  std::ofstream lrp("link_removal_percolation.dat");
  lrp << "#fraction  weak_link_removal_lcc susceptibility strong_link_removal_lcc susceptibility" << std::endl;
  fc = network.AnalyzeLinkRemovalPercolationVariableAccuracy( 0.02, 0.005, lrp );
  lrp.flush();
  }

  std::cerr << "Calculating local clustering coefficients" << std::endl;
  network.CalculateLocalCCs();
  if( is_weighted ) {
  std::cerr << "Calculating overlaps" << std::endl;
  network.CalculateOverlaps();
  }

  std::cerr << "Calculating degree distribution" << std::endl;
  std::ofstream dd("degree_distribution.dat");
  const auto degree_distribution = network.DegreeDistribution();
  for(const auto& f : degree_distribution ) {
    dd << f.first << ' ' << f.second << std::endl;
  }
  dd.flush();

  if( is_weighted ) {
  std::cerr << "Calculating link weight distribution" << std::endl;
  // double edge_weight_bin_size = 1.0;
  std::ofstream ewd("edge_weight_distribution.dat");
  for(const auto& f : network.EdgeWeightDistributionLogBin() ) {
    ewd << f.first << ' ' << f.second << std::endl;
  }
  ewd.flush();
  }

  std::map<double, size_t> strength_distribution;
  if( is_weighted ) {
  std::cerr << "Calculating node strength distribution" << std::endl;
  double avg_s = network.AverageEdgeWeight() * network.AverageDegree();
  double strength_bin_size = avg_s * 0.01;
  std::ofstream sd("strength_distribution.dat");
  strength_distribution = network.StrengthDistribution(strength_bin_size);
  for(const auto& f :strength_distribution) {
    sd << f.first << ' ' << f.second << std::endl;
  }
  sd.flush();
  }

  std::cerr << "Calculating c(k)" << std::endl;
  std::ofstream cc_d("cc_degree_correlation.dat");
  for(const auto& f : network.CC_DegreeCorrelation() ) {
    cc_d << f.first << ' ' << f.second << std::endl;
  }
  cc_d.flush();

  if( is_weighted ) {
  std::cerr << "Calculating s(k)" << std::endl;
  std::ofstream sdc("strength_degree_correlation.dat");
  for(const auto& f : network.StrengthDegreeCorrelation() ) {
    sdc << f.first << ' ' << f.second << std::endl;
  }
  sdc.flush();
  }

  std::cerr << "Calculating k_nn(k)" << std::endl;
  std::ofstream ndc("neighbor_degree_correlation.dat");
  for(const auto& f : network.NeighborDegreeCorrelation() ) {
    ndc << f.first << ' ' << f.second << std::endl;
  }
  ndc.flush();

  if( is_weighted ) {
  std::cerr << "Calculating O(w)" << std::endl;
  std::ofstream owc("overlap_weight_correlation.dat");
  for(const auto& f : network.OverlapWeightCorrelationLogBin() ) {
    owc << f.first << ' ' << f.second << std::endl;
  }
  owc.flush();
  }

  std::cerr << "Calculating scalar values" << std::endl;
  std::ofstream fout("_output.json");
  fout << "{" << std::endl;
  fout << "  \"NumNodes\": " << network.NumNodes() << ',' << std::endl;
  fout << "  \"NumEdges\": " << network.NumEdges() << ',' << std::endl;
  fout << "  \"AverageDegree\": " << network.AverageDegree() << ',' << std::endl;
  fout << "  \"Assortativity\": " << network.PCC_k_knn() << ',' << std::endl;
  fout << "  \"ArgMax_Pk\": " << ArgMax( degree_distribution ) << ',' << std::endl;
  fout << "  \"ClusteringCoefficient\": " << network.ClusteringCoefficient() << ',' << std::endl;
  fout << "  \"PCC_C_k\": " << network.PCC_C_k();
  if( is_weighted ) {
  fout << ',' << std::endl;
  double ave_w = network.AverageEdgeWeight();
  fout << "  \"AverageEdgeWeight\": " << ave_w << ',' << std::endl;
  double ave_k = network.AverageDegree();
  fout << "  \"AverageStrength\": " << ave_w * ave_k << ',' << std::endl;
  double argmax_ps = ArgMax( strength_distribution );
  fout << "  \"ArgMax_Ps\": " << argmax_ps << ',' << std::endl;
  fout << "  \"Normalized_ArgMax_Ps\": " << argmax_ps / (ave_w*ave_k) << ',' << std::endl;
  fout << "  \"PCC_s_k\": " << network.PCC_s_k() << ',' << std::endl;
  fout << "  \"AverageOverlap\": " << network.AverageOverlap() << ',' << std::endl;
  fout << "  \"PCC_O_w\": " << network.PCC_O_w() << ',' << std::endl;
  fout << "  \"Fc_Ascending\": " << fc.first << ',' << std::endl;
  fout << "  \"Fc_Descending\": " << fc.second << ',' << std::endl;
  fout << "  \"Delta_Fc\": " << fc.second - fc.first << std::endl;
  }
  fout << "}" << std::endl;
  return 0;
}
Example #4
0
wxBitmap ocpnFloatingCompassWindow::CreateBmp()
{
    wxString gpsIconName;
    ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();

    // In order to draw a horizontal compass window when the toolbar is vertical, we
    // need to save away the sizes and backgrounds for the two icons.

    static wxBitmap compassBg, gpsBg;
    static wxSize toolsize;
    static int topmargin, leftmargin, radius;

    if( ! compassBg.IsOk() ) {
        int orient = style->GetOrientation();
        style->SetOrientation( wxTB_HORIZONTAL );
        if( style->HasBackground() ) {
            compassBg = style->GetNormalBG();
            style->DrawToolbarLineStart( compassBg );
            gpsBg = style->GetNormalBG();
            style->DrawToolbarLineEnd( gpsBg );
        }

        leftmargin = style->GetLeftMargin();
        topmargin = style->GetTopMargin();
        toolsize = style->GetToolSize();
        toolsize.x *= 2;
        radius = style->GetToolbarCornerRadius();

        if( orient ) style->SetOrientation( wxTB_VERTICAL );
    }

    bool b_need_refresh = false;

    if( bGPSValid ) {
        if( g_bSatValid ) {
            gpsIconName = _T("gps3Bar");
            if( g_SatsInView <= 8 ) gpsIconName = _T("gps2Bar");
            if( g_SatsInView <= 4 ) gpsIconName = _T("gps1Bar");
            if( g_SatsInView < 0 ) gpsIconName = _T("gpsGry");

        } else
            gpsIconName = _T("gpsGrn");
    } else
        gpsIconName = _T("gpsRed");

    if( m_lastgpsIconName != gpsIconName ) b_need_refresh = true;

    double rose_angle = -999.;

    if( ( fabs( cc1->GetVPRotation() ) > .01 ) || ( fabs( cc1->GetVPSkew() ) > .01 ) ) {
        rose_angle = -cc1->GetVPRotation();

        if( !g_bCourseUp && !g_bskew_comp ) rose_angle = -cc1->GetVPRotation() - cc1->GetVPSkew();

        b_need_refresh = true;
    } else
        rose_angle = 0.;

    if( fabs( m_rose_angle - rose_angle ) > .001 ) b_need_refresh = true;

    if( b_need_refresh ) {
        wxBitmap StatBmp;

        StatBmp.Create(
                ( _img_compass.GetWidth() + _img_gpsRed.GetWidth() ) + style->GetLeftMargin() * 2
                        + style->GetToolSeparation(),
                _img_compass.GetHeight() + style->GetTopMargin() + style->GetBottomMargin() );

        if( StatBmp.IsOk() ) {

            wxMemoryDC mdc;
            mdc.SelectObject( StatBmp );
            mdc.SetBackground( wxBrush( GetGlobalColor( _T("GREY2") ), wxSOLID ) );
            mdc.Clear();

            mdc.SetPen( wxPen( GetGlobalColor( _T("UITX1") ), 1 ) );
            mdc.SetBrush( wxBrush( GetGlobalColor( _T("UITX1") ), wxTRANSPARENT ) );

            mdc.DrawRoundedRectangle( 0, 0, StatBmp.GetWidth(), StatBmp.GetHeight(),
                    style->GetToolbarCornerRadius() );

            wxPoint offset( style->GetLeftMargin(), style->GetTopMargin() );

            //    Build Compass Rose, rotated...
            wxBitmap BMPRose;
            wxPoint after_rotate;

            if( g_bCourseUp ) BMPRose = style->GetIcon( _T("CompassRose") );
            else
                BMPRose = style->GetIcon( _T("CompassRoseBlue") );
            if( ( fabs( cc1->GetVPRotation() ) > .01 ) || ( fabs( cc1->GetVPSkew() ) > .01 ) ) {
                wxPoint rot_ctr( BMPRose.GetWidth() / 2, BMPRose.GetHeight() / 2 );
                wxImage rose_img = BMPRose.ConvertToImage();

                wxImage rot_image = rose_img.Rotate( rose_angle, rot_ctr, true, &after_rotate );
                BMPRose = wxBitmap( rot_image ).GetSubBitmap( wxRect( -after_rotate.x, -after_rotate.y, BMPRose.GetWidth(), BMPRose.GetHeight()) );
            }

            wxBitmap iconBm;

            if( style->HasBackground() ) {
                iconBm = MergeBitmaps( compassBg, BMPRose, wxSize( 0, 0 ) );
            } else {
                iconBm = BMPRose;
            }

            mdc.DrawBitmap( iconBm, offset );
            offset.x += iconBm.GetWidth();

            m_rose_angle = rose_angle;

            if( style->HasBackground() ) {
                iconBm = MergeBitmaps( gpsBg, style->GetIcon( gpsIconName ), wxSize( 0, 0 ) );
            } else {
                iconBm = style->GetIcon( gpsIconName );
            }
            mdc.DrawBitmap( iconBm, offset );
            mdc.SelectObject( wxNullBitmap );
            m_lastgpsIconName = gpsIconName;
        }

        if( style->marginsInvisible ) {
            m_MaskBmp = wxBitmap( StatBmp.GetWidth(), StatBmp.GetHeight() );
            wxMemoryDC sdc( m_MaskBmp );
            sdc.SetBackground( *wxWHITE_BRUSH );
            sdc.Clear();
            sdc.SetBrush( *wxBLACK_BRUSH );
            sdc.SetPen( *wxBLACK_PEN );
            sdc.DrawRoundedRectangle( wxPoint( leftmargin, topmargin ), toolsize, radius );
            sdc.SelectObject( wxNullBitmap );
            SetShape( wxRegion( m_MaskBmp, *wxWHITE, 0 ) );
        }

        return StatBmp;
    }

    else
        return wxNullBitmap;
}
Example #5
0
        std::pair<float*,std::pair<unsigned int,unsigned int>>
        readHDF5(
            std::string const _filename
        )
        {
            splash::SerialDataCollector sdc( 0 );
            splash::DataCollector::FileCreationAttr fCAttr;
            splash::DataCollector::initFileCreationAttr( fCAttr );

            fCAttr.fileAccType = splash::DataCollector::FAT_READ;

            sdc.open( _filename.c_str( ), fCAttr );

            int32_t *ids = NULL;
            size_t num_ids = 0;
            sdc.getEntryIDs( NULL, &num_ids );

            if( num_ids == 0 )
            {
                sdc.close( );
#               ifdef IMRESH_DEBUG
                    std::cout << "imresh::io::readInFuncs::readHDF5(): Error opening empty file."
                        << std::endl;
#               endif
                exit( EXIT_FAILURE );
            }
            else
            {
                ids = new int32_t[num_ids];
                sdc.getEntryIDs( ids, &num_ids );
            }

            splash::DataCollector::DCEntry *entries = NULL;
            size_t num_entries = 0;
            sdc.getEntriesForID( ids[0], NULL, &num_entries );

            if( num_entries == 0 )
            {
                delete[] entries;
                delete[] ids;
                sdc.close( );
#               ifdef IMRESH_DEBUG
                    std::cout << "imresh::io::readInFuncs::readHDF5(): Error opening empty file."
                        << std::endl;
#               endif
                exit( EXIT_FAILURE );
            }
            else
            {
                entries = new splash::DataCollector::DCEntry[num_entries];
                sdc.getEntriesForID( ids[0], entries, &num_entries );
            }

            splash::DataCollector::DCEntry first_entry = entries[0];

            splash::Dimensions dim;
            sdc.read( ids[0], first_entry.name.c_str( ), dim, NULL );

            float* mem = NULL;
            if( dim.getScalarSize( ) == 0 )
            {
                delete[] entries;
                delete[] ids;
                delete[] mem;
                sdc.close( );
#               ifdef IMRESH_DEBUG
                    std::cout << "imresh::io::readInFuncs::readHDF5(): Error opening empty file."
                        << std::endl;
#               endif
                exit( EXIT_FAILURE );
            }
            else
            {
                mem = new float[dim.getScalarSize( )];
                sdc.read( ids[0], first_entry.name.c_str( ), dim, mem );
            }

            delete[] entries;
            delete[] ids;

#           ifdef IMRESH_DEBUG
                std::cout << "imresh::io::readInFuncs::readHDF5(): Successfully read file."
                    << std::endl;
#           endif
            return { mem, { dim[1], dim[2] } };
        }
Example #6
0
void ocpnCompass::CreateBmp( bool newColorScheme )
{
    if(!m_shown)
        return;

    wxString gpsIconName;
    ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();

    // In order to draw a horizontal compass window when the toolbar is vertical, we
    // need to save away the sizes and backgrounds for the two icons.

    static wxBitmap compassBg, gpsBg;
    static wxSize toolsize;
    static int topmargin, leftmargin, radius;

    if( ! compassBg.IsOk() || newColorScheme ) {
        int orient = style->GetOrientation();
        style->SetOrientation( wxTB_HORIZONTAL );
        if( style->HasBackground() ) {
            compassBg = style->GetNormalBG();
            style->DrawToolbarLineStart( compassBg );
            compassBg = style->SetBitmapBrightness( compassBg );
            gpsBg = style->GetNormalBG();
            style->DrawToolbarLineEnd( gpsBg );
            gpsBg = style->SetBitmapBrightness( gpsBg );
        }

        if(fabs(m_scale-1.0) > 0.1){
            wxImage bg_img = compassBg.ConvertToImage();
            bg_img.Rescale(compassBg.GetWidth() * m_scale, compassBg.GetHeight() *m_scale, wxIMAGE_QUALITY_NORMAL);
            compassBg = wxBitmap( bg_img );
            
            bg_img = gpsBg.ConvertToImage();
            bg_img.Rescale(gpsBg.GetWidth() * m_scale, gpsBg.GetHeight() *m_scale, wxIMAGE_QUALITY_NORMAL);
            gpsBg = wxBitmap( bg_img );
        }
    
        leftmargin = style->GetCompassLeftMargin();
        topmargin = style->GetCompassTopMargin();
        radius = style->GetCompassCornerRadius();

        if( orient ) style->SetOrientation( wxTB_VERTICAL );
    }

    bool b_need_refresh = false;

    if( bGPSValid ) {
        if( g_bSatValid ) {
            gpsIconName = _T("gps3Bar");
            if( g_SatsInView <= 8 ) gpsIconName = _T("gps2Bar");
            if( g_SatsInView <= 4 ) gpsIconName = _T("gps1Bar");
            if( g_SatsInView < 0 ) gpsIconName = _T("gpsGry");

        } else
            gpsIconName = _T("gpsGrn");
    } else
        gpsIconName = _T("gpsRed");

    if( m_lastgpsIconName != gpsIconName ) b_need_refresh = true;

    double rose_angle = -999.;

    if( ( fabs( cc1->GetVPRotation() ) > .01 ) || ( fabs( cc1->GetVPSkew() ) > .01 ) ) {
        rose_angle = -cc1->GetVPRotation();
    } else
        rose_angle = 0.;

    if( fabs( m_rose_angle - rose_angle ) > .1 ) b_need_refresh = true;

    if( !b_need_refresh )
        return;

    int width = compassBg.GetWidth() + gpsBg.GetWidth() + leftmargin;
    if( !style->marginsInvisible ) 
        width += leftmargin + style->GetToolSeparation();
        
    m_StatBmp.Create( width, compassBg.GetHeight() + topmargin + style->GetCompassBottomMargin() );

    m_rect.width = m_StatBmp.GetWidth();
    m_rect.height = m_StatBmp.GetHeight();
    
    if( !m_StatBmp.IsOk() )
        return;

    m_MaskBmp = wxBitmap( m_StatBmp.GetWidth(), m_StatBmp.GetHeight() );
    if( style->marginsInvisible ) {
        wxMemoryDC sdc( m_MaskBmp );
        sdc.SetBackground( *wxWHITE_BRUSH );
        sdc.Clear();
        sdc.SetBrush( *wxBLACK_BRUSH );
        sdc.SetPen( *wxBLACK_PEN );
        wxSize maskSize = wxSize(m_MaskBmp.GetWidth() - leftmargin,
                                 m_MaskBmp.GetHeight() - (2 * topmargin));
        sdc.DrawRoundedRectangle( wxPoint( leftmargin, topmargin ), maskSize, radius );
        sdc.SelectObject( wxNullBitmap );
    } else if(radius) {
        wxMemoryDC sdc( m_MaskBmp );
        sdc.SetBackground( *wxWHITE_BRUSH );
        sdc.Clear();
        sdc.SetBrush( *wxBLACK_BRUSH );
        sdc.SetPen( *wxBLACK_PEN );
        sdc.DrawRoundedRectangle( 0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(), radius );
        sdc.SelectObject( wxNullBitmap );
    }
    m_StatBmp.SetMask(new wxMask(m_MaskBmp, *wxWHITE));

    wxMemoryDC mdc;
    mdc.SelectObject( m_StatBmp );
    mdc.SetBackground( wxBrush( GetGlobalColor( _T("COMP1") ), wxSOLID ) );
    mdc.Clear();

    mdc.SetPen( wxPen( GetGlobalColor( _T("UITX1") ), 1 ) );
    mdc.SetBrush( wxBrush( GetGlobalColor( _T("UITX1") ), wxTRANSPARENT ) );
    
    if( !style->marginsInvisible )
        mdc.DrawRoundedRectangle( 0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),radius );

    wxPoint offset(leftmargin, topmargin);

    //    Build Compass Rose, rotated...
    wxBitmap BMPRose;
    wxPoint after_rotate;

    int cwidth = style->GetToolSize().x * m_scale;
    int cheight = style->GetToolSize().y * m_scale;
    cheight = wxMin(cheight, compassBg.GetHeight());
    cwidth = wxMin( cwidth, cheight );
    cheight = cwidth;
    
    if( g_bCourseUp )
        BMPRose = style->GetIcon( _T("CompassRose"), cwidth, cheight );
    else
        BMPRose = style->GetIcon( _T("CompassRoseBlue"), cwidth, cheight );
    if( ( fabs( cc1->GetVPRotation() ) > .01 ) || ( fabs( cc1->GetVPSkew() ) > .01 ) ) {
        
        wxImage rose_img = BMPRose.ConvertToImage();
        wxPoint rot_ctr( cwidth / 2, cheight / 2  );
        wxImage rot_image = rose_img.Rotate( rose_angle, rot_ctr, true, &after_rotate );
        BMPRose = wxBitmap( rot_image ).GetSubBitmap( wxRect( -after_rotate.x, -after_rotate.y, cwidth, cheight ));
    }

    wxBitmap iconBm;

    if( style->HasBackground() ) {
        iconBm = MergeBitmaps( compassBg, BMPRose, wxSize( 0, 0 ) );
    } else {
        iconBm = BMPRose;
    }
    
    iconBm = ConvertTo24Bit( wxColor(0,0,0), iconBm);
        
    mdc.DrawBitmap( iconBm, offset );
    offset.x += iconBm.GetWidth();
    offset.x += style->GetToolSeparation();

    m_rose_angle = rose_angle;

    //  GPS Icon
    int twidth = style->GetToolSize().x * m_scale;
    int theight = style->GetToolSize().y * m_scale;
    theight = wxMin(cheight, compassBg.GetHeight());
    int swidth = wxMax( twidth, theight );
    int sheight = wxMin( twidth, theight );
    
    //  Sometimes, the SVG renderer gets the size wrong due to some internal rounding error.
    //  If so found, it seems to work OK by just reducing the requested size by one pixel....
    wxBitmap gicon = style->GetIcon( gpsIconName, swidth, sheight );
    if( gicon.GetHeight() != sheight )
        gicon = style->GetIcon( gpsIconName, swidth-1, sheight-1, true );

    if( style->HasBackground() ) {
        iconBm = MergeBitmaps( gpsBg, gicon, wxSize( 0, 0 ) );
    } else {
        iconBm = gicon;
    }
    
    iconBm = ConvertTo24Bit( wxColor(0,0,0), iconBm);
    mdc.DrawBitmap( iconBm, offset );
    mdc.SelectObject( wxNullBitmap );
    
    m_lastgpsIconName = gpsIconName;

#if defined(ocpnUSE_GLES)   // GLES does not do ocpnDC::DrawBitmap(), so use texture
    if(g_bopengl){
        wxImage image = m_StatBmp.ConvertToImage(); 
        unsigned char *imgdata = image.GetData();
        unsigned char *imgalpha = image.GetAlpha();
        int tex_w = image.GetWidth();
        int tex_h = image.GetHeight();
        
        GLuint format = GL_RGBA;
        GLuint internalformat = format;
        int stride = 4;
        
        if(imgdata){
            unsigned char *teximage = (unsigned char *) malloc( stride * tex_w * tex_h );
        
            for( int j = 0; j < tex_w*tex_h; j++ ){
                for( int k = 0; k < 3; k++ )
                    teximage[j * stride + k] = imgdata[3*j + k];
                teximage[j * stride + 3] = imgalpha ? imgalpha[j] : 255;      // alpha
            }
                
            if(texobj){
                glDeleteTextures(1, &texobj);
                texobj = 0;
            }
                
            glGenTextures( 1, &texobj );
            glBindTexture( GL_TEXTURE_2D, texobj );
            
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST/*GL_LINEAR*/ );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
            
            glTexImage2D( GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0,
                        format, GL_UNSIGNED_BYTE, teximage );
                            
            free(teximage);
        }
   }
#endif
       
}
long MrsvrPlotCanvas::onCanvasRepaint(FXObject* sender, FXSelector, void* ptr)
{
  int fResize;

#ifdef DEBUG_MRSVR_PLOT_CANVAS
  //printf("MrsvrPlotCanvas::onCanvasRepaint()\n");
#endif //DEBUG_MRSVR_PLOT_CANVAS

  FXEvent  *ev   = (FXEvent*) ptr;
  int w = getWidth();
  int h = getHeight();

  // If the window size has been changed,
  // update all parameters for drawing axes.
  if (windowSizeX != w || windowSizeY != h) {
    fResize = 1;
    windowSizeX = w;
    windowSizeY = h;
  } else {
    fResize = 0;
  }

  if (ev->synthetic || fResize) { // update offscreen
#ifdef DEBUG_MRSVR_PLOT_CANVAS
    printf("MrsvrPlotCanvas::onCanvasRepaint(): update offscreen\n");
#endif //DEBUG_MRSVR_PLOT_CANVAS


    if (fUpdateAxisParam) {
      calcAxisParam();
      fResize = TRUE;
    }

    FXfloat fw = (FXfloat) w;
    FXfloat fh = (FXfloat) h;

    if (fResize) { 
      offscreen->resize(w, h);
      FXfloat mw = fw - (float)(windowMargLt+windowMargRt);
      FXfloat mh = fh - (float)(windowMargTop+windowMargBot);
      
      zeroX      = (FXuint) (pZeroX * mw) + windowMargLt;
      zeroY      = (FXuint) (pZeroY * mh) + windowMargTop;
      xAxisMin   = (FXuint) (pXAxisMin * mw) + windowMargLt;
      xAxisMax   = (FXuint) (pXAxisMax * mw) + windowMargLt;
      yAxisMin   = (FXuint) (pYAxisMin * mh) + windowMargTop;
      yAxisMax   = (FXuint) (pYAxisMax * mh) + windowMargTop;
      xAxisIntv  = (FXuint) (pXAxisIntv * mw);
      yAxisIntv  = (FXuint) (pYAxisIntv * mh);

      xLabelW    = labelFont->getTextWidth(xLabel->text(), xLabel->length());
      xLabelH    = labelFont->getTextHeight(xLabel->text(), xLabel->length());
      yLabelW    = labelFont->getTextWidth(yLabel->text(), yLabel->length());
      yLabelH    = labelFont->getTextHeight(yLabel->text(), yLabel->length());

      xLabelX    = xAxisMax - xLabelW;
      xLabelY    = zeroY + xLabelH*2;
      yLabelX    = zeroX + 2;
      yLabelY    = yAxisMax;

      // Calcurate the position of graduations for each axis.

      // pixel / unit
      unitX = ((float)(xAxisMax - xAxisMin)) * pUnitX / (pXAxisMax - pXAxisMin);
      float dx  = unitX * gradSpaceX;
      maxXGradNumHeight = labelFont->getTextHeight(xGrad[numGradX-1]->text(),
                                                   xGrad[numGradX-1]->length());
      maxXGradNumWidth  = labelFont->getTextWidth(xGrad[numGradX-1]->text(),
                                                  xGrad[numGradX-1]->length());
      for (int i = 0; i < numGradX; i ++) {
        xGradPos[i] = zeroX + (int) dx * (i-gradOrgX);
      }
      
      unitY = ((float)(yAxisMax - yAxisMin)) * pUnitY / (pYAxisMax - pYAxisMin);
      float dy  = unitY * gradSpaceY;
      maxYGradNumHeight = labelFont->getTextHeight(yGrad[numGradY-1]->text(),
                                                   yGrad[numGradY-1]->length());
      maxYGradNumWidth  = labelFont->getTextWidth(yGrad[0]->text(),
                                                  yGrad[0]->length());
      for (int i = 0; i < numGradY; i ++) {
        yGradPos[i] = zeroY + (int) dy * (i-gradOrgY);
      }
      
      legendTotalW = legendTotalH = 0;
      for (int i = 0; i < nLines; i ++) {
        if (legend[i]) {
          legendW[i] = labelFont->getTextWidth(legend[i]->text(),
                                               legend[i]->length());
          if (legendW[i] > legendTotalW) {
            legendTotalW = legendW[i];
          }
          legendH[i] = labelFont->getTextHeight(legend[i]->text(),
                                                legend[i]->length());
          legendTotalH += legendH[i] +1;
        }
      }
      
    }

    FXDCWindow dc(offscreen);
    //dc.setTextFont(labelFont);
    dc.setFont(labelFont);    // for fox 1.1
    
    dc.setForeground(colorBg);
    dc.fillRectangle(0, 0, w, h);
    
    // Draw Axes
    dc.setForeground(colorAxis);
    dc.setLineStyle(LINE_SOLID);
    dc.setLineCap(CAP_BUTT);
    dc.setFunction(BLT_SRC);
    dc.setLineJoin(JOIN_ROUND);
    
    dc.setLineWidth(xAxisWidth);
    dc.drawLine(xAxisMin, zeroY, xAxisMax, zeroY); // x axis
    for (int i = 0; i < numGradX; i ++) {
      dc.drawLine(xGradPos[i], zeroY+2, xGradPos[i], zeroY-1);
      dc.drawText(xGradPos[i], zeroY+maxXGradNumHeight,
                  xGrad[i]->text(), xGrad[i]->length());
    }

    dc.setLineWidth(yAxisWidth);
    dc.drawLine(zeroX, yAxisMin, zeroX, yAxisMax); // y axis
    for (int i = 0; i < numGradY; i ++) {
      dc.drawLine(zeroX-2, yGradPos[i], zeroX+1, yGradPos[i]);
      dc.drawText(zeroX-maxYGradNumWidth-2, yGradPos[i]+maxYGradNumHeight/2, 
                  yGrad[i]->text(), yGrad[i]->length());
    }

    dc.drawText(xLabelX, xLabelY, xLabel->text(), xLabel->length());
    dc.drawText(yLabelX, yLabelY, yLabel->text(), yLabel->length());

    int x = windowSizeX - legendTotalW - windowMargRt;
    int y = windowSizeY - legendTotalH - windowMargBot;
    int cy = y;
    for (int i = 0; i < nLines; i ++) {
      if (legend[i]) {
        dc.drawText(x, cy, legend[i]->text(), legend[i]->length());
        cy += legendH[i] + 1;
      }
    }

    // Plot data
    dc.setLineCap(lineCapStyle);
    dc.setFunction(lineDrawFunction);
    dc.setLineJoin(lineJoinStyle);
    cy = y - legendH[0]/2;
#ifdef DEBUG_MRSVR_PLOT_CANVAS
    printf("MrsvrPlotCanvas::onCanvasRepaint(): nLines = %d\n", nLines);
#endif //DEBUG_MRSVR_PLOT_CANVAS

    for (int i = 0; i < nLines; i ++) {
#ifdef DEBUG_MRSVR_PLOT_CANVAS    
      printf("MrsvrPlotCanvas::onCanvasRepaint(): plotPoints[%d] = %d\n", i, plotPoints[i]);
      printf("MrsvrPlotCanvas::onCanvasRepaint(): scaleY[%d] = %f\n", i, scaleY[i]);
#endif //DEBUG_MRSVR_PLOT_CANVAS
      dc.setForeground(lineColor[i]);
      dc.setLineWidth(lineWidth[i]);
      dc.setLineStyle(lineStyle[i]);
      
      // for legend
      if (legend[i]) {
        dc.drawLine(x-30, cy, x-10, cy);
        cy += legendH[i] + 1;
      }

      //float sy = scaleY[i] * (float)(yAxisMin - yAxisMax);
      float sy = scaleY[i]*scaleYAll*unitY;
      float oy = offsetYAll + offsetY[i];
      float* dptr = data[i];
      float intv = lineStepX[i] * unitX;
      FXPoint* pptr = pointsBuffer;
      int np = plotPoints[i];
      for (int n = 0; n < np; n ++) {
        pptr->x = (FXshort) (((float)n) * intv) + zeroX;
        pptr->y = (FXshort) (sy * (dptr[n]-oy)) + zeroY;
        //printf("MrsvrPlotCanvas::onCanvasRepaint(): (x, y) = (%d, %d)\n", pptr->x, pptr->y);
        pptr ++;
      }
      if (plotPoints[i] > 2) {
        //dc.drawLines(pointsBuffer, plotPoints[i]);
        dc.drawLines(pointsBuffer, plotCursor[i]);
        if (plotMode == MODE_SWEEP && plotCursor[i] < np) {}
        //dc.drawLines(pointsBuffer+plotCursor[i]+1, np - plotCursor[i]);
      }
    }
  }

  //FXDCWindow sdc(canvas, ev);
  FXDCWindow sdc(this, ev);
  //sdc.fillRectangle(0, 0, w, h);
  sdc.drawImage(offscreen, 0,0);

  return 1;
}
TEST( SDCFsiTest, reuse )
{
    std::vector<int> nbIter;

    for ( int nbReuse = 0; nbReuse < 3; nbReuse++ )
    {
        scalar r0 = 0.2;
        scalar a0 = M_PI * r0 * r0;
        scalar u0 = 0.1;
        scalar p0 = 0;
        scalar dt = 0.01;
        int N = 20;
        scalar L = 1;
        scalar T = 1;
        scalar dx = L / N;
        scalar rho = 1.225;
        scalar E = 490;
        scalar h = 1.0e-3;
        scalar cmk = std::sqrt( E * h / (2 * rho * r0) );
        scalar c0 = std::sqrt( cmk * cmk - p0 / (2 * rho) );
        scalar kappa = c0 / u0;

        bool parallel = false;
        int extrapolation = 0;
        scalar tol = 1.0e-5;
        int maxIter = 50;
        scalar initialRelaxation = 1.0e-3;
        int maxUsedIterations = 50;

        scalar singularityLimit = 1.0e-13;
        int reuseInformationStartingFromTimeIndex = 0;
        bool scaling = false;
        bool updateJacobian = false;
        scalar beta = 0.1;
        int minIter = 5;

        ASSERT_NEAR( kappa, 10, 1.0e-13 );
        ASSERT_TRUE( dx > 0 );

        std::shared_ptr<tubeflow::SDCTubeFlowFluidSolver> fluid( new tubeflow::SDCTubeFlowFluidSolver( a0, u0, p0, dt, cmk, N, L, T, rho ) );
        std::shared_ptr<tubeflow::SDCTubeFlowSolidSolver> solid( new tubeflow::SDCTubeFlowSolidSolver( a0, cmk, p0, rho, L, N ) );

        shared_ptr<RBFFunctionInterface> rbfFunction;
        shared_ptr<RBFInterpolation> rbfInterpolator;
        shared_ptr<RBFCoarsening> rbfInterpToCouplingMesh;
        shared_ptr<RBFCoarsening> rbfInterpToMesh;

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToCouplingMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        shared_ptr<MultiLevelSolver> fluidSolver( new MultiLevelSolver( fluid, fluid, rbfInterpToCouplingMesh, rbfInterpToMesh, 0, 0 ) );

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToCouplingMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        shared_ptr<MultiLevelSolver> solidSolver( new MultiLevelSolver( solid, fluid, rbfInterpToCouplingMesh, rbfInterpToMesh, 1, 0 ) );

        std::shared_ptr< std::list<std::shared_ptr<ConvergenceMeasure> > > convergenceMeasures;
        convergenceMeasures = std::shared_ptr<std::list<std::shared_ptr<ConvergenceMeasure> > >( new std::list<std::shared_ptr<ConvergenceMeasure> > );

        convergenceMeasures->push_back( std::shared_ptr<ConvergenceMeasure>( new RelativeConvergenceMeasure( 0, false, tol ) ) );
        convergenceMeasures->push_back( std::shared_ptr<ConvergenceMeasure>( new MinIterationConvergenceMeasure( 0, false, minIter ) ) );

        shared_ptr<MultiLevelFsiSolver> fsi( new MultiLevelFsiSolver( fluidSolver, solidSolver, convergenceMeasures, parallel, extrapolation ) );

        shared_ptr<PostProcessing> postProcessing( new AndersonPostProcessing( fsi, maxIter, initialRelaxation, maxUsedIterations, nbReuse, singularityLimit, reuseInformationStartingFromTimeIndex, scaling, beta, updateJacobian ) );

        std::shared_ptr<sdc::SDCFsiSolverInterface> sdcFluidSolver = std::dynamic_pointer_cast<sdc::SDCFsiSolverInterface>( fluid );
        std::shared_ptr<sdc::SDCFsiSolverInterface> sdcSolidSolver = std::dynamic_pointer_cast<sdc::SDCFsiSolverInterface>( solid );

        assert( sdcFluidSolver );
        assert( sdcSolidSolver );

        std::shared_ptr<fsi::SDCFsiSolver> fsiSolver( new fsi::SDCFsiSolver( sdcFluidSolver, sdcSolidSolver, postProcessing, extrapolation ) );

        int nbNodes = 3;

        std::shared_ptr<fsi::quadrature::IQuadrature<scalar> > quadrature;
        quadrature = std::shared_ptr<fsi::quadrature::IQuadrature<scalar> >( new fsi::quadrature::Uniform<scalar>( nbNodes ) );

        std::shared_ptr<sdc::SDC> sdc( new sdc::SDC( fsiSolver, quadrature, 1.0e-10, nbNodes, nbNodes ) );

        sdc->run();

        nbIter.push_back( fsi->nbIter );
    }

    int iprev;
    int index = 0;

    for ( int i : nbIter )
    {
        std::cout << "nbIter = " << i << std::endl;

        if ( index > 0 )
            ASSERT_LE( i, iprev );

        iprev = i;
        index++;
    }
}
TEST( SDCFsiTest, order )
{
    int N = 5;
    scalar r0 = 0.2;
    scalar a0 = M_PI * r0 * r0;
    scalar u0 = 0.1;
    scalar p0 = 0;
    scalar L = 1;
    scalar T = 1;
    scalar dx = L / N;
    scalar rho = 1.225;
    scalar E = 490;
    scalar h = 1.0e-3;
    scalar cmk = std::sqrt( E * h / (2 * rho * r0) );
    scalar c0 = std::sqrt( cmk * cmk - p0 / (2 * rho) );
    scalar kappa = c0 / u0;

    bool parallel = false;
    int extrapolation = 0;
    scalar tol = 1.0e-5;
    int maxIter = 20;
    scalar initialRelaxation = 1.0e-3;
    int maxUsedIterations = 50;
    int nbReuse = 0;
    scalar singularityLimit = 1.0e-13;
    int reuseInformationStartingFromTimeIndex = 0;
    bool scaling = false;
    scalar beta = 0.01;
    bool updateJacobian = false;
    int minIter = 5;

    ASSERT_NEAR( kappa, 10, 1.0e-13 );
    ASSERT_TRUE( dx > 0 );

    int nbComputations = 5;

    std::deque<std::shared_ptr<tubeflow::SDCTubeFlowFluidSolver> > fluidSolvers;
    std::deque<int> nbTimeStepsList;

    for ( int iComputation = 0; iComputation < nbComputations; iComputation++ )
    {
        int nbTimeSteps = 4 * std::pow( 2, iComputation );
        std::cout << "nbTimeSteps = " << nbTimeSteps << std::endl;
        scalar dt = T / nbTimeSteps;

        std::shared_ptr<tubeflow::SDCTubeFlowFluidSolver> fluid( new tubeflow::SDCTubeFlowFluidSolver( a0, u0, p0, dt, cmk, N, L, T, rho ) );
        std::shared_ptr<tubeflow::SDCTubeFlowSolidSolver> solid( new tubeflow::SDCTubeFlowSolidSolver( a0, cmk, p0, rho, L, N ) );

        shared_ptr<RBFFunctionInterface> rbfFunction;
        shared_ptr<RBFInterpolation> rbfInterpolator;
        shared_ptr<RBFCoarsening> rbfInterpToCouplingMesh;
        shared_ptr<RBFCoarsening> rbfInterpToMesh;

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToCouplingMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        shared_ptr<MultiLevelSolver> fluidSolver( new MultiLevelSolver( fluid, fluid, rbfInterpToCouplingMesh, rbfInterpToMesh, 0, 0 ) );

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToCouplingMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        rbfFunction = shared_ptr<RBFFunctionInterface>( new TPSFunction() );
        rbfInterpolator = shared_ptr<RBFInterpolation>( new RBFInterpolation( rbfFunction ) );
        rbfInterpToMesh = shared_ptr<RBFCoarsening> ( new RBFCoarsening( rbfInterpolator ) );

        shared_ptr<MultiLevelSolver> solidSolver( new MultiLevelSolver( solid, fluid, rbfInterpToCouplingMesh, rbfInterpToMesh, 1, 0 ) );

        std::shared_ptr< std::list<std::shared_ptr<ConvergenceMeasure> > > convergenceMeasures;
        convergenceMeasures = std::shared_ptr<std::list<std::shared_ptr<ConvergenceMeasure> > >( new std::list<std::shared_ptr<ConvergenceMeasure> > );

        convergenceMeasures->push_back( std::shared_ptr<ConvergenceMeasure>( new ResidualRelativeConvergenceMeasure( 0, false, tol ) ) );
        convergenceMeasures->push_back( std::shared_ptr<ConvergenceMeasure>( new MinIterationConvergenceMeasure( 0, false, minIter ) ) );

        shared_ptr<MultiLevelFsiSolver> fsi( new MultiLevelFsiSolver( fluidSolver, solidSolver, convergenceMeasures, parallel, extrapolation ) );

        shared_ptr<PostProcessing> postProcessing( new AndersonPostProcessing( fsi, maxIter, initialRelaxation, maxUsedIterations, nbReuse, singularityLimit, reuseInformationStartingFromTimeIndex, scaling, beta, updateJacobian ) );

        std::shared_ptr<sdc::SDCFsiSolverInterface> sdcFluidSolver = std::dynamic_pointer_cast<sdc::SDCFsiSolverInterface>( fluid );
        std::shared_ptr<sdc::SDCFsiSolverInterface> sdcSolidSolver = std::dynamic_pointer_cast<sdc::SDCFsiSolverInterface>( solid );

        assert( sdcFluidSolver );
        assert( sdcSolidSolver );

        std::shared_ptr<fsi::SDCFsiSolver> fsiSolver( new fsi::SDCFsiSolver( sdcFluidSolver, sdcSolidSolver, postProcessing, extrapolation ) );

        int nbNodes = 3;

        std::shared_ptr<fsi::quadrature::IQuadrature<scalar> > quadrature;
        quadrature = std::shared_ptr<fsi::quadrature::IQuadrature<scalar> >( new fsi::quadrature::GaussLobatto<scalar>( nbNodes ) );

        std::shared_ptr<sdc::SDC> sdc( new sdc::SDC( fsiSolver, quadrature, 1.0e-13, 10, 200 ) );

        sdc->run();
        ASSERT_TRUE( sdc->isConverged() );

        fluidSolvers.push_back( fluid );
        nbTimeStepsList.push_back( nbTimeSteps );
    }

    for ( int i = 0; i < 2; i++ )
    {
        fsi::vector ref;

        if ( i == 0 )
            ref = fluidSolvers.back()->u;
        else
            ref = fluidSolvers.back()->a;

        std::deque<scalar> errors;

        for ( int iComputation = 0; iComputation < nbComputations - 1; iComputation++ )
        {
            fsi::vector data;

            if ( i == 0 )
                data = fluidSolvers.at( iComputation )->u;
            else
                data = fluidSolvers.at( iComputation )->a;

            scalar error = (ref - data).norm() / data.norm();
            std::cout << "error = " << error << std::endl;
            errors.push_back( error );
        }

        for ( int iComputation = 0; iComputation < nbComputations - 2; iComputation++ )
        {
            scalar order = ( std::log10( errors.at( iComputation ) ) - std::log10( errors.at( iComputation + 1 ) ) ) / ( std::log10( nbTimeStepsList.at( iComputation + 1 ) ) - std::log10( nbTimeStepsList.at( iComputation ) ) );
            std::cout << "order = " << order << std::endl;
            ASSERT_GE( order, 3.8 );
        }
    }
}