Silence warning
[blender.git] / source / blender / editors / space_view3d / view3d_buttons.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) 2009 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_view3d/view3d_buttons.c
28  *  \ingroup spview3d
29  */
30
31
32 #include <string.h>
33 #include <stdio.h>
34 #include <math.h>
35 #include <float.h>
36
37 #include "DNA_armature_types.h"
38 #include "DNA_curve_types.h"
39 #include "DNA_lattice_types.h"
40 #include "DNA_meta_types.h"
41 #include "DNA_mesh_types.h"
42 #include "DNA_meshdata_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_scene_types.h"
45
46 #include "MEM_guardedalloc.h"
47
48 #include "BLT_translation.h"
49
50 #include "BLI_math.h"
51 #include "BLI_blenlib.h"
52 #include "BLI_utildefines.h"
53
54 #include "BKE_action.h"
55 #include "BKE_context.h"
56 #include "BKE_curve.h"
57 #include "BKE_customdata.h"
58 #include "BKE_screen.h"
59 #include "BKE_editmesh.h"
60 #include "BKE_deform.h"
61 #include "BKE_object.h"
62 #include "BKE_object_deform.h"
63 #include "BKE_report.h"
64
65 #include "DEG_depsgraph.h"
66
67 #include "WM_api.h"
68 #include "WM_types.h"
69
70 #include "RNA_access.h"
71
72 #include "ED_armature.h"
73 #include "ED_object.h"
74 #include "ED_mesh.h"
75 #include "ED_screen.h"
76
77 #include "UI_interface.h"
78 #include "UI_resources.h"
79
80 #include "view3d_intern.h"  /* own include */
81
82
83 /* ******************* view3d space & buttons ************** */
84 #define B_REDR              2
85 #define B_OBJECTPANELMEDIAN 1008
86 #define B_OBJECTPANEL_DIMS  1009
87
88 #define NBR_TRANSFORM_PROPERTIES 8
89
90 /* temporary struct for storing transform properties */
91 typedef struct {
92         float ob_eul[4];   /* used for quat too... */
93         float ob_scale[3]; /* need temp space due to linked values */
94         float ob_dims_orig[3];
95         float ob_dims[3];
96         short link_scale;
97         float ve_median[NBR_TRANSFORM_PROPERTIES];
98 } TransformProperties;
99
100 /* Helper function to compute a median changed value,
101  * when the value should be clamped in [0.0, 1.0].
102  * Returns either 0.0, 1.0 (both can be applied directly), a positive scale factor
103  * for scale down, or a negative one for scale up.
104  */
105 static float compute_scale_factor(const float ve_median, const float median)
106 {
107         if (ve_median <= 0.0f)
108                 return 0.0f;
109         else if (ve_median >= 1.0f)
110                 return 1.0f;
111         else {
112                 /* Scale value to target median. */
113                 float median_new = ve_median;
114                 float median_orig = ve_median - median; /* Previous median value. */
115
116                 /* In case of floating point error. */
117                 CLAMP(median_orig, 0.0f, 1.0f);
118                 CLAMP(median_new, 0.0f, 1.0f);
119
120                 if (median_new <= median_orig) {
121                         /* Scale down. */
122                         return median_new / median_orig;
123                 }
124                 else {
125                         /* Scale up, negative to indicate it... */
126                         return -(1.0f - median_new) / (1.0f - median_orig);
127                 }
128         }
129 }
130
131 /* Apply helpers.
132  * Note: In case we only have one element, copy directly the value instead of applying the diff or scale factor.
133  *       Avoids some glitches when going e.g. from 3 to 0.0001 (see T37327).
134  */
135 static void apply_raw_diff(float *val, const int tot, const float ve_median, const float median)
136 {
137         *val = (tot == 1) ? ve_median : (*val + median);
138 }
139
140 static void apply_raw_diff_v3(float val[3], const int tot, const float ve_median[3], const float median[3])
141 {
142         if (tot == 1) {
143                 copy_v3_v3(val, ve_median);
144         }
145         else {
146                 add_v3_v3(val, median);
147         }
148 }
149
150 static void apply_scale_factor(float *val, const int tot, const float ve_median, const float median, const float sca)
151 {
152         if (tot == 1 || ve_median == median) {
153                 *val = ve_median;
154         }
155         else {
156                 *val *= sca;
157         }
158 }
159
160 static void apply_scale_factor_clamp(float *val, const int tot, const float ve_median, const float sca)
161 {
162         if (tot == 1) {
163                 *val = ve_median;
164                 CLAMP(*val, 0.0f, 1.0f);
165         }
166         else if (ELEM(sca, 0.0f, 1.0f)) {
167                 *val = sca;
168         }
169         else {
170                 *val = (sca > 0.0f) ? (*val * sca) : (1.0f + ((1.0f - *val) * sca));
171                 CLAMP(*val, 0.0f, 1.0f);
172         }
173 }
174
175 static TransformProperties *v3d_transform_props_ensure(View3D *v3d)
176 {
177         if (v3d->properties_storage == NULL) {
178                 v3d->properties_storage = MEM_callocN(sizeof(TransformProperties), "TransformProperties");
179         }
180         return v3d->properties_storage;
181 }
182
183 /* is used for both read and write... */
184 static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float lim)
185 {
186 /* Get rid of those ugly magic numbers, even in a single func they become confusing! */
187 /* Location, common to all. */
188 /* Next three *must* remain contiguous (used as array)! */
189 #define LOC_X        0
190 #define LOC_Y        1
191 #define LOC_Z        2
192 /* Meshes... */
193 #define M_BV_WEIGHT  3
194 /* Next two *must* remain contiguous (used as array)! */
195 #define M_SKIN_X     4
196 #define M_SKIN_Y     5
197 #define M_BE_WEIGHT  6
198 #define M_CREASE     7
199 /* Curves... */
200 #define C_BWEIGHT    3
201 #define C_WEIGHT     4
202 #define C_RADIUS     5
203 #define C_TILT       6
204 /*Lattice... */
205 #define L_WEIGHT     4
206
207         uiBlock *block = (layout) ? uiLayoutAbsoluteBlock(layout) : NULL;
208         TransformProperties *tfp = v3d_transform_props_ensure(v3d);
209         float median[NBR_TRANSFORM_PROPERTIES], ve_median[NBR_TRANSFORM_PROPERTIES];
210         int tot, totedgedata, totcurvedata, totlattdata, totcurvebweight;
211         bool has_meshdata = false;
212         bool has_skinradius = false;
213         PointerRNA data_ptr;
214
215         copy_vn_fl(median, NBR_TRANSFORM_PROPERTIES, 0.0f);
216         tot = totedgedata = totcurvedata = totlattdata = totcurvebweight = 0;
217
218         if (ob->type == OB_MESH) {
219                 Mesh *me = ob->data;
220                 BMEditMesh *em = me->edit_btmesh;
221                 BMesh *bm = em->bm;
222                 BMVert *eve;
223                 BMEdge *eed;
224                 BMIter iter;
225
226                 const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
227                 const int cd_vert_skin_offset    = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN);
228                 const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
229                 const int cd_edge_crease_offset  = CustomData_get_offset(&bm->edata, CD_CREASE);
230
231                 has_skinradius = (cd_vert_skin_offset != -1);
232
233                 if (bm->totvertsel) {
234                         BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
235                                 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
236                                         tot++;
237                                         add_v3_v3(&median[LOC_X], eve->co);
238
239                                         if (cd_vert_bweight_offset != -1) {
240                                                 median[M_BV_WEIGHT] += BM_ELEM_CD_GET_FLOAT(eve, cd_vert_bweight_offset);
241                                         }
242
243                                         if (has_skinradius) {
244                                                 MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset);
245                                                 add_v2_v2(&median[M_SKIN_X], vs->radius); /* Third val not used currently. */
246                                         }
247                                 }
248                         }
249                 }
250
251                 if ((cd_edge_bweight_offset != -1) || (cd_edge_crease_offset  != -1)) {
252                         if (bm->totedgesel) {
253                                 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
254                                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
255                                                 if (cd_edge_bweight_offset != -1) {
256                                                         median[M_BE_WEIGHT] += BM_ELEM_CD_GET_FLOAT(eed, cd_edge_bweight_offset);
257                                                 }
258
259                                                 if (cd_edge_crease_offset != -1) {
260                                                         median[M_CREASE] += BM_ELEM_CD_GET_FLOAT(eed, cd_edge_crease_offset);
261                                                 }
262
263                                                 totedgedata++;
264                                         }
265                                 }
266                         }
267                 }
268                 else {
269                         totedgedata = bm->totedgesel;
270                 }
271
272                 has_meshdata = (tot || totedgedata);
273         }
274         else if (ob->type == OB_CURVE || ob->type == OB_SURF) {
275                 Curve *cu = ob->data;
276                 Nurb *nu;
277                 BPoint *bp;
278                 BezTriple *bezt;
279                 int a;
280                 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
281                 StructRNA *seltype = NULL;
282                 void *selp = NULL;
283
284                 nu = nurbs->first;
285                 while (nu) {
286                         if (nu->type == CU_BEZIER) {
287                                 bezt = nu->bezt;
288                                 a = nu->pntsu;
289                                 while (a--) {
290                                         if (bezt->f2 & SELECT) {
291                                                 add_v3_v3(&median[LOC_X], bezt->vec[1]);
292                                                 tot++;
293                                                 median[C_WEIGHT] += bezt->weight;
294                                                 median[C_RADIUS] += bezt->radius;
295                                                 median[C_TILT] += bezt->alfa;
296                                                 if (!totcurvedata) { /* I.e. first time... */
297                                                         selp = bezt;
298                                                         seltype = &RNA_BezierSplinePoint;
299                                                 }
300                                                 totcurvedata++;
301                                         }
302                                         else {
303                                                 if (bezt->f1 & SELECT) {
304                                                         add_v3_v3(&median[LOC_X], bezt->vec[0]);
305                                                         tot++;
306                                                 }
307                                                 if (bezt->f3 & SELECT) {
308                                                         add_v3_v3(&median[LOC_X], bezt->vec[2]);
309                                                         tot++;
310                                                 }
311                                         }
312                                         bezt++;
313                                 }
314                         }
315                         else {
316                                 bp = nu->bp;
317                                 a = nu->pntsu * nu->pntsv;
318                                 while (a--) {
319                                         if (bp->f1 & SELECT) {
320                                                 add_v3_v3(&median[LOC_X], bp->vec);
321                                                 median[C_BWEIGHT] += bp->vec[3];
322                                                 totcurvebweight++;
323                                                 tot++;
324                                                 median[C_WEIGHT] += bp->weight;
325                                                 median[C_RADIUS] += bp->radius;
326                                                 median[C_TILT] += bp->alfa;
327                                                 if (!totcurvedata) { /* I.e. first time... */
328                                                         selp = bp;
329                                                         seltype = &RNA_SplinePoint;
330                                                 }
331                                                 totcurvedata++;
332                                         }
333                                         bp++;
334                                 }
335                         }
336                         nu = nu->next;
337                 }
338
339                 if (totcurvedata == 1)
340                         RNA_pointer_create(&cu->id, seltype, selp, &data_ptr);
341         }
342         else if (ob->type == OB_LATTICE) {
343                 Lattice *lt = ob->data;
344                 BPoint *bp;
345                 int a;
346                 StructRNA *seltype = NULL;
347                 void *selp = NULL;
348
349                 a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw;
350                 bp = lt->editlatt->latt->def;
351                 while (a--) {
352                         if (bp->f1 & SELECT) {
353                                 add_v3_v3(&median[LOC_X], bp->vec);
354                                 tot++;
355                                 median[L_WEIGHT] += bp->weight;
356                                 if (!totlattdata) { /* I.e. first time... */
357                                         selp = bp;
358                                         seltype = &RNA_LatticePoint;
359                                 }
360                                 totlattdata++;
361                         }
362                         bp++;
363                 }
364
365                 if (totlattdata == 1)
366                         RNA_pointer_create(&lt->id, seltype, selp, &data_ptr);
367         }
368
369         if (tot == 0) {
370                 uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("Nothing selected"), 0, 130, 200, 20, NULL, 0, 0, 0, 0, "");
371                 return;
372         }
373
374         /* Location, X/Y/Z */
375         mul_v3_fl(&median[LOC_X], 1.0f / (float)tot);
376         if (v3d->flag & V3D_GLOBAL_STATS)
377                 mul_m4_v3(ob->obmat, &median[LOC_X]);
378
379         if (has_meshdata) {
380                 if (totedgedata) {
381                         median[M_CREASE] /= (float)totedgedata;
382                         median[M_BE_WEIGHT] /= (float)totedgedata;
383                 }
384                 if (tot) {
385                         median[M_BV_WEIGHT] /= (float)tot;
386                         if (has_skinradius) {
387                                 median[M_SKIN_X] /= (float)tot;
388                                 median[M_SKIN_Y] /= (float)tot;
389                         }
390                 }
391         }
392         else if (totcurvedata) {
393                 if (totcurvebweight) {
394                         median[C_BWEIGHT] /= (float)totcurvebweight;
395                 }
396                 median[C_WEIGHT] /= (float)totcurvedata;
397                 median[C_RADIUS] /= (float)totcurvedata;
398                 median[C_TILT] /= (float)totcurvedata;
399         }
400         else if (totlattdata) {
401                 median[L_WEIGHT] /= (float)totlattdata;
402         }
403
404         if (block) { /* buttons */
405                 uiBut *but;
406                 int yi = 200;
407                 const float tilt_limit = DEG2RADF(21600.0f);
408                 const int buth = 20 * UI_DPI_FAC;
409                 const int but_margin = 2;
410                 const char *c;
411
412                 memcpy(tfp->ve_median, median, sizeof(tfp->ve_median));
413
414                 UI_block_align_begin(block);
415                 if (tot == 1) {
416                         if (totcurvedata) /* Curve */
417                                 c = IFACE_("Control Point:");
418                         else /* Mesh or lattice */
419                                 c = IFACE_("Vertex:");
420                 }
421                 else
422                         c = IFACE_("Median:");
423                 uiDefBut(block, UI_BTYPE_LABEL, 0, c, 0, yi -= buth, 200, buth, NULL, 0, 0, 0, 0, "");
424
425                 UI_block_align_begin(block);
426
427                 /* Should be no need to translate these. */
428                 but = uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("X:"), 0, yi -= buth, 200, buth,
429                                 &(tfp->ve_median[LOC_X]), -lim, lim, 10, RNA_TRANSLATION_PREC_DEFAULT, "");
430                 UI_but_unit_type_set(but, PROP_UNIT_LENGTH);
431                 but = uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Y:"), 0, yi -= buth, 200, buth,
432                                 &(tfp->ve_median[LOC_Y]), -lim, lim, 10, RNA_TRANSLATION_PREC_DEFAULT, "");
433                 UI_but_unit_type_set(but, PROP_UNIT_LENGTH);
434                 but = uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Z:"), 0, yi -= buth, 200, buth,
435                                 &(tfp->ve_median[LOC_Z]), -lim, lim, 10, RNA_TRANSLATION_PREC_DEFAULT, "");
436                 UI_but_unit_type_set(but, PROP_UNIT_LENGTH);
437
438                 if (totcurvebweight == tot) {
439                         uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("W:"), 0, yi -= buth, 200, buth,
440                                   &(tfp->ve_median[C_BWEIGHT]), 0.01, 100.0, 1, 3, "");
441                 }
442
443                 UI_block_align_begin(block);
444                 uiDefButBitS(block, UI_BTYPE_TOGGLE, V3D_GLOBAL_STATS, B_REDR, IFACE_("Global"),
445                              0, yi -= buth + but_margin, 100, buth,
446                              &v3d->flag, 0, 0, 0, 0, TIP_("Displays global values"));
447                 uiDefButBitS(block, UI_BTYPE_TOGGLE_N, V3D_GLOBAL_STATS, B_REDR, IFACE_("Local"),
448                              100, yi, 100, buth,
449                              &v3d->flag, 0, 0, 0, 0, TIP_("Displays local values"));
450                 UI_block_align_end(block);
451
452                 /* Meshes... */
453                 if (has_meshdata) {
454                         if (tot) {
455                                 uiDefBut(block, UI_BTYPE_LABEL, 0, tot == 1 ? IFACE_("Vertex Data:") : IFACE_("Vertices Data:"),
456                                          0, yi -= buth + but_margin, 200, buth, NULL, 0.0, 0.0, 0, 0, "");
457                                 /* customdata layer added on demand */
458                                 uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN,
459                                           tot == 1 ? IFACE_("Bevel Weight:") : IFACE_("Mean Bevel Weight:"),
460                                           0, yi -= buth + but_margin, 200, buth,
461                                           &(tfp->ve_median[M_BV_WEIGHT]), 0.0, 1.0, 1, 2, TIP_("Vertex weight used by Bevel modifier"));
462                         }
463                         if (has_skinradius) {
464                                 UI_block_align_begin(block);
465                                 uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN,
466                                           tot == 1 ? IFACE_("Radius X:") : IFACE_("Mean Radius X:"),
467                                           0, yi -= buth + but_margin, 200, buth,
468                                           &(tfp->ve_median[M_SKIN_X]), 0.0, 100.0, 1, 3, TIP_("X radius used by Skin modifier"));
469                                 uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN,
470                                           tot == 1 ? IFACE_("Radius Y:") : IFACE_("Mean Radius Y:"),
471                                           0, yi -= buth + but_margin, 200, buth,
472                                           &(tfp->ve_median[M_SKIN_Y]), 0.0, 100.0, 1, 3, TIP_("Y radius used by Skin modifier"));
473                                 UI_block_align_end(block);
474                         }
475                         if (totedgedata) {
476                                 uiDefBut(block, UI_BTYPE_LABEL, 0, totedgedata == 1 ? IFACE_("Edge Data:") : IFACE_("Edges Data:"),
477                                          0, yi -= buth + but_margin, 200, buth, NULL, 0.0, 0.0, 0, 0, "");
478                                 /* customdata layer added on demand */
479                                 uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN,
480                                           totedgedata == 1 ? IFACE_("Bevel Weight:") : IFACE_("Mean Bevel Weight:"),
481                                           0, yi -= buth + but_margin, 200, buth,
482                                           &(tfp->ve_median[M_BE_WEIGHT]), 0.0, 1.0, 1, 2, TIP_("Edge weight used by Bevel modifier"));
483                                 /* customdata layer added on demand */
484                                 uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN,
485                                           totedgedata == 1 ? IFACE_("Crease:") : IFACE_("Mean Crease:"),
486                                           0, yi -= buth + but_margin, 200, buth,
487                                           &(tfp->ve_median[M_CREASE]), 0.0, 1.0, 1, 2, TIP_("Weight used by the Subdivision Surface modifier"));
488                         }
489                 }
490                 /* Curve... */
491                 else if (totcurvedata == 1) {
492                         uiDefButR(block, UI_BTYPE_NUM, 0, IFACE_("Weight:"), 0, yi -= buth + but_margin, 200, buth,
493                                   &data_ptr, "weight_softbody", 0, 0.0, 1.0, 1, 3, NULL);
494                         uiDefButR(block, UI_BTYPE_NUM, 0, IFACE_("Radius:"), 0, yi -= buth + but_margin, 200, buth,
495                                   &data_ptr, "radius", 0, 0.0, 100.0, 1, 3, NULL);
496                         uiDefButR(block, UI_BTYPE_NUM, 0, IFACE_("Tilt:"), 0, yi -= buth + but_margin, 200, buth,
497                                   &data_ptr, "tilt", 0, -tilt_limit, tilt_limit, 1, 3, NULL);
498                 }
499                 else if (totcurvedata > 1) {
500                         uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Mean Weight:"),
501                                   0, yi -= buth + but_margin, 200, buth,
502                                   &(tfp->ve_median[C_WEIGHT]), 0.0, 1.0, 1, 3, TIP_("Weight used for Soft Body Goal"));
503                         uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Mean Radius:"),
504                                   0, yi -= buth + but_margin, 200, buth,
505                                   &(tfp->ve_median[C_RADIUS]), 0.0, 100.0, 1, 3, TIP_("Radius of curve control points"));
506                         but = uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Mean Tilt:"),
507                                         0, yi -= buth + but_margin, 200, buth,
508                                         &(tfp->ve_median[C_TILT]), -tilt_limit, tilt_limit, 1, 3,
509                                         TIP_("Tilt of curve control points"));
510                         UI_but_unit_type_set(but, PROP_UNIT_ROTATION);
511                 }
512                 /* Lattice... */
513                 else if (totlattdata == 1) {
514                         uiDefButR(block, UI_BTYPE_NUM, 0, IFACE_("Weight:"), 0, yi -= buth + but_margin, 200, buth,
515                                   &data_ptr, "weight_softbody", 0, 0.0, 1.0, 1, 3, NULL);
516                 }
517                 else if (totlattdata > 1) {
518                         uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Mean Weight:"),
519                                   0, yi -= buth + but_margin, 200, buth,
520                                   &(tfp->ve_median[L_WEIGHT]), 0.0, 1.0, 1, 3, TIP_("Weight used for Soft Body Goal"));
521                 }
522
523                 UI_block_align_end(block);
524         }
525         else { /* apply */
526                 int i;
527                 bool apply_vcos;
528
529                 memcpy(ve_median, tfp->ve_median, sizeof(tfp->ve_median));
530
531                 if (v3d->flag & V3D_GLOBAL_STATS) {
532                         invert_m4_m4(ob->imat, ob->obmat);
533                         mul_m4_v3(ob->imat, &median[LOC_X]);
534                         mul_m4_v3(ob->imat, &ve_median[LOC_X]);
535                 }
536                 i = NBR_TRANSFORM_PROPERTIES;
537                 while (i--)
538                         median[i] = ve_median[i] - median[i];
539
540                 /* Note with a single element selected, we always do. */
541                 apply_vcos = (tot == 1) || (len_squared_v3(&median[LOC_X]) != 0.0f);
542
543                 if ((ob->type == OB_MESH) &&
544                     (apply_vcos || median[M_BV_WEIGHT] || median[M_SKIN_X] || median[M_SKIN_Y] ||
545                      median[M_BE_WEIGHT] || median[M_CREASE]))
546                 {
547                         Mesh *me = ob->data;
548                         BMEditMesh *em = me->edit_btmesh;
549                         BMesh *bm = em->bm;
550                         BMIter iter;
551                         BMVert *eve;
552                         BMEdge *eed;
553
554                         int cd_vert_bweight_offset = -1;
555                         int cd_vert_skin_offset = -1;
556                         int cd_edge_bweight_offset = -1;
557                         int cd_edge_crease_offset = -1;
558
559                         float scale_bv_weight = 1.0f;
560                         float scale_skin_x = 1.0f;
561                         float scale_skin_y = 1.0f;
562                         float scale_be_weight = 1.0f;
563                         float scale_crease = 1.0f;
564
565                         /* Vertices */
566
567                         if (apply_vcos || median[M_BV_WEIGHT] || median[M_SKIN_X] || median[M_SKIN_Y]) {
568                                 if (median[M_BV_WEIGHT]) {
569                                         BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT);
570                                         cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
571                                         BLI_assert(cd_vert_bweight_offset != -1);
572
573                                         scale_bv_weight = compute_scale_factor(ve_median[M_BV_WEIGHT], median[M_BV_WEIGHT]);
574                                 }
575
576                                 if (median[M_SKIN_X]) {
577                                         cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN);
578                                         BLI_assert(cd_vert_skin_offset != -1);
579
580                                         if (ve_median[M_SKIN_X] != median[M_SKIN_X]) {
581                                                 scale_skin_x = ve_median[M_SKIN_X] / (ve_median[M_SKIN_X] - median[M_SKIN_X]);
582                                         }
583                                 }
584                                 if (median[M_SKIN_Y]) {
585                                         if (cd_vert_skin_offset == -1) {
586                                                 cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN);
587                                                 BLI_assert(cd_vert_skin_offset != -1);
588                                         }
589
590                                         if (ve_median[M_SKIN_Y] != median[M_SKIN_Y]) {
591                                                 scale_skin_y = ve_median[M_SKIN_Y] / (ve_median[M_SKIN_Y] - median[M_SKIN_Y]);
592                                         }
593                                 }
594
595                                 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
596                                         if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
597                                                 if (apply_vcos) {
598                                                         apply_raw_diff_v3(eve->co, tot, &ve_median[LOC_X], &median[LOC_X]);
599                                                 }
600
601                                                 if (cd_vert_bweight_offset != -1) {
602                                                         float *bweight = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset);
603                                                         apply_scale_factor_clamp(bweight, tot, ve_median[M_BV_WEIGHT], scale_bv_weight);
604                                                 }
605
606                                                 if (cd_vert_skin_offset != -1) {
607                                                         MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset);
608
609                                                         /* That one is not clamped to [0.0, 1.0]. */
610                                                         if (median[M_SKIN_X] != 0.0f) {
611                                                                 apply_scale_factor(&vs->radius[0], tot, ve_median[M_SKIN_X], median[M_SKIN_X],
612                                                                                    scale_skin_x);
613                                                         }
614                                                         if (median[M_SKIN_Y] != 0.0f) {
615                                                                 apply_scale_factor(&vs->radius[1], tot, ve_median[M_SKIN_Y], median[M_SKIN_Y],
616                                                                                    scale_skin_y);
617                                                         }
618                                                 }
619                                         }
620                                 }
621                         }
622
623                         if (apply_vcos) {
624                                 EDBM_mesh_normals_update(em);
625                         }
626
627                         /* Edges */
628
629                         if (median[M_BE_WEIGHT] || median[M_CREASE]) {
630                                 if (median[M_BE_WEIGHT]) {
631                                         BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
632                                         cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
633                                         BLI_assert(cd_edge_bweight_offset != -1);
634
635                                         scale_be_weight = compute_scale_factor(ve_median[M_BE_WEIGHT], median[M_BE_WEIGHT]);
636                                 }
637
638                                 if (median[M_CREASE]) {
639                                         BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE);
640                                         cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
641                                         BLI_assert(cd_edge_crease_offset != -1);
642
643                                         scale_crease = compute_scale_factor(ve_median[M_CREASE], median[M_CREASE]);
644                                 }
645
646                                 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
647                                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
648                                                 if (median[M_BE_WEIGHT] != 0.0f) {
649                                                         float *bweight = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_bweight_offset);
650                                                         apply_scale_factor_clamp(bweight, tot, ve_median[M_BE_WEIGHT], scale_be_weight);
651                                                 }
652
653                                                 if (median[M_CREASE] != 0.0f) {
654                                                         float *crease = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_crease_offset);
655                                                         apply_scale_factor_clamp(crease, tot, ve_median[M_CREASE], scale_crease);
656                                                 }
657                                         }
658                                 }
659                         }
660                 }
661                 else if (ELEM(ob->type, OB_CURVE, OB_SURF) &&
662                          (apply_vcos || median[C_BWEIGHT] || median[C_WEIGHT] || median[C_RADIUS] || median[C_TILT]))
663                 {
664                         Curve *cu = ob->data;
665                         Nurb *nu;
666                         BPoint *bp;
667                         BezTriple *bezt;
668                         int a;
669                         ListBase *nurbs = BKE_curve_editNurbs_get(cu);
670                         const float scale_w = compute_scale_factor(ve_median[C_WEIGHT], median[C_WEIGHT]);
671
672                         nu = nurbs->first;
673                         while (nu) {
674                                 if (nu->type == CU_BEZIER) {
675                                         for (a = nu->pntsu, bezt = nu->bezt; a--; bezt++) {
676                                                 if (bezt->f2 & SELECT) {
677                                                         if (apply_vcos) {
678                                                                 /* Here we always have to use the diff... :/
679                                                                  * Cannot avoid some glitches when going e.g. from 3 to 0.0001 (see T37327),
680                                                                  * unless we use doubles.
681                                                                  */
682                                                                 add_v3_v3(bezt->vec[0], &median[LOC_X]);
683                                                                 add_v3_v3(bezt->vec[1], &median[LOC_X]);
684                                                                 add_v3_v3(bezt->vec[2], &median[LOC_X]);
685                                                         }
686                                                         if (median[C_WEIGHT]) {
687                                                                 apply_scale_factor_clamp(&bezt->weight, tot, ve_median[C_WEIGHT], scale_w);
688                                                         }
689                                                         if (median[C_RADIUS]) {
690                                                                 apply_raw_diff(&bezt->radius, tot, ve_median[C_RADIUS], median[C_RADIUS]);
691                                                         }
692                                                         if (median[C_TILT]) {
693                                                                 apply_raw_diff(&bezt->alfa, tot, ve_median[C_TILT], median[C_TILT]);
694                                                         }
695                                                 }
696                                                 else if (apply_vcos) {  /* Handles can only have their coordinates changed here. */
697                                                         if (bezt->f1 & SELECT) {
698                                                                 apply_raw_diff_v3(bezt->vec[0], tot, &ve_median[LOC_X], &median[LOC_X]);
699                                                         }
700                                                         if (bezt->f3 & SELECT) {
701                                                                 apply_raw_diff_v3(bezt->vec[2], tot, &ve_median[LOC_X], &median[LOC_X]);
702                                                         }
703                                                 }
704                                         }
705                                 }
706                                 else {
707                                         for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a--; bp++) {
708                                                 if (bp->f1 & SELECT) {
709                                                         if (apply_vcos) {
710                                                                 apply_raw_diff_v3(bp->vec, tot, &ve_median[LOC_X], &median[LOC_X]);
711                                                         }
712                                                         if (median[C_BWEIGHT]) {
713                                                                 apply_raw_diff(&bp->vec[3], tot, ve_median[C_BWEIGHT], median[C_BWEIGHT]);
714                                                         }
715                                                         if (median[C_WEIGHT]) {
716                                                                 apply_scale_factor_clamp(&bp->weight, tot, ve_median[C_WEIGHT], scale_w);
717                                                         }
718                                                         if (median[C_RADIUS]) {
719                                                                 apply_raw_diff(&bp->radius, tot, ve_median[C_RADIUS], median[C_RADIUS]);
720                                                         }
721                                                         if (median[C_TILT]) {
722                                                                 apply_raw_diff(&bp->alfa, tot, ve_median[C_TILT], median[C_TILT]);
723                                                         }
724                                                 }
725                                         }
726                                 }
727                                 BKE_nurb_test_2d(nu);
728                                 BKE_nurb_handles_test(nu, true); /* test for bezier too */
729
730                                 nu = nu->next;
731                         }
732                 }
733                 else if ((ob->type == OB_LATTICE) && (apply_vcos || median[L_WEIGHT])) {
734                         Lattice *lt = ob->data;
735                         BPoint *bp;
736                         int a;
737                         const float scale_w = compute_scale_factor(ve_median[L_WEIGHT], median[L_WEIGHT]);
738
739                         a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw;
740                         bp = lt->editlatt->latt->def;
741                         while (a--) {
742                                 if (bp->f1 & SELECT) {
743                                         if (apply_vcos) {
744                                                 apply_raw_diff_v3(bp->vec, tot, &ve_median[LOC_X], &median[LOC_X]);
745                                         }
746                                         if (median[L_WEIGHT]) {
747                                                 apply_scale_factor_clamp(&bp->weight, tot, ve_median[L_WEIGHT], scale_w);
748                                         }
749                                 }
750                                 bp++;
751                         }
752                 }
753
754 /*              ED_undo_push(C, "Transform properties"); */
755         }
756
757 /* Clean up! */
758 /* Location, common to all. */
759 #undef LOC_X
760 #undef LOC_Y
761 #undef LOC_Z
762 /* Meshes (and lattice)... */
763 #undef M_BV_WEIGHT
764 #undef M_SKIN_X
765 #undef M_SKIN_Y
766 #undef M_BE_WEIGHT
767 #undef M_CREASE
768 /* Curves... */
769 #undef C_BWEIGHT
770 #undef C_WEIGHT
771 #undef C_RADIUS
772 #undef C_TILT
773 /* Lattice... */
774 #undef L_WEIGHT
775 }
776 #undef NBR_TRANSFORM_PROPERTIES
777
778 static void v3d_object_dimension_buts(bContext *C, uiLayout *layout, View3D *v3d, Object *ob)
779 {
780         uiBlock *block = (layout) ? uiLayoutAbsoluteBlock(layout) : NULL;
781         TransformProperties *tfp = v3d_transform_props_ensure(v3d);
782
783         if (block) {
784                 BLI_assert(C == NULL);
785                 int yi = 200;
786                 const int butw = 200;
787                 const int buth = 20 * UI_DPI_FAC;
788
789                 BKE_object_dimensions_get(ob, tfp->ob_dims);
790                 copy_v3_v3(tfp->ob_dims_orig, tfp->ob_dims);
791
792                 uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("Dimensions:"), 0, yi -= buth, butw, buth, NULL, 0, 0, 0, 0, "");
793                 UI_block_align_begin(block);
794                 const float lim = 10000;
795                 for (int i = 0; i < 3; i++) {
796                         char text[3] = {'X' + i, ':', '\0'};
797                         uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANEL_DIMS, text, 0, yi -= buth, butw, buth,
798                                   &(tfp->ob_dims[i]), 0.0f, lim, 10, 3, "");
799                 }
800                 UI_block_align_end(block);
801         }
802         else {  /* apply */
803                 int axis_mask = 0;
804                 for (int i = 0; i < 3; i++) {
805                         if (tfp->ob_dims[i] == tfp->ob_dims_orig[i]) {
806                                 axis_mask |= (1 << i);
807                         }
808                 }
809                 BKE_object_dimensions_set(ob, tfp->ob_dims, axis_mask);
810
811                 PointerRNA obptr;
812                 RNA_id_pointer_create(&ob->id, &obptr);
813                 PropertyRNA *prop = RNA_struct_find_property(&obptr, "scale");
814                 RNA_property_update(C, &obptr, prop);
815         }
816 }
817
818 #define B_VGRP_PNL_EDIT_SINGLE 8       /* or greater */
819
820 static void do_view3d_vgroup_buttons(bContext *C, void *UNUSED(arg), int event)
821 {
822         if (event < B_VGRP_PNL_EDIT_SINGLE) {
823                 /* not for me */
824                 return;
825         }
826         else {
827                 ViewLayer *view_layer = CTX_data_view_layer(C);
828                 Object *ob = view_layer->basact->object;
829                 ED_vgroup_vert_active_mirror(ob, event - B_VGRP_PNL_EDIT_SINGLE);
830                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
831                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
832         }
833 }
834
835 static bool view3d_panel_vgroup_poll(const bContext *C, PanelType *UNUSED(pt))
836 {
837         ViewLayer *view_layer = CTX_data_view_layer(C);
838         Object *ob = OBACT(view_layer);
839         if (ob && (BKE_object_is_in_editmode_vgroup(ob) ||
840                    BKE_object_is_in_wpaint_select_vert(ob)))
841         {
842                 MDeformVert *dvert_act = ED_mesh_active_dvert_get_only(ob);
843                 if (dvert_act) {
844                         return (dvert_act->totweight != 0);
845                 }
846         }
847
848         return false;
849 }
850
851
852 static void view3d_panel_vgroup(const bContext *C, Panel *pa)
853 {
854         uiBlock *block = uiLayoutAbsoluteBlock(pa->layout);
855         Scene *scene = CTX_data_scene(C);
856         ViewLayer *view_layer = CTX_data_view_layer(C);
857         Object *ob = view_layer->basact->object;
858
859         MDeformVert *dv;
860
861         dv = ED_mesh_active_dvert_get_only(ob);
862
863         if (dv && dv->totweight) {
864                 ToolSettings *ts = scene->toolsettings;
865
866                 wmOperatorType *ot;
867                 PointerRNA op_ptr, tools_ptr;
868                 PointerRNA *but_ptr;
869
870                 uiLayout *col, *bcol;
871                 uiLayout *row;
872                 uiBut *but;
873                 bDeformGroup *dg;
874                 unsigned int i;
875                 int subset_count, vgroup_tot;
876                 const bool *vgroup_validmap;
877                 eVGroupSelect subset_type = ts->vgroupsubset;
878                 int yco = 0;
879                 int lock_count = 0;
880
881                 UI_block_func_handle_set(block, do_view3d_vgroup_buttons, NULL);
882
883                 bcol = uiLayoutColumn(pa->layout, true);
884                 row = uiLayoutRow(bcol, true); /* The filter button row */
885
886                 RNA_pointer_create(NULL, &RNA_ToolSettings, ts, &tools_ptr);
887                 uiItemR(row, &tools_ptr, "vertex_group_subset", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
888
889                 col = uiLayoutColumn(bcol, true);
890
891                 vgroup_validmap = BKE_object_defgroup_subset_from_select_type(ob, subset_type, &vgroup_tot, &subset_count);
892                 for (i = 0, dg = ob->defbase.first; dg; i++, dg = dg->next) {
893                         bool locked = (dg->flag & DG_LOCK_WEIGHT) != 0;
894                         if (vgroup_validmap[i]) {
895                                 MDeformWeight *dw = defvert_find_index(dv, i);
896                                 if (dw) {
897                                         int x, xco = 0;
898                                         int icon;
899                                         uiLayout *split = uiLayoutSplit(col, 0.45, true);
900                                         row = uiLayoutRow(split, true);
901
902                                         /* The Weight Group Name */
903
904                                         ot = WM_operatortype_find("OBJECT_OT_vertex_weight_set_active", true);
905                                         but = uiDefButO_ptr(block, UI_BTYPE_BUT, ot, WM_OP_EXEC_DEFAULT, dg->name,
906                                                             xco, yco, (x = UI_UNIT_X * 5), UI_UNIT_Y, "");
907                                         but_ptr = UI_but_operator_ptr_get(but);
908                                         RNA_int_set(but_ptr, "weight_group", i);
909                                         UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
910                                         if (ob->actdef != i + 1) {
911                                                 UI_but_flag_enable(but, UI_BUT_INACTIVE);
912                                         }
913                                         xco += x;
914
915                                         row = uiLayoutRow(split, true);
916                                         uiLayoutSetEnabled(row, !locked);
917
918                                         /* The weight group value */
919                                         /* To be reworked still */
920                                         but = uiDefButF(block, UI_BTYPE_NUM, B_VGRP_PNL_EDIT_SINGLE + i, "",
921                                                         xco, yco, (x = UI_UNIT_X * 4), UI_UNIT_Y,
922                                                         &dw->weight, 0.0, 1.0, 1, 3, "");
923                                         UI_but_drawflag_enable(but, UI_BUT_TEXT_LEFT);
924                                         if (locked) {
925                                                 lock_count++;
926                                         }
927                                         xco += x;
928
929                                         /* The weight group paste function */
930                                         icon = (locked) ? ICON_BLANK1 : ICON_PASTEDOWN;
931                                         uiItemFullO(row, "OBJECT_OT_vertex_weight_paste", "", icon, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
932                                         RNA_int_set(&op_ptr, "weight_group", i);
933
934                                         /* The weight entry delete function */
935                                         icon = (locked) ? ICON_LOCKED : ICON_X;
936                                         uiItemFullO(row, "OBJECT_OT_vertex_weight_delete", "", icon, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
937                                         RNA_int_set(&op_ptr, "weight_group", i);
938
939                                         yco -= UI_UNIT_Y;
940                                 }
941                         }
942                 }
943                 MEM_freeN((void *)vgroup_validmap);
944
945                 yco -= 2;
946
947                 col = uiLayoutColumn(pa->layout, true);
948                 row = uiLayoutRow(col, true);
949
950                 ot = WM_operatortype_find("OBJECT_OT_vertex_weight_normalize_active_vertex", 1);
951                 but = uiDefButO_ptr(block, UI_BTYPE_BUT, ot, WM_OP_EXEC_DEFAULT, "Normalize",
952                                     0, yco, UI_UNIT_X * 5, UI_UNIT_Y,
953                                     TIP_("Normalize weights of active vertex (if affected groups are unlocked)"));
954                 if (lock_count) {
955                         UI_but_flag_enable(but, UI_BUT_DISABLED);
956                 }
957
958                 ot = WM_operatortype_find("OBJECT_OT_vertex_weight_copy", 1);
959                 but = uiDefButO_ptr(block, UI_BTYPE_BUT, ot, WM_OP_EXEC_DEFAULT, "Copy",
960                                     UI_UNIT_X * 5, yco, UI_UNIT_X * 5, UI_UNIT_Y,
961                                     TIP_("Copy active vertex to other selected vertices (if affected groups are unlocked)"));
962                 if (lock_count) {
963                         UI_but_flag_enable(but, UI_BUT_DISABLED);
964                 }
965
966         }
967 }
968
969 static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr)
970 {
971         uiLayout *split, *colsub;
972
973         split = uiLayoutSplit(layout, 0.8f, false);
974
975         if (ptr->type == &RNA_PoseBone) {
976                 PointerRNA boneptr;
977                 Bone *bone;
978
979                 boneptr = RNA_pointer_get(ptr, "bone");
980                 bone = boneptr.data;
981                 uiLayoutSetActive(split, !(bone->parent && bone->flag & BONE_CONNECTED));
982         }
983         colsub = uiLayoutColumn(split, true);
984         uiItemR(colsub, ptr, "location", 0, NULL, ICON_NONE);
985         colsub = uiLayoutColumn(split, true);
986         uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
987         uiItemL(colsub, "", ICON_NONE);
988         uiItemR(colsub, ptr, "lock_location", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", ICON_DECORATE_UNLOCKED);
989
990         split = uiLayoutSplit(layout, 0.8f, false);
991
992         switch (RNA_enum_get(ptr, "rotation_mode")) {
993                 case ROT_MODE_QUAT: /* quaternion */
994                         colsub = uiLayoutColumn(split, true);
995                         uiItemR(colsub, ptr, "rotation_quaternion", 0, IFACE_("Rotation"), ICON_NONE);
996                         colsub = uiLayoutColumn(split, true);
997                         uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
998                         uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE);
999                         if (RNA_boolean_get(ptr, "lock_rotations_4d"))
1000                                 uiItemR(colsub, ptr, "lock_rotation_w", UI_ITEM_R_TOGGLE + UI_ITEM_R_ICON_ONLY, "", ICON_DECORATE_UNLOCKED);
1001                         else
1002                                 uiItemL(colsub, "", ICON_NONE);
1003                         uiItemR(colsub, ptr, "lock_rotation", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", ICON_DECORATE_UNLOCKED);
1004                         break;
1005                 case ROT_MODE_AXISANGLE: /* axis angle */
1006                         colsub = uiLayoutColumn(split, true);
1007                         uiItemR(colsub, ptr, "rotation_axis_angle", 0, IFACE_("Rotation"), ICON_NONE);
1008                         colsub = uiLayoutColumn(split, true);
1009                         uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1010                         uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE);
1011                         if (RNA_boolean_get(ptr, "lock_rotations_4d"))
1012                                 uiItemR(colsub, ptr, "lock_rotation_w", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", ICON_DECORATE_UNLOCKED);
1013                         else
1014                                 uiItemL(colsub, "", ICON_NONE);
1015                         uiItemR(colsub, ptr, "lock_rotation", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", ICON_DECORATE_UNLOCKED);
1016                         break;
1017                 default: /* euler rotations */
1018                         colsub = uiLayoutColumn(split, true);
1019                         uiItemR(colsub, ptr, "rotation_euler", 0, IFACE_("Rotation"), ICON_NONE);
1020                         colsub = uiLayoutColumn(split, true);
1021                         uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1022                         uiItemL(colsub, "", ICON_NONE);
1023                         uiItemR(colsub, ptr, "lock_rotation", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", ICON_DECORATE_UNLOCKED);
1024                         break;
1025         }
1026         uiItemR(layout, ptr, "rotation_mode", 0, "", ICON_NONE);
1027
1028         split = uiLayoutSplit(layout, 0.8f, false);
1029         colsub = uiLayoutColumn(split, true);
1030         uiItemR(colsub, ptr, "scale", 0, NULL, ICON_NONE);
1031         colsub = uiLayoutColumn(split, true);
1032         uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
1033         uiItemL(colsub, "", ICON_NONE);
1034         uiItemR(colsub, ptr, "lock_scale", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "", ICON_DECORATE_UNLOCKED);
1035 }
1036
1037 static void v3d_posearmature_buts(uiLayout *layout, Object *ob)
1038 {
1039         bPoseChannel *pchan;
1040         PointerRNA pchanptr;
1041         uiLayout *col;
1042
1043         pchan = BKE_pose_channel_active(ob);
1044
1045         if (!pchan) {
1046                 uiItemL(layout, IFACE_("No Bone Active"), ICON_NONE);
1047                 return;
1048         }
1049
1050         RNA_pointer_create(&ob->id, &RNA_PoseBone, pchan, &pchanptr);
1051
1052         col = uiLayoutColumn(layout, false);
1053
1054         /* XXX: RNA buts show data in native types (i.e. quats, 4-component axis/angle, etc.)
1055          * but old-school UI shows in eulers always. Do we want to be able to still display in Eulers?
1056          * Maybe needs RNA/ui options to display rotations as different types... */
1057         v3d_transform_butsR(col, &pchanptr);
1058 }
1059
1060 static void v3d_editarmature_buts(uiLayout *layout, Object *ob)
1061 {
1062         bArmature *arm = ob->data;
1063         EditBone *ebone;
1064         uiLayout *col;
1065         PointerRNA eboneptr;
1066
1067         ebone = arm->act_edbone;
1068
1069         if (!ebone || (ebone->layer & arm->layer) == 0) {
1070                 uiItemL(layout, IFACE_("Nothing selected"), ICON_NONE);
1071                 return;
1072         }
1073
1074         RNA_pointer_create(&arm->id, &RNA_EditBone, ebone, &eboneptr);
1075
1076         col = uiLayoutColumn(layout, false);
1077         uiItemR(col, &eboneptr, "head", 0, NULL, ICON_NONE);
1078         if (ebone->parent && ebone->flag & BONE_CONNECTED) {
1079                 PointerRNA parptr = RNA_pointer_get(&eboneptr, "parent");
1080                 uiItemR(col, &parptr, "tail_radius", 0, IFACE_("Radius (Parent)"), ICON_NONE);
1081         }
1082         else {
1083                 uiItemR(col, &eboneptr, "head_radius", 0, IFACE_("Radius"), ICON_NONE);
1084         }
1085
1086         uiItemR(col, &eboneptr, "tail", 0, NULL, ICON_NONE);
1087         uiItemR(col, &eboneptr, "tail_radius", 0, IFACE_("Radius"), ICON_NONE);
1088
1089         uiItemR(col, &eboneptr, "roll", 0, NULL, ICON_NONE);
1090         uiItemR(col, &eboneptr, "envelope_distance", 0, IFACE_("Envelope"), ICON_NONE);
1091 }
1092
1093 static void v3d_editmetaball_buts(uiLayout *layout, Object *ob)
1094 {
1095         PointerRNA mbptr, ptr;
1096         MetaBall *mball = ob->data;
1097         uiLayout *col;
1098
1099         if (!mball || !(mball->lastelem))
1100                 return;
1101
1102         RNA_pointer_create(&mball->id, &RNA_MetaBall, mball, &mbptr);
1103
1104         RNA_pointer_create(&mball->id, &RNA_MetaElement, mball->lastelem, &ptr);
1105
1106         col = uiLayoutColumn(layout, false);
1107         uiItemR(col, &ptr, "co", 0, NULL, ICON_NONE);
1108
1109         uiItemR(col, &ptr, "radius", 0, NULL, ICON_NONE);
1110         uiItemR(col, &ptr, "stiffness", 0, NULL, ICON_NONE);
1111
1112         uiItemR(col, &ptr, "type", 0, NULL, ICON_NONE);
1113
1114         col = uiLayoutColumn(layout, true);
1115         switch (RNA_enum_get(&ptr, "type")) {
1116                 case MB_BALL:
1117                         break;
1118                 case MB_CUBE:
1119                         uiItemL(col, IFACE_("Size:"), ICON_NONE);
1120                         uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1121                         uiItemR(col, &ptr, "size_y", 0, "Y", ICON_NONE);
1122                         uiItemR(col, &ptr, "size_z", 0, "Z", ICON_NONE);
1123                         break;
1124                 case MB_TUBE:
1125                         uiItemL(col, IFACE_("Size:"), ICON_NONE);
1126                         uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1127                         break;
1128                 case MB_PLANE:
1129                         uiItemL(col, IFACE_("Size:"), ICON_NONE);
1130                         uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1131                         uiItemR(col, &ptr, "size_y", 0, "Y", ICON_NONE);
1132                         break;
1133                 case MB_ELIPSOID:
1134                         uiItemL(col, IFACE_("Size:"), ICON_NONE);
1135                         uiItemR(col, &ptr, "size_x", 0, "X", ICON_NONE);
1136                         uiItemR(col, &ptr, "size_y", 0, "Y", ICON_NONE);
1137                         uiItemR(col, &ptr, "size_z", 0, "Z", ICON_NONE);
1138                         break;
1139         }
1140 }
1141
1142 static void do_view3d_region_buttons(bContext *C, void *UNUSED(index), int event)
1143 {
1144         ViewLayer *view_layer = CTX_data_view_layer(C);
1145         View3D *v3d = CTX_wm_view3d(C);
1146         Object *ob = OBACT(view_layer);
1147
1148         switch (event) {
1149
1150                 case B_REDR:
1151                         ED_area_tag_redraw(CTX_wm_area(C));
1152                         return; /* no notifier! */
1153
1154                 case B_OBJECTPANELMEDIAN:
1155                         if (ob) {
1156                                 v3d_editvertex_buts(NULL, v3d, ob, 1.0);
1157                                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1158                         }
1159                         break;
1160                 case B_OBJECTPANEL_DIMS:
1161                         if (ob) {
1162                                 v3d_object_dimension_buts(C, NULL, v3d, ob);
1163                         }
1164                         break;
1165         }
1166
1167         /* default for now */
1168         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
1169 }
1170
1171 static bool view3d_panel_transform_poll(const bContext *C, PanelType *UNUSED(pt))
1172 {
1173         ViewLayer *view_layer = CTX_data_view_layer(C);
1174         return (view_layer->basact != NULL);
1175 }
1176
1177 static void view3d_panel_transform(const bContext *C, Panel *pa)
1178 {
1179         uiBlock *block;
1180         Scene *scene = CTX_data_scene(C);
1181         ViewLayer *view_layer = CTX_data_view_layer(C);
1182         Object *obedit = CTX_data_edit_object(C);
1183         Object *ob = view_layer->basact->object;
1184         uiLayout *col;
1185
1186         block = uiLayoutGetBlock(pa->layout);
1187         UI_block_func_handle_set(block, do_view3d_region_buttons, NULL);
1188
1189         col = uiLayoutColumn(pa->layout, false);
1190
1191         if (ob == obedit) {
1192                 if (ob->type == OB_ARMATURE) {
1193                         v3d_editarmature_buts(col, ob);
1194                 }
1195                 else if (ob->type == OB_MBALL) {
1196                         v3d_editmetaball_buts(col, ob);
1197                 }
1198                 else {
1199                         View3D *v3d = CTX_wm_view3d(C);
1200                         const float lim = 10000.0f * max_ff(1.0f, ED_view3d_grid_scale(scene, v3d, NULL));
1201                         v3d_editvertex_buts(col, v3d, ob, lim);
1202                 }
1203         }
1204         else if (ob->mode & OB_MODE_POSE) {
1205                 v3d_posearmature_buts(col, ob);
1206         }
1207         else {
1208                 PointerRNA obptr;
1209
1210                 RNA_id_pointer_create(&ob->id, &obptr);
1211                 v3d_transform_butsR(col, &obptr);
1212
1213                 /* dimensions and editmode just happen to be the same checks */
1214                 if (OB_TYPE_SUPPORT_EDITMODE(ob->type)) {
1215                         View3D *v3d = CTX_wm_view3d(C);
1216                         v3d_object_dimension_buts(NULL, col, v3d, ob);
1217                 }
1218         }
1219 }
1220
1221 static void hide_collections_menu_draw(const bContext *C, Menu *menu)
1222 {
1223         ED_collection_hide_menu_draw(C, menu->layout);
1224 }
1225
1226 void view3d_buttons_register(ARegionType *art)
1227 {
1228         PanelType *pt;
1229
1230         pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel object");
1231         strcpy(pt->idname, "VIEW3D_PT_transform");
1232         strcpy(pt->label, N_("Transform"));  /* XXX C panels not  available through RNA (bpy.types)! */
1233         strcpy(pt->category, "View");
1234         strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
1235         pt->draw = view3d_panel_transform;
1236         pt->poll = view3d_panel_transform_poll;
1237         BLI_addtail(&art->paneltypes, pt);
1238
1239         pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel vgroup");
1240         strcpy(pt->idname, "VIEW3D_PT_vgroup");
1241         strcpy(pt->label, N_("Vertex Weights"));  /* XXX C panels are not available through RNA (bpy.types)! */
1242         strcpy(pt->category, "View");
1243         strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
1244         pt->draw = view3d_panel_vgroup;
1245         pt->poll = view3d_panel_vgroup_poll;
1246         BLI_addtail(&art->paneltypes, pt);
1247
1248         MenuType *mt;
1249
1250         mt = MEM_callocN(sizeof(MenuType), "spacetype view3d menu collections");
1251         strcpy(mt->idname, "VIEW3D_MT_collection");
1252         strcpy(mt->label, N_("Collection"));
1253         strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
1254         mt->draw = hide_collections_menu_draw;
1255         WM_menutype_add(mt);
1256 }
1257
1258 static int view3d_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1259 {
1260         ScrArea *sa = CTX_wm_area(C);
1261         ARegion *ar = view3d_has_buttons_region(sa);
1262
1263         if (ar)
1264                 ED_region_toggle_hidden(C, ar);
1265
1266         return OPERATOR_FINISHED;
1267 }
1268
1269 void VIEW3D_OT_properties(wmOperatorType *ot)
1270 {
1271         ot->name = "Toggle Sidebar";
1272         ot->description = "Toggle the properties region visibility";
1273         ot->idname = "VIEW3D_OT_properties";
1274
1275         ot->exec = view3d_properties_toggle_exec;
1276         ot->poll = ED_operator_view3d_active;
1277
1278         /* flags */
1279         ot->flag = 0;
1280 }
1281
1282 static int view3d_object_mode_menu(bContext *C, wmOperator *op)
1283 {
1284         Object *ob = CTX_data_active_object(C);
1285         if (ob == NULL) {
1286                 BKE_report(op->reports, RPT_WARNING, "No active object found");
1287                 return OPERATOR_CANCELLED;
1288         }
1289         else if (((ob->mode & OB_MODE_EDIT) == 0) && (ELEM(ob->type, OB_ARMATURE))) {
1290                 ED_object_mode_toggle(C, OB_MODE_POSE);
1291                 return OPERATOR_CANCELLED;
1292         }
1293         else {
1294                 UI_pie_menu_invoke(C, "VIEW3D_MT_object_mode_pie", CTX_wm_window(C)->eventstate);
1295                 return OPERATOR_CANCELLED;
1296         }
1297 }
1298
1299 void VIEW3D_OT_object_mode_pie_or_toggle(wmOperatorType *ot)
1300 {
1301         ot->name = "Object Mode Menu";
1302         ot->idname = "VIEW3D_OT_object_mode_pie_or_toggle";
1303
1304         ot->exec = view3d_object_mode_menu;
1305         ot->poll = ED_operator_view3d_active;
1306
1307         /* flags */
1308         ot->flag = 0;
1309 }