diff options
-rw-r--r-- | source/blender/blenkernel/BKE_curveprofile.h | 17 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curveprofile.c | 271 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_draw.c | 113 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 154 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_templates.c | 132 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_curveprofile_types.h | 20 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_curveprofile.c | 40 |
7 files changed, 501 insertions, 246 deletions
diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h index bf50cde1efc..877ab887138 100644 --- a/source/blender/blenkernel/BKE_curveprofile.h +++ b/source/blender/blenkernel/BKE_curveprofile.h @@ -45,6 +45,16 @@ void BKE_curveprofile_copy_data(struct CurveProfile *target, const struct CurveP struct CurveProfile *BKE_curveprofile_copy(const struct CurveProfile *profile); +bool BKE_curveprofile_move_handle(struct CurveProfilePoint *point, + const bool handle_1, + const bool snap, + const float delta[2]); + +bool BKE_curveprofile_move_point(struct CurveProfile *profile, + struct CurveProfilePoint *point, + const bool snap, + const float delta[2]); + bool BKE_curveprofile_remove_point(struct CurveProfile *profile, struct CurveProfilePoint *point); void BKE_curveprofile_remove_by_flag(struct CurveProfile *profile, const short flag); @@ -65,7 +75,12 @@ void BKE_curveprofile_create_samples(struct CurveProfile *profile, void BKE_curveprofile_initialize(struct CurveProfile *profile, short segments_len); /* Called for a complete update of the widget after modifications */ -void BKE_curveprofile_update(struct CurveProfile *profile, const bool rem_doubles); +enum { + PROF_UPDATE_NONE = 0, + PROF_UPDATE_REMOVE_DOUBLES = (1 << 0), + PROF_UPDATE_CLIP = (1 << 1), +}; +void BKE_curveprofile_update(struct CurveProfile *profile, const int update_flags); /* Need to find the total length of the curve to sample a portion of it */ float BKE_curveprofile_total_length(const struct CurveProfile *profile); diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c index f43e0355eaa..7b7cadfcb1b 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.c @@ -65,6 +65,11 @@ void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profil target->path = MEM_dupallocN(profile->path); target->table = MEM_dupallocN(profile->table); target->segments = MEM_dupallocN(profile->segments); + + /* Update the reference the points have to the profile. */ + for (int i = 0; i < target->path_len; i++) { + target->path[i].profile = target; + } } CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile) @@ -78,6 +83,101 @@ CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile) } /** + * Move a point's handle, accounting for the alignment of handles with the HD_ALIGN type. + * + * \param handle_1 Whether to move the 1st or 2nd control point. + * \param new_location The *relative* change in the handle's position. + * \note Requires #BKE_curveprofile_update call after. + * \return Whether the handle moved from its start position. + */ +bool BKE_curveprofile_move_handle(struct CurveProfilePoint *point, + const bool handle_1, + const bool snap, + const float delta[2]) +{ + short handle_type = (handle_1) ? point->h1 : point->h2; + float *handle_location = (handle_1) ? &point->h1_loc[0] : &point->h2_loc[0]; + + float start_position[2]; + copy_v2_v2(start_position, handle_location); + + /* Don't move the handle if it's not a free handle type. */ + if (!ELEM(handle_type, HD_FREE, HD_ALIGN)) { + return false; + } + + /* Move the handle. */ + handle_location[0] += delta ? delta[0] : 0.0f; + handle_location[1] += delta ? delta[1] : 0.0f; + if (snap) { + handle_location[0] = 0.125f * roundf(8.0f * handle_location[0]); + handle_location[1] = 0.125f * roundf(8.0f * handle_location[1]); + } + + /* Move the other handle if they are aligned. */ + if (handle_type == HD_ALIGN) { + short other_handle_type = (handle_1) ? point->h2 : point->h1; + if (other_handle_type == HD_ALIGN) { + float *other_handle_location = (handle_1) ? &point->h2_loc[0] : &point->h1_loc[0]; + other_handle_location[0] = 2.0f * point->x - handle_location[0]; + other_handle_location[1] = 2.0f * point->y - handle_location[1]; + } + } + + if (!equals_v2v2(handle_location, start_position)) { + return true; + } + return false; +} + +/** + * Moves a control point, accounting for clipping and snapping, and moving free handles. + * + * \param snap Whether to snap the point to the grid + * \param new_location The *relative* change of the point's location. + * \return Whether the point moved from its start position. + * \note Requires #BKE_curveprofile_update call after. + */ +bool BKE_curveprofile_move_point(struct CurveProfile *profile, + struct CurveProfilePoint *point, + const bool snap, + const float delta[2]) +{ + float origx = point->x; + float origy = point->y; + + point->x += delta[0]; + point->y += delta[1]; + if (snap) { + point->x = 0.125f * roundf(8.0f * point->x); + point->y = 0.125f * roundf(8.0f * point->y); + } + + /* Clip here instead to test clipping here to stop handles from moving too. */ + if (profile->flag & PROF_USE_CLIP) { + point->x = max_ff(point->x, profile->clip_rect.xmin); + point->x = min_ff(point->x, profile->clip_rect.xmax); + point->y = max_ff(point->y, profile->clip_rect.ymin); + point->y = min_ff(point->y, profile->clip_rect.ymax); + } + + /* Also move free handles even when they aren't selected. */ + if (ELEM(point->h1, HD_FREE, HD_ALIGN)) { + point->h1_loc[0] += point->x - origx; + point->h1_loc[1] += point->y - origy; + } + if (ELEM(point->h2, HD_FREE, HD_ALIGN)) { + point->h2_loc[0] += point->x - origx; + point->h2_loc[1] += point->y - origy; + } + + if (point->x != origx || point->y != origy) { + return true; + } + return false; +} + +/** * Removes a specific point from the path of control points. * \note Requires #BKE_curveprofile_update call after. */ @@ -100,8 +200,10 @@ bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *poi uint i_delete = (uint)(point - profile->path); /* Copy the before and after the deleted point. */ - memcpy(pts, profile->path, i_delete); - memcpy(pts + i_delete, profile->path + i_delete + 1, (size_t)profile->path_len - i_delete - 1); + memcpy(pts, profile->path, sizeof(CurveProfilePoint) * i_delete); + memcpy(pts + i_delete, + profile->path + i_delete + 1, + sizeof(CurveProfilePoint) * (profile->path_len - i_delete - 1)); MEM_freeN(profile->path); profile->path = pts; @@ -180,12 +282,9 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float "profile path"); for (int i_new = 0, i_old = 0; i_new < profile->path_len; i_new++) { if (i_new != i_insert) { - /* Insert old points */ - new_pts[i_new].x = profile->path[i_old].x; - new_pts[i_new].y = profile->path[i_old].y; - new_pts[i_new].flag = profile->path[i_old].flag & ~PROF_SELECT; /* Deselect old points. */ - new_pts[i_new].h1 = profile->path[i_old].h1; - new_pts[i_new].h2 = profile->path[i_old].h2; + /* Insert old points. */ + memcpy(&new_pts[i_new], &profile->path[i_old], sizeof(CurveProfilePoint)); + new_pts[i_new].flag &= ~PROF_SELECT; /* Deselect old points. */ i_old++; } else { @@ -201,6 +300,8 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float else { new_pt->h1 = new_pt->h2 = HD_AUTO; } + /* Give new point a reference to the profile. */ + new_pt->profile = profile; } } @@ -212,35 +313,19 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float /** * Sets the handle type of the selected control points. - * \param type_1, type_2: Either HD_VECT or HD_AUTO. Handle types for the first and second handles. - * + * \param type_* Handle type for the first handle. HD_VECT, HD_AUTO, HD_FREE, or HD_ALIGN. * \note Requires #BKE_curveprofile_update call after. */ void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int type_2) { for (int i = 0; i < profile->path_len; i++) { - if (profile->path[i].flag & PROF_SELECT) { - switch (type_1) { - case HD_AUTO: - profile->path[i].h1 = HD_AUTO; - break; - case HD_VECT: - profile->path[i].h1 = HD_VECT; - break; - default: - profile->path[i].h1 = HD_AUTO; - break; - } - switch (type_2) { - case HD_AUTO: - profile->path[i].h2 = HD_AUTO; - break; - case HD_VECT: - profile->path[i].h2 = HD_VECT; - break; - default: - profile->path[i].h1 = HD_AUTO; - break; + if (ELEM(profile->path[i].flag, PROF_SELECT, PROF_H1_SELECT, PROF_H2_SELECT)) { + profile->path[i].h1 = type_1; + profile->path[i].h2 = type_2; + + if (type_1 == HD_ALIGN && type_2 == HD_ALIGN) { + /* Align the handles. */ + BKE_curveprofile_move_handle(&profile->path[i], true, false, NULL); } } } @@ -261,11 +346,24 @@ void BKE_curveprofile_reverse(CurveProfile *profile) "profile path"); /* Mirror the new points across the y = x line */ for (int i = 0; i < profile->path_len; i++) { - new_pts[profile->path_len - i - 1].x = profile->path[i].y; - new_pts[profile->path_len - i - 1].y = profile->path[i].x; - new_pts[profile->path_len - i - 1].flag = profile->path[i].flag; - new_pts[profile->path_len - i - 1].h1 = profile->path[i].h1; - new_pts[profile->path_len - i - 1].h2 = profile->path[i].h2; + int i_reversed = profile->path_len - i - 1; + BLI_assert(i_reversed >= 0); + new_pts[i_reversed].x = profile->path[i].y; + new_pts[i_reversed].y = profile->path[i].x; + new_pts[i_reversed].flag = profile->path[i].flag; + new_pts[i_reversed].h1 = profile->path[i].h2; + new_pts[i_reversed].h2 = profile->path[i].h1; + new_pts[i_reversed].profile = profile; + + /* Mirror free handles, they can't be recalculated. */ + if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) { + new_pts[i_reversed].h1_loc[0] = profile->path[i].h2_loc[1]; + new_pts[i_reversed].h1_loc[1] = profile->path[i].h2_loc[0]; + } + if (ELEM(profile->path[i].h2, HD_FREE, HD_ALIGN)) { + new_pts[i_reversed].h2_loc[0] = profile->path[i].h1_loc[1]; + new_pts[i_reversed].h2_loc[1] = profile->path[i].h1_loc[0]; + } } /* Free the old points and use the new ones */ @@ -448,6 +546,13 @@ void BKE_curveprofile_reset(CurveProfile *profile) break; } + profile->flag &= ~PROF_DIRTY_PRESET; + + /* Ensure each point has a reference to the profile. */ + for (int i = 0; i < profile->path_len; i++) { + profile->path[i].profile = profile; + } + if (profile->table) { MEM_freeN(profile->table); profile->table = NULL; @@ -465,7 +570,7 @@ static bool is_curved_edge(BezTriple *bezt, int i) /** * Used to set bezier handle locations in the sample creation process. Reduced copy of - * #calchandleNurb_intern code in curve.c. + * #calchandleNurb_intern code in curve.c, mostly changed by removing the third dimension. */ static void calchandle_profile(BezTriple *bezt, const BezTriple *prev, const BezTriple *next) { @@ -600,7 +705,7 @@ static int sort_points_curvature(const void *in_a, const void *in_b) * this is true and there are only vector edges the straight edges will still be sampled. * \param r_samples: An array of points to put the sampled positions. Must have length n_segments. * \return r_samples: Fill the array with the sampled locations and if the point corresponds - * to a control point, its handle type + * to a control point, its handle type. */ void BKE_curveprofile_create_samples(CurveProfile *profile, int n_segments, @@ -621,21 +726,33 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, for (i = 0; i < totpoints; i++) { bezt[i].vec[1][0] = profile->path[i].x; bezt[i].vec[1][1] = profile->path[i].y; - bezt[i].h1 = (profile->path[i].h1 == HD_VECT) ? HD_VECT : HD_AUTO; - bezt[i].h2 = (profile->path[i].h2 == HD_VECT) ? HD_VECT : HD_AUTO; - } - /* Give the first and last bezier points the same handle type as their neighbors. */ - if (totpoints > 2) { - bezt[0].h1 = bezt[0].h2 = bezt[1].h1; - bezt[totpoints - 1].h1 = bezt[totpoints - 1].h2 = bezt[totpoints - 2].h2; + bezt[i].h1 = profile->path[i].h1; + bezt[i].h2 = profile->path[i].h2; + /* Copy handle locations if the handle type is free. */ + if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) { + bezt[i].vec[0][0] = profile->path[i].h1_loc[0]; + bezt[i].vec[0][1] = profile->path[i].h1_loc[1]; + } + if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) { + bezt[i].vec[2][0] = profile->path[i].h2_loc[0]; + bezt[i].vec[2][1] = profile->path[i].h2_loc[1]; + } } - /* Get handle positions for the bezier points. */ + /* Get handle positions for the non-free bezier points. */ calchandle_profile(&bezt[0], NULL, &bezt[1]); for (i = 1; i < totpoints - 1; i++) { calchandle_profile(&bezt[i], &bezt[i - 1], &bezt[i + 1]); } calchandle_profile(&bezt[totpoints - 1], &bezt[totpoints - 2], NULL); + /* Copy the handle locations back to the control points. */ + for (i = 0; i < totpoints; i++) { + profile->path[i].h1_loc[0] = bezt[i].vec[0][0]; + profile->path[i].h1_loc[1] = bezt[i].vec[0][1]; + profile->path[i].h2_loc[0] = bezt[i].vec[2][0]; + profile->path[i].h2_loc[1] = bezt[i].vec[2][1]; + } + /* Create a list of edge indices with the most curved at the start, least curved at the end. */ curve_sorted = MEM_callocN(sizeof(CurvatureSortPoint) * totedges, "curve sorted"); for (i = 0; i < totedges; i++) { @@ -720,7 +837,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, BLI_assert(j < n_segments); } - /* Do the sampling from bezier points, X values first, then Y values. */ + /* Sample from the bezier points. X then Y values. */ BKE_curve_forward_diff_bezier(bezt[i].vec[1][0], bezt[i].vec[2][0], bezt[i + 1].vec[0][0], @@ -740,7 +857,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, BLI_assert(i_sample <= n_segments); } -#ifdef DEBUG_profile_TABLE +#ifdef DEBUG_PROFILE_TABLE printf("CURVEPROFILE CREATE SAMPLES\n"); printf("n_segments: %d\n", n_segments); printf("totedges: %d\n", totedges); @@ -757,6 +874,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, } printf("\n"); #endif + MEM_freeN(bezt); MEM_freeN(curve_sorted); MEM_freeN(n_samples); @@ -827,8 +945,10 @@ void BKE_curveprofile_set_defaults(CurveProfile *profile) profile->path[0].x = 1.0f; profile->path[0].y = 0.0f; + profile->path[0].profile = profile; profile->path[1].x = 1.0f; profile->path[1].y = 1.0f; + profile->path[1].profile = profile; profile->changed_timestamp = 0; } @@ -852,13 +972,14 @@ struct CurveProfile *BKE_curveprofile_add(int preset) /** * Should be called after the widget is changed. Does profile and remove double checks and more * importantly, recreates the display / evaluation and segments tables. + * \param update_flags: Bitfield with fields defined in header file. Controls removing doubles and + * clipping. */ -void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double) +void BKE_curveprofile_update(CurveProfile *profile, const int update_flags) { CurveProfilePoint *points = profile->path; rctf *clipr = &profile->clip_rect; float thresh; - float dx, dy; int i; profile->changed_timestamp++; @@ -866,11 +987,16 @@ void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double) /* Clamp with the clipping rect in case something got past. */ if (profile->flag & PROF_USE_CLIP) { /* Move points inside the clip rectangle. */ - for (i = 0; i < profile->path_len; i++) { - points[i].x = max_ff(points[i].x, clipr->xmin); - points[i].x = min_ff(points[i].x, clipr->xmax); - points[i].y = max_ff(points[i].y, clipr->ymin); - points[i].y = min_ff(points[i].y, clipr->ymax); + if (update_flags & PROF_UPDATE_CLIP) { + for (i = 0; i < profile->path_len; i++) { + points[i].x = max_ff(points[i].x, clipr->xmin); + points[i].x = min_ff(points[i].x, clipr->xmax); + points[i].y = max_ff(points[i].y, clipr->ymin); + points[i].y = min_ff(points[i].y, clipr->ymax); + + /* Extra sanity assert to make sure the points have the right profile pointer. */ + BLI_assert(points[i].profile == profile); + } } /* Ensure zoom-level respects clipping. */ if (BLI_rctf_size_x(&profile->view_rect) > BLI_rctf_size_x(&profile->clip_rect)) { @@ -884,30 +1010,19 @@ void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double) } /* Remove doubles with a threshold set at 1% of default range. */ - thresh = 0.01f * BLI_rctf_size_x(clipr); - if (remove_double && profile->path_len > 2) { + thresh = pow2f(0.01f * BLI_rctf_size_x(clipr)); + if (update_flags & PROF_UPDATE_REMOVE_DOUBLES && profile->path_len > 2) { for (i = 0; i < profile->path_len - 1; i++) { - dx = points[i].x - points[i + 1].x; - dy = points[i].y - points[i + 1].y; - if (sqrtf(dx * dx + dy * dy) < thresh) { + if (len_squared_v2v2(&points[i].x, &points[i + 1].x) < thresh) { if (i == 0) { - points[i + 1].flag |= HD_VECT; - if (points[i + 1].flag & PROF_SELECT) { - points[i].flag |= PROF_SELECT; - } + BKE_curveprofile_remove_point(profile, &points[1]); } else { - points[i].flag |= HD_VECT; - if (points[i].flag & PROF_SELECT) { - points[i + 1].flag |= PROF_SELECT; - } + BKE_curveprofile_remove_point(profile, &points[i]); } - break; /* Assumes 1 deletion per edit is ok. */ + break; /* Assumes 1 deletion per update call is ok. */ } } - if (i != profile->path_len - 1) { - BKE_curveprofile_remove_by_flag(profile, 2); - } } /* Create the high resolution table for drawing and some evaluation functions. */ @@ -927,10 +1042,13 @@ void BKE_curveprofile_update(CurveProfile *profile, const bool remove_double) */ void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len) { + if (segments_len != profile->segments_len) { + profile->flag |= PROF_DIRTY_PRESET; + } profile->segments_len = segments_len; /* Calculate the higher resolution / segments tables for display and evaluation. */ - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); } /** @@ -1085,4 +1203,9 @@ void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurvePro BLO_read_data_address(reader, &profile->path); profile->table = NULL; profile->segments = NULL; + + /* Reset the points' pointers to the profile. */ + for (int i = 0; i < profile->path_len; i++) { + profile->path[i].profile = profile; + } } diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 1cb8565b38d..98cb85061f2 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -25,6 +25,7 @@ #include <string.h> #include "DNA_color_types.h" +#include "DNA_curve_types.h" #include "DNA_curveprofile_types.h" #include "DNA_movieclip_types.h" #include "DNA_screen_types.h" @@ -2159,7 +2160,19 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, immUnbindProgram(); } -/** Used to draw a curve profile widget. Somewhat similar to ui_draw_but_CURVE */ +/** + * Helper for #ui_draw_but_CURVEPROFILE. Used to tell whether to draw a control point's handles. + */ +static bool point_draw_handles(CurveProfilePoint *point) +{ + return (point->flag & PROF_SELECT && + (ELEM(point->h1, HD_FREE, HD_ALIGN) || ELEM(point->h2, HD_FREE, HD_ALIGN))) || + ELEM(point->flag, PROF_H1_SELECT, PROF_H2_SELECT); +} + +/** + * Draws the curve profile widget. Somewhat similar to ui_draw_but_CURVE. + */ void ui_draw_but_CURVEPROFILE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, @@ -2175,18 +2188,18 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, profile = (CurveProfile *)but->poin; } - /* Calculate offset and zoom */ + /* Calculate offset and zoom. */ float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect); float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect); float offsx = profile->view_rect.xmin - (1.0f / zoomx); float offsy = profile->view_rect.ymin - (1.0f / zoomy); - /* Exit early if too narrow */ + /* Exit early if too narrow. */ if (zoomx == 0.0f) { return; } - /* Test needed because path can draw outside of boundary */ + /* Test needed because path can draw outside of boundary. */ int scissor[4]; GPU_scissor_get_i(scissor); rcti scissor_new = { @@ -2208,7 +2221,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - /* Backdrop */ + /* Draw the backdrop. */ float color_backdrop[4] = {0, 0, 0, 1}; if (profile->flag & PROF_USE_CLIP) { gl_shaded_color_get_fl((uchar *)wcol->inner, -20, color_backdrop); @@ -2227,33 +2240,33 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); } - /* 0.25 step grid */ + /* 0.25 step grid. */ gl_shaded_color((uchar *)wcol->inner, -16); ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.25f); - /* 1.0 step grid */ + /* 1.0 step grid. */ gl_shaded_color((uchar *)wcol->inner, -24); ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 1.0f); - /* Draw the path's fill */ + /* Draw the path's fill. */ if (profile->table == NULL) { - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); } CurveProfilePoint *pts = profile->table; - /* Also add the last points on the right and bottom edges to close off the fill polygon */ + /* Also add the last points on the right and bottom edges to close off the fill polygon. */ bool add_left_tri = profile->view_rect.xmin < 0.0f; bool add_bottom_tri = profile->view_rect.ymin < 0.0f; uint tot_points = (uint)PROF_N_TABLE(profile->path_len) + 1 + add_left_tri + add_bottom_tri; uint tot_triangles = tot_points - 2; - /* Create array of the positions of the table's points */ + /* Create array of the positions of the table's points. */ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords"); for (i = 0; i < (uint)PROF_N_TABLE(profile->path_len); - i++) { /* Only add the points from the table here */ + i++) { /* Only add the points from the table here. */ table_coords[i][0] = pts[i].x; table_coords[i][1] = pts[i].y; } if (add_left_tri && add_bottom_tri) { - /* Add left side, bottom left corner, and bottom side points */ + /* Add left side, bottom left corner, and bottom side points. */ table_coords[tot_points - 3][0] = profile->view_rect.xmin; table_coords[tot_points - 3][1] = 1.0f; table_coords[tot_points - 2][0] = profile->view_rect.xmin; @@ -2262,30 +2275,30 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, table_coords[tot_points - 1][1] = profile->view_rect.ymin; } else if (add_left_tri) { - /* Add the left side and bottom left corner points */ + /* Add the left side and bottom left corner points. */ table_coords[tot_points - 2][0] = profile->view_rect.xmin; table_coords[tot_points - 2][1] = 1.0f; table_coords[tot_points - 1][0] = profile->view_rect.xmin; table_coords[tot_points - 1][1] = 0.0f; } else if (add_bottom_tri) { - /* Add the bottom side and bottom left corner points */ + /* Add the bottom side and bottom left corner points. */ table_coords[tot_points - 2][0] = 0.0f; table_coords[tot_points - 2][1] = profile->view_rect.ymin; table_coords[tot_points - 1][0] = 1.0f; table_coords[tot_points - 1][1] = profile->view_rect.ymin; } else { - /* Just add the bottom corner point. Side points would be redundant anyway */ + /* Just add the bottom corner point. Side points would be redundant anyway. */ table_coords[tot_points - 1][0] = 0.0f; table_coords[tot_points - 1][1] = 0.0f; } - /* Calculate the table point indices of the triangles for the profile's fill */ + /* Calculate the table point indices of the triangles for the profile's fill. */ uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, "return tri indices"); BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices); - /* Draw the triangles for the profile fill */ + /* Draw the triangles for the profile fill. */ immUniformColor3ubvAlpha((const uchar *)wcol->item, 128); GPU_blend(true); GPU_polygon_smooth(false); @@ -2301,7 +2314,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, immEnd(); MEM_freeN(tri_indices); - /* Draw the profile's path so the edge stands out a bit */ + /* Draw the profile's path so the edge stands out a bit. */ tot_points -= (add_left_tri + add_left_tri); GPU_line_width(1.0f); immUniformColor3ubvAlpha((const uchar *)wcol->item, 255); @@ -2313,9 +2326,44 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, immVertex2f(pos, fx, fy); } immEnd(); - immUnbindProgram(); MEM_freeN(table_coords); + /* Draw the handles for the selected control points. */ + pts = profile->path; + tot_points = (uint)profile->path_len; + int selected_free_points = 0; + for (i = 0; i < tot_points; i++) { + if (point_draw_handles(&pts[i])) { + selected_free_points++; + } + } + /* Draw the lines to the handles from the points. */ + if (selected_free_points > 0) { + GPU_line_width(1.0f); + gl_shaded_color((uchar *)wcol->inner, -24); + GPU_line_smooth(true); + immBegin(GPU_PRIM_LINES, selected_free_points * 4); + float ptx, pty; + for (i = 0; i < tot_points; i++) { + if (point_draw_handles(&pts[i])) { + ptx = rect->xmin + zoomx * (pts[i].x - offsx); + pty = rect->ymin + zoomy * (pts[i].y - offsy); + + fx = rect->xmin + zoomx * (pts[i].h1_loc[0] - offsx); + fy = rect->ymin + zoomy * (pts[i].h1_loc[1] - offsy); + immVertex2f(pos, ptx, pty); + immVertex2f(pos, fx, fy); + + fx = rect->xmin + zoomx * (pts[i].h2_loc[0] - offsx); + fy = rect->ymin + zoomy * (pts[i].h2_loc[1] - offsy); + immVertex2f(pos, ptx, pty); + immVertex2f(pos, fx, fy); + } + } + immEnd(); + } + immUnbindProgram(); + /* New GPU instructions for control points and sampled points. */ format = immVertexFormat(); pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -2339,8 +2387,6 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, } /* Draw the control points. */ - pts = profile->path; - tot_points = (uint)profile->path_len; GPU_line_smooth(false); GPU_blend(false); GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f))); @@ -2353,6 +2399,28 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, } immEnd(); + /* Draw the handle points. */ + if (selected_free_points > 0) { + GPU_line_smooth(false); + GPU_blend(false); + GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f))); + immBegin(GPU_PRIM_POINTS, selected_free_points * 2); + for (i = 0; i < tot_points; i++) { + if (point_draw_handles(&pts[i])) { + fx = rect->xmin + zoomx * (pts[i].h1_loc[0] - offsx); + fy = rect->ymin + zoomy * (pts[i].h1_loc[1] - offsy); + immAttr4fv(col, (pts[i].flag & PROF_H1_SELECT) ? color_vert_select : color_vert); + immVertex2f(pos, fx, fy); + + fx = rect->xmin + zoomx * (pts[i].h2_loc[0] - offsx); + fy = rect->ymin + zoomy * (pts[i].h2_loc[1] - offsy); + immAttr4fv(col, (pts[i].flag & PROF_H2_SELECT) ? color_vert_select : color_vert); + immVertex2f(pos, fx, fy); + } + } + immEnd(); + } + /* Draw the sampled points in addition to the control points if they have been created */ pts = profile->segments; tot_points = (uint)profile->segments_len; @@ -2367,7 +2435,6 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, } immEnd(); } - immUnbindProgram(); /* restore scissortest */ diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 3f3f0513184..f6bfb492c92 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -6944,7 +6944,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, d[0] = mx - data->dragstartx; d[1] = my - data->dragstarty; - if (len_squared_v2(d) < (3.0f * 3.0f)) { + if (len_squared_v2(d) < (9.0f * U.dpi_fac)) { snap = false; } } @@ -6953,32 +6953,38 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, fy = (my - dragy) / zoomy; if (data->dragsel != -1) { - CurveProfilePoint *point_last = NULL; + float last_x, last_y; const float mval_factor = ui_mouse_scale_warp_factor(shift); bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */ fx *= mval_factor; fy *= mval_factor; - /* Move all the points that aren't the last or the first */ - for (a = 1; a < profile->path_len - 1; a++) { - if (pts[a].flag & PROF_SELECT) { - float origx = pts[a].x, origy = pts[a].y; - pts[a].x += fx; - pts[a].y += fy; - if (snap) { - pts[a].x = 0.125f * roundf(8.0f * pts[a].x); - pts[a].y = 0.125f * roundf(8.0f * pts[a].y); + /* Move all selected points. */ + float delta[2] = {fx, fy}; + for (a = 0; a < profile->path_len; a++) { + /* Don't move the last and first control points. */ + if ((pts[a].flag & PROF_SELECT) && (a != 0) && (a != profile->path_len)) { + moved_point |= BKE_curveprofile_move_point(profile, &pts[a], snap, delta); + last_x = pts[a].x; + last_y = pts[a].y; + } + else { + /* Move handles when they're selected but the control point isn't. */ + if (ELEM(pts[a].h2, HD_FREE, HD_ALIGN) && pts[a].flag == PROF_H1_SELECT) { + moved_point |= BKE_curveprofile_move_handle(&pts[a], true, snap, delta); + last_x = pts[a].h1_loc[0]; + last_y = pts[a].h1_loc[1]; } - if (!moved_point && (pts[a].x != origx || pts[a].y != origy)) { - moved_point = true; + if (ELEM(pts[a].h2, HD_FREE, HD_ALIGN) && pts[a].flag == PROF_H2_SELECT) { + moved_point |= BKE_curveprofile_move_handle(&pts[a], false, snap, delta); + last_x = pts[a].h2_loc[0]; + last_y = pts[a].h2_loc[1]; } - - point_last = &pts[a]; } } - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); if (moved_point) { data->draglastx = evtx; @@ -6989,10 +6995,8 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, * but in practice this isnt really an issue */ if (ui_but_is_cursor_warp(but)) { /* OK but can go outside bounds */ - data->ungrab_mval[0] = but->rect.xmin + - ((point_last->x - profile->view_rect.xmin) * zoomx); - data->ungrab_mval[1] = but->rect.ymin + - ((point_last->y - profile->view_rect.ymin) * zoomy); + data->ungrab_mval[0] = but->rect.xmin + ((last_x - profile->view_rect.xmin) * zoomx); + data->ungrab_mval[1] = but->rect.ymin + ((last_y - profile->view_rect.ymin) * zoomy); BLI_rctf_clamp_pt_v(&but->rect, data->ungrab_mval); } #endif @@ -7000,7 +7004,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, data->dragchange = true; /* mark for selection */ } else { - /* clamp for clip */ + /* Clamp the view rect when clipping is on. */ if (profile->flag & PROF_USE_CLIP) { if (profile->view_rect.xmin - fx < profile->clip_rect.xmin) { fx = profile->view_rect.xmin - profile->clip_rect.xmin; @@ -7031,16 +7035,26 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, } /** + * Helper for #ui_do_but_CURVEPROFILE. Used to tell whether to select a control point's handles. + */ +static bool point_draw_handles(CurveProfilePoint *point) +{ + return (point->flag & PROF_SELECT && + (ELEM(point->h1, HD_FREE, HD_ALIGN) || ELEM(point->h2, HD_FREE, HD_ALIGN))) || + ELEM(point->flag, PROF_H1_SELECT, PROF_H2_SELECT); +} + +/** * Interaction for curve profile widget. * \note Uses hardcoded keys rather than the keymap. */ static int ui_do_but_CURVEPROFILE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my, i; + CurveProfile *profile = (CurveProfile *)but->poin; + int mx = event->x; + int my = event->y; - mx = event->x; - my = event->y; ui_window_to_block(data->region, block, &mx, &my); /* Move selected control points. */ @@ -7053,12 +7067,10 @@ static int ui_do_but_CURVEPROFILE( return WM_UI_HANDLER_BREAK; } - CurveProfile *profile = (CurveProfile *)but->poin; - /* Delete selected control points. */ if (event->type == EVT_XKEY && event->val == KM_RELEASE) { BKE_curveprofile_remove_by_flag(profile, PROF_SELECT); - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } @@ -7066,76 +7078,94 @@ static int ui_do_but_CURVEPROFILE( /* Selecting, adding, and starting point movements. */ if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS) { - CurveProfilePoint *pts; /* Path or table. */ const float m_xy[2] = {mx, my}; - float dist_min_sq; - int i_selected = -1; if (event->ctrl) { float f_xy[2]; BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy); BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]); - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_CLIP); } /* Check for selecting of a point by finding closest point in radius. */ - dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius for selecting points. */ - pts = profile->path; - for (i = 0; i < profile->path_len; i++) { + CurveProfilePoint *pts = profile->path; + float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius for selecting points. */ + int i_selected = -1; + short selection_type = 0; /* For handle selection. */ + for (int i = 0; i < profile->path_len; i++) { float f_xy[2]; BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x); - const float dist_sq = len_squared_v2v2(m_xy, f_xy); + float dist_sq = len_squared_v2v2(m_xy, f_xy); if (dist_sq < dist_min_sq) { i_selected = i; + selection_type = PROF_SELECT; dist_min_sq = dist_sq; } + + /* Also select handles if the point is selected and it has the right handle type. */ + if (point_draw_handles(&pts[i])) { + if (ELEM(profile->path[i].h1, HD_FREE, HD_ALIGN)) { + BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, pts[i].h1_loc); + dist_sq = len_squared_v2v2(m_xy, f_xy); + if (dist_sq < dist_min_sq) { + i_selected = i; + selection_type = PROF_H1_SELECT; + dist_min_sq = dist_sq; + } + } + if (ELEM(profile->path[i].h2, HD_FREE, HD_ALIGN)) { + BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, pts[i].h2_loc); + dist_sq = len_squared_v2v2(m_xy, f_xy); + if (dist_sq < dist_min_sq) { + i_selected = i; + selection_type = PROF_H2_SELECT; + dist_min_sq = dist_sq; + } + } + } } - /* Add a point if the click was close to the path but not a control point. */ - if (i_selected == -1) { /* No control point selected. */ + /* Add a point if the click was close to the path but not a control point or handle. */ + if (i_selected == -1) { float f_xy[2], f_xy_prev[2]; - pts = profile->table; - BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[0].x); + CurveProfilePoint *table = profile->table; + BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[0].x); dist_min_sq = square_f(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */ /* Loop through the path's high resolution table and find what's near the click. */ - for (i = 1; i <= PROF_N_TABLE(profile->path_len); i++) { + for (int i = 1; i <= PROF_N_TABLE(profile->path_len); i++) { copy_v2_v2(f_xy_prev, f_xy); - BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &pts[i].x); + BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[i].x); if (dist_squared_to_line_segment_v2(m_xy, f_xy_prev, f_xy) < dist_min_sq) { BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy); CurveProfilePoint *new_pt = BKE_curveprofile_insert(profile, f_xy[0], f_xy[1]); - BKE_curveprofile_update(profile, false); - - /* reset pts back to the control points. */ - pts = profile->path; + BKE_curveprofile_update(profile, PROF_UPDATE_CLIP); /* Get the index of the newly added point. */ - for (i = 0; i < profile->path_len; i++) { - if (&pts[i] == new_pt) { - i_selected = i; - } - } + i_selected = (int)(new_pt - profile->path); + BLI_assert(i_selected >= 0 && i_selected <= profile->path_len); + selection_type = PROF_SELECT; break; } } } - /* Change the flag for the point(s) if one was selected. */ + /* Change the flag for the point(s) if one was selected or added. */ if (i_selected != -1) { /* Deselect all if this one is deselected, except if we hold shift. */ - if (!event->shift) { - for (i = 0; i < profile->path_len; i++) { - pts[i].flag &= ~PROF_SELECT; - } - pts[i_selected].flag |= PROF_SELECT; + if (event->shift) { + pts[i_selected].flag ^= selection_type; } else { - pts[i_selected].flag ^= PROF_SELECT; + for (int i = 0; i < profile->path_len; i++) { + // pts[i].flag &= ~(PROF_SELECT | PROF_H1_SELECT | PROF_H2_SELECT); + profile->path[i].flag &= ~(PROF_SELECT | PROF_H1_SELECT | PROF_H2_SELECT); + } + profile->path[i_selected].flag |= selection_type; } } else { @@ -7166,19 +7196,13 @@ static int ui_do_but_CURVEPROFILE( else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { /* Finish move. */ if (data->dragsel != -1) { - CurveProfilePoint *pts = profile->path; if (data->dragchange == false) { /* Deselect all, select one. */ - if (!event->shift) { - for (i = 0; i < profile->path_len; i++) { - pts[i].flag &= ~PROF_SELECT; - } - pts[data->dragsel].flag |= PROF_SELECT; - } } else { - BKE_curveprofile_update(profile, true); /* Remove doubles after move. */ + /* Remove doubles, clip after move. */ + BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP); } } button_activate_state(C, but, BUTTON_STATE_EXIT); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 16b6b313f69..0d6717c8e12 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -4463,7 +4463,7 @@ static void CurveProfile_presets_dofunc(bContext *C, void *profile_v, int event) profile->preset = event; BKE_curveprofile_reset(profile); - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); ED_undo_push(C, "CurveProfile tools"); ED_region_tag_redraw(CTX_wm_region(C)); @@ -4579,7 +4579,7 @@ static void CurveProfile_tools_dofunc(bContext *C, void *profile_v, int event) switch (event) { case UIPROFILE_FUNC_RESET: /* reset */ BKE_curveprofile_reset(profile); - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); break; case UIPROFILE_FUNC_RESET_VIEW: /* reset view to clipping rect */ profile->view_rect = profile->clip_rect; @@ -4645,7 +4645,7 @@ static void CurveProfile_buttons_zoom_in(bContext *C, void *profile_v, void *UNU CurveProfile *profile = profile_v; float d; - /* we allow 20 times zoom */ + /* Allow a 20x zoom. */ if (BLI_rctf_size_x(&profile->view_rect) > 0.04f * BLI_rctf_size_x(&profile->clip_rect)) { d = 0.1154f * BLI_rctf_size_x(&profile->view_rect); profile->view_rect.xmin += d; @@ -4709,7 +4709,7 @@ static void CurveProfile_clipping_toggle(bContext *C, void *cb_v, void *profile_ profile->flag ^= PROF_USE_CLIP; - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); rna_update_cb(C, cb_v, NULL); } @@ -4718,7 +4718,7 @@ static void CurveProfile_buttons_reverse(bContext *C, void *cb_v, void *profile_ CurveProfile *profile = profile_v; BKE_curveprofile_reverse(profile); - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); rna_update_cb(C, cb_v, NULL); } @@ -4727,35 +4727,23 @@ static void CurveProfile_buttons_delete(bContext *C, void *cb_v, void *profile_v CurveProfile *profile = profile_v; BKE_curveprofile_remove_by_flag(profile, SELECT); - BKE_curveprofile_update(profile, false); - - rna_update_cb(C, cb_v, NULL); -} - -static void CurveProfile_buttons_setsharp(bContext *C, void *cb_v, void *profile_v) -{ - CurveProfile *profile = profile_v; - - BKE_curveprofile_selected_handle_set(profile, HD_VECT, HD_VECT); - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); rna_update_cb(C, cb_v, NULL); } -static void CurveProfile_buttons_setcurved(bContext *C, void *cb_v, void *profile_v) +static void CurveProfile_buttons_update(bContext *C, void *arg1_v, void *profile_v) { CurveProfile *profile = profile_v; - - BKE_curveprofile_selected_handle_set(profile, HD_AUTO, HD_AUTO); - BKE_curveprofile_update(profile, false); - - rna_update_cb(C, cb_v, NULL); + BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP); + rna_update_cb(C, arg1_v, NULL); } -static void CurveProfile_buttons_update(bContext *C, void *arg1_v, void *profile_v) +static void CurveProfile_buttons_reset(bContext *C, void *arg1_v, void *profile_v) { CurveProfile *profile = profile_v; - BKE_curveprofile_update(profile, true); + BKE_curveprofile_reset(profile); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); rna_update_cb(C, arg1_v, NULL); } @@ -4774,7 +4762,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp UI_block_emboss_set(block, UI_EMBOSS); - uiLayoutRow(layout, false); + uiLayoutSetPropSep(layout, false); /* Preset selector */ /* There is probably potential to use simpler "uiItemR" functions here, but automatic updating @@ -4783,6 +4771,29 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp block, CurveProfile_buttons_presets, profile, "Preset", 0, 0, UI_UNIT_X, UI_UNIT_X, ""); UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL); + /* Show a "re-apply" preset button when it has been changed from the preset. */ + if (profile->flag & PROF_DIRTY_PRESET) { + /* Only for dynamic presets. */ + if (ELEM(profile->preset, PROF_PRESET_STEPS, PROF_PRESET_SUPPORTS)) { + bt = uiDefIconTextBut(block, + UI_BTYPE_BUT, + 0, + ICON_NONE, + "Apply Preset", + 0, + 0, + UI_UNIT_X, + UI_UNIT_X, + NULL, + 0.0, + 0.0, + 0.0, + 0.0, + "Reapply and update the preset, removing changes"); + UI_but_funcN_set(bt, CurveProfile_buttons_reset, MEM_dupallocN(cb), profile); + } + } + row = uiLayoutRow(layout, false); /* (Left aligned) */ @@ -4890,11 +4901,24 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp ""); /* Position sliders for (first) selected point */ + float *selection_x, *selection_y; for (i = 0; i < profile->path_len; i++) { if (profile->path[i].flag & PROF_SELECT) { point = &profile->path[i]; + selection_x = &point->x; + selection_y = &point->y; break; } + else if (profile->path[i].flag & PROF_H1_SELECT) { + point = &profile->path[i]; + selection_x = &point->h1_loc[0]; + selection_y = &point->h1_loc[1]; + } + else if (profile->path[i].flag & PROF_H2_SELECT) { + point = &profile->path[i]; + selection_x = &point->h2_loc[0]; + selection_y = &point->h2_loc[1]; + } } if (i == 0 || i == profile->path_len - 1) { point_last_or_first = true; @@ -4910,46 +4934,19 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp bounds.xmax = bounds.ymax = 1000.0; } - uiLayoutRow(layout, true); - UI_block_funcN_set(block, CurveProfile_buttons_update, MEM_dupallocN(cb), profile); + row = uiLayoutRow(layout, true); - /* Sharp / Smooth */ - bt = uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_LINCURVE, - 0, - 0, - UI_UNIT_X, - UI_UNIT_X, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Set the point's handle type to sharp")); - if (point_last_or_first) { - UI_but_flag_enable(bt, UI_BUT_DISABLED); - } - UI_but_funcN_set(bt, CurveProfile_buttons_setsharp, MEM_dupallocN(cb), profile); - bt = uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_SMOOTHCURVE, - 0, - 0, - UI_UNIT_X, - UI_UNIT_X, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Set the point's handle type to smooth")); - UI_but_funcN_set(bt, CurveProfile_buttons_setcurved, MEM_dupallocN(cb), profile); - if (point_last_or_first) { - UI_but_flag_enable(bt, UI_BUT_DISABLED); - } + PointerRNA point_ptr; + RNA_pointer_create(ptr->owner_id, &RNA_CurveProfilePoint, point, &point_ptr); + PropertyRNA *prop_handle_type = RNA_struct_find_property(&point_ptr, "handle_type_1"); + uiItemFullR(row, + &point_ptr, + prop_handle_type, + RNA_NO_INDEX, + 0, + UI_ITEM_R_EXPAND | UI_ITEM_R_ICON_ONLY, + "", + ICON_NONE); /* Position */ bt = uiDefButF(block, @@ -4960,16 +4957,16 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp 2 * UI_UNIT_Y, UI_UNIT_X * 10, UI_UNIT_Y, - &point->x, + selection_x, bounds.xmin, bounds.xmax, 1, 5, ""); + UI_but_funcN_set(bt, CurveProfile_buttons_update, MEM_dupallocN(cb), profile); if (point_last_or_first) { UI_but_flag_enable(bt, UI_BUT_DISABLED); } - bt = uiDefButF(block, UI_BTYPE_NUM, 0, @@ -4978,12 +4975,13 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp 1 * UI_UNIT_Y, UI_UNIT_X * 10, UI_UNIT_Y, - &point->y, + selection_y, bounds.ymin, bounds.ymax, 1, 5, ""); + UI_but_funcN_set(bt, CurveProfile_buttons_update, MEM_dupallocN(cb), profile); if (point_last_or_first) { UI_but_flag_enable(bt, UI_BUT_DISABLED); } diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h index 63e1049b636..ca00f783905 100644 --- a/source/blender/makesdna/DNA_curveprofile_types.h +++ b/source/blender/makesdna/DNA_curveprofile_types.h @@ -43,13 +43,22 @@ typedef struct CurveProfilePoint { float x, y; /** Flag selection state and others. */ short flag; - /** Flags for both handle's type (eBezTriple_Handle). */ + /** Flags for both handle's type (eBezTriple_Handle auto, vect, free, and aligned supported). */ char h1, h2; + /** Handle locations, keep together. + * \note For now the two handle types are set to the same type in RNA. */ + float h1_loc[2]; + float h2_loc[2]; + char _pad[4]; + /** Runtime pointer to the point's profile for updating the curve with no direct reference. */ + struct CurveProfile *profile; } CurveProfilePoint; /** #CurveProfilePoint.flag */ enum { PROF_SELECT = (1 << 0), + PROF_H1_SELECT = (1 << 1), + PROF_H2_SELECT = (1 << 2), }; /** Defines a profile. */ @@ -76,10 +85,11 @@ typedef struct CurveProfile { /** #CurveProfile.flag */ enum { - PROF_USE_CLIP = (1 << 0), /* Keep control points inside bounding rectangle. */ - /* PROF_SYMMETRY_MODE = (1 << 1), */ /* Unused for now. */ - PROF_SAMPLE_STRAIGHT_EDGES = (1 << 2), /* Sample extra points on straight edges. */ - PROF_SAMPLE_EVEN_LENGTHS = (1 << 3), /* Put segments evenly spaced along the path. */ + PROF_USE_CLIP = (1 << 0), /* Keep control points inside bounding rectangle. */ + /* PROF_SYMMETRY_MODE = (1 << 1), Unused for now. */ + PROF_SAMPLE_STRAIGHT_EDGES = (1 << 2), /* Sample extra points on straight edges. */ + PROF_SAMPLE_EVEN_LENGTHS = (1 << 3), /* Put segments evenly spaced along the path. */ + PROF_DIRTY_PRESET = (1 << 4), /* Marks when the dynamic preset has been changed. */ }; typedef enum eCurveProfilePresets { diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c index 94a35bdede8..ce91fc79085 100644 --- a/source/blender/makesrna/intern/rna_curveprofile.c +++ b/source/blender/makesrna/intern/rna_curveprofile.c @@ -62,6 +62,22 @@ # include "IMB_colormanagement.h" # include "IMB_imbuf.h" +/** + * Set both handle types for all selected points in the profile-- faster than changing types + * for many points individually. Also set both handles for the points. + */ +static void rna_CurveProfilePoint_handle_type_set(PointerRNA *ptr, int value) +{ + CurveProfilePoint *point = ptr->data; + CurveProfile *profile = point->profile; + + if (profile) { + BKE_curveprofile_selected_handle_set(profile, value, value); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); + WM_main_add_notifier(NC_GEOM | ND_DATA, NULL); + } +} + static void rna_CurveProfile_clip_set(PointerRNA *ptr, bool value) { CurveProfile *profile = (CurveProfile *)ptr->data; @@ -73,7 +89,7 @@ static void rna_CurveProfile_clip_set(PointerRNA *ptr, bool value) profile->flag &= ~PROF_USE_CLIP; } - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_CLIP); } static void rna_CurveProfile_sample_straight_set(PointerRNA *ptr, bool value) @@ -87,7 +103,7 @@ static void rna_CurveProfile_sample_straight_set(PointerRNA *ptr, bool value) profile->flag &= ~PROF_SAMPLE_STRAIGHT_EDGES; } - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); } static void rna_CurveProfile_sample_even_set(PointerRNA *ptr, bool value) @@ -101,7 +117,7 @@ static void rna_CurveProfile_sample_even_set(PointerRNA *ptr, bool value) profile->flag &= ~PROF_SAMPLE_EVEN_LENGTHS; } - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); } static void rna_CurveProfile_remove_point(CurveProfile *profile, @@ -135,14 +151,16 @@ static void rna_CurveProfile_initialize(struct CurveProfile *profile, int segmen static void rna_CurveProfile_update(struct CurveProfile *profile) { - BKE_curveprofile_update(profile, false); + BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP); } #else static const EnumPropertyItem prop_handle_type_items[] = { - {HD_AUTO, "AUTO", 0, "Auto Handle", ""}, - {HD_VECT, "VECTOR", 0, "Vector Handle", ""}, + {HD_AUTO, "AUTO", ICON_HANDLE_AUTO, "Auto Handle", ""}, + {HD_VECT, "VECTOR", ICON_HANDLE_VECTOR, "Vector Handle", ""}, + {HD_FREE, "FREE", ICON_HANDLE_FREE, "Free Handle", ""}, + {HD_ALIGN, "ALIGN", ICON_HANDLE_ALIGNED, "Aligned Free Handles", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -162,14 +180,14 @@ static void rna_def_curveprofilepoint(BlenderRNA *brna) prop = RNA_def_property(srna, "handle_type_1", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "h1"); RNA_def_property_enum_items(prop, prop_handle_type_items); - RNA_def_property_ui_text( - prop, "First Handle Type", "Path interpolation at this point: Bezier or vector"); + RNA_def_property_enum_funcs(prop, NULL, "rna_CurveProfilePoint_handle_type_set", NULL); + RNA_def_property_ui_text(prop, "First Handle Type", "Path interpolation at this point"); prop = RNA_def_property(srna, "handle_type_2", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "h2"); RNA_def_property_enum_items(prop, prop_handle_type_items); - RNA_def_property_ui_text( - prop, "Second Handle Type", "Path interpolation at this point: Bezier or vector"); + RNA_def_property_enum_funcs(prop, NULL, "rna_CurveProfilePoint_handle_type_set", NULL); + RNA_def_property_ui_text(prop, "Second Handle Type", "Path interpolation at this point"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PROF_SELECT); @@ -260,7 +278,7 @@ static void rna_def_curveprofile(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, NULL, "rna_CurveProfile_sample_even_set"); func = RNA_def_function(srna, "update", "rna_CurveProfile_update"); - RNA_def_function_ui_description(func, "Update the profile"); + RNA_def_function_ui_description(func, "Refresh internal data, remove doubles and clip points"); func = RNA_def_function(srna, "initialize", "rna_CurveProfile_initialize"); parm = RNA_def_int(func, |