Initial Grease Pencil 3.0 stage #106848
|
@ -441,10 +441,10 @@ blender::Span<blender::bke::StrokePoint> GreasePencilDrawing::stroke_buffer()
|
|||
|
||||
/* GreasePencil API */
|
||||
|
||||
static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int added_capacity)
|
||||
static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int add_capacity)
|
||||
{
|
||||
BLI_assert(added_capacity > 0);
|
||||
const int new_drawing_array_size = self.drawing_array_size + added_capacity;
|
||||
BLI_assert(add_capacity > 0);
|
||||
const int new_drawing_array_size = self.drawing_array_size + add_capacity;
|
||||
GreasePencilDrawingOrReference **new_drawing_array =
|
||||
reinterpret_cast<GreasePencilDrawingOrReference **>(
|
||||
MEM_cnew_array<GreasePencilDrawingOrReference *>(new_drawing_array_size, __func__));
|
||||
|
@ -456,6 +456,21 @@ static void grease_pencil_grow_drawing_array_by(GreasePencil &self, const int ad
|
|||
self.drawing_array_size = new_drawing_array_size;
|
||||
}
|
||||
|
||||
static void grease_pencil_shrink_drawing_array_by(GreasePencil &self, const int remove_capacity)
|
||||
{
|
||||
BLI_assert(remove_capacity > 0);
|
||||
const int new_drawing_array_size = self.drawing_array_size - remove_capacity;
|
||||
GreasePencilDrawingOrReference **new_drawing_array =
|
||||
reinterpret_cast<GreasePencilDrawingOrReference **>(
|
||||
MEM_cnew_array<GreasePencilDrawingOrReference *>(new_drawing_array_size, __func__));
|
||||
|
||||
blender::uninitialized_move_n(self.drawing_array, new_drawing_array_size, new_drawing_array);
|
||||
MEM_freeN(self.drawing_array);
|
||||
|
||||
self.drawing_array = new_drawing_array;
|
||||
self.drawing_array_size = new_drawing_array_size;
|
||||
}
|
||||
|
||||
blender::Span<GreasePencilDrawingOrReference *> GreasePencil::drawings() const
|
||||
{
|
||||
return blender::Span<GreasePencilDrawingOrReference *>{this->drawing_array,
|
||||
|
@ -479,9 +494,76 @@ void GreasePencil::add_empty_drawings(int n)
|
|||
for (const int i : IndexRange(new_drawings.size())) {
|
||||
new_drawings[i] = reinterpret_cast<GreasePencilDrawingOrReference *>(
|
||||
MEM_new<GreasePencilDrawing>(__func__));
|
||||
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(new_drawings[i]);
|
||||
new (&drawing->geometry) bke::CurvesGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void GreasePencil::remove_drawing(int index_to_remove)
|
||||
{
|
||||
using namespace blender::bke::gpencil;
|
||||
/* In order to not change the indices of the drawings, we do the following to the drawing to be
|
||||
* removed:
|
||||
* - If the drawing (A) is not the last one:
|
||||
* 1.1) Find any frames in the layers that reference the last drawing (B) and point them to
|
||||
* A's index.
|
||||
* 1.2) Swap drawing A with drawing B.
|
||||
* 2) Destroy A and shrink the array by one.
|
||||
* 3) Remove any frames in the layers that reference the A's index.
|
||||
*/
|
||||
BLI_assert(this->drawing_array_size > 0);
|
||||
BLI_assert(index_to_remove >= 0 && index_to_remove < this->drawing_array_size);
|
||||
|
||||
/* Move the drawing that should be removed to the last index. */
|
||||
const int last_drawing_index = this->drawing_array_size - 1;
|
||||
if (index_to_remove != last_drawing_index) {
|
||||
this->root_group().foreach_layer_pre_order(
|
||||
[last_drawing_index, index_to_remove](Layer &layer) {
|
||||
blender::Map<int, int> &frames = layer.frames_for_write();
|
||||
for (auto item : frames.items()) {
|
||||
if (item.value == last_drawing_index) {
|
||||
item.value = index_to_remove;
|
||||
}
|
||||
else if (item.value == index_to_remove) {
|
||||
item.value = last_drawing_index;
|
||||
}
|
||||
}
|
||||
});
|
||||
std::swap(this->drawings_for_write()[index_to_remove],
|
||||
this->drawings_for_write()[last_drawing_index]);
|
||||
}
|
||||
|
||||
/* Delete the last drawing. */
|
||||
GreasePencilDrawingOrReference *drawing_or_ref_to_remove =
|
||||
this->drawings_for_write()[last_drawing_index];
|
||||
switch (drawing_or_ref_to_remove->type) {
|
||||
case GREASE_PENCIL_DRAWING: {
|
||||
GreasePencilDrawing *drawing_to_remove = reinterpret_cast<GreasePencilDrawing *>(
|
||||
drawing_or_ref_to_remove);
|
||||
drawing_to_remove->geometry.wrap().~CurvesGeometry();
|
||||
MEM_delete(drawing_to_remove->runtime);
|
||||
drawing_to_remove->runtime = nullptr;
|
||||
MEM_freeN(drawing_to_remove);
|
||||
break;
|
||||
}
|
||||
case GREASE_PENCIL_DRAWING_REFERENCE: {
|
||||
GreasePencilDrawingReference *drawing_reference_to_remove =
|
||||
reinterpret_cast<GreasePencilDrawingReference *>(drawing_or_ref_to_remove);
|
||||
MEM_freeN(drawing_reference_to_remove);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove any frame that points to the last drawing. */
|
||||
this->root_group().foreach_layer_pre_order([last_drawing_index](Layer &layer) {
|
||||
blender::Map<int, int> &frames = layer.frames_for_write();
|
||||
frames.remove_if([last_drawing_index](auto item) { return item.value == last_drawing_index; });
|
||||
});
|
||||
|
||||
/* Shrink drawing array. */
|
||||
grease_pencil_shrink_drawing_array_by(*this, 1);
|
||||
}
|
||||
|
||||
void GreasePencil::foreach_visible_drawing(
|
||||
int frame, blender::FunctionRef<void(GreasePencilDrawing &)> function)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
|
@ -49,6 +50,46 @@ TEST(gpencil, add_empty_drawings)
|
|||
EXPECT_EQ(grease_pencil.drawings().size(), 3);
|
||||
}
|
||||
|
||||
TEST(gpencil, remove_drawing)
|
||||
{
|
||||
GreasePencilIDTestContext ctx;
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(BKE_id_new(ctx.bmain, ID_GP, "GP"));
|
||||
grease_pencil.add_empty_drawings(3);
|
||||
|
||||
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(
|
||||
grease_pencil.drawings_for_write()[1]);
|
||||
drawing->geometry.wrap().resize(0, 10);
|
||||
|
||||
Layer layer1("Layer1");
|
||||
Layer layer2("Layer2");
|
||||
|
||||
layer1.frames_for_write().add(0, 0);
|
||||
layer1.frames_for_write().add(10, 1);
|
||||
layer1.frames_for_write().add(20, 2);
|
||||
|
||||
layer2.frames_for_write().add(0, 1);
|
||||
|
||||
grease_pencil.root_group().add_layer(std::move(layer1));
|
||||
grease_pencil.root_group().add_layer(std::move(layer2));
|
||||
|
||||
grease_pencil.remove_drawing(1);
|
||||
EXPECT_EQ(grease_pencil.drawings().size(), 2);
|
||||
|
||||
static int expected_frames_size[] = {2, 0};
|
||||
static int expected_frames_pairs_layer0[][2] = {{0, 0}, {20, 1}};
|
||||
int layer_i = 0;
|
||||
grease_pencil.root_group().foreach_layer_pre_order([&](Layer &layer) {
|
||||
EXPECT_EQ(layer.frames().size(), expected_frames_size[layer_i]);
|
||||
if (layer_i == 0) {
|
||||
for (const int i : IndexRange(2)) {
|
||||
EXPECT_EQ(layer.frames().lookup(expected_frames_pairs_layer0[i][0]),
|
||||
expected_frames_pairs_layer0[i][1]);
|
||||
}
|
||||
}
|
||||
layer_i++;
|
||||
});
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------- */
|
||||
/* Layer Tree Tests. */
|
||||
|
||||
|
|
|
@ -181,6 +181,7 @@ typedef struct GreasePencil {
|
|||
blender::Span<GreasePencilDrawingOrReference *> drawings() const;
|
||||
blender::MutableSpan<GreasePencilDrawingOrReference *> drawings_for_write();
|
||||
void add_empty_drawings(int n);
|
||||
|
||||
void remove_drawing(int index);
|
||||
void foreach_visible_drawing(int frame,
|
||||
blender::FunctionRef<void(GreasePencilDrawing &)> function);
|
||||
void read_drawing_array(BlendDataReader *reader);
|
||||
|
|
Loading…
Reference in New Issue
Pretty sure null-terminated goes without saying for
char *
pointers in DNA, I don't think it's worth mentioning hereNot sure about dynamic names for strings. It is not something typically used in the DNA.
It's been done more recently I think. It's properly integrated with RNA now too, to avoid that boilerplate. It's nice not to have to worry about choosing a future-proof length.