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.
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.
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.
17 /** \file \ingroup edmesh
20 #include "MEM_guardedalloc.h"
22 #include "DNA_object_types.h"
24 #include "BLI_string.h"
27 #include "BLT_translation.h"
29 #include "BKE_context.h"
30 #include "BKE_global.h"
31 #include "BKE_editmesh.h"
33 #include "BKE_layer.h"
36 #include "DNA_mesh_types.h"
38 #include "RNA_define.h"
39 #include "RNA_access.h"
44 #include "UI_interface.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"
56 #include "mesh_intern.h" /* own include */
59 #define MVAL_PIXEL_MARGIN 5.0f
61 #define PROFILE_HARD_MIN 0.0f
63 #define SEGMENTS_HARD_MAX 1000
65 /* which value is mouse movement and numeric input controlling? */
66 #define OFFSET_VALUE 0
67 #define OFFSET_VALUE_PERCENT 1
68 #define PROFILE_VALUE 2
69 #define SEGMENTS_VALUE 3
70 #define NUM_VALUE_KINDS 4
72 static const char *value_rna_name[NUM_VALUE_KINDS] = {"offset", "offset", "profile", "segments"};
73 static const float value_clamp_min[NUM_VALUE_KINDS] = {0.0f, 0.0f, PROFILE_HARD_MIN, 1.0f};
74 static const float value_clamp_max[NUM_VALUE_KINDS] = {1e6, 100.0f, 1.0f, SEGMENTS_HARD_MAX};
75 static const float value_start[NUM_VALUE_KINDS] = {0.0f, 0.0f, 0.5f, 1.0f};
76 static const float value_scale_per_inch[NUM_VALUE_KINDS] = { 0.0f, 100.0f, 1.0f, 4.0f};
85 float initial_length[NUM_VALUE_KINDS];
86 float scale[NUM_VALUE_KINDS];
87 NumInput num_input[NUM_VALUE_KINDS];
88 /** The current value when shift is pressed. Negative when shift not active. */
89 float shift_value[NUM_VALUE_KINDS];
92 BevelObjectStore *ob_store;
97 void *draw_handle_pixel;
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 */
104 BEV_MODAL_CANCEL = 1,
106 BEV_MODAL_VALUE_OFFSET,
107 BEV_MODAL_VALUE_PROFILE,
108 BEV_MODAL_VALUE_SEGMENTS,
109 BEV_MODAL_SEGMENTS_UP,
110 BEV_MODAL_SEGMENTS_DOWN,
111 BEV_MODAL_OFFSET_MODE_CHANGE,
112 BEV_MODAL_CLAMP_OVERLAP_TOGGLE,
113 BEV_MODAL_VERTEX_ONLY_TOGGLE,
114 BEV_MODAL_HARDEN_NORMALS_TOGGLE,
115 BEV_MODAL_MARK_SEAM_TOGGLE,
116 BEV_MODAL_MARK_SHARP_TOGGLE,
117 BEV_MODAL_OUTER_MITER_CHANGE,
118 BEV_MODAL_INNER_MITER_CHANGE,
121 static void edbm_bevel_update_header(bContext *C, wmOperator *op)
123 char header[UI_MAX_DRAW_STR];
124 char buf[UI_MAX_DRAW_STR];
126 int available_len = sizeof(buf);
127 Scene *sce = CTX_data_scene(C);
128 BevelData *opdata = op->customdata;
129 char offset_str[NUM_STR_REP_LEN];
130 const char *mode_str, *omiter_str, *imiter_str;
133 #define WM_MODALKEY(_id) \
134 WM_modalkeymap_operator_items_to_string_buf(op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
136 if (hasNumInput(&opdata->num_input[OFFSET_VALUE])) {
137 outputNumInput(&opdata->num_input[OFFSET_VALUE], offset_str, &sce->unit);
140 BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset"));
143 prop = RNA_struct_find_property(op->ptr, "offset_type");
144 RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &mode_str);
145 prop = RNA_struct_find_property(op->ptr, "miter_outer");
146 RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &omiter_str);
147 prop = RNA_struct_find_property(op->ptr, "miter_inner");
148 RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &imiter_str);
150 BLI_snprintf(header, sizeof(header),
151 IFACE_("%s: confirm, "
155 "%s: segments (%d), "
156 "%s: profile (%.3f), "
157 "%s: clamp overlap (%s), "
158 "%s: vertex only (%s), "
159 "%s: outer miter (%s), "
160 "%s: inner imter (%s), "
161 "%s: harden normals (%s), "
162 "%s: mark seam (%s), "
163 "%s: mark sharp (%s)"
165 WM_MODALKEY(BEV_MODAL_CONFIRM),
166 WM_MODALKEY(BEV_MODAL_CANCEL),
167 WM_MODALKEY(BEV_MODAL_OFFSET_MODE_CHANGE),
169 WM_MODALKEY(BEV_MODAL_VALUE_OFFSET),
171 WM_MODALKEY(BEV_MODAL_VALUE_SEGMENTS),
172 RNA_int_get(op->ptr, "segments"),
173 WM_MODALKEY(BEV_MODAL_VALUE_PROFILE),
174 RNA_float_get(op->ptr, "profile"),
175 WM_MODALKEY(BEV_MODAL_CLAMP_OVERLAP_TOGGLE),
176 WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")),
177 WM_MODALKEY(BEV_MODAL_VERTEX_ONLY_TOGGLE),
178 WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")),
179 WM_MODALKEY(BEV_MODAL_OUTER_MITER_CHANGE),
181 WM_MODALKEY(BEV_MODAL_INNER_MITER_CHANGE),
183 WM_MODALKEY(BEV_MODAL_HARDEN_NORMALS_TOGGLE),
184 WM_bool_as_string(RNA_boolean_get(op->ptr, "harden_normals")),
185 WM_MODALKEY(BEV_MODAL_MARK_SEAM_TOGGLE),
186 WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_seam")),
187 WM_MODALKEY(BEV_MODAL_MARK_SHARP_TOGGLE),
188 WM_bool_as_string(RNA_boolean_get(op->ptr, "mark_sharp"))
193 ED_workspace_status_text(C, header);
196 static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
198 Scene *scene = CTX_data_scene(C);
200 ViewLayer *view_layer = CTX_data_view_layer(C);
201 float pixels_per_inch;
205 RNA_float_set(op->ptr, "offset", 0.0f);
208 op->customdata = opdata = MEM_mallocN(sizeof(BevelData), "beveldata_mesh_operator");
209 uint objects_used_len = 0;
212 uint ob_store_len = 0;
213 Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
214 view_layer, CTX_wm_view3d(C), &ob_store_len);
215 opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
216 for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
217 Object *obedit = objects[ob_index];
218 BMEditMesh *em = BKE_editmesh_from_object(obedit);
219 if (em->bm->totvertsel > 0) {
220 opdata->ob_store[objects_used_len].em = em;
225 opdata->ob_store_len = objects_used_len;
228 opdata->is_modal = is_modal;
229 opdata->value_mode = OFFSET_VALUE;
230 opdata->segments = (float) RNA_int_get(op->ptr, "segments");
231 pixels_per_inch = U.dpi * U.pixelsize;
233 for (i = 0; i < NUM_VALUE_KINDS; i++) {
234 opdata->shift_value[i] = -1.0f;
235 opdata->initial_length[i] = -1.0f;
236 /* note: scale for OFFSET_VALUE will get overwritten in edbm_bevel_invoke */
237 opdata->scale[i] = value_scale_per_inch[i] / pixels_per_inch;
239 initNumInput(&opdata->num_input[i]);
240 opdata->num_input[i].idx_max = 0;
241 opdata->num_input[i].val_flag[0] |= NUM_NO_NEGATIVE;
242 if (i == SEGMENTS_VALUE) {
243 opdata->num_input[i].val_flag[0] |= NUM_NO_FRACTION | NUM_NO_ZERO;
245 if (i == OFFSET_VALUE) {
246 opdata->num_input[i].unit_sys = scene->unit.system;
248 /* Not sure this is a factor or a unit? */
249 opdata->num_input[i].unit_type[0] = B_UNIT_NONE;
252 /* avoid the cost of allocating a bm copy */
254 View3D *v3d = CTX_wm_view3d(C);
255 ARegion *ar = CTX_wm_region(C);
257 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
258 opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(opdata->ob_store[ob_index].em);
260 opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb,
261 opdata->mcenter, REGION_DRAW_POST_PIXEL);
262 G.moving = G_TRANSFORM_EDIT;
265 opdata->gizmo_flag = v3d->gizmo_flag;
266 v3d->gizmo_flag = V3D_GIZMO_HIDE;
273 static bool edbm_bevel_calc(wmOperator *op)
275 BevelData *opdata = op->customdata;
278 bool changed = false;
280 const float offset = RNA_float_get(op->ptr, "offset");
281 const int offset_type = RNA_enum_get(op->ptr, "offset_type");
282 const int segments = RNA_int_get(op->ptr, "segments");
283 const float profile = RNA_float_get(op->ptr, "profile");
284 const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
285 const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
286 int material = RNA_int_get(op->ptr, "material");
287 const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide");
288 const bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
289 const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
290 const bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
291 const int face_strength_mode = RNA_enum_get(op->ptr, "face_strength_mode");
292 const int miter_outer = RNA_enum_get(op->ptr, "miter_outer");
293 const int miter_inner = RNA_enum_get(op->ptr, "miter_inner");
294 const float spread = RNA_float_get(op->ptr, "spread");
296 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
297 em = opdata->ob_store[ob_index].em;
299 /* revert to original mesh */
300 if (opdata->is_modal) {
301 EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false);
305 material = CLAMPIS(material, -1, em->ob->totcol - 1);
308 Mesh *me = em->ob->data;
310 if (harden_normals && !(me->flag & ME_AUTOSMOOTH)) {
311 /* harden_normals only has a visible effect if autosmooth is on, so turn it on */
312 me->flag |= ME_AUTOSMOOTH;
317 "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f "
318 "clamp_overlap=%b material=%i loop_slide=%b mark_seam=%b mark_sharp=%b "
319 "harden_normals=%b face_strength_mode=%i "
320 "miter_outer=%i miter_inner=%i spread=%f smoothresh=%f",
321 BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile,
322 clamp_overlap, material, loop_slide, mark_seam, mark_sharp, harden_normals, face_strength_mode,
323 miter_outer, miter_inner, spread, me->smoothresh);
325 BMO_op_exec(em->bm, &bmop);
327 if (offset != 0.0f) {
328 /* not essential, but we may have some loose geometry that
329 * won't get bevel'd and better not leave it selected */
330 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
331 BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
334 /* no need to de-select existing geometry */
335 if (!EDBM_op_finish(em, &bmop, op, true)) {
339 EDBM_mesh_normals_update(em);
341 EDBM_update_generic(em, true, true);
347 static void edbm_bevel_exit(bContext *C, wmOperator *op)
349 BevelData *opdata = op->customdata;
351 ScrArea *sa = CTX_wm_area(C);
354 ED_area_status_text(sa, NULL);
357 if (opdata->is_modal) {
358 View3D *v3d = CTX_wm_view3d(C);
359 ARegion *ar = CTX_wm_region(C);
360 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
361 EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, NULL, false);
363 ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel);
365 v3d->gizmo_flag = opdata->gizmo_flag;
369 MEM_SAFE_FREE(opdata->ob_store);
370 MEM_SAFE_FREE(op->customdata);
371 op->customdata = NULL;
374 static void edbm_bevel_cancel(bContext *C, wmOperator *op)
376 BevelData *opdata = op->customdata;
377 if (opdata->is_modal) {
378 for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) {
379 EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true);
380 EDBM_update_generic(opdata->ob_store[ob_index].em, false, true);
384 edbm_bevel_exit(C, op);
386 /* need to force redisplay or we may still view the modified result */
387 ED_region_tag_redraw(CTX_wm_region(C));
391 static int edbm_bevel_exec(bContext *C, wmOperator *op)
393 if (!edbm_bevel_init(C, op, false)) {
394 return OPERATOR_CANCELLED;
397 if (!edbm_bevel_calc(op)) {
398 edbm_bevel_cancel(C, op);
399 return OPERATOR_CANCELLED;
402 edbm_bevel_exit(C, op);
404 return OPERATOR_FINISHED;
407 static void edbm_bevel_calc_initial_length(wmOperator *op, const wmEvent *event, bool mode_changed)
410 float mlen[2], len, value, sc, st;
413 opdata = op->customdata;
414 mlen[0] = opdata->mcenter[0] - event->mval[0];
415 mlen[1] = opdata->mcenter[1] - event->mval[1];
417 vmode = opdata->value_mode;
418 if (mode_changed || opdata->initial_length[vmode] == -1.0f) {
419 /* If current value is not default start value, adjust len so that
420 * the scaling and offset in edbm_bevel_mouse_set_value will
421 * start at current value */
422 value = (vmode == SEGMENTS_VALUE) ?
423 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
424 sc = opdata->scale[vmode];
425 st = value_start[vmode];
426 if (value != value_start[vmode]) {
427 len = (st + sc * (len - MVAL_PIXEL_MARGIN) - value) / sc;
430 opdata->initial_length[opdata->value_mode] = len;
433 static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
435 /* TODO make modal keymap (see fly mode) */
436 RegionView3D *rv3d = CTX_wm_region_view3d(C);
440 if (!edbm_bevel_init(C, op, true)) {
441 return OPERATOR_CANCELLED;
444 opdata = op->customdata;
446 /* initialize mouse values */
447 if (!calculateTransformCenter(C, V3D_AROUND_CENTER_MEDIAN, center_3d, opdata->mcenter)) {
448 /* in this case the tool will likely do nothing,
449 * ideally this will never happen and should be checked for above */
450 opdata->mcenter[0] = opdata->mcenter[1] = 0;
453 /* for OFFSET_VALUE only, the scale is the size of a pixel under the mouse in 3d space */
454 opdata->scale[OFFSET_VALUE] = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f;
456 edbm_bevel_calc_initial_length(op, event, false);
458 edbm_bevel_update_header(C, op);
460 if (!edbm_bevel_calc(op)) {
461 edbm_bevel_cancel(C, op);
462 return OPERATOR_CANCELLED;
465 WM_event_add_modal_handler(C, op);
467 return OPERATOR_RUNNING_MODAL;
470 static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event)
472 BevelData *opdata = op->customdata;
473 int vmode = opdata->value_mode;
477 mdiff[0] = opdata->mcenter[0] - event->mval[0];
478 mdiff[1] = opdata->mcenter[1] - event->mval[1];
480 value = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length[vmode]);
482 /* Scale according to value mode */
483 value = value_start[vmode] + value * opdata->scale[vmode];
485 /* Fake shift-transform... */
487 if (opdata->shift_value[vmode] < 0.0f) {
488 opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ?
489 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
491 value = (value - opdata->shift_value[vmode]) * 0.1f + opdata->shift_value[vmode];
493 else if (opdata->shift_value[vmode] >= 0.0f) {
494 opdata->shift_value[vmode] = -1.0f;
497 /* clamp accordingto value mode, and store value back */
498 CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
499 if (vmode == SEGMENTS_VALUE) {
500 opdata->segments = value;
501 RNA_int_set(op->ptr, "segments", (int)(value + 0.5f));
504 RNA_float_set(op->ptr, value_rna_name[vmode], value);
508 static void edbm_bevel_numinput_set_value(wmOperator *op)
510 BevelData *opdata = op->customdata;
514 vmode = opdata->value_mode;
515 value = (vmode == SEGMENTS_VALUE) ?
516 opdata->segments : RNA_float_get(op->ptr, value_rna_name[vmode]);
517 applyNumInput(&opdata->num_input[vmode], &value);
518 CLAMP(value, value_clamp_min[vmode], value_clamp_max[vmode]);
519 if (vmode == SEGMENTS_VALUE) {
520 opdata->segments = value;
521 RNA_int_set(op->ptr, "segments", (int)value);
524 RNA_float_set(op->ptr, value_rna_name[vmode], value);
528 wmKeyMap *bevel_modal_keymap(wmKeyConfig *keyconf)
530 static const EnumPropertyItem modal_items[] = {
531 {BEV_MODAL_CANCEL, "CANCEL", 0, "Cancel", "Cancel bevel"},
532 {BEV_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", "Confirm bevel"},
533 {BEV_MODAL_VALUE_OFFSET, "VALUE_OFFSET", 0, "Value is offset",
534 "Value changes offset"},
535 {BEV_MODAL_VALUE_PROFILE, "VALUE_PROFILE", 0, "Value is profile",
536 "Value changes profile"},
537 {BEV_MODAL_VALUE_SEGMENTS, "VALUE_SEGMENTS", 0, "Value is segments",
538 "Value changes segments"},
539 {BEV_MODAL_SEGMENTS_UP, "SEGMENTS_UP", 0, "Increase segments",
540 "Increase segments"},
541 {BEV_MODAL_SEGMENTS_DOWN, "SEGMENTS_DOWN", 0, "Decrease segments",
542 "Decrease segments"},
543 {BEV_MODAL_OFFSET_MODE_CHANGE, "OFFSET_MODE_CHANGE", 0, "Change offset mode",
544 "Cycle through offset modes"},
545 {BEV_MODAL_CLAMP_OVERLAP_TOGGLE, "CLAMP_OVERLAP_TOGGLE", 0, "Toggle clamp overlap",
546 "Toggle clamp overlap flag"},
547 {BEV_MODAL_VERTEX_ONLY_TOGGLE, "VERTEX_ONLY_TOGGLE", 0, "Toggle vertex only",
548 "Toggle vertex only flag"},
549 {BEV_MODAL_HARDEN_NORMALS_TOGGLE, "HARDEN_NORMALS_TOGGLE", 0, "Toggle harden normals",
550 "Toggle harden normals flag"},
551 {BEV_MODAL_MARK_SEAM_TOGGLE, "MARK_SEAM_TOGGLE", 0, "Toggle mark seam",
552 "Toggle mark seam flag"},
553 {BEV_MODAL_MARK_SHARP_TOGGLE, "MARK_SHARP_TOGGLE", 0, "Toggle mark sharp",
554 "Toggle mark sharp flag"},
555 {BEV_MODAL_OUTER_MITER_CHANGE, "OUTER_MITER_CHANGE", 0, "Change outer miter",
556 "Cycle through outer miter kinds"},
557 {BEV_MODAL_INNER_MITER_CHANGE, "INNER_MITER_CHANGE", 0, "Change inner miter",
558 "Cycle through inner miter kinds"},
559 {0, NULL, 0, NULL, NULL},
562 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Bevel Modal Map");
564 /* this function is called for each spacetype, only needs to add map once */
565 if (keymap && keymap->modal_items)
568 keymap = WM_modalkeymap_add(keyconf, "Bevel Modal Map", modal_items);
570 WM_modalkeymap_assign(keymap, "MESH_OT_bevel");
575 static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
577 BevelData *opdata = op->customdata;
578 const bool has_numinput = hasNumInput(&opdata->num_input[opdata->value_mode]);
579 bool handled = false;
581 /* Modal numinput active, try to handle numeric inputs first... */
582 if (event->type != EVT_MODAL_MAP && event->val == KM_PRESS && has_numinput && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
583 edbm_bevel_numinput_set_value(op);
585 edbm_bevel_update_header(C, op);
586 return OPERATOR_RUNNING_MODAL;
588 else if (event->type == MOUSEMOVE) {
590 edbm_bevel_mouse_set_value(op, event);
592 edbm_bevel_update_header(C, op);
596 else if (event->type == MOUSEPAN) {
597 float delta = 0.02f * (event->y - event->prevy);
598 if (opdata->segments >= 1 && opdata->segments + delta < 1)
599 opdata->segments = 1;
601 opdata->segments += delta;
602 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
604 edbm_bevel_update_header(C, op);
607 else if (event->type == EVT_MODAL_MAP){
608 switch (event->val) {
609 case BEV_MODAL_CANCEL:
610 edbm_bevel_cancel(C, op);
611 return OPERATOR_CANCELLED;
613 case BEV_MODAL_CONFIRM:
615 if ((event->val == KM_PRESS) ||
616 ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm")))
620 edbm_bevel_exit(C, op);
621 return OPERATOR_FINISHED;
625 case BEV_MODAL_SEGMENTS_UP:
626 opdata->segments = opdata->segments + 1;
627 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
629 edbm_bevel_update_header(C, op);
633 case BEV_MODAL_SEGMENTS_DOWN:
634 opdata->segments = max_ff(opdata->segments - 1, 1);
635 RNA_int_set(op->ptr, "segments", (int)opdata->segments);
637 edbm_bevel_update_header(C, op);
641 case BEV_MODAL_OFFSET_MODE_CHANGE:
643 int type = RNA_enum_get(op->ptr, "offset_type");
645 if (type > BEVEL_AMT_PERCENT) {
646 type = BEVEL_AMT_OFFSET;
648 if (opdata->value_mode == OFFSET_VALUE && type == BEVEL_AMT_PERCENT)
649 opdata->value_mode = OFFSET_VALUE_PERCENT;
650 else if (opdata->value_mode == OFFSET_VALUE_PERCENT && type != BEVEL_AMT_PERCENT)
651 opdata->value_mode = OFFSET_VALUE;
652 RNA_enum_set(op->ptr, "offset_type", type);
653 if (opdata->initial_length[opdata->value_mode] == -1.0f)
654 edbm_bevel_calc_initial_length(op, event, true);
656 /* Update offset accordingly to new offset_type. */
658 (opdata->value_mode == OFFSET_VALUE || opdata->value_mode == OFFSET_VALUE_PERCENT))
660 edbm_bevel_mouse_set_value(op, event);
663 edbm_bevel_update_header(C, op);
667 case BEV_MODAL_CLAMP_OVERLAP_TOGGLE:
669 bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap");
670 RNA_boolean_set(op->ptr, "clamp_overlap", !clamp_overlap);
672 edbm_bevel_update_header(C, op);
677 case BEV_MODAL_VALUE_OFFSET:
678 opdata->value_mode = OFFSET_VALUE;
679 edbm_bevel_calc_initial_length(op, event, true);
682 case BEV_MODAL_VALUE_PROFILE:
683 opdata->value_mode = PROFILE_VALUE;
684 edbm_bevel_calc_initial_length(op, event, true);
687 case BEV_MODAL_VALUE_SEGMENTS:
688 opdata->value_mode = SEGMENTS_VALUE;
689 edbm_bevel_calc_initial_length(op, event, true);
692 case BEV_MODAL_VERTEX_ONLY_TOGGLE:
694 bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only");
695 RNA_boolean_set(op->ptr, "vertex_only", !vertex_only);
697 edbm_bevel_update_header(C, op);
702 case BEV_MODAL_MARK_SEAM_TOGGLE:
704 bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam");
705 RNA_boolean_set(op->ptr, "mark_seam", !mark_seam);
707 edbm_bevel_update_header(C, op);
712 case BEV_MODAL_MARK_SHARP_TOGGLE:
714 bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
715 RNA_boolean_set(op->ptr, "mark_sharp", !mark_sharp);
717 edbm_bevel_update_header(C, op);
722 case BEV_MODAL_INNER_MITER_CHANGE:
724 int miter_inner = RNA_enum_get(op->ptr, "miter_inner");
726 if (miter_inner == BEVEL_MITER_PATCH)
727 miter_inner++; /* no patch option for inner miter */
728 if (miter_inner > BEVEL_MITER_ARC)
729 miter_inner = BEVEL_MITER_SHARP;
730 RNA_enum_set(op->ptr, "miter_inner", miter_inner);
732 edbm_bevel_update_header(C, op);
737 case BEV_MODAL_OUTER_MITER_CHANGE:
739 int miter_outer = RNA_enum_get(op->ptr, "miter_outer");
741 if (miter_outer > BEVEL_MITER_ARC)
742 miter_outer = BEVEL_MITER_SHARP;
743 RNA_enum_set(op->ptr, "miter_outer", miter_outer);
745 edbm_bevel_update_header(C, op);
750 case BEV_MODAL_HARDEN_NORMALS_TOGGLE:
752 bool harden_normals = RNA_boolean_get(op->ptr, "harden_normals");
753 RNA_boolean_set(op->ptr, "harden_normals", !harden_normals);
755 edbm_bevel_update_header(C, op);
762 /* Modal numinput inactive, try to handle numeric inputs last... */
763 if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input[opdata->value_mode], event)) {
764 edbm_bevel_numinput_set_value(op);
766 edbm_bevel_update_header(C, op);
767 return OPERATOR_RUNNING_MODAL;
770 return OPERATOR_RUNNING_MODAL;
773 static void mesh_ot_bevel_offset_range_func(PointerRNA *ptr, PropertyRNA *UNUSED(prop),
774 float *min, float *max, float *softmin, float *softmax)
776 const int offset_type = RNA_enum_get(ptr, "offset_type");
781 *softmax = (offset_type == BEVEL_AMT_PERCENT) ? 100.0f : 1.0f;
784 void MESH_OT_bevel(wmOperatorType *ot)
788 static const EnumPropertyItem offset_type_items[] = {
789 {BEVEL_AMT_OFFSET, "OFFSET", 0, "Offset", "Amount is offset of new edges from original"},
790 {BEVEL_AMT_WIDTH, "WIDTH", 0, "Width", "Amount is width of new face"},
791 {BEVEL_AMT_DEPTH, "DEPTH", 0, "Depth", "Amount is perpendicular distance from original edge to bevel face"},
792 {BEVEL_AMT_PERCENT, "PERCENT", 0, "Percent", "Amount is percent of adjacent edge length"},
793 {0, NULL, 0, NULL, NULL},
796 static const EnumPropertyItem face_strength_mode_items[] = {
797 {BEVEL_FACE_STRENGTH_NONE, "NONE", 0, "None", "Do not set face strength"},
798 {BEVEL_FACE_STRENGTH_NEW, "NEW", 0, "New", "Set face strength on new faces only"},
799 {BEVEL_FACE_STRENGTH_AFFECTED, "AFFECTED", 0, "Affected", "Set face strength on new and modified faces only"},
800 {BEVEL_FACE_STRENGTH_ALL, "ALL", 0, "All", "Set face strength on all faces"},
801 {0, NULL, 0, NULL, NULL},
804 static const EnumPropertyItem miter_outer_items[] = {
805 {BEVEL_MITER_SHARP, "SHARP", 0, "Sharp", "Outside of miter is sharp"},
806 {BEVEL_MITER_PATCH, "PATCH", 0, "Patch", "Outside of miter is squared-off patch"},
807 {BEVEL_MITER_ARC, "ARC", 0, "Arc", "Outside of miter is arc"},
808 {0, NULL, 0, NULL, NULL},
811 static const EnumPropertyItem miter_inner_items[] = {
812 {BEVEL_MITER_SHARP, "SHARP", 0, "Sharp", "Inside of miter is sharp"},
813 {BEVEL_MITER_ARC, "ARC", 0, "Arc", "Inside of miter is arc"},
814 {0, NULL, 0, NULL, NULL},
819 ot->description = "Edge Bevel";
820 ot->idname = "MESH_OT_bevel";
823 ot->exec = edbm_bevel_exec;
824 ot->invoke = edbm_bevel_invoke;
825 ot->modal = edbm_bevel_modal;
826 ot->cancel = edbm_bevel_cancel;
827 ot->poll = ED_operator_editmesh;
830 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
832 RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures");
833 prop = RNA_def_float(ot->srna, "offset", 0.0f, -1e6f, 1e6f, "Amount", "", 0.0f, 100.0f);
834 RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func);
835 RNA_def_int(ot->srna, "segments", 1, 1, SEGMENTS_HARD_MAX, "Segments", "Segments for curved edge", 1, 8);
836 RNA_def_float(ot->srna, "profile", 0.5f, PROFILE_HARD_MIN, 1.0f, "Profile",
837 "Controls profile shape (0.5 = round)", PROFILE_HARD_MIN, 1.0f);
838 RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices");
839 RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap",
840 "Do not allow beveled edges/vertices to overlap each other");
841 RNA_def_boolean(ot->srna, "loop_slide", true, "Loop Slide", "Prefer slide along edge to even widths");
842 RNA_def_boolean(ot->srna, "mark_seam", false, "Mark Seams", "Mark Seams along beveled edges");
843 RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark beveled edges as sharp");
844 RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material",
845 "Material for bevel faces (-1 means use adjacent faces)", -1, 100);
846 RNA_def_boolean(ot->srna, "harden_normals", false, "Harden Normals",
847 "Match normals of new faces to adjacent faces");
848 RNA_def_enum(ot->srna, "face_strength_mode", face_strength_mode_items, BEVEL_FACE_STRENGTH_NONE,
849 "Face Strength Mode", "Whether to set face strength, and which faces to set face strength on");
850 RNA_def_enum(ot->srna, "miter_outer", miter_outer_items, BEVEL_MITER_SHARP,
851 "Outer Miter", "Pattern to use for outside of miters");
852 RNA_def_enum(ot->srna, "miter_inner", miter_inner_items, BEVEL_MITER_SHARP,
853 "Inner Miter", "Pattern to use for inside of miters");
854 RNA_def_float(ot->srna, "spread", 0.1f, 0.0f, 1e6f, "Spread",
855 "Amount to spread arcs for arc inner miters", 0.0f, 100.0f);
856 prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
857 RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);