/* * Path.cpp * nlivarot * * Created by fred on Tue Jun 17 2003. * */ #include <algorithm> #include <iostream> #include <glib.h> #include "Path.h" #include "livarot/path-description.h" #include <libnr/nr-point.h> #include <libnr/nr-point-ops.h> #include <libnr/nr-matrix-ops.h> #include "../svg/stringstream.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 :