Bevel: Make modal keymap instead of hardcoded.
[blender.git] / source / blender / editors / mesh / editmesh_bevel.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file \ingroup edmesh
18  */
19
20 #include "MEM_guardedalloc.h"
21
22 #include "DNA_object_types.h"
23
24 #include "BLI_string.h"
25 #include "BLI_math.h"
26
27 #include "BLT_translation.h"
28
29 #include "BKE_context.h"
30 #include "BKE_global.h"
31 #include "BKE_editmesh.h"
32 #include "BKE_unit.h"
33 #include "BKE_layer.h"
34 #include "BKE_mesh.h"
35
36 #include "DNA_mesh_types.h"
37
38 #include "RNA_define.h"
39 #include "RNA_access.h"
40
41 #include "WM_api.h"
42 #include "WM_types.h"
43
44 #include "UI_interface.h"
45
46 #include "ED_mesh.h"
47 #include "ED_numinput.h"
48 #include "ED_screen.h"
49 #include "ED_space_api.h"
50 #include "ED_transform.h"
51 #include "ED_view3d.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "mesh_intern.h"  /* own include */
57
58
59 #define MVAL_PIXEL_MARGIN  5.0f
60
61 #define PROFILE_HARD_MIN 0.0f
62
63 #define SEGMENTS_HARD_MAX 1000
64
65 /* which value is mouse movement and numeric input controlling? */
66 #define OFFSET_VALUE 0
67 #define OFFSET_VALUE_PERCENT 1
68 #define PROFILE_VALUE 2
69 #define SEGMENTS_VALUE 3
70 #define NUM_VALUE_KINDS 4
71
72 static const char *value_rna_name[NUM_VALUE_KINDS] = {"offset", "offset", "profile", "segments"};
73 static const float value_clamp_min[NUM_VALUE_KINDS] = {0.0f, 0.0f, PROFILE_HARD_MIN, 1.0f};
74 static const float value_clamp_max[NUM_VALUE_KINDS] = {1e6, 100.0f, 1.0f, SEGMENTS_HARD_MAX};
75 static const float value_start[NUM_VALUE_KINDS] = {0.0f, 0.0f, 0.5f, 1.0f};
76 static const float value_scale_per_inch[NUM_VALUE_KINDS] = { 0.0f, 100.0f, 1.0f, 4.0f};
77
78 typedef struct {
79         BMEditMesh *em;
80         BMBackup mesh_backup;
81 } BevelObjectStore;
82
83
84 typedef struct {
85         float initial_length[NUM_VALUE_KINDS];
86         float scale[NUM_VALUE_KINDS];
87         NumInput num_input[NUM_VALUE_KINDS];
88         /** The current value when shift is pressed. Negative when shift not active. */
89         float shift_value[NUM_VALUE_KINDS];
90         bool is_modal;
91
92         BevelObjectStore *ob_store;
93         uint ob_store_len;
94
95         /* modal only */
96         float mcenter[2];
97         void *draw_handle_pixel;
98         short gizmo_flag;
99         short value_mode;  /* Which value does mouse movement and numeric input affect? */
100         float segments;     /* Segments as float so smooth mouse pan works in small increments */
101 } BevelData;
102
103 enum {
104         BEV_MODAL_CANCEL = 1,
105         BEV_MODAL_CONFIRM,
106         BEV_MODAL_VALUE_OFFSET,
107         BEV_MODAL_VALUE_PROFILE,
108         BEV_MODAL_VALUE_SEGMENTS,
109         BEV_MODAL_SEGMENTS_UP,
110         BEV_MODAL_SEGMENTS_DOWN,
111         BEV_MODAL_OFFSET_MODE_CHANGE,
112         BEV_MODAL_CLAMP_OVERLAP_TOGGLE,
113         BEV_MODAL_VERTEX_ONLY_TOGGLE,
114         BEV_MODAL_HARDEN_NORMALS_TOGGLE,
115         BEV_MODAL_MARK_SEAM_TOGGLE,
116         BEV_MODAL_MARK_SHARP_TOGGLE,
117         BEV_MODAL_OUTER_MITER_CHANGE,
118         BEV_MODAL_INNER_MITER_CHANGE,
119 };
120
121 static void edbm_bevel_update_header(bContext *C, wmOperator *op)
122 {
123         char header[UI_MAX_DRAW_STR];
124         char buf[UI_MAX_DRAW_STR];
125         char *p = buf;
126         int available_len = sizeof(buf);
127         Scene *sce = CTX_data_scene(C);
128         BevelData *opdata = op->customdata;
129         char offset_str[NUM_STR_REP_LEN];
130         const char *mode_str, *omiter_str, *imiter_str;
131         PropertyRNA *prop;
132
133 #define WM_MODALKEY(_id) \
134         WM_modalkeymap_operator_items_to_string_buf(op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
135
136         if (hasNumInput(&opdata->num_input[OFFSET_VALUE])) {
137                 outputNumInput(&opdata->num_input[OFFSET_VALUE], offset_str, &sce->unit);
138         }
139         else {
140                 BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset"));
141         }
142
143         prop = RNA_struct_find_property(op->ptr, "offset_type");
144         RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &mode_str);
145         prop = RNA_struct_find_property(op->ptr, "miter_outer");
146         RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &omiter_str);
147         prop = RNA_struct_find_property(op->ptr, "miter_inner");
148         RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &imiter_str);
149
150         BLI_snprintf(header, sizeof(header),
151                 IFACE_("%s: confirm, "
152                         "%s: cancel, "
153                         "%s: mode (%s), "
154                         "%s: offset (%s), "
155                         "%s: segments (%d), "
156                         "%s: profile (%.3f), "
157                         "%s: clamp overlap (%s), "
158                         "%s: vertex only (%s), "
159                         "%s: outer miter (%s), "
160                         "%s: inner imter (%s), "
161                         "%s: harden normals (%s), "
162                         "%s: mark seam (%s), "
163                         "%s: mark sharp (%s)"
164                 ),
165                 WM_MODALKEY(BEV_MODAL_CONFIRM),
166                 WM_MODALKEY(BEV_MODAL_CANCEL),
167                 WM_MODALKEY(BEV_MODAL_OFFSET_MODE_CHANGE),
168                 mode_str,
169                 WM_MODALKEY(BEV_MODAL_VALUE_OFFSET),
170                 offset_str,
171                 WM_MODALKEY(BEV_MODAL_VALUE_SEGMENTS),
172                 RNA_int_get(op->ptr, "segments"),
173                 WM_MODALKEY(BEV_MODAL_VALUE_PROFILE),
174                 RNA_float_get(op->ptr, "profile"),
175                 WM_MODALKEY(BEV_MODAL_CLAMP_OVERLAP_TOGGLE),
176                 WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")),
177                 WM_MODALKEY(BEV_MODAL_VERTEX_ONLY_TOGGLE),
178                 WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")),
179                 WM_MODALKEY(BEV_MODAL_OUTER_MITER_CHANGE),
180                 omiter_str,
181                 WM_MODALKEY(BEV_MODAL_INNER_MITER_CHANGE),
182                 imiter_str,
183                 WM_MODALKEY(BEV_MODAL_HARDEN_NORMALS_TOGGLE),
184                 WM_bool_as_string(RNA_boolean_get(op->ptr, "harden_normals")),
185                 WM_MODALKEY(BEV_MODAL_MARK_SEAM_TOGGLE),
186                 WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_seam")),
187                 WM_MODALKEY(BEV_MODAL_MARK_SHARP_TOGGLE),
188                 WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_sharp"))
189                 );
190
191 #undef WM_MODALKEY
192
193         ED_workspace_status_text(C, header);
194 }
195
196 static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
197 {
198         Scene *scene = CTX_data_scene(C);
199         BevelData *opdata;
200         ViewLayer *view_layer = CTX_data_view_layer(C);
201         float pixels_per_inch;
202         int i;
203
204         if (is_modal) {
205                 RNA_float_set(op->ptr, "offset", 0.0f);
206         }
207
208         op->customdata = opdata = MEM_mallocN(sizeof(BevelData), "beveldata_mesh_operator");
209         uint objects_used_len = 0;
210
211         {
212                 uint ob_store_len = 0;
213                 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
214                         view_layer, CTX_wm_view3d(C), &ob_store_len);
215                 opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
216                 for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
217                         Object *obedit = objects[ob_index];
218                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
219                         if (em->bm->totvertsel > 0) {
220                                 opdata->ob_store[objects_used_len].em = em;
221                                 objects_used_len++;
222                         }
223                 }
224                 MEM_freeN(objects);
225                 opdata->ob_store_len = objects_used_len;
226         }
227
228         opdata->is_modal = is_modal;
229         opdata->value_mode = OFFSET_VALUE;
230         opdata->segments = (float) RNA_int_get(op->ptr, "segments");
231         pixels_per_inch = U.dpi * U.pixelsize;
232
233         for (i = 0; i < NUM_VALUE_KINDS; i++) {
234                 opdata->shift_value[i] = -1.0f;
235                 opdata->initial_length[i] = -1.0f;
236                 /* note: scale for OFFSET_VALUE will get overwritten in edbm_bevel_invoke */
237                 opdata->scale[i] = value_scale_per_inch[i] / pixels_per_inch;
238
239                 initNumInput(&opdata->num_input[i]);
240                 opdata->num_input[i].idx_max = 0;
241                 opdata->num_input[i].val_flag[0] |= NUM_NO_NEGATIVE;
242                 if (i == SEGMENTS_VALUE) {
243                         opdata->num_input[i].val_flag[0] |= NUM_NO_FRACTION | NUM_NO_ZERO;
244                 }
245                 if (i == OFFSET_VALUE) {
246                         opdata->num_input[i].unit_sys = scene->unit.system;
247                 }
248                 /* Not sure this is a factor or a unit? */
249                 opdata->num_input[i].unit_type[0] = B_UNIT_NONE;
250         }
251
252         /* avoid the cost of allocating a bm copy */
253         if (is_modal) {
254                 View3D *v3d = CTX_wm_view3d(C);
255                 ARegion *ar = CTX_wm_region(C);
256
257                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
258                         opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(opdata->ob_store[ob_index].em);
259                 }
260                 opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb,
261                         opdata->mcenter, REGION_DRAW_POST_PIXEL);
262                 G.moving = G_TRANSFORM_EDIT;
263
264                 if (v3d) {
265                         opdata->gizmo_flag = v3d->gizmo_flag;
266                         v3d->gizmo_flag = V3D_GIZMO_HIDE;
267                 }
268         }
269
270         return true;
271 }
272
273 static bool edbm_bevel_calc(wmOperator *op)
274 {
275         BevelData *opdata = op->customdata;
276         BMEditMesh *em;
277         BMOperator bmop;
278         bool changed = false;
279
280         const float offset = RNA_float_get(op->ptr, "offset");
281         const int offset_type = RNA_enum_get(op->ptr, "offset_type");
282         const int segments = RNA_int_get(op->ptr, "segments");
283         const float profile = RNA_float_get(op->ptr, "profile");
284         const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
285         const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
286         int material = RNA_int_get(op->ptr, "material");
287         const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide");
288         const bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
289         const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
290         const bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
291         const int face_strength_mode = RNA_enum_get(op->ptr, "face_strength_mode");
292         const int miter_outer = RNA_enum_get(op->ptr, "miter_outer");
293         const int miter_inner = RNA_enum_get(op->ptr, "miter_inner");
294         const float spread = RNA_float_get(op->ptr, "spread");
295
296         for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
297                 em = opdata->ob_store[ob_index].em;
298
299                 /* revert to original mesh */
300                 if (opdata->is_modal) {
301                         EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false);
302                 }
303
304                 if (em->ob) {
305                         material = CLAMPIS(material, -1, em->ob->totcol - 1);
306                 }
307
308                 Mesh *me = em->ob->data;
309
310                 if (harden_normals && !(me->flag & ME_AUTOSMOOTH)) {
311                         /* harden_normals only has a visible effect if autosmooth is on, so turn it on */
312                         me->flag |= ME_AUTOSMOOTH;
313                 }
314
315                 EDBM_op_init(
316                         em, &bmop, op,
317                         "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f "
318                         "clamp_overlap=%b material=%i loop_slide=%b mark_seam=%b mark_sharp=%b "
319                         "harden_normals=%b face_strength_mode=%i "
320                         "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f",
321                         BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile,
322                         clamp_overlap, material, loop_slide, mark_seam, mark_sharp, harden_normals, face_strength_mode,
323                         miter_outer, miter_inner, spread, me->smoothresh);
324
325                 BMO_op_exec(em->bm, &bmop);
326
327                 if (offset != 0.0f) {
328                         /* not essential, but we may have some loose geometry that
329                          * won't get bevel'd and better not leave it selected */
330                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
331                         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
332                 }
333
334                 /* no need to de-select existing geometry */
335                 if (!EDBM_op_finish(em, &bmop, op, true)) {
336                         continue;
337                 }
338
339                 EDBM_mesh_normals_update(em);
340
341                 EDBM_update_generic(em, true, true);
342                 changed = true;
343         }
344         return changed;
345 }
346
347 static void edbm_bevel_exit(bContext *C, wmOperator *op)
348 {
349         BevelData *opdata = op->customdata;
350
351         ScrArea *sa = CTX_wm_area(C);
352
353         if (sa) {
354                 ED_area_status_text(sa, NULL);
355         }
356
357         if (opdata->is_modal) {
358                 View3D *v3d = CTX_wm_view3d(C);
359                 ARegion *ar = CTX_wm_region(C);
360                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
361                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, NULL, false);
362                 }
363                 ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel);
364                 if (v3d) {
365                         v3d->gizmo_flag = opdata->gizmo_flag;
366                 }
367                 G.moving = 0;
368         }
369         MEM_SAFE_FREE(opdata->ob_store);
370         MEM_SAFE_FREE(op->customdata);
371         op->customdata = NULL;
372 }
373
374 static void edbm_bevel_cancel(bContext *C, wmOperator *op)
375 {
376         BevelData *opdata = op->customdata;
377         if (opdata->is_modal) {
378                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
379                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true);
380                         EDBM_update_generic(opdata->ob_store[ob_index].em, false, true);
381                 }
382         }
383
384         edbm_bevel_exit(C, op);
385
386         /* need to force redisplay or we may still view the modified result */
387         ED_region_tag_redraw(CTX_wm_region(C));
388 }
389
390 /* bevel! yay!!*/
391 static int edbm_bevel_exec(bContext *C, wmOperator *op)
392 {
393         if (!edbm_bevel_init(C, op, false)) {
394                 return OPERATOR_CANCELLED;
395         }
396
397         if (!edbm_bevel_calc(op)) {
398                 edbm_bevel_cancel(C, op);
399                 return OPERATOR_CANCELLED;
400         }
401
402         edbm_bevel_exit(C, op);
403
404         return OPERATOR_FINISHED;
405 }
406
407 static void edbm_bevel_calc_initial_length(wmOperator *op, const wmEvent *event, bool mode_changed)
408 {
409         BevelData *opdata;
410         float mlen[2], len, value, sc, st;
411         int vmode;
412
413         opdata = op->customdata;
414         mlen[0] = opdata->mcenter[0] - event->mval[0];
415         mlen[1] = opdata->mcenter[1] - event->mval[1];
416         len = len_v2(mlen);
417         vmode = opdata->value_mode;
418         if (mode_changed || opdata->initial_length[vmode] == -1.0f) {
419                 /* If current value is not default start value, adjust len so that
420                  * the scaling and offset in edbm_bevel_mouse_set_value will
421                  * start at current value */
422                 value = (vmode == SEGMENTS_VALUE) ?
423                         opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
424                 sc = opdata->scale[vmode];
425                 st = value_start[vmode];
426                 if (value != value_start[vmode]) {
427                         len = (st + sc * (len - MVAL_PIXEL_MARGIN) - value) / sc;
428                 }
429         }
430         opdata->initial_length[opdata->value_mode] = len;
431 }
432
433 static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
434 {
435         /* TODO make modal keymap (see fly mode) */
436         RegionView3D *rv3d = CTX_wm_region_view3d(C);
437         BevelData *opdata;
438         float center_3d[3];
439
440         if (!edbm_bevel_init(C, op, true)) {
441                 return OPERATOR_CANCELLED;
442         }
443
444         opdata = op->customdata;
445
446         /* initialize mouse values */
447         if (!calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, center_3d, opdata->mcenter)) {
448                 /* in this case the tool will likely do nothing,
449                  * ideally this will never happen and should be checked for above */
450                 opdata->mcenter[0] = opdata->mcenter[1] = 0;
451         }
452
453         /* for OFFSET_VALUE only, the scale is the size of a pixel under the mouse in 3d space */
454         opdata->scale[OFFSET_VALUE] = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f;
455
456         edbm_bevel_calc_initial_length(op, event, false);
457
458         edbm_bevel_update_header(C, op);
459
460         if (!edbm_bevel_calc(op)) {
461                 edbm_bevel_cancel(C, op);
462                 return OPERATOR_CANCELLED;
463         }
464
465         WM_event_add_modal_handler(C, op);
466
467         return OPERATOR_RUNNING_MODAL;
468 }
469
470 static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event)
471 {
472         BevelData *opdata = op->customdata;
473         int vmode = opdata->value_mode;
474         float mdiff[2];
475         float value;
476
477         mdiff[0] = opdata->mcenter[0] - event->mval[0];
478         mdiff[1] = opdata->mcenter[1] - event->mval[1];
479
480         value = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length[vmode]);
481
482         /* Scale according to value mode */
483         value = value_start[vmode] + value * opdata->scale[vmode];
484
485         /* Fake shift-transform... */
486         if (event->shift) {
487                 if (opdata->shift_value[vmode] < 0.0f) {
488                         opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ?
489                                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
490                 }
491                 value = (value - opdata->shift_value[vmode]) * 0.1f + opdata->shift_value[vmode];
492         }
493         else if (opdata->shift_value[vmode] >= 0.0f) {
494                 opdata->shift_value[vmode] = -1.0f;
495         }
496
497         /* clamp accordingto value mode, and store value back */
498         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
499         if (vmode == SEGMENTS_VALUE) {
500                 opdata->segments = value;
501                 RNA_int_set(op->ptr, "segments", (int)(value + 0.5f));
502         }
503         else {
504                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
505         }
506 }
507
508 static void edbm_bevel_numinput_set_value(wmOperator *op)
509 {
510         BevelData *opdata = op->customdata;
511         float value;
512         int vmode;
513
514         vmode = opdata->value_mode;
515         value = (vmode == SEGMENTS_VALUE) ?
516                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
517         applyNumInput(&opdata->num_input[vmode], &value);
518         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
519         if (vmode == SEGMENTS_VALUE) {
520                 opdata->segments = value;
521                 RNA_int_set(op->ptr, "segments", (int)value);
522         }
523         else {
524                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
525         }
526 }
527
528 wmKeyMap *bevel_modal_keymap(wmKeyConfig *keyconf)
529 {
530         static const EnumPropertyItem modal_items[] = {
531                 {BEV_MODAL_CANCEL, "CANCEL", 0, "Cancel", "Cancel bevel"},
532                 {BEV_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", "Confirm bevel"},
533                 {BEV_MODAL_VALUE_OFFSET, "VALUE_OFFSET", 0, "Value is offset",
534                         "Value changes offset"},
535                 {BEV_MODAL_VALUE_PROFILE, "VALUE_PROFILE", 0, "Value is profile",
536                         "Value changes profile"},
537                 {BEV_MODAL_VALUE_SEGMENTS, "VALUE_SEGMENTS", 0, "Value is segments",
538                         "Value changes segments"},
539                 {BEV_MODAL_SEGMENTS_UP, "SEGMENTS_UP", 0, "Increase segments",
540                         "Increase segments"},
541                 {BEV_MODAL_SEGMENTS_DOWN, "SEGMENTS_DOWN", 0, "Decrease segments",
542                         "Decrease segments"},
543                 {BEV_MODAL_OFFSET_MODE_CHANGE, "OFFSET_MODE_CHANGE", 0, "Change offset mode",
544                         "Cycle through offset modes"},
545                 {BEV_MODAL_CLAMP_OVERLAP_TOGGLE, "CLAMP_OVERLAP_TOGGLE", 0, "Toggle clamp overlap",
546                         "Toggle clamp overlap flag"},
547                 {BEV_MODAL_VERTEX_ONLY_TOGGLE, "VERTEX_ONLY_TOGGLE", 0, "Toggle vertex only",
548                         "Toggle vertex only flag"},
549                 {BEV_MODAL_HARDEN_NORMALS_TOGGLE, "HARDEN_NORMALS_TOGGLE", 0, "Toggle harden normals",
550                         "Toggle harden normals flag"},
551                 {BEV_MODAL_MARK_SEAM_TOGGLE, "MARK_SEAM_TOGGLE", 0, "Toggle mark seam",
552                         "Toggle mark seam flag"},
553                 {BEV_MODAL_MARK_SHARP_TOGGLE, "MARK_SHARP_TOGGLE", 0, "Toggle mark sharp",
554                         "Toggle mark sharp flag"},
555                 {BEV_MODAL_OUTER_MITER_CHANGE, "OUTER_MITER_CHANGE", 0, "Change outer miter",
556                         "Cycle through outer miter kinds"},
557                 {BEV_MODAL_INNER_MITER_CHANGE, "INNER_MITER_CHANGE", 0, "Change inner miter",
558                         "Cycle through inner miter kinds"},
559                 {0, NULL, 0, NULL, NULL},
560         };
561
562         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Bevel Modal Map");
563
564         /* this function is called for each spacetype, only needs to add map once */
565         if (keymap && keymap->modal_items)
566                 return NULL;
567
568         keymap = WM_modalkeymap_add(keyconf, "Bevel Modal Map", modal_items);
569
570         WM_modalkeymap_assign(keymap, "MESH_OT_bevel");
571
572         return keymap;
573 }
574
575 static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
576 {
577         BevelData *opdata = op->customdata;
578         const bool has_numinput = hasNumInput(&opdata->num_input[opdata->value_mode]);
579         bool handled = false;
580
581         /* Modal numinput active, try to handle numeric inputs first... */
582         if (event->type != EVT_MODAL_MAP && event->val == KM_PRESS && has_numinput && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
583                 edbm_bevel_numinput_set_value(op);
584                 edbm_bevel_calc(op);
585                 edbm_bevel_update_header(C, op);
586                 return OPERATOR_RUNNING_MODAL;
587         }
588         else if (event->type == MOUSEMOVE) {
589                 if (!has_numinput) {
590                         edbm_bevel_mouse_set_value(op, event);
591                         edbm_bevel_calc(op);
592                         edbm_bevel_update_header(C, op);
593                         handled = true;
594                 }
595         }
596         else if (event->type == MOUSEPAN) {
597                 float delta = 0.02f * (event->y - event->prevy);
598                 if (opdata->segments >= 1 && opdata->segments + delta < 1)
599                         opdata->segments = 1;
600                 else
601                         opdata->segments += delta;
602                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
603                 edbm_bevel_calc(op);
604                 edbm_bevel_update_header(C, op);
605                 handled = true;
606         }
607         else if (event->type == EVT_MODAL_MAP){
608                 switch (event->val) {
609                         case BEV_MODAL_CANCEL:
610                                 edbm_bevel_cancel(C, op);
611                                 return OPERATOR_CANCELLED;
612
613                         case BEV_MODAL_CONFIRM:
614 #if 0
615                                 if ((event->val == KM_PRESS) ||
616                                     ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm")))
617 #endif
618                                 {
619                                         edbm_bevel_calc(op);
620                                         edbm_bevel_exit(C, op);
621                                         return OPERATOR_FINISHED;
622                                 }
623                                 break;
624
625                         case BEV_MODAL_SEGMENTS_UP:
626                                 opdata->segments = opdata->segments + 1;
627                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
628                                 edbm_bevel_calc(op);
629                                 edbm_bevel_update_header(C, op);
630                                 handled = true;
631                                 break;
632
633                         case BEV_MODAL_SEGMENTS_DOWN:
634                                 opdata->segments = max_ff(opdata->segments - 1, 1);
635                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
636                                 edbm_bevel_calc(op);
637                                 edbm_bevel_update_header(C, op);
638                                 handled = true;
639                                 break;
640
641                         case BEV_MODAL_OFFSET_MODE_CHANGE:
642                                 {
643                                         int type = RNA_enum_get(op->ptr, "offset_type");
644                                         type++;
645                                         if (type > BEVEL_AMT_PERCENT) {
646                                                 type = BEVEL_AMT_OFFSET;
647                                         }
648                                         if (opdata->value_mode == OFFSET_VALUE && type == BEVEL_AMT_PERCENT)
649                                                 opdata->value_mode = OFFSET_VALUE_PERCENT;
650                                         else if (opdata->value_mode == OFFSET_VALUE_PERCENT && type != BEVEL_AMT_PERCENT)
651                                                 opdata->value_mode = OFFSET_VALUE;
652                                         RNA_enum_set(op->ptr, "offset_type", type);
653                                         if (opdata->initial_length[opdata->value_mode] == -1.0f)
654                                                 edbm_bevel_calc_initial_length(op, event, true);
655                                 }
656                                 /* Update offset accordingly to new offset_type. */
657                                 if (!has_numinput &&
658                                     (opdata->value_mode == OFFSET_VALUE || opdata->value_mode == OFFSET_VALUE_PERCENT))
659                                 {
660                                         edbm_bevel_mouse_set_value(op, event);
661                                 }
662                                 edbm_bevel_calc(op);
663                                 edbm_bevel_update_header(C, op);
664                                 handled = true;
665                                 break;
666
667                         case BEV_MODAL_CLAMP_OVERLAP_TOGGLE:
668                                 {
669                                         bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
670                                         RNA_boolean_set(op->ptr, "clamp_overlap", !clamp_overlap);
671                                         edbm_bevel_calc(op);
672                                         edbm_bevel_update_header(C, op);
673                                         handled = true;
674                                         break;
675                                 }
676
677                         case BEV_MODAL_VALUE_OFFSET:
678                                 opdata->value_mode = OFFSET_VALUE;
679                                 edbm_bevel_calc_initial_length(op, event, true);
680                                 break;
681
682                         case BEV_MODAL_VALUE_PROFILE:
683                                 opdata->value_mode = PROFILE_VALUE;
684                                 edbm_bevel_calc_initial_length(op, event, true);
685                                 break;
686
687                         case BEV_MODAL_VALUE_SEGMENTS:
688                                 opdata->value_mode = SEGMENTS_VALUE;
689                                 edbm_bevel_calc_initial_length(op, event, true);
690                                 break;
691
692                         case BEV_MODAL_VERTEX_ONLY_TOGGLE:
693                                 {
694                                         bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
695                                         RNA_boolean_set(op->ptr, "vertex_only", !vertex_only);
696                                         edbm_bevel_calc(op);
697                                         edbm_bevel_update_header(C, op);
698                                         handled = true;
699                                         break;
700                                 }
701
702                         case BEV_MODAL_MARK_SEAM_TOGGLE:
703                                 {
704                                         bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
705                                         RNA_boolean_set(op->ptr, "mark_seam", !mark_seam);
706                                         edbm_bevel_calc(op);
707                                         edbm_bevel_update_header(C, op);
708                                         handled = true;
709                                         break;
710                                 }
711
712                         case BEV_MODAL_MARK_SHARP_TOGGLE:
713                                 {
714                                         bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
715                                         RNA_boolean_set(op->ptr, "mark_sharp", !mark_sharp);
716                                         edbm_bevel_calc(op);
717                                         edbm_bevel_update_header(C, op);
718                                         handled = true;
719                                         break;
720                                 }
721
722                         case BEV_MODAL_INNER_MITER_CHANGE:
723                                 {
724                                         int miter_inner = RNA_enum_get(op->ptr, "miter_inner");
725                                         miter_inner++;
726                                         if (miter_inner == BEVEL_MITER_PATCH)
727                                                 miter_inner++;  /* no patch option for inner miter */
728                                         if (miter_inner > BEVEL_MITER_ARC)
729                                                 miter_inner = BEVEL_MITER_SHARP;
730                                         RNA_enum_set(op->ptr, "miter_inner", miter_inner);
731                                         edbm_bevel_calc(op);
732                                         edbm_bevel_update_header(C, op);
733                                         handled = true;
734                                         break;
735                                 }
736
737                         case BEV_MODAL_OUTER_MITER_CHANGE:
738                                 {
739                                         int miter_outer = RNA_enum_get(op->ptr, "miter_outer");
740                                         miter_outer++;
741                                         if (miter_outer > BEVEL_MITER_ARC)
742                                                 miter_outer = BEVEL_MITER_SHARP;
743                                         RNA_enum_set(op->ptr, "miter_outer", miter_outer);
744                                         edbm_bevel_calc(op);
745                                         edbm_bevel_update_header(C, op);
746                                         handled = true;
747                                         break;
748                                 }
749
750                         case BEV_MODAL_HARDEN_NORMALS_TOGGLE:
751                                 {
752                                         bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
753                                         RNA_boolean_set(op->ptr, "harden_normals", !harden_normals);
754                                         edbm_bevel_calc(op);
755                                         edbm_bevel_update_header(C, op);
756                                         handled = true;
757                                         break;
758                                 }
759                 }
760         }
761
762         /* Modal numinput inactive, try to handle numeric inputs last... */
763         if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
764                 edbm_bevel_numinput_set_value(op);
765                 edbm_bevel_calc(op);
766                 edbm_bevel_update_header(C, op);
767                 return OPERATOR_RUNNING_MODAL;
768         }
769
770         return OPERATOR_RUNNING_MODAL;
771 }
772
773 static void mesh_ot_bevel_offset_range_func(PointerRNA *ptr, PropertyRNA *UNUSED(prop),
774                                             float *min, float *max, float *softmin, float *softmax)
775 {
776         const int offset_type = RNA_enum_get(ptr, "offset_type");
777
778         *min = -FLT_MAX;
779         *max = FLT_MAX;
780         *softmin = 0.0f;
781         *softmax = (offset_type == BEVEL_AMT_PERCENT) ? 100.0f : 1.0f;
782 }
783
784 void MESH_OT_bevel(wmOperatorType *ot)
785 {
786         PropertyRNA *prop;
787
788         static const EnumPropertyItem offset_type_items[] = {
789                 {BEVEL_AMT_OFFSET, "OFFSET", 0, "Offset", "Amount is offset of new edges from original"},
790                 {BEVEL_AMT_WIDTH, "WIDTH", 0, "Width", "Amount is width of new face"},
791                 {BEVEL_AMT_DEPTH, "DEPTH", 0, "Depth", "Amount is perpendicular distance from original edge to bevel face"},
792                 {BEVEL_AMT_PERCENT, "PERCENT", 0, "Percent", "Amount is percent of adjacent edge length"},
793                 {0, NULL, 0, NULL, NULL},
794         };
795
796         static const EnumPropertyItem face_strength_mode_items[] = {
797                 {BEVEL_FACE_STRENGTH_NONE, "NONE", 0, "None", "Do not set face strength"},
798                 {BEVEL_FACE_STRENGTH_NEW, "NEW", 0, "New", "Set face strength on new faces only"},
799                 {BEVEL_FACE_STRENGTH_AFFECTED, "AFFECTED", 0, "Affected", "Set face strength on new and modified faces only"},
800                 {BEVEL_FACE_STRENGTH_ALL, "ALL", 0, "All", "Set face strength on all faces"},
801                 {0, NULL, 0, NULL, NULL},
802         };
803
804         static const EnumPropertyItem miter_outer_items[] = {
805                 {BEVEL_MITER_SHARP, "SHARP", 0, "Sharp", "Outside of miter is sharp"},
806                 {BEVEL_MITER_PATCH, "PATCH", 0, "Patch", "Outside of miter is squared-off patch"},
807                 {BEVEL_MITER_ARC, "ARC", 0, "Arc", "Outside of miter is arc"},
808                 {0, NULL, 0, NULL, NULL},
809         };
810
811         static const EnumPropertyItem miter_inner_items[] = {
812                 {BEVEL_MITER_SHARP, "SHARP", 0, "Sharp", "Inside of miter is sharp"},
813                 {BEVEL_MITER_ARC, "ARC", 0, "Arc", "Inside of miter is arc"},
814                 {0, NULL, 0, NULL, NULL},
815         };
816
817         /* identifiers */
818         ot->name = "Bevel";
819         ot->description = "Edge Bevel";
820         ot->idname = "MESH_OT_bevel";
821
822         /* api callbacks */
823         ot->exec = edbm_bevel_exec;
824         ot->invoke = edbm_bevel_invoke;
825         ot->modal = edbm_bevel_modal;
826         ot->cancel = edbm_bevel_cancel;
827         ot->poll = ED_operator_editmesh;
828
829         /* flags */
830         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
831
832         RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures");
833         prop = RNA_def_float(ot->srna, "offset", 0.0f, -1e6f, 1e6f, "Amount", "", 0.0f, 100.0f);
834         RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func);
835         RNA_def_int(ot->srna, "segments", 1, 1, SEGMENTS_HARD_MAX, "Segments", "Segments for curved edge", 1, 8);
836         RNA_def_float(ot->srna, "profile", 0.5f, PROFILE_HARD_MIN, 1.0f, "Profile",
837                 "Controls profile shape (0.5 = round)", PROFILE_HARD_MIN, 1.0f);
838         RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices");
839         RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap",
840                 "Do not allow beveled edges/vertices to overlap each other");
841         RNA_def_boolean(ot->srna, "loop_slide", true, "Loop Slide", "Prefer slide along edge to even widths");
842         RNA_def_boolean(ot->srna, "mark_seam", false, "Mark Seams", "Mark Seams along beveled edges");
843         RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark beveled edges as sharp");
844         RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material",
845                 "Material for bevel faces (-1 means use adjacent faces)", -1, 100);
846         RNA_def_boolean(ot->srna, "harden_normals", false, "Harden Normals",
847                 "Match normals of new faces to adjacent faces");
848         RNA_def_enum(ot->srna, "face_strength_mode", face_strength_mode_items, BEVEL_FACE_STRENGTH_NONE,
849                 "Face Strength Mode", "Whether to set face strength, and which faces to set face strength on");
850         RNA_def_enum(ot->srna, "miter_outer", miter_outer_items, BEVEL_MITER_SHARP,
851                 "Outer Miter", "Pattern to use for outside of miters");
852         RNA_def_enum(ot->srna, "miter_inner", miter_inner_items, BEVEL_MITER_SHARP,
853                 "Inner Miter", "Pattern to use for inside of miters");
854         RNA_def_float(ot->srna, "spread", 0.1f, 0.0f, 1e6f, "Spread",
855                 "Amount to spread arcs for arc inner miters", 0.0f, 100.0f);
856         prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
857         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
858
859 }