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