PathOutline.cpp revision a50d91ef3645a5b439868b47f2d26ba898f845d9
/*
* nlivarot
*
* Created by fred on Fri Nov 28 2003.
*
*/
#include "livarot/path-description.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
{
if ( descr_flags & descr_adding_bezier ) {
CancelBezier();
}
if ( descr_flags & descr_doing_subpath ) {
CloseSubpath();
}
return;
}
return;
}
dest->SetBackData(false);
// we repeat the offset contour creation for each subpath
int curP = 0;
do {
do {
curP++;
break;
}
if (typ == descr_moveto) {
break;
}
}
// 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"
curD--;
}
if (typ == descr_moveto) {
// rev->Close();
curD--;
} else if (typ == descr_forced) {
// rev->Close();
curD--;
} else if (typ == descr_lineto) {
curD--;
} else if (typ == descr_cubicto) {
curD--;
} else if (typ == descr_arcto) {
curD--;
} else if (typ == descr_bezierto) {
curD--;
} else if (typ == descr_interm_bezier) {
// pas trouve le debut!?
// Not find the start?!
} else {
}
rev->EndBezierTo ();
}
} else {
curD--;
}
}
// offset the paths and glue everything
// actual offseting is done in SubContractOutline()
if (needClose) {
} else {
if (butt == butt_round) {
} else if (butt == butt_square) {
} else if (butt == butt_pointy) {
} else {
}
if (butt == butt_round) {
} else if (butt == butt_square) {
} else if (butt == butt_pointy) {
} else {
}
}
} // if (curD > lastM)
} // if (curP > lastM + 1)
delete rev;
}
// versions for outlining closed path: they only make one side of the offset contour
void
double miter)
{
if (descr_flags & descr_adding_bezier) {
CancelBezier();
}
if (descr_flags & descr_doing_subpath) {
CloseSubpath();
}
dest->SetBackData (false);
}
void
double miter)
{
if ( descr_flags & descr_adding_bezier ) {
CancelBezier();
}
if ( descr_flags & descr_doing_subpath ) {
CloseSubpath();
}
dest->SetBackData (false);
int curP = 0;
do {
do {
curP++;
if (typ == descr_moveto) break;
// Otherwise there's only one point. (tr: or "only a point")
// [sinon il n'y a qu'un point]
if (typ == descr_moveto) {
curD--;
} else if (typ == descr_forced) {
curD--;
} else if (typ == descr_lineto) {
curD--;
} else if (typ == descr_cubicto) {
curD--;
} else if (typ == descr_arcto) {
curD--;
} else if (typ == descr_bezierto) {
curD--;
} else if (typ == descr_interm_bezier) {
// pas trouve le debut!?
} else {
}
rev->EndBezierTo ();
}
} else {
curD--;
}
}
}
}
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
{
int curP = 1;
// le moveto
{
if ( firstTyp != descr_moveto ) {
curP = 0;
} else {
}
}
bool doFirst = true;
// et le reste, 1 par 1
{
if (nType == descr_forced) {
curP++;
} else if (nType == descr_moveto) {
// et on avance
if (doFirst) {
} else {
if (closeIfNeeded) {
} else {
stTle);
enTle);
// jointure
{
}
// jointure
{
}
}
}
}
curP++;
}
else if (nType == descr_close)
{
if (doFirst == false)
{
{
}
else
{
// jointure
{
}
// jointure
{
}
}
}
doFirst = true;
curP++;
}
else if (nType == descr_lineto)
{
// et on avance
// test de nullité du segment
{
} else {
curP++;
continue;
}
}
if (doFirst)
{
doFirst = false;
if (skipMoveto)
{
skipMoveto = false;
}
else
}
else
{
// jointure
}
if (n_d >= 0)
{
}
curP++;
}
else if (nType == descr_cubicto)
{
// test de nullite du segment
{
curP++;
continue;
}
// et on avance
if (doFirst)
{
doFirst = false;
if (skipMoveto)
{
skipMoveto = false;
}
else
}
else
{
// jointure
}
curP++;
}
else if (nType == descr_arcto)
{
// test de nullité du segment
{
curP++;
continue;
}
// et on avance
stRad);
enRad);
if (doFirst)
{
doFirst = false;
if (skipMoveto)
{
skipMoveto = false;
}
else
}
else
{
// jointure
}
curP++;
}
else if (nType == descr_bezierto)
{
continue;
}
curP++;
if (nbInterm <= 0) {
// et on avance
if (doFirst) {
doFirst = false;
if (skipMoveto) {
skipMoveto = false;
} else {
// jointure
}
if (n_d >= 0) {
}
} else if (nbInterm == 1) {
// et on avance
if (doFirst) {
doFirst = false;
if (skipMoveto) {
skipMoveto = false;
} else {
// jointure
}
} else if (nbInterm > 1) {
ip++;
// et on avance
if (stTle > 0) {
if (doFirst) {
doFirst = false;
if (skipMoveto) {
skipMoveto = false;
} else {
// jointure
// dest->LineTo(curX+width*stNor.x,curY+width*stNor.y);
}
}
for (int k = 0; k < nbInterm - 1; k++) {
ip++;
// double stw=(bw+cw)/2;
}
{
// double stw=(bw+cw)/2;
}
}
// et on avance
}
}
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
{
case descr_lineto:
{
return true;
}
return false;
}
case descr_cubicto:
{
return true;
}
return false;
}
case descr_arcto:
{
return true;
}
}
return false;
}
case descr_bezierto:
{
{
return true;
}
return false;
}
{
return true;
}
}
return false;
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.
*/
{
if (l <= 0.000001) {
len = 0;
} else {
len = l;
}
}
// barf
{
return;
if (l >= 4)
return;
l = sqrt(l);
csdx /= l;
csdy /= l;
csdx *= d;
csdy *= d;
double sang;
double eang;
if (rax < -1)
{
}
else if (rax > 1)
{
sang = 0;
}
else
{
if (ray < 0)
}
if (rax < -1)
{
}
else if (rax > 1)
{
eang = 0;
}
else
{
if (ray < 0)
}
if (wise)
{
if (large == true)
{
}
}
else
{
if (large == false)
{
}
}
if (wise) {
}
else
{
}
}
void
{
// lots of nasty cases. inversion points are sadly too common...
if (l <= 0.0001) {
len = 0;
if (l <= 0.0001) {
if (l <= 0.0001) {
// pas de segment....
return;
}
rad = 100000000;
if (before) {
}
return;
}
if (before) {
}
return;
}
len = l;
}
void
{
if (l <= 0.0001) {
if (l <= 0.0001) {
// pas de segment....
// Not a segment.
return;
}
if (before) {
}
return;
}
len = l;
}
void
Path::OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point enNor, double width,
{
/*
Arbitrarily decide if we're on the inside or outside of a half turn.
A turn of 180 degrees (line path leaves the node in the same direction as it arrived)
is symmetric and has no real inside and outside. However when outlining we shall handle
one path as inside and the reverse path as outside. Handling both as inside joins (as
was done previously) will cut off round joins. Handling both as outside joins could
ideally work because both should fall together, but it seems that this causes many
extra nodes (due to rounding errors). Solution: for the 'half turn'-case toggle
*/
static bool TurnInside = true;
} else {
if ((dest->descr_cmd[dest->descr_cmd.size() - 1]->getType() == descr_lineto) && (nType == descr_lineto)) {
PathDescrLineTo* nLine = dynamic_cast<PathDescrLineTo*>(dest->descr_cmd[dest->descr_cmd.size() - 1]);
} else {
}
} else {
// dest->LineTo (pos); // redundant
}
} else { // This is an outside join -> chosen JoinType should be applied.
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 ) {
Geom::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 {
Geom::Point biNor;
Geom::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) {
} else {
false);
}
} else if (join == join_pointy) {
} else {
PathDescrLineTo* nLine = dynamic_cast<PathDescrLineTo*>(dest->descr_cmd[dest->descr_cmd.size() - 1]);
} else {
}
if (nType != descr_lineto)
}
} else { // Bevel join
}
}
}
}
// les callbacks
// see http://www.home.unix-ag.org/simon/sketch/pathstroke.py to understand what's happening here
void
int lev)
{
// un cubic
{
stRad);
miRad);
enRad);
}
// 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 (lev <= 0) {
if (n_d >= 0) {
}
return;
}
{
}
// printf("%f <= %f %i\n",err,tol,lev);
if (n_d >= 0) {
}
} else {
}
}
void
{
// fflush (stdout);
}
void
{
}
void
int lev)
{
// un cubic
{
}
{
}
if (scal < 0)
if (lev <= 0)
{
if (n_d >= 0) {
}
return;
}
{
}
{
if (n_d >= 0) {
}
} else {
}
}
void
{
}
/*
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 :