1 /*
2  * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
3  * Applied Mathematics, Norway.
4  *
5  * Contact information: E-mail: tor.dokken@sintef.no
6  * SINTEF ICT, Department of Applied Mathematics,
7  * P.O. Box 124 Blindern,
8  * 0314 Oslo, Norway.
9  *
10  * This file is part of SISL.
11  *
12  * SISL is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU Affero General Public License as
14  * published by the Free Software Foundation, either version 3 of the
15  * License, or (at your option) any later version.
16  *
17  * SISL is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Affero General Public License for more details.
21  *
22  * You should have received a copy of the GNU Affero General Public
23  * License along with SISL. If not, see
24  * <http://www.gnu.org/licenses/>.
25  *
26  * In accordance with Section 7(b) of the GNU Affero General Public
27  * License, a covered work must retain the producer line in every data
28  * file that is created or manipulated using SISL.
29  *
30  * Other Usage
31  * You can be released from the requirements of the license by purchasing
32  * a commercial license. Buying such a license is mandatory as soon as you
33  * develop commercial activities involving the SISL library without
34  * disclosing the source code of your own applications.
35  *
36  * This file may be used in accordance with the terms contained in a
37  * written agreement between you and SINTEF ICT.
38  */
39 
40 #include "sisl-copyright.h"
41 
42 /*
43  *
44  * $Id: s1353.c,v 1.3 2006-05-02 15:06:56 sbr Exp $
45  *
46  */
47 
48 #define S1353
49 
50 #include "sislP.h"
51 
52 #if defined(SISLNEEDPROTOTYPES)
s1353(SISLCurve * curve,double eps[],rank_info * ranking,int * stat)53 void s1353(SISLCurve *curve,double eps[],rank_info *ranking,int *stat)
54 #else
55 void s1353(curve, eps, ranking, stat)
56      SISLCurve	*curve;
57      double	eps[];
58      rank_info	*ranking;
59      int *stat;
60 #endif
61 /*
62 *********************************************************************
63 *
64 *********************************************************************
65 *
66 * Purpose: To compute a ranking of the knots of curve, suitable for use
67 *          during knot removal.
68 *
69 *
70 *
71 * Input:
72 *          curve       - pointer to the curve whose knots are to be ranked.
73 *
74 *	   eps         - double array containing the tolerance in the
75 *			 different components to be used during knot removal.
76 *
77 *
78 *
79 * Output:  ranking     - a pointer to a rank_info object containing the
80 *                        result of the ranking computations. The struct
81 *			 rank_info contains four variables listed below
82 *                        (t, k, and n refer to the knot vector of curve,
83 *			 its polynomial order and the number of B-spline
84 *                        coefficients):
85 *	 ranking->prio - integer array of dimension (n-k) containing the
86 *			 ranking of the interior knots. The knots are listed
87 * 			 in order of increasing ranking number, cf. the
88 *			 second reference, with knots with the same ranking
89 *		         number listed in the order in which they occur
90 *		         in t. To differentiate between the different
91 *                        ranking numbers the array ranking->groups is used.
92 *      ranking->groups - integer array of dimension (n-k) used to partition
93 *	                 ranking->prio into blocks of knots with the same
94 *                        ranking number. ranking->groups[0] contains the
95 *			 number of knots with the smallest ranking number,
96 *                        ranking->groups[1] contains the number of knots with
97 *                        the two smallest ranking numbers etc.. Only the first
98 *                        ranking->antgr elements of ranking->groups are used.
99 *       ranking->antgr - the number of distinct ranking numbers among the
100 *                        interior knots of t.
101 *      ranking->antrem - an estimate of the number of knots that can be
102 *                        removed from curve.
103 *
104 *           stat       - status message
105 *                           > 0      : warning
106 *                           = 0      : ok
107 *                           < 0      : error
108 *
109 *
110 *
111 * Method:
112 *      The ranking strategy is described in the two references.
113 *      somewhat simplified, the computation of the ranking number of knot
114 *      number i consists of computing the error in the best approximation to
115 *      the input spline from a subspace where this knot has been removed,
116 *      and then finding in which of the intervals [0,eps/2], (eps/2,eps],
117 *      (2*eps,4*eps],... , the error lies. This interval number is then
118 *      the ranking number of the knot.
119 *      The best approximation is computed in a certain discrete max-norm,
120 *      and this leads to well conditioned linear systems.
121 *
122 *      NB! It is assumed that the first and lasty knots occur with
123 *      multiplicity k.
124 *
125 *      NB! The code is unreadable without close study of the two references.
126 *
127 *
128 * References:
129 *      1. A Data-Reduction Strategy for Splines with Applications to the
130 *         Approximation of Functions and data, IMA J. of Num. Anal. 8 (1988),
131 *         pp. 185-208.
132 *
133 *      2. Knot Removal for Parametric B-spline Curves and Surfaces,
134 *         CAGD 4 (1987), pp. 217-230.
135 *
136 *
137 * Written by: Knut Moerken, University of Oslo, July 1992, based on an
138 *              earlier Fortran version by the same author.
139 *
140 *********************************************************************
141 */
142 {
143   int k = curve->ik;            /* The basic parameters of curve will be */
144   int dim = curve->idim;        /* used extensively and are therefore    */
145   int n = curve->in;            /* extracted and stored in local         */
146   double *et = curve->et;       /* variables.                            */
147   double *ecoef = curve->ecoef;
148   int *prio = ranking->prio;    /* Local versions of the ranking arrays. */
149   int *groups = ranking->groups;
150   int antgr;                    /* The two integers of ranking.          */
151   int antrem;
152   int pos=0;                    /* Integer used by s6err.                */
153                                 /* The routine requires a large number of
154 				   integers and floats, see the code.    */
155   int i, ii, i2, inxt, mult, dm, j1, j2, nkt, dih, dj, dj1, hmult,
156       p, dp, di2, j, count, di, iw, ph, gh;
157   double h, x, pm1h, pm1, muj1, muj, mm, h1, h2, denom, ln2inv;
158   double *c, *hh, *mu, *w, *e, *s;
159 
160   /* We start by allocating space for local arrays. */
161 
162   /* c will contain the right-hand-sides in the linear systems that
163      are going to be solved. */
164 
165   c = newarray(dim*(k+1), double);
166   if (c == SISL_NULL) goto err101;
167 
168   /* hh will contain the last column of the coefficient matrix during
169      elimination. */
170 
171   hh = newarray(k+1, double);
172   if (hh == SISL_NULL) goto err101;
173 
174   /* mu will contain one of the two diagonals of the coefficient matrix. */
175 
176   mu = newarray(k+1, double);
177   if (mu == SISL_NULL) goto err101;
178 
179   /* w will contain the weights of the interior knots (of type double). */
180 
181   w = newarray(dim*(n-k), double);
182   if (w == SISL_NULL) goto err101;
183 
184   /* e and s are auxialiary arrays. */
185 
186   e = newarray(dim, double);
187   if (e == SISL_NULL) goto err101;
188 
189   s = newarray(dim, double);
190   if (s == SISL_NULL) goto err101;
191 
192   /* Start the computation of the weights. We first compute real (not integer)
193      weights, w. The weight of a knot is computed by removing it and computing
194      a best approximation without the knot. The weight is the error in this
195      approximation. The norm used is the max-norm of the B-spline coefficients
196      on the original knot vector et. */
197 
198   /* The integer i points to the previous knot that was removed, while
199      dih points to the location in w where the next weight is to be stored.
200      nkt gives the number of interior knots and therefore the number of
201      weights to be computed (the number of weights is nkt*dim).
202      We clearly have to loop until i=n-1 (remember that we assumed the first
203      and last knots to occur with multiplicity k). */
204 
205   i = k - 1; dih = 0; nkt = n - k;
206 
207   while (i < n-1)
208     {
209 
210       /* The knot that we want to compute a weight for now is x=et[i+1].
211 	 inxt will point to the smallest knot distinct from x. */
212 
213       inxt = i + 1; x = et[inxt];
214       while (x == et[inxt+1]) ++inxt;
215 
216       /* mult is the multiplicity of x, while dm is the dimension of
217 	 the linear system we are going to solve, see reference 1. */
218 
219       mult = inxt - i; dm = k - mult + 2;
220 
221       /* Set up the linear system of equations. The first dm-1 unknowns
222          are the coefficients of the spline approximation that are different
223 	 from the coefficients of curve while the last unknown is the
224 	 error in the approximation. */
225 
226       /* pm1h is the first entry in last column of the coefficient matrix. */
227 
228 	       pm1h = 1.0;
229       if ( fmod((float) dm, (float) 2.0) == 0 )  pm1h = -1.0;
230 
231       /* The integer j is used to step through the equations during
232 	 elimination, while muj is used to hold the mu of equation j
233 	 in ref. 1. The array mu (and muj1) will be used for storing
234 	 the inverse of muj, while mm is the factor that the previous
235 	 (or next0 equation is multiplied with to eliminate one variable.
236 	 hh is used for keeping track of the last column of the coefficient
237 	 matrix during elimination, while j1 and j2 are pointers to knots
238 	 in et that are needed for computing muj. dj1 is a pointer to the
239 	 B-spline coefficient that occurs on the right-hand-side
240 	 of the equation (really dim right-hand-sides), while dj
241          is a pointer to the right-hand-side after elimination
242 	 (i2 is always dim behind (or ahead of) dj.) */
243 
244       muj1 = 1.0; mu[0] = 1.0;
245       j1 = inxt - k + 1; dj1 = dim*j1;
246       j2 = inxt + 1;
247       hh[0] = pm1 = pm1h;
248 
249       di2 = (j1-1)*dim;
250 
251       for (ii = 0; ii < dim; ++ii, ++di2) c[ii] = ecoef[di2];
252 
253       j = 1; dj = dim;
254       i2 = 0; di2 = 0;
255 
256       /* Eliminate from the top until muj1 becomes larger than 2
257 	 (see ref. 1). */
258 
259       while (muj1 <= 2 && j < dm - 1)
260 	{
261 	  h = et[j1];
262 
263 	  /* The two conditions tested here correspond to a singular system
264 	     which can only occur with a buggy knot vector. */
265 
266 	  if (h == et[j2] || x == h) goto err112;
267 	  muj = (x-h) / (et[j2]-h);
268 	  mm = (1-muj) * muj1;
269 	  mu[j] = muj1 = 1 / muj;
270 	  pm1 = -pm1;
271 	  hh[j] = pm1 - mm*hh[i2];
272 	  for (ii=0; ii<dim; ++ii, ++dj, ++dj1, ++di2)
273 	    c[dj] = ecoef[dj1] - mm*c[di2];
274 
275 	  i2 = j; ++j; ++j1; ++j2;
276 	}
277 
278       /* Now we remember how far down we managed to eliminate in p and prepare
279 	 to eliminate from the last equation to p+1. The variables have the
280 	 same interpretations as before except for the obvious changes since
281 	 we are eliminating upwards from the bottom and not dowmwards from
282 	 the top. */
283 
284       p = i2;
285 
286       /* Initialize variables as above. */
287 
288       j1 = i; dj1 = dim*(j1+1)-1;
289       j2 = j1 + k;
290       i2 = j1 + 1; di2 = dim*(i2+1) - 1;
291       pm1 = 1;
292       hh[dm-1] = 1.0;
293       dj = dim*dm - 1;
294 
295       for (ii=0; ii<dim; ++ii, --dj, --di2) c[dj] = ecoef[di2];
296 
297       muj1 = mu[dm-1] = 1.0;
298       i2 = dm - 1; di2 = dim*dm - 1;
299 
300       /* Eliminate. */
301 
302       for (j=dm-2; j>p; --j)
303 	{
304 	  h = et[j2];
305 	  if (h == x || h == et[j1]) goto err112;
306 	  muj = (h-x) / (h-et[j1]);
307 	  mm = (1-muj)*muj1;
308 	  muj1 = 1/muj;
309 	  mu[j] = muj1;
310 	  pm1 = -pm1;
311 	  hh[j] = pm1 - mm*hh[i2];
312 	  for (ii=0; ii<dim; ++ii, --dj, --dj1, --di2)
313 	    c[dj] = ecoef[dj1] - mm*c[di2];
314 
315 	  i2 = j; --j1; --j2;
316 	}
317 
318       /* Now we are left with two equations (no. p and p+1) in two unknowns
319 	 (one B-spline coefficient and the error in the approximation) which
320 	 we solve the standard way. The weight is then obtained by taking
321 	 the absolute value of the error. */
322 
323       h1 = mu[i2]; h2 = mu[p];
324       denom = 1/(h1*hh[i2]-h2*hh[p]);
325       dp = dim*p; di2 = dim*i2;
326       for (ii=0; ii<dim; ++ii, ++di2, ++dp, ++dih)
327 	{
328 	  h = e[ii] = (h1*c[di2] - h2*c[dp])*denom;
329 	  w[dih] = fabs(h);
330 	}
331 
332       /* We now determine the weights of the remaining knots at x. */
333 
334       count = 1;
335 
336       for (hmult=mult-1; hmult>0; --hmult)
337 	{
338 
339 	  /* First do the back substitution of the system used to determine
340              the previous weight. In this way the nontrivial B-spline
341 	     coefficients of the spline that best approximates the given
342 	     spline curve but without the knot x, is determined.
343              The weight of the next knot at x is obtained by solving a
344              linear system of the same sort as the one above, but based on
345 	     this new spline instead. In this way a weight for the new
346 	     spline is found; the weight of this knot with respect to the
347 	     original spline is then set equal to this weight plus the
348 	     weight of the previous spline (at this knot). */
349 
350 	  /* Substitute to the end. */
351 
352 	  i2 = i + count; dj = dim*(p+1);
353 	  for (j=p+1; j<dm; ++j)
354 	    {
355 	      h1 = hh[j]; h2 = mu[j];
356 	      for (ii=0; ii<dim; ++ii, ++dj)
357 		c[dj] = (c[dj] - h1*e[ii])*h2;
358 	    }
359 
360 	  /* Substitute to the beginning. */
361 
362 	  dj1 = dim*(p+1) - 1; dj = dj1 - dim;
363 	  for (j=p-1; j>=0; --j)
364 	    {
365 	      h1 = hh[j]; h2 = mu[j];
366 	      for (ii=dim-1; ii>-1; --ii, --dj, --dj1)
367 		{
368 		  c[dj1] = (c[dj] - h1*e[ii])*h2;
369 		}
370 	    }
371 
372 	  /* Set up the new system of equations. The dimension will be
373 	     one greater than the previous one and the system is based
374 	     on the spline that was just computed so that the multiplicity
375 	     of x has been reduced by one. */
376 
377 	  /* The variables have the same interpretations as above. */
378 	  ++count; ++dm;
379 	  pm1h = -pm1h;
380 	  pm1 = hh[0] = pm1h;
381 	  j1 = i + hmult - k + 1;
382 	  j2 = inxt + 1;
383 	  i2 = j1 - 1; di2 = dim*i2;
384 	  muj1 = 1.0;
385 
386 	  for (ii=0; ii<dim; ++ii, ++di2) c[ii] = ecoef[di2];
387 
388 
389 	  j = 1; dj = dim;
390 	  i2 = 0; di2 = 0;
391 
392 	  /* Eliminate unknowns until muj1 becomes greater than 2. */
393 
394 	  while (muj1 <= 2 && j < dm - 1)
395 	    {
396 	      h = et[j1];
397 	      if (h == et[j2] || x == h) goto err112;
398 	      muj = (x-h) / (et[j2]-h);
399 	      mm = (1-muj) * muj1;
400 	      mu[j] = muj1 = 1 / muj;
401 	      pm1 = -pm1;
402 	      hh[j] = pm1 - mm*hh[i2];
403 	      for (ii=0; ii<dim; ++ii, ++dj, ++di2)
404 		c[dj] = c[dj] - mm*c[di2];
405 
406 	      i2 = j; ++j; ++j1; ++j2;
407 
408 	    }
409 
410 	  /* Remember where we have are. */
411 
412 	  p = i2;
413 
414 	  /* Prepare elimination from the end up to p. */
415 
416 	  j1 = i; dj1 = dim*(j1+1) - 1;
417 	  j2 = j1 + k + count - 1;
418 	  i2 = i + count; di2 = dim*(i2+1) - 1;
419 	  pm1 = 1;
420 	  hh[dm-1] = 1.0;
421 	  dj = dim*dm - 1;
422 
423 	  for (ii=0; ii<dim; ++ii, --dj, --di2) c[dj] = ecoef[di2];
424 
425 
426 	  muj1 = mu[dm-1] = 1.0;
427 	  i2 = dm - 1; di2 = dim*dm - 1;
428 
429 	  /* Eliminate from the end. */
430 	  for (j=dm-2; j>p; --j)
431 	    {
432 	      h = et[j2];
433 	      if (h == x || h == et[j1]) goto err112;
434 	      muj = (h-x) / (h-et[j1]);
435 	      mm = (1-muj)*muj1;
436 	      muj1 = 1/muj;
437 	      mu[j] = muj1;
438 	      pm1 = -pm1;
439 	      hh[j] = pm1 - mm*hh[i2];
440 	      for (ii=0; ii<dim; ++ii, --dj, --di2)
441 		c[dj] = c[dj] - mm*c[di2];
442 
443 	      i2 = j; --j1; --j2;
444 	    }
445 
446 	  /* Solve the two equations in two unknowns and determine w
447 	     by adding the absolute value of the current error to the
448 	     previous weight. */
449 
450 	  h1 = mu[i2]; h2 = mu[p];
451 	  denom = 1/(h1*hh[i2]-h2*hh[p]);
452 	  dp = dim*p; di2 = dim*i2;
453 	  for (ii=0; ii<dim; ++ii, ++di2, ++dp, ++dih)
454 	    {
455 	      h = e[ii] = (h1*c[di2] - h2*c[dp])*denom;
456 	      w[dih] = w[dih-dim] + fabs(h);
457 	    }
458 	}
459 
460       /* Go to the next distinct knot. */
461 
462       i = inxt;
463 
464     }
465 
466   /* We now have the information that is needed for computing the
467      ranking numbers, see refs. 1 and 2. */
468 
469   /* We start by computing the inverse of eps/2 (careful if eps is small). */
470 
471   for (ii=0; ii<dim; ++ii)
472     s[ii] = 2.0/MAX(eps[ii],1.0e-300);
473 
474   /* We need the constant 1/logarithm(2). */
475 
476   ln2inv = 1.0 / log((double) 2.0);
477   di = 0;
478 
479   /* Since all the weights are nonnegative, each of them must fall in one
480      of the intervals [0,eps/2), [eps/2, eps), [eps, 2eps), [2eps, 4eps), ...
481      If we number these intervals from 0, then the ranking number of a knot
482      is the number of the interval that contains its weight. For a parametic
483      curve we choose the ranking number to be the largest of the ranking
484      numbers of the dim components. The ranking numbers are stored in
485      the array groups. */
486 
487   for (i=0; i<nkt; ++i)
488     {
489       iw = 0;
490       for (ii=0; ii<dim; ++ii, ++di)
491 	{
492 	  h = w[di];
493 	  if (h > 0.5*eps[ii])
494 	    iw = MAX(iw, (int)floor(log(h*s[ii])*ln2inv+1));
495 	}
496       groups[i] = iw;
497     }
498 
499   /* We next sort the ranking numbers in increasing order. Since each ranking
500      number belongs to a specific knot, this induces an ordering of the knots.
501      This ordering is stored in prio. Note that if two knots have the same
502      ranking they are listed in prio in the order that they occur in et. */
503 
504   for (i=0; i<nkt; ++i) prio[i]=i;
505 
506   for (i=1; i<nkt; ++i)
507     {
508       gh = groups[i]; ph = prio[i];
509 
510       for (j=i-1; gh < groups[j];)
511 	{
512 	  groups[j+1] = groups[j];
513 	  prio[j+1] = prio[j];
514 	  --j;
515 	  if (j <0) goto out;
516 	}
517 
518     out:
519       groups[j+1] = gh; prio[j+1] = ph;
520     }
521 
522   /* We next prepare for estimating how many knots that can be removed (very
523      heuristic and not important for the perfomance of the knot removal. */
524 
525   if (groups[0] == 0) antrem = 0;
526     else
527       antrem = -1;
528 
529   /* The array groups should end up with the cumulative number of knots in
530      each interval. */
531 
532   j = 0;
533   for (i=0; i<nkt-1; ++i)
534     if (groups[i] < groups[i+1])
535       {
536 	groups[j] = i+1; ++j;
537       }
538 
539   antgr = ++j;
540   groups[antgr-1] = n - k;
541 
542   /* More guessing about antrem. */
543 
544   if (antrem == 0)
545     {
546       if (antgr > 1)
547 	antrem = (int)(0.75*groups[1]/(antgr-1));
548       else
549 	antrem = (int)(0.9*groups[0]);
550     }
551   else
552     antrem = 1;
553 
554   antrem = MAX(MIN(1, nkt), antrem);
555 
556   /* Finish of ranking. */
557 
558   ranking->antgr = antgr;
559   ranking->antrem = antrem;
560 
561 
562 
563   /* Free memory. */
564 
565   freearray(hh);
566   freearray(c);
567   freearray(mu);
568   freearray(w);
569   freearray(e);
570   freearray(s);
571 
572   /* Successful computations. */
573 
574   return;
575 
576   /* Error in allocation of memory. */
577 
578  err101:
579   *stat = -101;
580   s6err("s1353", *stat, pos);
581   goto err;
582 
583   /* Error in knot vector (singular system of equations). */
584 
585  err112:
586   *stat = -112;
587   s6err("s1353", *stat, pos);
588   goto err;
589 
590   /* Clean up. */
591 
592  err:
593   if (c != SISL_NULL) freearray(c);
594   if (hh != SISL_NULL) freearray(hh);
595   if (mu != SISL_NULL) freearray(mu);
596   if (w != SISL_NULL) freearray(w);
597   if (e != SISL_NULL) freearray(e);
598   if (s != SISL_NULL) freearray(s);
599 
600   return;
601 
602 }
603