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