f2ac54bf0f43b8824773dd30e2b08ab736a8a9fc
[blender.git] / source / blender / editors / mesh / editmesh_bevel.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  * Contributor(s): Joseph Eagar, Howard Trickey, Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/mesh/editmesh_bevel.c
24  *  \ingroup edmesh
25  */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_object_types.h"
30
31 #include "BLI_string.h"
32 #include "BLI_math.h"
33 #include "BLI_linklist_stack.h"
34
35 #include "BLT_translation.h"
36
37 #include "BKE_context.h"
38 #include "BKE_global.h"
39 #include "BKE_editmesh.h"
40 #include "BKE_unit.h"
41 #include "BKE_layer.h"
42 #include "BKE_mesh.h"
43
44 #include "DNA_mesh_types.h"
45
46 #include "RNA_define.h"
47 #include "RNA_access.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51
52 #include "UI_interface.h"
53
54 #include "ED_mesh.h"
55 #include "ED_numinput.h"
56 #include "ED_screen.h"
57 #include "ED_space_api.h"
58 #include "ED_transform.h"
59 #include "ED_view3d.h"
60
61 #include "mesh_intern.h"  /* own include */
62
63
64 #define MVAL_PIXEL_MARGIN  5.0f
65
66 #define PROFILE_HARD_MIN 0.0f
67
68 #define SEGMENTS_HARD_MAX 1000
69
70 /* which value is mouse movement and numeric input controlling? */
71 #define OFFSET_VALUE 0
72 #define OFFSET_VALUE_PERCENT 1
73 #define PROFILE_VALUE 2
74 #define SEGMENTS_VALUE 3
75 #define NUM_VALUE_KINDS 4
76
77 static const char *value_rna_name[NUM_VALUE_KINDS] = {"offset", "offset", "profile", "segments"};
78 static const float value_clamp_min[NUM_VALUE_KINDS] = {0.0f, 0.0f, PROFILE_HARD_MIN, 1.0f};
79 static const float value_clamp_max[NUM_VALUE_KINDS] = {1e6, 100.0f, 1.0f, SEGMENTS_HARD_MAX};
80 static const float value_start[NUM_VALUE_KINDS] = {0.0f, 0.0f, 0.5f, 1.0f};
81 static const float value_scale_per_inch[NUM_VALUE_KINDS] = { 0.0f, 100.0f, 1.0f, 4.0f};
82
83 typedef struct {
84         BMEditMesh *em;
85         BMBackup mesh_backup;
86 } BevelObjectStore;
87
88
89 typedef struct {
90         float initial_length[NUM_VALUE_KINDS];
91         float scale[NUM_VALUE_KINDS];
92         NumInput num_input[NUM_VALUE_KINDS];
93         /** The current value when shift is pressed. Negative when shift not active. */
94         float shift_value[NUM_VALUE_KINDS];
95         bool is_modal;
96
97         BevelObjectStore *ob_store;
98         uint ob_store_len;
99
100         /* modal only */
101         float mcenter[2];
102         void *draw_handle_pixel;
103         short gizmo_flag;
104         short value_mode;  /* Which value does mouse movement and numeric input affect? */
105         float segments;     /* Segments as float so smooth mouse pan works in small increments */
106 } BevelData;
107
108 static void edbm_bevel_update_header(bContext *C, wmOperator *op)
109 {
110         const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Mode: %s (M), Clamp Overlap: %s (C), "
111                                  "Vertex Only: %s (V), Profile Control: %s (P), Offset: %s, Segments: %d, Profile: %.3f");
112
113         char msg[UI_MAX_DRAW_STR];
114         ScrArea *sa = CTX_wm_area(C);
115         Scene *sce = CTX_data_scene(C);
116
117         if (sa) {
118                 BevelData *opdata = op->customdata;
119                 char offset_str[NUM_STR_REP_LEN];
120                 const char *type_str;
121                 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type");
122
123                 if (hasNumInput(&opdata->num_input[OFFSET_VALUE])) {
124                         outputNumInput(&opdata->num_input[OFFSET_VALUE], offset_str, &sce->unit);
125                 }
126                 else {
127                         BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset"));
128                 }
129
130                 RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &type_str);
131
132                 BLI_snprintf(msg, sizeof(msg), str, type_str,
133                              WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")),
134                              WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")),
135                              WM_bool_as_string(opdata->value_mode == PROFILE_VALUE),
136                              offset_str, RNA_int_get(op->ptr, "segments"), RNA_float_get(op->ptr, "profile"));
137
138                 ED_area_status_text(sa, msg);
139         }
140 }
141
142 static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
143 {
144         Scene *scene = CTX_data_scene(C);
145         BevelData *opdata;
146         ViewLayer *view_layer = CTX_data_view_layer(C);
147         float pixels_per_inch;
148         int i;
149
150         if (is_modal) {
151                 RNA_float_set(op->ptr, "offset", 0.0f);
152         }
153
154         op->customdata = opdata = MEM_mallocN(sizeof(BevelData), "beveldata_mesh_operator");
155         uint objects_used_len = 0;
156
157         {
158                 uint ob_store_len = 0;
159                 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &ob_store_len);
160                 opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
161                 for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
162                         Object *obedit = objects[ob_index];
163                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
164                         if (em->bm->totvertsel > 0) {
165                                 opdata->ob_store[objects_used_len].em = em;
166                                 objects_used_len++;
167                         }
168                 }
169                 MEM_freeN(objects);
170                 opdata->ob_store_len = objects_used_len;
171         }
172
173         opdata->is_modal = is_modal;
174         opdata->value_mode = OFFSET_VALUE;
175         opdata->segments = (float) RNA_int_get(op->ptr, "segments");
176         pixels_per_inch = U.dpi * U.pixelsize;
177
178         for (i = 0; i < NUM_VALUE_KINDS; i++) {
179                 opdata->shift_value[i] = -1.0f;
180                 opdata->initial_length[i] = -1.0f;
181                 /* note: scale for OFFSET_VALUE will get overwritten in edbm_bevel_invoke */
182                 opdata->scale[i] = value_scale_per_inch[i] / pixels_per_inch;
183
184                 initNumInput(&opdata->num_input[i]);
185                 opdata->num_input[i].idx_max = 0;
186                 opdata->num_input[i].val_flag[0] |= NUM_NO_NEGATIVE;
187                 if (i == SEGMENTS_VALUE) {
188                         opdata->num_input[i].val_flag[0] |= NUM_NO_FRACTION | NUM_NO_ZERO;
189                 }
190                 if (i == OFFSET_VALUE) {
191                         opdata->num_input[i].unit_sys = scene->unit.system;
192                 }
193                 /* Not sure this is a factor or a unit? */
194                 opdata->num_input[i].unit_type[0] = B_UNIT_NONE;
195         }
196
197         /* avoid the cost of allocating a bm copy */
198         if (is_modal) {
199                 View3D *v3d = CTX_wm_view3d(C);
200                 ARegion *ar = CTX_wm_region(C);
201
202                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
203                         opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(opdata->ob_store[ob_index].em);
204                 }
205                 opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb,
206                         opdata->mcenter, REGION_DRAW_POST_PIXEL);
207                 G.moving = G_TRANSFORM_EDIT;
208
209                 if (v3d) {
210                         opdata->gizmo_flag = v3d->gizmo_flag;
211                         v3d->gizmo_flag = V3D_GIZMO_HIDE;
212                 }
213         }
214
215         return true;
216 }
217
218 static bool edbm_bevel_calc(wmOperator *op)
219 {
220         BevelData *opdata = op->customdata;
221         BMEditMesh *em;
222         BMOperator bmop;
223         bool changed = false;
224
225         const float offset = RNA_float_get(op->ptr, "offset");
226         const int offset_type = RNA_enum_get(op->ptr, "offset_type");
227         const int segments = RNA_int_get(op->ptr, "segments");
228         const float profile = RNA_float_get(op->ptr, "profile");
229         const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
230         const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
231         int material = RNA_int_get(op->ptr, "material");
232         const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide");
233         const bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
234         const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
235         const bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
236         const int face_strength_mode = RNA_enum_get(op->ptr, "face_strength_mode");
237
238
239         for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
240                 em = opdata->ob_store[ob_index].em;
241
242                 /* revert to original mesh */
243                 if (opdata->is_modal) {
244                         EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false);
245                 }
246
247                 if (em->ob) {
248                         material = CLAMPIS(material, -1, em->ob->totcol - 1);
249                 }
250
251                 Mesh *me = em->ob->data;
252
253                 if (harden_normals && !(me->flag & ME_AUTOSMOOTH)) {
254                         /* harden_normals only has a visible effect if autosmooth is on, so turn it on */
255                         me->flag |= ME_AUTOSMOOTH;
256                 }
257
258                 EDBM_op_init(
259                         em, &bmop, op,
260                         "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f "
261                         "clamp_overlap=%b material=%i loop_slide=%b mark_seam=%b mark_sharp=%b "
262                         "harden_normals=%b face_strength_mode=%i smoothresh=%f",
263                         BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile,
264                         clamp_overlap, material, loop_slide, mark_seam, mark_sharp, harden_normals, face_strength_mode,
265                         me->smoothresh);
266
267                 BMO_op_exec(em->bm, &bmop);
268
269                 if (offset != 0.0f) {
270                         /* not essential, but we may have some loose geometry that
271                          * won't get bevel'd and better not leave it selected */
272                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
273                         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
274                 }
275
276                 /* no need to de-select existing geometry */
277                 if (!EDBM_op_finish(em, &bmop, op, true)) {
278                         continue;
279                 }
280
281                 EDBM_mesh_normals_update(em);
282
283                 EDBM_update_generic(em, true, true);
284                 changed = true;
285         }
286         return changed;
287 }
288
289 static void edbm_bevel_exit(bContext *C, wmOperator *op)
290 {
291         BevelData *opdata = op->customdata;
292
293         ScrArea *sa = CTX_wm_area(C);
294
295         if (sa) {
296                 ED_area_status_text(sa, NULL);
297         }
298
299         if (opdata->is_modal) {
300                 View3D *v3d = CTX_wm_view3d(C);
301                 ARegion *ar = CTX_wm_region(C);
302                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
303                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, NULL, false);
304                 }
305                 ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel);
306                 if (v3d) {
307                         v3d->gizmo_flag = opdata->gizmo_flag;
308                 }
309                 G.moving = 0;
310         }
311         MEM_SAFE_FREE(opdata->ob_store);
312         MEM_SAFE_FREE(op->customdata);
313         op->customdata = NULL;
314 }
315
316 static void edbm_bevel_cancel(bContext *C, wmOperator *op)
317 {
318         BevelData *opdata = op->customdata;
319         if (opdata->is_modal) {
320                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
321                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true);
322                         EDBM_update_generic(opdata->ob_store[ob_index].em, false, true);
323                 }
324         }
325
326         edbm_bevel_exit(C, op);
327
328         /* need to force redisplay or we may still view the modified result */
329         ED_region_tag_redraw(CTX_wm_region(C));
330 }
331
332 /* bevel! yay!!*/
333 static int edbm_bevel_exec(bContext *C, wmOperator *op)
334 {
335         if (!edbm_bevel_init(C, op, false)) {
336                 return OPERATOR_CANCELLED;
337         }
338
339         if (!edbm_bevel_calc(op)) {
340                 edbm_bevel_cancel(C, op);
341                 return OPERATOR_CANCELLED;
342         }
343
344         edbm_bevel_exit(C, op);
345
346         return OPERATOR_FINISHED;
347 }
348
349 static void edbm_bevel_calc_initial_length(wmOperator *op, const wmEvent *event, bool mode_changed)
350 {
351         BevelData *opdata;
352         float mlen[2], len, value, sc, st;
353         int vmode;
354
355         opdata = op->customdata;
356         mlen[0] = opdata->mcenter[0] - event->mval[0];
357         mlen[1] = opdata->mcenter[1] - event->mval[1];
358         len = len_v2(mlen);
359         vmode = opdata->value_mode;
360         if (mode_changed || opdata->initial_length[vmode] == -1.0f) {
361                 /* If current value is not default start value, adjust len so that
362                  * the scaling and offset in edbm_bevel_mouse_set_value will
363                  * start at current value */
364                 value = (vmode == SEGMENTS_VALUE) ?
365                         opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
366                 sc = opdata->scale[vmode];
367                 st = value_start[vmode];
368                 if (value != value_start[vmode]) {
369                         len = (st + sc * (len - MVAL_PIXEL_MARGIN) - value) / sc;
370                 }
371         }
372         opdata->initial_length[opdata->value_mode] = len;
373 }
374
375 static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
376 {
377         /* TODO make modal keymap (see fly mode) */
378         RegionView3D *rv3d = CTX_wm_region_view3d(C);
379         BevelData *opdata;
380         float center_3d[3];
381
382         if (!edbm_bevel_init(C, op, true)) {
383                 return OPERATOR_CANCELLED;
384         }
385
386         opdata = op->customdata;
387
388         /* initialize mouse values */
389         if (!calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, center_3d, opdata->mcenter)) {
390                 /* in this case the tool will likely do nothing,
391                  * ideally this will never happen and should be checked for above */
392                 opdata->mcenter[0] = opdata->mcenter[1] = 0;
393         }
394
395         /* for OFFSET_VALUE only, the scale is the size of a pixel under the mouse in 3d space */
396         opdata->scale[OFFSET_VALUE] = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f;
397
398         edbm_bevel_calc_initial_length(op, event, false);
399
400         edbm_bevel_update_header(C, op);
401
402         if (!edbm_bevel_calc(op)) {
403                 edbm_bevel_cancel(C, op);
404                 return OPERATOR_CANCELLED;
405         }
406
407         WM_event_add_modal_handler(C, op);
408
409         return OPERATOR_RUNNING_MODAL;
410 }
411
412 static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event)
413 {
414         BevelData *opdata = op->customdata;
415         int vmode = opdata->value_mode;
416         float mdiff[2];
417         float value;
418
419         mdiff[0] = opdata->mcenter[0] - event->mval[0];
420         mdiff[1] = opdata->mcenter[1] - event->mval[1];
421
422         value = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length[vmode]);
423
424         /* Scale according to value mode */
425         value = value_start[vmode] + value * opdata->scale[vmode];
426
427         /* Fake shift-transform... */
428         if (event->shift) {
429                 if (opdata->shift_value[vmode] < 0.0f) {
430                         opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ?
431                                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
432                 }
433                 value = (value - opdata->shift_value[vmode]) * 0.1f + opdata->shift_value[vmode];
434         }
435         else if (opdata->shift_value[vmode] >= 0.0f) {
436                 opdata->shift_value[vmode] = -1.0f;
437         }
438
439         /* clamp accordingto value mode, and store value back */
440         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
441         if (vmode == SEGMENTS_VALUE) {
442                 opdata->segments = value;
443                 RNA_int_set(op->ptr, "segments", (int)(value + 0.5f));
444         }
445         else {
446                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
447         }
448 }
449
450 static void edbm_bevel_numinput_set_value(wmOperator *op)
451 {
452         BevelData *opdata = op->customdata;
453         float value;
454         int vmode;
455
456         vmode = opdata->value_mode;
457         value = (vmode == SEGMENTS_VALUE) ?
458                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
459         applyNumInput(&opdata->num_input[vmode], &value);
460         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
461         if (vmode == SEGMENTS_VALUE) {
462                 opdata->segments = value;
463                 RNA_int_set(op->ptr, "segments", (int)value);
464         }
465         else {
466                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
467         }
468 }
469
470 static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
471 {
472         BevelData *opdata = op->customdata;
473         const bool has_numinput = hasNumInput(&opdata->num_input[opdata->value_mode]);
474
475         /* Modal numinput active, try to handle numeric inputs first... */
476         if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
477                 edbm_bevel_numinput_set_value(op);
478                 edbm_bevel_calc(op);
479                 edbm_bevel_update_header(C, op);
480                 return OPERATOR_RUNNING_MODAL;
481         }
482         else {
483                 bool handled = false;
484                 switch (event->type) {
485                         case ESCKEY:
486                         case RIGHTMOUSE:
487                                 edbm_bevel_cancel(C, op);
488                                 return OPERATOR_CANCELLED;
489
490                         case MOUSEMOVE:
491                                 if (!has_numinput) {
492                                         edbm_bevel_mouse_set_value(op, event);
493                                         edbm_bevel_calc(op);
494                                         edbm_bevel_update_header(C, op);
495                                         handled = true;
496                                 }
497                                 break;
498
499                         case LEFTMOUSE:
500                         case PADENTER:
501                         case RETKEY:
502                                 if ((event->val == KM_PRESS) ||
503                                     ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm")))
504                                 {
505                                         edbm_bevel_calc(op);
506                                         edbm_bevel_exit(C, op);
507                                         return OPERATOR_FINISHED;
508                                 }
509                                 break;
510
511                         case MOUSEPAN: {
512                                 float delta = 0.02f * (event->y - event->prevy);
513                                 if (opdata->segments >= 1 && opdata->segments + delta < 1)
514                                         opdata->segments = 1;
515                                 else
516                                         opdata->segments += delta;
517                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
518                                 edbm_bevel_calc(op);
519                                 edbm_bevel_update_header(C, op);
520                                 handled = true;
521                                 break;
522                         }
523
524                         /* Note this will prevent padplus and padminus to ever activate modal numinput.
525                          * This is not really an issue though, as we only expect positive values here...
526                          * Else we could force them to only modify segments number when shift is pressed, or so.
527                          */
528
529                         case WHEELUPMOUSE:  /* change number of segments */
530                         case PADPLUSKEY:
531                                 if (event->val == KM_RELEASE)
532                                         break;
533
534                                 opdata->segments = opdata->segments + 1;
535                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
536                                 edbm_bevel_calc(op);
537                                 edbm_bevel_update_header(C, op);
538                                 handled = true;
539                                 break;
540
541                         case WHEELDOWNMOUSE:  /* change number of segments */
542                         case PADMINUS:
543                                 if (event->val == KM_RELEASE)
544                                         break;
545
546                                 opdata->segments = max_ff(opdata->segments - 1, 1);
547                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
548                                 edbm_bevel_calc(op);
549                                 edbm_bevel_update_header(C, op);
550                                 handled = true;
551                                 break;
552
553                         case MKEY:
554                                 if (event->val == KM_RELEASE)
555                                         break;
556
557                                 {
558                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type");
559                                         int type = RNA_property_enum_get(op->ptr, prop);
560                                         type++;
561                                         if (type > BEVEL_AMT_PERCENT) {
562                                                 type = BEVEL_AMT_OFFSET;
563                                         }
564                                         if (opdata->value_mode == OFFSET_VALUE && type == BEVEL_AMT_PERCENT)
565                                                 opdata->value_mode = OFFSET_VALUE_PERCENT;
566                                         else if (opdata->value_mode == OFFSET_VALUE_PERCENT && type != BEVEL_AMT_PERCENT)
567                                                 opdata->value_mode = OFFSET_VALUE;
568                                         RNA_property_enum_set(op->ptr, prop, type);
569                                         if (opdata->initial_length[opdata->value_mode] == -1.0f)
570                                                 edbm_bevel_calc_initial_length(op, event, true);
571                                 }
572                                 /* Update offset accordingly to new offset_type. */
573                                 if (!has_numinput &&
574                                     (opdata->value_mode == OFFSET_VALUE || opdata->value_mode == OFFSET_VALUE_PERCENT))
575                                 {
576                                         edbm_bevel_mouse_set_value(op, event);
577                                 }
578                                 edbm_bevel_calc(op);
579                                 edbm_bevel_update_header(C, op);
580                                 handled = true;
581                                 break;
582                         case CKEY:
583                                 if (event->val == KM_RELEASE)
584                                         break;
585
586                                 {
587                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "clamp_overlap");
588                                         RNA_property_boolean_set(op->ptr, prop, !RNA_property_boolean_get(op->ptr, prop));
589                                 }
590                                 edbm_bevel_calc(op);
591                                 edbm_bevel_update_header(C, op);
592                                 handled = true;
593                                 break;
594                         case PKEY:
595                                 if (event->val == KM_RELEASE)
596                                         break;
597                                 if (opdata->value_mode == PROFILE_VALUE) {
598                                         opdata->value_mode = OFFSET_VALUE;
599                                 }
600                                 else {
601                                         opdata->value_mode = PROFILE_VALUE;
602                                 }
603                                 edbm_bevel_calc_initial_length(op, event, true);
604                                 break;
605                         case SKEY:
606                                 if (event->val == KM_RELEASE)
607                                         break;
608                                 if (opdata->value_mode == SEGMENTS_VALUE) {
609                                         opdata->value_mode = OFFSET_VALUE;
610                                 }
611                                 else {
612                                         opdata->value_mode = SEGMENTS_VALUE;
613                                 }
614                                 edbm_bevel_calc_initial_length(op, event, true);
615                                 break;
616                         case VKEY:
617                                 if (event->val == KM_RELEASE)
618                                         break;
619
620                                 {
621                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "vertex_only");
622                                         RNA_property_boolean_set(op->ptr, prop, !RNA_property_boolean_get(op->ptr, prop));
623                                 }
624                                 edbm_bevel_calc(op);
625                                 edbm_bevel_update_header(C, op);
626                                 handled = true;
627                                 break;
628                         case UKEY:
629                                 if (event->val == KM_RELEASE)
630                                         break;
631                                 else {
632                                         bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
633                                         RNA_boolean_set(op->ptr, "mark_seam", !mark_seam);
634                                         edbm_bevel_calc(op);
635                                         handled = true;
636                                         break;
637                                 }
638                         case KKEY:
639                                 if (event->val == KM_RELEASE)
640                                         break;
641                                 else {
642                                         bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
643                                         RNA_boolean_set(op->ptr, "mark_sharp", !mark_sharp);
644                                         edbm_bevel_calc(op);
645                                         handled = true;
646                                         break;
647                                 }
648
649                 }
650
651                 /* Modal numinput inactive, try to handle numeric inputs last... */
652                 if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
653                         edbm_bevel_numinput_set_value(op);
654                         edbm_bevel_calc(op);
655                         edbm_bevel_update_header(C, op);
656                         return OPERATOR_RUNNING_MODAL;
657                 }
658         }
659
660         return OPERATOR_RUNNING_MODAL;
661 }
662
663 static void mesh_ot_bevel_offset_range_func(PointerRNA *ptr, PropertyRNA *UNUSED(prop),
664                                             float *min, float *max, float *softmin, float *softmax)
665 {
666         const int offset_type = RNA_enum_get(ptr, "offset_type");
667
668         *min = -FLT_MAX;
669         *max = FLT_MAX;
670         *softmin = 0.0f;
671         *softmax = (offset_type == BEVEL_AMT_PERCENT) ? 100.0f : 1.0f;
672 }
673
674 void MESH_OT_bevel(wmOperatorType *ot)
675 {
676         PropertyRNA *prop;
677
678         static const EnumPropertyItem offset_type_items[] = {
679                 {BEVEL_AMT_OFFSET, "OFFSET", 0, "Offset", "Amount is offset of new edges from original"},
680                 {BEVEL_AMT_WIDTH, "WIDTH", 0, "Width", "Amount is width of new face"},
681                 {BEVEL_AMT_DEPTH, "DEPTH", 0, "Depth", "Amount is perpendicular distance from original edge to bevel face"},
682                 {BEVEL_AMT_PERCENT, "PERCENT", 0, "Percent", "Amount is percent of adjacent edge length"},
683                 {0, NULL, 0, NULL, NULL},
684         };
685
686         static const EnumPropertyItem face_strength_mode_items[] = {
687                 {BEVEL_FACE_STRENGTH_NONE, "NONE", 0, "None", "Do not set face strength"},
688                 {BEVEL_FACE_STRENGTH_NEW, "NEW", 0, "New", "Set face strength on new faces only"},
689                 {BEVEL_FACE_STRENGTH_AFFECTED, "AFFECTED", 0, "Affected", "Set face strength on new and modified faces only"},
690                 {BEVEL_FACE_STRENGTH_ALL, "ALL", 0, "All", "Set face strength on all faces"},
691                 {0, NULL, 0, NULL, NULL},
692         };
693
694         /* identifiers */
695         ot->name = "Bevel";
696         ot->description = "Edge Bevel";
697         ot->idname = "MESH_OT_bevel";
698
699         /* api callbacks */
700         ot->exec = edbm_bevel_exec;
701         ot->invoke = edbm_bevel_invoke;
702         ot->modal = edbm_bevel_modal;
703         ot->cancel = edbm_bevel_cancel;
704         ot->poll = ED_operator_editmesh;
705
706         /* flags */
707         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
708
709         RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures");
710         prop = RNA_def_float(ot->srna, "offset", 0.0f, -1e6f, 1e6f, "Amount", "", 0.0f, 100.0f);
711         RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func);
712         RNA_def_int(ot->srna, "segments", 1, 1, SEGMENTS_HARD_MAX, "Segments", "Segments for curved edge", 1, 8);
713         RNA_def_float(ot->srna, "profile", 0.5f, PROFILE_HARD_MIN, 1.0f, "Profile",
714                 "Controls profile shape (0.5 = round)", PROFILE_HARD_MIN, 1.0f);
715         RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices");
716         RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap",
717                 "Do not allow beveled edges/vertices to overlap each other");
718         RNA_def_boolean(ot->srna, "loop_slide", true, "Loop Slide", "Prefer slide along edge to even widths");
719         RNA_def_boolean(ot->srna, "mark_seam", false, "Mark Seams", "Mark Seams along beveled edges");
720         RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark beveled edges as sharp");
721         RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material",
722                 "Material for bevel faces (-1 means use adjacent faces)", -1, 100);
723         RNA_def_boolean(ot->srna, "harden_normals", false, "Harden Normals", "Match normals of new faces to adjacent faces");
724         RNA_def_enum(ot->srna, "face_strength_mode", face_strength_mode_items, BEVEL_FACE_STRENGTH_NONE,
725                 "Face Strength Mode", "Whether to set face strength, and which faces to set face strength on");
726         prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
727         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
728
729 }