summaryrefslogtreecommitdiffstats
path: root/src/livarot/PathOutline.cpp
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/livarot/PathOutline.cpp
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/livarot/PathOutline.cpp')
-rw-r--r--src/livarot/PathOutline.cpp1493
1 files changed, 1493 insertions, 0 deletions
diff --git a/src/livarot/PathOutline.cpp b/src/livarot/PathOutline.cpp
new file mode 100644
index 000000000..ee656f900
--- /dev/null
+++ b/src/livarot/PathOutline.cpp
@@ -0,0 +1,1493 @@
+/*
+ * PathOutline.cpp
+ * nlivarot
+ *
+ * Created by fred on Fri Nov 28 2003.
+ *
+ */
+
+#include "livarot/Path.h"
+#include "livarot/path-description.h"
+#include <libnr/nr-point-fns.h>
+
+/*
+ * the "outliner"
+ * takes a sequence of path commands and produces a set of commands that approximates the offset
+ * result is stored in dest (that paremeter is handed to all the subfunctions)
+ * not that the result is in general not mathematically correct; you can end up with unwanted holes in your
+ * beautiful offset. a better way is to do path->polyline->polygon->offset of polygon->polyline(=contours of the polygon)->path
+ * but computing offsets of the path is faster...
+ */
+
+// outline of a path.
+// computed by making 2 offsets, one of the "left" side of the path, one of the right side, and then glueing the two
+// the left side has to be reversed to make a contour
+void Path::Outline(Path *dest, double width, JoinType join, ButtType butt, double miter)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+ if ( descr_cmd.size() <= 1 ) {
+ return;
+ }
+ if ( dest == NULL ) {
+ return;
+ }
+
+ dest->Reset();
+ dest->SetBackData(false);
+
+ outline_callbacks calls;
+ NR::Point endButt;
+ NR::Point endPos;
+ calls.cubicto = StdCubicTo;
+ calls.bezierto = StdBezierTo;
+ calls.arcto = StdArcTo;
+
+ Path *rev = new Path;
+
+ // we repeat the offset contour creation for each subpath
+ int curP = 0;
+ do {
+ int lastM = curP;
+ do {
+ curP++;
+ if (curP >= int(descr_cmd.size())) {
+ break;
+ }
+ int typ = descr_cmd[curP]->getType();
+ if (typ == descr_moveto) {
+ break;
+ }
+ } while (curP < int(descr_cmd.size()));
+
+ if (curP >= int(descr_cmd.size())) {
+ curP = descr_cmd.size();
+ }
+
+ if (curP > lastM + 1) {
+ // we have isolated a subpath, now we make a reversed version of it
+ // we do so by taking the subpath in the reverse and constructing a path as appropriate
+ // the construct is stored in "rev"
+ int curD = curP - 1;
+ NR::Point curX;
+ NR::Point nextX;
+ int firstTyp = descr_cmd[curD]->getType();
+ bool const needClose = (firstTyp == descr_close);
+ while (curD > lastM && descr_cmd[curD]->getType() == descr_close) {
+ curD--;
+ }
+
+ int realP = curD + 1;
+ if (curD > lastM) {
+ curX = PrevPoint(curD);
+ rev->Reset ();
+ rev->MoveTo(curX);
+ while (curD > lastM) {
+ int const typ = descr_cmd[curD]->getType();
+ if (typ == descr_moveto) {
+ // rev->Close();
+ curD--;
+ } else if (typ == descr_forced) {
+ // rev->Close();
+ curD--;
+ } else if (typ == descr_lineto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_cubicto) {
+ PathDescrCubicTo* nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ NR::Point isD=-nData->start;
+ NR::Point ieD=-nData->end;
+ rev->CubicTo (nextX, ieD,isD);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_arcto) {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ rev->ArcTo (nextX, nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_bezierto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_interm_bezier) {
+ int nD = curD - 1;
+ while (nD > lastM && descr_cmd[nD]->getType() != descr_bezierto) nD--;
+ if ((descr_cmd[nD]->getType()) != descr_bezierto) {
+ // pas trouve le debut!?
+ // Not find the start?!
+ nextX = PrevPoint (nD);
+ rev->LineTo (nextX);
+ curX = nextX;
+ } else {
+ nextX = PrevPoint (nD - 1);
+ rev->BezierTo (nextX);
+ for (int i = curD; i > nD; i--) {
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[i]);
+ rev->IntermBezierTo (nData->p);
+ }
+ rev->EndBezierTo ();
+ curX = nextX;
+ }
+ curD = nD - 1;
+ } else {
+ curD--;
+ }
+ }
+
+ // offset the paths and glue everything
+ // actual offseting is done in SubContractOutline()
+ if (needClose) {
+ rev->Close ();
+ rev->SubContractOutline (0, rev->descr_cmd.size(),
+ dest, calls, 0.0025 * width * width, width,
+ join, butt, miter, true, false, endPos, endButt);
+ SubContractOutline (lastM, realP + 1 - lastM,
+ dest, calls, 0.0025 * width * width,
+ width, join, butt, miter, true, false, endPos, endButt);
+ } else {
+ rev->SubContractOutline (0, rev->descr_cmd.size(),
+ dest, calls, 0.0025 * width * width, width,
+ join, butt, miter, false, false, endPos, endButt);
+ NR::Point endNor=endButt.ccw();
+ if (butt == butt_round) {
+ dest->ArcTo (endPos+width*endNor, 1.0001 * width, 1.0001 * width, 0.0, true, true);
+ } else if (butt == butt_square) {
+ dest->LineTo (endPos-width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else if (butt == butt_pointy) {
+ dest->LineTo (endPos+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else {
+ dest->LineTo (endPos+width*endNor);
+ }
+ SubContractOutline (lastM, realP - lastM,
+ dest, calls, 0.0025 * width * width, width, join, butt,
+ miter, false, true, endPos, endButt);
+
+ endNor=endButt.ccw();
+ if (butt == butt_round) {
+ dest->ArcTo (endPos+width*endNor, 1.0001 * width, 1.0001 * width, 0.0, true, true);
+ } else if (butt == butt_square) {
+ dest->LineTo (endPos-width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else if (butt == butt_pointy) {
+ dest->LineTo (endPos+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else {
+ dest->LineTo (endPos+width*endNor);
+ }
+ dest->Close ();
+ }
+ } // if (curD > lastM)
+ } // if (curP > lastM + 1)
+
+ } while (curP < int(descr_cmd.size()));
+
+ delete rev;
+}
+
+// versions for outlining closed path: they only make one side of the offset contour
+void
+Path::OutsideOutline (Path * dest, double width, JoinType join, ButtType butt,
+ double miter)
+{
+ if (descr_flags & descr_adding_bezier) {
+ CancelBezier();
+ }
+ if (descr_flags & descr_doing_subpath) {
+ CloseSubpath();
+ }
+ if (int(descr_cmd.size()) <= 1) return;
+ if (dest == NULL) return;
+ dest->Reset ();
+ dest->SetBackData (false);
+
+ outline_callbacks calls;
+ NR::Point endButt, endPos;
+ calls.cubicto = StdCubicTo;
+ calls.bezierto = StdBezierTo;
+ calls.arcto = StdArcTo;
+ SubContractOutline (0, descr_cmd.size(),
+ dest, calls, 0.0025 * width * width, width, join, butt,
+ miter, true, false, endPos, endButt);
+}
+
+void
+Path::InsideOutline (Path * dest, double width, JoinType join, ButtType butt,
+ double miter)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+ if (int(descr_cmd.size()) <= 1) return;
+ if (dest == NULL) return;
+ dest->Reset ();
+ dest->SetBackData (false);
+
+ outline_callbacks calls;
+ NR::Point endButt, endPos;
+ calls.cubicto = StdCubicTo;
+ calls.bezierto = StdBezierTo;
+ calls.arcto = StdArcTo;
+
+ Path *rev = new Path;
+
+ int curP = 0;
+ do {
+ int lastM = curP;
+ do {
+ curP++;
+ if (curP >= int(descr_cmd.size())) break;
+ int typ = descr_cmd[curP]->getType();
+ if (typ == descr_moveto) break;
+ } while (curP < int(descr_cmd.size()));
+ if (curP >= int(descr_cmd.size())) curP = descr_cmd.size();
+ if (curP > lastM + 1) {
+ // Otherwise there's only one point. (tr: or "only a point")
+ // [sinon il n'y a qu'un point]
+ int curD = curP - 1;
+ NR::Point curX;
+ NR::Point nextX;
+ while (curD > lastM && (descr_cmd[curD]->getType()) == descr_close) curD--;
+ if (curD > lastM) {
+ curX = PrevPoint (curD);
+ rev->Reset ();
+ rev->MoveTo (curX);
+ while (curD > lastM) {
+ int typ = descr_cmd[curD]->getType();
+ if (typ == descr_moveto) {
+ rev->Close ();
+ curD--;
+ } else if (typ == descr_forced) {
+ curD--;
+ } else if (typ == descr_lineto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_cubicto) {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ NR::Point isD=-nData->start;
+ NR::Point ieD=-nData->end;
+ rev->CubicTo (nextX, ieD,isD);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_arcto) {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ rev->ArcTo (nextX, nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_bezierto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_interm_bezier) {
+ int nD = curD - 1;
+ while (nD > lastM && (descr_cmd[nD]->getType()) != descr_bezierto) nD--;
+ if (descr_cmd[nD]->getType() != descr_bezierto) {
+ // pas trouve le debut!?
+ nextX = PrevPoint (nD);
+ rev->LineTo (nextX);
+ curX = nextX;
+ } else {
+ nextX = PrevPoint (nD - 1);
+ rev->BezierTo (nextX);
+ for (int i = curD; i > nD; i--) {
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[i]);
+ rev->IntermBezierTo (nData->p);
+ }
+ rev->EndBezierTo ();
+ curX = nextX;
+ }
+ curD = nD - 1;
+ } else {
+ curD--;
+ }
+ }
+ rev->Close ();
+ rev->SubContractOutline (0, rev->descr_cmd.size(),
+ dest, calls, 0.0025 * width * width,
+ width, join, butt, miter, true, false,
+ endPos, endButt);
+ }
+ }
+ } while (curP < int(descr_cmd.size()));
+
+ delete rev;
+}
+
+
+// the offset
+// take each command and offset it.
+// the bezier spline is split in a sequence of bezier curves, and these are transformed in cubic bezier (which is
+// not hard since they are quadratic bezier)
+// joins are put where needed
+void Path::SubContractOutline(int off, int num_pd,
+ Path *dest, outline_callbacks & calls,
+ double tolerance, double width, JoinType join,
+ ButtType butt, double miter, bool closeIfNeeded,
+ bool skipMoveto, NR::Point &lastP, NR::Point &lastT)
+{
+ outline_callback_data callsData;
+
+ callsData.orig = this;
+ callsData.dest = dest;
+ int curP = 1;
+
+ // le moveto
+ NR::Point curX;
+ {
+ int firstTyp = descr_cmd[off]->getType();
+ if ( firstTyp != descr_moveto ) {
+ curX[0] = curX[1] = 0;
+ curP = 0;
+ } else {
+ PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[off]);
+ curX = nData->p;
+ }
+ }
+ NR::Point curT(0, 0);
+
+ bool doFirst = true;
+ NR::Point firstP(0, 0);
+ NR::Point firstT(0, 0);
+
+ // et le reste, 1 par 1
+ while (curP < num_pd)
+ {
+ int curD = off + curP;
+ int nType = descr_cmd[curD]->getType();
+ NR::Point nextX;
+ NR::Point stPos, enPos, stTgt, enTgt, stNor, enNor;
+ double stRad, enRad, stTle, enTle;
+ if (nType == descr_forced) {
+ curP++;
+ } else if (nType == descr_moveto) {
+ PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // et on avance
+ if (doFirst) {
+ } else {
+ if (closeIfNeeded) {
+ if ( NR::LInfty (curX- firstP) < 0.0001 ) {
+ OutlineJoin (dest, firstP, curT, firstT, width, join,
+ miter);
+ dest->Close ();
+ } else {
+ PathDescrLineTo temp(firstP);
+
+ TangentOnSegAt (0.0, curX, temp, stPos, stTgt,
+ stTle);
+ TangentOnSegAt (1.0, curX, temp, enPos, enTgt,
+ enTle);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ // jointure
+ {
+ NR::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join,
+ miter);
+ }
+ dest->LineTo (enPos+width*enNor);
+
+ // jointure
+ {
+ NR::Point pos;
+ pos = firstP;
+ OutlineJoin (dest, enPos, enNor, firstT, width, join,
+ miter);
+ dest->Close ();
+ }
+ }
+ }
+ }
+ firstP = nextX;
+ curP++;
+ }
+ else if (nType == descr_close)
+ {
+ if (doFirst == false)
+ {
+ if (NR::LInfty (curX - firstP) < 0.0001)
+ {
+ OutlineJoin (dest, firstP, curT, firstT, width, join,
+ miter);
+ dest->Close ();
+ }
+ else
+ {
+ PathDescrLineTo temp(firstP);
+ nextX = firstP;
+
+ TangentOnSegAt (0.0, curX, temp, stPos, stTgt, stTle);
+ TangentOnSegAt (1.0, curX, temp, enPos, enTgt, enTle);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ // jointure
+ {
+ OutlineJoin (dest, stPos, curT, stNor, width, join,
+ miter);
+ }
+
+ dest->LineTo (enPos+width*enNor);
+
+ // jointure
+ {
+ OutlineJoin (dest, enPos, enNor, firstT, width, join,
+ miter);
+ dest->Close ();
+ }
+ }
+ }
+ doFirst = true;
+ curP++;
+ }
+ else if (nType == descr_lineto)
+ {
+ PathDescrLineTo* nData = dynamic_cast<PathDescrLineTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // test de nullité du segment
+ if (IsNulCurve (descr_cmd, curD, curX))
+ {
+ curP++;
+ continue;
+ }
+ // et on avance
+ TangentOnSegAt (0.0, curX, *nData, stPos, stTgt, stTle);
+ TangentOnSegAt (1.0, curX, *nData, enPos, enTgt, enTle);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst)
+ {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto)
+ {
+ skipMoveto = false;
+ }
+ else
+ dest->MoveTo (curX+width*stNor);
+ }
+ else
+ {
+ // jointure
+ NR::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter);
+ }
+
+ int n_d = dest->LineTo (nextX+width*enNor);
+ if (n_d >= 0)
+ {
+ dest->descr_cmd[n_d]->associated = curP;
+ dest->descr_cmd[n_d]->tSt = 0.0;
+ dest->descr_cmd[n_d]->tEn = 1.0;
+ }
+ curP++;
+ }
+ else if (nType == descr_cubicto)
+ {
+ PathDescrCubicTo* nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // test de nullite du segment
+ if (IsNulCurve (descr_cmd, curD, curX))
+ {
+ curP++;
+ continue;
+ }
+ // et on avance
+ TangentOnCubAt (0.0, curX, *nData, false, stPos, stTgt,
+ stTle, stRad);
+ TangentOnCubAt (1.0, curX, *nData, true, enPos, enTgt,
+ enTle, enRad);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst)
+ {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto)
+ {
+ skipMoveto = false;
+ }
+ else
+ dest->MoveTo (curX+width*stNor);
+ }
+ else
+ {
+ // jointure
+ NR::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter);
+ }
+
+ callsData.piece = curP;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = curX[0];
+ callsData.y1 = curX[1];
+ callsData.x2 = nextX[0];
+ callsData.y2 = nextX[1];
+ callsData.d.c.dx1 = nData->start[0];
+ callsData.d.c.dy1 = nData->start[1];
+ callsData.d.c.dx2 = nData->end[0];
+ callsData.d.c.dy2 = nData->end[1];
+ (calls.cubicto) (&callsData, tolerance, width);
+
+ curP++;
+ }
+ else if (nType == descr_arcto)
+ {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // test de nullité du segment
+ if (IsNulCurve (descr_cmd, curD, curX))
+ {
+ curP++;
+ continue;
+ }
+ // et on avance
+ TangentOnArcAt (0.0, curX, *nData, stPos, stTgt, stTle,
+ stRad);
+ TangentOnArcAt (1.0, curX, *nData, enPos, enTgt, enTle,
+ enRad);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt; // tjs definie
+
+ if (doFirst)
+ {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto)
+ {
+ skipMoveto = false;
+ }
+ else
+ dest->MoveTo (curX+width*stNor);
+ }
+ else
+ {
+ // jointure
+ NR::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter);
+ }
+
+ callsData.piece = curP;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = curX[0];
+ callsData.y1 = curX[1];
+ callsData.x2 = nextX[0];
+ callsData.y2 = nextX[1];
+ callsData.d.a.rx = nData->rx;
+ callsData.d.a.ry = nData->ry;
+ callsData.d.a.angle = nData->angle;
+ callsData.d.a.clock = nData->clockwise;
+ callsData.d.a.large = nData->large;
+ (calls.arcto) (&callsData, tolerance, width);
+
+ curP++;
+ }
+ else if (nType == descr_bezierto)
+ {
+ PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(descr_cmd[curD]);
+ int nbInterm = nBData->nb;
+ nextX = nBData->p;
+
+ if (IsNulCurve (descr_cmd, curD, curX)) {
+ curP += nbInterm + 1;
+ continue;
+ }
+
+ curP++;
+
+ curD = off + curP;
+ int ip = curD;
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
+
+ if (nbInterm <= 0) {
+ // et on avance
+ PathDescrLineTo temp(nextX);
+ TangentOnSegAt (0.0, curX, temp, stPos, stTgt, stTle);
+ TangentOnSegAt (1.0, curX, temp, enPos, enTgt, enTle);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst) {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto) {
+ skipMoveto = false;
+ } else dest->MoveTo (curX+width*stNor);
+ } else {
+ // jointure
+ NR::Point pos;
+ pos = curX;
+ if (stTle > 0) OutlineJoin (dest, pos, curT, stNor, width, join, miter);
+ }
+ int n_d = dest->LineTo (nextX+width*enNor);
+ if (n_d >= 0) {
+ dest->descr_cmd[n_d]->associated = curP - 1;
+ dest->descr_cmd[n_d]->tSt = 0.0;
+ dest->descr_cmd[n_d]->tEn = 1.0;
+ }
+ } else if (nbInterm == 1) {
+ NR::Point midX;
+ midX = nData->p;
+ // et on avance
+ TangentOnBezAt (0.0, curX, *nData, *nBData, false, stPos, stTgt, stTle, stRad);
+ TangentOnBezAt (1.0, curX, *nData, *nBData, true, enPos, enTgt, enTle, enRad);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst) {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto) {
+ skipMoveto = false;
+ } else dest->MoveTo (curX+width*stNor);
+ } else {
+ // jointure
+ NR::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter);
+ }
+
+ callsData.piece = curP;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = curX[0];
+ callsData.y1 = curX[1];
+ callsData.x2 = nextX[0];
+ callsData.y2 = nextX[1];
+ callsData.d.b.mx = midX[0];
+ callsData.d.b.my = midX[1];
+ (calls.bezierto) (&callsData, tolerance, width);
+
+ } else if (nbInterm > 1) {
+ NR::Point bx=curX;
+ NR::Point cx=curX;
+ NR::Point dx=curX;
+
+ dx = nData->p;
+ TangentOnBezAt (0.0, curX, *nData, *nBData, false, stPos, stTgt, stTle, stRad);
+ stNor=stTgt.cw();
+
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
+ // et on avance
+ if (stTle > 0) {
+ if (doFirst) {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto) {
+ skipMoveto = false;
+ } else dest->MoveTo (curX+width*stNor);
+ } else {
+ // jointure
+ NR::Point pos=curX;
+ OutlineJoin (dest, pos, stTgt, stNor, width, join, miter);
+ // dest->LineTo(curX+width*stNor.x,curY+width*stNor.y);
+ }
+ }
+
+ cx = 2 * bx - dx;
+
+ for (int k = 0; k < nbInterm - 1; k++) {
+ bx = cx;
+ cx = dx;
+
+ dx = nData->p;
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
+ NR::Point stx = (bx + cx) / 2;
+ // double stw=(bw+cw)/2;
+
+ PathDescrBezierTo tempb((cx + dx) / 2, 1);
+ PathDescrIntermBezierTo tempi(cx);
+ TangentOnBezAt (1.0, stx, tempi, tempb, true, enPos, enTgt, enTle, enRad);
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ callsData.piece = curP + k;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = stx[0];
+ callsData.y1 = stx[1];
+ callsData.x2 = (cx[0] + dx[0]) / 2;
+ callsData.y2 = (cx[1] + dx[1]) / 2;
+ callsData.d.b.mx = cx[0];
+ callsData.d.b.my = cx[1];
+ (calls.bezierto) (&callsData, tolerance, width);
+ }
+ {
+ bx = cx;
+ cx = dx;
+
+ dx = nextX;
+ dx = 2 * dx - cx;
+
+ NR::Point stx = (bx + cx) / 2;
+ // double stw=(bw+cw)/2;
+
+ PathDescrBezierTo tempb((cx + dx) / 2, 1);
+ PathDescrIntermBezierTo tempi(cx);
+ TangentOnBezAt (1.0, stx, tempi, tempb, true, enPos,
+ enTgt, enTle, enRad);
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ callsData.piece = curP + nbInterm - 1;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = stx[0];
+ callsData.y1 = stx[1];
+ callsData.x2 = (cx[0] + dx[0]) / 2;
+ callsData.y2 = (cx[1] + dx[1]) / 2;
+ callsData.d.b.mx = cx[0];
+ callsData.d.b.my = cx[1];
+ (calls.bezierto) (&callsData, tolerance, width);
+
+ }
+ }
+
+ // et on avance
+ curP += nbInterm;
+ }
+ curX = nextX;
+ curT = enNor; // sera tjs bien definie
+ }
+ if (closeIfNeeded)
+ {
+ if (doFirst == false)
+ {
+ }
+ }
+
+}
+
+/*
+ *
+ * utilitaires pour l'outline
+ *
+ */
+
+// like the name says: check whether the path command is actually more than a dumb point.
+bool
+Path::IsNulCurve (std::vector<PathDescr*> const &cmd, int curD, NR::Point const &curX)
+{
+ switch(cmd[curD]->getType()) {
+ case descr_lineto:
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo*>(cmd[curD]);
+ if (NR::LInfty(nData->p - curX) < 0.00001) {
+ return true;
+ }
+ return false;
+ }
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo*>(cmd[curD]);
+ NR::Point A = nData->start + nData->end + 2*(curX - nData->p);
+ NR::Point B = 3*(nData->p - curX) - 2*nData->start - nData->end;
+ NR::Point C = nData->start;
+ if (NR::LInfty(A) < 0.0001
+ && NR::LInfty(B) < 0.0001
+ && NR::LInfty (C) < 0.0001) {
+ return true;
+ }
+ return false;
+ }
+ case descr_arcto:
+ {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(cmd[curD]);
+ if ( NR::LInfty(nData->p - curX) < 0.00001) {
+ if ((nData->large == false)
+ || (fabs (nData->rx) < 0.00001
+ || fabs (nData->ry) < 0.00001)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ case descr_bezierto:
+ {
+ PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(cmd[curD]);
+ if (nBData->nb <= 0)
+ {
+ if (NR::LInfty(nBData->p - curX) < 0.00001) {
+ return true;
+ }
+ return false;
+ }
+ else if (nBData->nb == 1)
+ {
+ if (NR::LInfty(nBData->p - curX) < 0.00001) {
+ int ip = curD + 1;
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(cmd[ip]);
+ if (NR::LInfty(nData->p - curX) < 0.00001) {
+ return true;
+ }
+ }
+ return false;
+ } else if (NR::LInfty(nBData->p - curX) < 0.00001) {
+ for (int i = 1; i <= nBData->nb; i++) {
+ int ip = curD + i;
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(cmd[ip]);
+ if (NR::LInfty(nData->p - curX) > 0.00001) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ default:
+ return true;
+ }
+}
+
+// tangents and cuvarture computing, for the different path command types.
+// the need for tangent is obvious: it gives the normal, along which we offset points
+// curvature is used to do strength correction on the length of the tangents to the offset (see
+// cubic offset)
+
+/**
+ * \param at Distance along a tangent (0 <= at <= 1).
+ * \param iS Start point.
+ * \param fin LineTo description containing end point.
+ * \param pos Filled in with the position of `at' on the segment.
+ * \param tgt Filled in with the normalised tangent vector.
+ * \param len Filled in with the length of the segment.
+ */
+
+void Path::TangentOnSegAt(double at, NR::Point const &iS, PathDescrLineTo const &fin,
+ NR::Point &pos, NR::Point &tgt, double &len)
+{
+ NR::Point const iE = fin.p;
+ NR::Point const seg = iE - iS;
+ double const l = L2(seg);
+ if (l <= 0.000001) {
+ pos = iS;
+ tgt = NR::Point(0, 0);
+ len = 0;
+ } else {
+ tgt = seg / l;
+ pos = (1 - at) * iS + at * iE; // in other words, pos = iS + at * seg
+ len = l;
+ }
+}
+
+// barf
+void Path::TangentOnArcAt(double at, const NR::Point &iS, PathDescrArcTo const &fin,
+ NR::Point &pos, NR::Point &tgt, double &len, double &rad)
+{
+ NR::Point const iE = fin.p;
+ double const rx = fin.rx;
+ double const ry = fin.ry;
+ double const angle = fin.angle;
+ bool const large = fin.large;
+ bool const wise = fin.clockwise;
+
+ pos = iS;
+ tgt[0] = tgt[1] = 0;
+ if (rx <= 0.0001 || ry <= 0.0001)
+ return;
+
+ double const sex = iE[0] - iS[0], sey = iE[1] - iS[1];
+ double const ca = cos (angle), sa = sin (angle);
+ double csex = ca * sex + sa * sey;
+ double csey = -sa * sex + ca * sey;
+ csex /= rx;
+ csey /= ry;
+ double l = csex * csex + csey * csey;
+ if (l >= 4)
+ return;
+ double const d = sqrt(std::max(1 - l / 4, 0.0));
+ double csdx = csey;
+ double csdy = -csex;
+ l = sqrt(l);
+ csdx /= l;
+ csdy /= l;
+ csdx *= d;
+ csdy *= d;
+
+ double sang;
+ double eang;
+ double rax = -csdx - csex / 2;
+ double ray = -csdy - csey / 2;
+ if (rax < -1)
+ {
+ sang = M_PI;
+ }
+ else if (rax > 1)
+ {
+ sang = 0;
+ }
+ else
+ {
+ sang = acos (rax);
+ if (ray < 0)
+ sang = 2 * M_PI - sang;
+ }
+ rax = -csdx + csex / 2;
+ ray = -csdy + csey / 2;
+ if (rax < -1)
+ {
+ eang = M_PI;
+ }
+ else if (rax > 1)
+ {
+ eang = 0;
+ }
+ else
+ {
+ eang = acos (rax);
+ if (ray < 0)
+ eang = 2 * M_PI - eang;
+ }
+
+ csdx *= rx;
+ csdy *= ry;
+ double drx = ca * csdx - sa * csdy;
+ double dry = sa * csdx + ca * csdy;
+
+ if (wise)
+ {
+ if (large == true)
+ {
+ drx = -drx;
+ dry = -dry;
+ double swap = eang;
+ eang = sang;
+ sang = swap;
+ eang += M_PI;
+ sang += M_PI;
+ if (eang >= 2 * M_PI)
+ eang -= 2 * M_PI;
+ if (sang >= 2 * M_PI)
+ sang -= 2 * M_PI;
+ }
+ }
+ else
+ {
+ if (large == false)
+ {
+ drx = -drx;
+ dry = -dry;
+ double swap = eang;
+ eang = sang;
+ sang = swap;
+ eang += M_PI;
+ sang += M_PI;
+ if (eang >= 2 * M_PI)
+ eang -= 2 * M_PI;
+ if (sang >= 2 * M_PI)
+ sang -= 2 * M_PI;
+ }
+ }
+ drx += (iS[0] + iE[0]) / 2;
+ dry += (iS[1] + iE[1]) / 2;
+
+ if (wise) {
+ if (sang < eang)
+ sang += 2 * M_PI;
+ double b = sang * (1 - at) + eang * at;
+ double cb = cos (b), sb = sin (b);
+ pos[0] = drx + ca * rx * cb - sa * ry * sb;
+ pos[1] = dry + sa * rx * cb + ca * ry * sb;
+ tgt[0] = ca * rx * sb + sa * ry * cb;
+ tgt[1] = sa * rx * sb - ca * ry * cb;
+ NR::Point dtgt;
+ dtgt[0] = -ca * rx * cb + sa * ry * sb;
+ dtgt[1] = -sa * rx * cb - ca * ry * sb;
+ len = L2(tgt);
+ rad = len * dot(tgt, tgt) / (tgt[0] * dtgt[1] - tgt[1] * dtgt[0]);
+ tgt /= len;
+ }
+ else
+ {
+ if (sang > eang)
+ sang -= 2 * M_PI;
+ double b = sang * (1 - at) + eang * at;
+ double cb = cos (b), sb = sin (b);
+ pos[0] = drx + ca * rx * cb - sa * ry * sb;
+ pos[1] = dry + sa * rx * cb + ca * ry * sb;
+ tgt[0] = ca * rx * sb + sa * ry * cb;
+ tgt[1] = sa * rx * sb - ca * ry * cb;
+ NR::Point dtgt;
+ dtgt[0] = -ca * rx * cb + sa * ry * sb;
+ dtgt[1] = -sa * rx * cb - ca * ry * sb;
+ len = L2(tgt);
+ rad = len * dot(tgt, tgt) / (tgt[0] * dtgt[1] - tgt[1] * dtgt[0]);
+ tgt /= len;
+ }
+}
+void
+Path::TangentOnCubAt (double at, NR::Point const &iS, PathDescrCubicTo const &fin, bool before,
+ NR::Point &pos, NR::Point &tgt, double &len, double &rad)
+{
+ const NR::Point E = fin.p;
+ const NR::Point Sd = fin.start;
+ const NR::Point Ed = fin.end;
+
+ pos = iS;
+ tgt = NR::Point(0,0);
+ len = rad = 0;
+
+ const NR::Point A = Sd + Ed - 2*E + 2*iS;
+ const NR::Point B = 0.5*(Ed - Sd);
+ const NR::Point C = 0.25*(6*E - 6*iS - Sd - Ed);
+ const NR::Point D = 0.125*(4*iS + 4*E - Ed + Sd);
+ const double atb = at - 0.5;
+ pos = (atb * atb * atb)*A + (atb * atb)*B + atb*C + D;
+ const NR::Point der = (3 * atb * atb)*A + (2 * atb)*B + C;
+ const NR::Point dder = (6 * atb)*A + 2*B;
+ const NR::Point ddder = 6 * A;
+
+ double l = NR::L2 (der);
+ // lots of nasty cases. inversion points are sadly too common...
+ if (l <= 0.0001) {
+ len = 0;
+ l = L2(dder);
+ if (l <= 0.0001) {
+ l = L2(ddder);
+ if (l <= 0.0001) {
+ // pas de segment....
+ return;
+ }
+ rad = 100000000;
+ tgt = ddder / l;
+ if (before) {
+ tgt = -tgt;
+ }
+ return;
+ }
+ rad = -l * (dot(dder,dder)) / (cross(ddder,dder));
+ tgt = dder / l;
+ if (before) {
+ tgt = -tgt;
+ }
+ return;
+ }
+ len = l;
+
+ rad = -l * (dot(der,der)) / (cross(dder,der));
+
+ tgt = der / l;
+}
+
+void
+Path::TangentOnBezAt (double at, NR::Point const &iS,
+ PathDescrIntermBezierTo & mid,
+ PathDescrBezierTo & fin, bool before, NR::Point & pos,
+ NR::Point & tgt, double &len, double &rad)
+{
+ pos = iS;
+ tgt = NR::Point(0,0);
+ len = rad = 0;
+
+ const NR::Point A = fin.p + iS - 2*mid.p;
+ const NR::Point B = 2*mid.p - 2 * iS;
+ const NR::Point C = iS;
+
+ pos = at * at * A + at * B + C;
+ const NR::Point der = 2 * at * A + B;
+ const NR::Point dder = 2 * A;
+ double l = NR::L2(der);
+
+ if (l <= 0.0001) {
+ l = NR::L2(dder);
+ if (l <= 0.0001) {
+ // pas de segment....
+ // Not a segment.
+ return;
+ }
+ rad = 100000000; // Why this number?
+ tgt = dder / l;
+ if (before) {
+ tgt = -tgt;
+ }
+ return;
+ }
+ len = l;
+ rad = -l * (dot(der,der)) / (cross(dder,der));
+
+ tgt = der / l;
+}
+
+void
+Path::OutlineJoin (Path * dest, NR::Point pos, NR::Point stNor, NR::Point enNor, double width,
+ JoinType join, double miter)
+{
+ const double angSi = cross (enNor,stNor);
+ const double angCo = dot (stNor, enNor);
+ // 1/1000 is very big/ugly, but otherwise it stuffs things up a little...
+ // 1/1000 est tres grossier, mais sinon ca merde tout azimut
+ if ((width >= 0 && angSi > -0.001)
+ || (width < 0 && angSi < 0.001)) {
+ if (angCo > 0.999) {
+ // straight ahead
+ // tout droit
+ } else if (angCo < -0.999) {
+ // half turn
+ // demit-tour
+ dest->LineTo (pos + width*enNor);
+ } else {
+ dest->LineTo (pos);
+ dest->LineTo (pos + width*enNor);
+ }
+ } else {
+ if (join == join_round) {
+ // Use the ends of the cubic: approximate the arc at the
+ // point where .., and support better the rounding of
+ // coordinates of the end points.
+
+ // utiliser des bouts de cubique: approximation de l'arc (au point ou on en est...), et supporte mieux
+ // l'arrondi des coordonnees des extremites
+ /* double angle=acos(angCo);
+ if ( angCo >= 0 ) {
+ NR::Point stTgt,enTgt;
+ RotCCWTo(stNor,stTgt);
+ RotCCWTo(enNor,enTgt);
+ dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
+ angle*width*stTgt.x,angle*width*stTgt.y,
+ angle*width*enTgt.x,angle*width*enTgt.y);
+ } else {
+ NR::Point biNor;
+ NR::Point stTgt,enTgt,biTgt;
+ biNor.x=stNor.x+enNor.x;
+ biNor.y=stNor.y+enNor.y;
+ double biL=sqrt(biNor.x*biNor.x+biNor.y*biNor.y);
+ biNor.x/=biL;
+ biNor.y/=biL;
+ RotCCWTo(stNor,stTgt);
+ RotCCWTo(enNor,enTgt);
+ RotCCWTo(biNor,biTgt);
+ dest->CubicTo(pos.x+width*biNor.x,pos.y+width*biNor.y,
+ angle*width*stTgt.x,angle*width*stTgt.y,
+ angle*width*biTgt.x,angle*width*biTgt.y);
+ dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
+ angle*width*biTgt.x,angle*width*biTgt.y,
+ angle*width*enTgt.x,angle*width*enTgt.y);
+ }*/
+ if (width > 0) {
+ dest->ArcTo (pos + width*enNor,
+ 1.0001 * width, 1.0001 * width, 0.0, false, true);
+ } else {
+ dest->ArcTo (pos + width*enNor,
+ -1.0001 * width, -1.0001 * width, 0.0, false,
+ false);
+ }
+ } else if (join == join_pointy) {
+ NR::Point const biss = unit_vector(NR::rot90( stNor - enNor ));
+ double c2 = NR::dot (biss, enNor);
+ double l = width / c2;
+ if ( fabs(l) > miter) {
+ dest->LineTo (pos + width*enNor);
+ } else {
+ dest->LineTo (pos+l*biss);
+ dest->LineTo (pos+width*enNor);
+ }
+ } else {
+ dest->LineTo (pos + width*enNor);
+ }
+ }
+}
+
+// les callbacks
+
+// see http://www.home.unix-ag.org/simon/sketch/pathstroke.py to understand what's happening here
+
+void
+Path::RecStdCubicTo (outline_callback_data * data, double tol, double width,
+ int lev)
+{
+ NR::Point stPos, miPos, enPos;
+ NR::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
+ double stRad, miRad, enRad;
+ double stTle, miTle, enTle;
+ // un cubic
+ {
+ PathDescrCubicTo temp(NR::Point(data->x2, data->y2),
+ NR::Point(data->d.c.dx1, data->d.c.dy1),
+ NR::Point(data->d.c.dx2, data->d.c.dy2));
+
+ NR::Point initial_point(data->x1, data->y1);
+ TangentOnCubAt (0.0, initial_point, temp, false, stPos, stTgt, stTle,
+ stRad);
+ TangentOnCubAt (0.5, initial_point, temp, false, miPos, miTgt, miTle,
+ miRad);
+ TangentOnCubAt (1.0, initial_point, temp, true, enPos, enTgt, enTle,
+ enRad);
+ stNor=stTgt.cw();
+ miNor=miTgt.cw();
+ enNor=enTgt.cw();
+ }
+
+ double stGue = 1, miGue = 1, enGue = 1;
+ // correction of the lengths of the tangent to the offset
+ // if you don't see why i wrote that, draw a little figure and everything will be clear
+ if (fabs (stRad) > 0.01)
+ stGue += width / stRad;
+ if (fabs (miRad) > 0.01)
+ miGue += width / miRad;
+ if (fabs (enRad) > 0.01)
+ enGue += width / enRad;
+ stGue *= stTle;
+ miGue *= miTle;
+ enGue *= enTle;
+
+
+ if (lev <= 0) {
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*stTgt,
+ enGue*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->tSt;
+ data->dest->descr_cmd[n_d]->tEn = data->tEn;
+ }
+ return;
+ }
+
+ NR::Point chk;
+ const NR::Point req = miPos + width * miNor;
+ {
+ PathDescrCubicTo temp(enPos + width * enNor,
+ stGue * stTgt,
+ enGue * enTgt);
+ double chTle, chRad;
+ NR::Point chTgt;
+ TangentOnCubAt (0.5, stPos+width*stNor,
+ temp, false, chk, chTgt, chTle, chRad);
+ }
+ const NR::Point diff = req - chk;
+ const double err = dot(diff,diff);
+ if (err <= tol ) { // tolerance is given as a quadratic value, no need to use tol*tol here
+// printf("%f <= %f %i\n",err,tol,lev);
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*stTgt,
+ enGue*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->tSt;
+ data->dest->descr_cmd[n_d]->tEn = data->tEn;
+ }
+ } else {
+ outline_callback_data desc = *data;
+
+ desc.tSt = data->tSt;
+ desc.tEn = (data->tSt + data->tEn) / 2;
+ desc.x1 = data->x1;
+ desc.y1 = data->y1;
+ desc.x2 = miPos[0];
+ desc.y2 = miPos[1];
+ desc.d.c.dx1 = 0.5 * stTle * stTgt[0];
+ desc.d.c.dy1 = 0.5 * stTle * stTgt[1];
+ desc.d.c.dx2 = 0.5 * miTle * miTgt[0];
+ desc.d.c.dy2 = 0.5 * miTle * miTgt[1];
+ RecStdCubicTo (&desc, tol, width, lev - 1);
+
+ desc.tSt = (data->tSt + data->tEn) / 2;
+ desc.tEn = data->tEn;
+ desc.x1 = miPos[0];
+ desc.y1 = miPos[1];
+ desc.x2 = data->x2;
+ desc.y2 = data->y2;
+ desc.d.c.dx1 = 0.5 * miTle * miTgt[0];
+ desc.d.c.dy1 = 0.5 * miTle * miTgt[1];
+ desc.d.c.dx2 = 0.5 * enTle * enTgt[0];
+ desc.d.c.dy2 = 0.5 * enTle * enTgt[1];
+ RecStdCubicTo (&desc, tol, width, lev - 1);
+ }
+}
+
+void
+Path::StdCubicTo (Path::outline_callback_data * data, double tol, double width)
+{
+// fflush (stdout);
+ RecStdCubicTo (data, tol, width, 8);
+}
+
+void
+Path::StdBezierTo (Path::outline_callback_data * data, double tol, double width)
+{
+ PathDescrBezierTo tempb(NR::Point(data->x2, data->y2), 1);
+ PathDescrIntermBezierTo tempi(NR::Point(data->d.b.mx, data->d.b.my));
+ NR::Point stPos, enPos, stTgt, enTgt;
+ double stRad, enRad, stTle, enTle;
+ NR::Point tmp(data->x1,data->y1);
+ TangentOnBezAt (0.0, tmp, tempi, tempb, false, stPos, stTgt,
+ stTle, stRad);
+ TangentOnBezAt (1.0, tmp, tempi, tempb, true, enPos, enTgt,
+ enTle, enRad);
+ data->d.c.dx1 = stTle * stTgt[0];
+ data->d.c.dy1 = stTle * stTgt[1];
+ data->d.c.dx2 = enTle * enTgt[0];
+ data->d.c.dy2 = enTle * enTgt[1];
+ RecStdCubicTo (data, tol, width, 8);
+}
+
+void
+Path::RecStdArcTo (outline_callback_data * data, double tol, double width,
+ int lev)
+{
+ NR::Point stPos, miPos, enPos;
+ NR::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
+ double stRad, miRad, enRad;
+ double stTle, miTle, enTle;
+ // un cubic
+ {
+ PathDescrArcTo temp(NR::Point(data->x2, data->y2),
+ data->d.a.rx, data->d.a.ry,
+ data->d.a.angle, data->d.a.large, data->d.a.clock);
+
+ NR::Point tmp(data->x1,data->y1);
+ TangentOnArcAt (data->d.a.stA, tmp, temp, stPos, stTgt,
+ stTle, stRad);
+ TangentOnArcAt ((data->d.a.stA + data->d.a.enA) / 2, tmp,
+ temp, miPos, miTgt, miTle, miRad);
+ TangentOnArcAt (data->d.a.enA, tmp, temp, enPos, enTgt,
+ enTle, enRad);
+ stNor=stTgt.cw();
+ miNor=miTgt.cw();
+ enNor=enTgt.cw();
+ }
+
+ double stGue = 1, miGue = 1, enGue = 1;
+ if (fabs (stRad) > 0.01)
+ stGue += width / stRad;
+ if (fabs (miRad) > 0.01)
+ miGue += width / miRad;
+ if (fabs (enRad) > 0.01)
+ enGue += width / enRad;
+ stGue *= stTle;
+ miGue *= miTle;
+ enGue *= enTle;
+ double sang, eang;
+ {
+ NR::Point tms(data->x1,data->y1),tme(data->x2,data->y2);
+ ArcAngles (tms,tme, data->d.a.rx,
+ data->d.a.ry, data->d.a.angle, data->d.a.large, !data->d.a.clock,
+ sang, eang);
+ }
+ double scal = eang - sang;
+ if (scal < 0)
+ scal += 2 * M_PI;
+ if (scal > 2 * M_PI)
+ scal -= 2 * M_PI;
+ scal *= data->d.a.enA - data->d.a.stA;
+
+ if (lev <= 0)
+ {
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*scal*stTgt,
+ enGue*scal*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
+ data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
+ }
+ return;
+ }
+
+ NR::Point chk;
+ const NR::Point req = miPos + width*miNor;
+ {
+ PathDescrCubicTo temp(enPos + width * enNor, stGue * scal * stTgt, enGue * scal * enTgt);
+ double chTle, chRad;
+ NR::Point chTgt;
+ TangentOnCubAt (0.5, stPos+width*stNor,
+ temp, false, chk, chTgt, chTle, chRad);
+ }
+ const NR::Point diff = req - chk;
+ const double err = (dot(diff,diff));
+ if (err <= tol * tol)
+ {
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*scal*stTgt,
+ enGue*scal*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
+ data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
+ }
+ } else {
+ outline_callback_data desc = *data;
+
+ desc.d.a.stA = data->d.a.stA;
+ desc.d.a.enA = (data->d.a.stA + data->d.a.enA) / 2;
+ RecStdArcTo (&desc, tol, width, lev - 1);
+
+ desc.d.a.stA = (data->d.a.stA + data->d.a.enA) / 2;
+ desc.d.a.enA = data->d.a.enA;
+ RecStdArcTo (&desc, tol, width, lev - 1);
+ }
+}
+
+void
+Path::StdArcTo (Path::outline_callback_data * data, double tol, double width)
+{
+ data->d.a.stA = 0.0;
+ data->d.a.enA = 1.0;
+ RecStdArcTo (data, tol, width, 8);
+}
+
+/*
+ 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 :