Cleanup: style, use braces for blenkernel
[blender.git] / source / blender / blenkernel / intern / deform.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bke
22  */
23
24 #include <string.h>
25 #include <math.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_meshdata_types.h"
33 #include "DNA_mesh_types.h"
34 #include "DNA_object_types.h"
35 #include "DNA_scene_types.h"
36
37 #include "BLI_listbase.h"
38 #include "BLI_math.h"
39 #include "BLI_string.h"
40 #include "BLI_string_utils.h"
41 #include "BLI_utildefines.h"
42
43 #include "BLT_translation.h"
44
45 #include "BKE_customdata.h"
46 #include "BKE_data_transfer.h"
47 #include "BKE_deform.h" /* own include */
48 #include "BKE_mesh.h"
49 #include "BKE_mesh_mapping.h"
50 #include "BKE_object.h"
51 #include "BKE_object_deform.h"
52
53 #include "data_transfer_intern.h"
54
55 bDeformGroup *BKE_defgroup_new(Object *ob, const char *name)
56 {
57   bDeformGroup *defgroup;
58
59   BLI_assert(OB_TYPE_SUPPORT_VGROUP(ob->type));
60
61   defgroup = MEM_callocN(sizeof(bDeformGroup), __func__);
62
63   BLI_strncpy(defgroup->name, name, sizeof(defgroup->name));
64
65   BLI_addtail(&ob->defbase, defgroup);
66   defgroup_unique_name(defgroup, ob);
67
68   BKE_object_batch_cache_dirty_tag(ob);
69
70   return defgroup;
71 }
72
73 void defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
74 {
75   bDeformGroup *defgroup, *defgroupn;
76
77   BLI_listbase_clear(outbase);
78
79   for (defgroup = inbase->first; defgroup; defgroup = defgroup->next) {
80     defgroupn = defgroup_duplicate(defgroup);
81     BLI_addtail(outbase, defgroupn);
82   }
83 }
84
85 bDeformGroup *defgroup_duplicate(const bDeformGroup *ingroup)
86 {
87   bDeformGroup *outgroup;
88
89   if (!ingroup) {
90     BLI_assert(0);
91     return NULL;
92   }
93
94   outgroup = MEM_callocN(sizeof(bDeformGroup), "copy deformGroup");
95
96   /* For now, just copy everything over. */
97   memcpy(outgroup, ingroup, sizeof(bDeformGroup));
98
99   outgroup->next = outgroup->prev = NULL;
100
101   return outgroup;
102 }
103
104 /**
105  * Overwrite weights filtered by vgroup_subset.
106  * - do nothing if neither are set.
107  * - add destination weight if needed
108  */
109 void defvert_copy_subset(MDeformVert *dvert_dst,
110                          const MDeformVert *dvert_src,
111                          const bool *vgroup_subset,
112                          const int vgroup_tot)
113 {
114   int defgroup;
115   for (defgroup = 0; defgroup < vgroup_tot; defgroup++) {
116     if (vgroup_subset[defgroup]) {
117       defvert_copy_index(dvert_dst, defgroup, dvert_src, defgroup);
118     }
119   }
120 }
121
122 /**
123  * Overwrite weights filtered by vgroup_subset and with mirroring specified by the flip map
124  * - do nothing if neither are set.
125  * - add destination weight if needed
126  */
127 void defvert_mirror_subset(MDeformVert *dvert_dst,
128                            const MDeformVert *dvert_src,
129                            const bool *vgroup_subset,
130                            const int vgroup_tot,
131                            const int *flip_map,
132                            const int flip_map_len)
133 {
134   int defgroup;
135   for (defgroup = 0; defgroup < vgroup_tot && defgroup < flip_map_len; defgroup++) {
136     if (vgroup_subset[defgroup] && (dvert_dst != dvert_src || flip_map[defgroup] != defgroup)) {
137       defvert_copy_index(dvert_dst, flip_map[defgroup], dvert_src, defgroup);
138     }
139   }
140 }
141
142 void defvert_copy(MDeformVert *dvert_dst, const MDeformVert *dvert_src)
143 {
144   if (dvert_dst->totweight == dvert_src->totweight) {
145     if (dvert_src->totweight) {
146       memcpy(dvert_dst->dw, dvert_src->dw, dvert_src->totweight * sizeof(MDeformWeight));
147     }
148   }
149   else {
150     if (dvert_dst->dw) {
151       MEM_freeN(dvert_dst->dw);
152     }
153
154     if (dvert_src->totweight) {
155       dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
156     }
157     else {
158       dvert_dst->dw = NULL;
159     }
160
161     dvert_dst->totweight = dvert_src->totweight;
162   }
163 }
164
165 /**
166  * Copy an index from one dvert to another.
167  * - do nothing if neither are set.
168  * - add destination weight if needed.
169  */
170 void defvert_copy_index(MDeformVert *dvert_dst,
171                         const int defgroup_dst,
172                         const MDeformVert *dvert_src,
173                         const int defgroup_src)
174 {
175   MDeformWeight *dw_src, *dw_dst;
176
177   dw_src = defvert_find_index(dvert_src, defgroup_src);
178
179   if (dw_src) {
180     /* source is valid, verify destination */
181     dw_dst = defvert_verify_index(dvert_dst, defgroup_dst);
182     dw_dst->weight = dw_src->weight;
183   }
184   else {
185     /* source was NULL, assign zero, could also remove */
186     dw_dst = defvert_find_index(dvert_dst, defgroup_dst);
187
188     if (dw_dst) {
189       dw_dst->weight = 0.0f;
190     }
191   }
192 }
193
194 /**
195  * Only sync over matching weights, don't add or remove groups
196  * warning, loop within loop.
197  */
198 void defvert_sync(MDeformVert *dvert_dst, const MDeformVert *dvert_src, const bool use_verify)
199 {
200   if (dvert_src->totweight && dvert_dst->totweight) {
201     int i;
202     MDeformWeight *dw_src;
203     for (i = 0, dw_src = dvert_src->dw; i < dvert_src->totweight; i++, dw_src++) {
204       MDeformWeight *dw_dst;
205       if (use_verify) {
206         dw_dst = defvert_verify_index(dvert_dst, dw_src->def_nr);
207       }
208       else {
209         dw_dst = defvert_find_index(dvert_dst, dw_src->def_nr);
210       }
211
212       if (dw_dst) {
213         dw_dst->weight = dw_src->weight;
214       }
215     }
216   }
217 }
218
219 /**
220  * be sure all flip_map values are valid
221  */
222 void defvert_sync_mapped(MDeformVert *dvert_dst,
223                          const MDeformVert *dvert_src,
224                          const int *flip_map,
225                          const int flip_map_len,
226                          const bool use_verify)
227 {
228   if (dvert_src->totweight && dvert_dst->totweight) {
229     int i;
230     MDeformWeight *dw_src;
231     for (i = 0, dw_src = dvert_src->dw; i < dvert_src->totweight; i++, dw_src++) {
232       if (dw_src->def_nr < flip_map_len) {
233         MDeformWeight *dw_dst;
234         if (use_verify) {
235           dw_dst = defvert_verify_index(dvert_dst, flip_map[dw_src->def_nr]);
236         }
237         else {
238           dw_dst = defvert_find_index(dvert_dst, flip_map[dw_src->def_nr]);
239         }
240
241         if (dw_dst) {
242           dw_dst->weight = dw_src->weight;
243         }
244       }
245     }
246   }
247 }
248
249 /**
250  * be sure all flip_map values are valid
251  */
252 void defvert_remap(MDeformVert *dvert, int *map, const int map_len)
253 {
254   MDeformWeight *dw = dvert->dw;
255   unsigned int i;
256   for (i = dvert->totweight; i != 0; i--, dw++) {
257     if (dw->def_nr < map_len) {
258       dw->def_nr = map[dw->def_nr];
259
260       /* just in case */
261       BLI_assert(dw->def_nr >= 0);
262     }
263   }
264 }
265
266 /**
267  * Same as #defvert_normalize but takes a bool array.
268  */
269 void defvert_normalize_subset(MDeformVert *dvert, const bool *vgroup_subset, const int vgroup_tot)
270 {
271   if (dvert->totweight == 0) {
272     /* nothing */
273   }
274   else if (dvert->totweight == 1) {
275     MDeformWeight *dw = dvert->dw;
276     if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
277       dw->weight = 1.0f;
278     }
279   }
280   else {
281     MDeformWeight *dw;
282     unsigned int i;
283     float tot_weight = 0.0f;
284
285     for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
286       if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
287         tot_weight += dw->weight;
288       }
289     }
290
291     if (tot_weight > 0.0f) {
292       float scalar = 1.0f / tot_weight;
293       for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
294         if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
295           dw->weight *= scalar;
296
297           /* in case of division errors with very low weights */
298           CLAMP(dw->weight, 0.0f, 1.0f);
299         }
300       }
301     }
302   }
303 }
304
305 void defvert_normalize(MDeformVert *dvert)
306 {
307   if (dvert->totweight == 0) {
308     /* nothing */
309   }
310   else if (dvert->totweight == 1) {
311     dvert->dw[0].weight = 1.0f;
312   }
313   else {
314     MDeformWeight *dw;
315     unsigned int i;
316     float tot_weight = 0.0f;
317
318     for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
319       tot_weight += dw->weight;
320     }
321
322     if (tot_weight > 0.0f) {
323       float scalar = 1.0f / tot_weight;
324       for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
325         dw->weight *= scalar;
326
327         /* in case of division errors with very low weights */
328         CLAMP(dw->weight, 0.0f, 1.0f);
329       }
330     }
331   }
332 }
333
334 /**
335  * Same as defvert_normalize() if the locked vgroup is not a member of the subset
336  */
337 void defvert_normalize_lock_single(MDeformVert *dvert,
338                                    const bool *vgroup_subset,
339                                    const int vgroup_tot,
340                                    const int def_nr_lock)
341 {
342   if (dvert->totweight == 0) {
343     /* nothing */
344   }
345   else if (dvert->totweight == 1) {
346     MDeformWeight *dw = dvert->dw;
347     if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
348       if (def_nr_lock != 0) {
349         dw->weight = 1.0f;
350       }
351     }
352   }
353   else {
354     MDeformWeight *dw_lock = NULL;
355     MDeformWeight *dw;
356     unsigned int i;
357     float tot_weight = 0.0f;
358     float lock_iweight = 1.0f;
359
360     for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
361       if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
362         if (dw->def_nr != def_nr_lock) {
363           tot_weight += dw->weight;
364         }
365         else {
366           dw_lock = dw;
367           lock_iweight = (1.0f - dw_lock->weight);
368           CLAMP(lock_iweight, 0.0f, 1.0f);
369         }
370       }
371     }
372
373     if (tot_weight > 0.0f) {
374       /* paranoid, should be 1.0 but in case of float error clamp anyway */
375
376       float scalar = (1.0f / tot_weight) * lock_iweight;
377       for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
378         if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
379           if (dw != dw_lock) {
380             dw->weight *= scalar;
381
382             /* in case of division errors with very low weights */
383             CLAMP(dw->weight, 0.0f, 1.0f);
384           }
385         }
386       }
387     }
388   }
389 }
390
391 /**
392  * Same as defvert_normalize() if no locked vgroup is a member of the subset
393  */
394 void defvert_normalize_lock_map(MDeformVert *dvert,
395                                 const bool *vgroup_subset,
396                                 const int vgroup_tot,
397                                 const bool *lock_flags,
398                                 const int defbase_tot)
399 {
400   if (dvert->totweight == 0) {
401     /* nothing */
402   }
403   else if (dvert->totweight == 1) {
404     MDeformWeight *dw = dvert->dw;
405     if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
406       if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) {
407         dw->weight = 1.0f;
408       }
409     }
410   }
411   else {
412     MDeformWeight *dw;
413     unsigned int i;
414     float tot_weight = 0.0f;
415     float lock_iweight = 0.0f;
416
417     for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
418       if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
419         if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) {
420           tot_weight += dw->weight;
421         }
422         else {
423           /* invert after */
424           lock_iweight += dw->weight;
425         }
426       }
427     }
428
429     lock_iweight = max_ff(0.0f, 1.0f - lock_iweight);
430
431     if (tot_weight > 0.0f) {
432       /* paranoid, should be 1.0 but in case of float error clamp anyway */
433
434       float scalar = (1.0f / tot_weight) * lock_iweight;
435       for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
436         if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
437           if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) {
438             dw->weight *= scalar;
439
440             /* in case of division errors with very low weights */
441             CLAMP(dw->weight, 0.0f, 1.0f);
442           }
443         }
444       }
445     }
446   }
447 }
448
449 void defvert_flip(MDeformVert *dvert, const int *flip_map, const int flip_map_len)
450 {
451   MDeformWeight *dw;
452   int i;
453
454   for (dw = dvert->dw, i = 0; i < dvert->totweight; dw++, i++) {
455     if (dw->def_nr < flip_map_len) {
456       if (flip_map[dw->def_nr] >= 0) {
457         dw->def_nr = flip_map[dw->def_nr];
458       }
459     }
460   }
461 }
462
463 void defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int flip_map_len)
464 {
465   MDeformWeight *dw, *dw_cpy;
466   float weight;
467   int i, totweight = dvert->totweight;
468
469   /* copy weights */
470   for (dw = dvert->dw, i = 0; i < totweight; dw++, i++) {
471     if (dw->def_nr < flip_map_len) {
472       if (flip_map[dw->def_nr] >= 0) {
473         /* error checkers complain of this but we'll never get NULL return */
474         dw_cpy = defvert_verify_index(dvert, flip_map[dw->def_nr]);
475         dw = &dvert->dw[i]; /* in case array got realloced */
476
477         /* distribute weights: if only one of the vertex groups was
478          * assigned this will halve the weights, otherwise it gets
479          * evened out. this keeps it proportional to other groups */
480         weight = 0.5f * (dw_cpy->weight + dw->weight);
481         dw_cpy->weight = weight;
482         dw->weight = weight;
483       }
484     }
485   }
486 }
487
488 bDeformGroup *defgroup_find_name(Object *ob, const char *name)
489 {
490   return (name && name[0] != '\0') ?
491              BLI_findstring(&ob->defbase, name, offsetof(bDeformGroup, name)) :
492              NULL;
493 }
494
495 int defgroup_name_index(Object *ob, const char *name)
496 {
497   return (name && name[0] != '\0') ?
498              BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name)) :
499              -1;
500 }
501
502 /**
503  * \note caller must free.
504  */
505 int *defgroup_flip_map(Object *ob, int *flip_map_len, const bool use_default)
506 {
507   int defbase_tot = *flip_map_len = BLI_listbase_count(&ob->defbase);
508
509   if (defbase_tot == 0) {
510     return NULL;
511   }
512   else {
513     bDeformGroup *dg;
514     char name_flip[sizeof(dg->name)];
515     int i, flip_num, *map = MEM_mallocN(defbase_tot * sizeof(int), __func__);
516
517     for (i = 0; i < defbase_tot; i++) {
518       map[i] = -1;
519     }
520
521     for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) {
522       if (map[i] == -1) { /* may be calculated previously */
523
524         /* in case no valid value is found, use this */
525         if (use_default) {
526           map[i] = i;
527         }
528
529         BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip));
530
531         if (!STREQ(name_flip, dg->name)) {
532           flip_num = defgroup_name_index(ob, name_flip);
533           if (flip_num >= 0) {
534             map[i] = flip_num;
535             map[flip_num] = i; /* save an extra lookup */
536           }
537         }
538       }
539     }
540     return map;
541   }
542 }
543
544 /**
545  * \note caller must free.
546  */
547 int *defgroup_flip_map_single(Object *ob, int *flip_map_len, const bool use_default, int defgroup)
548 {
549   int defbase_tot = *flip_map_len = BLI_listbase_count(&ob->defbase);
550
551   if (defbase_tot == 0) {
552     return NULL;
553   }
554   else {
555     bDeformGroup *dg;
556     char name_flip[sizeof(dg->name)];
557     int i, flip_num, *map = MEM_mallocN(defbase_tot * sizeof(int), __func__);
558
559     for (i = 0; i < defbase_tot; i++) {
560       map[i] = use_default ? i : -1;
561     }
562
563     dg = BLI_findlink(&ob->defbase, defgroup);
564
565     BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip));
566     if (!STREQ(name_flip, dg->name)) {
567       flip_num = defgroup_name_index(ob, name_flip);
568
569       if (flip_num != -1) {
570         map[defgroup] = flip_num;
571         map[flip_num] = defgroup;
572       }
573     }
574
575     return map;
576   }
577 }
578
579 int defgroup_flip_index(Object *ob, int index, const bool use_default)
580 {
581   bDeformGroup *dg = BLI_findlink(&ob->defbase, index);
582   int flip_index = -1;
583
584   if (dg) {
585     char name_flip[sizeof(dg->name)];
586     BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip));
587
588     if (!STREQ(name_flip, dg->name)) {
589       flip_index = defgroup_name_index(ob, name_flip);
590     }
591   }
592
593   return (flip_index == -1 && use_default) ? index : flip_index;
594 }
595
596 static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, Object *ob)
597 {
598   bDeformGroup *curdef;
599
600   for (curdef = ob->defbase.first; curdef; curdef = curdef->next) {
601     if (dg != curdef) {
602       if (STREQ(curdef->name, name)) {
603         return true;
604       }
605     }
606   }
607
608   return false;
609 }
610
611 static bool defgroup_unique_check(void *arg, const char *name)
612 {
613   struct {
614     Object *ob;
615     void *dg;
616   } *data = arg;
617   return defgroup_find_name_dupe(name, data->dg, data->ob);
618 }
619
620 void defgroup_unique_name(bDeformGroup *dg, Object *ob)
621 {
622   struct {
623     Object *ob;
624     void *dg;
625   } data;
626   data.ob = ob;
627   data.dg = dg;
628
629   BLI_uniquename_cb(defgroup_unique_check, &data, DATA_("Group"), '.', dg->name, sizeof(dg->name));
630 }
631
632 float defvert_find_weight(const struct MDeformVert *dvert, const int defgroup)
633 {
634   MDeformWeight *dw = defvert_find_index(dvert, defgroup);
635   return dw ? dw->weight : 0.0f;
636 }
637
638 /**
639  * Take care with this the rationale is:
640  * - if the object has no vertex group. act like vertex group isn't set and return 1.0,
641  * - if the vertex group exists but the 'defgroup' isn't found on this vertex, _still_ return 0.0
642  *
643  * This is a bit confusing, just saves some checks from the caller.
644  */
645 float defvert_array_find_weight_safe(const struct MDeformVert *dvert,
646                                      const int index,
647                                      const int defgroup)
648 {
649   /* Invalid defgroup index means the vgroup selected is invalid, does not exist, in that case it is OK to return 1.0
650    * (i.e. maximum weight, as if no vgroup was selected).
651    * But in case of valid defgroup and NULL dvert data pointer, it means that vgroup **is** valid,
652    * and just totally empty, so we shall return '0.0' value then!
653    */
654   if (defgroup == -1) {
655     return 1.0f;
656   }
657   else if (dvert == NULL) {
658     return 0.0f;
659   }
660
661   return defvert_find_weight(dvert + index, defgroup);
662 }
663
664 MDeformWeight *defvert_find_index(const MDeformVert *dvert, const int defgroup)
665 {
666   if (dvert && defgroup >= 0) {
667     MDeformWeight *dw = dvert->dw;
668     unsigned int i;
669
670     for (i = dvert->totweight; i != 0; i--, dw++) {
671       if (dw->def_nr == defgroup) {
672         return dw;
673       }
674     }
675   }
676   else {
677     BLI_assert(0);
678   }
679
680   return NULL;
681 }
682
683 /**
684  * Ensures that mv has a deform weight entry for the specified defweight group.
685  *
686  * \note this function is mirrored in editmesh_tools.c, for use for editvertices.
687  */
688 MDeformWeight *defvert_verify_index(MDeformVert *dvert, const int defgroup)
689 {
690   MDeformWeight *dw_new;
691
692   /* do this check always, this function is used to check for it */
693   if (!dvert || defgroup < 0) {
694     BLI_assert(0);
695     return NULL;
696   }
697
698   dw_new = defvert_find_index(dvert, defgroup);
699   if (dw_new) {
700     return dw_new;
701   }
702
703   dw_new = MEM_mallocN(sizeof(MDeformWeight) * (dvert->totweight + 1), "deformWeight");
704   if (dvert->dw) {
705     memcpy(dw_new, dvert->dw, sizeof(MDeformWeight) * dvert->totweight);
706     MEM_freeN(dvert->dw);
707   }
708   dvert->dw = dw_new;
709   dw_new += dvert->totweight;
710   dw_new->weight = 0.0f;
711   dw_new->def_nr = defgroup;
712   /* Group index */
713
714   dvert->totweight++;
715
716   return dw_new;
717 }
718
719 /* TODO. merge with code above! */
720
721 /**
722  * Adds the given vertex to the specified vertex group, with given weight.
723  *
724  * \warning this does NOT check for existing, assume caller already knows its not there.
725  */
726 void defvert_add_index_notest(MDeformVert *dvert, int defgroup, const float weight)
727 {
728   MDeformWeight *dw_new;
729
730   /* do this check always, this function is used to check for it */
731   if (!dvert || defgroup < 0) {
732     BLI_assert(0);
733     return;
734   }
735
736   dw_new = MEM_callocN(sizeof(MDeformWeight) * (dvert->totweight + 1),
737                        "defvert_add_to group, new deformWeight");
738   if (dvert->dw) {
739     memcpy(dw_new, dvert->dw, sizeof(MDeformWeight) * dvert->totweight);
740     MEM_freeN(dvert->dw);
741   }
742   dvert->dw = dw_new;
743   dw_new += dvert->totweight;
744   dw_new->weight = weight;
745   dw_new->def_nr = defgroup;
746   dvert->totweight++;
747 }
748
749 /**
750  * Removes the given vertex from the vertex group.
751  *
752  * \warning This function frees the given MDeformWeight, do not use it afterward!
753  */
754 void defvert_remove_group(MDeformVert *dvert, MDeformWeight *dw)
755 {
756   if (dvert && dw) {
757     int i = dw - dvert->dw;
758
759     /* Security check! */
760     if (i < 0 || i >= dvert->totweight) {
761       return;
762     }
763
764     dvert->totweight--;
765     /* If there are still other deform weights attached to this vert then remove
766      * this deform weight, and reshuffle the others.
767      */
768     if (dvert->totweight) {
769       BLI_assert(dvert->dw != NULL);
770
771       if (i != dvert->totweight) {
772         dvert->dw[i] = dvert->dw[dvert->totweight];
773       }
774
775       dvert->dw = MEM_reallocN(dvert->dw, sizeof(MDeformWeight) * dvert->totweight);
776     }
777     else {
778       /* If there are no other deform weights left then just remove this one. */
779       MEM_freeN(dvert->dw);
780       dvert->dw = NULL;
781     }
782   }
783 }
784
785 void defvert_clear(MDeformVert *dvert)
786 {
787   if (dvert->dw) {
788     MEM_freeN(dvert->dw);
789     dvert->dw = NULL;
790   }
791
792   dvert->totweight = 0;
793 }
794
795 /**
796  * \return The first group index shared by both deform verts
797  * or -1 if none are found.
798  */
799 int defvert_find_shared(const MDeformVert *dvert_a, const MDeformVert *dvert_b)
800 {
801   if (dvert_a->totweight && dvert_b->totweight) {
802     MDeformWeight *dw = dvert_a->dw;
803     unsigned int i;
804
805     for (i = dvert_a->totweight; i != 0; i--, dw++) {
806       if (dw->weight > 0.0f && defvert_find_weight(dvert_b, dw->def_nr) > 0.0f) {
807         return dw->def_nr;
808       }
809     }
810   }
811
812   return -1;
813 }
814
815 /**
816  * return true if has no weights
817  */
818 bool defvert_is_weight_zero(const struct MDeformVert *dvert, const int defgroup_tot)
819 {
820   MDeformWeight *dw = dvert->dw;
821   unsigned int i;
822   for (i = dvert->totweight; i != 0; i--, dw++) {
823     if (dw->weight != 0.0f) {
824       /* check the group is in-range, happens on rare situations */
825       if (LIKELY(dw->def_nr < defgroup_tot)) {
826         return false;
827       }
828     }
829   }
830   return true;
831 }
832
833 /**
834  * \return The representative weight of a multipaint group, used for
835  * viewport colors and actual painting.
836  *
837  * Result equal to sum of weights with auto normalize, and average otherwise.
838  * Value is not clamped, since painting relies on multiplication being always
839  * commutative with the collective weight function.
840  */
841 float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
842                                                int defbase_tot,
843                                                const bool *defbase_sel,
844                                                int defbase_tot_sel,
845                                                bool do_autonormalize)
846 {
847   int i;
848   float total = 0.0f;
849   const MDeformWeight *dw = dv->dw;
850
851   for (i = dv->totweight; i != 0; i--, dw++) {
852     /* in multipaint, get the average if auto normalize is inactive
853      * get the sum if it is active */
854     if (dw->def_nr < defbase_tot) {
855       if (defbase_sel[dw->def_nr]) {
856         total += dw->weight;
857       }
858     }
859   }
860
861   if (do_autonormalize == false) {
862     total /= defbase_tot_sel;
863   }
864
865   return total;
866 }
867
868 /* -------------------------------------------------------------------- */
869 /** \name Defvert Array functions
870  * \{ */
871
872 void BKE_defvert_array_copy(MDeformVert *dst, const MDeformVert *src, int copycount)
873 {
874   /* Assumes dst is already set up */
875   int i;
876
877   if (!src || !dst) {
878     return;
879   }
880
881   memcpy(dst, src, copycount * sizeof(MDeformVert));
882
883   for (i = 0; i < copycount; i++) {
884     if (src[i].dw) {
885       dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * src[i].totweight, "copy_deformWeight");
886       memcpy(dst[i].dw, src[i].dw, sizeof(MDeformWeight) * src[i].totweight);
887     }
888   }
889 }
890
891 void BKE_defvert_array_free_elems(MDeformVert *dvert, int totvert)
892 {
893   /* Instead of freeing the verts directly,
894    * call this function to delete any special
895    * vert data */
896   int i;
897
898   if (!dvert) {
899     return;
900   }
901
902   /* Free any special data from the verts */
903   for (i = 0; i < totvert; i++) {
904     if (dvert[i].dw) {
905       MEM_freeN(dvert[i].dw);
906     }
907   }
908 }
909
910 void BKE_defvert_array_free(MDeformVert *dvert, int totvert)
911 {
912   /* Instead of freeing the verts directly,
913    * call this function to delete any special
914    * vert data */
915   if (!dvert) {
916     return;
917   }
918
919   /* Free any special data from the verts */
920   BKE_defvert_array_free_elems(dvert, totvert);
921
922   MEM_freeN(dvert);
923 }
924
925 void BKE_defvert_extract_vgroup_to_vertweights(MDeformVert *dvert,
926                                                const int defgroup,
927                                                const int num_verts,
928                                                float *r_weights,
929                                                const bool invert_vgroup)
930 {
931   if (dvert && defgroup != -1) {
932     int i = num_verts;
933
934     while (i--) {
935       const float w = defvert_find_weight(&dvert[i], defgroup);
936       r_weights[i] = invert_vgroup ? (1.0f - w) : w;
937     }
938   }
939   else {
940     copy_vn_fl(r_weights, num_verts, invert_vgroup ? 1.0f : 0.0f);
941   }
942 }
943
944 /**
945  * The following three make basic interpolation,
946  * using temp vert_weights array to avoid looking up same weight several times.
947  */
948 void BKE_defvert_extract_vgroup_to_edgeweights(MDeformVert *dvert,
949                                                const int defgroup,
950                                                const int num_verts,
951                                                MEdge *edges,
952                                                const int num_edges,
953                                                float *r_weights,
954                                                const bool invert_vgroup)
955 {
956   if (dvert && defgroup != -1) {
957     int i = num_edges;
958     float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);
959
960     BKE_defvert_extract_vgroup_to_vertweights(
961         dvert, defgroup, num_verts, tmp_weights, invert_vgroup);
962
963     while (i--) {
964       MEdge *me = &edges[i];
965
966       r_weights[i] = (tmp_weights[me->v1] + tmp_weights[me->v2]) * 0.5f;
967     }
968
969     MEM_freeN(tmp_weights);
970   }
971   else {
972     copy_vn_fl(r_weights, num_edges, 0.0f);
973   }
974 }
975
976 void BKE_defvert_extract_vgroup_to_loopweights(MDeformVert *dvert,
977                                                const int defgroup,
978                                                const int num_verts,
979                                                MLoop *loops,
980                                                const int num_loops,
981                                                float *r_weights,
982                                                const bool invert_vgroup)
983 {
984   if (dvert && defgroup != -1) {
985     int i = num_loops;
986     float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);
987
988     BKE_defvert_extract_vgroup_to_vertweights(
989         dvert, defgroup, num_verts, tmp_weights, invert_vgroup);
990
991     while (i--) {
992       MLoop *ml = &loops[i];
993
994       r_weights[i] = tmp_weights[ml->v];
995     }
996
997     MEM_freeN(tmp_weights);
998   }
999   else {
1000     copy_vn_fl(r_weights, num_loops, 0.0f);
1001   }
1002 }
1003
1004 void BKE_defvert_extract_vgroup_to_polyweights(MDeformVert *dvert,
1005                                                const int defgroup,
1006                                                const int num_verts,
1007                                                MLoop *loops,
1008                                                const int UNUSED(num_loops),
1009                                                MPoly *polys,
1010                                                const int num_polys,
1011                                                float *r_weights,
1012                                                const bool invert_vgroup)
1013 {
1014   if (dvert && defgroup != -1) {
1015     int i = num_polys;
1016     float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);
1017
1018     BKE_defvert_extract_vgroup_to_vertweights(
1019         dvert, defgroup, num_verts, tmp_weights, invert_vgroup);
1020
1021     while (i--) {
1022       MPoly *mp = &polys[i];
1023       MLoop *ml = &loops[mp->loopstart];
1024       int j = mp->totloop;
1025       float w = 0.0f;
1026
1027       for (; j--; ml++) {
1028         w += tmp_weights[ml->v];
1029       }
1030       r_weights[i] = w / (float)mp->totloop;
1031     }
1032
1033     MEM_freeN(tmp_weights);
1034   }
1035   else {
1036     copy_vn_fl(r_weights, num_polys, 0.0f);
1037   }
1038 }
1039
1040 /** \} */
1041
1042 /* -------------------------------------------------------------------- */
1043 /** \name Data Transfer
1044  * \{ */
1045
1046 static void vgroups_datatransfer_interp(const CustomDataTransferLayerMap *laymap,
1047                                         void *dest,
1048                                         const void **sources,
1049                                         const float *weights,
1050                                         const int count,
1051                                         const float mix_factor)
1052 {
1053   MDeformVert **data_src = (MDeformVert **)sources;
1054   MDeformVert *data_dst = (MDeformVert *)dest;
1055   const int idx_src = laymap->data_src_n;
1056   const int idx_dst = laymap->data_dst_n;
1057
1058   const int mix_mode = laymap->mix_mode;
1059
1060   int i, j;
1061
1062   MDeformWeight *dw_src;
1063   MDeformWeight *dw_dst = defvert_find_index(data_dst, idx_dst);
1064   float weight_src = 0.0f, weight_dst = 0.0f;
1065
1066   if (sources) {
1067     for (i = count; i--;) {
1068       for (j = data_src[i]->totweight; j--;) {
1069         if ((dw_src = &data_src[i]->dw[j])->def_nr == idx_src) {
1070           weight_src += dw_src->weight * weights[i];
1071           break;
1072         }
1073       }
1074     }
1075   }
1076
1077   if (dw_dst) {
1078     weight_dst = dw_dst->weight;
1079   }
1080   else if (mix_mode == CDT_MIX_REPLACE_ABOVE_THRESHOLD) {
1081     return; /* Do not affect destination. */
1082   }
1083
1084   weight_src = data_transfer_interp_float_do(mix_mode, weight_dst, weight_src, mix_factor);
1085
1086   CLAMP(weight_src, 0.0f, 1.0f);
1087
1088   if (!dw_dst) {
1089     defvert_add_index_notest(data_dst, idx_dst, weight_src);
1090   }
1091   else {
1092     dw_dst->weight = weight_src;
1093   }
1094 }
1095
1096 static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map,
1097                                                                 const int mix_mode,
1098                                                                 const float mix_factor,
1099                                                                 const float *mix_weights,
1100                                                                 const int num_elem_dst,
1101                                                                 const bool use_create,
1102                                                                 const bool use_delete,
1103                                                                 Object *ob_src,
1104                                                                 Object *ob_dst,
1105                                                                 MDeformVert *data_src,
1106                                                                 MDeformVert *data_dst,
1107                                                                 CustomData *UNUSED(cd_src),
1108                                                                 CustomData *cd_dst,
1109                                                                 const bool UNUSED(use_dupref_dst),
1110                                                                 const int tolayers,
1111                                                                 bool *use_layers_src,
1112                                                                 const int num_layers_src)
1113 {
1114   int idx_src;
1115   int idx_dst;
1116   int tot_dst = BLI_listbase_count(&ob_dst->defbase);
1117
1118   const size_t elem_size = sizeof(*((MDeformVert *)NULL));
1119
1120   switch (tolayers) {
1121     case DT_LAYERS_INDEX_DST:
1122       idx_dst = tot_dst;
1123
1124       /* Find last source actually used! */
1125       idx_src = num_layers_src;
1126       while (idx_src-- && !use_layers_src[idx_src]) {
1127         ;
1128       }
1129       idx_src++;
1130
1131       if (idx_dst < idx_src) {
1132         if (use_create) {
1133           /* Create as much vgroups as necessary! */
1134           for (; idx_dst < idx_src; idx_dst++) {
1135             BKE_object_defgroup_add(ob_dst);
1136           }
1137         }
1138         else {
1139           /* Otherwise, just try to map what we can with existing dst vgroups. */
1140           idx_src = idx_dst;
1141         }
1142       }
1143       else if (use_delete && idx_dst > idx_src) {
1144         while (idx_dst-- > idx_src) {
1145           BKE_object_defgroup_remove(ob_dst, ob_dst->defbase.last);
1146         }
1147       }
1148       if (r_map) {
1149         /* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest!
1150          * Again, use_create is not relevant in this case */
1151         if (!data_dst) {
1152           data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst);
1153         }
1154
1155         while (idx_src--) {
1156           if (!use_layers_src[idx_src]) {
1157             continue;
1158           }
1159           data_transfer_layersmapping_add_item(r_map,
1160                                                CD_FAKE_MDEFORMVERT,
1161                                                mix_mode,
1162                                                mix_factor,
1163                                                mix_weights,
1164                                                data_src,
1165                                                data_dst,
1166                                                idx_src,
1167                                                idx_src,
1168                                                elem_size,
1169                                                0,
1170                                                0,
1171                                                0,
1172                                                vgroups_datatransfer_interp,
1173                                                NULL);
1174         }
1175       }
1176       break;
1177     case DT_LAYERS_NAME_DST: {
1178       bDeformGroup *dg_src, *dg_dst;
1179
1180       if (use_delete) {
1181         /* Remove all unused dst vgroups first, simpler in this case. */
1182         for (dg_dst = ob_dst->defbase.first; dg_dst;) {
1183           bDeformGroup *dg_dst_next = dg_dst->next;
1184
1185           if (defgroup_name_index(ob_src, dg_dst->name) == -1) {
1186             BKE_object_defgroup_remove(ob_dst, dg_dst);
1187           }
1188           dg_dst = dg_dst_next;
1189         }
1190       }
1191
1192       for (idx_src = 0, dg_src = ob_src->defbase.first; idx_src < num_layers_src;
1193            idx_src++, dg_src = dg_src->next) {
1194         if (!use_layers_src[idx_src]) {
1195           continue;
1196         }
1197
1198         if ((idx_dst = defgroup_name_index(ob_dst, dg_src->name)) == -1) {
1199           if (use_create) {
1200             BKE_object_defgroup_add_name(ob_dst, dg_src->name);
1201             idx_dst = ob_dst->actdef - 1;
1202           }
1203           else {
1204             /* If we are not allowed to create missing dst vgroups, just skip matching src one. */
1205             continue;
1206           }
1207         }
1208         if (r_map) {
1209           /* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest!
1210            * use_create is not relevant in this case */
1211           if (!data_dst) {
1212             data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst);
1213           }
1214
1215           data_transfer_layersmapping_add_item(r_map,
1216                                                CD_FAKE_MDEFORMVERT,
1217                                                mix_mode,
1218                                                mix_factor,
1219                                                mix_weights,
1220                                                data_src,
1221                                                data_dst,
1222                                                idx_src,
1223                                                idx_dst,
1224                                                elem_size,
1225                                                0,
1226                                                0,
1227                                                0,
1228                                                vgroups_datatransfer_interp,
1229                                                NULL);
1230         }
1231       }
1232       break;
1233     }
1234     default:
1235       return false;
1236   }
1237
1238   return true;
1239 }
1240
1241 bool data_transfer_layersmapping_vgroups(ListBase *r_map,
1242                                          const int mix_mode,
1243                                          const float mix_factor,
1244                                          const float *mix_weights,
1245                                          const int num_elem_dst,
1246                                          const bool use_create,
1247                                          const bool use_delete,
1248                                          Object *ob_src,
1249                                          Object *ob_dst,
1250                                          CustomData *cd_src,
1251                                          CustomData *cd_dst,
1252                                          const bool use_dupref_dst,
1253                                          const int fromlayers,
1254                                          const int tolayers)
1255 {
1256   int idx_src, idx_dst;
1257   MDeformVert *data_src, *data_dst = NULL;
1258
1259   const size_t elem_size = sizeof(*((MDeformVert *)NULL));
1260
1261   /* Note: VGroups are a bit hairy, since their layout is defined on object level (ob->defbase), while their actual
1262    *       data is a (mesh) CD layer.
1263    *       This implies we may have to handle data layout itself while having NULL data itself,
1264    *       and even have to support NULL data_src in transfer data code (we always create a data_dst, though).
1265    */
1266
1267   if (BLI_listbase_is_empty(&ob_src->defbase)) {
1268     if (use_delete) {
1269       BKE_object_defgroup_remove_all(ob_dst);
1270     }
1271     return true;
1272   }
1273
1274   data_src = CustomData_get_layer(cd_src, CD_MDEFORMVERT);
1275
1276   data_dst = CustomData_get_layer(cd_dst, CD_MDEFORMVERT);
1277   if (data_dst && use_dupref_dst && r_map) {
1278     /* If dest is a derivedmesh, we do not want to overwrite cdlayers of org mesh! */
1279     data_dst = CustomData_duplicate_referenced_layer(cd_dst, CD_MDEFORMVERT, num_elem_dst);
1280   }
1281
1282   if (fromlayers == DT_LAYERS_ACTIVE_SRC || fromlayers >= 0) {
1283     /* Note: use_delete has not much meaning in this case, ignored. */
1284
1285     if (fromlayers >= 0) {
1286       idx_src = fromlayers;
1287       if (idx_src >= BLI_listbase_count(&ob_src->defbase)) {
1288         /* This can happen when vgroups are removed from source object...
1289          * Remapping would be really tricky here, we'd need to go over all objects in
1290          * Main every time we delete a vgroup... for now, simpler and safer to abort. */
1291         return false;
1292       }
1293     }
1294     else if ((idx_src = ob_src->actdef - 1) == -1) {
1295       return false;
1296     }
1297
1298     if (tolayers >= 0) {
1299       /* Note: in this case we assume layer exists! */
1300       idx_dst = tolayers;
1301       BLI_assert(idx_dst < BLI_listbase_count(&ob_dst->defbase));
1302     }
1303     else if (tolayers == DT_LAYERS_ACTIVE_DST) {
1304       if ((idx_dst = ob_dst->actdef - 1) == -1) {
1305         bDeformGroup *dg_src;
1306         if (!use_create) {
1307           return true;
1308         }
1309         dg_src = BLI_findlink(&ob_src->defbase, idx_src);
1310         BKE_object_defgroup_add_name(ob_dst, dg_src->name);
1311         idx_dst = ob_dst->actdef - 1;
1312       }
1313     }
1314     else if (tolayers == DT_LAYERS_INDEX_DST) {
1315       int num = BLI_listbase_count(&ob_src->defbase);
1316       idx_dst = idx_src;
1317       if (num <= idx_dst) {
1318         if (!use_create) {
1319           return true;
1320         }
1321         /* Create as much vgroups as necessary! */
1322         for (; num <= idx_dst; num++) {
1323           BKE_object_defgroup_add(ob_dst);
1324         }
1325       }
1326     }
1327     else if (tolayers == DT_LAYERS_NAME_DST) {
1328       bDeformGroup *dg_src = BLI_findlink(&ob_src->defbase, idx_src);
1329       if ((idx_dst = defgroup_name_index(ob_dst, dg_src->name)) == -1) {
1330         if (!use_create) {
1331           return true;
1332         }
1333         BKE_object_defgroup_add_name(ob_dst, dg_src->name);
1334         idx_dst = ob_dst->actdef - 1;
1335       }
1336     }
1337     else {
1338       return false;
1339     }
1340
1341     if (r_map) {
1342       /* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest!
1343        * use_create is not relevant in this case */
1344       if (!data_dst) {
1345         data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst);
1346       }
1347
1348       data_transfer_layersmapping_add_item(r_map,
1349                                            CD_FAKE_MDEFORMVERT,
1350                                            mix_mode,
1351                                            mix_factor,
1352                                            mix_weights,
1353                                            data_src,
1354                                            data_dst,
1355                                            idx_src,
1356                                            idx_dst,
1357                                            elem_size,
1358                                            0,
1359                                            0,
1360                                            0,
1361                                            vgroups_datatransfer_interp,
1362                                            NULL);
1363     }
1364   }
1365   else {
1366     int num_src, num_sel_unused;
1367     bool *use_layers_src = NULL;
1368     bool ret = false;
1369
1370     switch (fromlayers) {
1371       case DT_LAYERS_ALL_SRC:
1372         use_layers_src = BKE_object_defgroup_subset_from_select_type(
1373             ob_src, WT_VGROUP_ALL, &num_src, &num_sel_unused);
1374         break;
1375       case DT_LAYERS_VGROUP_SRC_BONE_SELECT:
1376         use_layers_src = BKE_object_defgroup_subset_from_select_type(
1377             ob_src, WT_VGROUP_BONE_SELECT, &num_src, &num_sel_unused);
1378         break;
1379       case DT_LAYERS_VGROUP_SRC_BONE_DEFORM:
1380         use_layers_src = BKE_object_defgroup_subset_from_select_type(
1381             ob_src, WT_VGROUP_BONE_DEFORM, &num_src, &num_sel_unused);
1382         break;
1383     }
1384
1385     if (use_layers_src) {
1386       ret = data_transfer_layersmapping_vgroups_multisrc_to_dst(r_map,
1387                                                                 mix_mode,
1388                                                                 mix_factor,
1389                                                                 mix_weights,
1390                                                                 num_elem_dst,
1391                                                                 use_create,
1392                                                                 use_delete,
1393                                                                 ob_src,
1394                                                                 ob_dst,
1395                                                                 data_src,
1396                                                                 data_dst,
1397                                                                 cd_src,
1398                                                                 cd_dst,
1399                                                                 use_dupref_dst,
1400                                                                 tolayers,
1401                                                                 use_layers_src,
1402                                                                 num_src);
1403     }
1404
1405     MEM_SAFE_FREE(use_layers_src);
1406     return ret;
1407   }
1408
1409   return true;
1410 }
1411
1412 /** \} */
1413
1414 /* -------------------------------------------------------------------- */
1415 /** \name Various utils & helpers.
1416  * \{ */
1417
1418 void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight)
1419 {
1420   const float blend = ((weight / 2.0f) + 0.5f);
1421
1422   if (weight <= 0.25f) { /* blue->cyan */
1423     r_rgb[0] = 0.0f;
1424     r_rgb[1] = blend * weight * 4.0f;
1425     r_rgb[2] = blend;
1426   }
1427   else if (weight <= 0.50f) { /* cyan->green */
1428     r_rgb[0] = 0.0f;
1429     r_rgb[1] = blend;
1430     r_rgb[2] = blend * (1.0f - ((weight - 0.25f) * 4.0f));
1431   }
1432   else if (weight <= 0.75f) { /* green->yellow */
1433     r_rgb[0] = blend * ((weight - 0.50f) * 4.0f);
1434     r_rgb[1] = blend;
1435     r_rgb[2] = 0.0f;
1436   }
1437   else if (weight <= 1.0f) { /* yellow->red */
1438     r_rgb[0] = blend;
1439     r_rgb[1] = blend * (1.0f - ((weight - 0.75f) * 4.0f));
1440     r_rgb[2] = 0.0f;
1441   }
1442   else {
1443     /* exceptional value, unclamped or nan,
1444      * avoid uninitialized memory use */
1445     r_rgb[0] = 1.0f;
1446     r_rgb[1] = 0.0f;
1447     r_rgb[2] = 1.0f;
1448   }
1449 }
1450
1451 /** \} */