Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / mesh / editmesh_bevel.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Joseph Eagar, Howard Trickey, Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/mesh/editmesh_bevel.c
24  *  \ingroup edmesh
25  */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_object_types.h"
30
31 #include "BLI_string.h"
32 #include "BLI_math.h"
33
34 #include "BLT_translation.h"
35
36 #include "BKE_context.h"
37 #include "BKE_global.h"
38 #include "BKE_editmesh.h"
39 #include "BKE_unit.h"
40 #include "BKE_layer.h"
41
42 #include "RNA_define.h"
43 #include "RNA_access.h"
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "UI_interface.h"
49
50 #include "ED_mesh.h"
51 #include "ED_numinput.h"
52 #include "ED_screen.h"
53 #include "ED_space_api.h"
54 #include "ED_transform.h"
55 #include "ED_view3d.h"
56
57 #include "mesh_intern.h"  /* own include */
58
59
60 #define MVAL_PIXEL_MARGIN  5.0f
61
62 #define PROFILE_HARD_MIN 0.0f
63
64 #define SEGMENTS_HARD_MAX 1000
65
66 /* which value is mouse movement and numeric input controlling? */
67 #define OFFSET_VALUE 0
68 #define OFFSET_VALUE_PERCENT 1
69 #define PROFILE_VALUE 2
70 #define SEGMENTS_VALUE 3
71 #define NUM_VALUE_KINDS 4
72
73 static const char *value_rna_name[NUM_VALUE_KINDS] = {"offset", "offset", "profile", "segments"};
74 static const float value_clamp_min[NUM_VALUE_KINDS] = {0.0f, 0.0f, PROFILE_HARD_MIN, 1.0f};
75 static const float value_clamp_max[NUM_VALUE_KINDS] = {1e6, 100.0f, 1.0f, SEGMENTS_HARD_MAX};
76 static const float value_start[NUM_VALUE_KINDS] = {0.0f, 0.0f, 0.5f, 1.0f};
77 static const float value_scale_per_inch[NUM_VALUE_KINDS] = { 0.0f, 100.0f, 1.0f, 4.0f};
78
79 typedef struct {
80         BMEditMesh *em;
81         BMBackup mesh_backup;
82 } BevelObjectStore;
83
84
85 typedef struct {
86         float initial_length[NUM_VALUE_KINDS];
87         float scale[NUM_VALUE_KINDS];
88         NumInput num_input[NUM_VALUE_KINDS];
89         float shift_value[NUM_VALUE_KINDS]; /* The current value when shift is pressed. Negative when shift not active. */
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 twflag;
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 static void edbm_bevel_update_header(bContext *C, wmOperator *op)
104 {
105         const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Mode: %s (M), Clamp Overlap: %s (C), "
106                                  "Vertex Only: %s (V), Profile Control: %s (P), Offset: %s, Segments: %d, Profile: %.3f");
107
108         char msg[UI_MAX_DRAW_STR];
109         ScrArea *sa = CTX_wm_area(C);
110         Scene *sce = CTX_data_scene(C);
111
112         if (sa) {
113                 BevelData *opdata = op->customdata;
114                 char offset_str[NUM_STR_REP_LEN];
115                 const char *type_str;
116                 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type");
117
118                 if (hasNumInput(&opdata->num_input[OFFSET_VALUE])) {
119                         outputNumInput(&opdata->num_input[OFFSET_VALUE], offset_str, &sce->unit);
120                 }
121                 else {
122                         BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset"));
123                 }
124
125                 RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &type_str);
126
127                 BLI_snprintf(msg, sizeof(msg), str, type_str,
128                              WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")),
129                              WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")),
130                              WM_bool_as_string(opdata->value_mode == PROFILE_VALUE),
131                              offset_str, RNA_int_get(op->ptr, "segments"), RNA_float_get(op->ptr, "profile"));
132
133                 ED_area_headerprint(sa, msg);
134         }
135 }
136
137 static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
138 {
139         Scene *scene = CTX_data_scene(C);
140         BevelData *opdata;
141         ViewLayer *view_layer = CTX_data_view_layer(C);
142         float pixels_per_inch;
143         int i;
144
145         if (is_modal) {
146                 RNA_float_set(op->ptr, "offset", 0.0f);
147         }
148
149         op->customdata = opdata = MEM_mallocN(sizeof(BevelData), "beveldata_mesh_operator");
150         uint objects_used_len = 0;
151
152         {
153                 uint ob_store_len = 0;
154                 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &ob_store_len);
155                 opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
156                 for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
157                         Object *obedit = objects[ob_index];
158                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
159                         if (em->bm->totvertsel > 0) {
160                                 opdata->ob_store[objects_used_len].em = em;
161                                 objects_used_len++;
162                         }
163                 }
164                 MEM_freeN(objects);
165                 opdata->ob_store_len = objects_used_len;
166         }
167
168         opdata->is_modal = is_modal;
169         opdata->value_mode = OFFSET_VALUE;
170         opdata->segments = (float) RNA_int_get(op->ptr, "segments");
171         pixels_per_inch = U.dpi * U.pixelsize;
172
173         for (i = 0; i < NUM_VALUE_KINDS; i++) {
174                 opdata->shift_value[i] = -1.0f;
175                 opdata->initial_length[i] = -1.0f;
176                 /* note: scale for OFFSET_VALUE will get overwritten in edbm_bevel_invoke */
177                 opdata->scale[i] = value_scale_per_inch[i] / pixels_per_inch;
178
179                 initNumInput(&opdata->num_input[i]);
180                 opdata->num_input[i].idx_max = 0;
181                 opdata->num_input[i].val_flag[0] |= NUM_NO_NEGATIVE;
182                 if (i == SEGMENTS_VALUE) {
183                         opdata->num_input[i].val_flag[0] |= NUM_NO_FRACTION | NUM_NO_ZERO;
184                 }
185                 if (i == OFFSET_VALUE) {
186                         opdata->num_input[i].unit_sys = scene->unit.system;
187                 }
188                 opdata->num_input[i].unit_type[0] = B_UNIT_NONE;  /* Not sure this is a factor or a unit? */
189         }
190
191         /* avoid the cost of allocating a bm copy */
192         if (is_modal) {
193                 View3D *v3d = CTX_wm_view3d(C);
194                 ARegion *ar = CTX_wm_region(C);
195
196                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
197                         opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(opdata->ob_store[ob_index].em);
198                 }
199                 opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb,
200                         opdata->mcenter, REGION_DRAW_POST_PIXEL);
201                 G.moving = G_TRANSFORM_EDIT;
202
203                 if (v3d) {
204                         opdata->twflag = v3d->twflag;
205                         v3d->twflag = 0;
206                 }
207         }
208
209         return true;
210 }
211
212 static bool edbm_bevel_calc(wmOperator *op)
213 {
214         BevelData *opdata = op->customdata;
215         BMEditMesh *em;
216         BMOperator bmop;
217         bool changed = false;
218
219         const float offset = RNA_float_get(op->ptr, "offset");
220         const int offset_type = RNA_enum_get(op->ptr, "offset_type");
221         const int segments = RNA_int_get(op->ptr, "segments");
222         const float profile = RNA_float_get(op->ptr, "profile");
223         const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
224         const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
225         int material = RNA_int_get(op->ptr, "material");
226         const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide");
227
228
229         for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
230                 em = opdata->ob_store[ob_index].em;
231
232                 /* revert to original mesh */
233                 if (opdata->is_modal) {
234                         EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false);
235                 }
236
237                 if (em->ob) {
238                         material = CLAMPIS(material, -1, em->ob->totcol - 1);
239                 }
240
241                 EDBM_op_init(em, &bmop, op,
242                         "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f clamp_overlap=%b "
243                         "material=%i loop_slide=%b",
244                         BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile,
245                         clamp_overlap, material, loop_slide);
246
247                 BMO_op_exec(em->bm, &bmop);
248
249                 if (offset != 0.0f) {
250                         /* not essential, but we may have some loose geometry that
251                          * won't get bevel'd and better not leave it selected */
252                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
253                         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
254                 }
255
256                 /* no need to de-select existing geometry */
257                 if (!EDBM_op_finish(em, &bmop, op, true)) {
258                         continue;
259                 }
260
261                 EDBM_mesh_normals_update(em);
262
263                 EDBM_update_generic(em, true, true);
264                 changed = true;
265         }
266         return changed;
267 }
268
269 static void edbm_bevel_exit(bContext *C, wmOperator *op)
270 {
271         BevelData *opdata = op->customdata;
272
273         ScrArea *sa = CTX_wm_area(C);
274
275         if (sa) {
276                 ED_area_headerprint(sa, NULL);
277         }
278
279         if (opdata->is_modal) {
280                 View3D *v3d = CTX_wm_view3d(C);
281                 ARegion *ar = CTX_wm_region(C);
282                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
283                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, NULL, false);
284                 }
285                 ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel);
286                 if (v3d) {
287                         v3d->twflag = opdata->twflag;
288                 }
289                 G.moving = 0;
290         }
291         MEM_SAFE_FREE(opdata->ob_store);
292         MEM_SAFE_FREE(op->customdata);
293         op->customdata = NULL;
294 }
295
296 static void edbm_bevel_cancel(bContext *C, wmOperator *op)
297 {
298         BevelData *opdata = op->customdata;
299         if (opdata->is_modal) {
300                 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
301                         EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true);
302                         EDBM_update_generic(opdata->ob_store[ob_index].em, false, true);
303                 }
304         }
305
306         edbm_bevel_exit(C, op);
307
308         /* need to force redisplay or we may still view the modified result */
309         ED_region_tag_redraw(CTX_wm_region(C));
310 }
311
312 /* bevel! yay!!*/
313 static int edbm_bevel_exec(bContext *C, wmOperator *op)
314 {
315         if (!edbm_bevel_init(C, op, false)) {
316                 return OPERATOR_CANCELLED;
317         }
318
319         if (!edbm_bevel_calc(op)) {
320                 edbm_bevel_cancel(C, op);
321                 return OPERATOR_CANCELLED;
322         }
323
324         edbm_bevel_exit(C, op);
325
326         return OPERATOR_FINISHED;
327 }
328
329 static void edbm_bevel_calc_initial_length(wmOperator *op, const wmEvent *event, bool mode_changed)
330 {
331         BevelData *opdata;
332         float mlen[2], len, value, sc, st;
333         int vmode;
334
335         opdata = op->customdata;
336         mlen[0] = opdata->mcenter[0] - event->mval[0];
337         mlen[1] = opdata->mcenter[1] - event->mval[1];
338         len = len_v2(mlen);
339         vmode = opdata->value_mode;
340         if (mode_changed || opdata->initial_length[vmode] == -1.0f) {
341                 /* If current value is not default start value, adjust len so that
342                  * the scaling and offset in edbm_bevel_mouse_set_value will
343                  * start at current value */
344                 value = (vmode == SEGMENTS_VALUE) ?
345                         opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
346                 sc = opdata->scale[vmode];
347                 st = value_start[vmode];
348                 if (value != value_start[vmode]) {
349                         len = (st + sc * (len - MVAL_PIXEL_MARGIN) - value) / sc;
350                 }
351         }
352         opdata->initial_length[opdata->value_mode] = len;
353 }
354
355 static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
356 {
357         /* TODO make modal keymap (see fly mode) */
358         RegionView3D *rv3d = CTX_wm_region_view3d(C);
359         BevelData *opdata;
360         float center_3d[3];
361
362         if (!edbm_bevel_init(C, op, true)) {
363                 return OPERATOR_CANCELLED;
364         }
365
366         opdata = op->customdata;
367
368         /* initialize mouse values */
369         if (!calculateTransformCenter(C, V3D_AROUND_CENTER_MEAN, center_3d, opdata->mcenter)) {
370                 /* in this case the tool will likely do nothing,
371                  * ideally this will never happen and should be checked for above */
372                 opdata->mcenter[0] = opdata->mcenter[1] = 0;
373         }
374
375         /* for OFFSET_VALUE only, the scale is the size of a pixel under the mouse in 3d space */
376         opdata->scale[OFFSET_VALUE] = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f;
377
378         edbm_bevel_calc_initial_length(op, event, false);
379
380         edbm_bevel_update_header(C, op);
381
382         if (!edbm_bevel_calc(op)) {
383                 edbm_bevel_cancel(C, op);
384                 return OPERATOR_CANCELLED;
385         }
386
387         WM_event_add_modal_handler(C, op);
388
389         return OPERATOR_RUNNING_MODAL;
390 }
391
392 static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event)
393 {
394         BevelData *opdata = op->customdata;
395         int vmode = opdata->value_mode;
396         float mdiff[2];
397         float value;
398
399         mdiff[0] = opdata->mcenter[0] - event->mval[0];
400         mdiff[1] = opdata->mcenter[1] - event->mval[1];
401
402         value = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length[vmode]);
403
404         /* Scale according to value mode */
405         value = value_start[vmode] + value * opdata->scale[vmode];
406
407         /* Fake shift-transform... */
408         if (event->shift) {
409                 if (opdata->shift_value[vmode] < 0.0f) {
410                         opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ?
411                                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
412                 }
413                 value = (value - opdata->shift_value[vmode]) * 0.1f + opdata->shift_value[vmode];
414         }
415         else if (opdata->shift_value[vmode] >= 0.0f) {
416                 opdata->shift_value[vmode] = -1.0f;
417         }
418
419         /* clamp accordingto value mode, and store value back */
420         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
421         if (vmode == SEGMENTS_VALUE) {
422                 opdata->segments = value;
423                 RNA_int_set(op->ptr, "segments", (int)(value + 0.5f));
424         }
425         else {
426                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
427         }
428 }
429
430 static void edbm_bevel_numinput_set_value(wmOperator *op)
431 {
432         BevelData *opdata = op->customdata;
433         float value;
434         int vmode;
435
436         vmode = opdata->value_mode;
437         value = (vmode == SEGMENTS_VALUE) ?
438                 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
439         applyNumInput(&opdata->num_input[vmode], &value);
440         CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
441         if (vmode == SEGMENTS_VALUE) {
442                 opdata->segments = value;
443                 RNA_int_set(op->ptr, "segments", (int)value);
444         }
445         else {
446                 RNA_float_set(op->ptr, value_rna_name[vmode], value);
447         }
448 }
449
450 static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
451 {
452         BevelData *opdata = op->customdata;
453         const bool has_numinput = hasNumInput(&opdata->num_input[opdata->value_mode]);
454
455         /* Modal numinput active, try to handle numeric inputs first... */
456         if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
457                 edbm_bevel_numinput_set_value(op);
458                 edbm_bevel_calc(op);
459                 edbm_bevel_update_header(C, op);
460                 return OPERATOR_RUNNING_MODAL;
461         }
462         else {
463                 bool handled = false;
464                 switch (event->type) {
465                         case ESCKEY:
466                         case RIGHTMOUSE:
467                                 edbm_bevel_cancel(C, op);
468                                 return OPERATOR_CANCELLED;
469
470                         case MOUSEMOVE:
471                                 if (!has_numinput) {
472                                         edbm_bevel_mouse_set_value(op, event);
473                                         edbm_bevel_calc(op);
474                                         edbm_bevel_update_header(C, op);
475                                         handled = true;
476                                 }
477                                 break;
478
479                         case LEFTMOUSE:
480                         case PADENTER:
481                         case RETKEY:
482                                 if (event->val == KM_PRESS) {
483                                         edbm_bevel_calc(op);
484                                         edbm_bevel_exit(C, op);
485                                         return OPERATOR_FINISHED;
486                                 }
487                                 break;
488
489                         case MOUSEPAN: {
490                                 float delta = 0.02f * (event->y - event->prevy);
491                                 if (opdata->segments >= 1 && opdata->segments + delta < 1)
492                                         opdata->segments = 1;
493                                 else
494                                         opdata->segments += delta;
495                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
496                                 edbm_bevel_calc(op);
497                                 edbm_bevel_update_header(C, op);
498                                 handled = true;
499                                 break;
500                         }
501
502                         /* Note this will prevent padplus and padminus to ever activate modal numinput.
503                          * This is not really an issue though, as we only expect positive values here...
504                          * Else we could force them to only modify segments number when shift is pressed, or so.
505                          */
506
507                         case WHEELUPMOUSE:  /* change number of segments */
508                         case PADPLUSKEY:
509                                 if (event->val == KM_RELEASE)
510                                         break;
511
512                                 opdata->segments = opdata->segments + 1;
513                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
514                                 edbm_bevel_calc(op);
515                                 edbm_bevel_update_header(C, op);
516                                 handled = true;
517                                 break;
518
519                         case WHEELDOWNMOUSE:  /* change number of segments */
520                         case PADMINUS:
521                                 if (event->val == KM_RELEASE)
522                                         break;
523
524                                 opdata->segments = max_ff(opdata->segments - 1, 1);
525                                 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
526                                 edbm_bevel_calc(op);
527                                 edbm_bevel_update_header(C, op);
528                                 handled = true;
529                                 break;
530
531                         case MKEY:
532                                 if (event->val == KM_RELEASE)
533                                         break;
534
535                                 {
536                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type");
537                                         int type = RNA_property_enum_get(op->ptr, prop);
538                                         type++;
539                                         if (type > BEVEL_AMT_PERCENT) {
540                                                 type = BEVEL_AMT_OFFSET;
541                                         }
542                                         if (opdata->value_mode == OFFSET_VALUE && type == BEVEL_AMT_PERCENT)
543                                                 opdata->value_mode = OFFSET_VALUE_PERCENT;
544                                         else if (opdata->value_mode == OFFSET_VALUE_PERCENT && type != BEVEL_AMT_PERCENT)
545                                                 opdata->value_mode = OFFSET_VALUE;
546                                         RNA_property_enum_set(op->ptr, prop, type);
547                                         if (opdata->initial_length[opdata->value_mode] == -1.0f)
548                                                 edbm_bevel_calc_initial_length(op, event, true);
549                                 }
550                                 /* Update offset accordingly to new offset_type. */
551                                 if (!has_numinput &&
552                                     (opdata->value_mode == OFFSET_VALUE || opdata->value_mode == OFFSET_VALUE_PERCENT))
553                                 {
554                                         edbm_bevel_mouse_set_value(op, event);
555                                 }
556                                 edbm_bevel_calc(op);
557                                 edbm_bevel_update_header(C, op);
558                                 handled = true;
559                                 break;
560                         case CKEY:
561                                 if (event->val == KM_RELEASE)
562                                         break;
563
564                                 {
565                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "clamp_overlap");
566                                         RNA_property_boolean_set(op->ptr, prop, !RNA_property_boolean_get(op->ptr, prop));
567                                 }
568                                 edbm_bevel_calc(op);
569                                 edbm_bevel_update_header(C, op);
570                                 handled = true;
571                                 break;
572                         case PKEY:
573                                 if (event->val == KM_RELEASE)
574                                         break;
575                                 if (opdata->value_mode == PROFILE_VALUE) {
576                                         opdata->value_mode = OFFSET_VALUE;
577                                 }
578                                 else {
579                                         opdata->value_mode = PROFILE_VALUE;
580                                 }
581                                 edbm_bevel_calc_initial_length(op, event, true);
582                                 break;
583                         case SKEY:
584                                 if (event->val == KM_RELEASE)
585                                         break;
586                                 if (opdata->value_mode == SEGMENTS_VALUE) {
587                                         opdata->value_mode = OFFSET_VALUE;
588                                 }
589                                 else {
590                                         opdata->value_mode = SEGMENTS_VALUE;
591                                 }
592                                 edbm_bevel_calc_initial_length(op, event, true);
593                                 break;
594                         case VKEY:
595                                 if (event->val == KM_RELEASE)
596                                         break;
597
598                                 {
599                                         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "vertex_only");
600                                         RNA_property_boolean_set(op->ptr, prop, !RNA_property_boolean_get(op->ptr, prop));
601                                 }
602                                 edbm_bevel_calc(op);
603                                 edbm_bevel_update_header(C, op);
604                                 handled = true;
605                                 break;
606
607                 }
608
609                 /* Modal numinput inactive, try to handle numeric inputs last... */
610                 if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
611                         edbm_bevel_numinput_set_value(op);
612                         edbm_bevel_calc(op);
613                         edbm_bevel_update_header(C, op);
614                         return OPERATOR_RUNNING_MODAL;
615                 }
616         }
617
618         return OPERATOR_RUNNING_MODAL;
619 }
620
621 static void mesh_ot_bevel_offset_range_func(PointerRNA *ptr, PropertyRNA *UNUSED(prop),
622                                             float *min, float *max, float *softmin, float *softmax)
623 {
624         const int offset_type = RNA_enum_get(ptr, "offset_type");
625
626         *min = -FLT_MAX;
627         *max = FLT_MAX;
628         *softmin = 0.0f;
629         *softmax = (offset_type == BEVEL_AMT_PERCENT) ? 100.0f : 1.0f;
630 }
631
632 void MESH_OT_bevel(wmOperatorType *ot)
633 {
634         PropertyRNA *prop;
635
636         static const EnumPropertyItem offset_type_items[] = {
637                 {BEVEL_AMT_OFFSET, "OFFSET", 0, "Offset", "Amount is offset of new edges from original"},
638                 {BEVEL_AMT_WIDTH, "WIDTH", 0, "Width", "Amount is width of new face"},
639                 {BEVEL_AMT_DEPTH, "DEPTH", 0, "Depth", "Amount is perpendicular distance from original edge to bevel face"},
640                 {BEVEL_AMT_PERCENT, "PERCENT", 0, "Percent", "Amount is percent of adjacent edge length"},
641                 {0, NULL, 0, NULL, NULL},
642         };
643
644         /* identifiers */
645         ot->name = "Bevel";
646         ot->description = "Edge Bevel";
647         ot->idname = "MESH_OT_bevel";
648
649         /* api callbacks */
650         ot->exec = edbm_bevel_exec;
651         ot->invoke = edbm_bevel_invoke;
652         ot->modal = edbm_bevel_modal;
653         ot->cancel = edbm_bevel_cancel;
654         ot->poll = ED_operator_editmesh;
655
656         /* flags */
657         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
658
659         RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures");
660         prop = RNA_def_float(ot->srna, "offset", 0.0f, -1e6f, 1e6f, "Amount", "", 0.0f, 1.0f);
661         RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func);
662         RNA_def_int(ot->srna, "segments", 1, 1, SEGMENTS_HARD_MAX, "Segments", "Segments for curved edge", 1, 8);
663         RNA_def_float(ot->srna, "profile", 0.5f, PROFILE_HARD_MIN, 1.0f, "Profile",
664                 "Controls profile shape (0.5 = round)", PROFILE_HARD_MIN, 1.0f);
665         RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices");
666         RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap",
667                 "Do not allow beveled edges/vertices to overlap each other");
668         RNA_def_boolean(ot->srna, "loop_slide", true, "Loop Slide", "Prefer slide along edge to even widths");
669         RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material",
670                 "Material for bevel faces (-1 means use adjacent faces)", -1, 100);
671 }