diff options
author | Joshua Leung <aligorith@gmail.com> | 2008-05-05 10:38:19 +0400 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2008-05-05 10:38:19 +0400 |
commit | 945a083e70b79d361f5a9b1be76c9722565c9fe8 (patch) | |
tree | 997aa53ddbd5753490057f185f92824e1a27639c /source/blender | |
parent | 02afc949322950af19923df65b359215495abac7 (diff) |
Bugfix:
IPO-Smooth should now give better results. It uses a moving-window approach, which looks at two points on either side of the point in interest (a total of 5 in optimal conditions), and does a weighted average on it.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/src/editipo.c | 105 |
1 files changed, 76 insertions, 29 deletions
diff --git a/source/blender/src/editipo.c b/source/blender/src/editipo.c index d72bdc6fc80..a8b2ff761e5 100644 --- a/source/blender/src/editipo.c +++ b/source/blender/src/editipo.c @@ -3793,6 +3793,12 @@ void clean_ipo_curve(IpoCurve *icu) MEM_freeN(old_bezts); } + +/* temp struct used for smooth_ipo */ +typedef struct tSmooth_Bezt { + float *h1, *h2, *h3; /* bezt->vec[0,1,2][1] */ +} tSmooth_Bezt; + void smooth_ipo(void) { EditIpo *ei; @@ -3804,7 +3810,6 @@ void smooth_ipo(void) ei= G.sipo->editipo; for(b=0; b<G.sipo->totipo; b++, ei++) { if (ISPOIN3(ei, flag & IPO_VISIBLE, icu, icu->bezt)) { - ok= 0; if(G.sipo->showkey) ok= 1; else if(totipo_vert && (ei->flag & IPO_EDIT)) ok= 2; @@ -3813,52 +3818,94 @@ void smooth_ipo(void) if(ok) { IpoCurve *icu= ei->icu; BezTriple *bezt; - float meanValSum = 0.0f, meanVal; - float valDiff; - int i, totSel = 0; + int i, x, totSel = 0; /* check if enough points */ if (icu->totvert >= 3) { - /* first loop through - obtain average value */ + /* first loop through - count how many verts are selected, and fix up handles */ bezt= icu->bezt; - for (i=1; i < icu->totvert; i++, bezt++) { + for (i=0; i < icu->totvert; i++, bezt++) { if (BEZSELECTED(bezt)) { /* line point's handles up with point's vertical position */ bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1]; - if(bezt->h1==HD_AUTO || bezt->h1==HD_VECT) bezt->h1= HD_ALIGN; - if(bezt->h2==HD_AUTO || bezt->h2==HD_VECT) bezt->h2= HD_ALIGN; + if ((bezt->h1==HD_AUTO) || (bezt->h1==HD_VECT)) bezt->h1= HD_ALIGN; + if ((bezt->h2==HD_AUTO) || (bezt->h2==HD_VECT)) bezt->h2= HD_ALIGN; /* add value to total */ - meanValSum += bezt->vec[1][1]; totSel++; } } - /* calculate mean value */ - meanVal= meanValSum / totSel; - - /* second loop through - update point positions */ - bezt= icu->bezt; - for (i=0; i < icu->totvert; i++, bezt++) { - if (BEZSELECTED(bezt)) { - /* 1. calculate difference between the points - * 2. move point half-way along that distance - */ - if (bezt->vec[1][1] > meanVal) { - /* bezt val above mean */ - valDiff= bezt->vec[1][1] - meanVal; - bezt->vec[1][1]= meanVal + (valDiff / 2); + /* if any points were selected, allocate tSmooth_Bezt points to work on */ + if (totSel >= 3) { + tSmooth_Bezt *tarray, *tsb; + + /* allocate memory in one go */ + tsb= tarray= MEM_callocN(totSel*sizeof(tSmooth_Bezt), "tSmooth_Bezt Array"); + + /* populate tarray with data of selected points */ + bezt= icu->bezt; + for (i=0, x=0; (i < icu->totvert) && (x < totSel); i++, bezt++) { + if (BEZSELECTED(bezt)) { + /* tsb simply needs pointer to vec, and index */ + tsb->h1 = &bezt->vec[0][1]; + tsb->h2 = &bezt->vec[1][1]; + tsb->h3 = &bezt->vec[2][1]; + + /* advance to the next tsb to populate */ + if (x < totSel- 1) + tsb++; + else + break; } - else { - /* bezt val below mean */ - valDiff= meanVal - bezt->vec[1][1]; - bezt->vec[1][1] = bezt->vec[1][1] + (valDiff / 2); + } + + /* calculate the new smoothed ipo's with weighted averages: + * - this is done with two passes + * - uses 5 points for each operation (which stores in the relevant handles) + * - previous: w/a ratio = 3:5:2:1:1 + * - next: w/a ratio = 1:1:2:5:3 + */ + + /* round 1: calculate previous and next */ + tsb= tarray; + for (i=0; i < totSel; i++, tsb++) { + /* don't touch end points (otherwise, curves slowly explode) */ + if (ELEM(i, 0, (totSel-1)) == 0) { + tSmooth_Bezt *tP1 = tsb - 1; + tSmooth_Bezt *tP2 = (i-2 > 0) ? (tsb - 2) : (NULL); + tSmooth_Bezt *tN1 = tsb + 1; + tSmooth_Bezt *tN2 = (i+2 < totSel) ? (tsb + 2) : (NULL); + + float p1 = *tP1->h2; + float p2 = (tP2) ? (*tP2->h2) : (*tP1->h2); + float c1 = *tsb->h2; + float n1 = *tN1->h2; + float n2 = (tN2) ? (*tN2->h2) : (*tN1->h2); + + /* calculate previous and next */ + *tsb->h1= (3*p2 + 5*p1 + 2*c1 + n1 + n2) / 12; + *tsb->h3= (p2 + p1 + 2*c1 + 5*n1 + 3*n2) / 12; } } + + /* round 2: calculate new values and reset handles */ + tsb= tarray; + for (i=0; i < totSel; i++, tsb++) { + /* calculate new position by averaging handles */ + *tsb->h2 = (*tsb->h1 + *tsb->h3) / 2; + + /* reset handles now */ + *tsb->h1 = *tsb->h2; + *tsb->h3 = *tsb->h2; + } + + /* free memory required for tarray */ + MEM_freeN(tarray); } } - - /* recalc handles */ + + /* recalculate handles */ calchandles_ipocurve(icu); } } |