1
0
Files
wavefront-parser/src/scan.cpp
2026-02-21 14:47:30 +02:00

131 lines
4.6 KiB
C++

#include <algorithm>
#include <array>
#include <functional>
#include <format>
#include <iostream>
#include <optional>
#include <sstream>
#include <string_view>
#include "trim.hpp"
#include "scan.hpp"
namespace wavefront {
scan_result::scan_result() :
total_lines(0),
line_data(),
category_map{
{ "v", {0} },
{ "vn", {0} },
{ "vt", {0} },
{ "f", {} },
}
{}
scan_result scan(
std::istream &input,
const std::vector<std::string> &selected_objects,
const std::vector<std::string> &selected_groups
) {
using line_processor_func_t = std::function<
void(const std::string &type, const std::size_t &line_number)
>;
using store_filter_factory_func_t = std::function<
line_processor_func_t(
const std::map<std::size_t, std::string> &line_data,
std::optional<std::string> &target_filter
)
>;
using check_filter_func_t = std::function<bool()>;
static const auto return_true = []()->bool { return true; };
std::optional<std::string> current_object, current_group;
check_filter_func_t check_object = return_true;
check_filter_func_t check_group = return_true;
if (selected_objects.size() > 0) {
check_object = [&]()->bool {
auto begin = selected_objects.begin();
auto end = selected_objects.end();
return std::find(begin, end, current_object.value_or("")) != end;
};
}
if (selected_groups.size() > 0) {
check_group = [&]()->bool {
auto begin = selected_groups.begin();
auto end = selected_groups.end();
return std::find(begin, end, current_group.value_or("")) != end;
};
}
static const store_filter_factory_func_t create_filter_store = [](
const std::map<std::size_t, std::string> &line_data,
std::optional<std::string> &target_filter
) {
return [&](const std::string &type, const std::size_t &line_number) {
auto line = line_data.at(line_number);
auto trimmed_name = trim(std::string_view(line.data() + type.size(), line.size() - type.size()));
if (trimmed_name.empty()) [[unlikely]] {
target_filter.reset();
} else [[likely]] {
target_filter = std::string(trimmed_name);
}
};
};
std::array<char, 4096 + 1> buffer;
scan_result result;
const line_processor_func_t insert_by_type = [&](
const std::string &type,
const std::size_t &line_number
) {
result.category_map[type].push_back(line_number);
};
const std::map<std::string, line_processor_func_t> line_processor_map{
{ "v", insert_by_type },
{ "vn", insert_by_type },
{ "vt", insert_by_type },
{ "f", [&](const std::string &type, const std::size_t &line_number) {
if (check_object() && check_group()) {
insert_by_type(type, line_number);
}
}},
{ "o", create_filter_store(result.line_data, current_object) },
{ "g", create_filter_store(result.line_data, current_group) },
};
const auto max_type_length = [&]() {
std::size_t max_length = 0;
for (const auto &[type, _] : line_processor_map) {
if (type.size() > max_length) {
max_length = type.size();
}
}
return max_length;
}();
std::string line;
while (std::getline(input, line)) {
++result.total_lines;
if (line.empty() || line[0] == '#' || trim(line).empty()) [[unlikely]] {
continue;
}
auto line_type_end_pos = line.substr(0, max_type_length + 1).find_first_of(' ');
if (line_type_end_pos == std::string::npos) [[unlikely]] {
continue;
}
auto line_type = std::string(line.substr(0, line_type_end_pos));
auto type_processing_iterator = line_processor_map.find(line_type);
if (type_processing_iterator == line_processor_map.end()) [[unlikely]] {
continue;
}
result.line_data[result.total_lines] = std::string(line);
type_processing_iterator->second(line_type, result.total_lines);
}
return result;
}
}