Fix: View in Graph Editor not working when object is unselected
The new operator "View in Graph Editor" (#114407) only looked at the selection when generating the FCurve bounds. This failed in certain cases if things are not selected, or not selectable. The simplest example is to key the emission strength of the world settings and try to view that. This was mentioned by Jonathan Lampel on the PR #114407 The fix is to also resolve the bounds of the animation data on the button. This means for a few FCurves the bounds might be calculated twice but it doesn't change the overall result. This has another side effect though: The Graph Editor might zoom into an area where there is no FCurve visible because the thing is not selected so it isn't shown. This issue existed before if filters were applied, but is now more pronounced since by default the Graph Editor doesn't show unselected objects. A warning is raised when at least one of the FCurves that are viewed is not visible with the current filter settings. The view will still zoom into that area. Pull Request: https://projects.blender.org/blender/blender/pulls/118658
This commit is contained in:
committed by
Christoph Lendenfeld
parent
5d1d447c2b
commit
4d958ca19a
@@ -4532,13 +4532,70 @@ static void deselect_all_fcurves(bAnimContext *ac, const bool hide)
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
static rctf calculate_selection_fcurve_bounds_and_unhide(
|
||||
bContext *C,
|
||||
ListBase /* CollectionPointerLink */ *selection,
|
||||
PropertyRNA *prop,
|
||||
const blender::StringRefNull id_to_prop_path,
|
||||
const int index,
|
||||
const bool whole_array)
|
||||
static int count_fcurves_hidden_by_filter(bAnimContext *ac, const blender::Span<FCurve *> fcurves)
|
||||
{
|
||||
ListBase anim_data = {nullptr, nullptr};
|
||||
if (ac->sl->spacetype != SPACE_GRAPH) {
|
||||
return 0;
|
||||
}
|
||||
SpaceGraph *sipo = (SpaceGraph *)ac->sl;
|
||||
const eAnimFilter_Flags filter = eAnimFilter_Flags(sipo->ads->filterflag);
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
|
||||
|
||||
/* Adding FCurves to a map for quicker lookup times. */
|
||||
blender::Map<FCurve *, bool> filtered_fcurves;
|
||||
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
filtered_fcurves.add(fcu, true);
|
||||
}
|
||||
|
||||
int hidden_fcurve_count = fcurves.size();
|
||||
for (FCurve *fcu : fcurves) {
|
||||
if (filtered_fcurves.contains(fcu)) {
|
||||
hidden_fcurve_count--;
|
||||
}
|
||||
}
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
return hidden_fcurve_count;
|
||||
}
|
||||
|
||||
static blender::Vector<FCurve *> get_fcurves_of_property(
|
||||
ID *id, PointerRNA *ptr, PropertyRNA *prop, const bool whole_array, const int index)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
AnimData *anim_data = BKE_animdata_from_id(id);
|
||||
if (anim_data == nullptr) {
|
||||
return Vector<FCurve *>();
|
||||
}
|
||||
|
||||
const std::optional<std::string> path = RNA_path_from_ID_to_property(ptr, prop);
|
||||
|
||||
blender::Vector<FCurve *> fcurves;
|
||||
if (RNA_property_array_check(prop) && whole_array) {
|
||||
const int length = RNA_property_array_length(ptr, prop);
|
||||
for (int i = 0; i < length; i++) {
|
||||
FCurve *fcurve = BKE_animadata_fcurve_find_by_rna_path(
|
||||
anim_data, path->c_str(), i, nullptr, nullptr);
|
||||
if (fcurve != nullptr) {
|
||||
fcurves.append(fcurve);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
FCurve *fcurve = BKE_animadata_fcurve_find_by_rna_path(
|
||||
anim_data, path->c_str(), index, nullptr, nullptr);
|
||||
if (fcurve != nullptr) {
|
||||
fcurves.append(fcurve);
|
||||
}
|
||||
}
|
||||
return fcurves;
|
||||
}
|
||||
|
||||
static rctf calculate_fcurve_bounds_and_unhide(SpaceLink *space_link,
|
||||
Scene *scene,
|
||||
ID *id,
|
||||
const blender::Span<FCurve *> fcurves)
|
||||
{
|
||||
rctf bounds;
|
||||
bounds.xmin = INFINITY;
|
||||
@@ -4546,16 +4603,59 @@ static rctf calculate_selection_fcurve_bounds_and_unhide(
|
||||
bounds.ymin = INFINITY;
|
||||
bounds.ymax = -INFINITY;
|
||||
|
||||
SpaceLink *ge_space_link = CTX_wm_space_data(C);
|
||||
if (ge_space_link->spacetype != SPACE_GRAPH) {
|
||||
if (space_link->spacetype != SPACE_GRAPH) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
AnimData *anim_data = BKE_animdata_from_id(id);
|
||||
if (anim_data == nullptr) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
float frame_range[2];
|
||||
get_view_range(scene, true, frame_range);
|
||||
float mapped_frame_range[2];
|
||||
mapped_frame_range[0] = BKE_nla_tweakedit_remap(
|
||||
anim_data, frame_range[0], NLATIME_CONVERT_UNMAP);
|
||||
mapped_frame_range[1] = BKE_nla_tweakedit_remap(
|
||||
anim_data, frame_range[1], NLATIME_CONVERT_UNMAP);
|
||||
|
||||
const bool include_handles = false;
|
||||
|
||||
for (FCurve *fcurve : fcurves) {
|
||||
fcurve->flag |= (FCURVE_SELECTED | FCURVE_VISIBLE);
|
||||
rctf fcu_bounds;
|
||||
get_normalized_fcurve_bounds(fcurve,
|
||||
anim_data,
|
||||
space_link,
|
||||
scene,
|
||||
id,
|
||||
include_handles,
|
||||
mapped_frame_range,
|
||||
&fcu_bounds);
|
||||
|
||||
if (BLI_rctf_is_valid(&fcu_bounds)) {
|
||||
BLI_rctf_union(&bounds, &fcu_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
static rctf calculate_selection_fcurve_bounds(bAnimContext *ac,
|
||||
ListBase /* CollectionPointerLink */ *selection,
|
||||
PropertyRNA *prop,
|
||||
const blender::StringRefNull id_to_prop_path,
|
||||
const int index,
|
||||
const bool whole_array,
|
||||
int *r_filtered_fcurve_count)
|
||||
{
|
||||
rctf bounds;
|
||||
bounds.xmin = INFINITY;
|
||||
bounds.xmax = -INFINITY;
|
||||
bounds.ymin = INFINITY;
|
||||
bounds.ymax = -INFINITY;
|
||||
|
||||
LISTBASE_FOREACH (CollectionPointerLink *, selected, selection) {
|
||||
ID *selected_id = selected->ptr.owner_id;
|
||||
if (!BKE_animdata_id_is_animated(selected_id)) {
|
||||
@@ -4574,45 +4674,11 @@ static rctf calculate_selection_fcurve_bounds_and_unhide(
|
||||
resolved_ptr = selected->ptr;
|
||||
resolved_prop = prop;
|
||||
}
|
||||
const std::optional<std::string> path = RNA_path_from_ID_to_property(&resolved_ptr,
|
||||
resolved_prop);
|
||||
|
||||
AnimData *anim_data = BKE_animdata_from_id(selected_id);
|
||||
blender::Vector<FCurve *> fcurves;
|
||||
if (RNA_property_array_check(prop) && whole_array) {
|
||||
const int length = RNA_property_array_length(&selected->ptr, prop);
|
||||
for (int i = 0; i < length; i++) {
|
||||
FCurve *fcurve = BKE_animadata_fcurve_find_by_rna_path(
|
||||
anim_data, path->c_str(), i, nullptr, nullptr);
|
||||
if (fcurve != nullptr) {
|
||||
fcurves.append(fcurve);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
FCurve *fcurve = BKE_animadata_fcurve_find_by_rna_path(
|
||||
anim_data, path->c_str(), index, nullptr, nullptr);
|
||||
if (fcurve != nullptr) {
|
||||
fcurves.append(fcurve);
|
||||
}
|
||||
}
|
||||
|
||||
for (FCurve *fcurve : fcurves) {
|
||||
fcurve->flag |= (FCURVE_SELECTED | FCURVE_VISIBLE);
|
||||
rctf fcu_bounds;
|
||||
float mapped_frame_range[2];
|
||||
mapped_frame_range[0] = BKE_nla_tweakedit_remap(
|
||||
anim_data, frame_range[0], NLATIME_CONVERT_UNMAP);
|
||||
mapped_frame_range[1] = BKE_nla_tweakedit_remap(
|
||||
anim_data, frame_range[1], NLATIME_CONVERT_UNMAP);
|
||||
get_normalized_fcurve_bounds(fcurve,
|
||||
anim_data,
|
||||
ge_space_link,
|
||||
scene,
|
||||
selected_id,
|
||||
include_handles,
|
||||
mapped_frame_range,
|
||||
&fcu_bounds);
|
||||
blender::Vector<FCurve *> fcurves = get_fcurves_of_property(
|
||||
selected_id, &resolved_ptr, resolved_prop, whole_array, index);
|
||||
*r_filtered_fcurve_count += count_fcurves_hidden_by_filter(ac, fcurves);
|
||||
rctf fcu_bounds = calculate_fcurve_bounds_and_unhide(ac->sl, ac->scene, selected_id, fcurves);
|
||||
if (BLI_rctf_is_valid(&fcu_bounds)) {
|
||||
BLI_rctf_union(&bounds, &fcu_bounds);
|
||||
}
|
||||
}
|
||||
@@ -4622,12 +4688,12 @@ static rctf calculate_selection_fcurve_bounds_and_unhide(
|
||||
|
||||
static int view_curve_in_graph_editor_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
PointerRNA ptr = {nullptr};
|
||||
PropertyRNA *prop = nullptr;
|
||||
PointerRNA button_ptr = {nullptr};
|
||||
PropertyRNA *button_prop = nullptr;
|
||||
uiBut *but;
|
||||
int index;
|
||||
|
||||
if (!(but = UI_context_active_but_prop_get(C, &ptr, &prop, &index))) {
|
||||
if (!(but = UI_context_active_but_prop_get(C, &button_ptr, &button_prop, &index))) {
|
||||
/* Pass event on if no active button found. */
|
||||
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
|
||||
}
|
||||
@@ -4645,14 +4711,10 @@ static int view_curve_in_graph_editor_exec(bContext *C, wmOperator *op)
|
||||
bool path_from_id;
|
||||
std::optional<std::string> id_to_prop_path;
|
||||
const bool selected_list_success = UI_context_copy_to_selected_list(
|
||||
C, &ptr, prop, &selection, &path_from_id, &id_to_prop_path);
|
||||
C, &button_ptr, button_prop, &selection, &path_from_id, &id_to_prop_path);
|
||||
|
||||
if (BLI_listbase_is_empty(&selection) || !selected_list_success) {
|
||||
WM_report(RPT_ERROR, "Nothing selected");
|
||||
retval = OPERATOR_CANCELLED;
|
||||
}
|
||||
else if (!context_find_graph_editor(
|
||||
C, &wm_context_temp.win, &wm_context_temp.area, &wm_context_temp.region))
|
||||
if (!context_find_graph_editor(
|
||||
C, &wm_context_temp.win, &wm_context_temp.area, &wm_context_temp.region))
|
||||
{
|
||||
WM_report(RPT_WARNING, "No open Graph Editor window found");
|
||||
retval = OPERATOR_CANCELLED;
|
||||
@@ -4674,12 +4736,43 @@ static int view_curve_in_graph_editor_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
else {
|
||||
const bool isolate = RNA_boolean_get(op->ptr, "isolate");
|
||||
/* The index can be less than 0 e.g. on color properties. */
|
||||
const bool whole_array = RNA_boolean_get(op->ptr, "all") || index < 0;
|
||||
|
||||
deselect_all_fcurves(&ac, isolate);
|
||||
|
||||
const bool whole_array = RNA_boolean_get(op->ptr, "all");
|
||||
rctf bounds;
|
||||
bounds.xmin = INFINITY;
|
||||
bounds.xmax = -INFINITY;
|
||||
bounds.ymin = INFINITY;
|
||||
bounds.ymax = -INFINITY;
|
||||
int filtered_fcurve_count = 0;
|
||||
if (selected_list_success && !BLI_listbase_is_empty(&selection)) {
|
||||
rctf selection_bounds = calculate_selection_fcurve_bounds(&ac,
|
||||
&selection,
|
||||
button_prop,
|
||||
id_to_prop_path.value_or(""),
|
||||
index,
|
||||
whole_array,
|
||||
&filtered_fcurve_count);
|
||||
if (BLI_rctf_is_valid(&selection_bounds)) {
|
||||
BLI_rctf_union(&bounds, &selection_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
rctf bounds = calculate_selection_fcurve_bounds_and_unhide(
|
||||
C, &selection, prop, id_to_prop_path.value_or(""), index, whole_array);
|
||||
/* The object to which the button belongs might not be selected, or selectable. */
|
||||
blender::Vector<FCurve *> button_fcurves = get_fcurves_of_property(
|
||||
button_ptr.owner_id, &button_ptr, button_prop, whole_array, index);
|
||||
filtered_fcurve_count += count_fcurves_hidden_by_filter(&ac, button_fcurves);
|
||||
rctf button_bounds = calculate_fcurve_bounds_and_unhide(
|
||||
ac.sl, ac.scene, button_ptr.owner_id, button_fcurves);
|
||||
if (BLI_rctf_is_valid(&button_bounds)) {
|
||||
BLI_rctf_union(&bounds, &button_bounds);
|
||||
}
|
||||
|
||||
if (filtered_fcurve_count > 0) {
|
||||
WM_report(RPT_WARNING, "One or more F-Curves are not visible due to filter settings");
|
||||
}
|
||||
|
||||
if (!BLI_rctf_is_valid(&bounds)) {
|
||||
WM_report(RPT_ERROR, "F-Curves have no valid size");
|
||||
|
||||
Reference in New Issue
Block a user