diff --git a/main.cpp b/main.cpp index 1326a2a4..6d2340bc 100644 --- a/main.cpp +++ b/main.cpp @@ -9,7 +9,6 @@ #include "src/ext/ReflectionUtil.hpp" import std; -import std.compat; import Assets.LoaderRenderer; @@ -19,7 +18,7 @@ import Core.Platform; import OS.File; import ext.Concepts; import ext.Container.ObjectPool; -import Event; +import ext.Event; import StackTrace; import Graphic.Draw; @@ -38,7 +37,7 @@ import Font; import Math; import Math.Rand; -import Math.StripPacker2D; +import Math.Algo.StripPacker2D; import Geom.Vector2D; import Geom.Matrix3D; import Geom.Rect_Orthogonal; @@ -77,7 +76,7 @@ import GL.Buffer.FrameBuffer; import GL.Texture.TextureRegion; import GL.Texture.TextureNineRegion; import GL.Blending; -import Event; +import ext.Event; import Font.GlyphArrangement; import ext.Timer; @@ -113,7 +112,6 @@ import Game.Entity.Turret; import Game.Entity.Controller.AI; import Game.Entity.Controller.Player; - import Game.Content.Type.BasicBulletType; import Game.Content.Type.Turret.BasicTurretType; import Game.Content.Builtin.SpaceCrafts; @@ -130,7 +128,7 @@ import Game.Scene.MainMenu; import ext.Encoding; import ext.TreeStructure; -import ext.Algorithm; +import ext.algorithm; import ext.Heterogeneous; import ext.Json; import ext.Base64; @@ -138,7 +136,7 @@ import ext.StringParse; import ext.StaticReflection; import ext.ReflectData; -import Core.IO.JsonIO; +import ext.json.io; import ext.SpecIOSummary; using namespace Graphic; @@ -146,72 +144,129 @@ using namespace GL; using Geom::Vec2; import SideTemp; - -//TODO temp global static mut should be remove bool drawDebug{false}; +import Assets.Load.State; +import Assets.Load.Core; +import Assets.Load.Misc; -namespace GameCtrl{ - ::Ctrl::Operation moveLeft{ - "move-left", OS::KeyBind(::Ctrl::Key::A, ::Ctrl::Act::Continuous, +[]{ - Game::core->sendPlayerMoveAct(Geom::left); - }) - }; +import Assets.Load.TexturePacker; +import Assets.TexturePage; - ::Ctrl::Operation moveRight{ - "move-right", OS::KeyBind(::Ctrl::Key::D, ::Ctrl::Act::Continuous, +[]{ - Game::core->sendPlayerMoveAct(Geom::right); - }) - }; +import Math.Algo.TopologicalSort; - ::Ctrl::Operation moveForward{ - "move-up", OS::KeyBind(::Ctrl::Key::W, ::Ctrl::Act::Continuous, +[]{ - Game::core->sendPlayerMoveAct(Geom::up); - }) - }; +template +struct Print{ + void operator ()() const{ + std::println("{}", what); + std::cout.flush(); + } +}; - ::Ctrl::Operation moveBack{ - "move-down", OS::KeyBind(::Ctrl::Key::S, ::Ctrl::Act::Continuous, +[]{ - Game::core->sendPlayerMoveAct(Geom::down); - }) - }; - ::Ctrl::Operation shoot_rls{ - "shoot-rls", OS::KeyBind(::Ctrl::Mouse::LMB, ::Ctrl::Act::Release, +[]{ - if(Game::core->playerController){ - Game::core->playerController->shoot = false; - } - }) - }; - ::Ctrl::Operation shoot_prs{ - "shoot-prs", OS::KeyBind(::Ctrl::Mouse::LMB, ::Ctrl::Act::Press, +[]{ - if(Game::core->playerController && !Core::uiRoot->cursorCaptured()){ - Game::core->playerController->shoot = true; - } - }), - {shoot_rls.name} - }; +int main(){ + // ext::StringMap map{}; + Assets::TexturePage mainPage{"main"}; + Assets::TexturePage lightPage{"light"}; - ::Ctrl::Operation moveTrans_rls{ - "move-trans-rls", OS::KeyBind(::Ctrl::Key::Left_Shift, ::Ctrl::Act::Release, +[]{ - if(Game::core->playerController){ - Game::core->playerController->moveCommand.translatory = false; - } - }) - }; + OS::File testTextureDir{R"(D:\projects\GameEngine\properties\resource\assets\texture\main)"}; - ::Ctrl::Operation moveTrans_prs{ - "move-trans-prs", OS::KeyBind(::Ctrl::Key::Left_Shift, ::Ctrl::Act::Press, +[]{ - if(Game::core->playerController){ - Game::core->playerController->moveCommand.translatory = true; - } - }), - {moveTrans_rls.name} - }; + + using namespace Assets::Load; + // + LoadManager loadManager{}; + TexturePacker packer{}; + + packer.setCacheDir(OS::File{R"(D:\projects\GameEngine\properties\resource\cache)"}); + + testTextureDir.subFile("base").forAllSubs([&](auto&& file){ + mainPage.pullRequest(file.stem(), file); + }); + + testTextureDir.subFile("light").forAllSubs([&](auto&& file){ + auto name = file.stem(); + auto pos = name.find_first_of('.'); + name = name.substr(0, pos); + + lightPage.pullRequest(name, file); + }); + + lightPage.dependency = &mainPage; + packer.pushPage(&mainPage); + packer.pushPage(&lightPage); + loadManager.registerTask(packer); + + loadManager.launch(); + + while(!loadManager.isFinished()){ + loadManager.processRequests(); + } + + loadManager.requestDone(); + // page.p + + + // Math::TestT _1{nullptr, 1}; + // Math::TestT _2{nullptr, 2}; + // Math::TestT _3{nullptr, 3}; + // Math::TestT _4{nullptr, 4}; + // Math::TestT _5{nullptr, 5}; + // + // std::vector arr{ + // &_1, + // &_2, + // &_3, + // &_4, + // &_5, + // }; + // + // arr[0]->dependencyTarget = arr[4]; + // arr[4]->dependencyTarget = arr[2]; + // arr[2]->dependencyTarget = arr[3]; + // arr[3]->dependencyTarget = arr[1]; + // Math::sort_topological(arr, &Math::TestT::dependencyTarget); + // for (auto value : arr){ + // std::println("{}", value->val); + // } + + // MiscTaskManager miscTaskManager1{}; + // MiscTaskManager miscTaskManager2{}; + // + // miscTaskManager1.push(Phase::init, Print<1>{}); + // miscTaskManager1.push(Phase::pull, Print<2>{}); + // miscTaskManager1.push(Phase::load, Print<3>{}); + // miscTaskManager1.push(Phase::load, Print<4>{}); + // miscTaskManager1.push(Phase::post_load, Print<5>{}); + // miscTaskManager1.push(Phase::end, Print<6>{}); + // miscTaskManager1.push(Phase::clear, Print<7>{}); + // + // miscTaskManager2.push(Phase::init, Print<11>{}); + // miscTaskManager2.push(Phase::pull, Print<12>{}); + // miscTaskManager2.push(Phase::load, Print<13>{}); + // miscTaskManager2.push(Phase::load, Print<14>{}); + // miscTaskManager2.push(Phase::post_load, Print<15>{}); + // miscTaskManager2.push(Phase::end, Print<16>{}); + // miscTaskManager2.push(Phase::clear, Print<17>{}); + // // std::ranges::find_if() + // auto& t =miscTaskManager1.eventManager; + // auto i = std::to_underlying(Phase::end); + // + // miscTaskManager1.eventManager.on(Phase::end, []{ + // std::println("Manager 1 End Call"); + // }); + // + // loadManager.registerTask(miscTaskManager1); + // loadManager.registerTask(miscTaskManager2); + // + // loadManager.launch(); + // + // loadManager.requestDone(); } +//TODO temp global static mut should be remove + void setupUITest(){ const auto HUD = new UI::Table{}; @@ -254,7 +309,6 @@ void setupUITest(){ .setMargin(0, 10, 0, 10); - HUD->add([](UI::Table& table){ table.add([](UI::ProgressBar& bar){ bar.progressGetter = []{ @@ -289,74 +343,7 @@ void setupUITest(){ } }); }); - // table.add([](UI::Table& t){ - // t.selfMaskOpacity = 0.0f; - // t.setBorderZero(); - // t.defaultCellLayout.setMargin({.left = 2.0f, .right = 2.f}); - // for(int i = 0; i < 8; ++i){ - // t.add([i](UI::Button& button){ - // button.setCall([i](UI::Button& b, bool){ - // b.buildTooltip(); - // }); - // - // button.setTooltipBuilder({ - // .followTarget = UI::TooltipFollowTarget::parent, - // .minHoverTime = UI::DisableAutoTooltip, - // .followTargetAlign = Align::Mode::bottom_left, - // .builder = [i](UI::Table& hint){ - // // hint.setMinimumSize({600, 300}); - // hint.setCellAlignMode(Align::Mode::top_left); - // hint.add([i](UI::Label& label){ - // label.usingGlyphHeight = label.usingGlyphWidth = true; - // label.setText( - // std::format("\nButton$${}$<\\sub>", i)); - // label.getGlyphLayout()->setSCale(0.65f); - // label.setEmptyDrawer(); - // label.getBorder().expand(4.0f); - // }).expand().endLine(); - // hint.add([i](UI::Button& button){ - // button.setCall([i](UI::Button&, bool){ - // Core::uiRoot->showDialog(true, [i](UI::Table& builder){ - // builder.add([](UI::FileTreeSelector& selector){ - // const OS::File src{ - // R"(D:\projects\GameEngine\properties\resource\assets)" - // }; - // selector.gotoFile(src, false); - // }).fillParent().setAlign(Align::Mode::top_center); - // }); - // }); - // button.setTooltipBuilder({ - // .followTarget = UI::TooltipFollowTarget::parent, - // .followTargetAlign = Align::Mode::center_right, - // .tooltipSrcAlign = Align::Mode::center_left, - // .builder = [](UI::Table& hintInner){ - // hintInner.setCellAlignMode(Align::Mode::top_left); - // hintInner.add([](UI::Label& label){ - // label.usingGlyphHeight = label.usingGlyphWidth = true; - // label.setText(std::format( - // "$${}$<\\sub>", "Nesting")); - // label.getGlyphLayout()->setSCale(0.65f); - // label.setEmptyDrawer(); - // label.getBorder().expand(4.0f); - // }).expand().endLine(); - // } - // }); - // }).setHeight(120.0f).expandY().fillParentX(); - // hint.PointCheck = 180; - // } - // }); - // }); - // } - // }).fillParent(); - // - // table.add([](UI::Table& t){ - // t.setEmptyDrawer(); - // auto& slider = t.add([](UI::SliderBar& s){ - // s.setClampedOnHori(); - // }).fillParent().endLine().as(); - // - // - // }).fillParent().setPad({.left = 2.0f}); + }) .setAlign(Align::Layout::top_left) .setSizeScale(0.4f, 0.08f) @@ -412,71 +399,6 @@ void setupUITest(){ .setSizeScale(0.15f, 0.6f) .setSrcScale(0.0f, 0.2f) .setMargin(0, 0, 10, 10); - - - // HUD->add([](UI::Table& table){}) - // .setAlign(Align::Mode::bottom_left) - // .setSizeScale(0.25f, 0.2f) - // .setMargin(0, 10, 10, 10); - // - // - // HUD->transferElem(new UI::Table{}) - // .setAlign(Align::Mode::bottom_left) - // .setSizeScale(0.075f, 0.2f) - // .setSrcScale(0.25f, 0.0f) - // .setMargin(10, 0, 10, 10); - - - // { - // auto pane = new UI::ScrollPane{}; - // - // pane->setItem([](UI::Table& rt){ - // rt.setSize(400, 900); - // // rt.add([](UI::ScrollPane& pane){ - // // pane.setItem([](UI::Table& paneT){ - // // paneT.setHeight(600); - // // paneT.setFillparentX(); - // // - // // paneT.add(); - // // paneT.lineFeed(); - // // paneT.add(); - // // paneT.add(); - // // }); - // // }); - // // // rt->add(new UI::Elem); - // // rt.lineFeed(); - // // rt.transferElem(new UI::Widget{}); - // // rt.transferElem(new UI::Widget{}); - // }); - // - // HUD->transferElem(pane).setAlign(Align::Mode::top_right).setSizeScale(0.225f, 0.25f).setMargin(10, 0, 0, 10); - // } - - // HUD->add([](UI::Table& table){ - // table.add([](UI::ScrollPane& pane){ - // pane.setItem([](UI::InputArea& area){ - // area.usingGlyphWidth = area.usingGlyphHeight = true; - // area.setMaxTextLength(1000); - // - // area.getGlyphLayout()->setSCale(0.75f); - // area.setText("Input Test\n"); - // }); - // pane.setEmptyDrawer(); - // }).fillParent(); - // }) - // .setAlign(Align::top_right) - // .setSizeScale(0.185f, 0.575f).setSrcScale(0.0f, 0.25f) - // .setMargin(10, 0, 10, 0); - // - // HUD->transferElem(new UI::Table{}) - // .setAlign(Align::top_right) - // .setSizeScale(0.225f - 0.185f, 0.45f).setSrcScale(0.185f, 0.25f) - // .setMargin(10, 10, 10, 0); - // - // HUD->add([](UI::Table& table){}) - // .setAlign(Align::Mode::bottom_right) - // .setSizeScale(0.3f, 0.15f) - // .setMargin(10, 0, 10, 0); } void setupCtrl(){ @@ -497,104 +419,20 @@ void setupCtrl(){ Assets::Ctrl::gameGroup.loadInstruction(Core::bundle); Assets::Ctrl::gameGroup.targetGroup = &Game::core->gameBinds; Assets::Ctrl::gameGroup.applyToTarget(); - - // - // Core::input.binds.registerBind(Ctrl::Mouse::_2, Ctrl::Act::Press, []{ - // const auto transed = chamberFrame->getWorldToLocal(Core::Util::getMouseToWorld()); - // - // const auto pos = chamberFrame->getChambers().getNearbyPos(transed); - // Geom::OrthoRectInt bound{pos, 3, 3}; - // - // if(chamberFrame->getChambers().placementValid(bound)){ - // chamberFrame->getChambers().insert(testFactory->genChamberTile(bound)); - // } - // }); - // - // Core::input.binds.registerBind(Ctrl::Mouse::_3, Ctrl::Act::Press, []{ - // const auto transed = chamberFrame->getWorldToLocal(Core::Util::getMouseToWorld()); - // - // const auto pos = chamberFrame->getChambers().getNearbyPos(transed); - // - // chamberFrame->getChambers().erase(pos, true); - // }); - // - // Core::input.binds.registerBind(Ctrl::Key::S, Ctrl::Act::Press, Ctrl::Mode::Ctrl, []{ - // std::jthread jthread([](){ - // OS::File fi{R"(D:\projects\GameEngine\properties\resource\test.json)"}; - // ext::json::JsonValue jval = ext::json::getJsonOf(chamberFrame->getChambers()); - // fi.writeString(std::format("{:nf0}", jval)); - // }); - // - // jthread.detach(); - // }); - - // - // Core::input->registerKeyBind(Ctrl::Key::F1, Ctrl::Act_Act::Press, [&]{ - // const OS::File tgt{R"(D:\projects\GameEngine\properties\resource\tiles.png)"}; - // - // const auto pixmap = Game::ChamberUtil::saveToPixmap(chamberFrame->getChambers()); - // - // pixmap.write(tgt, true); - // }); - - // - // Core::input.binds.registerBind(Ctrl::Key::F, Ctrl::Act::Continuous, []{ - // static ext::Timer<> timer{}; - // - // timer.run(12, OS::updateDeltaTick(), []{ - // Core::audio->play(Assets::Sounds::laser5); - // Geom::RectBox box{}; - // box.setSize(180, 12); - // box.offset = box.sizeVec2; - // box.offset.mul(-0.5f); - // - // for(int i = -3; i <= 3; ++i){ - // const auto ptr = Game::EntityManage::obtain(); - // ptr->trait = &Game::Content::basicBulletType; - // ptr->trans.vec.set(Core::camera->getPosition()); - // ptr->trans.rot = (Core::renderer->getSize() * 0.5f).angleTo(Core::input.getCursorPos()); - // - // ptr->trans.vec.add(Geom::Vec2{}.setPolar(ptr->trans.rot + 90, 80.0f * i)); - // - // ptr->vel.vec.set(320, 0).rotate(ptr->trans.rot); - // Game::EntityManage::add(ptr); - // ptr->hitBox.init(box); - // ptr->physicsBody.inertialMass = 100; - // ptr->damage.materialDamage.fullDamage = 100; - // ptr->activate(); - // } - // }); - // }); - - // Core::input.binds.registerBind( - // Ctrl::Mouse::_2, Ctrl::Act::Press, - // Ctrl::Mode::Shift - // , []{ - // Game::EntityManage::realEntities.quadTree->intersectPoint( - // Core::camera->getScreenToWorld(Core::renderer->getNormalized(Core::input.getCursorPos())), - // [](decltype(Game::EntityManage::realEntities)::ValueType* entity){ - // entity->controller->selected = !entity->controller->selected; - // Game::core->overlayManager->registerSelected( - // std::dynamic_pointer_cast(std::move(entity->obtainSharedSelf()))); - // }); - // }); } -REFL_REGISTER_CLASS_DEF(::TestChamberFactory::ChamberType<>) - - void setupBaseDraw(){ - ::Core::renderer->getListener().on([](const auto& e){ + ::Core::renderer->getListener().on([](const auto& e){ Core::uiRoot->drawCursor(); }); - ::Core::renderer->getListener().on([]([[maybe_unused]] const auto& e){ + ::Core::renderer->getListener().on([]([[maybe_unused]] const auto& e){ Game::core->drawBeneathUI(e.renderer); // Graphic::Draw::Overlay::getBatch().flush(); }); } -int main(const int argc, char* argv[]){ +int main_(const int argc, char* argv[]){ //Init ::Test::init(argc, argv); @@ -663,7 +501,7 @@ int main(const int argc, char* argv[]){ // GL::Blendings::Disable.apply(worldFrameBuffer.getID()); - Core::renderer->getListener().on([&](const Event::Draw_After& event){ + Core::renderer->getListener().on([&](const ext::Draw_After& event){ if(!drawDebug) return; // event.renderer->effectBuffer.bind(); event.renderer->frameBegin(&frameBuffer); @@ -680,7 +518,7 @@ int main(const int argc, char* argv[]){ }); // glDepthRangef(Graphic::Draw::DepthNear, Graphic::Draw::DepthFar); - Core::renderer->getListener().on([&](const auto& event){ + Core::renderer->getListener().on([&](const auto& event){ GL::Blendings::Normal.apply(); Graphic::Mesh::meshBegin(); @@ -718,7 +556,7 @@ int main(const int argc, char* argv[]){ // GL::Blendings::Normal.apply(); }); - Core::renderer->getListener().on([&](const auto& e){ + Core::renderer->getListener().on([&](const auto& e){ if(!drawDebug) return; e.renderer->frameBegin(&frameBuffer); e.renderer->frameBegin(&multiSample); diff --git a/src/SideTemp.cppm b/src/SideTemp.cppm index 19bf7528..3557839c 100644 --- a/src/SideTemp.cppm +++ b/src/SideTemp.cppm @@ -74,27 +74,91 @@ export struct TestChamberFactory : Game::ChamberFactory{ } }; -REFL_REGISTER_CLASS_DEF(::TestChamberFactory::ChamberType<>) +export { + REFL_REGISTER_CLASS_DEF(::TestChamberFactory::ChamberType<>) +} + + +export namespace GameCtrl{ + ::Ctrl::Operation moveLeft{ + "move-left", OS::KeyBind(::Ctrl::Key::A, ::Ctrl::Act::Continuous, +[]{ + Game::core->sendPlayerMoveAct(Geom::left); + }) + }; + + ::Ctrl::Operation moveRight{ + "move-right", OS::KeyBind(::Ctrl::Key::D, ::Ctrl::Act::Continuous, +[]{ + Game::core->sendPlayerMoveAct(Geom::right); + }) + }; + + ::Ctrl::Operation moveForward{ + "move-up", OS::KeyBind(::Ctrl::Key::W, ::Ctrl::Act::Continuous, +[]{ + Game::core->sendPlayerMoveAct(Geom::up); + }) + }; + + ::Ctrl::Operation moveBack{ + "move-down", OS::KeyBind(::Ctrl::Key::S, ::Ctrl::Act::Continuous, +[]{ + Game::core->sendPlayerMoveAct(Geom::down); + }) + }; + + ::Ctrl::Operation shoot_rls{ + "shoot-rls", OS::KeyBind(::Ctrl::Mouse::LMB, ::Ctrl::Act::Release, +[]{ + if(Game::core->playerController){ + Game::core->playerController->shoot = false; + } + }) + }; + + ::Ctrl::Operation shoot_prs{ + "shoot-prs", OS::KeyBind(::Ctrl::Mouse::LMB, ::Ctrl::Act::Press, +[]{ + if(Game::core->playerController && !Core::uiRoot->cursorCaptured()){ + Game::core->playerController->shoot = true; + } + }), + {shoot_rls.name} + }; + + + ::Ctrl::Operation moveTrans_rls{ + "move-trans-rls", OS::KeyBind(::Ctrl::Key::Left_Shift, ::Ctrl::Act::Release, +[]{ + if(Game::core->playerController){ + Game::core->playerController->moveCommand.translatory = false; + } + }) + }; + + ::Ctrl::Operation moveTrans_prs{ + "move-trans-prs", OS::KeyBind(::Ctrl::Key::Left_Shift, ::Ctrl::Act::Press, +[]{ + if(Game::core->playerController){ + Game::core->playerController->moveCommand.translatory = true; + } + }), + {moveTrans_rls.name} + }; +} export template <> -struct ::Core::IO::JsonSerializator> : Game::ChamberGridData::JsonSrl{}; +struct ::ext::json::JsonSerializator> : Game::ChamberGridData::JsonSrl{}; export template <> -struct ::Core::IO::JsonSerializator>{ +struct ::ext::json::JsonSerializator>{ static void write(ext::json::JsonValue& jsonValue, const Game::ChamberGrid& data){ - ::Core::IO::JsonSerializator>::write(jsonValue, data); + ::ext::json::JsonSerializator>::write(jsonValue, data); } static void read(const ext::json::JsonValue& jsonValue, Game::ChamberGrid& data){ - ::Core::IO::JsonSerializator>::read(jsonValue, data); + ::ext::json::JsonSerializator>::read(jsonValue, data); data.reTree(); } }; export template<> -struct ::Core::IO::JsonSerializator> : Game::ChamberJsonSrl{}; +struct ::ext::json::JsonSerializator> : Game::ChamberJsonSrl{}; export namespace Test{ std::unique_ptr> chamberFrame{}; @@ -113,11 +177,11 @@ export namespace Test{ fi.writeString(std::format("{:nf0}", jval)); } else{ - auto str = fi.quickRead(); + auto str = fi.readString(); auto cur = std::chrono::high_resolution_clock::now(); - ext::json::JsonValue jval{ext::json::Parser::parse(str)}; + ext::json::JsonValue jval{ext::json::parse(str)}; auto dur = std::chrono::high_resolution_clock::now() - cur; std::cout << std::chrono::duration_cast(dur) << std::endl; @@ -351,7 +415,7 @@ void setupUITest_Old(){ }); }); // rt->add(new UI::Elem); - rt.lineFeed(); + rt.endline(); rt.transferElem(new UI::Elem{}); rt.transferElem(new UI::Elem{}); }); diff --git a/src/Test.cppm b/src/Test.cppm index 7c9a201e..7f78e306 100644 --- a/src/Test.cppm +++ b/src/Test.cppm @@ -48,7 +48,7 @@ import Game.Entity.Controller.Player; import Game.Entity.Controller.AI; import Core.IO.Specialized; -import Core.IO.JsonIO; +import ext.json.io; import ext.Encoding; import Image.Svg; @@ -115,7 +115,7 @@ export namespace Test{ Game::core = std::make_unique(); Core::loopManager->setGameCore(Game::core.get()); - ext::json::JsonValue json{ext::json::Parser::parse(Assets::Dir::settings.subFile("ctrl.json").quickRead())}; + ext::json::JsonValue json{ext::json::parse(Assets::Dir::settings.subFile("ctrl.json").readString())}; ext::json::getValueTo(Assets::Ctrl::basicGroup, json); Core::destructors.push_back([]{ diff --git a/src/arc-impl/AssetsManager.cpp b/src/arc-impl/AssetsManager.cpp index c777f999..b719506f 100644 --- a/src/arc-impl/AssetsManager.cpp +++ b/src/arc-impl/AssetsManager.cpp @@ -16,7 +16,7 @@ import Font; import OS.File; import Font.GlyphArrangement; import OS; -import Event; +import ext.Event; void Assets::Manager::pullRequest() { loadEvents.fire(AssetsLoadInit{this}); @@ -86,7 +86,7 @@ void Assets::Manager::loadPost() { Assets::loadAfter(); Font::defGlyphParser->context.defaultFont = Assets::Fonts::telegrama; - Font::defGlyphParser->fontLib = fonts.atlas.get(); + Font::defGlyphParser->loadedFonts = fonts.atlas.get(); } void Assets::Manager::loadEnd() { diff --git a/src/arc-impl/GlyphArrangement.cpp b/src/arc-impl/GlyphArrangement.cpp index e91e61f1..0357d228 100644 --- a/src/arc-impl/GlyphArrangement.cpp +++ b/src/arc-impl/GlyphArrangement.cpp @@ -61,7 +61,7 @@ void Font::GlyphLayout::render(const float alphaMask, float progress) const { } } -Font::TypesettingContext::TypesettingContext(const FontFlags* const font): defaultFont(font), currentFont(font), +Font::TypesettingContext::TypesettingContext(const FontFace* const font): defaultFont(font), currentFont(font), fallbackFont(font) { if(!currentFont) throw ext::NullPointerException{}; lineSpacing = currentFont->data->lineSpacingDef * 1.8f; @@ -223,7 +223,7 @@ void Font::GlyphParser::parse(const std::shared_ptr& layout) const layout->updateDrawbound(); } -void Font::initParser(const FontFlags* const defFont) { +void Font::initParser(const FontFace* const defFont) { defGlyphParser = std::make_unique(defFont); defGlyphParser->charParser->registerDefParser(); @@ -253,7 +253,7 @@ void Font::initParser(const FontFlags* const defFont) { } else { data.context.fallbackFont = data.context.currentFont; try { - data.context.currentFont = defGlyphParser->fontLib->obtain(std::stoi(static_cast(sub))); + data.context.currentFont = defGlyphParser->loadedFonts->obtain(std::stoi(static_cast(sub))); } catch([[maybe_unused]] std::invalid_argument& e) { //TODO throw maybe ? } diff --git a/src/arc-impl/ui/Cell.cpp b/src/arc-impl/ui/Cell.cpp index 3e966672..6288e49b 100644 --- a/src/arc-impl/ui/Cell.cpp +++ b/src/arc-impl/ui/Cell.cpp @@ -3,46 +3,52 @@ module UI.Cell; bool UI::LayoutCell::applySizeToItem(){ // NOLINT(*-make-member-function-const) const Geom::Vec2 originalSize = allocatedBound.getSize(); + Geom::Vec2 exportSize{}; + if(hasDefWidth()){ - item->setWidth(defSize.x); + exportSize.x = defSize.x; } if(hasDefHeight()){ - item->setHeight(defSize.y); + exportSize.y = defSize.y; } //Apply Expansion if(modifyParentX) { - const float width = Math::max(allocatedBound.getWidth(), item->getWidth()); + const float width = Math::max(allocatedBound.getWidth(), exportSize.x); allocatedBound.setWidth(width * getHoriScale()); }else{ // allocatedBound.setShorterWidth(item->getWidth()); } if(modifyParentY) { - const float height = Math::max(allocatedBound.getHeight(), item->getHeight()); + const float height = Math::max(allocatedBound.getHeight(), exportSize.y); allocatedBound.setHeight(height * getVertScale()); }else{ // allocatedBound.setShorterHeight(item->getHeight()); } if(scaleRelativeToParentX){ - item->setWidth(allocatedBound.getWidth() * getHoriScale()); + exportSize.x = allocatedBound.getWidth() * getHoriScale(); } if(scaleRelativeToParentY){ - item->setHeight(allocatedBound.getHeight() * getVertScale()); + exportSize.y = allocatedBound.getHeight() * getVertScale(); + } + + if(hasRatioFromWidth()){ + exportSize.y = exportSize.x * getRatio_W2H(); } + if(hasRatioFromHeight()){ + exportSize.x = exportSize.y * getRatio_H2W(); + } - //TODO uses validsize instead - item->setSize( - Math::clampPositive(item->getWidth() - getMarginHori()), - Math::clampPositive(item->getHeight() - getMarginVert()) - ); + item->setWidth_Quiet(Math::clampPositive(exportSize.x - getMarginHori())); + item->setHeight_Quiet(Math::clampPositive(exportSize.y - getMarginVert())); - Geom::Vec2 currentSize = item->bound.getSize(); - if(currentSize == Geom::Vec2{std::numeric_limits::max(), std::numeric_limits::max()}){ + const Geom::Vec2 currentSize = item->bound.getSize(); + if(currentSize == Geom::maxVec2){ item->setMaximumSize(allocatedBound.getSize()); } diff --git a/src/arc-impl/ui/Root.cpp b/src/arc-impl/ui/Root.cpp index 35c41c18..e9dd14c6 100644 --- a/src/arc-impl/ui/Root.cpp +++ b/src/arc-impl/ui/Root.cpp @@ -5,6 +5,7 @@ import UI.Drawer; import std; import Core; import OS.ApplicationListenerSetter; +import ext.algorithm; void UI::Root::setRootOf(Elem* widget){ widget->setRoot(this); @@ -39,8 +40,8 @@ UI::Root::Root(): tooltipManager{this}{ UI::Root::~Root(){ Core::input.eraseSubInput(&uiInput); - std::erase(Core::input.inputKeyListeners, this); - std::erase(Core::input.inputMouseListeners, this); + ext::algo::erase_unique_unstable(Core::input.inputKeyListeners, this); + ext::algo::erase_unique_unstable(Core::input.inputMouseListeners, this); } void UI::Root::loadBinds(const Elem* elem, OS::InputBindGroup& binds){ diff --git a/src/arc-impl/ui/Table.cpp b/src/arc-impl/ui/Table.cpp index 464d6b43..dfddae25 100644 --- a/src/arc-impl/ui/Table.cpp +++ b/src/arc-impl/ui/Table.cpp @@ -6,20 +6,20 @@ import Math; //TODO this is a temp import import Graphic.Draw; -void UI::Table::layoutRelative() { - if(cells.empty() || std::ranges::all_of(cells, std::identity{}, &LayoutCell::isIgnoreLayout))return; +void UI::Table::layoutRelative(){ + if(cells.empty() || std::ranges::all_of(cells, std::identity{}, &LayoutCell::isIgnoreLayout)) return; recalculateLayoutSize(); cells.back().item->setEndRow(true); // [y0, y1, ... yn, x1, x2, ... xn] - std::basic_string maxSizeArr{}; - maxSizeArr.resize(columns() + rows()); + std::vector> maxSizeArr(columns() + rows()); + // maxSizeArr.resize(); //Split all into boxes //TODO should cells have their own column or row data? - Geom::Point2 curPos{}; + Geom::Point2 scaleRequester{}; @@ -29,22 +29,27 @@ void UI::Table::layoutRelative() { bool expandX_ifLarger{true}; bool expandY_ifLarger{true}; - { //Register Self Adapted Row Max width & height + bool hasRatioCalculate{false}; + + { + //Register Self Adapted Row Max width & height Geom::Point2 currentLineScaleRequester{}; - for(const auto& cell : cells) { - if(cell.isIgnoreLayout())continue; + for(Geom::Point2 curPos{}; const auto& cell : cells){ + if(cell.isIgnoreLayout()) continue; - if(!cell.scaleRelativeToParentX) { + hasRatioCalculate = hasRatioCalculate || cell.hasRatioFromHeight() || cell.hasRatioFromWidth(); + + if(!cell.scaleRelativeToParentX){ const auto curPosX_indexed = rows() + curPos.x; - maxSizeArr[curPosX_indexed] = Math::max(maxSizeArr[curPosX_indexed], cell.getDefWidth()); - }else{ + maxSizeArr[curPosX_indexed] = {Math::max(maxSizeArr[curPosX_indexed].first, cell.getDefWidth()), true}; + } else{ currentLineScaleRequester.x++; } - if(!cell.scaleRelativeToParentY) { - maxSizeArr[curPos.y] = Math::max(maxSizeArr[curPos.y], cell.getDefHeight()); - }else{ + if(!cell.scaleRelativeToParentY){ + maxSizeArr[curPos.y] = {Math::max(maxSizeArr[curPos.y].first, cell.getDefHeight()), true}; + } else{ currentLineScaleRequester.y = 1; } @@ -58,7 +63,7 @@ void UI::Table::layoutRelative() { if(cell.isEndRow()){ curPos.y++; - curPos.x= 0; + curPos.x = 0; scaleRequester.maxX(currentLineScaleRequester.x); scaleRequester.y += currentLineScaleRequester.y; @@ -66,137 +71,175 @@ void UI::Table::layoutRelative() { currentLineScaleRequester.setZero(); } } - - curPos.setZero(); } { - //Assign Cell Position - float capturedW{0}; - float capturedH{0}; - - const int remainColomns{scaleRequester.x}; - const int remainRows{scaleRequester.y}; - - for(int y = 0; y < rows(); ++y){ - const float h = maxSizeArr[y]; - if(Math::zero(h)){ - maxSizeArr[y] -= 1.0f; - continue; + + allocateBound:{ + //Assign Cell Position + float capturedW{0}; + float capturedH{0}; + + const int remainColomns{scaleRequester.x}; + const int remainRows{scaleRequester.y}; + + auto captured = maxSizeArr | std::ranges::views::elements<1>; + + capturedW = std::accumulate(captured.begin() + rows(), captured.end(), 0); + capturedH = std::accumulate(captured.begin(), captured.begin() + rows(), 0); + + for(int y = 0; y < rows(); ++y){ + if(maxSizeArr[y].second) capturedH += maxSizeArr[y].first; } - capturedH += h; - } - for(int x = rows(); x < maxSizeArr.size(); ++x){ - const float w = maxSizeArr[x]; - if(Math::zero(w)){ - maxSizeArr[x] -= 1.0f; - continue; + for(int x = rows(); x < maxSizeArr.size(); ++x){ + if(maxSizeArr[x].second) capturedW += maxSizeArr[x].first; } - capturedW += w; - } - const float spacingX = remainColomns ? Math::max(0.0f, (getValidWidth() - capturedW) / static_cast(remainColomns)) : 0.0f; - const float spacingY = remainRows ? Math::max(0.0f, (getValidHeight() - capturedH) / static_cast(remainRows)) : 0.0f; + const float spacingX = remainColomns + ? Math::clampPositive( + (getValidWidth() - capturedW) / static_cast(remainColomns)) + : 0.0f; + const float spacingY = remainRows + ? Math::clampPositive( + (getValidHeight() - capturedH) / static_cast(remainRows)) + : 0.0f; + + for(int y = 0; y < rows(); ++y){ + if(!maxSizeArr[y].second) maxSizeArr[y].first = spacingY; + } - for(int y = 0; y < rows(); ++y){ - if(maxSizeArr[y] < 0)maxSizeArr[y] = spacingY; + for(int x = rows(); x < maxSizeArr.size(); ++x){ + if(!maxSizeArr[x].second) maxSizeArr[x].first = spacingX; + } } - for(int x = rows(); x < maxSizeArr.size(); ++x){ - if(maxSizeArr[x] < 0)maxSizeArr[x] = spacingX; + if(hasRatioCalculate){ + hasRatioCalculate = false; + + for(Geom::Point2 curPos{}; const auto& cell : cells){ + if(cell.isIgnoreLayout()) continue; + + if(cell.hasRatioFromHeight()){ + const auto curPosX_indexed = rows() + curPos.x; + maxSizeArr[curPosX_indexed].first = Math::max(maxSizeArr[curPosX_indexed].first, + maxSizeArr[curPos.y].first * cell.getRatio_H2W()); + maxSizeArr[curPosX_indexed].second |= maxSizeArr[curPos.y].second; + } + + + if(cell.hasRatioFromWidth()){ + const auto curPosX_indexed = rows() + curPos.x; + maxSizeArr[curPos.y].first = Math::max(maxSizeArr[curPos.y].first, + maxSizeArr[curPosX_indexed].first * cell.getRatio_W2H()); + maxSizeArr[curPos.y].second |= maxSizeArr[curPosX_indexed].second; + } + + curPos.x++; + + if(cell.isEndRow()){ + curPos.y++; + curPos.x = 0; + } + } + + goto allocateBound; } + auto sizes = maxSizeArr | std::ranges::views::elements<0>; Geom::Vec2 cellSize{ - std::accumulate(maxSizeArr.begin() + rows(), maxSizeArr.end(), 0.0f), - std::accumulate(maxSizeArr.begin(), maxSizeArr.begin() + rows(), 0.0f) + std::accumulate(sizes.begin() + rows(), sizes.end(), 0.0f), + std::accumulate(sizes.begin(), sizes.begin() + rows(), 0.0f) }; if(expandX && !fillParentX){ if(expandX_ifLarger){ setWidth(Math::max(cellSize.x + getBorderWidth(), getWidth())); - }else{ - if(!scaleRequester.x || Math::zero(getWidth()))setWidth(cellSize.x + getBorderWidth()); + } else{ + if(!scaleRequester.x || Math::zero(getWidth())) setWidth(cellSize.x + getBorderWidth()); } } if(expandY && !fillParentY){ if(expandY_ifLarger){ setHeight(Math::max(cellSize.y + getBorderHeight(), getHeight())); - }else{ - if(!scaleRequester.y || Math::zero(getHeight()))setHeight(cellSize.y + getBorderHeight()); + } else{ + if(!scaleRequester.y || Math::zero(getHeight())) setHeight(cellSize.y + getBorderHeight()); } } //TODO cells may expand during this process //Need another re-layout to handle this - Geom::Vec2 currentSrc{}; - Geom::Vec2 currentPad{}; - Geom::Vec2 maximumPad{}; - - Geom::Vec2 currentMaxSize{}; + { + Geom::Vec2 currentSrc{}; + Geom::Vec2 currentPad{}; + Geom::Vec2 maximumPad{}; - cellSize.setZero(); + Geom::Vec2 currentMaxSize{}; - for(auto& cell : cells) { - if(cell.isIgnoreLayout())continue; + cellSize.setZero(); - const auto curPosX_indexed = rows() + curPos.x; + for(Geom::Point2 curPos{}; auto& cell : cells){ + if(cell.isIgnoreLayout()) continue; - cell.allocatedBound.setSize(maxSizeArr[curPosX_indexed], maxSizeArr[curPos.y]); + const auto curPosX_indexed = rows() + curPos.x; - cell.allocatedBound.setSrc( - currentSrc.x, currentSrc.y - cell.allocatedBound.getHeight() //Top src to Bottom src transform - ); + cell.allocatedBound.setSize(maxSizeArr[curPosX_indexed].first, maxSizeArr[curPos.y].first); - const Geom::Vec2 thisPad{cell.pad.left, -cell.pad.top}; + cell.allocatedBound.setSrc( + currentSrc.x, currentSrc.y - cell.allocatedBound.getHeight() //Top src to Bottom src transform + ); - cell.applySizeToItem(); - cell.allocatedBound.move(Geom::Vec2{currentPad.x, -maximumPad.y} + thisPad); + const Geom::Vec2 thisPad{cell.pad.left, -cell.pad.top}; - curPos.x++; - currentSrc.x += maxSizeArr[curPosX_indexed]; + cell.applySizeToItem(); + cell.allocatedBound.move(Geom::Vec2{currentPad.x, -maximumPad.y} + thisPad); - currentPad.x += cell.getPadHori(); - currentPad.maxY(cell.getPadVert()); + curPos.x++; + currentSrc.x += maxSizeArr[curPosX_indexed].first; - currentMaxSize.x += cell.getCellWidth(); - currentMaxSize.maxY(cell.getCellHeight()); + currentPad.x += cell.getPadHori(); + currentPad.maxY(cell.getPadVert()); - if(cell.isEndRow()) { - maximumPad.maxX(currentPad.x); - maximumPad.y += currentPad.y; + currentMaxSize.x += cell.getCellWidth(); + currentMaxSize.maxY(cell.getCellHeight()); - cellSize.maxX(currentMaxSize.x); - cellSize.y += currentMaxSize.y; + if(cell.isEndRow()){ + maximumPad.maxX(currentPad.x); + maximumPad.y += currentPad.y; - currentPad.setZero(); - currentMaxSize.setZero(); + cellSize.maxX(currentMaxSize.x); + cellSize.y += currentMaxSize.y; - currentSrc.x = 0; - currentSrc.y -= maxSizeArr[curPos.y]; + currentPad.setZero(); + currentMaxSize.setZero(); - curPos.y++; - curPos.x = 0; + currentSrc.x = 0; + currentSrc.y -= maxSizeArr[curPos.y].first; + + curPos.y++; + curPos.x = 0; + } } - } - if(!maximumPad.isZero()){ - cellSize += maximumPad; - if(expandX && !fillParentX){ - if(expandX_ifLarger){ - setWidth(Math::max(cellSize.x + getBorderWidth(), getWidth())); - }else{ - if(!scaleRequester.x)setWidth(cellSize.x + getBorderWidth()); + if(!maximumPad.isZero()){ + cellSize += maximumPad; + + if(expandX && !fillParentX){ + if(expandX_ifLarger){ + setWidth(Math::max(cellSize.x + getBorderWidth(), getWidth())); + } else{ + if(!scaleRequester.x) setWidth(cellSize.x + getBorderWidth()); + } } - } - if(expandY && !fillParentY){ - if(expandY_ifLarger){ - setHeight(Math::max(cellSize.y + getBorderHeight(), getHeight())); - }else{ - if(!scaleRequester.y)setHeight(cellSize.y + getBorderHeight()); + if(expandY && !fillParentY){ + if(expandY_ifLarger){ + setHeight(Math::max(cellSize.y + getBorderHeight(), getHeight())); + } else{ + if(!scaleRequester.y) setHeight(cellSize.y + getBorderHeight()); + } } } } @@ -204,19 +247,18 @@ void UI::Table::layoutRelative() { Geom::Vec2 offset = Align::getOffsetOf(cellAlignMode, cellSize, getValidBound()); offset.y += cellSize.y; - for(auto& cell : cells) { - if(cell.isIgnoreLayout())continue; + for(auto& cell : cells){ + if(cell.isIgnoreLayout()) continue; cell.allocatedBound.move(offset); cell.applyPosToItem(this); } - } } -void UI::Table::layoutIrrelative() { - for(auto& cell : cells) { - if(cell.isIgnoreLayout())continue; +void UI::Table::layoutIrrelative(){ + for(auto& cell : cells){ + if(cell.isIgnoreLayout()) continue; cell.allocatedBound = getValidBound(); @@ -228,11 +270,9 @@ void UI::Table::layoutIrrelative() { void UI::Table::layout(){ layout_tryFillParent(); - layoutChildren(); - - if(relativeLayoutFormat) { + if(relativeLayoutFormat){ layoutRelative(); - }else { + } else{ layoutIrrelative(); } @@ -253,5 +293,4 @@ void UI::Table::drawContent() const{ // } Group::drawContent(); - } diff --git a/src/arc/asset/AssetsManager.cppm b/src/arc/asset/AssetsManager.cppm index 3112a2a5..91536ee4 100644 --- a/src/arc/asset/AssetsManager.cppm +++ b/src/arc/asset/AssetsManager.cppm @@ -7,7 +7,7 @@ import Assets.SoundLoader; import Graphic.TextureAtlas; import GL.Shader.Manager; import Font; -import Event; +import ext.Event; import std; @@ -18,22 +18,22 @@ export namespace Assets{ class Manager; - struct AssetsLoadInit final : Event::EventType { + struct AssetsLoadInit final : ext::EventType { Manager* const manager; [[nodiscard]] explicit AssetsLoadInit(Manager* const manager) : manager(manager) {} }; - struct AssetsLoadPull final : Event::EventType { + struct AssetsLoadPull final : ext::EventType { Manager* const manager; [[nodiscard]] explicit AssetsLoadPull(Manager* const manager) : manager(manager) {} }; - struct AssetsLoadPost final : Event::EventType { + struct AssetsLoadPost final : ext::EventType { Manager* const manager; [[nodiscard]] explicit AssetsLoadPost(Manager* const manager) : manager(manager) {} }; - struct AssetsLoadEnd final : Event::EventType { + struct AssetsLoadEnd final : ext::EventType { Manager* const manager; [[nodiscard]] explicit AssetsLoadEnd(Manager* const manager) : manager(manager) {} }; @@ -47,11 +47,11 @@ namespace Assets{ Assets::SoundLoader soundLoader{}; Assets::AssetsLoader loader{}; - Event::EventManager loadEvents{ - Event::indexOf(), - Event::indexOf(), - Event::indexOf(), - Event::indexOf() + ext::EventManager loadEvents{ + ext::indexOf(), + ext::indexOf(), + ext::indexOf(), + ext::indexOf() }; std::unique_ptr tempFontLoader{std::make_unique()}; @@ -75,7 +75,7 @@ namespace Assets{ return atlas; } - [[nodiscard]] Font::FontAtlas& getFonts() const{ + [[nodiscard]] Font::FontStorage& getFonts() const{ return *fonts.atlas; } @@ -91,7 +91,7 @@ namespace Assets{ return loader; } - [[nodiscard]] Event::EventManager& getEventTrigger() { + [[nodiscard]] ext::EventManager& getEventTrigger() { return loadEvents; } diff --git a/src/arc/asset/Assets_Ctrl.cppm b/src/arc/asset/Assets_Ctrl.cppm index 1b01734c..ff29d654 100644 --- a/src/arc/asset/Assets_Ctrl.cppm +++ b/src/arc/asset/Assets_Ctrl.cppm @@ -93,12 +93,12 @@ namespace Assets::Ctrl{ ::Ctrl::OperationGroup gameGroup{"game-group"}; - ext::StringMap<::Ctrl::OperationGroup*> allGroups{ + ext::StringHashMap<::Ctrl::OperationGroup*> allGroups{ {basicGroup.getName(), &basicGroup}, {gameGroup.getName(), &gameGroup} }; - ext::StringMap relatives{}; + ext::StringHashMap relatives{}; void apply(){ mainControlGroup.clearAllBinds(); diff --git a/src/arc/asset/Assets_Graphic.cppm b/src/arc/asset/Assets_Graphic.cppm index b94904be..d9a10d2b 100644 --- a/src/arc/asset/Assets_Graphic.cppm +++ b/src/arc/asset/Assets_Graphic.cppm @@ -147,7 +147,7 @@ export namespace Assets{ const std::vector targetChars = std::ranges::views::iota(' ' + 1, '~' + 1) | std::ranges::to>();// {{' ' + 1, '~'}}; std::vector targetChars_withChinese = std::vector{targetChars}; - Font::FontFlags + Font::FontFace *sourceHan_SC_SB{nullptr}, *consola_Regular{nullptr}, @@ -173,7 +173,7 @@ export namespace Assets{ loader->quickInit = true; telegrama = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("telegrama.otf"), targetChars, DefFlag, 120}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("telegrama.otf"), targetChars, DefFlag, 120}); loader->load(); @@ -184,39 +184,39 @@ export namespace Assets{ targetChars_withChinese.append_range(Font::genRefTable(unicodeRefDir.find("zh_cn.txt"))); sourceHan_SC_SB = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("SourceHanSerifSC-SemiBold.otf" ), targetChars_withChinese}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("SourceHanSerifSC-SemiBold.otf" ), targetChars_withChinese}); sourceHan_SC_SB->setDefErrorFallback(::Assets::Textures::error); consola_Regular = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("consola.ttf" ), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("consola.ttf" ), targetChars}); consola_Italic = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("consolai.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("consolai.ttf"), targetChars}); consola_Bold = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("consolab.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("consolab.ttf"), targetChars}); consola_Bold_Italic = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("consolaz.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("consolaz.ttf"), targetChars}); times_Regular = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("times.ttf" ), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("times.ttf" ), targetChars}); times_Italic = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("timesi.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("timesi.ttf"), targetChars}); times_Bold = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("timesbd.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("timesbd.ttf"), targetChars}); times_Bold_Italic = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("timesbi.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("timesbi.ttf"), targetChars}); josefinSans_Regular = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("josefinSans-ES-Regular.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("josefinSans-ES-Regular.ttf"), targetChars}); josefinSans_Bold = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("josefinSans-ES-Bold.ttf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("josefinSans-ES-Bold.ttf"), targetChars}); josefinSans_Regular_Large = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("josefinSans-ES-Regular.ttf"), targetChars, DefFlag, 90}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("josefinSans-ES-Regular.ttf"), targetChars, DefFlag, 90}); josefinSans_Bold_Large = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("josefinSans-ES-Bold.ttf"), targetChars, DefFlag, 90}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("josefinSans-ES-Bold.ttf"), targetChars, DefFlag, 90}); telegrama = - loader->registerFont(new Font::FontFlags{Dir::font.subFile("telegrama.otf"), targetChars}); + loader->registerFont(new Font::FontFace{Dir::font.subFile("telegrama.otf"), targetChars}); telegrama->fallback = sourceHan_SC_SB; diff --git a/src/arc/asset/Bundle.cppm b/src/arc/asset/Bundle.cppm index 0d61d670..81fd8b29 100644 --- a/src/arc/asset/Bundle.cppm +++ b/src/arc/asset/Bundle.cppm @@ -54,7 +54,7 @@ export namespace Assets{ } static ext::json::JsonValue loadFile(const OS::File& file){ - return ext::json::Parser::parse(file.quickRead()); + return ext::json::parse(file.readString()); } static std::optional find(const std::vector& dir, const ext::json::Object* last){ diff --git a/src/arc/asset/AssetsLoader.cppm b/src/arc/asset/LegacyAssetsLoader.cppm similarity index 96% rename from src/arc/asset/AssetsLoader.cppm rename to src/arc/asset/LegacyAssetsLoader.cppm index 31db5751..003f07d9 100644 --- a/src/arc/asset/AssetsLoader.cppm +++ b/src/arc/asset/LegacyAssetsLoader.cppm @@ -18,7 +18,7 @@ export namespace Assets{ }; - struct AssetsTaskHandler: ext::TaskHandler { + struct [[deprecated]] AssetsTaskHandler: ext::TaskHandler { std::exception_ptr lastExceptionPtr{}; AssetsLoader* target{nullptr}; @@ -44,7 +44,7 @@ export namespace Assets{ static constexpr auto maxLoadSpacing = ::std::chrono::milliseconds(330); - ext::Timestamper timer{}; + ext::TimeStamper timer{}; std::unordered_map tasks{}; AssetsTaskHandler postHandler{this}; @@ -79,7 +79,7 @@ export namespace Assets{ } } - [[nodiscard]] ext::Timestamper& getTimer() { + [[nodiscard]] ext::TimeStamper& getTimer() { return timer; } @@ -153,7 +153,7 @@ export namespace Assets{ task(); }catch(...){ //TODO exception handle - throw std::current_exception(); + throw; } } } diff --git a/src/arc/asset/TexturePacker.cppm b/src/arc/asset/LegacyTexturePacker.cppm similarity index 96% rename from src/arc/asset/TexturePacker.cppm rename to src/arc/asset/LegacyTexturePacker.cppm index 456fcdcb..9c66cd28 100644 --- a/src/arc/asset/TexturePacker.cppm +++ b/src/arc/asset/LegacyTexturePacker.cppm @@ -3,7 +3,7 @@ export module Assets.TexturePacker; import std; import Assets.Loader; -import Math.StripPacker2D; +import Math.Algo.StripPacker2D; import Graphic.Pixmap; import Geom.Rect_Orthogonal; import Geom.Vector2D; @@ -148,7 +148,7 @@ export namespace Assets { }; std::vector> textures{}; - ext::StringMap packData{}; + ext::StringHashMap packData{}; std::vector toMerge{}; std::vector mergedMaps{}; @@ -186,11 +186,11 @@ export namespace Assets { TexturePackPage(pageName, cacheDir, {0, 0, GL::getMaxTextureSize(), GL::getMaxTextureSize()}){ } - [[nodiscard]] const ext::StringMap& getData() const { + [[nodiscard]] const ext::StringHashMap& getData() const { return packData; } - [[nodiscard]] ext::StringMap& getData() { + [[nodiscard]] ext::StringHashMap& getData() { return packData; } @@ -384,7 +384,7 @@ export namespace Assets { void apply() { for(auto& map : mergedMaps) { - textures.push_back(std::make_unique(map.getWidth(), map.getHeight(), std::move(map).release())); + textures.push_back(std::make_unique(map.getWidth(), map.getHeight(), map.data())); } for(auto& data : packData | std::views::values) { @@ -435,7 +435,7 @@ export namespace Assets { texFutures.reserve(pageSize); for(int i = 0; i < pageSize; ++i) { - texFutures.emplace_back(std::async([file = cacheDir.subFile(static_cast(pageName) + std::to_string(i) + ".png")] { + texFutures.emplace_back(std::async([file = cacheDir.subFile(pageName + std::to_string(i) + ".png")] { return GL::Texture2D{file, false}; })); } @@ -488,10 +488,10 @@ export namespace Assets { packer.setMaxSize(texMaxBound.getWidth(), texMaxBound.getHeight()); packer.sortData(); packer.process(); - const Geom::OrthoRectInt r = packer.getResultBound(); + auto [w, h] = packer.getResultSize(); //Move it - toMerge.emplace_back(std::move(packer.packed), r.getWidth(), r.getHeight(), currentID); + toMerge.emplace_back(std::move(packer.packed), w, h, currentID); loadRemains(std::move(packer.getRemains()), currentID + 1); } diff --git a/src/arc/asset/font/Font.cppm b/src/arc/asset/font/Font.cppm index e11249a1..2e78690d 100644 --- a/src/arc/asset/font/Font.cppm +++ b/src/arc/asset/font/Font.cppm @@ -9,6 +9,7 @@ import std; import ext.Async; import Assets.Loader; +// import Assets.Load.Core; import GL.Texture.Texture2D; import GL.Texture.TextureRegion; @@ -19,7 +20,7 @@ import Graphic.Pixmap; import ext.RuntimeException; import OS.File; import Image; -import Event; +import ext.Event; import Math; import ext.Heterogeneous; @@ -31,31 +32,6 @@ namespace Font { throw ext::RuntimeException{"Font Load Failed : " + fontName}; } - std::fstream obtainStream(const OS::File& file) { - return std::fstream{file.absolutePath(), std::ios::binary | std::ios::in | std::ios::out}; - } - - void readCacheVersion(std::istream& stream, unsigned char& version) { - stream.read(reinterpret_cast(&version), sizeof(version)); - } - - void readCacheVersion(std::istream&& stream, unsigned char& version) { - stream.read(reinterpret_cast(&version), sizeof(version)); - } - - void saveCacheVersion(std::ostream& stream, const unsigned char& version) { - stream.write(reinterpret_cast(&version), sizeof(version)); - } - - //Just use 'size', not EOF, just my preference... - void readCacheDataSize(std::istream& stream, size_t& size) { - stream.read(reinterpret_cast(&size ), sizeof(size )); - } - - void saveCacheDataSize(std::ostream& stream, const size_t size) { - stream.write(reinterpret_cast(&size ), sizeof(size )); - } - void writeIntoPixmap(const FT_Bitmap& map, unsigned char* data) { for(unsigned size = 0; size < map.width * map.rows; ++size) { //Normal @@ -124,6 +100,11 @@ export namespace Font{ FT_Glyph_Metrics glyphMatrices{}; GL::TextureRegion* region{}; + [[nodiscard]] CharData() = default; + + [[nodiscard]] explicit CharData(const FT_Glyph_Metrics& glyphMatrices) + : glyphMatrices{glyphMatrices}{} + template [[nodiscard]] constexpr Geom::Vector2D getSize() const{ return {normalizeLen(glyphMatrices.width), normalizeLen(glyphMatrices.height)}; @@ -132,17 +113,15 @@ export namespace Font{ //TODO Should this be exported? struct FontData_Preload{ - FT_Glyph_Metrics glyphMatrices{}; + mutable CharData charData{}; CharCode charCode{0}; Graphic::Pixmap pixmap{}; - mutable GL::TextureRegion* region{}; - [[nodiscard]] FontData_Preload() = default; [[nodiscard]] FontData_Preload(const CharCode charCode, const FT_GlyphSlot glyph) : // NOLINT(*-misplaced-const) + charData(glyph->metrics), charCode(charCode), - glyphMatrices(glyph->metrics), pixmap(glyph->bitmap.width, glyph->bitmap.rows) { if(glyph->bitmap.buffer) { @@ -151,22 +130,12 @@ export namespace Font{ } FontData_Preload(const FT_Glyph_Metrics& glyphMatrices, const CharCode charCode, Graphic::Pixmap&& pixmap) - : glyphMatrices{glyphMatrices}, + : charData{glyphMatrices}, charCode{charCode}, pixmap{std::move(pixmap)}{} [[nodiscard]] Geom::Point2U getSize() const{ - return {normalizeLen(glyphMatrices.width), normalizeLen(glyphMatrices.height)}; - } - - void write(std::ostream& ostream) const{ - ostream.write(reinterpret_cast(&charCode), sizeof(charCode)); - ostream.write(reinterpret_cast(&glyphMatrices), sizeof(glyphMatrices)); - } - - void read(std::istream& istream){ - istream.read(reinterpret_cast(&charCode), sizeof(charCode)); - istream.read(reinterpret_cast(&glyphMatrices), sizeof(glyphMatrices)); + return {normalizeLen(charData.glyphMatrices.width), normalizeLen(charData.glyphMatrices.height)}; } friend bool operator==(const FontData_Preload& lhs, const FontData_Preload& rhs){ @@ -187,46 +156,26 @@ struct std::hash{ } }; -using PreloadDataSet = std::unordered_set, -ext::MemberEqualTo>; -export namespace Font{ + +namespace Font{ + export using PreloadDataSet = std::unordered_set, + ext::MemberEqualTo>; + + export struct FontData { std::unordered_map charDatas{}; float lineSpacingDef{-1}; explicit FontData(const PreloadDataSet& datas){ for (auto&& data : datas){ - charDatas.try_emplace(data.charCode, data.glyphMatrices, data.region); - } - } - - void write(std::ostream& ostream) const{ - const size_t size = charDatas.size(); - ostream.write(reinterpret_cast(&size), sizeof(size)); - - for(const auto& [key, value]: charDatas) { - ostream.write(reinterpret_cast(&key ), sizeof(key )); - ostream.write(reinterpret_cast(&value.glyphMatrices), sizeof(value.glyphMatrices)); - } - } - - void read(std::istream& istream){ - size_t size = 0; - istream.read(reinterpret_cast(&size), sizeof(size)); - charDatas.reserve(size); - - for(size_t i = 0; i < size; ++i) { - CharCode key; - istream.read(reinterpret_cast(&key ), sizeof(key )); - auto [kv, success] = charDatas.try_emplace(key); - - istream.read(reinterpret_cast(&kv->second.glyphMatrices), sizeof(kv->second.glyphMatrices)); + charDatas.try_emplace(data.charCode, data.charData); } } }; + export struct CustomeCharData{ CharCode code{}; Graphic::Pixmap data{}; @@ -247,23 +196,24 @@ export namespace Font{ } }; + export constexpr CharData emptyCharData{}; - struct FontFlags { + export + struct FontFace { static constexpr auto styleOffset = 16; static constexpr auto familyOffset = 8; static constexpr auto fontOffset = 0; OS::File fontFile{}; std::vector segments{}; - FT_Int loadFlags = FT_LOAD_RENDER; // + FT_Int loadFlags = FT_LOAD_RENDER; FT_UInt size = 48; unsigned char version{}; - + unsigned char expectedVersion{}; FT_Face face{nullptr}; - unsigned char expectedVersion{}; std::string styleName{regular}; std::string familyName{}; @@ -280,16 +230,12 @@ export namespace Font{ FT_UInt internalID{0}; //This should be assigned by internal operation! std::unique_ptr data{nullptr}; - std::vector customeCharDatas{}; - std::function loader = nullptr; - const FontFlags* fallback{nullptr}; - + const FontFace* fallback{nullptr}; - - [[nodiscard]] FontFlags( + [[nodiscard]] FontFace( const OS::File& fontFile, const std::vector& segments, const FT_Int loadFlags = FT_LOAD_RENDER, @@ -305,7 +251,7 @@ export namespace Font{ genDefNecessaryCharData(); } - [[nodiscard]] FontFlags( + [[nodiscard]] FontFace( OS::File&& fontFile, const std::vector& segments, const FT_Int loadFlags = FT_LOAD_RENDER, @@ -332,7 +278,7 @@ export namespace Font{ if(const auto state = FT_Load_Char(face, charCode, loadFlags); state != 0) { if(state == FT_Err_Invalid_Glyph_Index) { if(fallback){ //Seriously I don't want to use const cast - return const_cast(fallback)->tryLoad(charCode); + return const_cast(fallback)->tryLoad(charCode); } const std::string str = FT_Error_String(state); @@ -424,11 +370,11 @@ export namespace Font{ }); } - [[nodiscard]] const FontFlags* getFallback() const { + [[nodiscard]] const FontFace* getFallback() const { return fallback; } - void setFallback(const FontFlags* const fallback) { + void setFallback(const FontFace* const fallback) { this->fallback = fallback; } @@ -476,25 +422,31 @@ export namespace Font{ face = nullptr; } - ~FontFlags() { + FontFace(const FontFace& other) = delete; + + FontFace(FontFace&& other) noexcept = delete; + + FontFace& operator=(const FontFace& other) = delete; + + FontFace& operator=(FontFace&& other) noexcept = delete; + + ~FontFace() { if(!face)FT_Done_Face(face); face = nullptr; } }; + export //this will contain all the fonts with a single texture, for fast batch process - struct FontAtlas { + struct FontStorage { protected: std::unordered_map> supportedFonts{}; - std::unordered_map> fonts{}; //Access it by FontFlags.internalID + std::unordered_map> fonts{}; //Access it by FontFlags.internalID public: - [[nodiscard]] FontAtlas() = default; - - [[nodiscard]] explicit FontAtlas(std::vector>& fontsRaw){ - - // fontTexture->setScale(GL::TexParams::mipmap_linear_linear); + [[nodiscard]] FontStorage() = default; + [[nodiscard]] explicit FontStorage(std::vector>& fontsRaw){ fonts.reserve(fontsRaw.size()); for(auto& value: fontsRaw) { //Register valid chars @@ -506,18 +458,15 @@ export namespace Font{ value->data->lineSpacingDef = normalizeLen(value->face->size->metrics.ascender); } - //The pixmap data for a single font wont be needed anymore in all cases, release it if possible; - // if(value->data->fontPixmap)value->data->fontPixmap->free(); - fonts[value->internalID] = std::move(value); } } - [[nodiscard]] const FontFlags* obtain(const FontFlags* const flag) const { + [[nodiscard]] const FontFace* obtain(const FontFace* const flag) const { return obtain(flag->internalID); } - [[nodiscard]] const FontFlags* obtain(const FT_UInt id) const { + [[nodiscard]] const FontFace* obtain(const FT_UInt id) const { if(const auto itr = fonts.find(id); itr != fonts.end()) { return itr->second.get(); } @@ -534,13 +483,13 @@ export namespace Font{ } }; - class FontManager final : public ext::ProgressTask{ + class [[deprecated]] FontManager final : public ext::ProgressTask{ public: //TODO no public bool quickInit = false; - std::vector> flags{}; - Event::CycleSignalManager fontLoadListeners{}; + std::vector> flags{}; + ext::CycleSignalManager fontLoadListeners{}; OS::File rootCacheDir{}; - std::unique_ptr atlas{nullptr}; + std::unique_ptr atlas{nullptr}; Assets::TexturePackPage* texturePage{nullptr}; [[nodiscard]] FontManager() = default; @@ -553,7 +502,7 @@ export namespace Font{ rootCacheDir(root_cache_dir), texturePage{texturePage}{ } - FontFlags* registerFont(FontFlags* fontFlags) { + FontFace* registerFont(FontFace* fontFlags) { flags.emplace_back(fontFlags); return fontFlags; } @@ -563,7 +512,7 @@ export namespace Font{ } private: - void loadFont(FontFlags& params) const{ + void loadFont(FontFace& params) const{ const std::string fontFullName = params.fullname(); if(!params.face)exitLoad(fontFullName); @@ -700,16 +649,16 @@ export namespace Font{ loadedFamily.push_back(flag.familyName); } - flag.internalID |= static_cast(i) << FontFlags::fontOffset; - flag.internalID |= static_cast(dst) << FontFlags::familyOffset; - flag.internalID |= getStyleID(flag.styleName) << FontFlags::styleOffset; + flag.internalID |= static_cast(i) << FontFace::fontOffset; + flag.internalID |= static_cast(dst) << FontFace::familyOffset; + flag.internalID |= getStyleID(flag.styleName) << FontFace::styleOffset; } } public: void load() { //Dose not support hot load! //User Customized fonts to load; - fontLoadListeners.fire(Event::begin); + fontLoadListeners.fire(ext::begin); loadAllFace(); //Init face data; @@ -731,10 +680,10 @@ export namespace Font{ //Now only [loadTargets, mergedMap] matters. All remain operations should be done in the memory; //FontsManager obtain the texture from the total cache; before this no texture should be generated! if(quickInit) { - atlas = std::make_unique(flags); + atlas = std::make_unique(flags); }else { postToHandler([this] { - atlas = std::make_unique(flags); + atlas = std::make_unique(flags); }).get(); } @@ -747,7 +696,7 @@ export namespace Font{ //Release resouces - fontLoadListeners.fire(Event::end); + fontLoadListeners.fire(ext::end); setProgress(1.0f); setDone(); @@ -755,7 +704,7 @@ export namespace Font{ //Load End } - std::unique_ptr getManager() && { + std::unique_ptr getManager() && { return std::move(atlas); } diff --git a/src/arc/asset/font/UnicodeRefParser.cppm b/src/arc/asset/font/UnicodeRefParser.cppm index c7d3a75b..8951d735 100644 --- a/src/arc/asset/font/UnicodeRefParser.cppm +++ b/src/arc/asset/font/UnicodeRefParser.cppm @@ -10,7 +10,7 @@ import OS.File; export namespace Font{ std::vector genRefTable(const OS::File& reference){ - const std::string src = reference.quickRead(); + const std::string src = reference.readString(); std::vector dest{}; dest.reserve(src.size() * 4); diff --git a/src/arc/asset/load/AssetsLoadCore.cppm b/src/arc/asset/load/AssetsLoadCore.cppm new file mode 100644 index 00000000..bdc80321 --- /dev/null +++ b/src/arc/asset/load/AssetsLoadCore.cppm @@ -0,0 +1,283 @@ + +export module Assets.Load.Core; + +export import Assets.Load.State; +import Core.Async.TaskQueue; +import std; +import ext.algorithm; +import ext.TimeMark; + +export namespace Assets::Load{ + using LoadTaskType = void(); + struct LoadTaskHandler; + class LoadManager; + + struct LoadTaskHandler{ + LoadManager* manager{}; + + std::exception_ptr lastException{}; + std::stop_token stopToken{}; + + void join(); + + void setStopToken(std::stop_token&& token){ + stopToken = std::move(token); + } + + void setManager(LoadManager* manager); + + [[nodiscard]] Phase getCurrentPhase() const; + + bool tryJoin(); + + [[nodiscard]] auto getBarriar() const; + + [[nodiscard]] std::future postTask(std::packaged_task&& task) const; + + [[nodiscard]] std::future postTask(std::invocable<> auto&& task) const{ + return postTask(std::packaged_task{std::forward(task)}); + } + + void throwException(const std::exception_ptr& postException) noexcept; + + explicit operator bool() const noexcept{return !stopToken.stop_requested();} + }; + + class LoadTask{ + protected: + float progress{}; + std::launch defaultPolicy = std::launch::async; + LoadTaskHandler handler; + + friend LoadManager; + + public: + LoadEventManager postEventManager{}; + + [[nodiscard]] LoadTask() = default; + + [[nodiscard]] explicit LoadTask(std::launch defaultPolicy) + : defaultPolicy{defaultPolicy}{} + + void tryArriveAndWait() const noexcept; + + virtual ~LoadTask() = default; + + [[nodiscard]] virtual std::future launch(std::launch policy) = 0; + + [[nodiscard]] std::future launch(){ + return launch(defaultPolicy); + } + + [[nodiscard]] bool shouldStop() const noexcept{ + return handler.stopToken.stop_requested(); + } + + [[nodiscard]] float getProgress() const noexcept{return progress;} + + [[nodiscard]] virtual std::string_view getCurrentTaskName() const noexcept {return "";} + + //TODO better done setter + [[nodiscard]] virtual bool isFinished() const noexcept{return handler.stopToken.stop_requested();} + }; + + + class LoadManager{ + friend LoadTaskHandler; + + std::vector handlers{}; + std::unordered_map> tasks{}; + + Core::Async::TaskQueue taskQueue{}; + std::stop_source stopSource{}; + Phase currentPhase{Phase::init}; + static constexpr auto MaxPhaseCount = LoadEventManager::max(); + + void afterArrival() noexcept; + + using BarrierType = std::barrier(nullptr)))>; + std::unique_ptr barrier{}; + + mutable std::mutex handle_mtx{}; + mutable std::condition_variable cv{}; + + ext::StaticTimeStamper<2> timer{}; + static constexpr decltype(timer)::DurTy maxLoadSpacing{330}; + + + mutable std::ostringstream taskNameBuilder{}; + mutable float taskCurrentProgress{}; + + public: + LoadEventManager eventManager; + + void launch(); + + [[nodiscard]] bool isFinished() const noexcept{ + return std::to_underlying(currentPhase) >= MaxPhaseCount; + } + + float getCurrentProgress() const; + + void registerTask(LoadTask& task); + + void processRequests(){ + tryThrowException(); + + taskCurrentProgress = getCurrentProgress(); + if(tasks.empty() || isFinished())return; + + timer.mark(); + while(timer.get() < maxLoadSpacing) { + auto task = taskQueue.pop(); + + if(task){ + task.value()(); + } + + } + } + + void setupBarrier(){ + barrier = std::make_unique(tasks.size(), std::bind_front(&LoadManager::afterArrival, this)); + } + + void requestStop(){ + stopSource.request_stop(); + + while(!isFinished()){ + (void)barrier->arrive(); + } + + std::unique_lock lk{handle_mtx}; + cv.wait(lk, [this]{return handlers.empty();}); + requestDone(); + } + + void requestDone(){ + //TODO set wait time for timeout handle + for (auto& [task, future] : tasks){ + if(future.valid())future.get(); + } + } + + void tryThrowException() const; + + [[nodiscard]] auto& getTaskQueue(){ return taskQueue; } + }; +} + +module : private; + +namespace Assets::Load{ + + auto LoadTaskHandler::getBarriar() const { + if(stopToken.stop_requested() || !manager){ + return static_cast::BarrierType*>(nullptr); + } + return manager->barrier.get(); + } + + void LoadTaskHandler::join(){ + if(!manager)return; + std::unique_lock lk{manager->handle_mtx}; + ext::algo::erase_unique_unstable(manager->handlers, this); + + if(const auto b = getBarriar())b->arrive_and_drop(); + if(manager->handlers.empty())manager->cv.notify_one(); + } + + void LoadTaskHandler::setManager(LoadManager* manager){ + if(!manager)return; + this->manager = manager; + stopToken = manager->stopSource.get_token(); + } + + Phase LoadTaskHandler::getCurrentPhase() const{ + if(manager)return manager->currentPhase; + return Phase::clear; + } + + bool LoadTaskHandler::tryJoin(){ + if(stopToken.stop_requested()){ + join(); + return true; + } + return false; + } + + std::future LoadTaskHandler::postTask(std::packaged_task&& task) const{ + + if(stopToken.stop_requested() || !manager){ + task.operator()(); + return task.get_future(); + }else{ + return manager->getTaskQueue().push(std::move(task)); + } + } + + void LoadTaskHandler::throwException(const std::exception_ptr& postException) noexcept{ + if(stopToken.stop_requested()){ + std::rethrow_exception(postException); + }else{ + lastException = postException; + } + } + + void LoadTask::tryArriveAndWait() const noexcept{ + if(const auto barriar = handler.getBarriar()){ + barriar->arrive_and_wait(); + } + } + + void LoadManager::afterArrival() noexcept{ + if(std::to_underlying(currentPhase) < MaxPhaseCount){ + std::println("Phase {} Reached | {:.1f}% Completed.", std::to_underlying(currentPhase), getCurrentProgress() * 100.f); + std::cout.flush(); + for (const auto task : tasks | std::views::keys){ + task->postEventManager.fire(currentPhase); + } + + eventManager.fire(currentPhase); + + currentPhase = Phase{1 + std::to_underlying(currentPhase)}; + }else{ + stopSource.request_stop(); + } + } + + void LoadManager::launch(){ + std::println("Launch Assets Load | Main Thread: {}", std::this_thread::get_id()); + setupBarrier(); + + for (auto& [task, future] : tasks){ + task->handler.setManager(this); + future = task->launch(); + } + } + + float LoadManager::getCurrentProgress() const{ + float sum{}; + + for(const auto task : tasks | std::views::keys) { + sum += task->getProgress(); + } + + sum /= static_cast(tasks.size()); + + return std::clamp(sum, 0.f, 1.f); + } + + void LoadManager::registerTask(LoadTask& task){ + tasks.try_emplace(&task, std::future{}); + } + + void LoadManager::tryThrowException() const{ + std::scoped_lock lk{handle_mtx}; + for (auto handler : handlers){ + if(handler->lastException){ + std::rethrow_exception(handler->lastException); + } + } + } +} diff --git a/src/arc/asset/load/AssetsLoadState.cppm b/src/arc/asset/load/AssetsLoadState.cppm new file mode 100644 index 00000000..69204069 --- /dev/null +++ b/src/arc/asset/load/AssetsLoadState.cppm @@ -0,0 +1,29 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Assets.Load.State; + +import Core.Async.TaskQueue; +import ext.Event; +import std; + +export namespace Assets::Load{ + enum struct Phase : unsigned{ + init, //Task init + pull, //Pull & Process Request + load, //Texture & Audio Load + post_load, //Load Done - Post Process + end, //All Load Done + clear //Load Resource Clear + }; + + using LoadEventManager = ext::SignalManager; + + enum struct State{ + running, + blocked, + finished, + interrupted + }; +} \ No newline at end of file diff --git a/src/arc/asset/load/FontLoader.cppm b/src/arc/asset/load/FontLoader.cppm new file mode 100644 index 00000000..968f639e --- /dev/null +++ b/src/arc/asset/load/FontLoader.cppm @@ -0,0 +1,176 @@ +module; + +#include +#include + +export module Assets.Load.FontLoader; + +export import Font; +import Assets.Load.Core; +import Assets.TexturePage; + +import OS.File; +import ext.RuntimeException; +import ext.Heterogeneous; + +import Assets.Load.TexturePacker; + +export namespace Assets::Load{ + class FontLoader : public LoadTask{ + protected: + bool quickInit = false; + std::vector> flags{}; + // std::unique_ptr atlas{nullptr}; + Assets::TexturePage* bindedPage{nullptr}; + + static void exitLoad(std::string_view fontName) { + throw ext::RuntimeException{std::format("Font load failed during {}.", fontName)}; + } + + void processCustomeData(Font::FontFace& currentFace, Font::PreloadDataSet& fontDatas){ + for(auto& customeData : currentFace.customeCharDatas){ + if(!customeData.forceOverride && fontDatas.contains(customeData.code))continue; + + if(customeData.hasCopyTarget()){ + if(auto itr = fontDatas.find(customeData.copyTarget); itr != fontDatas.end()){ + customeData.glyphMatrices = itr->glyphMatrices; + } + } + + if(customeData.dataModifier){ + customeData.dataModifier(customeData); + } + + fontDatas.emplace(customeData.glyphMatrices, customeData.code, std::move(customeData.data)); + } + } + + void loadFont(Font::FontFace& currentFace) const{ + const std::string fontFullName = currentFace.fullname(); + + if(!currentFace.face)exitLoad(fontFullName); + + Font::PreloadDataSet fontDatas{}; + + // Graphic::Pixmap maxMap{}; + FT_Set_Pixel_Sizes(currentFace.face, 0, currentFace.size); + + for (const Font::CharCode i : currentFace.segments){ + if(const FT_Face face = currentFace.tryLoad(i)){ // NOLINT(*-misplaced-const) + if(!face->glyph) { + exitLoad(fontFullName); + } + + //TODO glyph effects + //TODO should this thing be here??? + if (currentFace.loader && face->glyph->format != FT_GLYPH_FORMAT_BITMAP) { + if (currentFace.loader(face)) { + exitLoad(fontFullName); + } + } + + fontDatas.emplace(i, face->glyph); + } + } + + processCustomeData(currentFace, fontDatas); + + for (auto& fontData : fontDatas){ + fontData.charData.region = bindedPage->pullRequest(fontFullName + std::to_string(fontData.charCode), std::move(fontData.pixmap), [](const Assets::BitmapLoadData& data){data.bitmapData.flipY();}); + } + + currentFace.data = std::make_unique(fontDatas); + } + + void loadAllFace() const { + for(const auto& params: flags){ + if(!params->face) { + if(FT_New_Face(Font::GlobalFreeTypeLib, params->fontFile.absolutePath().string().data(), 0, ¶ms->face)) { + exitLoad(params->fullname()); + } + //Correct the style name if necessary + params->familyName = params->face->family_name; + params->styleName = params->face->style_name; + } + } + } + + void assignID() const { + std::vector loadedFamily; + for(const auto& [index, face] : flags | std::ranges::views::enumerate) { + // ReSharper disable once CppUseStructuredBinding + + const auto itr = std::ranges::find(loadedFamily, face->familyName); + const auto dst = std::ranges::distance(loadedFamily.cbegin(), itr); + + if(itr == loadedFamily.end()){ + loadedFamily.push_back(face->familyName); + } + + face->internalID |= static_cast(index) << Font::FontFace::fontOffset; + face->internalID |= static_cast(dst) << Font::FontFace::familyOffset; + face->internalID |= Font::getStyleID(face->styleName) << Font::FontFace::styleOffset; + } + } + + //This virtual is not necessary, just fuck the IDE's warnings + virtual void load() { //Dose not support hot load! + Font::loadLib(); + + loadAllFace(); + + //Cache prepare + for(const auto& params : flags) { + loadFont(*params); + } + + assignID(); + + handler.join(); + } + + public: + [[nodiscard]] FontLoader() = default; + + FontLoader(const FontLoader& other) = delete; + + FontLoader(FontLoader&& other) noexcept = delete; + + FontLoader& operator=(const FontLoader& other) = delete; + + FontLoader& operator=(FontLoader&& other) noexcept = delete; + + [[nodiscard]] constexpr Assets::TexturePage* getBindedPage() const noexcept{ return bindedPage; } + + constexpr void setBindedPage(Assets::TexturePage* const bindedPage) noexcept{ this->bindedPage = bindedPage; } + + Font::FontFace* registerFont(Font::FontFace* fontFlags) { + flags.emplace_back(fontFlags); + return fontFlags; + } + + std::future launch(std::launch policy) override{ + return std::async(policy, &FontLoader::load, this); + } + }; + + class QuickInitFontLoader : public FontLoader{ + public: + [[nodiscard]] QuickInitFontLoader() = default; + + // ReSharper disable once CppHidingFunction + void load() override{ + LoadManager instantManager{}; + TexturePacker instantPacker{}; + instantPacker.pushPage(bindedPage); + instantManager.registerTask(instantPacker); + instantManager.registerTask(*this); + instantManager.launch(); + while(!instantManager.isFinished()){ + instantManager.processRequests(); + } + + instantManager.requestDone(); + } + }; +} diff --git a/src/arc/asset/load/MiscLoader.cppm b/src/arc/asset/load/MiscLoader.cppm new file mode 100644 index 00000000..56b8a31c --- /dev/null +++ b/src/arc/asset/load/MiscLoader.cppm @@ -0,0 +1,90 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Assets.Load.Misc; + +import Assets.Load.Core; +import std; +import ext.Concepts; + +export namespace Assets::Load{ + template + // using FuncTy = void(); + class MiscTaskManager : public LoadTask{ + using RetTy = typename Concepts::FunctionTraits::ReturnType; + + struct MiscTask{ + Phase phase{}; + std::move_only_function func{}; + std::optional> promise{}; + + [[nodiscard]] MiscTask(Phase phase, std::move_only_function&& func, + std::optional>&& promise = std::nullopt) + : phase{phase}, + func{std::move(func)}, + promise{std::move(promise)}{} + }; + + std::vector tasks{}; + + void execCurrentPhase(){ + for (auto& [phase, func, promise] : tasks){ + if(handler.getCurrentPhase() == phase){ + try{ + if constexpr (std::is_void_v){ + func(); + if(promise)promise->set_value(); + }else{ + auto rst = func(); + if(promise)promise->set_value(std::move(rst)); + } + }catch(...){ + handler.throwException(std::current_exception()); + } + + progress += 1 / static_cast(tasks.size()); + } + } + } + + void load(){ + while(!handler.stopToken.stop_requested() && !tasks.empty()){ + execCurrentPhase(); + if(auto* barriar = handler.getBarriar()){ + barriar->arrive_and_wait(); + } + } + + progress = 1.f; + handler.join(); + } + + public: + void push(std::move_only_function&& function){ + tasks.emplace_back(Phase::clear, std::move(function)); + } + + void push(const Phase requestedPhase, std::move_only_function&& function){ + tasks.emplace_back(requestedPhase, std::move(function)); + } + + [[nodiscard]] std::future pushAndGet(const Phase requestedPhase, std::move_only_function&& function){ + auto promise = std::promise{}; + + auto future = promise.get_future(); + + tasks.emplace_back(requestedPhase, std::move(function), std::move(promise)); + + return future; + } + + [[nodiscard]] std::future launch(std::launch policy) override{ + return std::async(policy, &MiscTaskManager::load, this); + } + + [[nodiscard]] std::string_view getCurrentTaskName() const noexcept override{ + return "Misc Load Tasks"; + } + }; +} diff --git a/src/arc/asset/load/TexturePacker.cppm b/src/arc/asset/load/TexturePacker.cppm new file mode 100644 index 00000000..3c34466c --- /dev/null +++ b/src/arc/asset/load/TexturePacker.cppm @@ -0,0 +1,478 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Assets.Load.TexturePacker; +import std; + +import Assets.Load.Core; +import Assets.TexturePage; + +import Graphic.Pixmap; +import OS.File; + +import Geom.Vector2D; +import Geom.Rect_Orthogonal; + +import GL.Texture.Texture2D; +import GL.Texture.TextureRegion; + +import ext.json.io; +import ext.Json; +import ext.Heterogeneous; +import Core.IO.Specialized; + +import GL; + +import Math; +import Math.Algo.TopologicalSort; +import Math.Algo.StripPacker2D; + +export namespace Assets::Load{ + /** + * @brief + * PackData Json Structure: + * @code + *{ + * "" : { + * "version" : xx + * "data" : { + * "" : { + * //Pack Data + * } + * } + * } + *} + * + * @endcode + * + */ + + struct PackData{ + Graphic::Pixmap bitmapData{}; + + Geom::OrthoRectInt region{}; + std::size_t pageID{}; + + void copyPositionDataFrom(const PackData& other){ + region = other.region; + pageID = other.pageID; + } + }; + + struct PackRawData{ + std::vector fragments{}; + std::size_t pageID{}; + + Graphic::Pixmap atlas{}; + + [[nodiscard]] PackRawData(std::vector&& datas, + const Geom::Point2 s, const std::size_t id) + : fragments(std::move(datas)), pageID(id) { + atlas.create(Math::max(s.x, 2), Math::max(s.y, 2)); + } + + [[nodiscard]] PackRawData() = default; + }; + + struct PageData{ + TexturePage* page{}; + int margin{0}; + OS::File cacheDir{}; + + LoadTaskHandler handler{}; + + ext::StringHashMap fragments{}; + + const ext::StringHashMap* allPages{}; + + std::vector rawAtlases{}; + Geom::OrthoRectInt maxBound{GL::getMaxTextureSize(), GL::getMaxTextureSize()}; + bool fromCache = false; + + [[nodiscard]] OS::File getDataFile() const{ + return cacheDir.subFile(std::format("{}-data.json", page->name)); + } + + [[nodiscard]] OS::File getImageFile(std::size_t index) const{ + return cacheDir.subFile(std::format("{}-{}.png", page->name, index)); + } + + ext::json::JsonValue pageJsonValue{}; + + [[nodiscard]] TexturePage* getDependency() const noexcept{ + return page->dependency; + } + + void load(){ + readCache(); + if(isCacheValid()){ + + if(!isCacheValid_Post()){ + loadFromPull(); + }else{ + loadFromCache(); + } + }else{ + loadFromPull(); + } + } + + void loadFromCache(){ + for(const auto& [index, element] : rawAtlases | std::views::enumerate){ + auto file = getImageFile(index); + element.atlas.loadFrom(file); + element.pageID = index; + } + } + + void loadFromPull(){ + if(page->dependency){ + loadFromFetch(); + }else{ + pack(); + } + + mergeTexture(); + } + + void loadFromFetch(){ + auto& targetLoadData = allPages->at(page->dependency->name); + auto& target = targetLoadData.fragments; + for (auto& [name, frag] : fragments){ + if(auto itr = target.find(name); itr != target.end()){ + frag.copyPositionDataFrom(itr->second); + } + } + + auto all = fragments | std::views::values | std::views::transform([](auto& i){return std::addressof(i);}) | std::ranges::to>(); + + for (const auto& raw : targetLoadData.rawAtlases){ + decltype(all) currentFragments{}; + auto rst = std::ranges::remove_if(all, std::bind_front(std::equal_to{}, raw.pageID), &PackData::pageID); + currentFragments.assign_range(rst); + all.erase(rst.begin(), rst.end()); + + rawAtlases.emplace_back(std::move(currentFragments), raw.atlas.size2D(), raw.pageID); + } + } + + void applyTextureToRegion(){ + for (auto& raw : rawAtlases){ + page->textures.emplace_back( + std::make_unique(raw.atlas.getWidth(), raw.atlas.getHeight(), std::move(raw.atlas).data(), false)); + } + + auto fut = handler.postTask([this]{ + for (const auto& [index, texture2D] : page->textures | std::ranges::views::enumerate){ + std::println("[load] {}-{} Try obtain GL texture at thread: {}", page->name, index, std::this_thread::get_id()); + // texture2D->init(); + } + }); + + for (const auto& [name, fragment] : fragments){ + auto& textureRegion = page->textureRegions[name]; + textureRegion.data = page->textures.at(fragment.pageID).get(); + textureRegion.fetchIntoCurrent(fragment.region); + } + + fut.get(); + } + + void loadPulledData(){ + for (const auto& bitmapLoadData : page->toLoad | std::views::values){ + bitmapLoadData->load(); + } + + fragments.reserve(page->toLoad.size()); + for (auto& [name, data] : page->toLoad){ + auto size = data->bitmapData.size2D(); + fragments.try_emplace(name, PackData{std::move(data->bitmapData), Geom::OrthoRectInt{size}}); + } + } + + [[nodiscard]] constexpr std::size_t getPageSize() const noexcept{ + return rawAtlases.size(); + } + /** + * \brief After this function, modifies to pixmaps wont work anymore. + */ + void pack() { + rawAtlases.clear(); + auto all = fragments | std::views::values | std::views::transform([](auto& i){return std::addressof(i);}) | std::ranges::to>(); + + loadRemains(std::move(all), 0); + } + + void loadRemains(Concepts::Iterable auto&& remains, const int currentPage) { + if(remains.empty())return; + // setProgress(TotalProgressWeight, 2, 1, packData.size() - remains.size(), packData.size()); + Math::StripPacker2D packer{remains}; + + packer.setMaxSize(maxBound.getWidth(), maxBound.getHeight()); + packer.setMargin(margin); + packer.process(); + auto size = packer.getResultSize(); + + //Move it + for (const auto packed : packer.packed){ + packed->pageID = currentPage; + } + + rawAtlases.emplace_back(std::move(packer.packed), size, currentPage); + loadRemains(std::move(packer.getRemains()), currentPage + 1); + } + + void mergeTexture() { + for(auto& mergeGroup : rawAtlases) { + Graphic::Pixmap& mergedMap = mergeGroup.atlas; + + for(const auto& data : mergeGroup.fragments) { + mergedMap.set(data->bitmapData, data->region.getSrcX(), data->region.getSrcY()); + } + + mergeGroup.fragments.clear(); + } + } + + void readCache(){ + auto data = getDataFile(); + if(data.exist()){ + pageJsonValue = ext::json::parse(data.readString_byLine()); + } + } + + + /** + * @brief + * @return false if failed. + */ + bool isCacheValid_Post(){ + ext::json::JsonSrlContBase_string_map::read(pageJsonValue.asObject().at(ext::json::keys::Value), fragments); + + if(std::ranges::any_of(fragments | std::ranges::views::values, std::bind_back(std::greater{}, rawAtlases.size()), &PackData::pageID)){ + return false; + } + + fromCache = true; + return true; + } + + void saveCache(){ + if(fromCache)return; + pageJsonValue.asObject(); + + for (const auto& [index, page] : rawAtlases | std::views::enumerate){ + page.atlas.write(getImageFile(index), true); + } + + pageJsonValue.append(ext::json::keys::Count, static_cast(rawAtlases.size())); + pageJsonValue.append(ext::json::keys::Version, static_cast(page->version)); + ext::json::JsonSrlContBase_string_map::write(pageJsonValue.asObject()[ext::json::keys::Value], fragments); + + std::ofstream stream{getDataFile().getPath()}; + stream << pageJsonValue; + } + + //TODO load by cache + bool isCacheValid(){ + auto& rootMap = pageJsonValue.asObject(); + + if(const auto version = rootMap.tryFind(ext::json::keys::Version)){ + if(version->getOr(-1) != page->version)return false; + }else return false; + + unsigned pageCount = ~0u; + if(const auto page = rootMap.tryFind(ext::json::keys::Count)){ + pageCount = page->getOr(-1); + + for(unsigned i = 0; i < pageCount; ++i){ + if(!getImageFile(i).exist())return false; + } + }else return false; + + rawAtlases.resize(pageCount); + + const auto datas = rootMap.tryFind(ext::json::keys::Value); + if(!datas)return false; + + auto& valueMap = datas->asObject(); + + if(valueMap.size() != page->toLoad.size()){ + return false; + } + + ext::StringHashSet<> cachedNames{valueMap.size()}; + + for (auto& value : valueMap | std::views::keys){ + cachedNames.insert(value); + } + + for(const auto& name : page->toLoad | std::views::keys){ + if(const auto itr = cachedNames.find(name); itr != cachedNames.end()){ + cachedNames.erase(itr); + }else{ + return false; + } + } + + if(!cachedNames.empty())return false; + + + + return true; + } + + }; + + class TexturePacker : public LoadTask{ + static constexpr int Load_PullWeight = 20; + static constexpr int Load_MergeWeight = 50; + + int margin = 0; + + ext::StringHashMap pagesToPack{}; + + Geom::OrthoRectInt texMaxBound{0, 0, 2048, 2048}; + + ext::json::JsonValue packDataJson{}; + OS::File cacheDir{}; + + void execCurrentPhase(){ + switch(handler.getCurrentPhase()){ + case Phase::init: + for (auto& pageData : pagesToPack | std::views::values){ + pageData.allPages = &pagesToPack; + } + texMaxBound.setSize(GL::getMaxTextureSize(), GL::getMaxTextureSize()); + break; + case Phase::pull: + for (auto& pageData : pagesToPack | std::views::values){ + pageData.loadPulledData(); + } + break; + case Phase::load:{ + loadPages(); + + std::vector> cacheTasks{}; + + for (auto& pageData : pagesToPack | std::views::values){ + cacheTasks.push_back(std::async(&PageData::saveCache, pageData)); + } + + try{ + std::ranges::for_each(cacheTasks, &std::future::get); + }catch(...){ + handler.throwException(std::current_exception()); + } + + for (auto& pageData : pagesToPack | std::views::values){ + pageData.applyTextureToRegion(); + } + + break; + } + case Phase::post_load:{ + break; + } + case Phase::end: + break; + case Phase::clear: + pagesToPack.clear(); + break; + } + } + + void load(){ + for (auto && toPack : pagesToPack | std::views::values){ + toPack.handler = handler; + } + while(!handler.stopToken.stop_requested()){ + execCurrentPhase(); + tryArriveAndWait(); + } + + progress = 1.f; + handler.join(); + } + + void loadPages(){ //TODO optm + const auto pages = pagesToPack | std::views::values | std::views::transform(&PageData::page) | std::ranges::to>(); + auto depthMap = Math::get_topological_depth_map(pages, &TexturePage::dependency); + + std::deque> loadSections(depthMap.size()); + + for (const auto& [page, depth] : depthMap){ + loadSections[depth].push_back(&pagesToPack.at(page->name)); + } + + std::vector> tasksFuture{}; + + while(!loadSections.empty()){ + auto top = std::move(loadSections.back()); + loadSections.pop_back(); + if(top.empty())continue; + tasksFuture.clear(); + + for (const auto pageData : top){ + tasksFuture.push_back(std::async(&PageData::load, pageData)); + } + + + for (auto& future : tasksFuture){ + try{ + future.get(); + }catch(...){ + handler.throwException(std::current_exception()); + } + } + } + } + + // void loadCacheData_pre(const TexturePage* page){ + // auto& rootMap = packDataJson.asObject(); + // + // const OS::File pageDataFile = getDataFileOf(page); + // + // if(!pageDataFile.exist())return; + // + // auto json = ext::json::parse(pageDataFile.quickRead()); + // + // rootMap.insert_or_assign(page->name, std::move(json)); + // } + + public: + [[nodiscard]] TexturePacker() = default; + + TexturePacker(const TexturePacker& other) = delete; + + TexturePacker& operator=(const TexturePacker& other) = delete; + + [[nodiscard]] const OS::File& getCacheDir() const noexcept{ return cacheDir; } + + void setCacheDir(const OS::File& cacheDir){ this->cacheDir = cacheDir; } + + [[nodiscard]] std::future launch(const std::launch policy) override{ + return std::async(policy, &TexturePacker::load, this); + } + + void pushPage(TexturePage* page, const int margin = 1){ + pagesToPack.insert_or_assign(page->name, PageData{page, margin, cacheDir}); + } + + + }; +} + +export template<> + struct ::ext::json::JsonSerializator{ + static void write(ext::json::JsonValue& jsonValue, const Assets::Load::PackData& data){ + ext::json::append(jsonValue, "region", data.region); + ext::json::append(jsonValue, "page", data.pageID); + } + + static void read(const ext::json::JsonValue& jsonValue, Assets::Load::PackData& data){ + ext::json::read(jsonValue, "region", data.region, {}); + ext::json::read(jsonValue, "page", data.pageID, 0ull); + } + }; \ No newline at end of file diff --git a/src/arc/asset/load/TexturePage.cppm b/src/arc/asset/load/TexturePage.cppm new file mode 100644 index 00000000..080c0d50 --- /dev/null +++ b/src/arc/asset/load/TexturePage.cppm @@ -0,0 +1,158 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Assets.TexturePage; + +import std; +import GL.Texture.Texture2D; +import GL.Texture.TextureRegion; +import ext.Heterogeneous; + +import Graphic.Pixmap; +import Geom.Vector2D; +import Geom.Rect_Orthogonal; +import GL.Texture.Texture2D; +import GL.Texture.TextureNineRegion; + +import Image; +import Image.Svg; + +import OS.File; + +export namespace Assets{ + namespace Load{ + class TexturePacker; + struct PageDataLoader; + } + + struct BitmapLoadData{ + virtual ~BitmapLoadData() = default; + + std::string stemName{}; + + //TODO is this name necessary? + std::string name{}; + Graphic::Pixmap bitmapData{}; + + + std::function modifier{}; + + [[nodiscard]] BitmapLoadData() = default; + + [[nodiscard]] BitmapLoadData(std::string_view familyName, std::string_view name) + : stemName{familyName}, + name{name}{} + + [[nodiscard]] explicit BitmapLoadData(std::string_view familyName) + : stemName{familyName}{} + + [[nodiscard]] BitmapLoadData(const std::string_view familyName, const Graphic::Pixmap& bitmapData, std::function&& modifier = nullptr) + : stemName{familyName}, + bitmapData{bitmapData}, modifier{std::move(modifier)}{} + + [[nodiscard]] BitmapLoadData(const std::string_view familyName, Graphic::Pixmap&& bitmapData, std::function&& modifier = nullptr) + : stemName{familyName}, + bitmapData{std::move(bitmapData)}, modifier{std::move(modifier)}{} + + virtual void load(){ + if(modifier)modifier(*this); + } + + [[nodiscard]] constexpr bool valid() const noexcept{ + return bitmapData.valid(); + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept{ + return valid(); + } + }; + + struct FileImportData : BitmapLoadData{ + OS::File srcFile{}; + + using BitmapLoadData::BitmapLoadData; + + [[nodiscard]] FileImportData(std::string_view familyName, const OS::File& srcFile) + : BitmapLoadData{familyName, srcFile.filename()}, + srcFile{srcFile}{} + + [[nodiscard]] explicit FileImportData(const OS::File& srcFile) + : BitmapLoadData{srcFile.filename(), srcFile.filename()}, + srcFile{srcFile}{} + + void load() override{ + //TODO more branch handle + if(const auto extension = srcFile.extension(); extension == ".svg"){ + bitmapData = ext::svgToBitmap(srcFile); + }else{ + bitmapData.loadFrom(srcFile); + } + + BitmapLoadData::load(); + } + }; + + struct SvgFileImportData : FileImportData{ + Geom::Point2U size{}; + bool mulWhite{}; + + using FileImportData::FileImportData; + + [[nodiscard]] SvgFileImportData(std::string_view familyName, const OS::File& srcFile, const Geom::Point2U& size, + const bool mulWhite = false) + : FileImportData{familyName, srcFile}, + size{size}, + mulWhite{mulWhite}{} + + [[nodiscard]] SvgFileImportData(const OS::File& srcFile, const Geom::Point2U& size, + const bool mulWhite = false) + : FileImportData{srcFile}, + size{size}, + mulWhite{mulWhite}{} + + void load() override{ + bitmapData = ext::svgToBitmap(srcFile, size.x, size.y); + if(mulWhite)bitmapData.mulWhite(); + + BitmapLoadData::load(); + } + }; + + struct TexturePage{ + std::string name{}; + std::vector> textures{}; + ext::StringMap textureRegions{}; + unsigned version{}; + + TexturePage* dependency; + + ext::StringHashMap> toLoad{}; + + friend Load::TexturePacker; + friend Load::PageDataLoader; + public: + + template T, typename ...Args> + GL::TextureRegion* pullRequest(std::string_view name, Args&& ...args){ + toLoad.try_emplace(std::string(name), std::make_unique(name, std::forward(args) ...)); + auto [rst, success] = textureRegions.try_emplace(std::string(name), GL::TextureRegion{}); + + if(success){ + return &rst->second; + } + + return nullptr; + } + + auto& getRegions() noexcept { + return textureRegions; + } + + [[nodiscard]] constexpr std::size_t getSize() const noexcept{ + return textures.size(); + } + + [[nodiscard]] constexpr bool hasDependency() const noexcept{return dependency != nullptr;} + }; +} diff --git a/src/arc/async/AsyncHandle.cppm b/src/arc/async/AsyncHandle.cppm new file mode 100644 index 00000000..a7da68c1 --- /dev/null +++ b/src/arc/async/AsyncHandle.cppm @@ -0,0 +1,22 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Core.Async.Handle; + +import std; + +export namespace Core::Async{ + struct Handle{ + [[nodiscard]] std::future postTask(std::packaged_task&& task) const{ + task.operator()(); + return task.get_future(); + } + + void throwException(const std::exception_ptr& postException) const noexcept{ + std::rethrow_exception(postException); + } + + explicit operator bool() const noexcept{return true;} + }; +} diff --git a/src/arc/async/AsyncManager.cppm b/src/arc/async/AsyncManager.cppm new file mode 100644 index 00000000..d8883e8d --- /dev/null +++ b/src/arc/async/AsyncManager.cppm @@ -0,0 +1,35 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Core.Async.Manager; + +import Core.Async.TaskQueue; +import std; + +export namespace Core::Async{ + struct AsyncTask{ + virtual ~AsyncTask() = default; + + [[nodiscard]] virtual std::future launch(std::launch policy) = 0; + + [[nodiscard]] std::future launch(){ + return launch(std::launch::async); + } + + [[nodiscard]] virtual float getProgress() const noexcept = 0; + + [[nodiscard]] virtual std::string_view getTaskName() const noexcept {return "";} + + + }; + struct AsyncHandler; + class AsyncManager{ + TaskQueue taskQueue{}; + std::vector handlers{}; + + friend AsyncManager; + // void + }; + +} diff --git a/src/arc/async/TaskQueue.cppm b/src/arc/async/TaskQueue.cppm new file mode 100644 index 00000000..bf3135e0 --- /dev/null +++ b/src/arc/async/TaskQueue.cppm @@ -0,0 +1,56 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Core.Async.TaskQueue; +import std; +import ext.Concepts; + +export namespace Core::Async{ + template + class TaskQueue{ + public: + using TaskTy = std::packaged_task; + using RstTy = typename Concepts::FunctionTraits::ReturnType; + + private: + std::queue> tasks{}; + std::mutex mtx{}; + + public: + [[nodiscard]] std::future push(TaskTy&& task){ + std::future future = task.get_future(); + + { + std::scoped_lock lk{mtx}; + tasks.push(std::move(task)); + } + + return future; + } + + [[nodiscard]] std::optional pop() noexcept{ + std::scoped_lock lk{mtx}; + + if(tasks.empty())return std::nullopt; + + auto func = std::move(tasks.front()); + tasks.pop(); + return std::move(func); + } + + [[nodiscard]] bool empty() const noexcept{ + std::scoped_lock lk{mtx}; + return tasks.empty(); + } + + [[nodiscard]] auto size() const noexcept{ + std::scoped_lock lk{mtx}; + return tasks.size(); + } + + explicit operator bool() const noexcept{ + return !empty(); + } + }; +} diff --git a/src/arc/graphic/Pixmap.cppm b/src/arc/graphic/Pixmap.cppm index fa30f6d5..ecba98f8 100644 --- a/src/arc/graphic/Pixmap.cppm +++ b/src/arc/graphic/Pixmap.cppm @@ -42,7 +42,7 @@ export namespace Graphic{ return height; } - [[nodiscard]] Geom::Point2 getSize() const{ + [[nodiscard]] Geom::Point2 size2D() const{ return {width, height}; } @@ -125,7 +125,7 @@ export namespace Graphic{ return *this; } - [[nodiscard]] bool valid() const { + [[nodiscard]] constexpr bool valid() const noexcept{ return bitmapData != nullptr && pixelSize() > 0; } @@ -142,7 +142,7 @@ export namespace Graphic{ void free() { width = height = 0; - if(bitmapData)bitmapData.reset(nullptr); + bitmapData.reset(nullptr); } void fill(const Color color) const{ diff --git a/src/arc/graphic/Renderer.cppm b/src/arc/graphic/Renderer.cppm index 2300cff5..ea9771ef 100644 --- a/src/arc/graphic/Renderer.cppm +++ b/src/arc/graphic/Renderer.cppm @@ -8,7 +8,7 @@ import std; import GL.Buffer.FrameBuffer; import GL; import GL.Shader; -import Event; +import ext.Event; import Graphic.Color; import Graphic.Resizeable; import Graphic.PostProcessor; @@ -24,8 +24,8 @@ export namespace Core{ class Renderer; } -export namespace Event{ - struct DrawEvent : Event::EventType{ +export namespace ext{ + struct DrawEvent : ext::EventType{ ::Core::Renderer* const renderer; [[nodiscard]] explicit DrawEvent(::Core::Renderer* const renderer) @@ -63,17 +63,17 @@ export namespace Core{ std::vector synchronizedSizedObjects{}; int width{200}, height{200}; - Event::EventManager drawControlHook{ - Event::indexOf(), - Event::indexOf(), - Event::indexOf(), - Event::indexOf() + ext::EventManager drawControlHook{ + ext::indexOf(), + ext::indexOf(), + ext::indexOf(), + ext::indexOf() }; - Event::Draw_Post draw_post{this}; - Event::Draw_Prepare draw_prepare{this}; - Event::Draw_After draw_after{this}; - Event::Draw_Overlay draw_overlay{this}; + ext::Draw_Post draw_post{this}; + ext::Draw_Prepare draw_prepare{this}; + ext::Draw_After draw_after{this}; + ext::Draw_Overlay draw_overlay{this}; public: GL::FrameBuffer defaultFrameBuffer{}; @@ -98,7 +98,7 @@ export namespace Core{ Renderer::resize(w, h); } - Event::EventManager& getListener(){ + ext::EventManager& getListener(){ return drawControlHook; } diff --git a/src/arc/graphic/TextureAtlas.cppm b/src/arc/graphic/TextureAtlas.cppm index f63af095..6a87f7ec 100644 --- a/src/arc/graphic/TextureAtlas.cppm +++ b/src/arc/graphic/TextureAtlas.cppm @@ -30,9 +30,9 @@ export namespace Graphic { class TextureAtlas { protected: std::unordered_map pages{}; - ext::StringMap regions{}; + ext::StringHashMap regions{}; - ext::StringMap> textureGroups{}; + ext::StringHashMap> textureGroups{}; const GL::TextureRegion* fallbackTextureRegion{nullptr}; @@ -191,7 +191,7 @@ export namespace Graphic { return pages; } - [[nodiscard]] ext::StringMap& getRegions(){ + [[nodiscard]] ext::StringHashMap& getRegions(){ return regions; } diff --git a/src/arc/graphic/draw/Draw_Base.cppm b/src/arc/graphic/draw/Draw_Base.cppm index 103cf48e..cdc90001 100644 --- a/src/arc/graphic/draw/Draw_Base.cppm +++ b/src/arc/graphic/draw/Draw_Base.cppm @@ -249,7 +249,7 @@ export namespace Graphic{ static void rectPoint(const Geom::Vec2 pos, const float size){ - Fill::rectOrtho(ProvTy::contextTexture, pos.x - size * 0.5f, pos.y - size * 0.5f, size, size); + Fill::rectOrtho(*ProvTy::contextTexture, pos.x - size * 0.5f, pos.y - size * 0.5f, size, size); } diff --git a/src/arc/graphic/gl/GL.cppm b/src/arc/graphic/gl/GL.cppm index e6eb4d0a..c94aa0a1 100644 --- a/src/arc/graphic/gl/GL.cppm +++ b/src/arc/graphic/gl/GL.cppm @@ -135,7 +135,7 @@ export namespace GL { INVERT = GL_INVERT, }; - int getMaxTextureSize() { + int getMaxTextureSize() noexcept{ return maxTexSize; } diff --git a/src/arc/graphic/gl/shader/ShaderSource.cppm b/src/arc/graphic/gl/shader/ShaderSource.cppm index feb177cf..26273e85 100644 --- a/src/arc/graphic/gl/shader/ShaderSource.cppm +++ b/src/arc/graphic/gl/shader/ShaderSource.cppm @@ -86,12 +86,12 @@ export namespace GL { }; protected: - ext::StringMap uniformInfoMap{}; + ext::StringHashMap uniformInfoMap{}; GLuint programID = 0; std::function uniformSetter = [](const ShaderProgram&){}; public: - [[nodiscard]] ext::StringMap& getUniformInfoMap() noexcept{ return uniformInfoMap; } + [[nodiscard]] ext::StringHashMap& getUniformInfoMap() noexcept{ return uniformInfoMap; } [[nodiscard]] GLuint getProgramId() const noexcept{ return programID; } @@ -381,7 +381,7 @@ export namespace GL { void readSource() { for(auto& [file, source] : typeList | std::views::values) { - source = shaderDir.find(file).quickRead(); + source = shaderDir.find(file).readString(); } } diff --git a/src/arc/graphic/gl/texture/Texture2D.cppm b/src/arc/graphic/gl/texture/Texture2D.cppm index 8a47e26e..57143170 100644 --- a/src/arc/graphic/gl/texture/Texture2D.cppm +++ b/src/arc/graphic/gl/texture/Texture2D.cppm @@ -45,12 +45,15 @@ export namespace GL{ * \param h * \param data */ - Texture2D(const int w, const int h, unsigned char*&& data) : Texture(GL_TEXTURE_2D, w, h){ - init(std::forward(data)); + Texture2D(const int w, const int h, const unsigned char* data, const bool initInstantly = true) : Texture(GL_TEXTURE_2D, w, h){ + if(initInstantly)init(data); } - Texture2D(const int w, const int h, std::unique_ptr&& data) : - Texture2D(w, h, data.release()) {} + Texture2D(const int w, const int h, std::unique_ptr&& data, const bool initInstantly = true) : Texture(GL_TEXTURE_2D, w, h), localData{std::move(data)}{ + if(initInstantly){ + init(); + } + } Texture2D(const int w, const int h) : Texture2D(w, h, nullptr) { @@ -118,14 +121,11 @@ export namespace GL{ * \brief The data will be released after init, uses rv-ref to pass the pointer * \param data */ - void init(unsigned char*&& data){ - if(data != localData.get())localData.reset(data); - + void init(const unsigned char* data){ glCreateTextures(targetFlag, 1, &nameID); - glTextureStorage2D(nameID, MipMapGeneralLevel, GL_RGBA8, - static_cast(width), static_cast(height)); + glTextureStorage2D(nameID, MipMapGeneralLevel, GL_RGBA8, width, height); - if(localData)glTextureSubImage2D(nameID, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, localData.get()); + if(data)glTextureSubImage2D(nameID, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); //TODO : Check if needed here. setWrap(); glGenerateTextureMipmap(nameID); diff --git a/src/arc/graphic/gl/texture/TextureRegion.cppm b/src/arc/graphic/gl/texture/TextureRegion.cppm index 79ca940c..a4bffc36 100644 --- a/src/arc/graphic/gl/texture/TextureRegion.cppm +++ b/src/arc/graphic/gl/texture/TextureRegion.cppm @@ -78,6 +78,13 @@ export namespace GL{ //Below functions work properly only on rect regions + constexpr void copyPositionDataFrom(const TextureRegion& other) noexcept{ + v00 = other.v00; + v10 = other.v10; + v01 = other.v01; + v11 = other.v11; + } + [[nodiscard]] float getSrcX() const { return v00.x * data->getWidth(); } diff --git a/src/arc/io/File.cppm b/src/arc/io/File.cppm index 2ab4212e..ceff26f2 100644 --- a/src/arc/io/File.cppm +++ b/src/arc/io/File.cppm @@ -250,14 +250,14 @@ namespace OS{ return files; } - void forSubs(Concepts::Invokable auto&& consumer) const{ + void forSubs(std::invocable auto&& consumer) const{ for(const auto& item : fs::directory_iterator(getPath())){ consumer(File{item}); } } template - void forAllSubs(Concepts::Invokable auto&& consumer) const{ + void forAllSubs(std::invocable auto&& consumer) const{ for(const auto& item : fs::recursive_directory_iterator(getPath())){ if(File f{item}; f.isRegular()){ consumer(std::move(f)); @@ -283,7 +283,7 @@ namespace OS{ } template Consumer = std::nullptr_t> - [[nodiscard]] std::string readString(Consumer&& consumer = nullptr) const{ + [[nodiscard]] std::string readString_byLine(Consumer&& consumer = nullptr) const{ std::ifstream file_stream(getPath()); if(!file_stream.is_open()) return ""; @@ -305,7 +305,7 @@ namespace OS{ return std::move(file_contents).str(); } - [[nodiscard]] std::string quickRead() const{ + [[nodiscard]] std::string readString() const{ std::ifstream file_stream(getPath(), std::ios::binary | std::ios::ate); if(!file_stream.is_open()) return ""; @@ -330,8 +330,8 @@ namespace OS{ using Filter = std::pair>; template - [[nodiscard]] ext::StringMap> sortSubs() const{ - ext::StringMap> map; + [[nodiscard]] ext::StringHashMap> sortSubs() const{ + ext::StringHashMap> map; this->forAllSubs([&map](File&& file){ const std::string& extension = file.extension(); map[extension.empty() ? static_cast(EMPTY_EXTENSION) : extension].push_back(file); @@ -341,8 +341,8 @@ namespace OS{ } template - [[nodiscard]] ext::StringMap> sortSubsBy(const std::span& standards) const{ - ext::StringMap> map; + [[nodiscard]] ext::StringHashMap> sortSubsBy(const std::span& standards) const{ + ext::StringHashMap> map; this->forAllSubs([&standards, &map](File&& file){ if(const auto it = std::ranges::find_if(standards, [&file](const Filter& pair){ @@ -355,9 +355,9 @@ namespace OS{ return map; } - static ext::StringMap> sortBy( + static ext::StringHashMap> sortBy( const std::span& files, const std::span& standards){ - ext::StringMap> map; + ext::StringHashMap> map; for(const File& file : files){ if( diff --git a/src/arc/io/FileTree.cppm b/src/arc/io/FileTree.cppm index 50e215cd..d2fe1993 100644 --- a/src/arc/io/FileTree.cppm +++ b/src/arc/io/FileTree.cppm @@ -25,9 +25,9 @@ export namespace OS{ /** * \brief Key - directory name | Value - File * */ - ext::StringMap> files{}; + ext::StringHashMap> files{}; - ext::StringMap flatView{}; + ext::StringHashMap flatView{}; File root{}; @@ -71,7 +71,7 @@ export namespace OS{ return os; } - [[nodiscard]] const ext::StringMap>& getFiles() const{ + [[nodiscard]] const ext::StringHashMap>& getFiles() const{ return files; } @@ -97,7 +97,7 @@ export namespace OS{ } } - [[nodiscard]] ext::StringMap& getFlatView(){ return flatView; } + [[nodiscard]] ext::StringHashMap& getFlatView(){ return flatView; } template OS::File flatFind(const std::string_view fileName) const { diff --git a/src/arc/io/SpecializedIO.cppm b/src/arc/io/SpecializedIO.cppm index 1099209a..23972ff2 100644 --- a/src/arc/io/SpecializedIO.cppm +++ b/src/arc/io/SpecializedIO.cppm @@ -5,8 +5,8 @@ module; export module Core.IO.Specialized; export import Core.IO.BinaryIO; -export import Core.IO.JsonIO; export import ext.Json; +export import ext.json.io; export import Geom.Vector2D; export import Geom.Rect_Orthogonal; @@ -19,33 +19,31 @@ import ext.Base64; import ext.RuntimeException; import ext.Heterogeneous; -#define IO___FLATTEN_FUNC2(x,y) x##y +#define IO_FLATTEN_FUNC2(x,y) x##y -#define IO___FLATTEN_FUNC1(x,y) IO___FLATTEN_FUNC2(x,y) +#define IO_FLATTEN_FUNC1(x,y) IO_FLATTEN_FUNC2(x,y) -#define IO_GEN_NAME(x) IO___FLATTEN_FUNC1(x,__COUNTER__) +#define IO_GEN_NAME(x) IO_FLATTEN_FUNC1(x,__COUNTER__) #define IO_INSTANCE_NAMESPACE Core::IO::_instantiation -#define EXPLICITE_VALID(type) template<> constexpr bool ::Core::IO::jsonDirectSerializable = true; +#define EXPLICITE_VALID(type) template<> constexpr bool ::ext::json::jsonDirectSerializable = true; #define IO_INSTANCE(type) namespace IO_INSTANCE_NAMESPACE{ type IO_GEN_NAME(_instance_){};}\ // EXPLICITE_VALID(type)\ -#define DEFINE_SPEC(base, templateT, type) export template <> struct ::Core::IO::JsonSerializator> : base{};\ -IO_INSTANCE(::Core::IO::JsonSerializator>)\ +#define DEFINE_SPEC(base, templateT, type) export template <> struct ::ext::json::JsonSerializator> : base{};\ +IO_INSTANCE(::ext::json::JsonSerializator>)\ -#define DEFINE_FUND(base, type) export template <> struct ::Core::IO::JsonSerializator : base{};\ -IO_INSTANCE(::Core::IO::JsonSerializator)\ +#define DEFINE_FUND(base, type) export template <> struct ::ext::json::JsonSerializator : base{};\ +IO_INSTANCE(::ext::json::JsonSerializator)\ #define DEFINE_SPEC_IO_INSTANCE(templateT, type) \ -IO_INSTANCE(::Core::IO::JsonSerializator>)\ - - -namespace Core::IO{ +IO_INSTANCE(::ext::json::JsonSerializator>)\ +namespace ext::json{ export template struct JsonSrlFundamentalBase{ @@ -126,7 +124,7 @@ namespace Core::IO{ }; template <> - struct JsonSerializator{ + struct ext::json::JsonSerializator{ static void write(ext::json::JsonValue& jsonValue, const Graphic::Color& data){ jsonValue.setData(data.toString()); } @@ -137,130 +135,7 @@ namespace Core::IO{ }; template <> - struct JsonSerializator{ - static void write(ext::json::JsonValue& jsonValue, const std::string_view data){ - jsonValue.setData(static_cast(data)); - } - - static void read(const ext::json::JsonValue& jsonValue, std::string_view& data) = delete; - }; - - template <> - struct JsonSerializator{ - static void write(ext::json::JsonValue& jsonValue, const std::string& data){ - jsonValue.setData(data); - } - - static void read(const ext::json::JsonValue& jsonValue, std::string& data){ - data = jsonValue.as(); - } - }; - - template - requires !std::is_pointer_v> && Core::IO::jsonSerializable< - std::ranges::range_value_t> - struct JsonSrlContBase_vector{ - static void write(ext::json::JsonValue& jsonValue, const Cont& data){ - auto& val = jsonValue.asArray(); - val.resize(data.size()); - std::ranges::transform(data, val.begin(), ext::json::getJsonOf); - } - - static void read(const ext::json::JsonValue& jsonValue, Cont& data){ - if(auto* ptr = jsonValue.tryGetValue()){ - data.resize(ptr->size()); - - for(auto& [index, element] : data | std::ranges::views::enumerate){ - ext::json::getValueTo(element, ptr->at(index)); - } - } - } - }; - - template , class Keyeq = std::equal_to, - class Alloc = std::allocator>> - requires - !std::is_pointer_v && !std::is_pointer_v && - std::is_default_constructible_v && std::is_default_constructible_v - struct JsonSrlContBase_unordered_map{ - //OPTM using array instead of object to be the KV in json? - static void write(ext::json::JsonValue& jsonValue, const std::unordered_map& data){ - auto& val = jsonValue.asArray(); - val.reserve(data.size()); - - for(auto& [k, v] : data){ - ext::json::JsonValue jval{}; - jval.asObject(); - jval.append(ext::json::keys::Key, ext::json::getJsonOf(k)); - jval.append(ext::json::keys::Value, ext::json::getJsonOf(v)); - val.push_back(std::move(jval)); - } - } - - static void read(const ext::json::JsonValue& jsonValue, std::unordered_map& data){ - if(auto* ptr = jsonValue.tryGetValue()){ - data.reserve(ptr->size()); - - for(const auto& jval : *ptr){ - auto& pair = jval.asObject(); - - K k{}; - V v{}; - ext::json::getValueTo(k, pair.at(ext::json::keys::Key)); - ext::json::getValueTo(v, pair.at(ext::json::keys::Value)); - - data.insert_or_assign(std::move(k), std::move(v)); - } - } - } - }; - - template - requires !std::is_pointer_v && std::is_default_constructible_v - struct JsonSrlContBase_string_map{ - //OPTM using array instead of object to be the KV in json? - static void write(ext::json::JsonValue& jsonValue, const ext::StringMap& data){ - auto& val = jsonValue.asArray(); - val.reserve(data.size()); - - for(auto& [k, v] : data){ - ext::json::JsonValue jval{}; - jval.asObject(); - jval.append(ext::json::keys::Key, ext::json::getJsonOf(k)); - jval.append(ext::json::keys::Value, ext::json::getJsonOf(v)); - val.push_back(std::move(jval)); - } - } - - static void read(const ext::json::JsonValue& jsonValue, ext::StringMap& data){ - if(auto* ptr = jsonValue.tryGetValue()){ - data.reserve(ptr->size()); - - for(const auto& jval : *ptr){ - auto& pair = jval.asObject(); - - if constexpr (overwirte){ - V* d = data.tryFind(pair.at(ext::json::keys::Key).as()); - if(d){ - ext::json::getValueTo(*d, pair.at(ext::json::keys::Value)); - } - }else{ - std::string k{}; - V v{}; - ext::json::getValueTo(k, pair.at(ext::json::keys::Key)); - ext::json::getValueTo(v, pair.at(ext::json::keys::Value)); - - data.insert_or_assign(std::move(k), std::move(v)); - } - - } - } - } - }; - - template <> - struct JsonSerializator{ + struct ext::json::JsonSerializator{ static void write(ext::json::JsonValue& jsonValue, const Ctrl::Operation& data){ jsonValue.asObject(); jsonValue.append("full", static_cast(data.customeBind.getFullKey())); @@ -282,7 +157,7 @@ namespace Core::IO{ }; template <> - struct JsonSerializator{ + struct ext::json::JsonSerializator{ using UmapIO = JsonSrlContBase_string_map; static void write(ext::json::JsonValue& jsonValue, const Ctrl::OperationGroup& data){ @@ -303,72 +178,72 @@ namespace Core::IO{ } -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, char); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, signed char); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, unsigned char); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, wchar_t); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, char8_t); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, char16_t); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, char32_t); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, short); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, unsigned short); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, int); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, unsigned int); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, long); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, unsigned long); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, long long); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, unsigned long long); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, float); -DEFINE_SPEC(::Core::IO::JsonSrlSectionBase, Math::Section, double); - -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, char); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, signed char); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, unsigned char); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, wchar_t); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, char8_t); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, char16_t); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, char32_t); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, short); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, unsigned short); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, int); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, unsigned int); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, long); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, unsigned long); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, long long); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, unsigned long long); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, float); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, double); -DEFINE_FUND(::Core::IO::JsonSrlFundamentalBase, bool); - -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); -DEFINE_FUND(::Core::IO::JsonSrlContBase_vector, std::vector); - -DEFINE_SPEC(::Core::IO::JsonSrlVecBase, Geom::Vector2D, short); -DEFINE_SPEC(::Core::IO::JsonSrlVecBase, Geom::Vector2D, unsigned short); -DEFINE_SPEC(::Core::IO::JsonSrlVecBase, Geom::Vector2D, int); -DEFINE_SPEC(::Core::IO::JsonSrlVecBase, Geom::Vector2D, unsigned int) -DEFINE_SPEC(::Core::IO::JsonSrlVecBase, Geom::Vector2D, float); - - -DEFINE_SPEC(::Core::IO::JsonSrlRectBase, Geom::Rect_Orthogonal, int); -DEFINE_SPEC(::Core::IO::JsonSrlRectBase, Geom::Rect_Orthogonal, unsigned int); -DEFINE_SPEC(::Core::IO::JsonSrlRectBase, Geom::Rect_Orthogonal, float); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, char); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, signed char); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, unsigned char); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, wchar_t); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, char8_t); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, char16_t); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, char32_t); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, short); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, unsigned short); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, int); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, unsigned int); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, long); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, unsigned long); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, long long); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, unsigned long long); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, float); +DEFINE_SPEC(::ext::json::JsonSrlSectionBase, Math::Section, double); + +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, char); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, signed char); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, unsigned char); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, wchar_t); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, char8_t); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, char16_t); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, char32_t); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, short); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, unsigned short); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, int); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, unsigned int); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, long); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, unsigned long); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, long long); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, unsigned long long); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, float); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, double); +DEFINE_FUND(::ext::json::JsonSrlFundamentalBase, bool); + +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); +DEFINE_FUND(::ext::json::JsonSrlContBase_vector, std::vector); + +DEFINE_SPEC(::ext::json::JsonSrlVecBase, Geom::Vector2D, short); +DEFINE_SPEC(::ext::json::JsonSrlVecBase, Geom::Vector2D, unsigned short); +DEFINE_SPEC(::ext::json::JsonSrlVecBase, Geom::Vector2D, int); +DEFINE_SPEC(::ext::json::JsonSrlVecBase, Geom::Vector2D, unsigned int) +DEFINE_SPEC(::ext::json::JsonSrlVecBase, Geom::Vector2D, float); + + +DEFINE_SPEC(::ext::json::JsonSrlRectBase, Geom::Rect_Orthogonal, int); +DEFINE_SPEC(::ext::json::JsonSrlRectBase, Geom::Rect_Orthogonal, unsigned int); +DEFINE_SPEC(::ext::json::JsonSrlRectBase, Geom::Rect_Orthogonal, float); diff --git a/src/arc/math/algorithm/StripPacker2D.cppm b/src/arc/math/algorithm/StripPacker2D.cppm index b3c2c0e0..1536c57f 100644 --- a/src/arc/math/algorithm/StripPacker2D.cppm +++ b/src/arc/math/algorithm/StripPacker2D.cppm @@ -1,4 +1,4 @@ -export module Math.StripPacker2D; +export module Math.Algo.StripPacker2D; #if DEBUG_CHECK @@ -8,50 +8,69 @@ import ext.RuntimeException; import std; import ext.Concepts; import Geom.Rect_Orthogonal; +import Geom.Vector2D; import Math; export namespace Math { /** * @brief - * @tparam Tgt Should Avoid Copy When Tgt Type is provided + * @tparam T Should Avoid Copy When Tgt Type is provided * @tparam N pack arithmetic type * @tparam trans Trans the Tgt to a non-temp rect */ - template - requires Concepts::Invokable&(Tgt)> && Concepts::InvokeNoexcept + template + requires requires(){ + requires std::is_pointer_v; + std::same_as&, std::invoke_result_t>; + } struct StripPacker2D { protected: using Rect = Geom::Rect_Orthogonal; using SubRectArr = std::array; - static Rect& obtain(Tgt cont) noexcept { - return trans(cont); + static Rect& obtain(T cont) noexcept { + return std::invoke(trans, cont); } - [[nodiscard]] bool contains(Tgt cont) const noexcept {return all.contains(cont);} + [[nodiscard]] bool contains(T cont) const noexcept {return all.contains(cont);} - std::vector boxes_widthAscend{}; - std::vector boxes_heightAscend{}; - std::unordered_set all{}; + std::vector boxes_widthAscend{}; + std::vector boxes_heightAscend{}; + std::unordered_set all{}; + using SizeTy = Geom::Vector2D; public: - std::vector packed{}; + std::vector packed{}; - N rstWidth{0}, rstHeight{0}; - N maxWidth{2750}, maxHeight{2750}; + SizeTy exportSize{}; + SizeTy maxSize{}; - void setMaxSize(const N maxWidth, const N maxHeight) noexcept { - this->maxHeight = maxHeight; - this->maxWidth = maxWidth; + N margin{0}; + + constexpr void setMaxSize(const SizeTy size) noexcept { + maxSize.set(size); + } + + constexpr void setMaxSize(const N maxWidth, const N maxHeight) noexcept { + maxSize.set(maxWidth, maxHeight); } [[nodiscard]] StripPacker2D() = default; - void push(Concepts::Iterable auto& targets){ + template Rng> + requires std::ranges::sized_range + [[nodiscard]] explicit StripPacker2D(Rng& targets){ + this->template push(targets); + } + + template Rng> + requires std::ranges::sized_range + void push(Rng& targets){ all.reserve(targets.size()); boxes_widthAscend.reserve(targets.size()); boxes_heightAscend.reserve(targets.size()); - for(auto element : targets) { //Why transform crashes? + + for(auto& element : targets) { //Why transform crashes? all.insert(element); boxes_widthAscend.push_back(element); boxes_heightAscend.push_back(element); @@ -61,40 +80,60 @@ export namespace Math { } void sortData() { - std::ranges::sort(boxes_widthAscend , [this](const Rect& r1, const Rect& r2) { - return r1.getWidth() > r2.getWidth(); - }, &obtain); + std::ranges::sort(boxes_widthAscend , std::greater{}, [](const T& t){ + return StripPacker2D::obtain(t).getWidth(); + }); - std::ranges::sort(boxes_heightAscend, [this](const Rect& r1, const Rect& r2) { - return r1.getHeight() > r2.getHeight(); - }, &obtain); + std::ranges::sort(boxes_heightAscend, std::greater{}, [](const T& t){ + return StripPacker2D::obtain(t).getHeight(); + }); } - void process() noexcept { - this->tryPlace({SubRectArr{Rect{}, Rect{}, Rect{maxWidth, maxHeight}}}); + [[nodiscard]] constexpr N getMargin() const noexcept{ return margin; } + + constexpr void setMargin(const N margin) noexcept{ this->margin = margin; } + + void process() noexcept { + sortData(); + + if(margin != 0){ + for (auto& data : boxes_widthAscend){ + this->obtain(data).addSize(margin * 2, margin * 2); + } + } + this->tryPlace({SubRectArr{Rect{}, Rect{}, Rect{maxSize}}}); + if(margin != 0){ + for (auto& data : boxes_widthAscend){ + this->obtain(data).shrink(margin, margin); + } + } } - Rect getResultBound() const noexcept {return Rect{0, 0, rstWidth, rstHeight};} + Geom::Vector2D getResultSize() const noexcept {return exportSize;} - std::unordered_set& getRemains() noexcept {return all;} + std::unordered_set& getRemains() noexcept {return all;} protected: [[nodiscard]] bool shouldStop() const noexcept {return all.empty();} - Rect* tryPlace(const Rect& bound, std::vector& targets) noexcept { + Rect* tryPlace(const Rect& bound, std::vector& targets) noexcept { for(auto& cont : targets){ if(!this->contains(cont))continue; + Rect& rect = this->obtain(cont); + rect.setSrc(bound.getSrcX(), bound.getSrcY()); - if( + + if( //Contains rect.getEndX() <= bound.getEndX() && rect.getEndY() <= bound.getEndY() ){ all.erase(cont); packed.push_back(cont); - rstWidth = Math::max(rect.getEndX(), rstWidth); - rstHeight = Math::max(rect.getEndY(), rstHeight); + + exportSize.maxX(rect.getEndX()); + exportSize.maxY(rect.getEndY()); return ▭ } @@ -134,15 +173,16 @@ export namespace Math { std::vector next{}; next.reserve(bounds.size() * 3); + //BFS for(const SubRectArr& currentBound : bounds) { - for(int i = 0; i < currentBound.size(); ++i) { - if(const Rect& bound = currentBound[i]; bound.area() > 0) { - if(const Rect* const result = this->tryPlace(bound, (i & 1) ? boxes_widthAscend : boxes_heightAscend)){ - //if(result != nullptr) - auto arr = this->splitQuad(bound, *result); - if(arr[0].area() == 0 && arr[1].area() == 0 && arr[2].area() == 0)continue; - next.push_back(std::move(arr)); - } + for(const auto& [i, bound] : currentBound | std::ranges::views::enumerate){ + if(bound.area() == 0)continue; + + if(const Rect* const result = this->tryPlace(bound, (i & 1) ? boxes_widthAscend : boxes_heightAscend)){ + //if(result != nullptr) + auto arr = this->splitQuad(bound, *result); + if(arr[0].area() == 0 && arr[1].area() == 0 && arr[2].area() == 0)continue; + next.push_back(std::move(arr)); } } } diff --git a/src/arc/math/algorithm/TopologicalSort.cppm b/src/arc/math/algorithm/TopologicalSort.cppm new file mode 100644 index 00000000..d3b1179b --- /dev/null +++ b/src/arc/math/algorithm/TopologicalSort.cppm @@ -0,0 +1,98 @@ +// +// Created by Matrix on 2024/6/8. +// + +export module Math.Algo.TopologicalSort; + +import std; +import ext.RuntimeException; + +export namespace Math{ + struct TestT{ + TestT* dependencyTarget{}; + int val{}; + }; + + /** + * @brief Sort given range by provided dependency relation + * @tparam Comp Comparator Type + * @tparam Rng Range Type + * @tparam Proj Projection Type + * @tparam KeyProj Projection Type to its key + * @tparam Pred Check if is valid + */ + template < + std::ranges::sized_range Rng, + std::indirectly_unary_invocable> Proj, + std::indirectly_unary_invocable> KeyProj = std::identity, + std::indirect_unary_predicate, Proj>> Pred = std::identity> + requires requires{ + requires std::invocable, Proj>::value_type>; + requires std::permutable>; + } + auto get_topological_depth_map(const Rng& rng, Proj proj, KeyProj keyProj = {}, Pred pred = {}) + -> std::unordered_map, KeyProj>::value_type, std::size_t> + { + using ProjTy = typename std::projected, Proj>::value_type; + using K = typename std::projected, KeyProj>::value_type; + + std::unordered_map depth{std::ranges::size(rng)}; + std::unordered_set visited(std::ranges::size(rng) / 2); + + for(auto curItr = std::ranges::begin(rng); curItr != std::ranges::end(rng); ++curItr){ + visited.clear(); + ProjTy curNode = std::invoke(proj, *curItr); + + while(pred(curNode)){ + auto key = std::invoke(keyProj, curNode); + + if(visited.contains(key) || visited.size() > std::ranges::size(rng)){ + throw ext::IllegalArguments{"Loop Reference detected during topological sort"}; + } + + visited.insert(key); + ++depth[key]; + curNode = std::invoke(proj, curNode); + } + } + +#if DEBUG_CHECK + if(depth.size() > std::ranges::size(rng)){ + throw ext::IllegalArguments{"Illegal Key: Depth Size Not Equal To Src Size!"}; + } +#endif + + for(auto curItr = std::ranges::begin(rng); curItr != std::ranges::end(rng); ++curItr){ + depth.try_emplace(std::invoke(keyProj, *curItr), 0); + } + + return depth; + } + + /** + * @brief Sort given range by provided dependency relation + * @tparam Comp Comparator Type + * @tparam Rng Range Type + * @tparam Proj Projection Type + * @tparam KeyProj Projection Type to its key + * @tparam Pred Check if is valid + */ + template < + template typename Comp = std::less, + typename Rng, + typename Proj, + typename KeyProj = std::identity, + typename Pred = std::identity> + requires requires{ + requires std::regular_invocable, std::size_t, std::size_t>; + requires std::permutable>; + } + void sort_topological(Rng& rng, Proj proj, KeyProj keyProj = {}, Pred pred = {}){ + auto depth = Math::get_topological_depth_map(rng, proj, keyProj, pred); + + std::ranges::sort(rng, [&](auto& l, auto& r){ + static constexpr Comp comp{}; + return comp.operator()(depth.at(std::invoke(keyProj, l)), depth.at(std::invoke(keyProj, r))); + }); + } +} diff --git a/src/arc/math/geom/QuadTree.cppm b/src/arc/math/geom/QuadTree.cppm index 9d13f3f0..beb01f33 100644 --- a/src/arc/math/geom/QuadTree.cppm +++ b/src/arc/math/geom/QuadTree.cppm @@ -292,10 +292,10 @@ namespace Geom{ func(*this); if(!isLeaf()){ - topLeft->each(std::forward(func)); - topRight->each(std::forward(func)); - bottomLeft->each(std::forward(func)); - bottomRight->each(std::forward(func)); + topLeft->each(func); + topRight->each(func); + bottomLeft->each(func); + bottomRight->each(func); } } @@ -432,10 +432,10 @@ namespace Geom{ //If this node has children, check if the rectangle overlaps with any rectangle in the children if(auto _ = this->getLeafGruard_Shared(); !isLeaf()){ - topLeft->intersectRect(rect, std::forward(func)); - topRight->intersectRect(rect, std::forward(func)); - bottomLeft->intersectRect(rect, std::forward(func)); - bottomRight->intersectRect(rect, std::forward(func)); + topLeft->intersectRect(rect, func); + topRight->intersectRect(rect, func); + bottomLeft->intersectRect(rect, func); + bottomRight->intersectRect(rect, func); } for(auto _ = this->getItemGruard_Shared(); auto* cont : items){ @@ -452,10 +452,10 @@ namespace Geom{ // If this node has children, check if the rectangle overlaps with any rectangle in the children if(auto _ = this->getLeafGruard_Shared(); !isLeaf()){ - topLeft->template intersectRegion(region, std::forward(boundCheck), std::forward(func)); - topRight->template intersectRegion(region, std::forward(boundCheck), std::forward(func)); - bottomLeft->template intersectRegion(region, std::forward(boundCheck), std::forward(func)); - bottomRight->template intersectRegion(region, std::forward(boundCheck), std::forward(func)); + topLeft->template intersectRegion(region, boundCheck, func); + topRight->template intersectRegion(region, boundCheck, func); + bottomLeft->template intersectRegion(region, boundCheck, func); + bottomRight->template intersectRegion(region, boundCheck, func); } for(auto _ = this->getItemGruard_Shared(); auto* cont : items){ @@ -463,20 +463,20 @@ namespace Geom{ } } - void intersectPoint(const Vec2 point, Concepts::Invokable auto&& pred){ + void intersectPoint(const Vec2 point, Concepts::Invokable auto&& func){ if(!this->inbound(point)) return; // If this node has children, check if the rectangle overlaps with any rectangle in the children if(auto _ = this->getLeafGruard_Shared(); !isLeaf()){ - topLeft->intersectPoint(point, std::forward(pred)); - topRight->intersectPoint(point, std::forward(pred)); - bottomLeft->intersectPoint(point, std::forward(pred)); - bottomRight->intersectPoint(point, std::forward(pred)); + topLeft->intersectPoint(point, func); + topRight->intersectPoint(point, func); + bottomLeft->intersectPoint(point, func); + bottomRight->intersectPoint(point, func); } for(auto _ = this->getItemGruard_Shared(); auto* cont : items){ if(this->intersectWith(point, *cont)){ - pred(*cont); + func(*cont); } } } diff --git a/src/arc/math/geom/Transform.cppm b/src/arc/math/geom/Transform.cppm index 0fe2384b..48ccc9c1 100644 --- a/src/arc/math/geom/Transform.cppm +++ b/src/arc/math/geom/Transform.cppm @@ -25,46 +25,63 @@ export namespace Geom{ rot = NaN; } - constexpr Transform& operator|=(const Transform& parentRef){ - vec.rotate(parentRef.rot).add(parentRef.vec); - rot += parentRef.rot; + constexpr Transform& applyInv(const Transform& transform) noexcept{ + vec.sub(transform.vec).rotate(-transform.rot); + rot -= transform.rot; return *this; } - constexpr Transform& operator+=(const Transform& other){ + constexpr Transform& apply(const Transform& transform) noexcept{ + vec.rotate(transform.rot).add(transform.vec); + rot += transform.rot; + + return *this; + } + + constexpr Transform& operator|=(const Transform& parentRef) noexcept{ + return apply(parentRef); + } + + constexpr Transform& operator+=(const Transform& other) noexcept{ vec += other.vec; rot += other.rot; return *this; } - constexpr Transform& operator-=(const Transform& other){ + constexpr Transform& operator-=(const Transform& other) noexcept{ vec -= other.vec; rot -= other.rot; return *this; } - constexpr Transform& operator*=(const float scl){ + constexpr Transform& operator*=(const float scl) noexcept{ vec *= scl; rot *= scl; return *this; } - [[nodiscard]] constexpr friend Transform operator*(Transform self, const float scl){ + [[nodiscard]] constexpr friend Transform operator*(Transform self, const float scl) noexcept{ return self *= scl; } - [[nodiscard]] constexpr friend Transform operator+(Transform self, const Transform& other){ + [[nodiscard]] constexpr friend Transform operator+(Transform self, const Transform& other) noexcept{ return self += other; } - [[nodiscard]] constexpr friend Transform operator-(Transform self, const Transform& other){ + [[nodiscard]] constexpr friend Transform operator-(Transform self, const Transform& other) noexcept{ return self -= other; } + [[nodiscard]] constexpr friend Transform operator-(Transform self) noexcept{ + self.vec.reverse(); + self.rot *= -1; + return self; + } + /** * @brief Local To Parent * @param self To Trans diff --git a/src/arc/math/geom/Vector2D.cppm b/src/arc/math/geom/Vector2D.cppm index ebd14390..e7880a70 100644 --- a/src/arc/math/geom/Vector2D.cppm +++ b/src/arc/math/geom/Vector2D.cppm @@ -320,7 +320,7 @@ export namespace Geom{ return *this; } - Vector2D& rotateRad(const float rad) noexcept{ + constexpr Vector2D& rotateRad(const float rad) noexcept{ // Matrix Multi // cos rad -sin rad x crx -sry // sin rad cos rad y srx cry @@ -338,11 +338,11 @@ export namespace Geom{ } } - Vector2D& rotate(const float degree) noexcept{ + constexpr Vector2D& rotate(const float degree) noexcept{ return rotateRad(degree * Math::DEGREES_TO_RADIANS); } - Vector2D& lerp(const PassType tgt, const float alpha) noexcept{ + constexpr Vector2D& lerp(const PassType tgt, const float alpha) noexcept{ return this->set(Math::lerp(x, tgt.x, alpha), Math::lerp(y, tgt.y, alpha)); } @@ -656,6 +656,10 @@ export namespace Geom{ return Vector2D{Math::trac(x), Math::trac(y)}; } + [[nodiscard]] constexpr T area() const noexcept { + return x * y; + } + [[nodiscard]] constexpr bool isZero() const noexcept { return length2() == static_cast(0); } @@ -764,24 +768,22 @@ export } }; +template