//-----------------------------------------------------------------------------
// computeDualView
//-----------------------------------------------------------------------------
void computeDualView(int a, int b, int c, int d, float tx, float ty, float tz) {
  for (int i=0; i<NSAMPLES; i++)
    for (int j=0; j<NSAMPLES; j++)
      intersection_count[i][j] = 0;
  
  for (int i=0; i<NLINES; i++) 
    if (displayL[i])
      computeDual(lines[i][0], lines[i][1], lines[i][2], lines[i][3], a, b, c, d);

  if (displayV)
    computeDual(v_x[0], v_y[0], v_x[1], v_y[1], a, b, c, d);
  displayDual2();
  displayBox();
}
double RigidBodies3DSobogusInterface::solve( Eigen::VectorXd& r, Eigen::VectorXd& v, unsigned& num_iterations, const unsigned max_threads, const double& tol, const unsigned max_iters, const unsigned eval_every, const bool use_infinity_norm )
{
  assert( m_primal );
  assert( r.size() == 3 * m_primal->H.rowsOfBlocks() );
  assert( v.size() == m_primal->H.cols() );

  // If dual has not been computed yet
  if( !m_dual )
  {
    computeDual();
  }

  // r to local coords
  Eigen::VectorXd r_loc{ m_primal->E.transpose() * r };

  // Setup GS parameters
  bogus::DualFrictionProblem<3u>::GaussSeidelType gs;
  if( tol != 0.0 ) { gs.setTol( tol ); }
  if( max_iters != 0 ) { gs.setMaxIters( max_iters ); }
  assert( eval_every > 0 ); assert( eval_every <= max_iters );
  gs.setEvalEvery( eval_every );
  gs.setMaxThreads( max_threads );
  gs.setAutoRegularization( 0.0 );
  gs.useInfinityNorm( use_infinity_norm );

  // Compute coloring if multithreading
  gs.coloring().update( max_threads > 1, m_dual->W );

  m_dual->undoPermutation();
  if( max_threads > 1 )
  {
    m_dual->applyPermutation( gs.coloring().permutation );
    gs.coloring().resetPermutation();
  }
  m_dual->W.cacheTranspose();

  const bool try_zero{ false };
  const double res{ m_dual->solveWith( gs, r_loc.data(), num_iterations, try_zero ) };

  // Compute the outgoing velocity
  v = m_primal->MInv * ( m_primal->H.transpose() * r_loc - Eigen::VectorXd::Map( m_primal->f, m_primal->H.cols() ) );

  // Convert r to world coords
  r = m_primal->E * r_loc;

  return res;
}
double RigidBodies3DSobogusInterface::evalInfNormError( const Eigen::VectorXd& r )
{
  if( !m_dual )
  {
    computeDual();
  }

  // Convert r to local coords
  assert( m_primal != nullptr );
  const Eigen::VectorXd r_loc{ m_primal->E.transpose() * r };

  // Setup GS parameters
  bogus::DualFrictionProblem<3u>::GaussSeidelType gs;
  gs.useInfinityNorm( true );
  assert( m_dual != nullptr );
  gs.setMatrix( m_dual->W );

  return m_dual->evalWith( gs, r_loc.data() );
}