PathSimplify.cpp revision 6b15695578f07a3f72c4c9475c1a261a3021472a
/*
* nlivarot
*
* Created by fred on Fri Dec 12 2003.
*
*/
#include <libnr/nr-point-matrix-ops.h>
#include "livarot/path-description.h"
/*
* Reassembling polyline segments into cubic bezier patches
* thes functions do not need the back data. but they are slower than recomposing
* path descriptions when you have said back data (it's always easier with a model)
* there's a bezier fitter in bezier-utils.cpp too. the main difference is the way bezier patch are split
* here: walk on the polyline, trying to extend the portion you can fit by respecting the treshhold, split when
* treshhold is exceeded. when encountering a "forced" point, lower the treshhold to favor splitting at that point
* in bezier-utils: fit the whole polyline, get the position with the higher deviation to the fitted curve, split
* there and recurse
*/
// need the b-spline basis for cubic splines
// pas oublier que c'est une b-spline clampee
// et que ca correspond a une courbe de bezier normale
#define N33(t) (t*t*t)
// quadratic b-splines (jsut in case)
#define N22(t) (t*t)
// linear interpolation b-splines
#define N01(t) ((1.0-t))
#define N11(t) (t)
{
return;
}
Reset();
int lastM = 0;
{
lastP++;
}
}
}
// dichomtomic method to get distance to curve approximation
// a real polynomial solver would get the minimum more efficiently, but since the polynom
// would likely be of degree >= 5, that would imply using some generic solver, liek using the sturm metod
{
if ( lev <= 0 ) {
return current;
}
}
}
}
}
}
return current;
}
{
}
}
}
}
return nle;
}
/**
* Simplification on a subpath.
*/
{
// non-dichotomic method: grow an interval of points approximated by a curve, until you reach the treshhold, and repeat
if (N <= 1) {
return;
}
int curP = 0;
while (curP < N - 1) {
int M = 2;
// remettre a zero
bool contains_forced = false;
int step = 64;
while ( step > 0 ) {
int forced_pt = -1;
int worstP = -1;
do {
contains_forced = true;
}
M += step;
if (lastP >= N) {
M -= step;
} else {
// le dernier a echoue
M -= step;
if ( contains_forced ) {
}
}
step /= 2;
}
if (M <= 2) {
} else {
}
}
Close();
}
}
// warning: slow
// idea behing this feature: splotches appear when trying to fit a small number of points: you can
// get a cubic bezier that fits the points very well but doesn't fit the polyline itself
// so we add a bit of the error at the middle of each segment of the polyline
// also we restrict this to <=20 points, to avoid unnecessary computations
#define with_splotch_killer
// primitive= calc the cubic bezier patche that fits Xk and Yk best
// Qk est deja alloue
// retourne false si probleme (matrice non-inversible)
{
// la matrice tNN
}
return false;
}
M = iM;
// phase 1: abcisses
// calcul des Qk
}
// le vecteur Q
}
// phase 2: les ordonnees
}
// le vecteur Q
}
P = Q * M;
return true;
}
bool Path::ExtendFit(int off, int N, fitting_tables &data, double treshhold, PathDescrCubicTo &res, int &worstP)
{
}
}
double prevLen = 0;
}
}
}
}
}
// on est all� trop loin
// faut recalculer les tk
for (int i = 1; i < N; i++) {
}
for (int i = 1; i < N; i++) {
}
}
return false;
}
worstP = 1;
if ( N <= 2 ) {
return true;
}
double worstD = 0;
worstP = -1;
for (int i = 1; i < N; i++) {
if ( isForced ) {
// forced points are favored for splitting the recursion; we do this by increasing their distance
worstP = i;
}
} else {
worstP = i;
}
}
}
return true;
}
}
// fit a polyline to a bezier patch, return true is treshhold not exceeded (ie: you can continue)
// version that uses tables from the previous iteration, to minimize amount of work done
bool Path::AttemptSimplify (fitting_tables &data,double treshhold, PathDescrCubicTo & res,int &worstP)
{
// pour une coordonnee
worstP = 1;
return true;
}
// start -> cp1 -> end
worstP = 1;
return true;
}
} else {
// aie, non-inversible
double worstD = 0;
worstP = -1;
// forced points are favored for splitting the recursion; we do this by increasing their distance
worstP = i;
}
} else {
worstP = i;
}
}
}
return false;
}
// calcul du delta= pondere par les longueurs des segments
double delta = 0;
{
double worstD = 0;
worstP = -1;
double prevDist;
prevDist = 0;
#ifdef with_splotch_killer
double curDist;
double midDist;
worstP = i;
worstP = i;
}
}
} else {
#endif
double curDist;
worstP = i;
worstP = i;
}
}
#ifdef with_splotch_killer
}
#endif
}
// premier jet
// Refine a little.
// Force tk to be monotonic non-decreasing.
}
}
// ca devrait jamais arriver, mais bon
return true;
}
double ndelta = 0;
{
double worstD = 0;
worstP = -1;
double prevDist = 0;
#ifdef with_splotch_killer
double curDist;
double midDist;
worstP = i;
worstP = i;
}
}
} else {
#endif
double curDist;
worstP = i;
worstP = i;
}
}
#ifdef with_splotch_killer
}
#endif
}
return true;
} else {
// nothing better to do
}
return true;
}
return false;
}
{
// pour une coordonnee
double *Xk; // la coordonnee traitee (x puis y)
double *Yk; // la coordonnee traitee (x puis y)
double *lk; // les longueurs de chaque segment
double *tk; // les tk
double *Qk; // les Qk
char *fk; // si point force
if (N == 2) {
worstP = 1;
return true;
}
if (N == 3) {
// start -> cp1 -> end
worstP = 1;
return true;
}
// Totally inefficient, allocates & deallocates all the time.
// chord length method
tk[0] = 0.0;
lk[0] = 0.0;
{
for (int i = 1; i < N; i++) {
fk[i] = 0x01;
} else {
fk[i] = 0;
}
}
}
// longueur nulle
double worstD = 0;
worstP = -1;
for (int i = 1; i < N; i++) {
if ( isForced ) {
// forced points are favored for splitting the recursion; we do this by increasing their distance
worstP = i;
}
} else {
worstP = i;
}
}
}
return false;
}
for (int i = 1; i < N - 1; i++) {
}
} else {
// aie, non-inversible
double worstD = 0;
worstP = -1;
for (int i = 1; i < N; i++) {
if ( isForced ) {
// forced points are favored for splitting the recursion; we do this by increasing their distance
worstP = i;
}
} else {
worstP = i;
}
}
}
return false;
}
// calcul du delta= pondere par les longueurs des segments
double delta = 0;
{
double worstD = 0;
worstP = -1;
double prevDist;
prevDist = 0;
#ifdef with_splotch_killer
if ( N <= 20 ) {
for (int i = 1; i < N - 1; i++)
{
double curDist;
double midDist;
curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
midAppP[0] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[0] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[0] + N03 (0.5*(tk[i]+tk[i-1])) * Xk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Xk[N - 1];
midAppP[1] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[1] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[1] + N03 (0.5*(tk[i]+tk[i-1])) * Yk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Yk[N - 1];
worstP = i;
worstP = i;
}
}
} else {
#endif
for (int i = 1; i < N - 1; i++)
{
double curDist;
curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
worstP = i;
worstP = i;
}
}
#ifdef with_splotch_killer
}
#endif
}
{
// premier jet
// Refine a little.
for (int i = 1; i < N - 1; i++)
{
pt;
{
// Force tk to be monotonic non-decreasing.
}
}
} else {
// ca devrait jamais arriver, mais bon
return true;
}
double ndelta = 0;
{
double worstD = 0;
worstP = -1;
double prevDist;
prevDist = 0;
#ifdef with_splotch_killer
if ( N <= 20 ) {
for (int i = 1; i < N - 1; i++)
{
double curDist;
double midDist;
curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
midAppP[0] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[0] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[0] + N03 (0.5*(tk[i]+tk[i-1])) * Xk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Xk[N - 1];
midAppP[1] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[1] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[1] + N03 (0.5*(tk[i]+tk[i-1])) * Yk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Yk[N - 1];
worstP = i;
worstP = i;
}
}
} else {
#endif
for (int i = 1; i < N - 1; i++)
{
double curDist;
curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
worstP=i;
worstP=i;
}
}
#ifdef with_splotch_killer
}
#endif
}
{
return true;
} else {
// nothing better to do
}
return true;
} else {
// nothing better to do
}
return false;
}
double Path::RaffineTk (NR::Point pt, NR::Point p0, NR::Point p1, NR::Point p2, NR::Point p3, double it)
{
// Refinement of the tk values.
// Just one iteration of Newtow Raphson, given that we're approaching the curve anyway.
// [fr: vu que de toute facon la courbe est approchC)e]
}
return it;
}
// variation on the fitting theme: try to merge path commands into cubic bezier patches
// the goal was to reduce the number of path commands, especially when ooperations on path produce
// lots of small path elements; ideally you could get rid of very small segments at reduced visual cost
{
if ( descr_flags & descr_adding_bezier ) {
CancelBezier();
}
if ( descr_flags & descr_doing_subpath ) {
CloseSubpath();
}
return;
}
SetBackData(false);
tempDest->SetBackData(false);
int lastP = 0;
int lastAP = -1;
// As the elements are stored in a separate tableau, it's no longer worth optimizing
// the rewriting in the same tableau.
// [[comme les elements sont stockes dans un tableau a part, plus la peine d'optimiser
// la r��criture dans la meme tableau]]
/* FIXME: the use of this variable probably causes a leak or two.
** It's a hack anyway, and probably only needs to be a type rather than
** a full PathDescr.
*/
bool containsForced = false;
if (typ == descr_moveto) {
}
// Added automatically (too bad about multiple moveto's).
// [fr: (tant pis pour les moveto multiples)]
containsForced = false;
} else if (typ == descr_close) {
int worstP = -1;
if (AttemptSimplify(lastA, nextA - lastA + 1, (containsForced) ? 0.05 * tresh : tresh, res, worstP)) {
pending_cubic = res;
lastAP = -1;
}
} else {
}
containsForced = false;
} else if (typ == descr_forced) {
int worstP = -1;
// plus sensible parce que point force
// ca passe
containsForced = true;
} else {
// on force l'addition
containsForced = false;
}
}
int worstP = -1;
pending_cubic = res;
lastAP = -1;
} else {
if ( typ == descr_cubicto ) {
}
containsForced = false;
}
} else {
if ( typ == descr_cubicto ) {
}
containsForced = false;
}
} else if (typ == descr_bezierto) {
}
lastAP = -1;
}
} else if (typ == descr_interm_bezier) {
continue;
} else {
continue;
}
}
}
delete tempDest;
}
{
switch (lastAddition->getType()) {
case descr_moveto:
if ( lastAP >= 0 ) {
}
break;
case descr_close:
break;
case descr_cubicto:
break;
case descr_lineto:
if ( lastAP >= 0 ) {
}
break;
case descr_arcto:
if ( lastAP >= 0 ) {
}
break;
case descr_bezierto:
if ( lastAP >= 0 ) {
}
break;
case descr_interm_bezier:
if ( lastAP >= 0 ) {
}
break;
}
}
/*
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:encoding=utf-8:textwidth=99 :