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