Blender V5.0
editcurve_undo.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "CLG_log.h"
12
13#include "DNA_anim_types.h"
14#include "DNA_object_types.h"
15#include "DNA_scene_types.h"
16
17#include "BLI_array_utils.h"
18#include "BLI_ghash.h"
19#include "BLI_listbase.h"
20
21#include "BKE_anim_data.hh"
22#include "BKE_context.hh"
23#include "BKE_curve.hh"
24#include "BKE_fcurve.hh"
25#include "BKE_layer.hh"
26#include "BKE_main.hh"
27#include "BKE_object.hh"
28#include "BKE_undo_system.hh"
29
30#include "DEG_depsgraph.hh"
31
32#include "ED_curve.hh"
33#include "ED_undo.hh"
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
38#include "curve_intern.hh"
39
40using blender::Vector;
41
43static CLG_LogRef LOG = {"undo.curve"};
44
45/* -------------------------------------------------------------------- */
48
49namespace {
50
51struct UndoCurve {
52 ListBase nubase;
53 int actvert;
54 GHash *undoIndex;
55
56 /* Historical note: Once upon a time, this code also made a backup of F-Curves, in an attempt to
57 * enable undo of animation changes. This was very limited, as it only backed up the animation
58 * of the curve ID; all the other IDs whose animation was shown in the dope sheet, timeline, etc.
59 * was ignored. It also ignored the NLA, and deleted Action groups even when the animation was
60 * not touched by the user.
61 *
62 * With the introduction of slotted Actions, a decision had to be made to either port this
63 * behavior or remove it. The latter was chosen. For more information, see #135585. */
64 ListBase drivers;
65
66 int actnu;
67 int flag;
68
69 /* Stored in the object, needed since users may change the active key while in edit-mode. */
70 struct {
71 short shapenr;
72 } obedit;
73
74 size_t undo_size;
75};
76
77} // namespace
78
79static void undocurve_to_editcurve(Main *bmain, UndoCurve *ucu, Curve *cu, short *r_shapenr)
80{
81 ListBase *undobase = &ucu->nubase;
82 ListBase *editbase = BKE_curve_editNurbs_get(cu);
83 EditNurb *editnurb = cu->editnurb;
85
86 BKE_nurbList_free(editbase);
87
88 if (ucu->undoIndex) {
90 editnurb->keyindex = ED_curve_keyindex_hash_duplicate(ucu->undoIndex);
91 }
92
93 if (ad) {
95 BKE_fcurves_copy(&ad->drivers, &ucu->drivers);
96 }
97
98 /* Copy. */
99 LISTBASE_FOREACH (Nurb *, nu, undobase) {
100 Nurb *newnu = BKE_nurb_duplicate(nu);
101
102 if (editnurb->keyindex) {
103 ED_curve_keyindex_update_nurb(editnurb, nu, newnu);
104 }
105
106 BLI_addtail(editbase, newnu);
107 }
108
109 cu->actvert = ucu->actvert;
110 cu->actnu = ucu->actnu;
111 cu->flag = ucu->flag;
112 *r_shapenr = ucu->obedit.shapenr;
113 ED_curve_updateAnimPaths(bmain, cu);
114}
115
116static void undocurve_from_editcurve(UndoCurve *ucu, Curve *cu, const short shapenr)
117{
119 ListBase *nubase = BKE_curve_editNurbs_get(cu);
120 EditNurb *editnurb = cu->editnurb, tmpEditnurb;
121 AnimData *ad = BKE_animdata_from_id(&cu->id);
122
123 /* TODO: include size of drivers & undoIndex */
124 // ucu->undo_size = 0;
125
126 if (editnurb->keyindex) {
127 ucu->undoIndex = ED_curve_keyindex_hash_duplicate(editnurb->keyindex);
128 tmpEditnurb.keyindex = ucu->undoIndex;
129 }
130
131 if (ad) {
132 BKE_fcurves_copy(&ucu->drivers, &ad->drivers);
133 }
134
135 /* Copy. */
136 LISTBASE_FOREACH (Nurb *, nu, nubase) {
137 Nurb *newnu = BKE_nurb_duplicate(nu);
138
139 if (ucu->undoIndex) {
140 ED_curve_keyindex_update_nurb(&tmpEditnurb, nu, newnu);
141 }
142
143 BLI_addtail(&ucu->nubase, newnu);
144
145 ucu->undo_size += ((nu->bezt ? (sizeof(BezTriple) * nu->pntsu) : 0) +
146 (nu->bp ? (sizeof(BPoint) * (nu->pntsu * nu->pntsv)) : 0) +
147 (nu->knotsu ? (sizeof(float) * KNOTSU(nu)) : 0) +
148 (nu->knotsv ? (sizeof(float) * KNOTSV(nu)) : 0) + sizeof(Nurb));
149 }
150
151 ucu->actvert = cu->actvert;
152 ucu->actnu = cu->actnu;
153 ucu->flag = cu->flag;
154
155 ucu->obedit.shapenr = shapenr;
156}
157
158static void undocurve_free_data(UndoCurve *uc)
159{
160 BKE_nurbList_free(&uc->nubase);
161
162 BKE_curve_editNurb_keyIndex_free(&uc->undoIndex);
163
164 BKE_fcurves_free(&uc->drivers);
165}
166
168{
169 Scene *scene = CTX_data_scene(C);
170 ViewLayer *view_layer = CTX_data_view_layer(C);
171 BKE_view_layer_synced_ensure(scene, view_layer);
172 Object *obedit = BKE_view_layer_edit_object_get(view_layer);
173 if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
174 Curve *cu = static_cast<Curve *>(obedit->data);
175 if (BKE_curve_editNurbs_get(cu) != nullptr) {
176 return obedit;
177 }
178 }
179 return nullptr;
180}
181
183
184/* -------------------------------------------------------------------- */
189
191 UndoRefID_Object obedit_ref;
192 UndoCurve data;
193};
194
202
204{
206 return (obedit != nullptr);
207}
208
210{
211 CurveUndoStep *us = (CurveUndoStep *)us_p;
212
213 /* Important not to use the 3D view when getting objects because all objects
214 * outside of this list will be moved out of edit-mode when reading back undo steps. */
215 Scene *scene = CTX_data_scene(C);
216 ViewLayer *view_layer = CTX_data_view_layer(C);
218
219 us->scene_ref.ptr = scene;
220 us->elems = MEM_calloc_arrayN<CurveUndoStep_Elem>(objects.size(), __func__);
221 us->elems_len = objects.size();
222
223 for (uint i = 0; i < objects.size(); i++) {
224 Object *ob = objects[i];
225 Curve *cu = static_cast<Curve *>(ob->data);
226 CurveUndoStep_Elem *elem = &us->elems[i];
227
228 elem->obedit_ref.ptr = ob;
229 undocurve_from_editcurve(&elem->data, static_cast<Curve *>(ob->data), ob->shapenr);
231 us->step.data_size += elem->data.undo_size;
232 }
233
234 bmain->is_memfile_undo_flush_needed = true;
235
236 return true;
237}
238
240 bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir /*dir*/, bool /*is_final*/)
241{
242 CurveUndoStep *us = (CurveUndoStep *)us_p;
243 Scene *scene = CTX_data_scene(C);
244 ViewLayer *view_layer = CTX_data_view_layer(C);
245
247 CTX_wm_manager(C), us->scene_ref.ptr, &scene, &view_layer);
249 scene, view_layer, &us->elems[0].obedit_ref.ptr, us->elems_len, sizeof(*us->elems));
250
252
253 for (uint i = 0; i < us->elems_len; i++) {
254 CurveUndoStep_Elem *elem = &us->elems[i];
255 Object *obedit = elem->obedit_ref.ptr;
256 Curve *cu = static_cast<Curve *>(obedit->data);
257 if (cu->editnurb == nullptr) {
258 /* Should never fail, may not crash but can give odd behavior. */
260 "name='%s', failed to enter edit-mode for object '%s', undo state invalid",
261 us_p->name,
262 obedit->id.name);
263 continue;
264 }
266 bmain, &elem->data, static_cast<Curve *>(obedit->data), &obedit->shapenr);
269 }
270
271 /* The first element is always active */
273 scene, view_layer, us->elems[0].obedit_ref.ptr, us_p->name, &LOG);
274
275 /* Check after setting active (unless undoing into another scene). */
277
278 bmain->is_memfile_undo_flush_needed = true;
279
281}
282
284{
285 CurveUndoStep *us = (CurveUndoStep *)us_p;
286
287 for (uint i = 0; i < us->elems_len; i++) {
288 CurveUndoStep_Elem *elem = &us->elems[i];
290 }
291 MEM_freeN(us->elems);
292}
293
295 UndoTypeForEachIDRefFn foreach_ID_ref_fn,
296 void *user_data)
297{
298 CurveUndoStep *us = (CurveUndoStep *)us_p;
299
300 foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->scene_ref));
301 for (uint i = 0; i < us->elems_len; i++) {
302 CurveUndoStep_Elem *elem = &us->elems[i];
303 foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref));
304 }
305}
306
321
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
#define KNOTSU(nu)
Definition BKE_curve.hh:74
#define KNOTSV(nu)
Definition BKE_curve.hh:76
void BKE_curve_editNurb_keyIndex_free(GHash **keyindex)
Definition curve.cc:349
void BKE_nurbList_free(ListBase *lb)
Definition curve.cc:597
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:419
Nurb * BKE_nurb_duplicate(const Nurb *nu)
Definition curve.cc:609
void BKE_fcurves_free(ListBase *list)
void BKE_fcurves_copy(ListBase *dst, ListBase *src)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_edit_object_get(const ViewLayer *view_layer)
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
@ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE
void(*)(void *user_data, UndoRefID *id_ref) UndoTypeForEachIDRefFn
eUndoStepDir
Generic array manipulation API.
#define BLI_array_is_zeroed(arr, arr_len)
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GHash GHash
Definition BLI_ghash.h:39
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
unsigned int uint
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
struct ListBase ListBase
Object is a sort of wrapper for general info.
@ OB_SURF
@ OB_CURVES_LEGACY
void ED_undo_object_set_active_or_warn(Scene *scene, ViewLayer *view_layer, Object *ob, const char *info, CLG_LogRef *log)
Definition ed_undo.cc:775
void ED_undo_object_editmode_restore_helper(Scene *scene, ViewLayer *view_layer, Object **object_array, uint object_array_len, uint object_array_stride)
Definition ed_undo.cc:811
blender::Vector< Object * > ED_undo_editmode_objects_from_view_layer(const Scene *scene, ViewLayer *view_layer)
Definition ed_undo.cc:856
void ED_undo_object_editmode_validate_scene_from_windows(wmWindowManager *wm, const Scene *scene_ref, Scene **scene_p, ViewLayer **view_layer_p)
Definition ed_undo.cc:794
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
int64_t size() const
nullptr float
void ED_curve_keyindex_update_nurb(EditNurb *editnurb, Nurb *nu, Nurb *newnu)
Definition editcurve.cc:358
GHash * ED_curve_keyindex_hash_duplicate(GHash *keyindex)
Definition editcurve.cc:547
int ED_curve_updateAnimPaths(Main *bmain, Curve *cu)
static void curve_undosys_foreach_ID_ref(UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
static void curve_undosys_step_free(UndoStep *us_p)
static bool curve_undosys_poll(bContext *C)
static Object * editcurve_object_from_context(bContext *C)
static bool curve_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
void ED_curve_undosys_type(UndoType *ut)
static void undocurve_to_editcurve(Main *bmain, UndoCurve *ucu, Curve *cu, short *r_shapenr)
static void undocurve_free_data(UndoCurve *uc)
static void undocurve_from_editcurve(UndoCurve *ucu, Curve *cu, const short shapenr)
static void curve_undosys_step_decode(bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir, bool)
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ListBase drivers
UndoRefID_Object obedit_ref
CurveUndoStep_Elem * elems
UndoRefID_Scene scene_ref
EditNurb * editnurb
char needs_flush_to_id
struct GHash * keyindex
char name[258]
Definition DNA_ID.h:432
bool is_memfile_undo_flush_needed
Definition BKE_main.hh:213
size_t data_size
char name[64]
void(* step_foreach_ID_ref)(UndoStep *us, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
const char * name
void(* step_free)(UndoStep *us)
bool(* poll)(struct bContext *C)
void(* step_decode)(bContext *C, Main *bmain, UndoStep *us, eUndoStepDir dir, bool is_final)
bool(* step_encode)(bContext *C, Main *bmain, UndoStep *us)
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)