Anim: View FCurve of Property in the Graph Editor
This commit adds a new operator that allows to view an FCurve in the Graph Editor from the animated property. # Features * Frame a single property or a whole array property by right-clicking an animated property * Works on a property anywhere in blender * Framed FCurves are selected and set to visible * Works on the selection. If an object/bone doesn't have a property it is ignored. * Works with NLA offset and normalization * Isolate curves. This is a property on the operator # Caveats * Frames on the first Graph Editor it finds * Since it works on the selection but the n-panel works on the active object, you can create a situation where nothing happens because you can have an active object without it being selected. * Assigning a shortcut doesn't work through right clicking the menu entry. You have to go to the keymap and create a new entry manually (e.g. in the user interface category) Pull Request: https://projects.blender.org/blender/blender/pulls/114407
This commit is contained in:
committed by
Christoph Lendenfeld
parent
78b2c15c4d
commit
a91a8f3fed
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_path.hh"
|
||||
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_anim_data.h"
|
||||
@@ -40,6 +41,7 @@
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_workspace.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
@@ -67,7 +69,7 @@ static bool get_normalized_fcurve_bounds(FCurve *fcu,
|
||||
AnimData *anim_data,
|
||||
SpaceLink *space_link,
|
||||
Scene *scene,
|
||||
bAnimListElem *ale,
|
||||
ID *id,
|
||||
const bool include_handles,
|
||||
const float range[2],
|
||||
rctf *r_bounds)
|
||||
@@ -83,7 +85,7 @@ static bool get_normalized_fcurve_bounds(FCurve *fcu,
|
||||
const short mapping_flag = ANIM_get_normalization_flags(space_link);
|
||||
|
||||
float offset;
|
||||
const float unit_fac = ANIM_unit_mapping_get_factor(scene, ale->id, fcu, mapping_flag, &offset);
|
||||
const float unit_fac = ANIM_unit_mapping_get_factor(scene, id, fcu, mapping_flag, &offset);
|
||||
|
||||
r_bounds->ymin = (r_bounds->ymin + offset) * unit_fac;
|
||||
r_bounds->ymax = (r_bounds->ymax + offset) * unit_fac;
|
||||
@@ -181,7 +183,7 @@ static bool get_channel_bounds(bAnimContext *ac,
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
AnimData *anim_data = ANIM_nla_mapping_get(ac, ale);
|
||||
found_bounds = get_normalized_fcurve_bounds(
|
||||
fcu, anim_data, ac->sl, ac->scene, ale, include_handles, range, r_bounds);
|
||||
fcu, anim_data, ac->sl, ac->scene, ale->id, include_handles, range, r_bounds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -897,6 +899,7 @@ void ANIM_frame_channel_y_extents(bContext *C, bAnimContext *ac)
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -4307,6 +4310,239 @@ static void ANIM_OT_channel_view_pick(wmOperatorType *ot)
|
||||
"Ignore frames outside of the preview range");
|
||||
}
|
||||
|
||||
/* Find a Graph Editor area and modify the given context to be the window region of it. */
|
||||
static bool move_context_to_graph_editor(bContext *C)
|
||||
{
|
||||
bool found_graph_editor = false;
|
||||
LISTBASE_FOREACH (wmWindow *, win, &CTX_wm_manager(C)->windows) {
|
||||
bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
|
||||
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
if (area->spacetype != SPACE_GRAPH) {
|
||||
continue;
|
||||
}
|
||||
ARegion *window_region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
|
||||
|
||||
if (!window_region) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CTX_wm_window_set(C, win);
|
||||
CTX_wm_screen_set(C, screen);
|
||||
CTX_wm_area_set(C, area);
|
||||
CTX_wm_region_set(C, window_region);
|
||||
found_graph_editor = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found_graph_editor) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found_graph_editor;
|
||||
}
|
||||
|
||||
static void deselect_all_fcurves(bAnimContext *ac, const bool hide)
|
||||
{
|
||||
ListBase anim_data = {nullptr, nullptr};
|
||||
const eAnimFilter_Flags filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE |
|
||||
ANIMFILTER_FCURVESONLY | ANIMFILTER_NODUPLIS);
|
||||
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
|
||||
|
||||
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
|
||||
FCurve *fcu = (FCurve *)ale->key_data;
|
||||
fcu->flag &= ~FCURVE_SELECTED;
|
||||
fcu->flag &= ~FCURVE_ACTIVE;
|
||||
if (hide) {
|
||||
fcu->flag &= ~FCURVE_VISIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
}
|
||||
|
||||
static rctf calculate_selection_fcurve_bounds_and_unhide(
|
||||
bContext *C,
|
||||
ListBase /* CollectionPointerLink */ *selection,
|
||||
PropertyRNA *prop,
|
||||
char *id_to_prop_path,
|
||||
const int index,
|
||||
const bool whole_array)
|
||||
{
|
||||
rctf bounds;
|
||||
bounds.xmin = INFINITY;
|
||||
bounds.xmax = -INFINITY;
|
||||
bounds.ymin = INFINITY;
|
||||
bounds.ymax = -INFINITY;
|
||||
|
||||
SpaceLink *ge_space_link = CTX_wm_space_data(C);
|
||||
if (ge_space_link->spacetype != SPACE_GRAPH) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
float frame_range[2];
|
||||
get_view_range(scene, true, frame_range);
|
||||
const bool include_handles = false;
|
||||
|
||||
LISTBASE_FOREACH (CollectionPointerLink *, selected, selection) {
|
||||
ID *selected_id = selected->ptr.owner_id;
|
||||
if (!BKE_animdata_id_is_animated(selected_id)) {
|
||||
continue;
|
||||
}
|
||||
PointerRNA resolved_ptr;
|
||||
PropertyRNA *resolved_prop;
|
||||
if (id_to_prop_path != nullptr) {
|
||||
const bool resolved = RNA_path_resolve_property(
|
||||
&selected->ptr, id_to_prop_path, &resolved_ptr, &resolved_prop);
|
||||
if (!resolved) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
resolved_ptr = selected->ptr;
|
||||
resolved_prop = prop;
|
||||
}
|
||||
char *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, i, nullptr, nullptr);
|
||||
if (fcurve != nullptr) {
|
||||
fcurves.append(fcurve);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
FCurve *fcurve = BKE_animadata_fcurve_find_by_rna_path(
|
||||
anim_data, path, index, nullptr, nullptr);
|
||||
if (fcurve != nullptr) {
|
||||
fcurves.append(fcurve);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_freeN(path);
|
||||
|
||||
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);
|
||||
BLI_rctf_union(&bounds, &fcu_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
static int view_curve_in_graph_editor_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
PointerRNA ptr = {nullptr};
|
||||
PropertyRNA *prop = nullptr;
|
||||
uiBut *but;
|
||||
int index;
|
||||
|
||||
if (!(but = UI_context_active_but_prop_get(C, &ptr, &prop, &index))) {
|
||||
/* Pass event on if no active button found. */
|
||||
return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
|
||||
}
|
||||
|
||||
ListBase selection = {nullptr, nullptr};
|
||||
|
||||
bool path_from_id;
|
||||
char *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);
|
||||
|
||||
if (BLI_listbase_is_empty(&selection) || !selected_list_success) {
|
||||
WM_report(RPT_ERROR, "Nothing selected");
|
||||
BLI_freelistN(&selection);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const bool found_graph_editor = move_context_to_graph_editor(C);
|
||||
|
||||
if (!found_graph_editor) {
|
||||
WM_report(RPT_WARNING, "No open Graph Editor window found");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bAnimContext ac;
|
||||
if (!ANIM_animdata_get_context(C, &ac)) {
|
||||
/* This might never be called since we are manually setting the Graph Editor just before. */
|
||||
WM_report(RPT_ERROR, "Cannot create the Animation Context");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const bool isolate = RNA_boolean_get(op->ptr, "isolate");
|
||||
deselect_all_fcurves(&ac, isolate);
|
||||
|
||||
const bool whole_array = RNA_boolean_get(op->ptr, "all");
|
||||
|
||||
rctf bounds = calculate_selection_fcurve_bounds_and_unhide(
|
||||
C, &selection, prop, id_to_prop_path, index, whole_array);
|
||||
|
||||
BLI_freelistN(&selection);
|
||||
|
||||
if (id_to_prop_path != nullptr) {
|
||||
MEM_freeN(id_to_prop_path);
|
||||
}
|
||||
|
||||
if (!BLI_rctf_is_valid(&bounds)) {
|
||||
WM_report(RPT_ERROR, "F-Curves have no valid size");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
add_region_padding(C, region, &bounds);
|
||||
|
||||
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
|
||||
UI_view2d_smooth_view(C, region, &bounds, smooth_viewtx);
|
||||
|
||||
/* This ensures the channel list updates. */
|
||||
ED_area_tag_redraw(CTX_wm_area(C));
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void ANIM_OT_view_curve_in_graph_editor(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers */
|
||||
ot->name = "View In Graph Editor";
|
||||
ot->idname = "ANIM_OT_view_curve_in_graph_editor";
|
||||
ot->description = "Frame the property under the cursor in the Graph Editor";
|
||||
|
||||
/* API callbacks */
|
||||
ot->exec = view_curve_in_graph_editor_exec;
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"all",
|
||||
false,
|
||||
"Show All",
|
||||
"Frame the whole array property instead of only the index under the cursor");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"isolate",
|
||||
false,
|
||||
"Isolate",
|
||||
"Hides all other F-Curves other than the ones being framed");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -4330,6 +4566,7 @@ void ED_operatortypes_animchannels()
|
||||
|
||||
WM_operatortype_append(ANIM_OT_channel_view_pick);
|
||||
WM_operatortype_append(ANIM_OT_channels_view_selected);
|
||||
WM_operatortype_append(ANIM_OT_view_curve_in_graph_editor);
|
||||
|
||||
WM_operatortype_append(ANIM_OT_channels_delete);
|
||||
|
||||
|
||||
@@ -655,6 +655,50 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
|
||||
}
|
||||
}
|
||||
|
||||
if (but->flag & UI_BUT_ANIMATED) {
|
||||
uiItemS(layout);
|
||||
if (is_array_component) {
|
||||
PointerRNA op_ptr;
|
||||
wmOperatorType *ot;
|
||||
ot = WM_operatortype_find("ANIM_OT_view_curve_in_graph_editor", false);
|
||||
uiItemFullO_ptr(
|
||||
layout,
|
||||
ot,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "View Single in Graph Editor"),
|
||||
ICON_NONE,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&op_ptr);
|
||||
RNA_boolean_set(&op_ptr, "all", false);
|
||||
|
||||
uiItemFullO_ptr(layout,
|
||||
ot,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "View All in Graph Editor"),
|
||||
ICON_NONE,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&op_ptr);
|
||||
RNA_boolean_set(&op_ptr, "all", true);
|
||||
}
|
||||
else {
|
||||
PointerRNA op_ptr;
|
||||
wmOperatorType *ot;
|
||||
ot = WM_operatortype_find("ANIM_OT_view_curve_in_graph_editor", false);
|
||||
|
||||
uiItemFullO_ptr(layout,
|
||||
ot,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "View in Graph Editor"),
|
||||
ICON_NONE,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&op_ptr);
|
||||
RNA_boolean_set(&op_ptr, "all", false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Drivers */
|
||||
if (but->flag & UI_BUT_DRIVEN) {
|
||||
uiItemS(layout);
|
||||
|
||||
Reference in New Issue
Block a user