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