diff --git a/.vscode/settings.json b/.vscode/settings.json index a0db1a6..51d1e32 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -91,7 +91,9 @@ "__split_buffer": "cpp", "__tree": "cpp", "stack": "cpp", - "future": "cpp" + "future": "cpp", + "coroutine": "cpp", + "resumable": "cpp" }, "files.exclude": { "**/*.rpyc": true, diff --git a/tests/g2chromatic2.cpp b/tests/g2chromatic2.cpp index b2617fe..04c0e4f 100644 --- a/tests/g2chromatic2.cpp +++ b/tests/g2chromatic2.cpp @@ -40,10 +40,13 @@ void Preview(Grid2 grid) { TIME_FUNCTION; int width; int height; - std::vector rgbData; + //std::vector rgbData; - grid.getGridAsRGB(width,height,rgbData); - bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData, width, height); + frame rgbData = grid.getGridAsFrame(frame::colormap::RGB); + bool success = BMPWriter::saveBMP("output/grayscalesource.bmp", rgbData); + if (!success) { + std::cout << "yo! this failed in Preview" << std::endl; + } } std::vector> pickSeeds(Grid2 grid, AnimationConfig config) { @@ -58,7 +61,7 @@ std::vector> pickSeeds(Grid2 grid, AnimationConfi for (int i = 0; i < config.numSeeds; ++i) { Vec2 point(xDist(gen), yDist(gen)); - Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 1.0f); + Vec4 color(colorDist(gen), colorDist(gen), colorDist(gen), 255); size_t id = grid.getPositionVec(point); grid.setColor(id, color); seeds.push_back(std::make_tuple(id,point, color)); @@ -172,8 +175,10 @@ bool exportavi(std::vector frames, AnimationConfig config) { int main() { AnimationConfig config; + // std::cout << "g2c2175" << std::endl; Grid2 grid = setup(config); + // std::cout << "g2c2178" << std::endl; Preview(grid); std::vector> seeds = pickSeeds(grid,config); std::vector frames; @@ -189,6 +194,9 @@ int main() { bgrframe.printCompressionStats(); //(bgrframe, i + 1); frames.push_back(bgrframe); + //bgrframe.decompress(); + //BMPWriter::saveBMP(std::format("output/grayscalesource.{}.bmp", i), bgrframe); + bgrframe.compressFrameLZ78(); } } diff --git a/util/grid/grid2.hpp b/util/grid/grid2.hpp index 92d74e5..f020d18 100644 --- a/util/grid/grid2.hpp +++ b/util/grid/grid2.hpp @@ -480,6 +480,7 @@ public: void getGridRegionAsRGB(const Vec2& minCorner, const Vec2& maxCorner, int& width, int& height, std::vector& rgbData) const { TIME_FUNCTION; + // std::cout << "excessdebug g2.483" << std::endl; // Calculate dimensions width = static_cast(maxCorner.x - minCorner.x); height = static_cast(maxCorner.y - minCorner.y); @@ -490,99 +491,133 @@ public: rgbData.shrink_to_fit(); return; } + // std::cout << "excessdebug g2.494" << std::endl; // Initialize RGB data (3 bytes per pixel: R, G, B) - rgbData.resize(width * height * 3, 0); + std::vector rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f)); // For each position in the grid, find the corresponding pixel - //#pragma omp parallel for for (const auto& [id, pos] : Positions) { + // std::cout << "excessdebug g2.501." << id << std::endl; size_t size = Sizes.at(id); - // if (pos.x >= minCorner.x && pos.x < maxCorner.x && - // pos.y >= minCorner.y && pos.y < maxCorner.y) { - // Calculate pixel coordinates - int pixelXm = static_cast(pos.x - size/2 - minCorner.x); - int pixelXM = static_cast(pos.x + size/2 - minCorner.x); - int pixelYm = static_cast(pos.y - size/2 - minCorner.y); - int pixelYM = static_cast(pos.y + size/2 - minCorner.y); - - pixelXm = std::max(0, pixelXm); - pixelXM = std::min(width - 1, pixelXM); - pixelYm = std::max(0, pixelYm); - pixelYM = std::min(height - 1, pixelYM); + // Calculate pixel coordinates + int pixelXm = static_cast(pos.x - size/2 - minCorner.x); + int pixelXM = static_cast(pos.x + size/2 - minCorner.x); + int pixelYm = static_cast(pos.y - size/2 - minCorner.y); + int pixelYM = static_cast(pos.y + size/2 - minCorner.y); + + pixelXm = std::max(0, pixelXm); + pixelXM = std::min(width - 1, pixelXM); + pixelYm = std::max(0, pixelYm); + pixelYM = std::min(height - 1, pixelYM); + // std::cout << "excessdebug g2.514." << id << std::endl; - // Ensure within bounds - if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { - const Vec4& color = Colors.at(id); - for (int py = pixelYm; py <= pixelYM; ++py){ - for (int px = pixelXm; px <= pixelXM; ++px){ - // Get color and convert to RGB - int index = (py * width + px) * 3; - - // Convert from [0,1] to [0,255] and store as RGB - rgbData[index] = static_cast(color.r * 255); - rgbData[index + 1] = static_cast(color.g * 255); - rgbData[index + 2] = static_cast(color.b * 255); - } + // Ensure within bounds + if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { + // std::cout << "excessdebug g2.518." << id << " - (" << pixelXm << "," << pixelYM << ")" << std::endl; + const Vec4& color = Colors.at(id); + float srcAlpha = color.a; + float invSrcAlpha = 1.0f - srcAlpha; + for (int py = pixelYm; py <= pixelYM; ++py){ + for (int px = pixelXm; px <= pixelXM; ++px){ + // std::cout << "excessdebug g2.524." << id << " - (" << py << "," << px << ")" << std::endl; + int index = (py * width + px); + Vec4 dest = rgbaBuffer[index]; + + dest.r = color.r * srcAlpha + dest.r; // * invSrcAlpha; + dest.g = color.g * srcAlpha + dest.g; // * invSrcAlpha; + dest.b = color.b * srcAlpha + dest.b; // * invSrcAlpha; + dest.a = srcAlpha + dest.a; // * invSrcAlpha; + rgbaBuffer[index] = dest; } } - //} + } + } + rgbData.resize(rgbaBuffer.size() * 3); + for (int i = 0; i < rgbaBuffer.size(); ++i) { + const Vec4& color = rgbaBuffer[i]; + int bgrIndex = i * 3; + + // Convert from [0,1] to [0,255] and store as RGB + rgbData[bgrIndex + 0] = static_cast(color.r * 255); + rgbData[bgrIndex + 1] = static_cast(color.g * 255); + rgbData[bgrIndex + 2] = static_cast(color.b * 255); + } } // Get region as BGR void getGridRegionAsBGR(const Vec2& minCorner, const Vec2& maxCorner, - int& width, int& height, std::vector& bgrData) const { + int& width, int& height, std::vector& rgbData) const { TIME_FUNCTION; + // std::cout << "excessdebug g2.483" << std::endl; // Calculate dimensions width = static_cast(maxCorner.x - minCorner.x); height = static_cast(maxCorner.y - minCorner.y); if (width <= 0 || height <= 0) { width = height = 0; - bgrData.clear(); - bgrData.shrink_to_fit(); + rgbData.clear(); + rgbData.shrink_to_fit(); return; } + // std::cout << "excessdebug g2.494" << std::endl; // Initialize RGB data (3 bytes per pixel: R, G, B) - bgrData.resize(width * height * 3, 0); + std::vector rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f)); // For each position in the grid, find the corresponding pixel - //#pragma omp parallel for for (const auto& [id, pos] : Positions) { + // std::cout << "excessdebug g2.501." << id << std::endl; size_t size = Sizes.at(id); - // if (pos.x >= minCorner.x && pos.x < maxCorner.x && - // pos.y >= minCorner.y && pos.y < maxCorner.y) { - // Calculate pixel coordinates - int pixelXm = static_cast(pos.x - size/2 - minCorner.x); - int pixelXM = static_cast(pos.x + size/2 - minCorner.x); - int pixelYm = static_cast(pos.y - size/2 - minCorner.y); - int pixelYM = static_cast(pos.y + size/2 - minCorner.y); - - pixelXm = std::max(0, pixelXm); - pixelXM = std::min(width - 1, pixelXM); - pixelYm = std::max(0, pixelYm); - pixelYM = std::min(height - 1, pixelYM); + // Calculate pixel coordinates + int pixelXm = static_cast(pos.x - size/2 - minCorner.x); + int pixelXM = static_cast(pos.x + size/2 - minCorner.x); + int pixelYm = static_cast(pos.y - size/2 - minCorner.y); + int pixelYM = static_cast(pos.y + size/2 - minCorner.y); + + pixelXm = std::max(0, pixelXm); + pixelXM = std::min(width - 1, pixelXM); + pixelYm = std::max(0, pixelYm); + pixelYM = std::min(height - 1, pixelYM); + // std::cout << "excessdebug g2.514." << id << std::endl; - // Ensure within bounds - if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { - const Vec4& color = Colors.at(id); - for (int py = pixelYm; py <= pixelYM; ++py){ - for (int px = pixelXm; px <= pixelXM; ++px){ - // Get color and convert to RGB - int index = (py * width + px) * 3; - - // Convert from [0,1] to [0,255] and store as RGB - bgrData[index + 2] = static_cast(color.r * 255); - bgrData[index + 1] = static_cast(color.g * 255); - bgrData[index] = static_cast(color.b * 255); - } + // Ensure within bounds + if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { + // std::cout << "excessdebug g2.518." << id << " - (" << pixelXm << "," << pixelYM << ")" << std::endl; + const Vec4& color = Colors.at(id); + float srcAlpha = color.a; + float invSrcAlpha = 1.0f - srcAlpha; + for (int py = pixelYm; py <= pixelYM; ++py){ + for (int px = pixelXm; px <= pixelXM; ++px){ + // std::cout << "excessdebug g2.524." << id << " - (" << py << "," << px << ")" << std::endl; + int index = (py * width + px); + Vec4 dest = rgbaBuffer[index]; + + dest.r = color.r * srcAlpha + dest.r; // * invSrcAlpha; + dest.g = color.g * srcAlpha + dest.g; // * invSrcAlpha; + dest.b = color.b * srcAlpha + dest.b; // * invSrcAlpha; + dest.a = srcAlpha + dest.a; // * invSrcAlpha; + rgbaBuffer[index] = dest; } } - //} + } + } + rgbData.resize(rgbaBuffer.size() * 3); + for (int i = 0; i < rgbaBuffer.size(); ++i) { + const Vec4& color = rgbaBuffer[i]; + int bgrIndex = i * 3; + + // Convert from [0,1] to [0,255] and store as RGB + // rgbData.push_back(color.r); + // rgbData.push_back(color.g); + // rgbData.push_back(color.b); + rgbData[bgrIndex + 2] = static_cast(color.r * 255); + rgbData[bgrIndex + 1] = static_cast(color.g * 255); + rgbData[bgrIndex + 0] = static_cast(color.b * 255); + } } @@ -599,7 +634,6 @@ public: getGridRegionAsBGR(minCorner, maxCorner, width, height, bgrData); } - //frame stuff frame getGridRegionAsFrameRGB(const Vec2& minCorner, const Vec2& maxCorner) const { TIME_FUNCTION; @@ -624,79 +658,6 @@ public: return resultFrame; } - // Get region as frame (RGBA format) - frame getGridRegionAsFrameRGBA(const Vec2& minCorner, const Vec2& maxCorner) const { - TIME_FUNCTION; - int width, height; - std::vector rgbData; - getGridRegionAsRGB(minCorner, maxCorner, width, height, rgbData); - - // Convert RGB to RGBA - std::vector rgbaData; - rgbaData.reserve(width * height * 4); - - //#pragma omp parallel for - for (size_t i = 0; i < rgbData.size(); i += 3) { - rgbaData.push_back(rgbData[i]); // R - rgbaData.push_back(rgbData[i + 1]); // G - rgbaData.push_back(rgbData[i + 2]); // B - rgbaData.push_back(255); // A (fully opaque) - } - - frame resultFrame(width, height, frame::colormap::RGBA); - resultFrame.setData(rgbaData); - return resultFrame; - } - - // Get region as frame (BGRA format) - frame getGridRegionAsFrameBGRA(const Vec2& minCorner, const Vec2& maxCorner) const { - TIME_FUNCTION; - int width, height; - std::vector bgrData; - getGridRegionAsBGR(minCorner, maxCorner, width, height, bgrData); - - // Convert BGR to BGRA - std::vector bgraData; - bgraData.reserve(width * height * 4); - - //#pragma omp parallel for - for (size_t i = 0; i < bgrData.size(); i += 3) { - bgraData.push_back(bgrData[i]); // B - bgraData.push_back(bgrData[i + 1]); // G - bgraData.push_back(bgrData[i + 2]); // R - bgraData.push_back(255); // A (fully opaque) - } - - frame resultFrame(width, height, frame::colormap::BGRA); - resultFrame.setData(bgraData); - return resultFrame; - } - - // Get region as frame (Grayscale format) - frame getGridRegionAsFrameGrayscale(const Vec2& minCorner, const Vec2& maxCorner) const { - int width, height; - std::vector rgbData; - getGridRegionAsRGB(minCorner, maxCorner, width, height, rgbData); - - // Convert RGB to grayscale - std::vector grayData; - grayData.reserve(width * height); - - //#pragma omp parallel for - for (size_t i = 0; i < rgbData.size(); i += 3) { - uint8_t r = rgbData[i]; - uint8_t g = rgbData[i + 1]; - uint8_t b = rgbData[i + 2]; - // Standard grayscale conversion formula - uint8_t gray = static_cast(0.299 * r + 0.587 * g + 0.114 * b); - grayData.push_back(gray); - } - - frame resultFrame(width, height, frame::colormap::B); // B for single channel/grayscale - resultFrame.setData(grayData); - return resultFrame; - } - // Get entire grid as frame with specified format frame getGridAsFrame(frame::colormap format = frame::colormap::RGB) { TIME_FUNCTION; @@ -711,22 +672,13 @@ public: case frame::colormap::BGR: Frame = std::move(getGridRegionAsFrameBGR(minCorner, maxCorner)); break; - case frame::colormap::RGBA: - Frame = std::move(getGridRegionAsFrameRGBA(minCorner, maxCorner)); - break; - case frame::colormap::BGRA: - Frame = std::move(getGridRegionAsFrameBGRA(minCorner, maxCorner)); - break; - case frame::colormap::B: - Frame = std::move(getGridRegionAsFrameGrayscale(minCorner, maxCorner)); - break; default: Frame = std::move(getGridRegionAsFrameRGB(minCorner, maxCorner)); break; } //Frame.compressFrameDiff(); //Frame.compressFrameRLE(); - Frame.compressFrameLZ78(); + //Frame.compressFrameLZ78(); return Frame; } @@ -755,7 +707,6 @@ public: } } - //get bounding box void getBoundingBox(Vec2& minCorner, Vec2& maxCorner) { TIME_FUNCTION; diff --git a/util/grid/sprite2.hpp b/util/grid/sprite2.hpp index 4303f8c..a738ae3 100644 --- a/util/grid/sprite2.hpp +++ b/util/grid/sprite2.hpp @@ -8,7 +8,6 @@ class SpriteMap2 : public Grid2 { private: // id, sprite - //std::unordered_map> Sprites; std::unordered_map spritesComped; std::unordered_map Layers; std::unordered_map Orientations; @@ -27,7 +26,124 @@ public: return spritesComped.at(id); } - + void setSprite(size_t id, const frame& sprite) { + spritesComped[id] = sprite; + } + + int getLayer(size_t id) { + return Layers.at(id); + } + + size_t setLayer(size_t id, int layer) { + Layers[id] = layer; + return id; + } + + float getOrientation(size_t id) { + return Orientations.at(id); + } + size_t setOrientation(size_t id, float orientation) { + Orientations[id] = orientation; + } + + void getGridRegionAsBGR(const Vec2& minCorner, const Vec2& maxCorner, int& width, int& height, std::vector& bgrData) const { + TIME_FUNCTION; + // Calculate dimensions + width = static_cast(maxCorner.x - minCorner.x); + height = static_cast(maxCorner.y - minCorner.y); + + if (width <= 0 || height <= 0) { + width = height = 0; + bgrData.clear(); + bgrData.shrink_to_fit(); + return; + } + + // Initialize RGB data (3 bytes per pixel: R, G, B) + std::vector rgbaBuffer(width * height, Vec4(0.0f, 0.0f, 0.0f, 0.0f)); + + // For each position in the grid, find the corresponding pixel + for (const auto& [id, pos] : Positions) { + size_t size = Sizes.at(id); + + // Calculate pixel coordinates + int pixelXm = static_cast(pos.x - size/2 - minCorner.x); + int pixelXM = static_cast(pos.x + size/2 - minCorner.x); + int pixelYm = static_cast(pos.y - size/2 - minCorner.y); + int pixelYM = static_cast(pos.y + size/2 - minCorner.y); + + pixelXm = std::max(0, pixelXm); + pixelXM = std::min(width - 1, pixelXM); + pixelYm = std::max(0, pixelYm); + pixelYM = std::min(height - 1, pixelYM); + + // Ensure within bounds + if (pixelXM >= minCorner.x && pixelXm < width && pixelYM >= minCorner.y && pixelYm < height) { + const Vec4& color = Colors.at(id); + float srcAlpha = color.a; + float invSrcAlpha = 1.0f - srcAlpha; + for (int py = pixelYm; py <= pixelYM; ++py){ + for (int px = pixelXm; px <= pixelXM; ++px){ + int index = (py * width + px) * 3; + Vec4& dest = rgbaBuffer[index]; + + dest.r = color.r * srcAlpha + dest.r * invSrcAlpha; + dest.g = color.g * srcAlpha + dest.g * invSrcAlpha; + dest.b = color.b * srcAlpha + dest.b * invSrcAlpha; + dest.a = srcAlpha + dest.a * invSrcAlpha; + } + } + } + } + bgrData.resize(dest.size() * 4); + for (int i = 0; i < width * height; ++i) { + const Vec4& color = rgbaBuffer[i]; + int bgrIndex = i * 3; + + // Convert from [0,1] to [0,255] and store as BGR + bgrData[bgrIndex + 0] = static_cast(color.b * 255); // Blue + bgrData[bgrIndex + 1] = static_cast(color.g * 255); // Green + bgrData[bgrIndex + 2] = static_cast(color.r * 255); // Red + } + } + + size_t removeSprite(size_t id) { + spritesComped.erase(id); + Layers.erase(id); + Orientations.erase(id); + return removeID(id); + } + + // Remove sprite by position + size_t removeSprite(const Vec2& pos) { + size_t id = getPositionVec(pos); + return removeSprite(id); + } + + void clear() { + Grid2::clear(); + spritesComped.clear(); + Layers.clear(); + Orientations.clear(); + spritesComped.rehash(0); + Layers.rehash(0); + Orientations.rehash(0); + } + + // Get all sprite IDs + std::vector getAllSpriteIDs() { + return getAllIDs(); + } + + // Check if ID has a sprite + bool hasSprite(size_t id) const { + return spritesComped.find(id) != spritesComped.end(); + } + + // Get number of sprites + size_t getSpriteCount() const { + return spritesComped.size(); + } }; diff --git a/util/output/aviwriter.hpp b/util/output/aviwriter.hpp index 877b93f..fb41f02 100644 --- a/util/output/aviwriter.hpp +++ b/util/output/aviwriter.hpp @@ -157,29 +157,29 @@ private: break; case frame::colormap::RGBA: for (uint32_t x = 0; x < width; ++x) { - dstRow[x * 3] = srcRow[x * 4]; // R + dstRow[x * 3 + 2] = srcRow[x * 4 + 0]; // R dstRow[x * 3 + 1] = srcRow[x * 4 + 1]; // G - dstRow[x * 3 + 2] = srcRow[x * 4 + 2]; // B + dstRow[x * 3 + 0] = srcRow[x * 4 + 2]; // B } break; case frame::colormap::BGR: for (uint32_t x = 0; x < width; ++x) { - dstRow[x * 3] = srcRow[x * 3 + 2]; // R + dstRow[x * 3 + 2] = srcRow[x * 3 + 2]; // R dstRow[x * 3 + 1] = srcRow[x * 3 + 1]; // G - dstRow[x * 3 + 2] = srcRow[x * 3]; // B + dstRow[x * 3 + 0] = srcRow[x * 3 + 0]; // B } break; case frame::colormap::BGRA: for (uint32_t x = 0; x < width; ++x) { - dstRow[x * 3] = srcRow[x * 4 + 2]; // R + dstRow[x * 3 + 2] = srcRow[x * 4 + 2]; // R dstRow[x * 3 + 1] = srcRow[x * 4 + 1]; // G - dstRow[x * 3 + 2] = srcRow[x * 4]; // B + dstRow[x * 3 + 0] = srcRow[x * 4 + 0]; // B } break; case frame::colormap::B: for (uint32_t x = 0; x < width; ++x) { uint8_t gray = srcRow[x]; - dstRow[x * 3] = gray; // R + dstRow[x * 3 + 0] = gray; // R dstRow[x * 3 + 1] = gray; // G dstRow[x * 3 + 2] = gray; // B } diff --git a/util/output/bmpwriter.hpp b/util/output/bmpwriter.hpp index a3e3a40..8ea5bac 100644 --- a/util/output/bmpwriter.hpp +++ b/util/output/bmpwriter.hpp @@ -8,6 +8,7 @@ #include #include #include "../vectorlogic/vec3.hpp" +#include "frame.hpp" class BMPWriter { private: @@ -137,6 +138,10 @@ public: return true; } + static bool saveBMP(const std::string& filename, frame& frame) { + return saveBMP(filename, frame.getData(), frame.getWidth(), frame.getHeight()); + } + private: static bool saveBMP(const std::string& filename, const std::vector>& pixels, int width, int height) { // Create directory if needed diff --git a/util/output/frame.hpp b/util/output/frame.hpp index 3406830..632ebd8 100644 --- a/util/output/frame.hpp +++ b/util/output/frame.hpp @@ -15,6 +15,7 @@ #include #include #include +#include "../timing_decorator.hpp" class frame { private: @@ -47,10 +48,11 @@ public: colormap colorFormat = colormap::RGB; compresstype cformat = compresstype::RAW; - size_t getWidth() { + const size_t& getWidth() { return width; } - size_t getHeight() { + + const size_t& getHeight() { return height; } frame() {}; @@ -73,6 +75,8 @@ public: _compressedData.clear(); _compressedData.shrink_to_fit(); overheadmap.clear(); + sourceSize = data.size(); + } const std::vector& getData() const { @@ -221,9 +225,6 @@ public: // Get compressed size including dictionary overhead size_t getTotalCompressedSize() const { size_t baseSize = getCompressedDataSize(); - if (cformat == compresstype::LZ78) { - baseSize += getDictionarySize(); - } return baseSize; } @@ -237,7 +238,7 @@ public: } size_t getCompressedDataSize() const { - return _compressedData.size(); + return _compressedData.size() * 2; } void printCompressionInfo() const { @@ -274,16 +275,9 @@ public: } void printCompressionStats() const { - if (cformat == compresstype::LZ78) { - std::cout << "[" << getCompressionTypeString() << "] " - << "Source Size: " << getSourceSize() << " bytes" - << getTotalCompressedSize() << "B " - << "(ratio: " << getCompressionRatio() << ":1)" << std::endl; - } else { - std::cout << "[" << getCompressionTypeString() << "] " - << getSourceSize() << "B -> " << getTotalCompressedSize() << "B " - << "(ratio: " << getCompressionRatio() << ":1)" << std::endl; - } + std::cout << "[" << getCompressionTypeString() << "] " + << getSourceSize() << "B -> " << getTotalCompressedSize() << "B " + << "(ratio: " << getCompressionRatio() << ":1)" << std::endl; } // Get compression type as string @@ -318,8 +312,6 @@ public: } private: - //moving decompression to private to prevent breaking stuff from external calls - std::vector> sortvecs(std::vector> source) { std::sort(source.begin(), source.end(), [](const std::vector & a, const std::vector & b) {return a.size() > b.size();}); return source;