diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index e028c08091c..cfb76fea66d 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -48,6 +48,23 @@ bool uvedit_face_visible_nolocal(struct Scene *scene, struct BMFace *efa); /* geometric utilities */ void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len); void uv_poly_center(struct BMFace *f, float r_cent[2], const int cd_loop_uv_offset); +float uv_poly_calc_area(const BMFace *f, const int cd_loop_uv_offset); + +/* + * extra face data (computed data) + * (copied form bmo_similar.c; need to find a cleaner solution) + */ +typedef struct SimSel_UVFaceExt { + BMFace *f; /* the face */ + float c[3]; /* center */ + bool selected; /* initially selected */ + union { + float area; /* area */ + float perim; /* perimeter */ + float d; /* 4th component of plane (the first three being the normal) */ + struct Image *t; /* image pointer */ + }; +} SimSel_UVFaceExt; /* find nearest */ @@ -80,5 +97,6 @@ void UV_OT_reset(struct wmOperatorType *ot); void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); void UV_OT_stitch(struct wmOperatorType *ot); +void UV_OT_select_similar(struct wmOperatorType *ot); #endif /* __UVEDIT_INTERN_H__ */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 193b006cf0d..2098880ff5c 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -640,6 +640,25 @@ void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float as } } +/* get the area of the UV */ +float uv_poly_calc_area(const BMFace *f, const int cd_loop_uv_offset) +{ + const BMLoop *l_iter, *l_first; + MLoopUV *luv1, *luv2; + float n; + + /* calculate area using 2D cross product (shortcut of Newell's Method) */ + n = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + luv1 = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + luv2 = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset); + n += cross_v2v2(luv1->uv, luv2->uv); + } while ((l_iter = l_iter->next) != l_first); + + return n * 0.5f; +} + bool ED_uvedit_minmax(Scene *scene, Image *ima, Object *obedit, float r_min[2], float r_max[2]) { BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -4229,6 +4248,261 @@ static void UV_OT_mark_seam(wmOperatorType *ot) RNA_def_boolean(ot->srna, "clear", false, "Clear Seams", "Clear instead of marking seams"); } +/* ************************** uv select similar operator ********************/ +/* **************** SIMILAR "group" SELECTS. UV FACE, EDGE AND VERTEX ******* */ +static EnumPropertyItem prop_similar_compare_types[] = { + {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""}, + {SIM_CMP_GT, "GREATER", 0, "Greater", ""}, + {SIM_CMP_LT, "LESS", 0, "Less", ""}, + + {0, NULL, 0, NULL, NULL} +}; + +/* for selecting simular UVs - This list might need be modified to make sense for UVs. */ +static EnumPropertyItem prop_similar_types[] = { +// {SIMVERT_NORMAL, "NORMAL", 0, "Normal", ""}, +// {SIMVERT_FACE, "FACE", 0, "Amount of Adjacent Faces", ""}, +// {SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""}, +// {SIMVERT_EDGE, "EDGE", 0, "Amount of connecting edges", ""}, + +// {SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""}, +// {SIMEDGE_DIR, "DIR", 0, "Direction", ""}, +// {SIMEDGE_FACE, "FACE", 0, "Amount of Faces Around an Edge", ""}, +// {SIMEDGE_FACE_ANGLE, "FACE_ANGLE", 0, "Face Angles", ""}, + +// {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""}, +// {SIMFACE_IMAGE, "IMAGE", 0, "Image", ""}, + {SIMFACE_AREA, "AREA", 0, "Area", "Select UV faces with similar area"}, +// {SIMFACE_SIDES, "SIDES", 0, "Polygon Sides", ""}, +// {SIMFACE_PERIMETER, "PERIMETER", 0, "Perimeter", ""}, +// {SIMFACE_NORMAL, "NORMAL", 0, "Normal", ""}, +// {SIMFACE_SMOOTH, "SMOOTH", 0, "Flat/Smooth", ""}, + + {0, NULL, 0, NULL, NULL} +}; + +/* copied from bmo_similar.c - need a better solution... */ +static int uv_select_similar_cmp_fl(const float delta, const float thresh, const int compare) +{ + switch (compare) { + case SIM_CMP_EQ: + return (fabsf(delta) <= thresh); + case SIM_CMP_GT: + return ((delta + thresh) >= 0.0f); + case SIM_CMP_LT: + return ((delta - thresh) <= 0.0f); + default: + BLI_assert(0); + return 0; + } +} + +/* selects new UV verts based on the existing selection */ +static int uv_similar_vert_select_exec(bContext *C, wmOperator *op) +{ + // stub + return OPERATOR_FINISHED; +} + +/* selects new UV edges based on the existing selection */ +static int uv_similar_edge_select_exec(bContext *C, wmOperator *op) +{ + // stub + return OPERATOR_FINISHED; +} + +/* selects new UV faces based on the existing selection */ +static int uv_similar_face_select_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + Image *ima = CTX_data_edit_image(C); + + Mesh *me = (Mesh *)obedit->data; + BMEditMesh *em = me->edit_btmesh; + BMesh *bm = em->bm; + + BMFace *efa, *f_selected, *f_mark; + BMIter iter; + MTexPoly *tf; + + SimSel_UVFaceExt *uv_face_extra = NULL; + int *selected_indices = NULL; + int selected_uvface_count = 0; + int visible_uvface_count = 0; + + int i, j; + int sel; /* current selected index */ + float delta_fl; /* initial_elem - other_elem */ + int delta_i; + + /* get the type from RNA */ + const int type = RNA_enum_get(op->ptr, "type"); + const float threshold = RNA_float_get(op->ptr, "threshold"); + const int compare = RNA_enum_get(op->ptr, "compare"); + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + const int cd_poly_tex_offset = CustomData_get_offset(&bm->pdata, CD_MTEXPOLY); + const int total_face_count = BM_mesh_elem_count(bm, BM_FACE); + + uv_face_extra = MEM_callocN(sizeof(*uv_face_extra) * total_face_count, + "uv_face_extra uvedit_ops.c"); + + /* store face data for comparison later */ + i = 0; + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + if (!uvedit_face_visible_test(scene, ima, efa, tf)) + continue; // skip invisible UVs + + uv_face_extra[i].f = efa; + + if (type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE) { + switch (type) { + case SIMFACE_AREA: + uv_face_extra[i].area = uv_poly_calc_area(efa, cd_loop_uv_offset); + break; + } + } + if (uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + selected_uvface_count++; + uv_face_extra[i].selected = true; + } + visible_uvface_count++; + i++; + } + + /* store selected indices */ + selected_indices = MEM_callocN(sizeof(*selected_indices) * selected_uvface_count, + "selected_indices uvedit_ops.c"); + j = 0; + for (i = 0; i < visible_uvface_count; i++) { + if (uv_face_extra[i].selected) + selected_indices[j] = i; + } + + /* now go through and select any similar UV faces */ + for (i = 0; i < visible_uvface_count; i++) { + if (uv_face_extra[i].selected) + continue; + + f_mark = uv_face_extra[i].f; + for (j = 0; j < selected_uvface_count; j++) { + sel = selected_indices[j]; + f_selected = uv_face_extra[sel].f; /* will be used in future expansions */ + switch (type) { + case SIMFACE_AREA: + delta_fl = uv_face_extra[i].area - uv_face_extra[sel].area; + if (uv_select_similar_cmp_fl(delta_fl, threshold, compare)) { + uvedit_face_select_enable(scene, em, f_mark, true, cd_loop_uv_offset); + goto continue_outer_loop; /* https://stackoverflow.com/a/9695942/3638059 */ + } + break; + + default: + BLI_assert(0); + break; + } + } + continue_outer_loop: ; + } + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + + MEM_freeN(uv_face_extra); + MEM_freeN(selected_indices); + + return OPERATOR_FINISHED; +} + +/* select new UV faces/edges/verts based on current selection */ +static int uv_select_similar_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold"); + + const int type = RNA_enum_get(op->ptr, "type"); + + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_set(op->ptr, prop, ts->select_thresh); + } + else { + ts->select_thresh = RNA_property_float_get(op->ptr, prop); + } + + if (type < 100) return uv_similar_vert_select_exec(C, op); + else if (type < 200) return uv_similar_edge_select_exec(C, op); + else return uv_similar_face_select_exec(C, op); +} + +/* enumerate types of similar features for call menu */ +static EnumPropertyItem *uv_select_similar_type_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free) +{ + Object *obedit; + ToolSettings *ts = CTX_data_tool_settings(C); + + if (!C) /* needed for docs and i18n tools */ + return prop_similar_types; + + obedit = CTX_data_edit_object(C); + + if (obedit && obedit->type == OB_MESH) { + EnumPropertyItem *item = NULL; + int a, totitem = 0; + + if (ts->uv_selectmode & SCE_SELECT_VERTEX) { + for (a = SIMVERT_NORMAL; a < SIMEDGE_LENGTH; a++) { + RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a); + } + } + else if (ts->uv_selectmode & SCE_SELECT_EDGE) { + for (a = SIMEDGE_LENGTH; a < SIMFACE_MATERIAL; a++) { + RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a); + } + } + else if (ts->uv_selectmode & SCE_SELECT_FACE) { + const int a_end = SIMFACE_SMOOTH; + for (a = SIMFACE_MATERIAL; a <= a_end; a++) { + RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a); + } + } + RNA_enum_item_end(&item, &totitem); + + *r_free = true; + + return item; + } + + return prop_similar_types; +} + +void UV_OT_select_similar(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select Similar"; + ot->description = "Select similar UV vertices, edges or faces by property types"; + ot->idname = "UV_OT_select_similar"; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_similar_exec; + ot->invoke = WM_menu_invoke; + ot->poll = ED_operator_uvedit; + + /* properties */ + prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", ""); + RNA_def_enum_funcs(prop, uv_select_similar_type_itemf); + + RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", ""); + + RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); +} + /* ************************** registration **********************************/ @@ -4246,6 +4520,7 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_circle_select); WM_operatortype_append(UV_OT_select_more); WM_operatortype_append(UV_OT_select_less); + WM_operatortype_append(UV_OT_select_similar); WM_operatortype_append(UV_OT_snap_cursor); WM_operatortype_append(UV_OT_snap_selected);