// Filename: nurbsSurfaceEvaluator.I
// Created by:  drose (10Oct03)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::set_u_order
//       Access: Published
//  Description: Sets the order of the surface in the U direction.
//               This resets the knot vector to the default knot
//               vector for the number of vertices.
//
//               The order must be 1, 2, 3, or 4, and the value is one
//               more than the degree of the surface.
////////////////////////////////////////////////////////////////////
INLINE void NurbsSurfaceEvaluator::
set_u_order(int u_order) {
  _u_order = u_order;
  _u_knots_dirty = true;
  _u_basis_dirty = true;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_u_order
//       Access: Published
//  Description: Returns the order of the surface in the U direction
//               as set by a previous call to set_u_order().
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_u_order() const {
  return _u_order;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::set_v_order
//       Access: Published
//  Description: Sets the order of the surface in the V direction.
//               This resets the knot vector to the default knot
//               vector for the number of vertices.
//
//               The order must be 1, 2, 3, or 4, and the value is one
//               more than the degree of the surface.
////////////////////////////////////////////////////////////////////
INLINE void NurbsSurfaceEvaluator::
set_v_order(int v_order) {
  _v_order = v_order;
  _v_knots_dirty = true;
  _v_basis_dirty = true;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_v_order
//       Access: Published
//  Description: Returns the order of the surface in the V direction
//               as set by a previous call to set_v_order().
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_v_order() const {
  return _v_order;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_num_u_vertices
//       Access: Published
//  Description: Returns the number of control vertices in the U
//               direction on the surface.  This is the number passed
//               to the last call to reset().
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_num_u_vertices() const {
  return _num_u_vertices;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_num_v_vertices
//       Access: Published
//  Description: Returns the number of control vertices in the V
//               direction on the surface.  This is the number passed
//               to the last call to reset().
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_num_v_vertices() const {
  return _num_v_vertices;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::set_vertex
//       Access: Published
//  Description: Sets the nth control vertex of the surface, as a vertex
//               in 4-d homogeneous space.  In this form, the first
//               three components of the vertex should already have
//               been scaled by the fourth component, which is the
//               homogeneous weight.
////////////////////////////////////////////////////////////////////
INLINE void NurbsSurfaceEvaluator::
set_vertex(int ui, int vi, const LVecBase4f &vertex) {
  nassertv(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices);
  vert(ui, vi).set_vertex(vertex);
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::set_vertex
//       Access: Published
//  Description: Sets the nth control vertex of the surface.  This
//               flavor sets the vertex as a 3-d coordinate and a
//               weight; the 3-d coordinate values are implicitly
//               scaled up by the weight factor.
////////////////////////////////////////////////////////////////////
INLINE void NurbsSurfaceEvaluator::
set_vertex(int ui, int vi, const LVecBase3f &vertex, float weight) {
  nassertv(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices);
  vert(ui, vi).set_vertex(LVecBase4f(vertex[0] * weight, vertex[1] * weight, vertex[2] * weight, weight));
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_vertex
//       Access: Published
//  Description: Returns the nth control vertex of the surface, relative
//               to its indicated coordinate space.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase4f &NurbsSurfaceEvaluator::
get_vertex(int ui, int vi) const {
  nassertr(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices, LVecBase4f::zero());
  return vert(ui, vi).get_vertex();
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_vertex
//       Access: Published
//  Description: Returns the nth control vertex of the surface, relative
//               to the given coordinate space.
////////////////////////////////////////////////////////////////////
INLINE LVecBase4f NurbsSurfaceEvaluator::
get_vertex(int ui, int vi, const NodePath &rel_to) const {
  nassertr(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices, LVecBase4f::zero());

  NodePath space = vert(ui, vi).get_space(rel_to);
  const LVecBase4f &vertex = vert(ui, vi).get_vertex();
  if (space.is_empty()) {
    return vertex;
  } else {
    const LMatrix4f &mat = space.get_mat(rel_to);
    return vertex * mat;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::set_vertex_space
//       Access: Published
//  Description: Sets the coordinate space of the nth control vertex.
//               If this is not specified, or is set to an empty
//               NodePath, the nth control vertex is deemed to be in
//               the coordinate space passed to evaluate().
//
//               This specifies the space as a fixed NodePath, which
//               is always the same NodePath.  Also see setting the
//               space as a path string, which can specify a different
//               NodePath for different instances of the surface.
////////////////////////////////////////////////////////////////////
INLINE void NurbsSurfaceEvaluator::
set_vertex_space(int ui, int vi, const NodePath &space) {
  nassertv(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices);
  vert(ui, vi).set_space(space);
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::set_vertex_space
//       Access: Published
//  Description: Sets the coordinate space of the nth control vertex.
//               If this is not specified, or is set to an empty
//               string, the nth control vertex is deemed to be in
//               the coordinate space passed to evaluate().
//
//               This specifies the space as a string, which describes
//               the path to find the node relative to the rel_to
//               NodePath when the surface is evaluated.
////////////////////////////////////////////////////////////////////
INLINE void NurbsSurfaceEvaluator::
set_vertex_space(int ui, int vi, const string &space) {
  nassertv(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices);
  vert(ui, vi).set_space(space);
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::set_extended_vertex
//       Access: Public
//  Description: Sets an n-dimensional vertex value.  This allows
//               definition of a NURBS surface or surface in a sparse
//               n-dimensional space, typically used for associating
//               additional properties (like color or joint
//               membership) with each vertex of a surface.
//
//               The value d is an arbitrary integer value and
//               specifies the dimension of question for this
//               particular vertex.  Any number of dimensions may be
//               specified, and they need not be consecutive.  If a
//               value for a given dimension is not specified, is it
//               implicitly 0.0.
//
//               The value is implicitly scaled by the homogenous
//               weight value--that is, the fourth component of the
//               value passed to set_vertex().  This means the
//               ordinary vertex must be set first, before the
//               extended vertices can be set.
////////////////////////////////////////////////////////////////////
INLINE void NurbsSurfaceEvaluator::
set_extended_vertex(int ui, int vi, int d, float value) {
  nassertv(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices);
  vert(ui, vi).set_extended_vertex(d, value);
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::float_extended_vertex
//       Access: Public
//  Description: Returns an n-dimensional vertex value.  See
//               set_extended_vertex().  This returns the value set
//               for the indicated dimension, or 0.0 if nothing has
//               been set.
////////////////////////////////////////////////////////////////////
INLINE float NurbsSurfaceEvaluator::
get_extended_vertex(int ui, int vi, int d) const {
  nassertr(ui >= 0 && ui < _num_u_vertices &&
           vi >= 0 && vi < _num_v_vertices, 0.0f);
  return vert(ui, vi).get_extended_vertex(d);
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_num_u_knots
//       Access: Published
//  Description: Returns the number of knot values in the surface in
//               the U direction.  This is based on the number of
//               vertices and the order.
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_num_u_knots() const {
  return _num_u_vertices + _u_order;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_num_v_knots
//       Access: Published
//  Description: Returns the number of knot values in the surface in
//               the V direction.  This is based on the number of
//               vertices and the order.
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_num_v_knots() const {
  return _num_v_vertices + _v_order;
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_num_u_segments
//       Access: Published
//  Description: Returns the number of piecewise continuous segments
//               in the surface in the U direction.  This is based on
//               the knot vector.
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_num_u_segments() const {
  if (_u_basis_dirty) {
    ((NurbsSurfaceEvaluator *)this)->recompute_u_basis();
  }
  return _u_basis.get_num_segments();
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::get_num_v_segments
//       Access: Published
//  Description: Returns the number of piecewise continuous segments
//               in the surface in the V direction.  This is based on
//               the knot vector.
////////////////////////////////////////////////////////////////////
INLINE int NurbsSurfaceEvaluator::
get_num_v_segments() const {
  if (_v_basis_dirty) {
    ((NurbsSurfaceEvaluator *)this)->recompute_v_basis();
  }
  return _v_basis.get_num_segments();
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::vert
//       Access: Private
//  Description: Internal accessor to dereference the 2-d vertex
//               coordinate pair into a linear list of vertices.
////////////////////////////////////////////////////////////////////
INLINE NurbsVertex &NurbsSurfaceEvaluator::
vert(int ui, int vi) {
  return _vertices[ui * _num_v_vertices + vi];
}

////////////////////////////////////////////////////////////////////
//     Function: NurbsSurfaceEvaluator::vert
//       Access: Private
//  Description: Internal accessor to dereference the 2-d vertex
//               coordinate pair into a linear list of vertices.
////////////////////////////////////////////////////////////////////
INLINE const NurbsVertex &NurbsSurfaceEvaluator::
vert(int ui, int vi) const {
  return _vertices[ui * _num_v_vertices + vi];
}

INLINE ostream &
operator << (ostream &out, const NurbsSurfaceEvaluator &n) {
  n.output(out);
  return out;
}
