diff --git a/src/blt/std/format.cpp b/src/blt/std/format.cpp index 9dd1c63..e07b209 100755 --- a/src/blt/std/format.cpp +++ b/src/blt/std/format.cpp @@ -6,16 +6,10 @@ #include #include #include "blt/std/logging.h" +#include "blt/std/assert.h" #include #include - -std::string createPadding(int padAmount) -{ - std::string padStr; - for (int i = 0; i < padAmount; i++) - padStr += " "; - return padStr; -} +#include std::vector blt::string::TableFormatter::createTable(bool top, bool bottom) { @@ -152,32 +146,149 @@ void blt::string::TableFormatter::updateMaxColumnLengths() struct node_data { blt::string::TreeFormatter::Node* node; + std::vector box; size_t level; node_data(blt::string::TreeFormatter::Node* node, size_t level): node(node), level(level) {} }; +struct level_data +{ + std::vector level; + size_t count = 0; + size_t depth = 0; + size_t max_horizontal_length = 0; +}; + std::vector blt::string::TreeFormatter::construct() { - std::vector lines; - - std::stack nodes; - + std::stack levels; std::queue bfs; bfs.emplace(root, 0); + // construct a stack of the highest node -> the lowest node. + level_data currentLevel; + currentLevel.depth = 0; while (!bfs.empty()) { auto n = bfs.front(); - nodes.push(n); - std::cout << "Node at level " << n.level << " " << bfs.size() << "\n"; + if (currentLevel.depth != n.level) + { + levels.push(currentLevel); + currentLevel = {}; + } + currentLevel.count++; + if (n.node != nullptr) + { + auto box = generateBox(n.node); + currentLevel.max_horizontal_length = std::max(currentLevel.max_horizontal_length, box[0].size()); + n.box = std::move(box); + } + currentLevel.level.push_back(n); + currentLevel.depth = n.level; + bfs.pop(); + //std::cout << "Node at level " << n.level << " " << n.node << "\n"; + if (n.node == nullptr) + continue; if (n.node->left != nullptr) bfs.emplace(n.node->left, n.level + 1); + else + bfs.emplace(nullptr, n.level + 1); + if (n.node->right != nullptr) bfs.emplace(n.node->right, n.level + 1); - bfs.pop(); + else + bfs.emplace(nullptr, n.level + 1); + } + std::vector lines; + size_t lineLength = 0; + const size_t lineHeight = format.verticalPadding * 2 + 3; + //std::cout << levels.size() << "\n"; + while (!levels.empty()) + { + std::vector currentLines; + const auto& n = levels.top(); + + for (const auto& b : n.level) + { + std::vector box = b.box; + if (b.node == nullptr || box.empty()) + { + for (size_t i = 0; i < lineHeight; i++) + box.push_back(createPadding(n.max_horizontal_length)); + } + if (currentLines.empty()) + { + for (const auto& v : box) + currentLines.push_back(v); + } else + { + BLT_ASSERT(currentLines.size() == box.size() && "Box lines should match current lines!"); + for (size_t i = 0; i < currentLines.size(); i++) + { + currentLines[i] += createPadding(format.horizontalSpacing); + currentLines[i] += box[i]; + } + } + } + std::int64_t padLength = (static_cast(lineLength) - static_cast(currentLines[0].length())) / 2; + if (padLength < 0) + padLength = 0; + for (const auto& v : currentLines) + { + lineLength = std::max(lineLength, v.length()); + lines.push_back(createPadding(padLength) + v); + } + levels.pop(); + //if (!levels.empty()) + // for (int i = 0; i < format.verticalSpacing; i++) + // lines.emplace_back("&"); } + size_t index = 1; + size_t startLine = 0; + size_t endLine = 0; + while (index < lines.size() - 1) + { + auto& line = lines[index]; + size_t beginMarkerIndex = 0; + size_t endMarkerIndex = 0; + startLine = index++; + beginMarkerIndex = line.find('%'); + if (beginMarkerIndex != std::string::npos) + { + // find endLine we need to connect with + while (index < lines.size()) + { + auto& line2 = lines[index]; + endMarkerIndex = line2.find('%'); + if (endMarkerIndex != std::string::npos) + { + endLine = index; + break; + } + index++; + } + + while (true) + { + if (beginMarkerIndex == std::string::npos || endMarkerIndex == std::string::npos) + break; + + BLT_TRACE(std::abs(static_cast(endMarkerIndex) - static_cast(beginMarkerIndex))); + auto connector = createPadding(std::abs(static_cast(endMarkerIndex) - static_cast(beginMarkerIndex)), '-'); + auto frontPad = createPadding(std::min(beginMarkerIndex, endMarkerIndex)); + lines.insert(lines.begin() + static_cast(startLine) + 1, frontPad += connector); + index++; + + beginMarkerIndex = line.find('%', beginMarkerIndex + 1); + endMarkerIndex = line.find('%', endMarkerIndex + 1); + } + index++; + } + } + + std::reverse(lines.begin(), lines.end()); return lines; } @@ -201,6 +312,7 @@ std::vector blt::string::TreeFormatter::generateBox(blt::string::Tr // create horizontal line based on the calculated length std::string hline = createLine(totalLength, '+', '-'); + hline[(hline.size() - 1) / 2] = '%'; std::string paddedLine = createLine(totalLength, '|', ' '); std::string dataStr; dataStr.reserve(totalLength); diff --git a/tests/src/utility_test.cpp b/tests/src/utility_test.cpp index f3758cd..29f1ffa 100644 --- a/tests/src/utility_test.cpp +++ b/tests/src/utility_test.cpp @@ -44,52 +44,28 @@ void blt::test::utility::run() } - blt::string::TableFormatter parkerLove("Intrinsic Action Value Table"); - parkerLove.addColumn({"Thing"}); - parkerLove.addColumn({"Value"}); - - parkerLove.addRow({"Cuddles", "1 / minute"}); - parkerLove.addRow({"Hand Job", "10"}); - parkerLove.addRow({"Head", "100"}); - parkerLove.addRow({"Sleeping Together (Non-Sexual)", "1,000"}); - parkerLove.addRow({"Actual Sex", "5,000"}); - parkerLove.addRow({"Sleeping Together (Sexual)", "10,000"}); - parkerLove.addRow({"Relationship (I would do anything for you)", "1,000,000,000,000"}); - - - printLines(parkerLove.createTable(true, true)); - - - - - - - - - - - - - - - - - - - - - - - + blt::string::TableFormatter tableTest("Intrinsic Action Value Table"); + tableTest.addColumn({"Thing"}); + tableTest.addColumn({"Value"}); + tableTest.addRow({"Cuddles", "1 / minute"}); + tableTest.addRow({"Hand Job", "10"}); + tableTest.addRow({"Head", "100"}); + tableTest.addRow({"Sleeping Together (Non-Sexual)", "1,000"}); + tableTest.addRow({"Actual Sex", "5,000"}); + tableTest.addRow({"Sleeping Together (Sexual)", "10,000"}); + tableTest.addRow({"Relationship (I would do anything for you)", "1,000,000,000,000"}); + printLines(tableTest.createTable(true, true)); blt::string::TreeFormatter treeFormatter("I love Men"); treeFormatter.getRoot()->with( - new string::TreeFormatter::Node("Guys"), - new string::TreeFormatter::Node("Femboys")); + (new string::TreeFormatter::Node("Guys")) + ->with(new string::TreeFormatter::Node("Child1"), nullptr), + (new string::TreeFormatter::Node("Femboys")) + ->with(nullptr, new string::TreeFormatter::Node("Child2"))); printLines(treeFormatter.construct());