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