Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/uvedit/uvedit_ops.c
| Context not available. | |||||
| #include "DNA_image_types.h" | #include "DNA_image_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_curve_types.h" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLI_alloca.h" | #include "BLI_alloca.h" | ||||
| Context not available. | |||||
| #include "BKE_node.h" | #include "BKE_node.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| #include "BKE_curve.h" | |||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "DEG_depsgraph_query.h" | #include "DEG_depsgraph_query.h" | ||||
| Context not available. | |||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_select_utils.h" | #include "ED_select_utils.h" | ||||
| #include "ED_transform.h" | #include "ED_transform.h" | ||||
| #include "ED_curve.h" | |||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| Context not available. | |||||
| static void uv_select_tag_update_for_object(Depsgraph *depsgraph, | static void uv_select_tag_update_for_object(Depsgraph *depsgraph, | ||||
| const ToolSettings *ts, | const ToolSettings *ts, | ||||
| Object *obedit); | Object *obedit); | ||||
| static void nurbsuv_toggle_selected(bContext *C); | |||||
| static void nurbsuv_set_selected_all(bContext *C, bool selected); | |||||
| static void nurbsuv_invert_selection(bContext *C); | |||||
| static void nurbsuv_selection_perform_action(bContext *C, int action); | |||||
| static void propagate_ctrlpt_sel_to_trim_sel(Nurb *nu); | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name State Testing | /** \name State Testing | ||||
| Context not available. | |||||
| for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | ||||
| Object *obedit = objects[ob_index]; | Object *obedit = objects[ob_index]; | ||||
| if (obedit && obedit->type == OB_SURF) { | |||||
| nurbsuv_selection_perform_action(C, action);} | |||||
| uv_select_tag_update_for_object(depsgraph, ts, obedit); | uv_select_tag_update_for_object(depsgraph, ts, obedit); | ||||
| } | } | ||||
| Context not available. | |||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = uv_select_all_exec; | ot->exec = uv_select_all_exec; | ||||
| ot->poll = ED_operator_uvedit; | ot->poll = ED_operator_uvedit_or_nurbsuv; | ||||
| WM_operator_properties_select_all(ot); | WM_operator_properties_select_all(ot); | ||||
| } | } | ||||
| Context not available. | |||||
| return ret; | return ret; | ||||
| } | } | ||||
| /* Minimum square distance between co (mouse click coords) | |||||
| * and the line defined by points (u,vmin),(u,vmax). */ | |||||
| static double dist2_ubreak(const float co[2], double u, double vmin, double vmax) { | |||||
| if (co[1]<vmin) return (co[0]-u)*(co[0]-u) + (co[1]-vmin)*(co[1]-vmin); | |||||
| if (co[1]>vmax) return (co[0]-u)*(co[0]-u) + (co[1]-vmax)*(co[1]-vmax); | |||||
| return (co[0]-u)*(co[0]-u); | |||||
| } | |||||
| /* Minimum square distance between co (mouse click coords) | |||||
| * and the line defined by points (u,vmin),(u,vmax). */ | |||||
| static double dist2_vbreak(const float co[2], double v, double umin, double umax) { | |||||
| if (co[0]<umin) return (co[0]-umin)*(co[0]-umin) + (co[1]-v)*(co[1]-v); | |||||
| if (co[0]>umax) return (co[0]-umax)*(co[0]-umax) + (co[1]-v)*(co[1]-v); | |||||
| return (co[1]-v)*(co[1]-v); | |||||
| } | |||||
| static double dist2_pt_lineseg(const float co[2], double a1, double a2, double b1, double b2) { | |||||
| /* Let d be the closest point to co on the line running through a,b. | |||||
| * x is such that d=x*a+(1-x)*b */ | |||||
| double x,x1,x2; | |||||
| x = (co[0]+co[1]-b1-b2)*(a1-b1)/((a1-b1)*(a1-b1)+(a2-b2)*(a2-b2)); | |||||
| if (x<0) x=0; | |||||
| if (x>1) x=1; | |||||
| x1 = x*a1 + (1-x)*b1; | |||||
| x2 = x*a2 + (1-x)*b2; | |||||
| return (co[0]-x1)*(co[0]-x1) + (co[1]-x2)*(co[1]-x2); | |||||
| } | |||||
| static void nurbsuv_set_selected_all(bContext *C, bool selected) { | |||||
| struct Object *editobj; | |||||
| Curve *cu; | |||||
| Nurb *nu, *trimnu; | |||||
| NurbEditKnot *ek; | |||||
| NurbTrim *nt; | |||||
| int i,numbp; | |||||
| editobj = CTX_data_edit_object(C); | |||||
| BLI_assert(editobj->type==OB_SURF); | |||||
| cu = (Curve*)editobj->data; | |||||
| BLI_assert(cu->editnurb); | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| ek = BKE_nurbs_editKnot_get(nu); | |||||
| for (i=0; i<ek->num_breaksu; i++) { | |||||
| if (selected) ek->breaksu[i].flag |= SELECT; | |||||
| else ek->breaksu[i].flag &= ~SELECT; | |||||
| } | |||||
| for (i=0; i<ek->num_breaksv; i++) { | |||||
| if (selected) ek->breaksv[i].flag |= SELECT; | |||||
| else ek->breaksv[i].flag &= ~SELECT; | |||||
| } | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| if (selected) nt->flag |= SELECT; | |||||
| else nt->flag &= ~SELECT; | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| numbp = trimnu->pntsu*trimnu->pntsv; | |||||
| for (i=0; i<numbp; i++) { | |||||
| if (selected) trimnu->bp[i].f1 |= SELECT; | |||||
| else trimnu->bp[i].f1 &= ~SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static void nurbsuv_invert_selection(bContext *C) { | |||||
| struct Object *editobj; | |||||
| Curve *cu; | |||||
| Nurb *nu, *trimnu; | |||||
| NurbTrim *nt; | |||||
| int i,numbp; | |||||
| editobj = CTX_data_edit_object(C); | |||||
| BLI_assert(editobj->type==OB_SURF); | |||||
| cu = (Curve*)editobj->data; | |||||
| BLI_assert(cu->editnurb); | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| /* Invert knot selection: probably less useful. Base on active subobj? | |||||
| ek = BKE_nurbs_editKnot_get(nu); | |||||
| for (i=0; i<ek->num_breaksu; i++) { | |||||
| if (ek->breaksu[i].flag & SELECT) | |||||
| ek->breaksu[i].flag &= ~SELECT; | |||||
| else | |||||
| ek->breaksu[i].flag |= SELECT; | |||||
| } | |||||
| for (i=0; i<ek->num_breaksv; i++) { | |||||
| if (ek->breaksv[i].flag&SELECT) | |||||
| ek->breaksv[i].flag &= ~SELECT; | |||||
| else | |||||
| ek->breaksv[i].flag |= SELECT; | |||||
| }*/ | |||||
| /* For now, invert the selection in evry selected trim curve */ | |||||
| propagate_ctrlpt_sel_to_trim_sel(nu); | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| if (!(nt->flag&SELECT)) continue; | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| numbp = trimnu->pntsu*trimnu->pntsv; | |||||
| for (i=0; i<numbp; i++) { | |||||
| if (trimnu->bp[i].f1&SELECT) | |||||
| trimnu->bp[i].f1 &= ~SELECT; | |||||
| else | |||||
| trimnu->bp[i].f1 |= SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static int nurbsuv_num_selected_knots(bContext *C) { | |||||
| struct Object *editobj; | |||||
| Curve *cu; | |||||
| Nurb *nu; | |||||
| NurbEditKnot *ek; | |||||
| int i, num_pts; | |||||
| editobj = CTX_data_edit_object(C); | |||||
| BLI_assert(editobj->type==OB_SURF); | |||||
| cu = (Curve*)editobj->data; | |||||
| BLI_assert(cu->editnurb); | |||||
| num_pts = 0; | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| ek = BKE_nurbs_editKnot_get(nu); | |||||
| for (i=0; i<ek->num_breaksu; i++) { | |||||
| if (ek->breaksu[i].flag & SELECT) num_pts++; | |||||
| } | |||||
| for (i=0; i<ek->num_breaksv; i++) { | |||||
| if (ek->breaksv[i].flag&SELECT) num_pts++; | |||||
| } | |||||
| } | |||||
| return num_pts; | |||||
| } | |||||
| static void propagate_ctrlpt_sel_to_trim_sel(Nurb *nu) { | |||||
| NurbTrim *nt; | |||||
| Nurb *trimnu; | |||||
| int num_bp,i; | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| nt->flag &= ~SELECT; | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| num_bp = trimnu->pntsu * trimnu->pntsv; | |||||
| for (i=0; i<num_bp; i++) { | |||||
| if (trimnu->bp[i].f1&SELECT) { | |||||
| nt->flag |= SELECT; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (nt->flag&SELECT) break; | |||||
| } | |||||
| } | |||||
| } | |||||
| static int nurbsuv_num_selected_trims(bContext *C) { | |||||
| struct Object *editobj; | |||||
| Curve *cu; | |||||
| Nurb *nu; | |||||
| NurbTrim *nt; | |||||
| int num_trims; | |||||
| editobj = CTX_data_edit_object(C); | |||||
| BLI_assert(editobj->type==OB_SURF); | |||||
| cu = (Curve*)editobj->data; | |||||
| BLI_assert(cu->editnurb); | |||||
| num_trims = 0; | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| propagate_ctrlpt_sel_to_trim_sel(nu); | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| if (nt->flag&SELECT) num_trims++; | |||||
| } | |||||
| } | |||||
| return num_trims; | |||||
| } | |||||
| static void nurbsuv_selection_perform_action(bContext *C, int action) { | |||||
| switch (action) { | |||||
| case SEL_TOGGLE: | |||||
| nurbsuv_toggle_selected(C); | |||||
| break; | |||||
| case SEL_SELECT: | |||||
| nurbsuv_set_selected_all(C, true); | |||||
| break; | |||||
| case SEL_DESELECT: | |||||
| nurbsuv_set_selected_all(C, false); | |||||
| break; | |||||
| case SEL_INVERT: | |||||
| nurbsuv_invert_selection(C); | |||||
| break; | |||||
| } | |||||
| } | |||||
| static void nurbsuv_toggle_selected(bContext *C) { | |||||
| if (nurbsuv_num_selected_knots(C) || nurbsuv_num_selected_trims(C)) { | |||||
| nurbsuv_set_selected_all(C, false); | |||||
| } else { | |||||
| nurbsuv_set_selected_all(C, true); | |||||
| } | |||||
| } | |||||
| static int nurbsuv_mouse_select(bContext *C, const float co[2], bool extend) { | |||||
| ARegion *ar = CTX_wm_region(C); | |||||
| View2D *v2d = &ar->v2d; | |||||
| struct Object *editobj; | |||||
| Curve *cu; | |||||
| Nurb *nu; | |||||
| NurbEditKnot* ek; | |||||
| NurbTrim *nt; | |||||
| /* Smallest difference between co[0] and a {u,v} breakpoint / trim */ | |||||
| double u=INFINITY,v=INFINITY,trimcp=INFINITY,trim=INFINITY; | |||||
| Nurb *nearest_u, *nearest_v, *nearest_trimcp_nu, *trimnu; | |||||
| NurbTrim *nearest_trimcp_nt=NULL, *nearest_trim_nt=NULL; | |||||
| int nearest_trim_cp,cp,num_bp; /* A trim ctrlpt is defined by: nearest_trim_nt > nearest_trim_nu > nearest_trim_cp */ | |||||
| /* The index of said nearest breakpoint or trim*/ | |||||
| int u_bkp=-1,v_bkp=-1,i,num_trim_verts; | |||||
| float umin,umax,vmin,vmax,max_miss_dist,(*trim_verts)[2]; | |||||
| double dist; | |||||
| editobj = CTX_data_edit_object(C); | |||||
| BLI_assert(editobj->type==OB_SURF); | |||||
| cu = (Curve*)editobj->data; | |||||
| BLI_assert(cu->editnurb); | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| ED_curve_propagate_selected_pts_to_flag2(cu); | |||||
| /* if (!(nu->flag2 & ~CU_SELECTED2)) continue;*/ | |||||
| ek = BKE_nurbs_editKnot_get(nu); | |||||
| BKE_nurbs_uvbounds(nu, &umin, &umax, &vmin, &vmax); | |||||
| /* Figure out nearest u break to click co */ | |||||
| for (i=0; i<ek->num_breaksu; i++) { | |||||
| dist = dist2_ubreak(co, ek->breaksu[i].loc, vmin, vmax); | |||||
| if (dist<u) { | |||||
| u=dist; | |||||
| nearest_u=nu; | |||||
| u_bkp=i; | |||||
| } | |||||
| } | |||||
| /* Figure out nearest v break to click co */ | |||||
| for (i=0; i<ek->num_breaksv; i++) { | |||||
| dist = dist2_vbreak(co, ek->breaksv[i].loc, umin, umax); | |||||
| if (dist<v) { | |||||
| v=dist; | |||||
| nearest_v=nu; | |||||
| v_bkp=i; | |||||
| } | |||||
| } | |||||
| /* Figure out nearest trim to click co */ | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| num_trim_verts = BKE_nurbTrim_tess(nt, &trim_verts); | |||||
| for (i=0; i<num_trim_verts-1; i++) { | |||||
| dist = dist2_pt_lineseg(co, trim_verts[i][0], trim_verts[i][1], trim_verts[i+1][0], trim_verts[i+1][1]); | |||||
| if (dist<trim) { | |||||
| trim = dist; | |||||
| nearest_trim_nt = nt; | |||||
| } | |||||
| } | |||||
| MEM_freeN(trim_verts); | |||||
| } | |||||
| /* Figure out nearest trim control point to click co */ | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| num_bp = trimnu->pntsu * trimnu->pntsv; | |||||
| for (cp=0; cp<num_bp; cp++) { | |||||
| dist = len_squared_v2v2(co, trimnu->bp[cp].vec); | |||||
| if (dist<trimcp) { | |||||
| trimcp = dist; | |||||
| nearest_trimcp_nt = nt; | |||||
| nearest_trimcp_nu = trimnu; | |||||
| nearest_trim_cp = cp; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!extend) nurbsuv_set_selected_all(C, false); | |||||
| /* The mouse click should count as a selection if it misses by <10px */ | |||||
| max_miss_dist = 10.0 / BLI_rcti_size_x(&v2d->mask) * BLI_rctf_size_x(&v2d->cur); | |||||
| if (trimcp<max_miss_dist) { | |||||
| nearest_trimcp_nu->bp[nearest_trim_cp].f1 ^= SELECT; | |||||
| } else if (trim<=u && trim<=v && trim<max_miss_dist) { | |||||
| nearest_trim_nt->flag ^= SELECT; | |||||
| } else if (u<=v && u<=trim && u<max_miss_dist) { | |||||
| nearest_u->editknot->breaksu[u_bkp].flag ^= SELECT; | |||||
| } else if (v<=u && v<= trim && v<max_miss_dist) { | |||||
| nearest_v->editknot->breaksv[v_bkp].flag ^= SELECT; | |||||
| } | |||||
| WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static int uv_select_exec(bContext *C, wmOperator *op) | static int uv_select_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| float co[2]; | float co[2]; | ||||
| Context not available. | |||||
| const bool extend = RNA_boolean_get(op->ptr, "extend"); | const bool extend = RNA_boolean_get(op->ptr, "extend"); | ||||
| const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); | const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); | ||||
| const bool loop = false; | const bool loop = false; | ||||
| Object *obedit; | |||||
| obedit = CTX_data_edit_object(C); | |||||
| if (obedit && obedit->type==OB_SURF) { | |||||
| return nurbsuv_mouse_select(C, co, extend); | |||||
| } | |||||
| return uv_mouse_select(C, co, extend, deselect_all, loop); | return uv_mouse_select(C, co, extend, deselect_all, loop); | ||||
| } | } | ||||
| Context not available. | |||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = uv_select_exec; | ot->exec = uv_select_exec; | ||||
| ot->invoke = uv_select_invoke; | ot->invoke = uv_select_invoke; | ||||
| ot->poll = ED_operator_uvedit; /* requires space image */ | ot->poll = ED_operator_uvedit_or_nurbsuv; /* requires space image */ | ||||
| /* properties */ | /* properties */ | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| Context not available. | |||||
| 100.0f); | 100.0f); | ||||
| } | } | ||||
| static int nurbs_trim_duplicate(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| Curve *cu = (Curve*)obedit->data; | |||||
| Nurb *nu, *trimnu; | |||||
| NurbTrim *nt, *dup_nt, *first_dup_nt; | |||||
| int i,num_bp; | |||||
| BLI_assert(obedit->type == OB_SURF); | |||||
| for (nu=(Nurb*)cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| first_dup_nt = NULL; | |||||
| for (nt=(NurbTrim*)nu->trims.first; nt && nt!=first_dup_nt; nt=nt->next) { | |||||
| /* First, propagate vertex selections to trim curve selections */ | |||||
| for (trimnu=(Nurb*)nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| num_bp = trimnu->pntsu * trimnu->pntsv; | |||||
| for (i=0; i<num_bp; i++) { | |||||
| if (trimnu->bp[i].f1&SELECT) { | |||||
| nt->flag |= SELECT; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (nt->flag&SELECT) break; | |||||
| } | |||||
| /* If this trim curve is selected, duplicate it */ | |||||
| if (nt->flag&SELECT) { | |||||
| dup_nt = BKE_nurbTrim_duplicate(nt); | |||||
| if (!first_dup_nt) first_dup_nt = dup_nt; | |||||
| BLI_addtail(&nu->trims, dup_nt); | |||||
| dup_nt->flag |= SELECT; | |||||
| } | |||||
| /* Clear the selection on the original, leaving dup to be dragged alone */ | |||||
| nt->flag &= ~SELECT; | |||||
| for (trimnu=(Nurb*)nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| num_bp = trimnu->pntsu * trimnu->pntsv; | |||||
| for (i=0; i<num_bp; i++) trimnu->bp[i].f1 &= ~SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static void UV_OT_nurbs_trim_duplicate(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Duplicate Trims"; | |||||
| ot->description = "Duplicate selected NURBS trim curves"; | |||||
| ot->idname = "UV_OT_nurbs_trim_duplicate"; | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* api callbacks */ | |||||
| ot->exec = nurbs_trim_duplicate; | |||||
| ot->poll = ED_operator_nurbsuv; /* requires space image */; | |||||
| } | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| Context not available. | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| static int nurbsuv_select_linked(bContext *C, wmOperator *UNUSED(op)) { | |||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| Curve *cu; | |||||
| Nurb *nu, *trimnu; | |||||
| NurbEditKnot *ek; | |||||
| NurbTrim *nt; | |||||
| int i,u,v,numbp; | |||||
| int sel_knots, sel_trimpts; | |||||
| BLI_assert(obedit->type == OB_SURF); | |||||
| cu = (Curve*)obedit->data; | |||||
| /* For Nurb *nu in the editobject | |||||
| * if any component of nu (U breakpoint, V breakpoint, trim) is selected | |||||
| * select all components of nu */ | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| sel_knots = 0; | |||||
| sel_trimpts = 0; | |||||
| ek = BKE_nurbs_editKnot_get(nu); | |||||
| for (u=0; u<ek->num_breaksu && !sel_knots; u++) { | |||||
| if (ek->breaksu[u].flag & SELECT) sel_knots = 1; | |||||
| } | |||||
| for (v=0; v<ek->num_breaksv && !sel_knots; v++) { | |||||
| if (ek->breaksv[v].flag&SELECT) sel_knots = 1; | |||||
| } | |||||
| for (nt=nu->trims.first; nt && !sel_knots; nt=nt->next) { | |||||
| nt->flag &= ~SELECT; | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| numbp = trimnu->pntsu*trimnu->pntsv; | |||||
| for (i=0; i<numbp; i++) { | |||||
| if (trimnu->bp[i].f1&SELECT) { | |||||
| sel_trimpts = 1; | |||||
| nt->flag |= SELECT; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (nt->flag&SELECT) break; | |||||
| } | |||||
| } | |||||
| if (sel_knots) { | |||||
| for (u=0; u<ek->num_breaksu; u++) ek->breaksu[u].flag |= SELECT; | |||||
| for (v=0; v<ek->num_breaksv; v++) ek->breaksv[v].flag |= SELECT; | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| nt->flag |= SELECT; | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| numbp = trimnu->pntsu*trimnu->pntsv; | |||||
| for (i=0; i<numbp; i++) trimnu->bp[i].f1 |= SELECT; | |||||
| } | |||||
| } | |||||
| } else if (sel_trimpts) { | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| if (!(nt->flag&SELECT)) continue; | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| numbp = trimnu->pntsu*trimnu->pntsv; | |||||
| for (i=0; i<numbp; i++) { | |||||
| trimnu->bp[i].f1 |= SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static int uv_select_linked_exec(bContext *C, wmOperator *op) | static int uv_select_linked_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| SpaceImage *sima = CTX_wm_space_image(C); | |||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| if (ED_space_image_show_nurbsuv(sima, obedit)) | |||||
| return nurbsuv_select_linked(C, op); | |||||
| else | |||||
| return uv_select_linked_internal(C, op, NULL, false); | return uv_select_linked_internal(C, op, NULL, false); | ||||
| } | } | ||||
| Context not available. | |||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = uv_select_linked_exec; | ot->exec = uv_select_linked_exec; | ||||
| ot->poll = ED_operator_uvedit; /* requires space image */ | ot->poll = ED_operator_uvedit_or_nurbsuv; /* requires space image */ | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| Context not available. | |||||
| /** \name Box Select Operator | /** \name Box Select Operator | ||||
| * \{ */ | * \{ */ | ||||
| static bool uline_touches_rctf(float umin, float umax, float v, rctf *rect) | |||||
| { | |||||
| if (v<rect->ymin || v>rect->ymax) return false; | |||||
| if (umax < rect->xmin) return false; | |||||
| if (umin > rect->xmax) return false; | |||||
| return true; | |||||
| } | |||||
| static bool vline_touches_rctf(float u, float vmin, float vmax, rctf *rect) | |||||
| { | |||||
| if (u<rect->xmin || u>rect->xmax) return false; | |||||
| if (vmax < rect->ymin) return false; | |||||
| if (vmin > rect->ymax) return false; | |||||
| return true; | |||||
| } | |||||
| static bool point_in_rctf(float *pt, rctf *rect) { | |||||
| if (pt[0]<rect->xmin || pt[0]>rect->xmax) return false; | |||||
| if (pt[1]<rect->ymin || pt[1]>rect->ymax) return false; | |||||
| return true; | |||||
| } | |||||
| static int uv_border_select_exec_nurbs(bContext *C, wmOperator *op) | |||||
| { | |||||
| ARegion *ar = CTX_wm_region(C); | |||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| Curve *cu = (Curve*)obedit->data; | |||||
| Nurb *nu, *trimnu; | |||||
| NurbEditKnot *ek; | |||||
| NurbTrim *nt; | |||||
| rctf selrect; | |||||
| int selection_changed=0; | |||||
| float umin,umax,vmin,vmax; | |||||
| bool select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT); | |||||
| bool extend = RNA_boolean_get(op->ptr, "extend"); | |||||
| int i,num_bp; | |||||
| WM_operator_properties_border_to_rctf(op, &selrect); | |||||
| UI_view2d_region_to_view_rctf(&ar->v2d, &selrect, &selrect); | |||||
| if (!extend) nurbsuv_set_selected_all(C, false); | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| ek = nu->editknot; | |||||
| BKE_nurbs_uvbounds(nu, &umin, &umax, &vmin, &vmax); | |||||
| if (ek->breaksu) for (i=0; i<ek->num_breaksu; i++) { | |||||
| if (vline_touches_rctf(ek->breaksu[i].loc, vmin, vmax, &selrect)) { | |||||
| if (select) ek->breaksu[i].flag |= SELECT; | |||||
| else ek->breaksu[i].flag &= ~SELECT; | |||||
| selection_changed = 1; | |||||
| } | |||||
| } | |||||
| if (ek->breaksv) for (i=0; i<ek->num_breaksv; i++) { | |||||
| if (uline_touches_rctf(umin, umax, ek->breaksv[i].loc, &selrect)) { | |||||
| if (select) ek->breaksv[i].flag |= SELECT; | |||||
| else ek->breaksv[i].flag &= ~SELECT; | |||||
| selection_changed = 1; | |||||
| } | |||||
| } | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| num_bp = trimnu->pntsu * trimnu->pntsv; | |||||
| for (i=0; i<num_bp; i++) { | |||||
| if (point_in_rctf(trimnu->bp[i].vec, &selrect)) { | |||||
| if (select) trimnu->bp[i].f1 |= SELECT; | |||||
| else trimnu->bp[i].f1 &= ~SELECT; | |||||
| selection_changed = 1; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return (selection_changed)? OPERATOR_FINISHED : OPERATOR_CANCELLED; | |||||
| } | |||||
| static int uv_box_select_exec(bContext *C, wmOperator *op) | static int uv_box_select_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | ||||
| Context not available. | |||||
| ot->invoke = WM_gesture_box_invoke; | ot->invoke = WM_gesture_box_invoke; | ||||
| ot->exec = uv_box_select_exec; | ot->exec = uv_box_select_exec; | ||||
| ot->modal = WM_gesture_box_modal; | ot->modal = WM_gesture_box_modal; | ||||
| ot->poll = ED_operator_uvedit_space_image; /* requires space image */ | ot->poll = ED_operator_uvedit_or_nurbsuv_space_image; /* requires space image */ | ||||
| ot->cancel = WM_gesture_box_cancel; | ot->cancel = WM_gesture_box_cancel; | ||||
| /* flags */ | /* flags */ | ||||
| Context not available. | |||||
| return ((x * x + y * y) < 1.0f); | return ((x * x + y * y) < 1.0f); | ||||
| } | } | ||||
| static int uline_inside_circle(float umin, float umax, float v, const float offset[2], const float ellipse[2]) | |||||
| { | |||||
| float leftend[2]={umin,v}, rightend[2]={umax,v}, y; | |||||
| if (offset[0]<umin) return uv_inside_circle(leftend, offset, ellipse); | |||||
| if (offset[0]>umax) return uv_inside_circle(rightend, offset, ellipse); | |||||
| y = (v - offset[1]) * ellipse[1]; | |||||
| return y*y<1.0f; | |||||
| } | |||||
| static int vline_inside_circle(float u, float vmin, float vmax, const float offset[2], const float ellipse[2]) | |||||
| { | |||||
| float botend[2]={u,vmin}, topend[2]={u,vmax}, x; | |||||
| if (offset[1]<vmin) return uv_inside_circle(botend, offset, ellipse); | |||||
| if (offset[0]>vmax) return uv_inside_circle(topend, offset, ellipse); | |||||
| x = (u - offset[0]) * ellipse[0]; | |||||
| return x*x<1.0f; | |||||
| } | |||||
| static int uv_circle_select_exec_nurbs(bContext *C, wmOperator *op, Object *obedit, float offset[2], float ellipse[2]) | |||||
| { | |||||
| int gesture_mode = RNA_int_get(op->ptr, "gesture_mode"); | |||||
| bool select = (gesture_mode == GESTURE_MODAL_SELECT); | |||||
| Nurb *nu, *trimnu; | |||||
| NurbTrim *nt; | |||||
| NurbEditKnot *ek; | |||||
| Curve *cu = (Curve*)obedit->data; | |||||
| float umin, umax, vmin, vmax; | |||||
| int i, num_bp, selection_changed=0; | |||||
| for (nu=cu->editnurb->nurbs.first; nu; nu=nu->next) { | |||||
| ek = nu->editknot; | |||||
| BKE_nurbs_uvbounds(nu, &umin, &umax, &vmin, &vmax); | |||||
| if (ek->breaksu) for (i=0; i<ek->num_breaksu; i++) { | |||||
| if (vline_inside_circle(ek->breaksu[i].loc, vmin, vmax, offset, ellipse)) { | |||||
| if (select) ek->breaksu[i].flag |= SELECT; | |||||
| else ek->breaksu[i].flag &= ~SELECT; | |||||
| selection_changed = 1; | |||||
| } | |||||
| } | |||||
| if (ek->breaksv) for (i=0; i<ek->num_breaksv; i++) { | |||||
| if (uline_inside_circle(umin, umax, ek->breaksv[i].loc, offset, ellipse)) { | |||||
| if (select) ek->breaksv[i].flag |= SELECT; | |||||
| else ek->breaksv[i].flag &= ~SELECT; | |||||
| selection_changed = 1; | |||||
| } | |||||
| } | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| for (trimnu=nt->nurb_list.first; trimnu; trimnu=trimnu->next) { | |||||
| num_bp = trimnu->pntsu * trimnu->pntsv; | |||||
| for (i=0; i<num_bp; i++) { | |||||
| if (uv_inside_circle(trimnu->bp[i].vec, offset, ellipse)) { | |||||
| if (select) trimnu->bp[i].f1 |= SELECT; | |||||
| else trimnu->bp[i].f1 &= ~SELECT; | |||||
| selection_changed = 1; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); | |||||
| return OPERATOR_FINISHED; /* Ignored by modal loop */ | |||||
| } | |||||
| static int uv_circle_select_exec(bContext *C, wmOperator *op) | static int uv_circle_select_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | ||||
| Context not available. | |||||
| ot->invoke = WM_gesture_circle_invoke; | ot->invoke = WM_gesture_circle_invoke; | ||||
| ot->modal = WM_gesture_circle_modal; | ot->modal = WM_gesture_circle_modal; | ||||
| ot->exec = uv_circle_select_exec; | ot->exec = uv_circle_select_exec; | ||||
| ot->poll = ED_operator_uvedit_space_image; /* requires space image */ | ot->poll = ED_operator_uvedit_or_nurbsuv_space_image; /* requires space image */ | ||||
| ot->cancel = WM_gesture_circle_cancel; | ot->cancel = WM_gesture_circle_cancel; | ||||
| /* flags */ | /* flags */ | ||||
| Context not available. | |||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| static int nurbsuv_add_square(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| SpaceImage *sima = CTX_wm_space_image(C); | |||||
| ARegion *ar = CTX_wm_region(C); | |||||
| Curve *cu; | |||||
| Nurb *nu, *new_trimnu; | |||||
| NurbTrim *new_nt; | |||||
| BPoint *bp; | |||||
| float ls = BLI_rctf_size_x(&ar->v2d.cur)/10; /* length of a side */ | |||||
| float ll[4]={0,0,0,1}, lr[4]={ls,0,0,1}, ur[4]={ls,ls,0,1}, ul[4]={0,ls,0,1}; | |||||
| if (obedit->type != OB_SURF) return OPERATOR_CANCELLED; | |||||
| if (!sima) return OPERATOR_CANCELLED; | |||||
| cu = (Curve*)obedit->data; | |||||
| nu = BKE_curve_nurb_active_get(cu); | |||||
| new_trimnu = (Nurb*)MEM_callocN(sizeof(Nurb),"nurbsuv_add_square.Nurb"); | |||||
| new_trimnu->flag = CU_2D; | |||||
| new_trimnu->flagu = CU_NURB_ENDPOINT; | |||||
| new_trimnu->type = CU_NURBS; | |||||
| new_trimnu->resolu = 1; | |||||
| new_trimnu->resolv = 1; | |||||
| new_trimnu->pntsu = 4; | |||||
| new_trimnu->pntsv = 1; | |||||
| new_trimnu->orderu = 2; | |||||
| new_trimnu->orderv = 1; | |||||
| bp = (BPoint*)MEM_callocN(sizeof(BPoint)*new_trimnu->pntsu,"nurbsuv_add_square.BPoint"); | |||||
| add_v4_v4v4(bp[0].vec, sima->cursor, ll); | |||||
| add_v4_v4v4(bp[1].vec, sima->cursor, lr); | |||||
| add_v4_v4v4(bp[2].vec, sima->cursor, ur); | |||||
| add_v4_v4v4(bp[3].vec, sima->cursor, ul); | |||||
| new_trimnu->bp = bp; | |||||
| BKE_nurb_knot_calc_u(new_trimnu); | |||||
| new_nt = (NurbTrim*)MEM_callocN(sizeof(NurbTrim),"nurbsuv_add_square.NurbTrim"); | |||||
| new_nt->type = CU_TRIM_SUB; | |||||
| new_nt->parent_nurb = new_trimnu; | |||||
| BLI_addtail(&new_nt->nurb_list, new_trimnu); | |||||
| BLI_addtail(&nu->trims, new_nt); | |||||
| BKE_nurbs_cached_UV_mesh_clear(nu, true); | |||||
| WM_event_add_notifier(C, NC_GEOM | ND_DATA, cu); | |||||
| DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); | |||||
| nurbsuv_set_selected_all(C, false); | |||||
| new_nt->flag |= SELECT; | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static int nurbsuv_add_circle(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| SpaceImage *sima = CTX_wm_space_image(C); | |||||
| ARegion *ar = CTX_wm_region(C); | |||||
| Curve *cu; | |||||
| Nurb *nu, *new_trimnu; | |||||
| NurbTrim *new_nt; | |||||
| BPoint *bp; | |||||
| float ls = BLI_rctf_size_x(&ar->v2d.cur)/10; /* length of a side */ | |||||
| float sqrt2o4 = sqrt(2)/4; | |||||
| float pts[9][4] = { | |||||
| {0.5, 0.0, 0.0, 1.0}, | |||||
| {1.0, 0.0, 0.0, sqrt2o4}, /* LR */ | |||||
| {1.0, 0.5, 0.0, 1.0}, | |||||
| {1.0, 1.0, 0.0, sqrt2o4}, /* UR */ | |||||
| {0.5, 1.0, 0.0, 1.0}, | |||||
| {0.0, 1.0, 0.0, sqrt2o4}, /* UL */ | |||||
| {0.0, 0.5, 0.0, 1.0}, | |||||
| {0.0, 0.0, 0.0, sqrt2o4}, /* LL */ | |||||
| {0.5, 0.0, 0.0, 1.0} | |||||
| }; | |||||
| int i; | |||||
| if (obedit->type != OB_SURF) return OPERATOR_CANCELLED; | |||||
| if (!sima) return OPERATOR_CANCELLED; | |||||
| cu = (Curve*)obedit->data; | |||||
| nu = BKE_curve_nurb_active_get(cu); | |||||
| new_trimnu = (Nurb*)MEM_callocN(sizeof(Nurb),"nurbsuv_add_circle.Nurb"); | |||||
| new_trimnu->flag = CU_2D; | |||||
| new_trimnu->flagu = CU_NURB_ENDPOINT; | |||||
| new_trimnu->type = CU_NURBS; | |||||
| new_trimnu->resolu = 3; | |||||
| new_trimnu->resolv = 1; | |||||
| new_trimnu->pntsu = 9; | |||||
| new_trimnu->pntsv = 1; | |||||
| new_trimnu->orderu = 4; | |||||
| new_trimnu->orderv = 1; | |||||
| bp = (BPoint*)MEM_callocN(sizeof(BPoint)*new_trimnu->pntsu,"nurbsuv_add_cicrcle.BPoint"); | |||||
| for (i=0; i<9; i++) { | |||||
| add_v2_v2(bp[i].vec, sima->cursor); | |||||
| madd_v3_v3fl(bp[i].vec, pts[i], ls); | |||||
| bp[i].vec[3] = pts[i][3]; | |||||
| } | |||||
| new_trimnu->bp = bp; | |||||
| BKE_nurb_knot_calc_u(new_trimnu); | |||||
| new_nt = (NurbTrim*)MEM_callocN(sizeof(NurbTrim),"nurbsuv_add_circle.NurbTrim"); | |||||
| new_nt->type = CU_TRIM_SUB; | |||||
| new_nt->parent_nurb = new_trimnu; | |||||
| BLI_addtail(&new_nt->nurb_list, new_trimnu); | |||||
| BLI_addtail(&nu->trims, new_nt); | |||||
| BKE_nurbs_cached_UV_mesh_clear(nu, true); | |||||
| WM_event_add_notifier(C, NC_GEOM | ND_DATA, cu); | |||||
| DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); | |||||
| nurbsuv_set_selected_all(C, false); | |||||
| new_nt->flag |= SELECT; | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static int nurbsuv_delete_trim(bContext *C, wmOperator *UNUSED(op)) | |||||
| { | |||||
| Object *obedit = CTX_data_edit_object(C); | |||||
| SpaceImage *sima = CTX_wm_space_image(C); | |||||
| Curve *cu; | |||||
| Nurb *nu; | |||||
| NurbTrim *nt; | |||||
| bool finished = false; | |||||
| if (obedit->type != OB_SURF) return OPERATOR_CANCELLED; | |||||
| if (!sima) return OPERATOR_CANCELLED; | |||||
| cu = (Curve*)obedit->data; | |||||
| nu = BKE_curve_nurb_active_get(cu); | |||||
| while (nu->trims.first && !finished) { | |||||
| for (nt=nu->trims.first; nt; nt=nt->next) { | |||||
| if (nt->flag & SELECT) { | |||||
| BLI_remlink_safe(&nu->trims, nt); | |||||
| BKE_nurbTrim_free(nt); | |||||
| break; | |||||
| } | |||||
| if (!nt->next) { | |||||
| finished = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| BKE_nurbs_cached_UV_mesh_clear(nu, true); | |||||
| DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT); | |||||
| WM_event_add_notifier(C, NC_GEOM | ND_DATA, cu); | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static void UV_OT_select_pinned(wmOperatorType *ot) | static void UV_OT_select_pinned(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| Context not available. | |||||
| ot->poll = ED_operator_uvedit; | ot->poll = ED_operator_uvedit; | ||||
| } | } | ||||
| static void UV_OT_nurbsuv_add_square(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Add Square"; | |||||
| ot->description = "Add a square (degree 2, i.e. polyline) NURBS 'curve' to the trims of the active NURBS surface"; | |||||
| ot->idname = "UV_OT_nurbsuv_add_square"; | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* api callbacks */ | |||||
| ot->exec = nurbsuv_add_square; | |||||
| ot->poll = ED_operator_nurbsuv; | |||||
| } | |||||
| static void UV_OT_nurbsuv_add_circle(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Add Circle"; | |||||
| ot->description = "Add a circular NURBS 'curve' to the trims of the active NURBS surface"; | |||||
| ot->idname = "UV_OT_nurbsuv_add_circle"; | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* api callbacks */ | |||||
| ot->exec = nurbsuv_add_circle; | |||||
| ot->poll = ED_operator_nurbsuv; | |||||
| } | |||||
| static void UV_OT_nurbsuv_delete_trim(wmOperatorType *ot) | |||||
| { | |||||
| /* identifiers */ | |||||
| ot->name = "Delete Trim"; | |||||
| ot->description = "Deletes all selected trim curves from the active NURBS surface"; | |||||
| ot->idname = "UV_OT_nurbsuv_delete_trim"; | |||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | |||||
| /* api callbacks */ | |||||
| ot->exec = nurbsuv_delete_trim; | |||||
| ot->poll = ED_operator_nurbsuv; | |||||
| } | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| Context not available. | |||||
| WM_operatortype_append(UV_OT_hide); | WM_operatortype_append(UV_OT_hide); | ||||
| WM_operatortype_append(UV_OT_cursor_set); | WM_operatortype_append(UV_OT_cursor_set); | ||||
| WM_operatortype_append(UV_OT_nurbsuv_add_square); | |||||
| WM_operatortype_append(UV_OT_nurbsuv_add_circle); | |||||
| WM_operatortype_append(UV_OT_nurbsuv_delete_trim); | |||||
| WM_operatortype_append(UV_OT_nurbs_trim_duplicate); | |||||
| } | |||||
| void ED_operatormacros_uvedit(void) { | |||||
| wmOperatorType *ot; | |||||
| wmOperatorTypeMacro *otmacro; | |||||
| ot = WM_operatortype_append_macro("UV_OT_nurbs_trim_duplicate_move", "Duplicate Trims", | |||||
| "Duplicate selected trims and move them", OPTYPE_UNDO | OPTYPE_REGISTER); | |||||
| ot->description = "Duplicate selected trims and move them"; | |||||
| WM_operatortype_macro_define(ot, "UV_OT_nurbs_trim_duplicate"); | |||||
| otmacro = WM_operatortype_macro_define(ot, "UV_OT_translate"); | |||||
| RNA_enum_set(otmacro->ptr, "use_proportional_edit", false); | |||||
| } | } | ||||
| void ED_keymap_uvedit(wmKeyConfig *keyconf) | void ED_keymap_uvedit(wmKeyConfig *keyconf) | ||||
| Context not available. | |||||