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