/*
 *  Path.cpp
 *  nlivarot
 *
 *  Created by fred on Tue Jun 17 2003.
 *
 */

#include <glib.h>
#include "Path.h"
#include "livarot/path-description.h"
#include <libnr/nr-matrix-ops.h>

/*
 * manipulation of the path data: path description and polyline
 * grunt work...
 * at the end of this file, 2 utilitary functions to get the point and tangent to path associated with a (command no;abcissis)
 */


Path::Path()
{
	descr_flags = 0;
	pending_bezier_cmd = -1;
	pending_moveto_cmd = -1;
  
	back = false;
}

Path::~Path()
{
    for (std::vector<PathDescr*>::iterator i = descr_cmd.begin(); i != descr_cmd.end(); i++) {
        delete *i;
    }
}

// debug function do dump the path contents on stdout
void Path::Affiche()
{
    std::cout << "path: " << descr_cmd.size() << " commands." << std::endl;
    for (std::vector<PathDescr*>::const_iterator i = descr_cmd.begin(); i != descr_cmd.end(); i++) {
        (*i)->dump(std::cout);
        std::cout << std::endl;
    }

    std::cout << std::endl;
}

void Path::Reset()
{
    for (std::vector<PathDescr*>::iterator i = descr_cmd.begin(); i != descr_cmd.end(); i++) {
        delete *i;
    }
    
    descr_cmd.clear();
    pending_bezier_cmd = -1;
    pending_moveto_cmd = -1;
    descr_flags = 0;
}

void Path::Copy(Path * who)
{
    ResetPoints();
    
    for (std::vector<PathDescr*>::iterator i = descr_cmd.begin(); i != descr_cmd.end(); i++) {
        delete *i;
    }
        
    descr_cmd.clear();
        
    for (std::vector<PathDescr*>::const_iterator i = who->descr_cmd.begin();
         i != who->descr_cmd.end();
         i++)
    {
        descr_cmd.push_back((*i)->clone());
    }
}

void Path::CloseSubpath()
{
    descr_flags &= ~(descr_doing_subpath);
    pending_moveto_cmd = -1;
}

int Path::ForcePoint()
{
    if (descr_flags & descr_adding_bezier) {
        EndBezierTo ();
    }
    
    if ( (descr_flags & descr_doing_subpath) == 0 ) {
        return -1;
    }
    
    if (descr_cmd.empty()) {
        return -1;
    }

    descr_cmd.push_back(new PathDescrForced);
    return descr_cmd.size() - 1;
}


void Path::InsertForcePoint(int at)
{
    if ( at < 0 || at > int(descr_cmd.size()) ) {
	return;
    }
    
    if ( at == int(descr_cmd.size()) ) {
	ForcePoint();
	return;
    }
    
    descr_cmd.insert(descr_cmd.begin() + at, new PathDescrForced);
}

int Path::Close()
{
    if ( descr_flags & descr_adding_bezier ) {
        CancelBezier();
    }
    if ( descr_flags & descr_doing_subpath ) {
        CloseSubpath();
    } else {
        // Nothing to close.
        return -1;
    }

    descr_cmd.push_back(new PathDescrClose);
    
    descr_flags &= ~(descr_doing_subpath);
    pending_moveto_cmd = -1;
    
    return descr_cmd.size() - 1;
}

int Path::MoveTo(NR::Point const &iPt)
{
    if ( descr_flags & descr_adding_bezier ) {
	EndBezierTo(iPt);
    }
    if ( descr_flags & descr_doing_subpath ) {
	CloseSubpath();
    }
    pending_moveto_cmd = descr_cmd.size();
    
    descr_cmd.push_back(new PathDescrMoveTo(iPt));

    descr_flags |= descr_doing_subpath;
    return descr_cmd.size() - 1;
}

void Path::InsertMoveTo(NR::Point const &iPt, int at)
{
    if ( at < 0 || at > int(descr_cmd.size()) ) {
        return;
    }
    
    if ( at == int(descr_cmd.size()) ) {
        MoveTo(iPt);
        return;
    }

  descr_cmd.insert(descr_cmd.begin() + at, new PathDescrMoveTo(iPt));
}

int Path::LineTo(NR::Point const &iPt)
{
    if (descr_flags & descr_adding_bezier) {
	EndBezierTo (iPt);
    }
    if (!( descr_flags & descr_doing_subpath )) {
	return MoveTo (iPt);
    }
    
    descr_cmd.push_back(new PathDescrLineTo(iPt));
    return descr_cmd.size() - 1;
}

void Path::InsertLineTo(NR::Point const &iPt, int at)
{
    if ( at < 0 || at > int(descr_cmd.size()) ) {
        return;
    }
    
    if ( at == int(descr_cmd.size()) ) {
        LineTo(iPt);
        return;
    }
    
    descr_cmd.insert(descr_cmd.begin() + at, new PathDescrLineTo(iPt));
}

int Path::CubicTo(NR::Point const &iPt, NR::Point const &iStD, NR::Point const &iEnD)
{
    if (descr_flags & descr_adding_bezier) {
	EndBezierTo(iPt);
    }
    if ( (descr_flags & descr_doing_subpath) == 0) {
	return MoveTo (iPt);
    }

    descr_cmd.push_back(new PathDescrCubicTo(iPt, iStD, iEnD));
    return descr_cmd.size() - 1;
}


void Path::InsertCubicTo(NR::Point const &iPt, NR::Point const &iStD, NR::Point const &iEnD, int at)
{
    if ( at < 0 || at > int(descr_cmd.size()) ) {
	return;
    }
    
    if ( at == int(descr_cmd.size()) ) {
	CubicTo(iPt,iStD,iEnD);
	return;
    }
  
    descr_cmd.insert(descr_cmd.begin() + at, new PathDescrCubicTo(iPt, iStD, iEnD));
}

int Path::ArcTo(NR::Point const &iPt, double iRx, double iRy, double angle,
		bool iLargeArc, bool iClockwise)
{
    if (descr_flags & descr_adding_bezier) {
	EndBezierTo(iPt);
    }
    if ( (descr_flags & descr_doing_subpath) == 0 ) {
	return MoveTo(iPt);
    }

    descr_cmd.push_back(new PathDescrArcTo(iPt, iRx, iRy, angle, iLargeArc, iClockwise));
    return descr_cmd.size() - 1;
}


void Path::InsertArcTo(NR::Point const &iPt, double iRx, double iRy, double angle,
		       bool iLargeArc, bool iClockwise, int at)
{
    if ( at < 0 || at > int(descr_cmd.size()) ) {
	return;
    }
    
    if ( at == int(descr_cmd.size()) ) {
	ArcTo(iPt, iRx, iRy, angle, iLargeArc, iClockwise);
	return;
    }
  
    descr_cmd.insert(descr_cmd.begin() + at, new PathDescrArcTo(iPt, iRx, iRy,
                                                                angle, iLargeArc, iClockwise));
}

int Path::TempBezierTo()
{
    if (descr_flags & descr_adding_bezier) {
	CancelBezier();
    }
    if ( (descr_flags & descr_doing_subpath) == 0) {
	// No starting point -> bad.
	return -1;
    }
    pending_bezier_cmd = descr_cmd.size();
    
    descr_cmd.push_back(new PathDescrBezierTo(NR::Point(0, 0), 0));
    descr_flags |= descr_adding_bezier;
    descr_flags |= descr_delayed_bezier;
    return descr_cmd.size() - 1;
}

void Path::CancelBezier()
{
    descr_flags &= ~(descr_adding_bezier);
    descr_flags &= ~(descr_delayed_bezier);
    if (pending_bezier_cmd < 0) {
	return;
    }

    /* FIXME: I think there's a memory leak here */
    descr_cmd.resize(pending_bezier_cmd);
    pending_bezier_cmd = -1;
}

int Path::EndBezierTo()
{
    if (descr_flags & descr_delayed_bezier) {
	CancelBezier ();
    } else {
	pending_bezier_cmd = -1;
	descr_flags &= ~(descr_adding_bezier);
	descr_flags &= ~(descr_delayed_bezier);
    }
    return -1;
}

int Path::EndBezierTo(NR::Point const &iPt)
{
    if ( (descr_flags & descr_adding_bezier) == 0 ) {
	return LineTo(iPt);
    }
    if ( (descr_flags & descr_doing_subpath) == 0 ) {
	return MoveTo(iPt);
    }
    if ( (descr_flags & descr_delayed_bezier) == 0 ) {
	return EndBezierTo();
    }
    PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[pending_bezier_cmd]);
    nData->p = iPt;
    pending_bezier_cmd = -1;
    descr_flags &= ~(descr_adding_bezier);
    descr_flags &= ~(descr_delayed_bezier);
    return -1;
}


int Path::IntermBezierTo(NR::Point const &iPt)
{
    if ( (descr_flags & descr_adding_bezier) == 0 ) {
	return LineTo (iPt);
    }
    
    if ( (descr_flags & descr_doing_subpath) == 0) {
	return MoveTo (iPt);
    }

    descr_cmd.push_back(new PathDescrIntermBezierTo(iPt));

    PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[pending_bezier_cmd]);
    nBData->nb++;
    return descr_cmd.size() - 1;
}


void Path::InsertIntermBezierTo(NR::Point const &iPt, int at)
{
    if ( at < 0 || at > int(descr_cmd.size()) ) {
        return;
    }
    
    if ( at == int(descr_cmd.size()) ) {
        IntermBezierTo(iPt);
        return;
    }
    
    descr_cmd.insert(descr_cmd.begin() + at, new PathDescrIntermBezierTo(iPt));
}


int Path::BezierTo(NR::Point const &iPt)
{
    if ( descr_flags & descr_adding_bezier ) {
	EndBezierTo(iPt);
    }
    
    if ( (descr_flags & descr_doing_subpath) == 0 ) {
	return MoveTo (iPt);
    }
    
    pending_bezier_cmd = descr_cmd.size();
    
    descr_cmd.push_back(new PathDescrBezierTo(iPt, 0));
    descr_flags |= descr_adding_bezier;
    descr_flags &= ~(descr_delayed_bezier);
    return descr_cmd.size() - 1;
}


void Path::InsertBezierTo(NR::Point const &iPt, int iNb, int at)
{
    if ( at < 0 || at > int(descr_cmd.size()) ) {
	return;
    }
    
    if ( at == int(descr_cmd.size()) ) {
	BezierTo(iPt);
	return;
    }
  
    descr_cmd.insert(descr_cmd.begin() + at, new PathDescrBezierTo(iPt, iNb));
}


/*
 * points de la polyligne
 */
void
Path::SetBackData (bool nVal)
{
	if (back == false) {
		if (nVal == true && back == false) {
			back = true;
			ResetPoints();
		} else if (nVal == false && back == true) {
			back = false;
			ResetPoints();
		}
	} else {
		if (nVal == true && back == false) {
			back = true;
			ResetPoints();
		} else if (nVal == false && back == true) {
			back = false;
			ResetPoints();
		}
	}
}


void Path::ResetPoints()
{
    pts.clear();
}


int Path::AddPoint(NR::Point const &iPt, bool mvto)
{
    if (back) {
        return AddPoint (iPt, -1, 0.0, mvto);
    }
  
    if ( !mvto && pts.empty() == false && pts.back().p == iPt ) {
        return -1;
    }
    
    int const n = pts.size();
    pts.push_back(path_lineto(mvto ? polyline_moveto : polyline_lineto, iPt));
    return n;
}


int Path::AddPoint(NR::Point const &iPt, int ip, double it, bool mvto)
{
    if (back == false) {
        return AddPoint (iPt, mvto);
    }
    
    if ( !mvto && pts.empty() == false && pts.back().p == iPt ) {
        return -1;
    }
    
    int const n = pts.size();
    pts.push_back(path_lineto(mvto ? polyline_moveto : polyline_lineto, iPt, ip, it));
    return n;
}

int Path::AddForcedPoint(NR::Point const &iPt)
{
    if (back) {
        return AddForcedPoint (iPt, -1, 0.0);
    }
    
    if ( pts.empty() || pts.back().isMoveTo != polyline_lineto ) {
        return -1;
    }
    
    int const n = pts.size();
    pts.push_back(path_lineto(polyline_forced, pts[n - 1].p));
    return n;
}


int Path::AddForcedPoint(NR::Point const &iPt, int /*ip*/, double /*it*/)
{
    /* FIXME: ip & it aren't used.  Is this deliberate? */
    if (!back) {
        return AddForcedPoint (iPt);
    }
    
    if ( pts.empty() || pts.back().isMoveTo != polyline_lineto ) {
        return -1;
    }
    
    int const n = pts.size();
    pts.push_back(path_lineto(polyline_forced, pts[n - 1].p, pts[n - 1].piece, pts[n - 1].t));
    return n;
}

void Path::PolylineBoundingBox(double &l, double &t, double &r, double &b)
{
  l = t = r = b = 0.0;
  if ( pts.empty() ) {
      return;
  }

  std::vector<path_lineto>::const_iterator i = pts.begin();
  l = r = i->p[NR::X];
  t = b = i->p[NR::Y];
  i++;

  for (; i != pts.end(); i++) {
      r = std::max(r, i->p[NR::X]);
      l = std::min(l, i->p[NR::X]);
      b = std::max(b, i->p[NR::Y]);
      t = std::min(t, i->p[NR::Y]);
  }
}


/**
 *    \param piece Index of a one of our commands.
 *    \param at Distance along the segment that corresponds to `piece' (0 <= at <= 1)
 *    \param pos Filled in with the point at `at' on `piece'.
 */

void Path::PointAt(int piece, double at, NR::Point &pos)
{
    if (piece < 0 || piece >= int(descr_cmd.size())) {
	// this shouldn't happen: the piece we are asked for doesn't
	// exist in the path
	pos = NR::Point(0,0);
	return;
    }
    
    PathDescr const *theD = descr_cmd[piece];
    int const typ = theD->getType();
    NR::Point tgt;
    double len;
    double rad;
    
    if (typ == descr_moveto) {
	
	return PointAt (piece + 1, 0.0, pos);
	
    } else if (typ == descr_close || typ == descr_forced) {
	
	return PointAt (piece - 1, 1.0, pos);
	
    } else if (typ == descr_lineto) {
	
	PathDescrLineTo const *nData = dynamic_cast<PathDescrLineTo const *>(theD);
	TangentOnSegAt(at, PrevPoint (piece - 1), *nData, pos, tgt, len);
	
    } else if (typ == descr_arcto) {
	
	PathDescrArcTo const *nData = dynamic_cast<PathDescrArcTo const *>(theD);
	TangentOnArcAt(at,PrevPoint (piece - 1), *nData, pos, tgt, len, rad);
	
    } else if (typ == descr_cubicto) {
	
	PathDescrCubicTo const *nData = dynamic_cast<PathDescrCubicTo const *>(theD);
	TangentOnCubAt(at, PrevPoint (piece - 1), *nData, false, pos, tgt, len, rad);
	
    } else if (typ == descr_bezierto || typ == descr_interm_bezier) {
	
	int bez_st = piece;
	while (bez_st >= 0) {
	    int nt = descr_cmd[bez_st]->getType();
	    if (nt == descr_bezierto)
		break;
	    bez_st--;
	}
	if ( bez_st < 0 ) {
	    // Didn't find the beginning of the spline (bad).
	    // [pas trouvé le dubut de la spline (mauvais)]
	    return PointAt(piece - 1, 1.0, pos);
	}
	
	PathDescrBezierTo *stB = dynamic_cast<PathDescrBezierTo *>(descr_cmd[bez_st]);
	if ( piece > bez_st + stB->nb ) {
	    // The spline goes past the authorized number of commands (bad).
	    // [la spline sort du nombre de commandes autorisé (mauvais)]
	    return PointAt(piece - 1, 1.0, pos);
	}
	
	int k = piece - bez_st;
	NR::Point const bStPt = PrevPoint(bez_st - 1);
	if (stB->nb == 1 || k <= 0) {
	    PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
	    TangentOnBezAt(at, bStPt, *nData, *stB, false, pos, tgt, len, rad);
	} else {
	    // forcement plus grand que 1
	    if (k == 1) {
		PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
		PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 2]);
		PathDescrBezierTo fin(0.5 * (nextI->p + nnextI->p), 1);
		TangentOnBezAt(at, bStPt, *nextI,  fin, false, pos, tgt, len, rad);
	    } else if (k == stB->nb) {
		PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
		PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
		NR::Point stP = 0.5 * ( prevI->p + nextI->p );
		TangentOnBezAt(at, stP, *nextI, *stB, false, pos, tgt, len, rad);
	    } else {
		PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
		PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
		PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k + 1]);
		NR::Point stP = 0.5 * ( prevI->p + nextI->p );
		PathDescrBezierTo fin(0.5 * (nextI->p + nnextI->p), 1);
		TangentOnBezAt(at, stP, *nextI, fin, false, pos, tgt, len, rad);
	    }
	}
    }
}


void Path::PointAndTangentAt(int piece, double at, NR::Point &pos, NR::Point &tgt)
{
    if (piece < 0 || piece >= int(descr_cmd.size())) {
	// this shouldn't happen: the piece we are asked for doesn't exist in the path
	pos = NR::Point(0, 0);
	return;
    }
    
    PathDescr const *theD = descr_cmd[piece];
    int typ = theD->getType();
    double len;
    double rad;
    if (typ == descr_moveto) {
	
	return PointAndTangentAt(piece + 1, 0.0, pos, tgt);
	
    } else if (typ == descr_close ) {
	
	int cp = piece - 1;
	while ( cp >= 0 && (descr_cmd[cp]->getType()) != descr_moveto ) {
	    cp--;
	}
	if ( cp >= 0 ) {
	    PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[cp]);
	    PathDescrLineTo dst(nData->p);
	    TangentOnSegAt(at, PrevPoint (piece - 1), dst, pos, tgt, len);
	}
	
    } else if ( typ == descr_forced) {
	
	return PointAndTangentAt(piece - 1, 1.0, pos,tgt);
	
    } else if (typ == descr_lineto) {

	PathDescrLineTo const *nData = dynamic_cast<PathDescrLineTo const *>(theD);
	TangentOnSegAt(at, PrevPoint (piece - 1), *nData, pos, tgt, len);
	
    } else if (typ == descr_arcto) {
	
	PathDescrArcTo const *nData = dynamic_cast<PathDescrArcTo const *>(theD);
	TangentOnArcAt (at,PrevPoint (piece - 1), *nData, pos, tgt, len, rad);
	
    } else if (typ == descr_cubicto) {
	
	PathDescrCubicTo const *nData = dynamic_cast<PathDescrCubicTo const *>(theD);
	TangentOnCubAt (at, PrevPoint (piece - 1), *nData, false, pos, tgt, len, rad);
	
    } else if (typ == descr_bezierto || typ == descr_interm_bezier) {
	int bez_st = piece;
	while (bez_st >= 0) {
	    int nt = descr_cmd[bez_st]->getType();
	    if (nt == descr_bezierto) break;
	    bez_st--;
	}
	if ( bez_st < 0 ) {
	    return PointAndTangentAt(piece - 1, 1.0, pos, tgt);
	    // Didn't find the beginning of the spline (bad).
	    // [pas trouvé le dubut de la spline (mauvais)]
	}
	
	PathDescrBezierTo* stB = dynamic_cast<PathDescrBezierTo*>(descr_cmd[bez_st]);
	if ( piece > bez_st + stB->nb ) {
	    return PointAndTangentAt(piece - 1, 1.0, pos, tgt);
	    // The spline goes past the number of authorized commands (bad).
	    // [la spline sort du nombre de commandes autorisé (mauvais)]
	}
	
	int k = piece - bez_st;
	NR::Point const bStPt(PrevPoint( bez_st - 1 ));
	if (stB->nb == 1 || k <= 0) {
	    PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
	    TangentOnBezAt (at, bStPt, *nData, *stB, false, pos, tgt, len, rad);
	} else {
	    // forcement plus grand que 1
	    if (k == 1) {
		PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
		PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 2]);
		PathDescrBezierTo fin(0.5 * (nextI->p + nnextI->p), 1);
		TangentOnBezAt(at, bStPt, *nextI, fin, false, pos, tgt, len, rad);
	    } else if (k == stB->nb) {
		PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
		PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
		NR::Point stP = 0.5 * ( prevI->p + nextI->p );
		TangentOnBezAt(at, stP, *nextI, *stB, false, pos, tgt, len, rad);
	    } else {
		PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
		PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
		PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k + 1]);
		NR::Point stP = 0.5 * ( prevI->p + nextI->p );
		PathDescrBezierTo fin(0.5 * (nnextI->p + nnextI->p), 1);
		TangentOnBezAt(at, stP, *nextI, fin, false, pos, tgt, len, rad);
	    }
	}
    }
}

void Path::Transform(const NR::Matrix &trans)
{
    for (std::vector<PathDescr*>::iterator i = descr_cmd.begin(); i != descr_cmd.end(); i++) {
        (*i)->transform(trans);
    }
}

void Path::FastBBox(double &l,double &t,double &r,double &b)
{
    l = t = r = b = 0;
    bool empty = true;
    NR::Point lastP(0, 0);
    
    for (int i = 0; i < int(descr_cmd.size()); i++) {
	int const typ = descr_cmd[i]->getType();
	switch ( typ ) {
	case descr_lineto:
	{
	    PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
	    if ( empty ) {
		l = r = nData->p[NR::X];
		t = b = nData->p[NR::Y];
		empty = false;
	    } else {
		if ( nData->p[NR::X] < l ) {
		    l = nData->p[NR::X];
		}
		if ( nData->p[NR::X] > r ) {
		    r = nData->p[NR::X];
		}
		if ( nData->p[NR::Y] < t ) {
		    t = nData->p[NR::Y];
		}
		if ( nData->p[NR::Y] > b ) {
		    b = nData->p[NR::Y];
		}
	    }
	    lastP = nData->p;
	}
        break;
	
	case descr_moveto:
	{
	    PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
	    if ( empty ) {
		l = r = nData->p[NR::X];
		t = b = nData->p[NR::Y];
		empty = false;
	    } else {
		if ( nData->p[NR::X] < l ) {
		    l = nData->p[NR::X];
		}
		if ( nData->p[NR::X] > r ) {
		    r = nData->p[NR::X];
		}
		if ( nData->p[NR::Y] < t ) {
		    t = nData->p[NR::Y];
		}
		if ( nData->p[NR::Y] > b ) {
		    b = nData->p[NR::Y];
		}
	    }
	    lastP = nData->p;
	}
        break;
	
	case descr_arcto:
	{
	    PathDescrArcTo *nData  =  dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
	    if ( empty ) {
		l = r = nData->p[NR::X];
		t = b = nData->p[NR::Y];
		empty = false;
	    } else {
		if ( nData->p[NR::X] < l ) {
		    l = nData->p[NR::X];
		}
		if ( nData->p[NR::X] > r ) {
		    r = nData->p[NR::X];
		}
		if ( nData->p[NR::Y] < t ) {
		    t = nData->p[NR::Y];
		}
		if ( nData->p[NR::Y] > b ) {
		    b = nData->p[NR::Y];
		}
	    }
	    lastP = nData->p;
	}
        break;
	
	case descr_cubicto:
	{
	    PathDescrCubicTo *nData  =  dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
	    if ( empty ) {
		l = r = nData->p[NR::X];
		t = b = nData->p[NR::Y];
		empty = false;
	    } else {
		if ( nData->p[NR::X] < l ) {
		    l = nData->p[NR::X];
		}
		if ( nData->p[NR::X] > r ) {
		    r = nData->p[NR::X];
		}
		if ( nData->p[NR::Y] < t ) {
		    t = nData->p[NR::Y];
		}
		if ( nData->p[NR::Y] > b ) {
		    b = nData->p[NR::Y];
		}
	    }
	    
	    NR::Point np = nData->p - nData->end;
	    if ( np[NR::X] < l ) {
		l = np[NR::X];
	    }
	    if ( np[NR::X] > r ) {
		r = np[NR::X];
	    }
	    if ( np[NR::Y] < t ) {
		t = np[NR::Y];
	    }
	    if ( np[NR::Y] > b ) {
		b = np[NR::Y];
	    }
	    
	    np = lastP + nData->start;
	    if ( np[NR::X] < l ) {
		l = np[NR::X];
	    }
	    if ( np[NR::X] > r ) {
		r = np[NR::X];
	    }
	    if ( np[NR::Y] < t ) {
		t = np[NR::Y];
	    }
	    if ( np[NR::Y] > b ) {
		b = np[NR::Y];
	    }
	    lastP = nData->p;
	}
        break;
	
	case descr_bezierto:
	{
	    PathDescrBezierTo *nData  =  dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
	    if ( empty ) {
		l = r = nData->p[NR::X];
		t = b = nData->p[NR::Y];
		empty = false;
	    } else {
		if ( nData->p[NR::X] < l ) {
		    l = nData->p[NR::X];
		}
		if ( nData->p[NR::X] > r ) {
		    r = nData->p[NR::X];
		}
		if ( nData->p[NR::Y] < t ) {
		    t = nData->p[NR::Y];
		}
		if ( nData->p[NR::Y] > b ) {
		    b = nData->p[NR::Y];
		}
	    }
	    lastP = nData->p;
	}
        break;
	
	case descr_interm_bezier:
	{
	    PathDescrIntermBezierTo *nData  =  dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i]);
	    if ( empty ) {
		l = r = nData->p[NR::X];
		t = b = nData->p[NR::Y];
		empty = false;
	    } else {
		if ( nData->p[NR::X] < l ) {
		    l = nData->p[NR::X];
		}
		if ( nData->p[NR::X] > r ) {
		    r = nData->p[NR::X];
		}
		if ( nData->p[NR::Y] < t ) {
		    t = nData->p[NR::Y];
		}
		if ( nData->p[NR::Y] > b ) {
		    b = nData->p[NR::Y];
		}
	    }
	}
        break;
	}
    }
}

char *Path::svg_dump_path() const
{
    Inkscape::SVGOStringStream os;

    for (int i = 0; i < int(descr_cmd.size()); i++) {
        NR::Point const p = (i == 0) ? NR::Point(0, 0) : PrevPoint(i - 1);
        descr_cmd[i]->dumpSVG(os, p);
    }
  
    return g_strdup (os.str().c_str());
}

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
