Fix #119596: Multiple points operations of Curvemap and Curveprofile #120242

Open
XDzZyq wants to merge 15 commits from Zyq-XDz/blender:curve-mapping-multiselect into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
8 changed files with 238 additions and 52 deletions

View File

@ -59,11 +59,17 @@ void BKE_curvemap_remove(CurveMap *cuma, short flag);
*/
bool BKE_curvemap_remove_point(CurveMap *cuma, CurveMapPoint *cmp);
CurveMapPoint *BKE_curvemap_insert(CurveMap *cuma, float x, float y);
/**
* Shift specified point from center.
*/
void BKE_translate_selection(CurveMap *cuma, const float delta_x, const float delta_y);
/**
* \param type: #eBezTriple_Handle
*/
void BKE_curvemap_handle_set(CurveMap *cuma, int type);
void BKE_curvemap_get_selection_center(CurveMap *cuma, float *center_x_out, float *center_y_out);
/**
* \note only does current curvemap!.
*/

View File

@ -64,6 +64,10 @@ bool BKE_curveprofile_move_point(struct CurveProfile *profile,
bool snap,
const float delta[2]);
void BKE_curveprofile_translate_selection(struct CurveProfile *profile,
const float delta_x,
const float delta_y);
/**
* Removes a specific point from the path of control points.
* \note Requires #BKE_curveprofile_update call after.
@ -150,6 +154,9 @@ void BKE_curveprofile_evaluate_length_portion(const struct CurveProfile *profile
float length_portion,
float *x_out,
float *y_out);
void BKE_curveprofile_get_selection_center(const struct CurveProfile *profile,
float *center_x_out,
float *center_y_out);
void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile);
/**

View File

@ -113,6 +113,9 @@ void BKE_curvemapping_free_data(CurveMapping *cumap)
MEM_freeN(cumap->cm[a].premultable);
cumap->cm[a].premultable = nullptr;
}
if (cumap->cm[a].runtime.runtime_storage) {
cumap->cm[a].runtime.runtime_storage_free(cumap->cm[a].runtime.runtime_storage);
}
}
}
@ -141,6 +144,9 @@ void BKE_curvemapping_copy_data(CurveMapping *target, const CurveMapping *cumap)
target->cm[a].premultable = static_cast<CurveMapPoint *>(
MEM_dupallocN(cumap->cm[a].premultable));
}
if (cumap->cm[a].runtime.runtime_storage) {
target->cm[a].runtime.runtime_storage = MEM_dupallocN(cumap->cm[a].runtime.runtime_storage);
}
}
}
@ -930,6 +936,36 @@ void BKE_curvemapping_premultiply(CurveMapping *cumap, bool restore)
}
/* ************************ more CurveMapping calls *************** */
void BKE_curvemap_get_selection_center(CurveMap *cuma, float *center_x_out, float *center_y_out)
{
int n = 0;
*center_x_out = 0.0f;
*center_y_out = 0.0f;
for (int i = 0; i < cuma->totpoint; i++) {
CurveMapPoint *pt = &cuma->curve[i];
if (pt->flag & CUMA_SELECT) {
*center_x_out += pt->x;
*center_y_out += pt->y;
n++;
}
}
if (n > 0) {
*center_x_out /= n;
*center_y_out /= n;
}
}
void BKE_translate_selection(CurveMap *cuma, const float delta_x, const float delta_y)
{
for (int i = 0; i < cuma->totpoint; i++) {
CurveMapPoint *pt = &cuma->curve[i];
if (pt->flag & CUMA_SELECT) {
pt->x += delta_x;
pt->y += delta_y;
}
}
}
void BKE_curvemapping_changed(CurveMapping *cumap, const bool rem_doubles)
{
@ -1387,6 +1423,8 @@ void BKE_curvemapping_blend_read(BlendDataReader *reader, CurveMapping *cumap)
BLO_read_data_address(reader, &cumap->cm[a].curve);
cumap->cm[a].table = nullptr;
cumap->cm[a].premultable = nullptr;
cumap->cm[a].runtime.runtime_storage = nullptr;
cumap->cm[a].runtime.runtime_storage_free = nullptr;
}
}

View File

@ -47,6 +47,9 @@ void BKE_curveprofile_free_data(CurveProfile *profile)
MEM_SAFE_FREE(profile->path);
MEM_SAFE_FREE(profile->table);
MEM_SAFE_FREE(profile->segments);
if (profile->runtime.runtime_storage) {
profile->runtime.runtime_storage_free(profile->runtime.runtime_storage);
}
}
void BKE_curveprofile_free(CurveProfile *profile)
@ -64,6 +67,7 @@ void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profil
target->path = (CurveProfilePoint *)MEM_dupallocN(profile->path);
target->table = (CurveProfilePoint *)MEM_dupallocN(profile->table);
target->segments = (CurveProfilePoint *)MEM_dupallocN(profile->segments);
target->runtime.runtime_storage = MEM_dupallocN(profile->runtime.runtime_storage);
/* Update the reference the points have to the profile. */
for (int i = 0; i < target->path_len; i++) {
@ -92,6 +96,8 @@ void BKE_curveprofile_blend_read(BlendDataReader *reader, CurveProfile *profile)
BLO_read_data_address(reader, &profile->path);
profile->table = nullptr;
profile->segments = nullptr;
profile->runtime.runtime_storage = nullptr;
profile->runtime.runtime_storage_free = nullptr;
/* Reset the points' pointers to the profile. */
for (int i = 0; i < profile->path_len; i++) {
@ -194,6 +200,27 @@ bool BKE_curveprofile_move_point(CurveProfile *profile,
return false;
}
void BKE_curveprofile_translate_selection(struct CurveProfile *profile,
const float delta_x,
const float delta_y)
{
for (int i = 0; i < profile->path_len; i++) {
CurveProfilePoint *pt = &profile->path[i];
if (pt->flag & PROF_SELECT) {
pt->x += delta_x;
pt->y += delta_y;
}
if (pt->flag & PROF_H1_SELECT) {
pt->h1_loc[0] += delta_x;
pt->h1_loc[1] += delta_y;
}
if (pt->flag & PROF_H2_SELECT) {
pt->h2_loc[0] += delta_x;
pt->h2_loc[1] += delta_y;
}
}
}
bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *point)
{
/* Must have 2 points minimum. */
@ -1020,6 +1047,39 @@ void BKE_curveprofile_update(CurveProfile *profile, const int update_flags)
}
}
void BKE_curveprofile_get_selection_center(const struct CurveProfile *profile,
float *center_x_out,
float *center_y_out)
{
int n = 0;
*center_x_out = 0.0f;
*center_y_out = 0.0f;
for (int i = 0; i < profile->path_len; i++) {
CurveProfilePoint *pt = &profile->path[i];
if (pt->flag & PROF_SELECT) {
*center_x_out += pt->x;
*center_y_out += pt->y;
n++;
}
if (pt->flag & PROF_H1_SELECT) {
*center_x_out += pt->h1_loc[0];
*center_y_out += pt->h1_loc[1];
n++;
}
if (pt->flag & PROF_H2_SELECT) {
*center_x_out += pt->h2_loc[0];
*center_y_out += pt->h2_loc[1];
n++;
}
}
if (n > 0) {
*center_x_out /= n;
*center_y_out /= n;
}
}
void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile,
float length_portion,
float *x_out,

View File

@ -107,6 +107,13 @@ using blender::Vector;
#define TEMPLATE_SEARCH_TEXTBUT_MIN_WIDTH (UI_UNIT_X * 4)
#define TEMPLATE_SEARCH_TEXTBUT_HEIGHT UI_UNIT_Y
/* temporary struct for storing curvemap/curveprofile properties */
struct CurveRuntimeProperties {
float center_x;
float center_y;
};
/* -------------------------------------------------------------------- */
/** \name Header Template
* \{ */
@ -4382,6 +4389,24 @@ static void curvemap_buttons_redraw(bContext &C)
ED_region_tag_redraw(CTX_wm_region(&C));
}
static CurveRuntimeProperties *curvemap_runtime_props_ensure(CurveMap *cum)
{
if (cum->runtime.runtime_storage == nullptr) {
CurveRuntimeProperties *crp = static_cast<CurveRuntimeProperties *>(
MEM_callocN(sizeof(CurveRuntimeProperties), "CurveRuntimeProperties"));
/* Construct C++ structures in otherwise zero initialized struct. */
new (crp) CurveRuntimeProperties();
cum->runtime.runtime_storage = crp;
cum->runtime.runtime_storage_free = [](void *properties_storage) {
CurveRuntimeProperties *tar = static_cast<CurveRuntimeProperties *>(properties_storage);
MEM_delete(tar);
};
}
return static_cast<CurveRuntimeProperties *>(cum->runtime.runtime_storage);
}
/**
* \note Still unsure how this call evolves.
*
@ -4558,20 +4583,22 @@ static void curvemap_buttons_layout(uiLayout *layout,
curve_but->gradient_type = bg;
/* Sliders for selected curve point. */
int i;
CurveMapPoint *cmp = nullptr;
Vector<CurveMapPoint *> cmps;
bool point_last_or_first = false;
for (i = 0; i < cm->totpoint; i++) {
if (cm->curve[i].flag & CUMA_SELECT) {
cmp = &cm->curve[i];
break;
for (int i = 0; i < cm->totpoint; i++) {
const bool selected = cm->curve[i].flag & CUMA_SELECT;
if (selected) {
cmps.append(&cm->curve[i]);
}
if (ELEM(i, 0, cm->totpoint - 1) && selected) {
point_last_or_first = true;
}
}
if (ELEM(i, 0, cm->totpoint - 1)) {
point_last_or_first = true;
}
if (cmp) {
if (!cmps.is_empty()) {
CurveMap *active_cm = cumap->cm + cumap->cur;
CurveRuntimeProperties *crp = curvemap_runtime_props_ensure(active_cm);
rctf bounds;
if (cumap->flag & CUMA_DO_CLIP) {
bounds = cumap->clipr;
@ -4604,10 +4631,11 @@ static void curvemap_buttons_layout(uiLayout *layout,
BKE_curvemapping_changed(cumap, false);
rna_update_cb(C, cb);
});
if (((cmp->flag & CUMA_HANDLE_AUTO_ANIM) == false) &&
((cmp->flag & CUMA_HANDLE_VECTOR) == false))
{
bt->flag |= UI_SELECT_DRAW;
for (const CurveMapPoint *cmp : cmps) {
const bool auto_anim_vec = ((cmp->flag & CUMA_HANDLE_AUTO_ANIM) == false) &&
((cmp->flag & CUMA_HANDLE_VECTOR) == false);
bt->flag |= UI_SELECT_DRAW && auto_anim_vec;
}
bt = uiDefIconBut(block,
@ -4628,8 +4656,10 @@ static void curvemap_buttons_layout(uiLayout *layout,
BKE_curvemapping_changed(cumap, false);
rna_update_cb(C, cb);
});
if (cmp->flag & CUMA_HANDLE_VECTOR) {
bt->flag |= UI_SELECT_DRAW;
for (const CurveMapPoint *cmp : cmps) {
const bool vec = (cmp->flag & CUMA_HANDLE_VECTOR);
bt->flag |= UI_SELECT_DRAW && vec;
}
bt = uiDefIconBut(block,
@ -4650,11 +4680,14 @@ static void curvemap_buttons_layout(uiLayout *layout,
BKE_curvemapping_changed(cumap, false);
rna_update_cb(C, cb);
});
if (cmp->flag & CUMA_HANDLE_AUTO_ANIM) {
bt->flag |= UI_SELECT_DRAW;
for (const CurveMapPoint *cmp : cmps) {
const bool auto_anim = (cmp->flag & CUMA_HANDLE_AUTO_ANIM);
bt->flag |= UI_SELECT_DRAW && auto_anim;
}
/* Curve handle position */
BKE_curvemap_get_selection_center(active_cm, &crp->center_x, &crp->center_y);
Zyq-XDz marked this conversation as resolved Outdated
for (CurveMapPoint *cmp : cmps) {
  ...
}

See: https://developer.blender.org/docs/handbook/guidelines/c_cpp/#braces

And the same above.

```Cpp for (CurveMapPoint *cmp : cmps) { ... } ``` See: https://developer.blender.org/docs/handbook/guidelines/c_cpp/#braces And the same above.
bt = uiDefButF(block,
UI_BTYPE_NUM,
0,
@ -4663,13 +4696,19 @@ static void curvemap_buttons_layout(uiLayout *layout,
2 * UI_UNIT_Y,
UI_UNIT_X * 10,
UI_UNIT_Y,
&cmp->x,
&crp->center_x,
bounds.xmin,
bounds.xmax,
"");
UI_but_number_step_size_set(bt, 1);
UI_but_number_precision_set(bt, 5);
UI_but_func_set(bt, [cumap, cb](bContext &C) {
CurveMap *cuma = cumap->cm + cumap->cur;
CurveRuntimeProperties *crp = curvemap_runtime_props_ensure(cuma);
float center_x_pre = 0.0f;
float center_y_pre = 0.0f;
BKE_curvemap_get_selection_center(cuma, &center_x_pre, &center_y_pre);
BKE_translate_selection(cuma, crp->center_x - center_x_pre, crp->center_y - center_y_pre);
BKE_curvemapping_changed(cumap, true);
rna_update_cb(C, cb);
});
@ -4682,13 +4721,19 @@ static void curvemap_buttons_layout(uiLayout *layout,
1 * UI_UNIT_Y,
UI_UNIT_X * 10,
UI_UNIT_Y,
&cmp->y,
&crp->center_y,
bounds.ymin,
bounds.ymax,
"");
UI_but_number_step_size_set(bt, 1);
UI_but_number_precision_set(bt, 5);
UI_but_func_set(bt, [cumap, cb](bContext &C) {
CurveMap *cuma = cumap->cm + cumap->cur;
CurveRuntimeProperties *crp = curvemap_runtime_props_ensure(cuma);
float center_x_pre = 0.0f;
float center_y_pre = 0.0f;
BKE_curvemap_get_selection_center(cuma, &center_x_pre, &center_y_pre);
BKE_translate_selection(cuma, crp->center_x - center_x_pre, crp->center_y - center_y_pre);
BKE_curvemapping_changed(cumap, true);
rna_update_cb(C, cb);
});
@ -4901,6 +4946,22 @@ static bool curve_profile_can_zoom_out(CurveProfile *profile)
return BLI_rctf_size_x(&profile->view_rect) < BLI_rctf_size_x(&profile->clip_rect);
}
static CurveRuntimeProperties *curve_profile_runtime_props_ensure(CurveProfile *profile)
{
if (profile->runtime.runtime_storage == nullptr) {
CurveRuntimeProperties *crp = static_cast<CurveRuntimeProperties *>(
MEM_callocN(sizeof(CurveRuntimeProperties), "CurveRuntimeProperties"));
/* Construct C++ structures in otherwise zero initialized struct. */
new (crp) CurveRuntimeProperties();
profile->runtime.runtime_storage = crp;
profile->runtime.runtime_storage_free = [](void *properties_storage) {
MEM_delete(static_cast<CurveRuntimeProperties *>(properties_storage));
};
}
return static_cast<CurveRuntimeProperties *>(profile->runtime.runtime_storage);
}
static void curve_profile_zoom_in(bContext *C, CurveProfile *profile)
{
if (curve_profile_can_zoom_in(profile)) {
@ -4960,6 +5021,7 @@ static void curve_profile_zoom_out(bContext *C, CurveProfile *profile)
static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, const RNAUpdateCb &cb)
{
CurveProfile *profile = static_cast<CurveProfile *>(ptr->data);
CurveRuntimeProperties *crp = curve_profile_runtime_props_ensure(profile);
uiBut *bt;
uiBlock *block = uiLayoutGetBlock(layout);
@ -5132,36 +5194,21 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, const
1.0f,
"");
/* Position sliders for (first) selected point */
int i;
float *selection_x, *selection_y;
/* Position sliders for all selected points */
Vector<CurveProfilePoint *> cfps;
bool point_last_or_first = false;
CurveProfilePoint *point = nullptr;
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;
for (int i = 0; i < profile->path_len; i++) {
if (profile->path[i].flag & (PROF_SELECT | PROF_H1_SELECT | PROF_H2_SELECT)) {
cfps.append(&profile->path[i]);
if (ELEM(i, 0, profile->path_len - 1) && profile->path[i].flag & PROF_SELECT) {
point_last_or_first = true;
}
}
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 (ELEM(i, 0, profile->path_len - 1)) {
point_last_or_first = true;
}
/* Selected point data */
rctf bounds;
if (point) {
/* Selected points data */
if (!cfps.is_empty()) {
rctf bounds;
if (profile->flag & PROF_USE_CLIP) {
bounds = profile->clip_rect;
}
@ -5172,7 +5219,8 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, const
row = uiLayoutRow(layout, true);
PointerRNA point_ptr = RNA_pointer_create(ptr->owner_id, &RNA_CurveProfilePoint, point);
/* TODO: Buttons for multiple selections */
PointerRNA point_ptr = RNA_pointer_create(ptr->owner_id, &RNA_CurveProfilePoint, cfps[0]);
PropertyRNA *prop_handle_type = RNA_struct_find_property(&point_ptr, "handle_type_1");
uiItemFullR(row,
&point_ptr,
@ -5184,6 +5232,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, const
ICON_NONE);
/* Position */
BKE_curveprofile_get_selection_center(profile, &crp->center_x, &crp->center_y);
bt = uiDefButF(block,
UI_BTYPE_NUM,
0,
@ -5192,13 +5241,18 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, const
2 * UI_UNIT_Y,
UI_UNIT_X * 10,
UI_UNIT_Y,
selection_x,
&crp->center_x,
bounds.xmin,
bounds.xmax,
"");
UI_but_number_step_size_set(bt, 1);
UI_but_number_precision_set(bt, 5);
UI_but_func_set(bt, [profile, cb](bContext &C) {
UI_but_func_set(bt, [profile, crp, cb](bContext &C) {
float center_x_pre = 0.0f;
float center_y_pre = 0.0f;
BKE_curveprofile_get_selection_center(profile, &center_x_pre, &center_y_pre);
BKE_curveprofile_translate_selection(
profile, crp->center_x - center_x_pre, crp->center_y - center_y_pre);
BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP);
rna_update_cb(C, cb);
});
@ -5213,13 +5267,18 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, const
1 * UI_UNIT_Y,
UI_UNIT_X * 10,
UI_UNIT_Y,
selection_y,
&crp->center_y,
bounds.ymin,
bounds.ymax,
"");
UI_but_number_step_size_set(bt, 1);
UI_but_number_precision_set(bt, 5);
UI_but_func_set(bt, [profile, cb](bContext &C) {
UI_but_func_set(bt, [profile, crp, cb](bContext &C) {
float center_x_pre = 0.0f;
float center_y_pre = 0.0f;
BKE_curveprofile_get_selection_center(profile, &center_x_pre, &center_y_pre);
BKE_curveprofile_translate_selection(
profile, crp->center_x - center_x_pre, crp->center_y - center_y_pre);
BKE_curveprofile_update(profile, PROF_UPDATE_REMOVE_DOUBLES | PROF_UPDATE_CLIP);
rna_update_cb(C, cb);
});

View File

@ -27,6 +27,12 @@ typedef struct CurveMapPoint {
short flag, shorty;
} CurveMapPoint;
typedef struct CurveMap_Runtime {
/** Temp storage for multiple selections operation. */
void *runtime_storage;
void (*runtime_storage_free)(void *properties_storage);
} CurveMap_Runtime;
/** #CurveMapPoint.flag */
enum {
CUMA_SELECT = (1 << 0),
@ -58,6 +64,8 @@ typedef struct CurveMap {
float premul_ext_out[2];
short default_handle_type;
char _pad[6];
CurveMap_Runtime runtime;
} CurveMap;
typedef struct CurveMapping {

View File

@ -31,6 +31,12 @@ typedef struct CurveProfilePoint {
struct CurveProfile *profile;
} CurveProfilePoint;
typedef struct CurveProfile_Runtime {
/** Temp storage for multiple selections operation. */
void *runtime_storage;
void (*runtime_storage_free)(void *properties_storage);
} CurveProfile_Runtime;
/** #CurveProfilePoint.flag */
enum {
PROF_SELECT = (1 << 0),
@ -58,6 +64,8 @@ typedef struct CurveProfile {
int changed_timestamp;
/** Widget's current view, and clipping rect (is default rect too). */
rctf view_rect, clip_rect;
CurveProfile_Runtime runtime;
} CurveProfile;
/** #CurveProfile.flag */

View File

@ -32,7 +32,7 @@ static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
static void node_composit_init_huecorrect(bNodeTree * /*ntree*/, bNode *node)
{
node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
node->storage = BKE_curvemapping_add(3, 0.0f, 0.0f, 1.0f, 1.0f);
CurveMapping *cumapping = (CurveMapping *)node->storage;