Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/curveprofile.c')
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c295
1 files changed, 217 insertions, 78 deletions
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
index a748dfbab3e..8577990dc03 100644
--- a/source/blender/blenkernel/intern/curveprofile.c
+++ b/source/blender/blenkernel/intern/curveprofile.c
@@ -41,6 +41,8 @@
#include "BKE_curveprofile.h"
#include "BKE_fcurve.h"
+#include "BLO_read_write.h"
+
void BKE_curveprofile_free_data(CurveProfile *profile)
{
MEM_SAFE_FREE(profile->path);
@@ -63,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)
@@ -76,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 delta: 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 delta: 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.
*/
@@ -98,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;
@@ -178,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 {
@@ -199,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;
}
}
@@ -210,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_1, type_2: 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);
}
}
}
@@ -259,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 */
@@ -446,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;
@@ -463,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)
{
@@ -598,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,
@@ -619,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++) {
@@ -718,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],
@@ -738,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);
@@ -755,6 +874,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile,
}
printf("\n");
#endif
+
MEM_freeN(bezt);
MEM_freeN(curve_sorted);
MEM_freeN(n_samples);
@@ -766,7 +886,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile,
*/
static void curveprofile_make_table(CurveProfile *profile)
{
- int n_samples = PROF_N_TABLE(profile->path_len);
+ int n_samples = PROF_TABLE_LEN(profile->path_len);
CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1),
"high-res table");
@@ -825,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;
}
@@ -850,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++;
@@ -864,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)) {
@@ -882,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. */
@@ -925,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);
}
/**
@@ -939,7 +1059,7 @@ void BKE_curveprofile_initialize(CurveProfile *profile, short segments_len)
*/
static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i)
{
- BLI_assert(i < PROF_N_TABLE(profile->path_len));
+ BLI_assert(i < PROF_TABLE_LEN(profile->path_len));
return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
}
@@ -952,7 +1072,7 @@ static float curveprofile_distance_to_next_table_point(const CurveProfile *profi
float BKE_curveprofile_total_length(const CurveProfile *profile)
{
float total_length = 0;
- for (int i = 0; i < PROF_N_TABLE(profile->path_len) - 1; i++) {
+ for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) {
total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x);
}
return total_length;
@@ -1040,7 +1160,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile,
float length_travelled = 0.0f;
while (length_travelled < requested_length) {
/* Check if we reached the last point before the final one. */
- if (i == PROF_N_TABLE(profile->path_len) - 2) {
+ if (i == PROF_TABLE_LEN(profile->path_len) - 2) {
break;
}
float new_length = curveprofile_distance_to_next_table_point(profile, i);
@@ -1070,3 +1190,22 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile,
*x_out = interpf(profile->table[i].x, profile->table[i + 1].x, lerp_factor);
*y_out = interpf(profile->table[i].y, profile->table[i + 1].y, lerp_factor);
}
+
+void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile)
+{
+ BLO_write_struct(writer, CurveProfile, profile);
+ BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path);
+}
+
+/* Expects that the curve profile itself has been read already. */
+void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile)
+{
+ 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;
+ }
+}