// Filename: hermiteCurve.h
// Created by:  drose (27Feb98)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////

#ifndef HERMITECURVE_H
#define HERMITECURVE_H

#include "piecewiseCurve.h"
#include "cubicCurveseg.h"


BEGIN_PUBLISH //[
// Hermite curve continuity types.
#define HC_CUT         1
// The curve is disconnected at this point.  All points between
// this and the following CV are not part of the curve.

#define HC_FREE        2
// Tangents are unconstrained.  The curve is continuous, but its first
// derivative is not.  This is G0 geometric continuity.

#define HC_G1          3
// Tangents are constrained to be collinear.  The curve's derivative
// is not continuous in parametric space, but its geometric slope is.
// The distinction is mainly relevant in the context of animation
// along the curve--when crossing the join point, direction of motion
// will change continuously, but the speed of motion may change
// suddenly.  This is G1 geometric continuity.

#define HC_SMOOTH     4
// Tangents are constrained to be identical.  The curve and its first
// derivative are continuous in parametric space.  When animating
// motion across the join point, speed and direction of motion will
// change continuously.  This is C1 parametric continuity.
END_PUBLISH //]

////////////////////////////////////////////////////////////////////
//       Class : HermiteCurveCV
// Description : A single CV of a Hermite curve.  Hermite curve CV's
//               include an in and out tangent, as well as a position.
////////////////////////////////////////////////////////////////////
class HermiteCurveCV {
public:
  HermiteCurveCV();
  HermiteCurveCV(const HermiteCurveCV &c);
  ~HermiteCurveCV();

  void set_point(const LVecBase3f &point) { _p = point; }
  void set_in(const LVecBase3f &in);
  void set_out(const LVecBase3f &out);
  void set_type(int type);
  void set_name(const string &name);

  void format_egg(ostream &out, int indent, int num_dimensions,
              bool show_in, bool show_out,
              float scale_in, float scale_out) const;

  void write_datagram(BamWriter *manager, Datagram &me) const;
  void fillin(DatagramIterator &scan, BamReader *manager);

  LVecBase3f _p, _in, _out;
  int _type;
  string _name;
};

////////////////////////////////////////////////////////////////////
//       Class : HermiteCurve
// Description : A parametric curve defined by a sequence of control
//               vertices, each with an in and out tangent.
//
//               This class is actually implemented as a
//               PiecewiseCurve made up of several CubicCurvesegs,
//               each of which is created using the hermite_basis()
//               method.  The HermiteCurve class itself keeps its own
//               list of the CV's that are used to define the curve
//               (since the CubicCurveseg class doesn't retain these).
////////////////////////////////////////////////////////////////////
class HermiteCurve : public PiecewiseCurve {
PUBLISHED:
  HermiteCurve();
  HermiteCurve(const ParametricCurve &pc);
  virtual ~HermiteCurve();

  int get_num_cvs() const;

  int insert_cv(float t);
  int append_cv(int type, float x, float y, float z);
  inline int append_cv(int type, const LVecBase3f &v) {
    return append_cv(type, v[0], v[1], v[2]);
  }

  bool remove_cv(int n);
  void remove_all_cvs();

  bool set_cv_type(int n, int type);
  bool set_cv_point(int n, float x, float y, float z);
  inline bool set_cv_point(int n, const LVecBase3f &v) {
    return set_cv_point(n, v[0], v[1], v[2]);
  }
  bool set_cv_in(int n, float x, float y, float z);
  inline bool set_cv_in(int n, const LVecBase3f &v) {
    return set_cv_in(n, v[0], v[1], v[2]);
  }
  bool set_cv_out(int n, float x, float y, float z);
  inline bool set_cv_out(int n, const LVecBase3f &v) {
    return set_cv_out(n, v[0], v[1], v[2]);
  }
  bool set_cv_tstart(int n, float tstart);
  bool set_cv_name(int n, const char *name);


  int get_cv_type(int n) const;
  const LVecBase3f &get_cv_point(int n) const;
  void get_cv_point(int n, LVecBase3f &v) const;
  const LVecBase3f &get_cv_in(int n) const;
  void get_cv_in(int n, LVecBase3f &v) const;
  const LVecBase3f &get_cv_out(int n) const;
  void get_cv_out(int n, LVecBase3f &v) const;
  float get_cv_tstart(int n) const;
  string get_cv_name(int n) const;

  virtual void output(ostream &out) const;
  void write_cv(ostream &out, int n) const;

public:

  CubicCurveseg *get_curveseg(int ti) {
    return (CubicCurveseg *)PiecewiseCurve::get_curveseg(ti);
  }

  virtual bool
  rebuild_curveseg(int rtype0, float t0, const LVecBase4f &v0,
                   int rtype1, float t1, const LVecBase4f &v1,
                   int rtype2, float t2, const LVecBase4f &v2,
                   int rtype3, float t3, const LVecBase4f &v3);

protected:
  virtual bool format_egg(ostream &out, const string &name,
                          const string &curve_type, int indent_level) const;

  void invalidate_cv(int n, bool redo_all);
  int find_cv(float t);
  void recompute_basis();

  pvector<HermiteCurveCV> _points;

// TypedWritable stuff
public:
  static void register_with_read_factory();

protected:
  static TypedWritable *make_HermiteCurve(const FactoryParams &params);
  virtual void write_datagram(BamWriter *manager, Datagram &me);
  void fillin(DatagramIterator &scan, BamReader *manager);

public:
  static TypeHandle get_class_type() {
    return _type_handle;
  }
  static void init_type() {
    register_type(_type_handle, "HermiteCurve");
  }
  virtual TypeHandle get_type() const {
    return get_class_type();
  }
  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}

private:
  static TypeHandle _type_handle;
};

#endif
