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