diff --git a/src/app/script/cels_class.cpp b/src/app/script/cels_class.cpp index ad867a1cf..023988455 100644 --- a/src/app/script/cels_class.cpp +++ b/src/app/script/cels_class.cpp @@ -10,9 +10,11 @@ #include "app/script/luacpp.h" #include "doc/cels_range.h" +#include "doc/layer.h" #include "doc/sprite.h" #include +#include namespace app { namespace script { @@ -22,8 +24,11 @@ using namespace doc; namespace { struct CelsObj { - Sprite* sprite; - CelsObj(Sprite* sprite) : sprite(sprite) { } + CelList cels; + CelsObj(CelsRange&& range) { + std::copy(range.begin(), range.end(), std::back_inserter(cels)); + } + CelsObj(CelList&& cels) : cels(std::move(cels)) { } CelsObj(const CelsObj&) = delete; CelsObj& operator=(const CelsObj&) = delete; }; @@ -37,7 +42,7 @@ int Cels_gc(lua_State* L) int Cels_len(lua_State* L) { auto obj = get_obj(L, 1); - lua_pushinteger(L, obj->sprite->cels().size()); + lua_pushinteger(L, obj->cels.size()); return 1; } @@ -45,13 +50,9 @@ int Cels_index(lua_State* L) { auto obj = get_obj(L, 1); const int i = lua_tointeger(L, 2); - if (i < 1 || i > obj->sprite->cels().size()) + if (i < 1 || i > obj->cels.size()) return luaL_error(L, "index out of bounds %d", i); - - auto it = obj->sprite->cels().begin(); - std::advance(it, i-1); // TODO improve this - - push_ptr(L, *it); + push_ptr(L, obj->cels[i-1]); return 1; } @@ -74,7 +75,15 @@ void register_cels_class(lua_State* L) void push_sprite_cels(lua_State* L, Sprite* sprite) { - push_new(L, sprite); + push_new(L, sprite->cels()); +} + +void push_layer_cels(lua_State* L, Layer* layer) +{ + CelList cels; + if (layer->isImage()) + static_cast(layer)->getCels(cels); + push_new(L, std::move(cels)); } } // namespace script diff --git a/src/app/script/engine.cpp b/src/app/script/engine.cpp index f4c0500f2..0bacdb45e 100644 --- a/src/app/script/engine.cpp +++ b/src/app/script/engine.cpp @@ -16,6 +16,8 @@ #include "base/chrono.h" #include "base/fs.h" #include "base/fstream_path.h" +#include "doc/anidir.h" +#include "doc/blend_mode.h" #include "doc/color_mode.h" #include @@ -154,6 +156,14 @@ Engine::Engine() setfield_integer(L, "INDEXED", doc::ColorMode::INDEXED); lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setglobal(L, "AniDir"); + setfield_integer(L, "FORWARD", doc::AniDir::FORWARD); + setfield_integer(L, "REVERSE", doc::AniDir::REVERSE); + setfield_integer(L, "PING_PONG", doc::AniDir::PING_PONG); + lua_pop(L, 1); + lua_newtable(L); lua_pushvalue(L, -1); lua_setglobal(L, "BlendMode"); diff --git a/src/app/script/engine.h b/src/app/script/engine.h index 2ec290793..679878920 100644 --- a/src/app/script/engine.h +++ b/src/app/script/engine.h @@ -85,6 +85,7 @@ namespace app { void push_cel_image(lua_State* L, doc::Cel* cel); void push_sprite_cel(lua_State* L, doc::Cel* cel); void push_sprite_cels(lua_State* L, doc::Sprite* sprite); + void push_layer_cels(lua_State* L, doc::Layer* layer); void push_sprite_frame(lua_State* L, doc::Sprite* sprite, doc::frame_t frame); void push_sprite_frames(lua_State* L, doc::Sprite* sprite); void push_sprite_layers(lua_State* L, doc::Sprite* sprite); @@ -101,6 +102,7 @@ namespace app { app::Color convert_args_into_color(lua_State* L, int index); doc::color_t convert_args_into_pixel_color(lua_State* L, int index); doc::Palette* get_palette_from_arg(lua_State* L, int index); + doc::Image* may_get_image_from_arg(lua_State* L, int index); } // namespace script } // namespace app diff --git a/src/app/script/image_class.cpp b/src/app/script/image_class.cpp index c4155c8c8..f108aee7c 100644 --- a/src/app/script/image_class.cpp +++ b/src/app/script/image_class.cpp @@ -172,5 +172,14 @@ void push_cel_image(lua_State* L, doc::Cel* cel) push_new(L, cel->imageRef(), cel); } +doc::Image* may_get_image_from_arg(lua_State* L, int index) +{ + auto obj = may_get_obj(L, index); + if (obj) + return obj->image.get(); + else + return nullptr; +} + } // namespace script } // namespace app diff --git a/src/app/script/layer_class.cpp b/src/app/script/layer_class.cpp index d0b0d03bf..591ca929e 100644 --- a/src/app/script/layer_class.cpp +++ b/src/app/script/layer_class.cpp @@ -97,6 +97,13 @@ int Layer_get_isBackground(lua_State* L) return 1; } +int Layer_get_cels(lua_State* L) +{ + auto layer = get_ptr(L, 1); + push_layer_cels(L, layer); + return 1; +} + int Layer_set_name(lua_State* L) { auto layer = get_ptr(L, 1); @@ -148,6 +155,7 @@ const Property Layer_properties[] = { { "isGroup", Layer_get_isGroup, nullptr }, { "isTransparent", Layer_get_isTransparent, nullptr }, { "isBackground", Layer_get_isBackground, nullptr }, + { "cels", Layer_get_cels, nullptr }, { "color", UserData_get_color, UserData_set_color }, { "data", UserData_get_text, UserData_set_text }, { nullptr, nullptr, nullptr } diff --git a/src/app/script/luacpp.h b/src/app/script/luacpp.h index f5dff0bf9..47ca45cff 100644 --- a/src/app/script/luacpp.h +++ b/src/app/script/luacpp.h @@ -58,6 +58,15 @@ template T* get_obj(lua_State* L, int index) { return (T*)luaL_checkudata(L, index, get_mtname()); } +// Returns nil if the index doesn't have the given metatable +template T* may_get_ptr(lua_State* L, int index) { + T** ptr = (T**)luaL_testudata(L, index, get_mtname()); + if (ptr) + return *ptr; + else + return nullptr; +} + // Returns nil if the index doesn't have the given metatable template T* may_get_obj(lua_State* L, int index) { return (T*)luaL_testudata(L, index, get_mtname()); diff --git a/src/app/script/slice_class.cpp b/src/app/script/slice_class.cpp index 7a317e706..831fc0193 100644 --- a/src/app/script/slice_class.cpp +++ b/src/app/script/slice_class.cpp @@ -8,6 +8,8 @@ #include "config.h" #endif +#include "app/cmd/set_slice_key.h" +#include "app/cmd/set_slice_name.h" #include "app/script/engine.h" #include "app/script/luacpp.h" #include "app/script/userdata.h" @@ -37,20 +39,6 @@ int Slice_get_sprite(lua_State* L) return 1; } -int Slice_get_fromFrame(lua_State* L) -{ - auto slice = get_ptr(L, 1); - lua_pushinteger(L, slice->fromFrame()); - return 1; -} - -int Slice_get_toFrame(lua_State* L) -{ - auto slice = get_ptr(L, 1); - lua_pushinteger(L, slice->toFrame()); - return 1; -} - int Slice_get_name(lua_State* L) { auto slice = get_ptr(L, 1); @@ -88,6 +76,60 @@ int Slice_get_pivot(lua_State* L) return 1; } +int Slice_set_name(lua_State* L) +{ + auto slice = get_ptr(L, 1); + const char* name = lua_tostring(L, 2); + if (name) { + Tx tx; + tx(new cmd::SetSliceName(slice, name)); + tx.commit(); + } + return 0; +} + +int Slice_set_bounds(lua_State* L) +{ + auto slice = get_ptr(L, 1); + gfx::Rect bounds = convert_args_into_rect(L, 2); + SliceKey key; + if (const SliceKey* srcKey = slice->getByFrame(0)) + key = *srcKey; + key.setBounds(bounds); + Tx tx; + tx(new cmd::SetSliceKey(slice, 0, key)); + tx.commit(); + return 0; +} + +int Slice_set_center(lua_State* L) +{ + auto slice = get_ptr(L, 1); + gfx::Rect center = convert_args_into_rect(L, 2); + SliceKey key; + if (const SliceKey* srcKey = slice->getByFrame(0)) + key = *srcKey; + key.setCenter(center); + Tx tx; + tx(new cmd::SetSliceKey(slice, 0, key)); + tx.commit(); + return 0; +} + +int Slice_set_pivot(lua_State* L) +{ + auto slice = get_ptr(L, 1); + gfx::Point pivot = convert_args_into_point(L, 2); + SliceKey key; + if (const SliceKey* srcKey = slice->getByFrame(0)) + key = *srcKey; + key.setPivot(pivot); + Tx tx; + tx(new cmd::SetSliceKey(slice, 0, key)); + tx.commit(); + return 0; +} + const luaL_Reg Slice_methods[] = { { "__eq", Slice_eq }, { nullptr, nullptr } @@ -95,12 +137,10 @@ const luaL_Reg Slice_methods[] = { const Property Slice_properties[] = { { "sprite", Slice_get_sprite, nullptr }, - { "fromFrame", Slice_get_fromFrame, nullptr }, - { "toFrame", Slice_get_toFrame, nullptr }, - { "name", Slice_get_name, nullptr }, - { "bounds", Slice_get_bounds, nullptr }, - { "center", Slice_get_center, nullptr }, - { "pivot", Slice_get_pivot, nullptr }, + { "name", Slice_get_name, Slice_set_name }, + { "bounds", Slice_get_bounds, Slice_set_bounds }, + { "center", Slice_get_center, Slice_set_center }, + { "pivot", Slice_get_pivot, Slice_set_pivot }, { "color", UserData_get_color, UserData_set_color }, { "data", UserData_get_text, UserData_set_text }, { nullptr, nullptr, nullptr } diff --git a/src/app/script/sprite_class.cpp b/src/app/script/sprite_class.cpp index a62b803ff..cf5450ddc 100644 --- a/src/app/script/sprite_class.cpp +++ b/src/app/script/sprite_class.cpp @@ -9,6 +9,11 @@ #endif #include "app/app.h" +#include "app/cmd/add_layer.h" +#include "app/cmd/clear_cel.h" +#include "app/cmd/remove_frame_tag.h" +#include "app/cmd/remove_layer.h" +#include "app/cmd/remove_slice.h" #include "app/cmd/set_sprite_size.h" #include "app/cmd/set_transparent_color.h" #include "app/commands/commands.h" @@ -23,9 +28,11 @@ #include "app/transaction.h" #include "app/tx.h" #include "app/ui/doc_view.h" +#include "doc/frame_tag.h" #include "doc/layer.h" #include "doc/mask.h" #include "doc/palette.h" +#include "doc/slice.h" #include "doc/sprite.h" namespace app { @@ -178,6 +185,243 @@ int Sprite_setPalette(lua_State* L) return 0; } +int Sprite_newLayer(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + doc::Layer* newLayer = new doc::LayerImage(sprite); + + Tx tx; + tx(new cmd::AddLayer(sprite->root(), newLayer, sprite->root()->lastLayer())); + tx.commit(); + + push_ptr(L, newLayer); + return 1; +} + +int Sprite_newGroup(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + doc::Layer* newGroup = new doc::LayerGroup(sprite); + + Tx tx; + tx(new cmd::AddLayer(sprite->root(), newGroup, sprite->root()->lastLayer())); + tx.commit(); + + push_ptr(L, newGroup); + return 1; +} + +int Sprite_deleteLayer(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + auto layer = may_get_ptr(L, 2); + if (!layer && lua_isstring(L, 2)) { + const char* layerName = lua_tostring(L, 2); + if (layerName) { + for (Layer* child : sprite->allLayers()) { + if (child->name() == layerName) { + layer = child; + break; + } + } + } + } + if (layer) { + Tx tx; + tx(new cmd::RemoveLayer(layer)); + tx.commit(); + return 0; + } + else { + return luaL_error(L, "layer not found"); + } +} + +int Sprite_newFrame(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + doc::frame_t frame = sprite->lastFrame()+1; + if (lua_gettop(L) >= 2) { + frame = lua_tointeger(L, 2)-1; + if (frame < 0) + return luaL_error(L, "frame index out of bounds %d", frame+1); + } + + Doc* doc = static_cast(sprite->document()); + + Tx tx; + doc->getApi(tx).addFrame(sprite, frame); + tx.commit(); + + push_sprite_frame(L, sprite, frame); + return 1; +} + +int Sprite_newEmptyFrame(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + doc::frame_t frame = sprite->lastFrame()+1; + if (lua_gettop(L) >= 1) { + frame = lua_tointeger(L, 2)-1; + if (frame < 0) + return luaL_error(L, "frame index out of bounds %d", frame+1); + } + + Doc* doc = static_cast(sprite->document()); + + Tx tx; + DocApi(doc, tx).addEmptyFrame(sprite, frame); + tx.commit(); + + push_sprite_frame(L, sprite, frame); + return 1; +} + +int Sprite_deleteFrame(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + doc::frame_t frame = lua_tointeger(L, 2)-1; + if (frame < 0 || frame > sprite->lastFrame()) + return luaL_error(L, "frame index out of bounds %d", frame+1); + + Doc* doc = static_cast(sprite->document()); + + Tx tx; + doc->getApi(tx).removeFrame(sprite, frame); + tx.commit(); + return 0; +} + +int Sprite_newCel(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + auto layerBase = get_ptr(L, 2); + if (!layerBase->isImage()) + return luaL_error(L, "unexpected kinf of layer in Sprite:newCel()"); + + frame_t frame = lua_tointeger(L, 3)-1; + if (frame < 0 || frame > sprite->lastFrame()) + return luaL_error(L, "frame index out of bounds %d", frame+1); + + LayerImage* layer = static_cast(layerBase); + ImageRef image(nullptr); + gfx::Point pos(0, 0); + + Image* srcImage = may_get_image_from_arg(L, 4); + if (srcImage) { + image.reset(Image::createCopy(srcImage)); + pos = convert_args_into_point(L, 5); + } + else { + image.reset(Image::create(sprite->spec())); + } + + auto cel = new Cel(frame, image); + cel->setPosition(pos); + + Doc* doc = static_cast(sprite->document()); + + Tx tx; + DocApi api = doc->getApi(tx); + if (layer->cel(frame)) + api.clearCel(layer, frame); + api.addCel(layer, cel); + tx.commit(); + + push_ptr(L, cel); + return 1; +} + +int Sprite_deleteCel(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + (void)sprite; // unused + + auto cel = may_get_ptr(L, 2); + if (!cel) { + if (auto layer = may_get_ptr(L, 2)) { + doc::frame_t frame = lua_tointeger(L, 3); + if (layer->isImage()) + cel = static_cast(layer)->cel(frame); + } + } + + if (cel) { + Tx tx; + tx(new cmd::ClearCel(cel)); + tx.commit(); + return 0; + } + else { + return luaL_error(L, "cel not found"); + } +} + +int Sprite_newTag(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + auto from = lua_tointeger(L, 2)-1; + auto to = lua_tointeger(L, 3)-1; + auto tag = new doc::FrameTag(from, to); + sprite->frameTags().add(tag); + push_ptr(L, tag); + return 1; +} + +int Sprite_deleteTag(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + auto tag = may_get_ptr(L, 2); + if (!tag && lua_isstring(L, 2)) { + const char* tagName = lua_tostring(L, 2); + if (tagName) + tag = sprite->frameTags().getByName(tagName); + } + if (tag) { + Tx tx; + tx(new cmd::RemoveFrameTag(sprite, tag)); + tx.commit(); + return 0; + } + else { + return luaL_error(L, "tag not found"); + } +} + +int Sprite_newSlice(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + auto slice = new doc::Slice(); + + gfx::Rect bounds = convert_args_into_rect(L, 2); + if (!bounds.isEmpty()) + slice->insert(0, doc::SliceKey(bounds)); + + sprite->slices().add(slice); + push_ptr(L, slice); + return 1; +} + +int Sprite_deleteSlice(lua_State* L) +{ + auto sprite = get_ptr(L, 1); + doc::Slice* slice = may_get_ptr(L, 2); + if (!slice && lua_isstring(L, 2)) { + const char* sliceName = lua_tostring(L, 2); + if (sliceName) + slice = sprite->slices().getByName(sliceName); + } + if (slice) { + Tx tx; + tx(new cmd::RemoveSlice(sprite, slice)); + tx.commit(); + return 0; + } + else { + return luaL_error(L, "slice not found"); + } +} + int Sprite_get_filename(lua_State* L) { auto sprite = get_ptr(L, 1); @@ -312,6 +556,23 @@ const luaL_Reg Sprite_methods[] = { { "saveCopyAs", Sprite_saveCopyAs }, { "loadPalette", Sprite_loadPalette }, { "setPalette", Sprite_setPalette }, + // Layers + { "newLayer", Sprite_newLayer }, + { "newGroup", Sprite_newGroup }, + { "deleteLayer", Sprite_deleteLayer }, + // Frames + { "newFrame", Sprite_newFrame }, + { "newEmptyFrame", Sprite_newEmptyFrame }, + { "deleteFrame", Sprite_deleteFrame }, + // Cel + { "newCel", Sprite_newCel }, + { "deleteCel", Sprite_deleteCel }, + // Tag + { "newTag", Sprite_newTag }, + { "deleteTag", Sprite_deleteTag }, + // Slices + { "newSlice", Sprite_newSlice }, + { "deleteSlice", Sprite_deleteSlice }, { nullptr, nullptr } }; diff --git a/src/app/script/tag_class.cpp b/src/app/script/tag_class.cpp index 94fd074d5..83e51e1f2 100644 --- a/src/app/script/tag_class.cpp +++ b/src/app/script/tag_class.cpp @@ -31,17 +31,24 @@ int Tag_eq(lua_State* L) return 1; } +int Tag_get_sprite(lua_State* L) +{ + auto tag = get_ptr(L, 1); + push_ptr(L, tag->owner()->sprite()); + return 1; +} + int Tag_get_fromFrame(lua_State* L) { auto tag = get_ptr(L, 1); - lua_pushinteger(L, tag->fromFrame()); + lua_pushinteger(L, tag->fromFrame()+1); return 1; } int Tag_get_toFrame(lua_State* L) { auto tag = get_ptr(L, 1); - lua_pushinteger(L, tag->toFrame()); + lua_pushinteger(L, tag->toFrame()+1); return 1; } @@ -69,7 +76,7 @@ int Tag_get_aniDir(lua_State* L) int Tag_set_fromFrame(lua_State* L) { auto tag = get_ptr(L, 1); - const int fromFrame = lua_tointeger(L, 2); + const int fromFrame = lua_tointeger(L, 2)-1; Tx tx; tx(new cmd::SetFrameTagRange(tag, fromFrame, std::max(fromFrame, tag->toFrame()))); @@ -80,7 +87,7 @@ int Tag_set_fromFrame(lua_State* L) int Tag_set_toFrame(lua_State* L) { auto tag = get_ptr(L, 1); - const int toFrame = lua_tointeger(L, 2); + const int toFrame = lua_tointeger(L, 2)-1; Tx tx; tx(new cmd::SetFrameTagRange(tag, std::min(tag->fromFrame(), toFrame), @@ -117,6 +124,7 @@ const luaL_Reg Tag_methods[] = { }; const Property Tag_properties[] = { + { "sprite", Tag_get_sprite, nullptr }, { "fromFrame", Tag_get_fromFrame, Tag_set_fromFrame }, { "toFrame", Tag_get_toFrame, Tag_set_toFrame }, { "frames", Tag_get_frames, nullptr },