diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:24:48 +0000 |
commit | cca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch) | |
tree | 146f39ded1c938019e1ed42d30923c2ac9e86789 /src/livarot/PathStroke.cpp | |
parent | Initial commit. (diff) | |
download | inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip |
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/livarot/PathStroke.cpp | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/src/livarot/PathStroke.cpp b/src/livarot/PathStroke.cpp new file mode 100644 index 0000000..e70ce36 --- /dev/null +++ b/src/livarot/PathStroke.cpp @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: + * see git history + * Fred + * + * Copyright (C) 2018 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "Path.h" +#include "Shape.h" +#include <2geom/transforms.h> + +/* + * stroking polylines into a Shape instance + * grunt work. + * if the goal is to raster the stroke, polyline stroke->polygon->uncrossed polygon->raster is grossly + * inefficient (but reuse the intersector, so that's what a lazy programmer like me does). the correct way would be + * to set up a supersampled buffer, raster each polyline stroke's part (one part per segment in the polyline, plus + * each join) because these are all convex polygons, then transform in alpha values + */ + +// until i find something better +static Geom::Point StrokeNormalize(const Geom::Point value) { + double length = L2(value); + if ( length < 0.0000001 ) { + return Geom::Point(0, 0); + } else { + return value/length; + } +} + +// faster version if length is known +static Geom::Point StrokeNormalize(const Geom::Point value, double length) { + if ( length < 0.0000001 ) { + return Geom::Point(0, 0); + } else { + return value/length; + } +} + +void Path::Stroke(Shape *dest, bool doClose, double width, JoinType join, + ButtType butt, double miter, bool justAdd) +{ + if (dest == nullptr) { + return; + } + + if (justAdd == false) { + dest->Reset(3 * pts.size(), 3 * pts.size()); + } + + dest->MakeBackData(false); + + int lastM = 0; + while (lastM < int(pts.size())) { + + int lastP = lastM + 1; + while (lastP < int(pts.size()) // select one subpath + && (pts[lastP].isMoveTo == polyline_lineto + || pts[lastP].isMoveTo == polyline_forced)) + { + lastP++; + } + + if ( lastP > lastM+1 ) { + Geom::Point sbStart = pts[lastM].p; + Geom::Point sbEnd = pts[lastP - 1].p; + // if ( pts[lastP - 1].closed ) { // this is correct, but this bugs text rendering (doesn't close text stroke)... + if ( Geom::LInfty(sbEnd-sbStart) < 0.00001 ) { // why close lines that shouldn't be closed? + // ah I see, because close is defined here for + // a whole path and should be defined per subpath. + // debut==fin => ferme (on devrait garder un element pour les close(), mais tant pis) + DoStroke(lastM, lastP - lastM, dest, true, width, join, butt, miter, true); + } else { + DoStroke(lastM, lastP - lastM, dest, doClose, width, join, butt, miter, true); + } + } else if (butt == butt_round) { // special case: zero length round butt is a circle + int last[2] = { -1, -1 }; + Geom::Point dir; + dir[0] = 1; + dir[1] = 0; + Geom::Point pos = pts[lastM].p; + DoButt(dest, width, butt, pos, dir, last[RIGHT], last[LEFT]); + int end[2]; + dir = -dir; + DoButt(dest, width, butt, pos, dir, end[LEFT], end[RIGHT]); + dest->AddEdge (end[LEFT], last[LEFT]); + dest->AddEdge (last[RIGHT], end[RIGHT]); + } + lastM = lastP; + } +} + +void Path::DoStroke(int off, int N, Shape *dest, bool doClose, double width, JoinType join, + ButtType butt, double miter, bool /*justAdd*/) +{ + if (N <= 1) { + return; + } + + Geom::Point prevP, nextP; + int prevI, nextI; + int upTo; + + int curI = 0; + Geom::Point curP = pts[off].p; + + if (doClose) { + + prevI = N - 1; + while (prevI > 0) { + prevP = pts[off + prevI].p; + Geom::Point diff = curP - prevP; + double dist = dot(diff, diff); + if (dist > 0.001) { + break; + } + prevI--; + } + if (prevI <= 0) { + return; + } + upTo = prevI; + + } else { + + prevP = curP; + prevI = curI; + upTo = N - 1; + } + + { + nextI = 1; + while (nextI <= upTo) { + nextP = pts[off + nextI].p; + Geom::Point diff = curP - nextP; + double dist = dot(diff, diff); + if (dist > 0.0) { // more tolerance for the first distance, to give the cap the right direction + break; + } + nextI++; + } + if (nextI > upTo) { + if (butt == butt_round) { // special case: zero length round butt is a circle + int last[2] = { -1, -1 }; + Geom::Point dir; + dir[0] = 1; + dir[1] = 0; + DoButt(dest, width, butt, curP, dir, last[RIGHT], last[LEFT]); + int end[2]; + dir = -dir; + DoButt(dest, width, butt, curP, dir, end[LEFT], end[RIGHT]); + dest->AddEdge (end[LEFT], last[LEFT]); + dest->AddEdge (last[RIGHT], end[RIGHT]); + } + return; + } + } + + int start[2] = { -1, -1 }; + int last[2] = { -1, -1 }; + Geom::Point prevD = curP - prevP; + Geom::Point nextD = nextP - curP; + double prevLe = Geom::L2(prevD); + double nextLe = Geom::L2(nextD); + prevD = StrokeNormalize(prevD, prevLe); + nextD = StrokeNormalize(nextD, nextLe); + + if (doClose) { + DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, start, last); + } else { + nextD = -nextD; + DoButt(dest, width, butt, curP, nextD, last[RIGHT], last[LEFT]); + nextD = -nextD; + } + + do { + prevP = curP; + prevI = curI; + curP = nextP; + curI = nextI; + prevD = nextD; + prevLe = nextLe; + nextI++; + while (nextI <= upTo) { + nextP = pts[off + nextI].p; + Geom::Point diff = curP - nextP; + double dist = dot(diff, diff); + if (dist > 0.001 || (nextI == upTo && dist > 0.0)) { // more tolerance for the last distance too, for the right cap direction + break; + } + nextI++; + } + if (nextI > upTo) { + break; + } + + nextD = nextP - curP; + nextLe = Geom::L2(nextD); + nextD = StrokeNormalize(nextD, nextLe); + int nSt[2] = { -1, -1 }; + int nEn[2] = { -1, -1 }; + DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn); + dest->AddEdge(nSt[LEFT], last[LEFT]); + last[LEFT] = nEn[LEFT]; + dest->AddEdge(last[RIGHT], nSt[RIGHT]); + last[RIGHT] = nEn[RIGHT]; + } while (nextI <= upTo); + + if (doClose) { + /* prevP=curP; + prevI=curI; + curP=nextP; + curI=nextI; + prevD=nextD;*/ + nextP = pts[off].p; + + nextD = nextP - curP; + nextLe = Geom::L2(nextD); + nextD = StrokeNormalize(nextD, nextLe); + int nSt[2] = { -1, -1 }; + int nEn[2] = { -1, -1 }; + DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn); + dest->AddEdge (nSt[LEFT], last[LEFT]); + last[LEFT] = nEn[LEFT]; + dest->AddEdge (last[RIGHT], nSt[RIGHT]); + last[RIGHT] = nEn[RIGHT]; + + dest->AddEdge (start[LEFT], last[LEFT]); + dest->AddEdge (last[RIGHT], start[RIGHT]); + + } else { + + int end[2]; + DoButt (dest, width, butt, curP, prevD, end[LEFT], end[RIGHT]); + dest->AddEdge (end[LEFT], last[LEFT]); + dest->AddEdge (last[RIGHT], end[RIGHT]); + } +} + + +void Path::DoButt(Shape *dest, double width, ButtType butt, Geom::Point pos, Geom::Point dir, + int &leftNo, int &rightNo) +{ + Geom::Point nor; + nor = dir.ccw(); + + if (butt == butt_square) + { + Geom::Point x; + x = pos + width * dir + width * nor; + int bleftNo = dest->AddPoint (x); + x = pos + width * dir - width * nor; + int brightNo = dest->AddPoint (x); + x = pos + width * nor; + leftNo = dest->AddPoint (x); + x = pos - width * nor; + rightNo = dest->AddPoint (x); + dest->AddEdge (rightNo, brightNo); + dest->AddEdge (brightNo, bleftNo); + dest->AddEdge (bleftNo, leftNo); + } + else if (butt == butt_pointy) + { + leftNo = dest->AddPoint (pos + width * nor); + rightNo = dest->AddPoint (pos - width * nor); + int mid = dest->AddPoint (pos + width * dir); + dest->AddEdge (rightNo, mid); + dest->AddEdge (mid, leftNo); + } + else if (butt == butt_round) + { + const Geom::Point sx = pos + width * nor; + const Geom::Point ex = pos - width * nor; + leftNo = dest->AddPoint (sx); + rightNo = dest->AddPoint (ex); + + RecRound (dest, rightNo, leftNo, ex, sx, -nor, nor, pos, width); + } + else + { + leftNo = dest->AddPoint (pos + width * nor); + rightNo = dest->AddPoint (pos - width * nor); + dest->AddEdge (rightNo, leftNo); + } +} + + +void Path::DoJoin (Shape *dest, double width, JoinType join, Geom::Point pos, Geom::Point prev, + Geom::Point next, double miter, double /*prevL*/, double /*nextL*/, + int *stNo, int *enNo) +{ + Geom::Point pnor = prev.ccw(); + Geom::Point nnor = next.ccw(); + double angSi = cross(prev, next); + + /* FIXED: this special case caused bug 1028953 */ + if (angSi > -0.0001 && angSi < 0.0001) { + double angCo = dot (prev, next); + if (angCo > 0.9999) { + // tout droit + stNo[LEFT] = enNo[LEFT] = dest->AddPoint(pos + width * pnor); + stNo[RIGHT] = enNo[RIGHT] = dest->AddPoint(pos - width * pnor); + } else { + // demi-tour + const Geom::Point sx = pos + width * pnor; + const Geom::Point ex = pos - width * pnor; + stNo[LEFT] = enNo[RIGHT] = dest->AddPoint (sx); + stNo[RIGHT] = enNo[LEFT] = dest->AddPoint (ex); + if (join == join_round) { + RecRound (dest, enNo[LEFT], stNo[LEFT], ex, sx, -pnor, pnor, pos, width); + dest->AddEdge(stNo[RIGHT], enNo[RIGHT]); + } else { + dest->AddEdge(enNo[LEFT], stNo[LEFT]); + dest->AddEdge(stNo[RIGHT], enNo[RIGHT]); // two times because both are crossing each other + } + } + return; + } + + if (angSi < 0) { + int midNo = dest->AddPoint(pos); + stNo[LEFT] = dest->AddPoint(pos + width * pnor); + enNo[LEFT] = dest->AddPoint(pos + width * nnor); + dest->AddEdge(enNo[LEFT], midNo); + dest->AddEdge(midNo, stNo[LEFT]); + + if (join == join_pointy) { + + stNo[RIGHT] = dest->AddPoint(pos - width * pnor); + enNo[RIGHT] = dest->AddPoint(pos - width * nnor); + + const Geom::Point biss = StrokeNormalize(prev - next); + double c2 = dot(biss, nnor); + double l = width / c2; + double emiter = width * c2; + if (emiter < miter) { + emiter = miter; + } + + if (fabs(l) < miter) { + int const n = dest->AddPoint(pos - l * biss); + dest->AddEdge(stNo[RIGHT], n); + dest->AddEdge(n, enNo[RIGHT]); + } else { + dest->AddEdge(stNo[RIGHT], enNo[RIGHT]); + } + + } else if (join == join_round) { + Geom::Point sx = pos - width * pnor; + stNo[RIGHT] = dest->AddPoint(sx); + Geom::Point ex = pos - width * nnor; + enNo[RIGHT] = dest->AddPoint(ex); + + RecRound(dest, stNo[RIGHT], enNo[RIGHT], + sx, ex, -pnor, -nnor, pos, width); + + } else { + stNo[RIGHT] = dest->AddPoint(pos - width * pnor); + enNo[RIGHT] = dest->AddPoint(pos - width * nnor); + dest->AddEdge(stNo[RIGHT], enNo[RIGHT]); + } + + } else { + + int midNo = dest->AddPoint(pos); + stNo[RIGHT] = dest->AddPoint(pos - width * pnor); + enNo[RIGHT] = dest->AddPoint(pos - width * nnor); + dest->AddEdge(stNo[RIGHT], midNo); + dest->AddEdge(midNo, enNo[RIGHT]); + + if (join == join_pointy) { + + stNo[LEFT] = dest->AddPoint(pos + width * pnor); + enNo[LEFT] = dest->AddPoint(pos + width * nnor); + + const Geom::Point biss = StrokeNormalize(next - prev); + double c2 = dot(biss, nnor); + double l = width / c2; + double emiter = width * c2; + if (emiter < miter) { + emiter = miter; + } + if ( fabs(l) < miter) { + int const n = dest->AddPoint (pos + l * biss); + dest->AddEdge (enNo[LEFT], n); + dest->AddEdge (n, stNo[LEFT]); + } + else + { + dest->AddEdge (enNo[LEFT], stNo[LEFT]); + } + + } else if (join == join_round) { + + Geom::Point sx = pos + width * pnor; + stNo[LEFT] = dest->AddPoint(sx); + Geom::Point ex = pos + width * nnor; + enNo[LEFT] = dest->AddPoint(ex); + + RecRound(dest, enNo[LEFT], stNo[LEFT], + ex, sx, nnor, pnor, pos, width); + + } else { + stNo[LEFT] = dest->AddPoint(pos + width * pnor); + enNo[LEFT] = dest->AddPoint(pos + width * nnor); + dest->AddEdge(enNo[LEFT], stNo[LEFT]); + } + } +} + + void +Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos, + Geom::Point prev, Geom::Point next, double miter, double /*prevL*/, double /*nextL*/, + int &leftStNo, int &leftEnNo,int pathID,int pieceID,double tID) +{ + Geom::Point pnor=prev.ccw(); + Geom::Point nnor=next.ccw(); + double angSi = cross(prev, next); + if (angSi > -0.0001 && angSi < 0.0001) + { + double angCo = dot (prev, next); + if (angCo > 0.9999) + { + // tout droit + leftEnNo = leftStNo = dest->AddPoint (pos + width * pnor); + } + else + { + // demi-tour + leftStNo = dest->AddPoint (pos + width * pnor); + leftEnNo = dest->AddPoint (pos - width * pnor); + int nEdge=dest->AddEdge (leftEnNo, leftStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + return; + } + if (angSi < 0) + { + /* Geom::Point biss; + biss.x=next.x-prev.x; + biss.y=next.y-prev.y; + double c2=cross(next, biss); + double l=width/c2; + double projn=l*(dot(biss,next)); + double projp=-l*(dot(biss,prev)); + if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) { + double x,y; + x=pos.x+l*biss.x; + y=pos.y+l*biss.y; + leftEnNo=leftStNo=dest->AddPoint(x,y); + } else {*/ + leftStNo = dest->AddPoint (pos + width * pnor); + leftEnNo = dest->AddPoint (pos + width * nnor); +// int midNo = dest->AddPoint (pos); +// int nEdge=dest->AddEdge (leftEnNo, midNo); + int nEdge=dest->AddEdge (leftEnNo, leftStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } +// nEdge=dest->AddEdge (midNo, leftStNo); +// if ( dest->hasBackData() ) { +// dest->ebData[nEdge].pathID=pathID; +// dest->ebData[nEdge].pieceID=pieceID; +// dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; +// } + // } + } + else + { + if (join == join_pointy) + { + leftStNo = dest->AddPoint (pos + width * pnor); + leftEnNo = dest->AddPoint (pos + width * nnor); + + const Geom::Point biss = StrokeNormalize (pnor + nnor); + double c2 = dot (biss, nnor); + double l = width / c2; + double emiter = width * c2; + if (emiter < miter) + emiter = miter; + if (l <= emiter) + { + int nleftStNo = dest->AddPoint (pos + l * biss); + int nEdge=dest->AddEdge (leftEnNo, nleftStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + nEdge=dest->AddEdge (nleftStNo, leftStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + else + { + double s2 = cross(nnor, biss); + double dec = (l - emiter) * c2 / s2; + const Geom::Point tbiss=biss.ccw(); + + int nleftStNo = dest->AddPoint (pos + emiter * biss + dec * tbiss); + int nleftEnNo = dest->AddPoint (pos + emiter * biss - dec * tbiss); + int nEdge=dest->AddEdge (nleftEnNo, nleftStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + nEdge=dest->AddEdge (leftEnNo, nleftEnNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + nEdge=dest->AddEdge (nleftStNo, leftStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + } + else if (join == join_round) + { + const Geom::Point sx = pos + width * pnor; + leftStNo = dest->AddPoint (sx); + const Geom::Point ex = pos + width * nnor; + leftEnNo = dest->AddPoint (ex); + + RecRound(dest, leftEnNo, leftStNo, + sx, ex, pnor, nnor ,pos, width); + + } + else + { + leftStNo = dest->AddPoint (pos + width * pnor); + leftEnNo = dest->AddPoint (pos + width * nnor); + int nEdge=dest->AddEdge (leftEnNo, leftStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + } +} + void +Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos, + Geom::Point prev, Geom::Point next, double miter, double /*prevL*/, + double /*nextL*/, int &rightStNo, int &rightEnNo,int pathID,int pieceID,double tID) +{ + const Geom::Point pnor=prev.ccw(); + const Geom::Point nnor=next.ccw(); + double angSi = cross(prev, next); + if (angSi > -0.0001 && angSi < 0.0001) + { + double angCo = dot (prev, next); + if (angCo > 0.9999) + { + // tout droit + rightEnNo = rightStNo = dest->AddPoint (pos - width*pnor); + } + else + { + // demi-tour + rightEnNo = dest->AddPoint (pos + width*pnor); + rightStNo = dest->AddPoint (pos - width*pnor); + int nEdge=dest->AddEdge (rightStNo, rightEnNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + return; + } + if (angSi < 0) + { + if (join == join_pointy) + { + rightStNo = dest->AddPoint (pos - width*pnor); + rightEnNo = dest->AddPoint (pos - width*nnor); + + const Geom::Point biss = StrokeNormalize (pnor + nnor); + double c2 = dot (biss, nnor); + double l = width / c2; + double emiter = width * c2; + if (emiter < miter) + emiter = miter; + if (l <= emiter) + { + int nrightStNo = dest->AddPoint (pos - l * biss); + int nEdge=dest->AddEdge (rightStNo, nrightStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + nEdge=dest->AddEdge (nrightStNo, rightEnNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + else + { + double s2 = cross(nnor, biss); + double dec = (l - emiter) * c2 / s2; + const Geom::Point tbiss=biss.ccw(); + + int nrightStNo = dest->AddPoint (pos - emiter*biss - dec*tbiss); + int nrightEnNo = dest->AddPoint (pos - emiter*biss + dec*tbiss); + int nEdge=dest->AddEdge (rightStNo, nrightStNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + nEdge=dest->AddEdge (nrightStNo, nrightEnNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + nEdge=dest->AddEdge (nrightEnNo, rightEnNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + } + else if (join == join_round) + { + const Geom::Point sx = pos - width * pnor; + rightStNo = dest->AddPoint (sx); + const Geom::Point ex = pos - width * nnor; + rightEnNo = dest->AddPoint (ex); + + RecRound(dest, rightStNo, rightEnNo, + sx, ex, -pnor, -nnor ,pos, width); + } + else + { + rightStNo = dest->AddPoint (pos - width * pnor); + rightEnNo = dest->AddPoint (pos - width * nnor); + int nEdge=dest->AddEdge (rightStNo, rightEnNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } + } + } + else + { + /* Geom::Point biss; + biss=next.x-prev.x; + biss.y=next.y-prev.y; + double c2=cross(biss, next); + double l=width/c2; + double projn=l*(dot(biss,next)); + double projp=-l*(dot(biss,prev)); + if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) { + double x,y; + x=pos.x+l*biss.x; + y=pos.y+l*biss.y; + rightEnNo=rightStNo=dest->AddPoint(x,y); + } else {*/ + rightStNo = dest->AddPoint (pos - width*pnor); + rightEnNo = dest->AddPoint (pos - width*nnor); +// int midNo = dest->AddPoint (pos); +// int nEdge=dest->AddEdge (rightStNo, midNo); + int nEdge=dest->AddEdge (rightStNo, rightEnNo); + if ( dest->hasBackData() ) { + dest->ebData[nEdge].pathID=pathID; + dest->ebData[nEdge].pieceID=pieceID; + dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; + } +// nEdge=dest->AddEdge (midNo, rightEnNo); +// if ( dest->hasBackData() ) { +// dest->ebData[nEdge].pathID=pathID; +// dest->ebData[nEdge].pieceID=pieceID; +// dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID; +// } + // } + } +} + + +// a very ugly way to produce round joins: doing one (or two, depend on the angle of the join) quadratic bezier curves +// but since most joins are going to be small, nobody will notice -- but somebody noticed and now the ugly stuff is gone! so: + +// a very nice way to produce round joins, caps or dots +void Path::RecRound(Shape *dest, int sNo, int eNo, // start and end index + Geom::Point const &iS, Geom::Point const &iE, // start and end point + Geom::Point const &nS, Geom::Point const &nE, // start and end normal vector + Geom::Point &origine, float width) // center and radius of round +{ + //Geom::Point diff = iS - iE; + //double dist = dot(diff, diff); + if (width < 0.5 || dot(iS - iE, iS - iE)/width < 2.0) { + dest->AddEdge(sNo, eNo); + return; + } + double ang, sia, lod; + if (nS == -nE) { + ang = M_PI; + sia = 1; + } else { + double coa = dot(nS, nE); + sia = cross(nE, nS); + ang = acos(coa); + if ( coa >= 1 ) { + ang = 0; + } + if ( coa <= -1 ) { + ang = M_PI; + } + } + lod = 0.02 + 10 / (10 + width); // limit detail to about 2 degrees (180 * 0.02/Pi degrees) + ang /= lod; + + int nbS = (int) floor(ang); + Geom::Rotate omega(((sia > 0) ? -lod : lod)); + Geom::Point cur = iS - origine; + // StrokeNormalize(cur); + // cur*=width; + int lastNo = sNo; + for (int i = 0; i < nbS; i++) { + cur = cur * omega; + Geom::Point m = origine + cur; + int mNo = dest->AddPoint(m); + dest->AddEdge(lastNo, mNo); + lastNo = mNo; + } + dest->AddEdge(lastNo, eNo); +} + +/* + 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:fileencoding=utf-8:textwidth=99 : |