// File         Graphic3d_ArrayOfPrimitives.cxx
// Created      16/06/2000 : ATS/GG : G005

#define TRACE 0

#include <Graphic3d_ArrayOfPrimitives.ixx>
#include <Standard.hxx>
#include <TCollection_AsciiString.hxx>
#include <OSD_Environment.hxx>

#include <stdio.h>
#include <stdlib.h>

static Standard_Integer enableArray = 1;
static Standard_Boolean interleavedArray = Standard_False;

Graphic3d_ArrayOfPrimitives :: Graphic3d_ArrayOfPrimitives (
                        const Graphic3d_TypeOfPrimitiveArray aType,
                        const Standard_Integer maxVertexs,
                        const Standard_Integer maxBounds,
                        const Standard_Integer maxEdges,
                        const Standard_Boolean hasVNormals,
                        const Standard_Boolean hasVColors,
                        const Standard_Boolean hasFColors,
                        const Standard_Boolean hasVTexels,
                        const Standard_Boolean hasEdgeInfos
) : myMaxBounds(0),myMaxVertexs(0),myMaxEdges(0) {
  Standard_Integer size = sizeof(CALL_DEF_PARRAY);
  Standard_Integer format = MVERTICE;
  if( hasVNormals ) format |= MVNORMAL;
  if( hasVColors ) format |= MVCOLOR;
  if( hasVTexels ) format |= MVTEXEL;
  char *parray=NULL;

#ifdef IARRAY
  if( interleavedArray ) {
    Standard_Integer isize=0;
    if( format == MVERTICE ) {
      isize = sizeof(IV3F) * maxVertexs;
    } else if( format == (MVERTICE | MVNORMAL) ) {
      isize = sizeof(IN3FV3F) * maxVertexs;
    } else if( format == (MVERTICE | MVCOLOR) ) {
      isize = sizeof(IC3FV3F) * maxVertexs;
    } else if( format == (MVERTICE | MVTEXEL) ) {
      isize = sizeof(IT2FV3F) * maxVertexs;
    } else if( format == (MVERTICE | MVCOLOR | MVNORMAL) ) {
      isize = sizeof(IC4FN3FV3F) * maxVertexs;
    } else if( format == (MVERTICE | MVNORMAL | MVTEXEL) ) {
      isize = sizeof(IT2FN3FV3F) * maxVertexs;
    } else if( format == (MVERTICE | MVCOLOR | MVTEXEL) ) {
      isize = sizeof(IT2FC3FV3F) * maxVertexs;
    } else if( format == (MVERTICE | MVCOLOR | MVTEXEL | MVNORMAL) ) {
      isize = sizeof(IT2FC4FN3FV3F) * maxVertexs;
    }
    size += isize;
    size += sizeof(Tint) * maxBounds;
    size += sizeof(Tint) * maxEdges;
    size += sizeof(Tchar) * maxVertexs;
    if( hasEdgeInfos ) size += sizeof(Tchar) * maxEdges;
    myPrimitiveArray = (Graphic3d_PrimitiveArray) Standard::Allocate(size);
    memset ( myPrimitiveArray, 0, size );
    if( myPrimitiveArray ) {
      parray = (char*) myPrimitiveArray + sizeof(CALL_DEF_PARRAY);
      myPrimitiveArray->ivertex.any = parray;
      parray += isize;
    }
  } else 
#endif
  {
    size += sizeof(TEL_POINT) * maxVertexs;
    size += sizeof(Tint) * maxBounds;
    size += sizeof(Tint) * maxEdges;
    size += sizeof(Tchar) * maxVertexs;
    if( hasVNormals ) size += sizeof(TEL_POINT) * maxVertexs;
    if( hasVColors ) size += sizeof(TEL_COLOUR) * maxVertexs;
    if( hasFColors ) size += sizeof(TEL_COLOUR) * maxBounds;
    if( hasVTexels ) size += sizeof(TEL_TEXTURE_COORD) * maxVertexs;
    if( hasEdgeInfos ) size += sizeof(Tchar) * maxEdges;
    myPrimitiveArray = (Graphic3d_PrimitiveArray) Standard::Allocate(size); 
    memset ( myPrimitiveArray, 0, size );
    if( myPrimitiveArray ) {
      parray = (char*) myPrimitiveArray + sizeof(CALL_DEF_PARRAY);
      myPrimitiveArray->vertices = (TEL_POINT*) parray;
      parray += sizeof(TEL_POINT) * maxVertexs;
      if( hasVNormals ) {
        myPrimitiveArray->vnormals = (TEL_POINT*) parray;
        parray += sizeof(TEL_POINT) * maxVertexs;
      }
      if( hasVColors ) {
        myPrimitiveArray->vcolours = (TEL_COLOUR*) parray;
        parray += sizeof(TEL_COLOUR) * maxVertexs;
      }
      if( hasVTexels ) {
        myPrimitiveArray->vtexels = (TEL_TEXTURE_COORD*) parray;
        parray += sizeof(TEL_TEXTURE_COORD) * maxVertexs;
      }
      if( hasFColors && (maxBounds > 0) ) {
        myPrimitiveArray->fcolours = (TEL_COLOUR*) parray;
        parray += sizeof(TEL_COLOUR) * maxBounds;
      }
    }
  }
  if( !myPrimitiveArray ) {
    Graphic3d_InitialisationError::Raise(" BAD Allocation");
  }
  if( maxBounds > 0 ) {
    myPrimitiveArray->bounds = (Tint*) parray;
    parray += sizeof(Tint) * maxBounds;
  }
  if( maxEdges > 0 ) {
    myPrimitiveArray->edges = (Tint*) parray;
    parray += sizeof(Tint) * maxEdges;
  }
  if( hasEdgeInfos && (maxEdges > 0) ) {
    myPrimitiveArray->edge_vis = (Tchar*) parray;
    parray += sizeof(Tchar) * maxEdges;
  }
  myPrimitiveArray->keys = (Tchar*) parray;
  myMaxVertexs = maxVertexs;
  myMaxBounds = maxBounds;
  myMaxEdges = maxEdges;
  myPrimitiveArray->type = (TelPrimitivesArrayType) aType;
  myPrimitiveArray->format = format;
  myPrimitiveArray->num_bounds = 0;
  myPrimitiveArray->num_vertexs = 0;
  myPrimitiveArray->num_edges = 0;
}

void Graphic3d_ArrayOfPrimitives::Destroy (  )
{

    if( myPrimitiveArray ) {
      Standard::Free((void*&)myPrimitiveArray);
#if TRACE > 0
      cout << " Graphic3d_ArrayOfPrimitives::Destroy()" << endl;
#endif
    }
}

void Graphic3d_ArrayOfPrimitives::Enable() {
  enableArray = 1;
}

void Graphic3d_ArrayOfPrimitives::Disable() {
  enableArray = -1;
}

Standard_Boolean Graphic3d_ArrayOfPrimitives::IsEnable() {

  if( enableArray == 0 ) {
    OSD_Environment csf(TCollection_AsciiString("CSF_USE_ARRAY_OF_PRIMITIVES"));
    TCollection_AsciiString value = csf.Value();
    enableArray = -1;
    if( value.Length() > 0 ) {
      if( value.IsIntegerValue() ) {
        enableArray = value.IntegerValue();
        if( enableArray > 1 ) {
          enableArray = 1;
#ifdef IARRAY
          interleavedArray = Standard_True; 
#endif
        } else interleavedArray = Standard_False;
      }
    }
#if TRACE > 0
    if( enableArray > 0 ) {
      if( interleavedArray ) 
        cout << " ! ENABLE to use Interleaved arrays of primitives" << endl;
      else
        cout << " ! ENABLE to use Single arrays of primitives" << endl;
    } else
      cout << " ! DISABLE to use arrays of primitives" << endl;
#endif
  }
  if( enableArray > 0 ) return Standard_True;

  return Standard_False;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
                const gp_Pnt& aVertice) {
  Standard_Real x,y,z;
  aVertice.Coord(x,y,z);
  return AddVertex(x,y,z);
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
        const Standard_Real X, const Standard_Real Y, const Standard_Real Z) {

  if( !myPrimitiveArray ) return 0;

  Standard_Integer index = myPrimitiveArray->num_vertexs + 1;
  if( index > myMaxVertexs ) {
    Standard_OutOfRange::Raise(" TOO many VERTEX");
  }
  SetVertice(index,X,Y,Z);
  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
                const gp_Pnt& aVertice,
                const Quantity_Color& aColor) {
  Standard_Real x,y,z;
  aVertice.Coord(x,y,z);
  Standard_Integer index = AddVertex(x,y,z);
  Standard_Real r,g,b;
  aColor.Values(r,g,b,Quantity_TOC_RGB);
  SetVertexColor(index,r,g,b);
  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
                const gp_Pnt& aVertice,
                const gp_Dir& aNormal) {
  Standard_Real x,y,z;
  aVertice.Coord(x,y,z);
  Standard_Real nx,ny,nz;
  aNormal.Coord(nx,ny,nz);
  return AddVertex(x,y,z,nx,ny,nz);
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
        const Standard_Real X, const Standard_Real Y, const Standard_Real Z,
        const Standard_Real NX, const Standard_Real NY, const Standard_Real NZ) {
  if( !myPrimitiveArray ) return 0;

  Standard_Integer index = myPrimitiveArray->num_vertexs + 1;
  if( index > myMaxVertexs ) {
    Standard_OutOfRange::Raise(" TOO many VERTEX");
  }
  SetVertice(index,X,Y,Z);
  SetVertexNormal(index,NX,NY,NZ);
  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
                const gp_Pnt& aVertice,
                const gp_Dir& aNormal,
                const Quantity_Color& aColor) {
  Standard_Real x,y,z;
  aVertice.Coord(x,y,z);
  Standard_Real nx,ny,nz;
  aNormal.Coord(nx,ny,nz);
  Standard_Integer index = AddVertex(x,y,z,nx,ny,nz);
  Standard_Real r,g,b;
  aColor.Values(r,g,b,Quantity_TOC_RGB);
  SetVertexColor(index,r,g,b);
  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
                const gp_Pnt& aVertice,
                const gp_Pnt2d& aTexel) {
  Standard_Real x,y,z;
  aVertice.Coord(x,y,z);
  Standard_Real tx,ty;
  aTexel.Coord(tx,ty);
  return AddVertex(x,y,z,tx,ty);
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
        const Standard_Real X, const Standard_Real Y, const Standard_Real Z,
        const Standard_Real TX, const Standard_Real TY) {
  if( !myPrimitiveArray ) return 0;

  Standard_Integer index = myPrimitiveArray->num_vertexs + 1;
  if( index > myMaxVertexs ) {
    Standard_OutOfRange::Raise(" TOO many VERTEX");
  }
  SetVertice(index,X,Y,Z);
  SetVertexTexel(index,TX,TY);
  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
                const gp_Pnt& aVertice,
                const gp_Dir& aNormal,
                const gp_Pnt2d& aTexel) {
  Standard_Real x,y,z;
  aVertice.Coord(x,y,z);
  Standard_Real nx,ny,nz;
  aNormal.Coord(nx,ny,nz);
  Standard_Real tx,ty;
  aTexel.Coord(tx,ty);
  return AddVertex(x,y,z,nx,ny,nz,tx,ty);
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddVertex(
        const Standard_Real X, const Standard_Real Y, const Standard_Real Z,
        const Standard_Real NX, const Standard_Real NY, const Standard_Real NZ,
        const Standard_Real TX, const Standard_Real TY) {
  if( !myPrimitiveArray ) return 0;

  Standard_Integer index = myPrimitiveArray->num_vertexs + 1;
  if( index > myMaxVertexs ) {
    Standard_OutOfRange::Raise(" TOO many VERTEX");
  }
  SetVertice(index,X,Y,Z);
  SetVertexNormal(index,NX,NY,NZ);
  SetVertexTexel(index,TX,TY);
  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddBound(
                const Standard_Integer edgeNumber
) {
  Standard_Integer index = 0;
  if( myPrimitiveArray && myPrimitiveArray->bounds ) {
    index = myPrimitiveArray->num_bounds;
    if( index < myMaxBounds ) {
      myPrimitiveArray->bounds[index] = edgeNumber;
      myPrimitiveArray->num_bounds = ++index;
    } else {
      Standard_OutOfRange::Raise(" TOO many BOUNDS");
    }
  }

  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddBound(
                const Standard_Integer edgeNumber,
                const Quantity_Color& aFColor
) {
  Standard_Real r,g,b;
  aFColor.Values(r,g,b,Quantity_TOC_RGB);
  return AddBound(edgeNumber,r,g,b);
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddBound(
        const Standard_Integer edgeNumber,
        const Standard_Real R, const Standard_Real G, const Standard_Real B) { 
  if( !myPrimitiveArray ) return 0;

  Standard_Integer index = myPrimitiveArray->num_bounds;
  if( index >= myMaxBounds ) {
    Standard_OutOfRange::Raise(" TOO many BOUND");
  }
  myPrimitiveArray->bounds[index] = edgeNumber;
  myPrimitiveArray->num_bounds = ++index;
  SetBoundColor(index,R,G,B);
  return index;
}

Standard_Integer Graphic3d_ArrayOfPrimitives::AddEdge(
                const Standard_Integer vertexIndex,
                const Standard_Boolean isVisible) {
  if( !myPrimitiveArray ) return 0;

  Standard_Integer index = myPrimitiveArray->num_edges;
  if( index >= myMaxEdges ) {
    Standard_OutOfRange::Raise(" TOO many EDGE");
  }
  Standard_Integer vindex = vertexIndex-1;
  if( vertexIndex > 0 && vindex < myMaxVertexs ) {
    myPrimitiveArray->edges[index] = vindex;
    if( myPrimitiveArray->edge_vis ) {
      myPrimitiveArray->edge_vis[index] = (Tchar) (isVisible ? 1 : 0);
    }
    myPrimitiveArray->num_edges = ++index;
  } else {
    Standard_OutOfRange::Raise(" BAD EDGE vertex index");
  }

  return index;
}

Standard_Boolean Graphic3d_ArrayOfPrimitives::Orientate(
                        const gp_Dir& aNormal) {
  return Orientate(1,Max(VertexNumber(),EdgeNumber()),aNormal);
}

Standard_Boolean Graphic3d_ArrayOfPrimitives::Orientate(
                        const Standard_Integer aVertexIndex,
                        const Standard_Integer aVertexNumber,
                        const gp_Dir& aNormal) {
  Standard_Boolean somethingHasChange = Standard_False;
  if( myPrimitiveArray && (myPrimitiveArray->num_vertexs > 2) ) {
    Standard_Integer i,j,k=aVertexNumber,n=aVertexIndex-1;
    Standard_ShortReal x,y,z;
    if( myPrimitiveArray->edges ) {
      if( n >= 0 && (n+k) <= myPrimitiveArray->num_edges ) {
        Standard_Integer i1 = myPrimitiveArray->edges[n];
        Standard_Integer i2 = myPrimitiveArray->edges[n+1];
        Standard_Integer i3 = myPrimitiveArray->edges[n+2];
        gp_Pnt p1(myPrimitiveArray->vertices[i1].xyz[0],
                  myPrimitiveArray->vertices[i1].xyz[1],
                  myPrimitiveArray->vertices[i1].xyz[2]);
        gp_Pnt p2(myPrimitiveArray->vertices[i2].xyz[0],
                  myPrimitiveArray->vertices[i2].xyz[1],
                  myPrimitiveArray->vertices[i2].xyz[2]);
        gp_Pnt p3(myPrimitiveArray->vertices[i3].xyz[0],
                  myPrimitiveArray->vertices[i3].xyz[1],
                  myPrimitiveArray->vertices[i3].xyz[2]);
        gp_Vec v21(p1,p2),v31(p1,p3),fn = v21.Crossed(v31);
        if( aNormal.IsOpposite(fn,Standard_PI/4.) ) {
          Standard_Integer e; char v;
          for( i=0,j=k-1 ; i<k/2 ; i++,j-- ) {
            e = myPrimitiveArray->edges[n+i];
            myPrimitiveArray->edges[n+i] = myPrimitiveArray->edges[n+j];
            myPrimitiveArray->edges[n+j] = e;
            if( myPrimitiveArray->edge_vis ) {
              v = myPrimitiveArray->edge_vis[n+i];
              myPrimitiveArray->edge_vis[n+i] = myPrimitiveArray->edge_vis[n+j];
              myPrimitiveArray->edge_vis[n+j] = v;
            }
            if( myPrimitiveArray->vnormals ) {
              e = myPrimitiveArray->edges[n+i];
              x = myPrimitiveArray->vnormals[e].xyz[0];
              y = myPrimitiveArray->vnormals[e].xyz[1];
              z = myPrimitiveArray->vnormals[e].xyz[2];
              gp_Vec vn(x,y,z);
              if( aNormal.IsOpposite(vn,Standard_PI/4.) ) {
                myPrimitiveArray->vnormals[e].xyz[0] = -x;
                myPrimitiveArray->vnormals[e].xyz[1] = -y;
                myPrimitiveArray->vnormals[e].xyz[2] = -z;
              }
            }
          }
          somethingHasChange = Standard_True;
        }
      } else {
        Standard_OutOfRange::Raise(" BAD EDGE index or number");
      }
      return somethingHasChange;
    } 

    if( n >= 0 && (n+k) <= myPrimitiveArray->num_vertexs ) {
      gp_Pnt p1(myPrimitiveArray->vertices[n].xyz[0],
                  myPrimitiveArray->vertices[n].xyz[1],
                  myPrimitiveArray->vertices[n].xyz[2]);
      gp_Pnt p2(myPrimitiveArray->vertices[n+1].xyz[0],
                  myPrimitiveArray->vertices[n+1].xyz[1],
                  myPrimitiveArray->vertices[n+1].xyz[2]);
      gp_Pnt p3(myPrimitiveArray->vertices[n+2].xyz[0],
                  myPrimitiveArray->vertices[n+2].xyz[1],
                  myPrimitiveArray->vertices[n+2].xyz[2]);
      gp_Vec v21(p1,p2),v31(p1,p3),fn = v21.Crossed(v31);
      if( aNormal.IsOpposite(fn,Standard_PI/4.) ) {
        for( i=0,j=k-1 ; i<k/2 ; i++,j-- ) {
          x = myPrimitiveArray->vertices[n+i].xyz[0];
          y = myPrimitiveArray->vertices[n+i].xyz[1];
          z = myPrimitiveArray->vertices[n+i].xyz[2];
          myPrimitiveArray->vertices[n+i].xyz[0] = myPrimitiveArray->vertices[n+j].xyz[0];
          myPrimitiveArray->vertices[n+i].xyz[1] = myPrimitiveArray->vertices[n+j].xyz[1];
          myPrimitiveArray->vertices[n+i].xyz[2] = myPrimitiveArray->vertices[n+j].xyz[2];
          myPrimitiveArray->vertices[n+j].xyz[0] = x;
          myPrimitiveArray->vertices[n+j].xyz[1] = y;
          myPrimitiveArray->vertices[n+j].xyz[2] = z;
          if( myPrimitiveArray->vnormals ) {
            x = myPrimitiveArray->vnormals[n+i].xyz[0];
            y = myPrimitiveArray->vnormals[n+i].xyz[1];
            z = myPrimitiveArray->vnormals[n+i].xyz[2];
            myPrimitiveArray->vnormals[n+i].xyz[0] = myPrimitiveArray->vnormals[n+j].xyz[0];
            myPrimitiveArray->vnormals[n+i].xyz[1] = myPrimitiveArray->vnormals[n+j].xyz[1];
            myPrimitiveArray->vnormals[n+i].xyz[2] = myPrimitiveArray->vnormals[n+j].xyz[2];
            myPrimitiveArray->vnormals[n+j].xyz[0] = x;
            myPrimitiveArray->vnormals[n+j].xyz[1] = y;
            myPrimitiveArray->vnormals[n+j].xyz[2] = z;

            x = myPrimitiveArray->vnormals[n+i].xyz[0];
            y = myPrimitiveArray->vnormals[n+i].xyz[1];
            z = myPrimitiveArray->vnormals[n+i].xyz[2];
            gp_Vec vn(x,y,z);
            if( aNormal.IsOpposite(vn,Standard_PI/4.) ) {
              myPrimitiveArray->vnormals[n+i].xyz[0] = -x;
              myPrimitiveArray->vnormals[n+i].xyz[1] = -y;
              myPrimitiveArray->vnormals[n+i].xyz[2] = -z;
            }
          }
          if( myPrimitiveArray->vcolours ) {
            x = myPrimitiveArray->vcolours[n+i].rgb[0];
            y = myPrimitiveArray->vcolours[n+i].rgb[1];
            z = myPrimitiveArray->vcolours[n+i].rgb[2];
            myPrimitiveArray->vcolours[n+i].rgb[0] = myPrimitiveArray->vcolours[n+j].rgb[0];
            myPrimitiveArray->vcolours[n+i].rgb[1] = myPrimitiveArray->vcolours[n+j].rgb[1];
            myPrimitiveArray->vcolours[n+i].rgb[2] = myPrimitiveArray->vcolours[n+j].rgb[2];
            myPrimitiveArray->vcolours[n+j].rgb[0] = x;
            myPrimitiveArray->vcolours[n+j].rgb[1] = y;
            myPrimitiveArray->vcolours[n+j].rgb[2] = z;
          }
          if( myPrimitiveArray->vtexels ) {
            x = myPrimitiveArray->vtexels[n+i].xy[0];
            y = myPrimitiveArray->vtexels[n+i].xy[1];
            myPrimitiveArray->vtexels[n+i].xy[0] = myPrimitiveArray->vtexels[n+j].xy[0];
            myPrimitiveArray->vtexels[n+i].xy[1] = myPrimitiveArray->vtexels[n+j].xy[1];
            myPrimitiveArray->vtexels[n+j].xy[0] = x;
            myPrimitiveArray->vtexels[n+j].xy[1] = y;
          }
        }
        somethingHasChange = Standard_True;
      }
    }
  }
  return somethingHasChange;
}

Standard_Boolean Graphic3d_ArrayOfPrimitives::Orientate(
                        const Standard_Integer aBoundIndex,
                        const gp_Dir& aNormal) {
  Standard_Boolean somethingHasChange = Standard_False;
  if( myPrimitiveArray && myPrimitiveArray->vertices ) {
    if( myPrimitiveArray->bounds && 
        (aBoundIndex > 0) && (aBoundIndex <= myPrimitiveArray->num_bounds) ) {
      Standard_Integer k,n;
      for( k=n=1 ; k<aBoundIndex ; k++ )
        n += myPrimitiveArray->bounds[k];
      k = myPrimitiveArray->bounds[aBoundIndex-1];
      somethingHasChange = Orientate(n,k,aNormal);
    } else if( myPrimitiveArray->bounds ) {
      Standard_OutOfRange::Raise(" BAD BOUND index");
    } else if( (aBoundIndex > 0) && (aBoundIndex <= ItemNumber()) ) {
      switch( myPrimitiveArray->type ) {
        case TelPointsArrayType:
        case TelPolylinesArrayType:
        case TelSegmentsArrayType:
          break;
        case TelPolygonsArrayType:
        case TelTriangleStripsArrayType:
        case TelTriangleFansArrayType:
        case TelQuadrangleStripsArrayType:
          somethingHasChange = Orientate(1,VertexNumber(),aNormal);
          break;
        case TelTrianglesArrayType:
          somethingHasChange = Orientate(aBoundIndex*3-2,3,aNormal);
          break;
        case TelQuadranglesArrayType:
          somethingHasChange = Orientate(aBoundIndex*4-3,4,aNormal);
          break;
        default:
          break;
      }
    } else {
      Standard_OutOfRange::Raise(" BAD ITEM index");
    }
  }
  return somethingHasChange;
}

void Graphic3d_ArrayOfPrimitives::SetVertice(
                        const Standard_Integer anIndex,
                        const gp_Pnt& aVertice) {
  Standard_Real x,y,z;
  aVertice.Coord(x,y,z);
  SetVertice(anIndex,x,y,z);
}

void Graphic3d_ArrayOfPrimitives::SetVertexColor(
                        const Standard_Integer anIndex,
                        const Quantity_Color& aColor) {
  Standard_Real r,g,b;
  aColor.Values(r,g,b,Quantity_TOC_RGB);
  SetVertexColor(anIndex,r,g,b);
}

void Graphic3d_ArrayOfPrimitives::SetVertexNormal(
                        const Standard_Integer anIndex,
                        const gp_Dir& aNormal) {
  Standard_Real x,y,z;
  aNormal.Coord(x,y,z);
  SetVertexNormal(anIndex,x,y,z);
}

void Graphic3d_ArrayOfPrimitives::SetVertexTexel(
                        const Standard_Integer anIndex,
                        const gp_Pnt2d& aTexel) {
  Standard_Real x,y;
  aTexel.Coord(x,y);
  SetVertexTexel(anIndex,x,y);
}

void Graphic3d_ArrayOfPrimitives::SetBoundColor(
                        const Standard_Integer anIndex,
                        const Quantity_Color& aColor) {
  Standard_Real r,g,b;
  aColor.Values(r,g,b,Quantity_TOC_RGB);
  SetBoundColor(anIndex,r,g,b);
}

Standard_CString Graphic3d_ArrayOfPrimitives::StringType() const {
  TCollection_AsciiString name("UndefinedArray");
  switch( myPrimitiveArray->type ) {
    case TelPointsArrayType:
      name = "ArrayOfPoints";
      break;
    case TelPolylinesArrayType:
      name = "ArrayOfPolylines";
      break;
    case TelSegmentsArrayType:
      name = "ArrayOfSegments";
      break;
    case TelPolygonsArrayType:
      name = "ArrayOfPolygons";
      break;
    case TelTrianglesArrayType:
      name = "ArrayOfTriangles";
      break;
    case TelQuadranglesArrayType:
      name = "ArrayOfQuadrangles";
      break;
    case TelTriangleStripsArrayType:
      name = "ArrayOfTriangleStrips";
      break;
    case TelQuadrangleStripsArrayType:
      name = "ArrayOfQuadrangleStrips";
      break;
    case TelTriangleFansArrayType:
      name = "ArrayOfTriangleFans";
      break;
    default:
      break;
  }

  return name.ToCString();
}

gp_Pnt Graphic3d_ArrayOfPrimitives::Vertice(const Standard_Integer aRank) const {
  Standard_Real x,y,z;
  Vertice(aRank,x,y,z);
  return gp_Pnt(x,y,z);
}

Quantity_Color Graphic3d_ArrayOfPrimitives::VertexColor(const Standard_Integer aRank) const {
  Standard_Real r,g,b;
  VertexColor(aRank,r,g,b);
  return Quantity_Color(r,g,b,Quantity_TOC_RGB);
}

gp_Dir Graphic3d_ArrayOfPrimitives::VertexNormal(const Standard_Integer aRank) const {
  Standard_Real x,y,z;
  VertexNormal(aRank,x,y,z);
  return gp_Dir(x,y,z);
}

gp_Pnt2d Graphic3d_ArrayOfPrimitives::VertexTexel(const Standard_Integer aRank) const {
  Standard_Real x,y;
  VertexTexel(aRank,x,y);
  return gp_Pnt2d(x,y);
}

Quantity_Color Graphic3d_ArrayOfPrimitives::BoundColor(const Standard_Integer aRank) const {
  Standard_Real r,g,b;
  BoundColor(aRank,r,g,b);
  return Quantity_Color(r,g,b,Quantity_TOC_RGB);
}

Standard_Integer Graphic3d_ArrayOfPrimitives::ItemNumber() const {
  Standard_Integer number=-1;
  if( myPrimitiveArray ) switch( myPrimitiveArray->type ) {
    case TelPointsArrayType:
      number = myPrimitiveArray->num_vertexs;
      break;
    case TelPolylinesArrayType:
    case TelPolygonsArrayType:
      if( myPrimitiveArray->num_bounds > 0 ) 
        number = myPrimitiveArray->num_bounds;
      else number = 1;
      break;
    case TelSegmentsArrayType:
      if( myPrimitiveArray->num_edges > 0 ) 
        number = myPrimitiveArray->num_edges/2;
      else number = myPrimitiveArray->num_vertexs/2;
      break;
    case TelTrianglesArrayType:
      if( myPrimitiveArray->num_edges > 0 ) 
        number = myPrimitiveArray->num_edges/3;
      else number = myPrimitiveArray->num_vertexs/3;
      break;
    case TelQuadranglesArrayType:
      if( myPrimitiveArray->num_edges > 0 ) 
        number = myPrimitiveArray->num_edges/4;
      else number = myPrimitiveArray->num_vertexs/4;
      break;
    case TelTriangleStripsArrayType:
      if( myPrimitiveArray->num_bounds > 0 ) 
        number = myPrimitiveArray->num_vertexs-2*myPrimitiveArray->num_bounds;
      else number = myPrimitiveArray->num_vertexs-2;
      break;
    case TelQuadrangleStripsArrayType:
      if( myPrimitiveArray->num_bounds > 0 ) 
        number = myPrimitiveArray->num_vertexs/2-myPrimitiveArray->num_bounds;
      else number = myPrimitiveArray->num_vertexs/2-1;
      break;
    case TelTriangleFansArrayType:
      if( myPrimitiveArray->num_bounds > 0 ) 
        number = myPrimitiveArray->num_vertexs-2*myPrimitiveArray->num_bounds;
      else number = myPrimitiveArray->num_vertexs-2;
      break;
    default:
      break;
  }

  return number;
}

void Graphic3d_ArrayOfPrimitives::ComputeVNormals(
                        const Standard_Integer from,
                        const Standard_Integer to) {
  Standard_Integer next = from+1;
  Standard_Integer last = to+1;
  gp_Pnt p1,p2,p3;

  if( myMaxEdges > 0 ) {
    p1 = Vertice(Edge(next++));
    p2 = Vertice(Edge(next++));
  } else {
    p1 = Vertice(next++);
    p2 = Vertice(next++);
  }

  gp_Vec vn;
  
  while ( next <= last ) {
    if( myMaxEdges > 0 ) {
      p3 = Vertice(Edge(next));
    } else {
      p3 = Vertice(next);
    }
    gp_Vec v21(p2,p1);
    gp_Vec v31(p3,p1);
    vn = v21 ^ v31;
    if( vn.SquareMagnitude() > 0. ) break;
    next++;
  }

  if( next > last ) {
#if TRACE > 0
    cout << " An item has a NULL computed facet normal" << endl;
#endif
    return;
  }

  vn.Normalize();
  if( myMaxEdges > 0 ) {
    for( int i=from+1 ; i<=to+1 ; i++ ) {
      SetVertexNormal(Edge(i),vn);
    }
  } else {
    for( int i=from+1 ; i<=to+1 ; i++ ) {
      SetVertexNormal(i,vn);
    }
  }
}

Standard_Boolean Graphic3d_ArrayOfPrimitives::IsValid() {

  if( !myPrimitiveArray ) return Standard_False;

  Standard_Integer nvertexs = myPrimitiveArray->num_vertexs;
  Standard_Integer nbounds = myPrimitiveArray->num_bounds;
  Standard_Integer nedges = myPrimitiveArray->num_edges;
  Standard_Integer i,j,k,n;

#if TRACE > 0
  Standard_CString name = StringType();
  cout << " !!! An " << name << " has " << ItemNumber() << " items" << endl;
#endif

  switch( myPrimitiveArray->type ) {
    case TelPointsArrayType:
      if( nvertexs < 1 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        return Standard_False;
      }
      break;
    case TelPolylinesArrayType:
      if( nedges > 0 && nedges < 2 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of edges " << nedges << endl;
#endif
        return Standard_False;
      }
      if( nvertexs < 2 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        return Standard_False;
      }
      break;
    case TelSegmentsArrayType:
      if( nvertexs < 2 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        return Standard_False;
      }
      break;
    case TelPolygonsArrayType:
      if( nedges > 0 && nedges < 3 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of edges " << nedges << endl;
#endif
        return Standard_False;
      }
      if( nvertexs < 3 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        return Standard_False;
      }
      break;
    case TelTrianglesArrayType:
      if( nedges > 0 ) {
        if( nedges < 3 || nedges % 3 != 0 ) {
#if TRACE > 0
          cout << " *** An " << name << " is unavailable with a too lower number of edges " << nedges << endl;
#endif
          if( nedges > 3 ) myPrimitiveArray->num_edges = 3 * (nedges / 3);
          else return Standard_False;
        }
      } else if( nvertexs < 3 || nvertexs % 3 != 0 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        if( nvertexs > 3 ) myPrimitiveArray->num_vertexs = 3 * (nvertexs / 3);
        else return Standard_False;
      }
      break;
    case TelQuadranglesArrayType:
      if( nedges > 0 ) {
        if( nedges < 4 || nedges % 4 != 0 ) {
#if TRACE > 0
          cout << " *** An " << name << " is unavailable with a too lower number of edges " << nedges << endl;
#endif
          if( nedges > 4 ) myPrimitiveArray->num_edges = 4 * (nedges / 4);
          else return Standard_False;
        }
      } else if( nvertexs < 4 || nvertexs % 4 != 0 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        if( nvertexs > 4 ) myPrimitiveArray->num_vertexs = 4 * (nvertexs / 4);
        else return Standard_False;
      }
      break;
    case TelTriangleFansArrayType:
    case TelTriangleStripsArrayType:
      if( nvertexs < 3 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        return Standard_False;
      }
      break;
    case TelQuadrangleStripsArrayType:
      if( nvertexs < 4 ) {
#if TRACE > 0
        cout << " *** An " << name << " is unavailable with a too lower number of vertex " << nvertexs << endl;
#endif
        return Standard_False;
      }
      break;
    default:
#if TRACE > 0
      cout << " *** UNKNOWN Array of primitives type found" << endl;
#endif
      return Standard_False;
  }

  if( nbounds > 0 ) {
    for( i=n=0 ; i<nbounds ; i++ ) {
      n += myPrimitiveArray->bounds[i];
    }
    if( nedges > 0 && n != nedges ) {
#if TRACE > 0
      cout << " *** An " << name << " has an incoherent number of edges " << nedges << endl;
#endif
      if( nedges > n ) myPrimitiveArray->num_edges = n;
      else return Standard_False;
    } else if( n != nvertexs ) {
#if TRACE > 0
      cout << " *** An " << name << " has an incoherent number of vertexs " << nvertexs << endl;
#endif
      if( nvertexs > n ) myPrimitiveArray->num_vertexs = n;
      else return Standard_False;
    }
  }

  if( nedges > 0 ) {
    for( i=0 ; i<nedges ; i++ ) {
      if( myPrimitiveArray->edges[i] >= myPrimitiveArray->num_vertexs ) {
#if TRACE > 0
        cout << " *** An " << name << " has a vertex index " << myPrimitiveArray->edges[i] << " greater than the number of defined vertexs " << myPrimitiveArray->num_vertexs << endl;
#endif
        myPrimitiveArray->edges[i] = myPrimitiveArray->num_vertexs-1;
      }
    }
  }

  Standard_Integer format;
  Standard_Boolean valid = Standard_True;
  for( i=0 ; i<nvertexs ; i++ ) {
    format = myPrimitiveArray->keys[i];
    if( format != myPrimitiveArray->format ) {
      valid = Standard_False;
#if TRACE > 0
      cout << " *** An " << name << " has an incoherent kind of vertexs " 
        << i+1 << " of type " << myPrimitiveArray->format << "/" << format << endl;
#endif
      break;
    }
  }

  if( valid ) return Standard_True;

  if( nbounds > 0 ) 
  {
    for( j=n=0 ; j<nbounds ; j++ ) 
    {
      k = myPrimitiveArray->bounds[j];
      for( i=0 ; i<k ; i++ ) 
      {
        format = myPrimitiveArray->keys[n+i];
        if( format != myPrimitiveArray->format ) 
        {
          if( myPrimitiveArray->format & MVNORMAL ) switch( myPrimitiveArray->type ) 
          {
      case TelPolygonsArrayType:
        if( i == 0 ) ComputeVNormals(n+i,n+k-1);
        break;
      case TelTrianglesArrayType:
        if( !(i % 3) ) ComputeVNormals(n+i,n+i+2);
        break;
      case TelQuadranglesArrayType:
        if( !(i % 4) ) ComputeVNormals(n+i,n+i+3);
        break;
      case TelTriangleFansArrayType:
      case TelTriangleStripsArrayType:
        if( i < k-2 ) ComputeVNormals(n+i,n+i+2);
        else
          break;
      case TelQuadrangleStripsArrayType:
        if( !(i %2) && (i < k-3) ) ComputeVNormals(n+i,n+i+3);
        break;
      default:
        break;
          }
        }
      }
      n += myPrimitiveArray->bounds[j];
    }
  } else if( nedges > 0 ) {
    for( i=0 ; i<nedges ; i++ ) {
      k = myPrimitiveArray->edges[i];
      format = myPrimitiveArray->keys[k];
      if( format != myPrimitiveArray->format ) {
        if( myPrimitiveArray->format & MVNORMAL ) switch( myPrimitiveArray->type ) {
          case TelPolygonsArrayType:
            if( i == 0 ) ComputeVNormals(i,nedges-1);
            break;
          case TelTrianglesArrayType:
            k = i/3*3;
            ComputeVNormals(k,k+2);
            break;
          case TelQuadranglesArrayType:
            k = i/4*4;
            ComputeVNormals(k,k+3);
            break;
          case TelTriangleFansArrayType:
          case TelTriangleStripsArrayType:
            if( i < nedges-2 ) ComputeVNormals(i,i+2);
            else
            break;
          case TelQuadrangleStripsArrayType:
            if( !(i %2) && (i < nedges-3) ) ComputeVNormals(i,i+3);
            break;
          default:
            break;
        }
      }
    }
  } else if( nvertexs > 0 ) {
    for( i=0 ; i<nvertexs ; i++ ) {
      format = myPrimitiveArray->keys[i];
      if( format != myPrimitiveArray->format ) {
        if( myPrimitiveArray->format & MVNORMAL ) switch( myPrimitiveArray->type ) {
          case TelPolygonsArrayType:
            if( i == 0 ) ComputeVNormals(i,nvertexs-1);
            break;
          case TelTrianglesArrayType:
            if( !(i % 3) ) ComputeVNormals(i,i+2);
            break;
          case TelQuadranglesArrayType:
            if( !(i % 4) ) ComputeVNormals(i,i+3);
            break;
          case TelTriangleFansArrayType:
          case TelTriangleStripsArrayType:
            if( i < nvertexs-2 ) ComputeVNormals(i,i+2);
            else
            break;
          case TelQuadrangleStripsArrayType:
            if( !(i %2) && (i < nvertexs-3) ) ComputeVNormals(i,i+3);
            break;
          default:
            break;
        }
      }
    }
  }

  return Standard_True;
}
