//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
//
// $Id: G4BezierSurface.cc,v 1.10 2008/03/13 14:18:57 gcosmo Exp $
// GEANT4 tag $Name: geant4-09-03 $
//
// ----------------------------------------------------------------------
// GEANT 4 class source file
//
// G4BezierSurface.cc
//
// ----------------------------------------------------------------------
// History:
// -------
// - Replaced addition of coordinates by addition of 2 points
//   (L. Broglia, 10/10/98)
// ----------------------------------------------------------------------

#include "G4BezierSurface.hh"
#include "G4ConvexHull.hh"

G4double G4BezierSurface::Tolerance=0;
G4int G4BezierSurface::Clips=0;
G4int G4BezierSurface::Splits=0;


G4BezierSurface::G4BezierSurface()
{
  oslo_m     = (G4OsloMatrix*)0;
  new_knots  = (G4KnotVector*)0;
  old_points = (G4ControlPoints*)0;

  u[0]=0; u[1]=0;
  v[0]=0; v[1]=0;
}

G4BezierSurface::~G4BezierSurface()
{
  delete u_knots;
  delete v_knots;
  delete new_knots;
  delete ctl_points;
  delete old_points;
  
  G4OsloMatrix* temp_oslo = oslo_m;
  
  while(oslo_m != (G4OsloMatrix*)0)
  {
    oslo_m = oslo_m->GetNextNode();
    delete temp_oslo;
    temp_oslo = oslo_m;
  }

  delete oslo_m;
  delete bbox;
}

G4BezierSurface::G4BezierSurface(const G4BezierSurface&)
  : G4Surface()
{
}

G4Vector3D G4BezierSurface::SurfaceNormal(const G4Point3D&) const
{
  return G4Vector3D(0,0,0);
}

G4int G4BezierSurface::ClipBothDirs()
{
  dir = ROW;
  ClipSurface(); 
  
  //   G4cout << "\n CLIP BOTH DIRS  1: " << smin << "  " << smax;

  if(smin > 1.0 || smax < 0.0)
  {
    bezier_list->RemoveSurface(this);
    return 1;
  }
  else
    if((smax - smin) > 0.8)
    {
      SplitNURBSurface();
      return 0;
    }
  
  LocalizeClipValues();
  SetValues();
  
  // Other G4Vector3D clipping and testing.
  dir = COL;
  ClipSurface();
  //    G4cout << "\n CLIP BOTH DIRS  2: " << smin << "  " << smax;
    
  if(smin > 1.0 || smax < 0.0)
  {
    bezier_list->RemoveSurface(this);
    return 1;
  }
  else
    if((smax - smin) > 0.8)
    {
      SplitNURBSurface();
      return 0;
    }

  LocalizeClipValues();    
  SetValues();
  CalcAverage();
  return 1;
}


void G4BezierSurface::CalcBBox()
{
  // Finds the bounds of the 2D-projected nurb iow
  // calculates the bounds for a bounding rectangle
  // to the surface. The bounding rectangle is used
  // for a preliminary check of intersection.
  G4Point3D box_min = G4Point3D(PINFINITY);
  G4Point3D box_max = G4Point3D(-PINFINITY);
 
    
  // Loop to search the whole control point mesh
  // for the minimum and maximum values for.X() and y.
  for(register G4int a = ctl_points->GetRows()-1; a>=0;a--)
    for(register G4int b = ctl_points->GetCols()-1; b>=0;b--)
    {
/* L. Broglia
      G4Point2d& tmp = (G4Point2d&)ctl_points->get(a,b);
      if((box_min.X()) > (tmp.X())) box_min.X(tmp.X());
      if((box_max.X()) < (tmp.X())) box_max.X(tmp.X());	
      if((box_min.Y()) > (tmp.Y())) box_min.Y(tmp.Y());	
      if((box_max.Y()) < (tmp.Y())) box_max.Y(tmp.Y());	
*/
      G4Point3D tmp = ctl_points->Get3D(a,b);
      if((box_min.x()) > (tmp.x())) box_min.setX(tmp.x());
      if((box_max.x()) < (tmp.x())) box_max.setX(tmp.x());	
      if((box_min.y()) > (tmp.y())) box_min.setY(tmp.y());	
      if((box_max.y()) < (tmp.y())) box_max.setY(tmp.y());    
    }
	
  bbox = new G4BoundingBox3D(box_min, box_max);
}


void G4BezierSurface::CalcAverage()
{
  // Calculate the average point from the average clip-values.
  average_u = (u_min + u_max)/2.0;
  average_v = (v_min + v_max)/2.0;    
}


void G4BezierSurface::CalcDistance(const G4Point3D& ray_start)
{
  // Calculate the distance between the average point and
  // the ray starting point.
  distance = ((((ray_start.x() - average_pt.x())*
		(ray_start.x() - average_pt.x()))+
	       ((ray_start.y() - average_pt.y())*
		(ray_start.y() - average_pt.y()))+
	       ((ray_start.z() - average_pt.z())*
		(ray_start.z() - average_pt.z()))));
}


void G4BezierSurface::SetValues()
{
  if(dir)
  {
    v_min = smin;
    v_max = smax;
  }
  else
  {
    u_min = smin;
    u_max = smax;
  }
}

	
G4int G4BezierSurface::BIntersect(G4SurfaceList& bez_list)
{
  bezier_list = &bez_list;
  G4int clip_regions = 0; // Used for tolerance/efficiency-testing
  
  do
  {
    // Calc bbox
    CalcBBox();

    // Test bbox
/* L. Broglia
    bbox->Test2dBBox();
*/
    // bbox->Test();

    // Check result
    if(!bbox->GetTestResult())
      return 0;
    
    // The first clipping has already been Done
    // previously so we continue by doing the
    // actual clip.
    
    // Cut out the clipped region of the surface
    GetClippedRegionFromSurface();
    clip_regions++;
    
    // Calculate the knot vectors and control points
    // for the clipped surface
    RefineSurface();    

    // Gets the u- and v-bounds for the clipped surface
    u_min = u_knots->GetKnot(0);	
    u_max = u_knots->GetKnot(u_knots->GetSize() - 1);	
    v_min = v_knots->GetKnot(0);	
    v_max = v_knots->GetKnot(v_knots->GetSize() - 1); 
    
    // Choose the G4Vector3D for the next() clipping so that
    // the larger side will be clipped.  
    if( (u_max - u_min) < (v_max - v_min) )	
      dir = 1;
    else
      dir = 0;

    // Calculate the clip points
    ClipSurface();
    //	    G4cout << "\n       SMINMAX : " << smin << "  " << smax; 
    
    // The ray intersects with the bounding box
    // but not with the surface itself.   
    if( smin > 1.0 || smax < 0.0 )
    {
      //	    G4cout << "\nG4BezierSurface::Intersect : bezier missed!"; 
      //	    bezier_list->RemoveSurface(this);
      return 0;
    }
    
    if( (smax - smin) > 0.8)
    {
      // Multiple intersections
      //	    G4cout << "\nG4BezierSurface::Intersect : Bezier split.";
      SplitNURBSurface();
      // Now the two new surfaces should also be
      // clipped in both G4Vector3Ds i.e the
      // last and the second last surface
      // in the List. This is Done after returning
      // from this function.
      //	    G4cout << "\n\n  BEZ SPLIT in final Calc! \n\n";

	    
      return 2;
    }
    
    // Calculate the smin and smax values on the
    // b_spline.
    LocalizeClipValues();	
    
    // Check if the size of the remaining surface is within the
    // Tolerance .  
  } while ((u_max - u_min > Tolerance) || (v_max - v_min) > Tolerance);    
  
  SetValues();
  //    G4cout << "\nG4BezierSurface::Intersect :Regions were cut " 
  //           << clip_regions << "  Times.\n";
  
  return 1;
}


void G4BezierSurface::ClipSurface()
{
  // This routine is described in Computer Graphics, Volume 24, 
  // Number 4, August 1990 under the title Ray Tracing Trimmed
  // Rational Surface Patches.
  

  //    G4cout << "\nBezier clip.";
  
  register G4int i,j;
  register G4ConvexHull *ch_ptr=0, *ch_tmp=0, *ch_first=0;
  register G4int col_size = ctl_points->GetCols();
  register G4int row_size = ctl_points->GetRows();
  
  // The four cornerpoints of the controlpoint mesh.

/* L. Broglia
  G4Point2d pt1 = ctl_points->get(0,0);    
  G4Point2d pt2 = ctl_points->get(0,col_size-1);    
  G4Point2d pt3 = ctl_points->get(row_size-1,0);    
  G4Point2d pt4 = ctl_points->get(row_size-1,col_size-1);    
  G4Point2d v1,v2,v3;
*/
  G4Point3D pt1 = ctl_points->Get3D(0,0);    
  G4Point3D pt2 = ctl_points->Get3D(0,col_size-1);    
  G4Point3D pt3 = ctl_points->Get3D(row_size-1,0);    
  G4Point3D pt4 = ctl_points->Get3D(row_size-1,col_size-1);    
  G4Point3D v1,v2,v3;

  if ( dir == ROW)
  {
    // Vectors from cornerpoints
    v1 = (pt1 - pt3);
    //	v1.X() = pt1.X() - pt3.X();
    //	v1.Y() = pt1.Y() - pt3.Y();
    v2 = (pt2 - pt4);
    //	v2.X() = pt2.X() - pt4.X();
    //	v2.Y() = pt2.Y() - pt4.Y();
  } 
  else
  {
    v1 = pt1 - pt2;
    v2 = pt3 - pt4;
    //	v1.X() = pt1.X() - pt2.X();
    //	v1.Y() = pt1.Y() - pt2.Y();
    //	v2.X() = pt3.X() - pt4.X();
    //	v2.Y() = pt3.Y() - pt4.Y();		
  }
/* L. Broglia  
  v3.X(v1.X() + v2.X());
  v3.Y(v1.Y() + v1.Y());
*/
  v3 = v1 + v2 ;
  
  smin =  1.0e8;
  smax = -1.0e8;
  
  G4double norm = std::sqrt(v3.x() * v3.x() + v3.y() * v3.y());
  if(!norm)
  {
    G4cout << "\nNormal zero!";
    G4cout << "\nLINE & DIR: " << line.x() << " " << line.y() << "  " << dir; 
    G4cout << "\n";
    
    if((std::abs(line.x())) > kCarTolerance) 
      line.setX(-line.x());
    else
      if((std::abs(line.y())) > kCarTolerance)
	line.setY(-line.y());
      else
      {
	G4cout << "\n  RETURNING FROm CLIP..";
	smin = 0; smax = 1;
	return;
      }

    G4cout << "\nCHANGED LINE & DIR: " << line.x() << " " 
	   << line.y() << "  " << dir;		
  }
  else
  {
    line.setX( v3.y() / norm);
    line.setY(-v3.x() / norm);
  }

  //    smin =  1.0e8;
  //    smax = -1.0e8;
  //	G4cout << "\n  FINAL LINE & DIR: " << line.X() << " " 
  //           << line.Y() << "  " << dir;	
  
  if( dir == ROW)
  {
    // Create a Convex() hull List 
    for(G4int a = 0; a < col_size; a++)
    {
      ch_ptr = new G4ConvexHull(a/(col_size - 1.0),1.0e8,-1.0e8);
      if(! a) 
      {
	ch_first=ch_ptr;ch_tmp=ch_ptr;
      }
      else ch_tmp->SetNextHull(ch_ptr);
      
      ch_tmp=ch_ptr;
    }
    
    ch_ptr=ch_first;
    register G4double value;
    
    // Loops through the control point mesh and calculates
    // the nvex() hull for the surface.
    
    for( G4int h = 0; h < row_size; h++)
    {
      for(G4int k = 0; k < col_size; k++)
      {
/* L. Broglia
	G4Point2d& coordstmp = (G4Point2d&)ctl_points->get(h,k);  
   	value = - ((coordstmp.X() * line.X() + coordstmp.Y() * line.Y()));
*/
	G4Point3D coordstmp = ctl_points->Get3D(h,k);  
   	value = - ((coordstmp.x() * line.x() + coordstmp.y() * line.y()));

	if( value <= (ch_ptr->GetMin()+kCarTolerance)) ch_ptr->SetMin(value);
	if( value >= (ch_ptr->GetMax()-kCarTolerance)) ch_ptr->SetMax(value);
	    
	ch_ptr=ch_ptr->GetNextHull();
      }
      
      ch_ptr=ch_first;
    }
    
    ch_ptr=ch_first;
    // Finds the points where the nvex() hull intersects
    // with the coordinate .X()is. These points are the
    // minimum and maximum values to where to clip the
    // surface.
    
    for(G4int l = 0; l < col_size - 1; l++)
    {
      ch_tmp=ch_ptr->GetNextHull();
      for(G4int m = l+1; m < col_size; m++)
      {
	register G4double d;
	register G4double param1, param2;
	param1 = ch_ptr->GetParam();
	param2 = ch_tmp->GetParam();
	
	if(ch_tmp->GetMax() - ch_ptr->GetMax())
	{
	  d = Findzero( param1, param2, ch_ptr->GetMax(), ch_tmp->GetMax());
	  if( d <= (smin + kCarTolerance) ) smin = d * .99;
	  if( d >= (smax - kCarTolerance) ) smax = d * .99 + .01;
	}
	
	if(ch_tmp->GetMin() - ch_ptr->GetMin())
	{
	  d = Findzero( param1, param2, ch_ptr->GetMin(), ch_tmp->GetMin());
	  if( d <= (smin + kCarTolerance)) smin = d * .99;
	  if( d >= (smax - kCarTolerance)) smax = d * .99 + .01;
	}
	
	ch_tmp=ch_tmp->GetNextHull();
      }
      
      ch_ptr=ch_ptr->GetNextHull();
    }
    
    ch_ptr=ch_first;

    if (smin <= 0.0)   smin = 0.0;
    if (smax >= 1.0)   smax = 1.0;

    if ( Sign(ch_ptr->GetMin()) != Sign(ch_ptr->GetMax()))  smin = 0.0;
    
    i = Sign(ch_tmp->GetMin()); // ch_tmp points to last nvex()_hull in List
    j = Sign(ch_tmp->GetMax());
    
    if ( std::abs(i-j) > kCarTolerance ) smax = 1.0;
    //	if ( i != j)  smax = 1.0;
    
  } 
  else // Other G4Vector3D
  {
    for(G4int n = 0; n < row_size; n++)
      {
	ch_ptr = new G4ConvexHull(n/(row_size - 1.0),1.0e8,-1.0e8);
	if(!n) 
	{
	  ch_first=ch_ptr;
	  ch_tmp=ch_ptr;
	}
	else ch_tmp->SetNextHull(ch_ptr);
	
	ch_tmp=ch_ptr;
      }
    
    ch_ptr=ch_first;
    
    for( G4int o = 0; o < col_size; o++)
    {
      for(G4int p = 0; p < row_size; p++)
      {
	register G4double value;

/* L. Broglia
	G4Point2d& coordstmp =(G4Point2d&) ctl_points->get(p,o);  	      
	value = - ((coordstmp.X() * line.X() + coordstmp.Y() * line.Y()));
*/
	G4Point3D coordstmp = ctl_points->Get3D(p,o);  	      
	value = - ((coordstmp.x() * line.x() + coordstmp.y() * line.y()));

	if( value <= (ch_ptr->GetMin()+kCarTolerance)) ch_ptr->SetMin(value);
	if( value >= (ch_ptr->GetMax()-kCarTolerance)) ch_ptr->SetMax(value);
	
	ch_ptr=ch_ptr->GetNextHull();
      }

      ch_ptr=ch_first;
    }
    
    ch_ptr=ch_first;
    ch_tmp=ch_first;
    
    for(G4int q = 0; q < row_size - 1; q++)
    {
      ch_tmp=ch_ptr->GetNextHull();
      for(G4int r = q+1; r < row_size; r++)
      {
	register G4double param1 = ch_ptr->GetParam();
	register G4double param2 = ch_tmp->GetParam();
	register G4double d;
	
	if(ch_tmp->GetMax() - ch_ptr->GetMax())
	{
	  d = Findzero( param1, param2, ch_ptr->GetMax(), ch_tmp->GetMax());
	  if( d <= (smin + kCarTolerance) ) smin = d * .99;
	  if( d >= (smax - kCarTolerance) ) smax = d * .99 + .01;
	}

	if(ch_tmp->GetMin()-ch_ptr->GetMin())
	{
	  d = Findzero( param1, param2, ch_ptr->GetMin(), ch_tmp->GetMin());
	  if( d <= (smin + kCarTolerance) ) smin = d * .99;
	  if( d >= (smax - kCarTolerance) ) smax = d * .99 + .01;
	}
	
	ch_tmp=ch_tmp->GetNextHull();
      }

      ch_ptr=ch_ptr->GetNextHull();
      }
    
    ch_tmp=ch_ptr;
    ch_ptr=ch_first;
	
    if (smin <= 0.0)  smin = 0.0;
    if (smax >= 1.0)  smax = 1.0;
    
    if ( Sign(ch_ptr->GetMin()) != Sign(ch_ptr->GetMax())) smin = 0.0;
    
    i = Sign(ch_tmp->GetMin()); // ch_tmp points to last nvex()_hull in List
    j = Sign(ch_tmp->GetMax());

    //
    if ( (std::abs(i-j) > kCarTolerance)) smax = 1.0;
  }

  ch_ptr=ch_first;
  while(ch_ptr!=ch_ptr->GetNextHull())
  {
    ch_tmp=ch_ptr;
    ch_ptr=ch_ptr->GetNextHull();
    delete ch_tmp;
  }

  delete ch_ptr;
  
  // Testing...    
  Clips++; 
}


void G4BezierSurface::GetClippedRegionFromSurface()
{
  // Returns the clipped part of the surface. First calculates the
  // length of the new knotvector. Then uses the refinement function to 
  // get the new knotvector and controlmesh.

  //    G4cout << "\nBezier region clipped.";
    
  delete new_knots;
  if ( dir == ROW) 
  {
    new_knots = new G4KnotVector(GetOrder(0) * 2);
    for (register G4int i = 0; i < GetOrder(0); i++) 
    {
      new_knots->PutKnot(i, smin);
      new_knots->PutKnot(i+ GetOrder(0), smax);
    }
  }
  else
  {
    new_knots = new G4KnotVector( GetOrder(1) * 2);
    for ( register G4int i = 0; i <  GetOrder(1); i++) 
    {
      new_knots->PutKnot(i, smin);
      new_knots->PutKnot(i+ GetOrder(1), smax);
    }
  }
} // NURB_REGION_FROM_SURFACE


void G4BezierSurface::RefineSurface()
{
  // Returns the new clipped surface. Calculates the new controlmesh
  // and knotvectorvalues for the surface by using the Oslo-algorithm
  
  delete old_points;
  if (dir == ROW) 
  {
    // Row (u) G4Vector3D 
    ord = GetOrder(0);
    CalcOsloMatrix();
    for(register G4int a=0;a<new_knots->GetSize();a++)
      u_knots->PutKnot(a, new_knots->GetKnot(a));
	
    lower = 0; 
    upper = new_knots->GetSize() - GetOrder(0);
  
    // Copy of the old points.
    old_points = new G4ControlPoints(*ctl_points);
    MapSurface(this);
  }
  else 	
  {	
    ord = GetOrder(1);
    CalcOsloMatrix ();
    for(register G4int a=0;a < new_knots->GetSize();a++)
      v_knots->PutKnot(a, new_knots->GetKnot(a));
	
    // Copy of the old points.
    old_points = new G4ControlPoints(*ctl_points);
    
    // Make new controlpoint matrix,
    register G4int cols = ctl_points->GetCols();
    delete ctl_points;

    ctl_points = new G4ControlPoints(2,(new_knots->GetSize()-
					GetOrder(1)),cols);   
    lower = 0; 
    upper = new_knots->GetSize() - GetOrder(1);
    MapSurface(this);
  }
}// REFINE_SURFACE


void G4BezierSurface::CalcOsloMatrix()
{
  // This algorithm is described in the paper "Making the Oslo-algorithm
  // more efficient" in SIAM J.NUMER.ANAL. Vol.23, No. 3, June '86
  // Calculates the oslo-matrix , which is used in mapping the new
  // knotvector- and controlpoint-values.
 
  register G4KnotVector *ah;
  register G4KnotVector *newknots;		     
  register G4int         i;
  register G4int         j;
  register G4int         mu, muprim;
  register G4int         vv, p;
  register G4int         iu, il, ih, n1;		
  register G4int         ahi;	
  register G4double      beta1;
  register G4double      tj;

  ah       = new G4KnotVector(ord*(ord + 1)/2);
  newknots = new G4KnotVector(ord * 2 );
  
  n1 = new_knots->GetSize() - ord;
  mu = 0;		
  
  if(oslo_m!=(G4OsloMatrix*)0)
  {
    G4OsloMatrix* tmp;
    
    //	    while(oslo_m!=oslo_m->next)
    while(oslo_m!=(G4OsloMatrix*)0)	    
    {
      tmp=oslo_m->GetNextNode();delete oslo_m; oslo_m=tmp;
    }
  }
	
  delete oslo_m;
  oslo_m = new G4OsloMatrix();
  
  register G4OsloMatrix* o_ptr = oslo_m;
  
  register G4KnotVector* old_knots;
  if(dir)
    old_knots = v_knots;
  else
    old_knots = u_knots;
  
  for (j = 0; j < n1; j++) 
  {
    if ( j != 0 )
    {
      oslo_m->SetNextNode(new G4OsloMatrix());
      oslo_m = oslo_m->GetNextNode();
    }
    
    while (old_knots->GetKnot(mu + 1) <= new_knots->GetKnot(j))
      mu = mu + 1;		// find the bounding mu 
    
    i = j + 1;
    muprim = mu;
    
    while ((new_knots->GetKnot(i) == old_knots->GetKnot(muprim)) && 
	   i < (j + ord)) 
    {
      i++;
      muprim--;
    }
    
    ih = muprim + 1;
    
    for (vv = 0, p = 1; p < ord; p++) 
    {
      if (new_knots->GetKnot(j + p) == old_knots->GetKnot(ih))
	ih++;
      else
	newknots->PutKnot(++vv - 1,new_knots->GetKnot(j + p));
    }
    
    ahi = AhIndex(0, ord - 1,ord);
    ah->PutKnot(ahi, 1.0);
    
    for (p = 1; p <= vv; p++) 
    {
      beta1 = 0.0;
      tj = newknots->GetKnot(p-1);
      
      if (p - 1 >= muprim) 
      {
	beta1 = AhIndex(p - 1, ord - muprim,ord);
	beta1 = ((tj - old_knots->GetKnot(0)) * beta1) /
	  (old_knots->GetKnot(p + ord - vv) - old_knots->GetKnot(0));
      }

      i  = muprim - p + 1;
      il = Amax (1, i);
      i  = n1 - 1 + vv - p;
      iu = Amin (muprim, i);
      
      for (i = il; i <= iu; i++) 
      {
	register G4double d1, d2;
	register G4double beta;
	
	d1 = tj - old_knots->GetKnot(i);
	d2 = old_knots->GetKnot(i + p + ord - vv - 1) - tj;

	beta = ah->GetKnot(AhIndex(p - 1, i + ord - muprim - 1,ord)) / 
	  (d1 + d2);
				
	
	ah->PutKnot(AhIndex(p, i + ord - muprim - 2,ord), d2 * beta + beta1) ; 
	beta1 = d1 * beta;
      }
      
      ah->PutKnot(AhIndex(p, iu + ord - muprim - 1,ord), beta1);

      if (iu < muprim) 
      {
	register G4double kkk;
	register G4double ahv;
	
	kkk = old_knots->GetKnot(n1 - 1 + ord);
	ahv = AhIndex (p - 1, iu + ord - muprim,ord); 
	ah->PutKnot(AhIndex(p, iu + ord - muprim - 1,ord),
		    beta1 + (kkk - tj) * ahv /
		    (kkk - old_knots->GetKnot(iu + 1)));
      }
    }

    // Remove the oslo matrix List
    G4OsloMatrix* temp_oslo = oslo_m;

/*
      if(oslo_m != (G4OsloMatrix*)0)
      while(oslo_m->next != oslo_m)
      {
      oslo_m = oslo_m->next;
      delete temp_oslo;
      temp_oslo = oslo_m;
      }
      
      // Remove the last
      delete oslo_m;
*/

    while(oslo_m != (G4OsloMatrix*)0)
    {
      oslo_m = oslo_m->GetNextNode();
      delete temp_oslo;
      temp_oslo = oslo_m;
    }
    
    delete oslo_m;
    
    // Create a new oslo matrix    
    oslo_m = new G4OsloMatrix(vv+1, Amax(muprim - vv,0), vv);
    
    for ( i = vv, p = 0; i >= 0; i--)
      oslo_m->GetKnotVector()
            ->PutKnot ( p++, ah->GetKnot(AhIndex (vv, (ord-1) - i,ord)));
    
  }

  delete ah;
  delete newknots;
  oslo_m->SetNextNode(0);
  oslo_m = o_ptr;
}


void G4BezierSurface::MapSurface(G4Surface*)
{
  // This algorithm is described in the paper Making the Oslo-algorithm
  // more efficient in SIAM J.NUMER.ANAL. Vol.23, No. 3, June '86
  // Maps the new controlpoints into the new surface.
  
  register G4ControlPoints *c_ptr;
  register G4OsloMatrix *o_ptr;
  register G4ControlPoints* new_pts;
  register G4ControlPoints* old_pts;
  
  new_pts = ctl_points;
  
  // Copy the old points so they can be used in calculating the new ones.
  //  old_pts = new G4ControlPoints(*ctl_points);
  old_pts = old_points;
  register G4int j, 			//	 j loop 
                 i;			//	 oslo loop 

  c_ptr = new_pts;
  register G4int size; // The number of rows or columns, 
                       // depending on processing order

  if(!dir)
    size=new_pts->GetRows();
  else
    size=new_pts->GetCols();

  for(G4int a=0; a<size;a++)
  {
    if ( lower != 0)
    {
      for ( i = 0,  o_ptr = oslo_m; 
	    i < lower; 
	    i++,  o_ptr = o_ptr->GetNextNode()){;}
    }
    else
    {
      o_ptr = oslo_m;
    }
    
    if(!dir)// Direction ROW
    {
      for ( j = lower; j < upper; j++, o_ptr = o_ptr->GetNextNode()) 
      {
	register G4double o_scale;
	register G4int x;
	x=a;

/* L. Broglia	
	G4Point2d o_pts= (G4Point2d&)old_pts->Get2d(x, o_ptr->GetOffset());
	G4Point2d tempc= (G4Point2d&)c_ptr->Get2d(j/upper,(j)%upper-lower);
*/
	G4Point3D o_pts = old_pts->Get3D(x, o_ptr->GetOffset());
	G4Point3D tempc = c_ptr->Get3D(j/upper, (j)%upper-lower);

	o_scale = o_ptr->GetKnotVector()->GetKnot(0);
	
	tempc.setX(o_pts.x() * o_scale);
	tempc.setY(o_pts.x() * o_scale);
	
	for ( i = 1; i <= o_ptr->GetSize(); i++)
	{
	  o_scale = o_ptr->GetKnotVector()->GetKnot(i);

/* L. Broglia
	  o_pts = (G4Point2d&)old_pts->get(x, i+o_ptr->GetOffset());
	  tempc.X(tempc.X() + o_scale * o_pts.X());
	  tempc.Y(tempc.Y() + o_scale * o_pts.Y());
*/
	  o_pts = old_pts->Get3D(x, i+o_ptr->GetOffset());
	  tempc.setX(tempc.x() + o_scale * o_pts.x());
	  tempc.setY(tempc.y() + o_scale * o_pts.y());

	}
	
	c_ptr->put(a,(j)%upper-lower,tempc);		
      }	
    }
    else  // dir = COL
    {
      for ( j = lower; j < upper; j++, o_ptr = o_ptr->GetNextNode())
      {
	register G4double o_scale;
	register G4int x;
	x=a;

/* L. Broglia	
	G4Point2d o_pts= (G4Point2d&)old_pts->Get2d(o_ptr->GetOffset(), x);
	G4Point2d tempc = (G4Point2d&)c_ptr->Get2d((j)%upper-lower,j/upper);
*/
	G4Point3D o_pts = old_pts->Get3D(o_ptr->GetOffset(), x);
	G4Point3D tempc = c_ptr->Get3D((j)%upper-lower,j/upper);

	o_scale = o_ptr->GetKnotVector()->GetKnot(0);
	
	tempc.setX(o_pts.x() * o_scale);
	tempc.setY(o_pts.y() * o_scale);
	
	for ( i = 1; i <= o_ptr->GetSize(); i++)
	{
	  o_scale = o_ptr->GetKnotVector()->GetKnot(i);
/* L. Broglia	
	  o_pts= (G4Point2d&)old_pts->get(i+o_ptr->GetOffset(),a);
*/
	  o_pts= old_pts->Get3D(i+o_ptr->GetOffset(),a);			
	  tempc.setX(tempc.x() + o_scale * o_pts.x());
	  tempc.setY(tempc.y() + o_scale * o_pts.y());
	}
	
	c_ptr->put((j)%upper-lower,a,tempc);		
      }
    }
  }
}


void G4BezierSurface::SplitNURBSurface()
{
  // Divides the surface in two parts. Uses the oslo-algorithm to calculate
  // the new knotvectors and controlpoints for  the subsurfaces.
  
  //    G4cout << "\nBezier splitted.";
  
  register G4double value;
  register G4int i;
  register G4int k_index=0;
  G4BezierSurface *srf1, *srf2;
  G4int nr,nc;
  
  if ( dir == ROW )
  {
    value = u_knots->GetKnot((u_knots->GetSize()-1)/2);
    
    for( i = 0; i < u_knots->GetSize(); i++)
      if( value == u_knots->GetKnot(i) )
      {
	k_index = i;
	break;
      }	

    if ( k_index == 0)
    {
      value = ( value + u_knots->GetKnot(u_knots->GetSize() -1))/2.0;
      k_index = GetOrder(ROW);
    }
	
    new_knots = u_knots->MultiplyKnotVector(GetOrder(ROW), value);
    
    ord = GetOrder(ROW);
    CalcOsloMatrix();
	
    srf1 = new G4BezierSurface(*this);
    //	srf1->dir=ROW;
    srf1->dir=COL;	
    
    new_knots->ExtractKnotVector(srf1->u_knots, k_index +
				 srf1->GetOrder(ROW),0); 

    nr= srf1->v_knots->GetSize() - srf1->GetOrder(COL);
    nc= srf1->u_knots->GetSize() - srf1->GetOrder(ROW);
    delete srf1->ctl_points;
    
    srf1->ctl_points= new G4ControlPoints(2, nr, nc);
    srf2 = new G4BezierSurface(*this);

    //	srf2->dir = ROW;
    srf2->dir = COL;	

    new_knots->ExtractKnotVector(srf2->u_knots, 
				 new_knots->GetSize(), k_index); 
    
    nr= srf2->v_knots->GetSize() - srf2->GetOrder(COL);
    nc= srf2->u_knots->GetSize() - srf2->GetOrder(ROW);
    
    delete  srf2->ctl_points;
    srf2->ctl_points = new G4ControlPoints(2, nr, nc);
    
    lower = 0;
    upper = k_index;
    MapSurface(srf1);
    
    lower = k_index;
    upper = new_knots->GetSize() - srf2->GetOrder(ROW);
    MapSurface(srf2);
  }
  else // G4Vector3D = col
  {
    value = v_knots->GetKnot((v_knots->GetSize() -1)/2);
    
    for( i = 0; i < v_knots->GetSize(); i++)
      if( value == v_knots->GetKnot(i))
      {
	k_index = i;
	break;
      }
    if ( k_index == 0)
    {
      value = ( value + v_knots->GetKnot(v_knots->GetSize() -1))/2.0;
      k_index = GetOrder(COL);
    }
    
    new_knots = v_knots->MultiplyKnotVector( GetOrder(COL), value );
    ord = GetOrder(COL);
    
    CalcOsloMatrix();
    
    srf1 = new G4BezierSurface(*this);
    //	srf1->dir = COL;
    srf1->dir = ROW;
    
    new_knots->ExtractKnotVector(srf1->v_knots, 
				 k_index + srf1->GetOrder(COL), 0);
	
    nr = srf1->v_knots->GetSize() - srf1->GetOrder(COL);
    nc = srf1->u_knots->GetSize() - srf1->GetOrder(ROW);
    
    delete srf1->ctl_points;
    srf1->ctl_points = new G4ControlPoints(2, nr, nc);
    
    srf2 = new G4BezierSurface(*this);
    //	srf2->dir = COL;
    srf2->dir = ROW;
    
    new_knots->ExtractKnotVector(srf2->v_knots, new_knots->GetSize(), k_index);
	
    nr = srf2->v_knots->GetSize() - srf2->GetOrder(COL);
    nc = srf2->u_knots->GetSize() - srf2->GetOrder(ROW);
    
    delete srf2->ctl_points;
    srf2->ctl_points = new G4ControlPoints(2,nr, nc);
    
    lower = 0;
    upper = k_index; 
    MapSurface(srf1);

    //	next->oslo_m = oslo_m;
    lower = k_index;
    upper = new_knots->GetSize() - srf2->GetOrder(COL);
    MapSurface(srf2);
  }
  
  bezier_list->AddSurface(srf1);
  bezier_list->AddSurface(srf2);
  delete new_knots;
  
  // Testing
  Splits++;  
}
