Index: release/scripts/startup/bl_ui/space_view3d_toolbar.py =================================================================== --- release/scripts/startup/bl_ui/space_view3d_toolbar.py (revision 58015) +++ release/scripts/startup/bl_ui/space_view3d_toolbar.py (working copy) @@ -285,8 +285,9 @@ row.operator("curve.handle_type_set", text="Auto").type = 'AUTOMATIC' row.operator("curve.handle_type_set", text="Vector").type = 'VECTOR' row = col.row() + row.operator("curve.handle_type_set", text="Free").type = 'FREE_ALIGN' row.operator("curve.handle_type_set", text="Align").type = 'ALIGNED' - row.operator("curve.handle_type_set", text="Free").type = 'FREE_ALIGN' + row.operator("curve.handle_type_set", text="Align + Constrain").type = 'CONSTRAIN' col = layout.column(align=True) col.label(text="Modeling:") Index: source/blender/blenkernel/BKE_curve.h =================================================================== --- source/blender/blenkernel/BKE_curve.h (revision 58015) +++ source/blender/blenkernel/BKE_curve.h (working copy) @@ -24,6 +24,7 @@ * * ***** END GPL LICENSE BLOCK ***** */ + #ifndef __BKE_CURVE_H__ #define __BKE_CURVE_H__ @@ -100,7 +101,7 @@ void BKE_nurbList_free(struct ListBase *lb); void BKE_nurbList_duplicate(struct ListBase *lb1, struct ListBase *lb2); -void BKE_nurbList_handles_set(struct ListBase *editnurb, short code); +void BKE_nurbList_handles_set(struct ListBase *editnurb, enum eHandleSetModes mode); void BKE_nurbList_handles_autocalc(ListBase *editnurb, int flag); Index: source/blender/blenkernel/intern/curve.c =================================================================== --- source/blender/blenkernel/intern/curve.c (revision 58015) +++ source/blender/blenkernel/intern/curve.c (working copy) @@ -3011,37 +3011,96 @@ } } -void BKE_nurbList_handles_set(ListBase *editnurb, short code) +void BKE_nurbList_handles_set(ListBase *editnurb, eHandleSetModes code) { - /* code==1: set autohandle */ - /* code==2: set vectorhandle */ - /* code==3 (HD_ALIGN) it toggle, vectorhandles become HD_FREE */ - /* code==4: sets icu flag to become IPO_AUTO_HORIZ, horizontal extremes on auto-handles */ - /* code==5: Set align, like 3 but no toggle */ - /* code==6: Clear align, like 3 but no toggle */ + /* code meaning: */ + /* HD_SET_AUTO : set auto-curve mode for handle */ + /* HD_SET_VECT : set auto-vector mode for handle */ + /* HD_SET_TOGGLE_FREE_ALIGN : toggle handle(s) to FREE, unless they are all FREE, then toggle to ALIGN */ + /* HD_SET_AUTO_ANIM : set handle(s) to HD_AUTO_ANIM */ + /* HD_SET_ALIGN : align handle(s) to other side */ + /* HD_SET_FREE : allow handle(s) to move separately from other side */ + /* HD_SET_CONSTRAIN : lock handle(s) rotation, allow scale */ + Nurb *nu; BezTriple *bezt; - short a, ok = 0; + short a, newmode = HD_FREE; - if (code == 1 || code == 2) { + if (code == 1 || code == 2 || code == 4 || code == 5 || code == 6 || code==7) { + /* handle all non-toggle codes */ nu = editnurb->first; while (nu) { if (nu->type == CU_BEZIER) { bezt = nu->bezt; a = nu->pntsu; while (a--) { - if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { - if (bezt->f1 & SELECT) - bezt->h1 = code; - if (bezt->f3 & SELECT) - bezt->h2 = code; - if (bezt->h1 != bezt->h2) { - if (ELEM(bezt->h1, HD_ALIGN, HD_AUTO)) - bezt->h1 = HD_FREE; - if (ELEM(bezt->h2, HD_ALIGN, HD_AUTO)) - bezt->h2 = HD_FREE; - } - } + switch (code) { + case HD_SET_AUTO: // allow AUTO/AUTO AUTO/VECT + // if either handle is selected, set that handle, and any FREE/ALIGN to AUTO + // TODO: enable AUTO/ALIGN or AUTO/FREE combinations by fixing drag code + if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { + if (bezt->f1 & SELECT) bezt->h1 = HD_AUTO; + if (bezt->f3 & SELECT) bezt->h2 = HD_AUTO; + // AUTO is currently incompatible with FREE or ALIGN, so set those to AUTO also + if (ELEM(bezt->h1, HD_FREE, HD_ALIGN)) bezt->h1 = HD_AUTO; + if (ELEM(bezt->h2, HD_FREE, HD_ALIGN)) bezt->h2 = HD_AUTO; + } + break; + case HD_SET_VECTOR: + // if only the center point is selected, toggle both handles + if (!(bezt->f1 & SELECT) && !(bezt->f3 & SELECT) && (bezt->f2 & SELECT)) { + bezt->h1 = bezt->h2 = HD_VECT; + } + else { + // otherwise only toggle the handle which is selected... + if (bezt->f1 & SELECT) bezt->h1 = HD_VECT; + if (bezt->f3 & SELECT) bezt->h2 = HD_VECT; + } + break; + case HD_SET_AUTO_ANIM: // allow AUTO_ANIM, AUTO_ANIM + // if only the center point is selected, toggle both handles + if (!(bezt->f1 & SELECT) && !(bezt->f3 & SELECT) && (bezt->f2 & SELECT)) { + bezt->h1 = bezt->h2 = HD_AUTO_ANIM; + } + if (bezt->f1 & SELECT) bezt->h1 = HD_AUTO_ANIM; + if (bezt->f3 & SELECT) bezt->h2 = HD_AUTO_ANIM; + break; + case HD_SET_ALIGN: // "Align" -> HD_ALIGN / HD_ALIGN + case HD_SET_FREE: // "Free" -> HD_FREE / HD_FREE + if (code == HD_SET_ALIGN) newmode = HD_ALIGN; + if (code == HD_SET_FREE) newmode = HD_FREE; + // if only middle handle is selected, change both + if (!(bezt->f1 & SELECT) && !(bezt->f3 & SELECT) && (bezt->f2 & SELECT)) { + bezt->h1 = bezt->h2 = newmode; + } + // if individual handles are selected... + if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) { + // change the selected handle + if (bezt->f1 & SELECT) bezt->h1 = newmode; + if (bezt->f3 & SELECT) bezt->h2 = newmode; + + // if other handles were free or align or AUTO, they need to be FREE, or we get lockup + if (ELEM3(bezt->h1, HD_FREE, HD_ALIGN, HD_AUTO)) bezt->h1 = newmode; + if (ELEM3(bezt->h2, HD_FREE, HD_ALIGN, HD_AUTO)) bezt->h2 = newmode; + } + break; + case HD_SET_CONSTRAIN: // "Constrain" -> ALIGN/FREE + // we can currently only constrain if only one handle is selected, so we check each side + if ((bezt->f1 & SELECT) && !(bezt->f3 & SELECT)) { + bezt->h1 = HD_ALIGN; + // if the other handle is ALIGN, we have to FREE it or it won't constain this one + if (bezt->h2 == HD_ALIGN) bezt->h2 = HD_FREE; + } + if ((bezt->f3 & SELECT) && !(bezt->f1 & SELECT)) { + bezt->h2 = HD_ALIGN; + // if the other handle is ALIGN, we have to free it or it won't constrain this one + if (bezt->h1 == HD_ALIGN) bezt->h1 = HD_FREE; + } + break; + default: + printf("ERROR: invalid eHandleSetMode (%d) in BKE_nurbList_handles_set-1\n", code); + return; + } bezt++; } BKE_nurb_handles_calc(nu); @@ -3049,42 +3108,44 @@ nu = nu->next; } } - else { - /* there is 1 handle not FREE: FREE it all, else make ALIGNED */ + else if (code == 3) { /* Toggle */ + /* (1) figure out what change to make */ + /* ... if ANY selected handle is not-FREE, free them all, otherwise align them all */ + newmode = HD_ALIGN; + nu = editnurb->first; + while (nu) { + if (nu->type == CU_BEZIER) { + bezt = nu->bezt; + a = nu->pntsu; + while (a--) { + if ((bezt->f1 & SELECT) && (bezt->h1 != HD_FREE)) newmode = HD_FREE; + if ((bezt->f3 & SELECT) && (bezt->h2 != HD_FREE)) newmode = HD_FREE; + if (newmode == HD_FREE) break; /* no need to keep searching */ + bezt++; + } + } + nu = nu->next; + } + + /* (2) now make the change */ nu = editnurb->first; - if (code == 5) { - ok = HD_ALIGN; - } - else if (code == 6) { - ok = HD_FREE; - } - else { - /* Toggle */ - while (nu) { - if (nu->type == CU_BEZIER) { - bezt = nu->bezt; - a = nu->pntsu; - while (a--) { - if ((bezt->f1 & SELECT) && bezt->h1) ok = 1; - if ((bezt->f3 & SELECT) && bezt->h2) ok = 1; - if (ok) break; - bezt++; - } - } - nu = nu->next; - } - if (ok) ok = HD_FREE; - else ok = HD_ALIGN; - } - nu = editnurb->first; while (nu) { if (nu->type == CU_BEZIER) { bezt = nu->bezt; a = nu->pntsu; while (a--) { - if (bezt->f1 & SELECT) bezt->h1 = ok; - if (bezt->f3 & SELECT) bezt->h2 = ok; - + switch (newmode) { + case HD_FREE: + case HD_ALIGN: + if ((bezt->f1 & SELECT) || (bezt->f2 & SELECT) || (bezt->f3 & SELECT)) { + // if any either handle or the anchor is selected, toggle them both + // to prevent unintended constrained handles + bezt->h1 = bezt->h2 = newmode; + } + break; + default: + printf("ERROR: unhandled handle mode toggle (%d) in BKE_nurbList_handles_set()\n", newmode); + } bezt++; } BKE_nurb_handles_calc(nu); @@ -3092,6 +3153,11 @@ nu = nu->next; } } + else { /* unknown action code */ + printf("ERROR: Unsupported eHandleSetMode (%d) in BKE_nurbList_handles_set()\n", code); + return; + } + } void BKE_nurb_direction_switch(Nurb *nu) Index: source/blender/makesdna/DNA_curve_types.h =================================================================== --- source/blender/makesdna/DNA_curve_types.h (revision 58015) +++ source/blender/makesdna/DNA_curve_types.h (working copy) @@ -329,6 +329,18 @@ HD_AUTO_ANIM = 4 /* auto-clamped handles for animation */ } eBezTriple_Handle; +/* handle type set modes for BKE_nurbList_handles_set() */ +typedef enum eHandleSetModes { + // do not allow zero value... + HD_SET_AUTO = 1, + HD_SET_VECTOR = 2, + HD_SET_TOGGLE_FREE_ALIGN = 3, + HD_SET_AUTO_ANIM = 4, + HD_SET_ALIGN = 5, + HD_SET_FREE = 6, + HD_SET_CONSTRAIN = 7 +} eHandleSetModes; + /* interpolation modes (used only for BezTriple->ipo) */ typedef enum eBezTriple_Interpolation { BEZT_IPO_CONST = 0, /* constant interpolation */ Index: source/blender/editors/curve/editcurve.c =================================================================== --- source/blender/editors/curve/editcurve.c (revision 58015) +++ source/blender/editors/curve/editcurve.c (working copy) @@ -3480,12 +3480,19 @@ { /* keep in sync with graphkeys_handle_type_items */ static EnumPropertyItem editcurve_handle_type_items[] = { - {HD_AUTO, "AUTOMATIC", 0, "Automatic", ""}, - {HD_VECT, "VECTOR", 0, "Vector", ""}, - {5, "ALIGNED", 0, "Aligned", ""}, - {6, "FREE_ALIGN", 0, "Free", ""}, - {3, "TOGGLE_FREE_ALIGN", 0, "Toggle Free/Align", ""}, - {0, NULL, 0, NULL, NULL} + {HD_SET_AUTO, "AUTOMATIC", 0, "Automatic", "Maintain a smooth curve to the next point."}, + {HD_SET_VECTOR, "VECTOR", 0, "Vector", "Maintain a straight line to the next point."}, + /* this is not exposed in the menu currently */ + /* {HD_SET_AUTO_ANIM, "AUTO_ANIM", 0, "Automatic (clamped)", "Maintain a (clamped) smooth curve to the next point."}, */ + {0, "", 0, NULL, NULL }, + + {HD_SET_ALIGN, "ALIGNED", 0, "Align", "Align the handle(s) to the other."}, + {HD_SET_CONSTRAIN, "CONSTRAIN", 0, "Align + Constrain", "Align and rotation constrain the handle(s)."}, + {HD_SET_FREE, "FREE_ALIGN", 0, "Free", "Allow the handle(s) to freely move."}, + {0, "", 0, NULL, NULL }, + + {HD_SET_TOGGLE_FREE_ALIGN, "TOGGLE_FREE_ALIGN", 0, "Toggle Free/Align", "Toggle handles Free. If they are all Free, toggle to Align."}, + {0, NULL, 0, NULL, NULL} /* end */ }; /* identifiers */ Index: source/blender/editors/transform/transform_conversions.c =================================================================== --- source/blender/editors/transform/transform_conversions.c (revision 58015) +++ source/blender/editors/transform/transform_conversions.c (working copy) @@ -1414,13 +1414,19 @@ if (bezt->hide == 0) { TransDataCurveHandleFlags *hdata = NULL; + /* the first spline handle */ if (propmode || ((bezt->f2 & SELECT) && hide_handles) || ((bezt->f1 & SELECT) && hide_handles == 0)) { copy_v3_v3(td->iloc, bezt->vec[0]); td->loc = bezt->vec[0]; - copy_v3_v3(td->center, bezt->vec[(hide_handles || bezt->f2 & SELECT) ? 1 : 0]); + // use the spline anchor as the translation center, if.... + copy_v3_v3(td->center, + bezt->vec[(hide_handles || // handles are hidden, or + bezt->f2 & SELECT || // the center is selected, or + countsel==1) // we are the only point selected + ? 1 : 0]); if (hide_handles) { if (bezt->f2 & SELECT) td->flag = TD_SELECTED; else td->flag = 0; @@ -1476,13 +1482,19 @@ count++; tail++; } + /* the second spline handle */ if (propmode || ((bezt->f2 & SELECT) && hide_handles) || ((bezt->f3 & SELECT) && hide_handles == 0)) { copy_v3_v3(td->iloc, bezt->vec[2]); td->loc = bezt->vec[2]; - copy_v3_v3(td->center, bezt->vec[(hide_handles || bezt->f2 & SELECT) ? 1 : 2]); + // use the spline anchor as the translation center, if... + copy_v3_v3(td->center, + bezt->vec[(hide_handles || // handles are hidden, or + bezt->f2 & SELECT || // the center is selected, or + countsel==1) // we are the only point selected + ? 1 : 2]); if (hide_handles) { if (bezt->f2 & SELECT) td->flag = TD_SELECTED; else td->flag = 0; Index: source/blender/editors/space_view3d/drawobject.c =================================================================== --- source/blender/editors/space_view3d/drawobject.c (revision 58015) +++ source/blender/editors/space_view3d/drawobject.c (working copy) @@ -5158,12 +5158,42 @@ /* place to add drawers */ +static void drawhandleConstraintIndicator_ifNecessary( + eBezTriple_Handle hndState, eBezTriple_Handle oHndState, + float *v3_anchorPoint, float *v3_handlePoint) +{ + float xyz[3], bar_end[3], bar_start[3], bar_dir[3], axis[3] = {0, 0, 1}; /* we really need the vector z-up axis */ + + /* draw constraint bar if rotation constrained */ + if ((hndState == HD_ALIGN) && !(ELEM(oHndState, HD_ALIGN, HD_AUTO))) { + // handle axis vector + sub_v3_v3v3(xyz, v3_anchorPoint, v3_handlePoint); + // a vector perpendicular to handle and axis, to make the constraint "T" + cross_v3_v3v3(bar_dir,xyz,axis); + + // set the size of the bar + normalize_v3(bar_dir); + mul_v3_fl(bar_dir,0.02); + + // draw the bar + // glVertex3fv(v3_handlePoint); + add_v3_v3v3(bar_start, v3_handlePoint,bar_dir); + glVertex3fv(bar_start); + mul_v3_fl(bar_dir,-1); + add_v3_v3v3(bar_end, v3_handlePoint,bar_dir); + glVertex3fv(bar_end); + } + +} + static void drawhandlesN(Nurb *nu, const char sel, const bool hide_handles) { BezTriple *bezt; + float *fp; int a; + if (nu->hide || hide_handles) return; glBegin(GL_LINES); @@ -5183,31 +5213,32 @@ a = nu->pntsu; while (a--) { if (bezt->hide == 0) { - if ((bezt->f2 & SELECT) == sel) { - fp = bezt->vec[0]; + /* if handle-f1 or center-f2 selection state matches cur-sel-draw state */ + if (((bezt->f2 & SELECT) == sel ) || + ((bezt->f1 & SELECT) == sel )) { + fp = bezt->vec[0]; + + /* draw center-to-handle-1 line */ glColor3ubv(handle_cols[MIN2(bezt->h1, TH_HANDLE_COL_TOT - 1)]); glVertex3fv(fp); glVertex3fv(fp + 3); + + drawhandleConstraintIndicator_ifNecessary(bezt->h1, bezt->h2, fp + 3, fp); + } + /* if handle-f3 or center-f2 selection state matches cur-sel-draw state */ + if (((bezt->f2 & SELECT) == sel ) || + ((bezt->f3 & SELECT) == sel )) { + fp = bezt->vec[1]; + + /* draw center-to-handle-2 line */ glColor3ubv(handle_cols[MIN2(bezt->h2, TH_HANDLE_COL_TOT - 1)]); - glVertex3fv(fp + 3); - glVertex3fv(fp + 6); - } - else if ((bezt->f1 & SELECT) == sel) { - fp = bezt->vec[0]; - - glColor3ubv(handle_cols[MIN2(bezt->h1, TH_HANDLE_COL_TOT - 1)]); glVertex3fv(fp); glVertex3fv(fp + 3); + + drawhandleConstraintIndicator_ifNecessary(bezt->h2, bezt->h1, fp, fp + 3); } - else if ((bezt->f3 & SELECT) == sel) { - fp = bezt->vec[1]; - - glColor3ubv(handle_cols[MIN2(bezt->h2, TH_HANDLE_COL_TOT - 1)]); - glVertex3fv(fp); - glVertex3fv(fp + 3); - } } bezt++; } @@ -5241,8 +5272,14 @@ glVertex3fv(fp); glVertex3fv(fp + 3); + drawhandleConstraintIndicator_ifNecessary(bezt->h1, bezt->h2, fp + 3, fp); + + glVertex3fv(fp + 3); glVertex3fv(fp + 6); + + drawhandleConstraintIndicator_ifNecessary(bezt->h2, bezt->h1, fp + 3, fp + 6); + } bezt++; }